@decohere/http 0.0.1-dev.125 → 0.0.1-dev.5

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 (292) hide show
  1. package/package.json +4 -13
  2. package/src/app/app.ts +147 -0
  3. package/src/app/module.ts +19 -0
  4. package/src/auth/ability.ts +56 -0
  5. package/src/auth/auth.config.ts +34 -0
  6. package/src/auth/auth.extensions.ts +115 -0
  7. package/src/auth/auth.types.ts +124 -0
  8. package/src/auth/casl-authorization.service.ts +62 -0
  9. package/{types/src/auth/principal-provider.d.ts → src/auth/principal-provider.ts} +13 -5
  10. package/src/auth/safe-outbound-url.ts +117 -0
  11. package/src/auth/token-service.ts +192 -0
  12. package/src/builder/builder.ts +118 -0
  13. package/{es6/src/const.js → src/const.ts} +25 -17
  14. package/src/health/health.controller.ts +34 -0
  15. package/src/health/health.extensions.ts +53 -0
  16. package/src/health/readiness.service.ts +33 -0
  17. package/{types/src/health/readiness.types.d.ts → src/health/readiness.types.ts} +4 -8
  18. package/src/hosting/hosted-service-host.ts +102 -0
  19. package/src/hosting/hosted-service.ts +31 -0
  20. package/src/http/http.compression.ts +38 -0
  21. package/src/http/http.extensions.ts +244 -0
  22. package/{types/src/http/http.options.store.d.ts → src/http/http.options.store.ts} +11 -1
  23. package/src/http/http.options.ts +41 -0
  24. package/{types/src/http/http.security.d.ts → src/http/http.security.ts} +16 -10
  25. package/src/http/http.static.ts +359 -0
  26. package/{es6/src/index.js → src/index.ts} +1 -0
  27. package/src/openapi/index.ts +22 -0
  28. package/src/openapi/openapi.generator.ts +78 -0
  29. package/src/openapi/openapi.types.ts +60 -0
  30. package/src/openapi/request-schema.ts +155 -0
  31. package/src/openapi/request-validator.ts +95 -0
  32. package/src/providers/fastify.provider.ts +454 -0
  33. package/src/routing/anvil.types.ts +150 -0
  34. package/src/routing/decorators/argument.decorator.ts +33 -0
  35. package/src/routing/decorators/auth.decorator.ts +79 -0
  36. package/src/routing/decorators/controller.decorator.ts +9 -0
  37. package/src/routing/decorators/field.decorator.ts +15 -0
  38. package/src/routing/decorators/request.decorator.ts +34 -0
  39. package/src/routing/decorators/response.decorator.ts +18 -0
  40. package/src/routing/decorators/route.decorator.ts +52 -0
  41. package/src/routing/filters/exception-filter-host.ts +90 -0
  42. package/src/routing/filters/exception-filter.ts +29 -0
  43. package/src/routing/middleware/http-access-log.middleware.ts +36 -0
  44. package/src/routing/middleware/middleware.ts +26 -0
  45. package/src/routing/middleware/request-id.ts +18 -0
  46. package/src/routing/path.util.ts +38 -0
  47. package/src/routing/pipes/pipes.ts +132 -0
  48. package/src/routing/services/route-argument-resolver.ts +92 -0
  49. package/src/routing/services/route-registry.ts +229 -0
  50. package/src/routing/services/router.ts +166 -0
  51. package/src/sse/sse.decorator.ts +41 -0
  52. package/src/sse/sse.encoder.ts +86 -0
  53. package/src/sse/sse.types.ts +47 -0
  54. package/src/sse/sse.wire.ts +34 -0
  55. package/src/testing/boot-module.ts +63 -0
  56. package/src/testing/sse-reader.ts +120 -0
  57. package/tests/app.hosted-services.spec.ts +292 -0
  58. package/tests/app.shutdown.integration.spec.ts +160 -0
  59. package/tests/app.spec.ts +132 -0
  60. package/tests/auth.ability.spec.ts +48 -0
  61. package/tests/auth.decorators.spec.ts +20 -0
  62. package/tests/auth.fail-closed.pentest.spec.ts +77 -0
  63. package/tests/auth.integration.spec.ts +234 -0
  64. package/tests/auth.jwks-cache.spec.ts +52 -0
  65. package/tests/auth.missing-auth.pentest.spec.ts +56 -0
  66. package/tests/auth.oidc-ssrf.pentest.spec.ts +72 -0
  67. package/tests/auth.oidc.integration.spec.ts +161 -0
  68. package/tests/auth.token-service.spec.ts +251 -0
  69. package/tests/builder.spec.ts +12 -0
  70. package/tests/health.integration.spec.ts +111 -0
  71. package/tests/helpers/listen-ephemeral.ts +13 -0
  72. package/tests/helpers/local-oidc-server.ts +100 -0
  73. package/tests/http.access-log.integration.spec.ts +111 -0
  74. package/tests/http.boolean-query.integration.spec.ts +59 -0
  75. package/tests/http.compression.integration.spec.ts +604 -0
  76. package/tests/http.date-query.integration.spec.ts +71 -0
  77. package/tests/http.global-prefix.integration.spec.ts +284 -0
  78. package/tests/http.number-query.integration.spec.ts +65 -0
  79. package/tests/http.pino-roll-hmr.integration.spec.ts +79 -0
  80. package/tests/http.pipeline-order.integration.spec.ts +95 -0
  81. package/tests/http.request-cleanup.integration.spec.ts +57 -0
  82. package/tests/http.request-id-scope.pentest.spec.ts +50 -0
  83. package/tests/http.security.integration.spec.ts +106 -0
  84. package/tests/http.static.integration.spec.ts +847 -0
  85. package/tests/http.static.spec.ts +111 -0
  86. package/tests/http.surface.integration.spec.ts +167 -0
  87. package/tests/openapi.integration.spec.ts +193 -0
  88. package/tests/openapi.validator-cache.integration.spec.ts +55 -0
  89. package/tests/routing.decorators.spec.ts +15 -0
  90. package/tests/routing.registry.spec.ts +44 -0
  91. package/tests/routing.resolver.spec.ts +92 -0
  92. package/tests/sse.integration.spec.ts +421 -0
  93. package/tests/sse.wire.spec.ts +33 -0
  94. package/LICENSE +0 -15
  95. package/commonjs/src/app/app.js +0 -166
  96. package/commonjs/src/app/index.js +0 -19
  97. package/commonjs/src/app/module.js +0 -17
  98. package/commonjs/src/auth/ability.js +0 -54
  99. package/commonjs/src/auth/auth.config.js +0 -23
  100. package/commonjs/src/auth/auth.extensions.js +0 -106
  101. package/commonjs/src/auth/auth.types.js +0 -65
  102. package/commonjs/src/auth/casl-authorization.service.js +0 -36
  103. package/commonjs/src/auth/index.js +0 -23
  104. package/commonjs/src/auth/principal-provider.js +0 -19
  105. package/commonjs/src/auth/safe-outbound-url.js +0 -102
  106. package/commonjs/src/auth/token-service.js +0 -151
  107. package/commonjs/src/builder/builder.js +0 -95
  108. package/commonjs/src/builder/index.js +0 -18
  109. package/commonjs/src/const.js +0 -87
  110. package/commonjs/src/health/health.controller.js +0 -75
  111. package/commonjs/src/health/health.extensions.js +0 -50
  112. package/commonjs/src/health/index.js +0 -21
  113. package/commonjs/src/health/readiness.service.js +0 -67
  114. package/commonjs/src/health/readiness.types.js +0 -7
  115. package/commonjs/src/hosting/hosted-service-host.js +0 -88
  116. package/commonjs/src/hosting/hosted-service.js +0 -11
  117. package/commonjs/src/hosting/index.js +0 -19
  118. package/commonjs/src/http/http.compression.js +0 -27
  119. package/commonjs/src/http/http.extensions.js +0 -269
  120. package/commonjs/src/http/http.options.js +0 -26
  121. package/commonjs/src/http/http.options.store.js +0 -20
  122. package/commonjs/src/http/http.security.js +0 -27
  123. package/commonjs/src/http/http.static.js +0 -248
  124. package/commonjs/src/http/index.js +0 -22
  125. package/commonjs/src/index.js +0 -27
  126. package/commonjs/src/openapi/index.js +0 -45
  127. package/commonjs/src/openapi/openapi.generator.js +0 -103
  128. package/commonjs/src/openapi/openapi.types.js +0 -4
  129. package/commonjs/src/openapi/request-schema.js +0 -187
  130. package/commonjs/src/openapi/request-validator.js +0 -85
  131. package/commonjs/src/providers/fastify.provider.js +0 -391
  132. package/commonjs/src/providers/index.js +0 -18
  133. package/commonjs/src/routing/anvil.types.js +0 -99
  134. package/commonjs/src/routing/decorators/argument.decorator.js +0 -49
  135. package/commonjs/src/routing/decorators/auth.decorator.js +0 -70
  136. package/commonjs/src/routing/decorators/controller.decorator.js +0 -16
  137. package/commonjs/src/routing/decorators/field.decorator.js +0 -25
  138. package/commonjs/src/routing/decorators/index.js +0 -24
  139. package/commonjs/src/routing/decorators/request.decorator.js +0 -30
  140. package/commonjs/src/routing/decorators/response.decorator.js +0 -34
  141. package/commonjs/src/routing/decorators/route.decorator.js +0 -67
  142. package/commonjs/src/routing/filters/exception-filter-host.js +0 -145
  143. package/commonjs/src/routing/filters/exception-filter.js +0 -28
  144. package/commonjs/src/routing/index.js +0 -25
  145. package/commonjs/src/routing/middleware/http-access-log.middleware.js +0 -47
  146. package/commonjs/src/routing/middleware/middleware.js +0 -20
  147. package/commonjs/src/routing/middleware/request-id.js +0 -30
  148. package/commonjs/src/routing/path.util.js +0 -51
  149. package/commonjs/src/routing/pipes/index.js +0 -18
  150. package/commonjs/src/routing/pipes/pipes.js +0 -145
  151. package/commonjs/src/routing/services/index.js +0 -18
  152. package/commonjs/src/routing/services/route-argument-resolver.js +0 -77
  153. package/commonjs/src/routing/services/route-registry.js +0 -194
  154. package/commonjs/src/routing/services/router.js +0 -158
  155. package/commonjs/src/sse/index.js +0 -26
  156. package/commonjs/src/sse/sse.decorator.js +0 -42
  157. package/commonjs/src/sse/sse.encoder.js +0 -73
  158. package/commonjs/src/sse/sse.types.js +0 -36
  159. package/commonjs/src/sse/sse.wire.js +0 -40
  160. package/commonjs/src/testing/boot-module.js +0 -38
  161. package/commonjs/src/testing/index.js +0 -20
  162. package/commonjs/src/testing/sse-reader.js +0 -98
  163. package/es6/src/app/app.js +0 -148
  164. package/es6/src/app/index.js +0 -2
  165. package/es6/src/app/module.js +0 -7
  166. package/es6/src/auth/ability.js +0 -45
  167. package/es6/src/auth/auth.config.js +0 -19
  168. package/es6/src/auth/auth.extensions.js +0 -83
  169. package/es6/src/auth/auth.types.js +0 -35
  170. package/es6/src/auth/casl-authorization.service.js +0 -40
  171. package/es6/src/auth/index.js +0 -6
  172. package/es6/src/auth/principal-provider.js +0 -14
  173. package/es6/src/auth/safe-outbound-url.js +0 -99
  174. package/es6/src/auth/token-service.js +0 -141
  175. package/es6/src/builder/builder.js +0 -85
  176. package/es6/src/builder/index.js +0 -1
  177. package/es6/src/health/health.controller.js +0 -65
  178. package/es6/src/health/health.extensions.js +0 -40
  179. package/es6/src/health/index.js +0 -4
  180. package/es6/src/health/readiness.service.js +0 -57
  181. package/es6/src/health/readiness.types.js +0 -4
  182. package/es6/src/hosting/hosted-service-host.js +0 -81
  183. package/es6/src/hosting/hosted-service.js +0 -8
  184. package/es6/src/hosting/index.js +0 -2
  185. package/es6/src/http/http.compression.js +0 -9
  186. package/es6/src/http/http.extensions.js +0 -259
  187. package/es6/src/http/http.options.js +0 -16
  188. package/es6/src/http/http.options.store.js +0 -13
  189. package/es6/src/http/http.security.js +0 -9
  190. package/es6/src/http/http.static.js +0 -224
  191. package/es6/src/http/index.js +0 -5
  192. package/es6/src/openapi/index.js +0 -3
  193. package/es6/src/openapi/openapi.generator.js +0 -82
  194. package/es6/src/openapi/openapi.types.js +0 -1
  195. package/es6/src/openapi/request-schema.js +0 -160
  196. package/es6/src/openapi/request-validator.js +0 -71
  197. package/es6/src/providers/fastify.provider.js +0 -376
  198. package/es6/src/providers/index.js +0 -1
  199. package/es6/src/routing/anvil.types.js +0 -57
  200. package/es6/src/routing/decorators/argument.decorator.js +0 -28
  201. package/es6/src/routing/decorators/auth.decorator.js +0 -58
  202. package/es6/src/routing/decorators/controller.decorator.js +0 -6
  203. package/es6/src/routing/decorators/field.decorator.js +0 -15
  204. package/es6/src/routing/decorators/index.js +0 -7
  205. package/es6/src/routing/decorators/request.decorator.js +0 -24
  206. package/es6/src/routing/decorators/response.decorator.js +0 -16
  207. package/es6/src/routing/decorators/route.decorator.js +0 -37
  208. package/es6/src/routing/filters/exception-filter-host.js +0 -127
  209. package/es6/src/routing/filters/exception-filter.js +0 -10
  210. package/es6/src/routing/index.js +0 -8
  211. package/es6/src/routing/middleware/http-access-log.middleware.js +0 -37
  212. package/es6/src/routing/middleware/middleware.js +0 -10
  213. package/es6/src/routing/middleware/request-id.js +0 -14
  214. package/es6/src/routing/path.util.js +0 -33
  215. package/es6/src/routing/pipes/index.js +0 -1
  216. package/es6/src/routing/pipes/pipes.js +0 -118
  217. package/es6/src/routing/services/index.js +0 -1
  218. package/es6/src/routing/services/route-argument-resolver.js +0 -67
  219. package/es6/src/routing/services/route-registry.js +0 -184
  220. package/es6/src/routing/services/router.js +0 -148
  221. package/es6/src/sse/index.js +0 -2
  222. package/es6/src/sse/sse.decorator.js +0 -31
  223. package/es6/src/sse/sse.encoder.js +0 -67
  224. package/es6/src/sse/sse.types.js +0 -22
  225. package/es6/src/sse/sse.wire.js +0 -30
  226. package/es6/src/testing/boot-module.js +0 -35
  227. package/es6/src/testing/index.js +0 -2
  228. package/es6/src/testing/sse-reader.js +0 -91
  229. package/types/src/app/app.d.ts +0 -41
  230. package/types/src/app/module.d.ts +0 -15
  231. package/types/src/auth/ability.d.ts +0 -19
  232. package/types/src/auth/auth.config.d.ts +0 -28
  233. package/types/src/auth/auth.extensions.d.ts +0 -53
  234. package/types/src/auth/auth.types.d.ts +0 -73
  235. package/types/src/auth/casl-authorization.service.d.ts +0 -28
  236. package/types/src/auth/safe-outbound-url.d.ts +0 -9
  237. package/types/src/auth/token-service.d.ts +0 -31
  238. package/types/src/builder/builder.d.ts +0 -36
  239. package/types/src/const.d.ts +0 -32
  240. package/types/src/health/health.controller.d.ts +0 -16
  241. package/types/src/health/health.extensions.d.ts +0 -31
  242. package/types/src/health/readiness.service.d.ts +0 -9
  243. package/types/src/hosting/hosted-service-host.d.ts +0 -37
  244. package/types/src/hosting/hosted-service.d.ts +0 -31
  245. package/types/src/http/http.compression.d.ts +0 -32
  246. package/types/src/http/http.extensions.d.ts +0 -79
  247. package/types/src/http/http.options.d.ts +0 -39
  248. package/types/src/http/http.static.d.ts +0 -160
  249. package/types/src/index.d.ts +0 -10
  250. package/types/src/openapi/index.d.ts +0 -6
  251. package/types/src/openapi/openapi.generator.d.ts +0 -6
  252. package/types/src/openapi/openapi.types.d.ts +0 -58
  253. package/types/src/openapi/request-schema.d.ts +0 -21
  254. package/types/src/openapi/request-validator.d.ts +0 -9
  255. package/types/src/providers/fastify.provider.d.ts +0 -56
  256. package/types/src/routing/anvil.types.d.ts +0 -119
  257. package/types/src/routing/decorators/argument.decorator.d.ts +0 -11
  258. package/types/src/routing/decorators/auth.decorator.d.ts +0 -30
  259. package/types/src/routing/decorators/controller.decorator.d.ts +0 -3
  260. package/types/src/routing/decorators/field.decorator.d.ts +0 -2
  261. package/types/src/routing/decorators/request.decorator.d.ts +0 -8
  262. package/types/src/routing/decorators/response.decorator.d.ts +0 -4
  263. package/types/src/routing/decorators/route.decorator.d.ts +0 -7
  264. package/types/src/routing/filters/exception-filter-host.d.ts +0 -19
  265. package/types/src/routing/filters/exception-filter.d.ts +0 -16
  266. package/types/src/routing/middleware/http-access-log.middleware.d.ts +0 -8
  267. package/types/src/routing/middleware/middleware.d.ts +0 -10
  268. package/types/src/routing/middleware/request-id.d.ts +0 -6
  269. package/types/src/routing/path.util.d.ts +0 -9
  270. package/types/src/routing/pipes/pipes.d.ts +0 -21
  271. package/types/src/routing/services/route-argument-resolver.d.ts +0 -10
  272. package/types/src/routing/services/route-registry.d.ts +0 -20
  273. package/types/src/routing/services/router.d.ts +0 -22
  274. package/types/src/sse/sse.decorator.d.ts +0 -17
  275. package/types/src/sse/sse.encoder.d.ts +0 -16
  276. package/types/src/sse/sse.types.d.ts +0 -37
  277. package/types/src/sse/sse.wire.d.ts +0 -9
  278. package/types/src/testing/boot-module.d.ts +0 -34
  279. package/types/src/testing/sse-reader.d.ts +0 -29
  280. /package/{types/src/app/index.d.ts → src/app/index.ts} +0 -0
  281. /package/{types/src/auth/index.d.ts → src/auth/index.ts} +0 -0
  282. /package/{types/src/builder/index.d.ts → src/builder/index.ts} +0 -0
  283. /package/{types/src/health/index.d.ts → src/health/index.ts} +0 -0
  284. /package/{types/src/hosting/index.d.ts → src/hosting/index.ts} +0 -0
  285. /package/{types/src/http/index.d.ts → src/http/index.ts} +0 -0
  286. /package/{types/src/providers/index.d.ts → src/providers/index.ts} +0 -0
  287. /package/{types/src/routing/decorators/index.d.ts → src/routing/decorators/index.ts} +0 -0
  288. /package/{types/src/routing/index.d.ts → src/routing/index.ts} +0 -0
  289. /package/{types/src/routing/pipes/index.d.ts → src/routing/pipes/index.ts} +0 -0
  290. /package/{types/src/routing/services/index.d.ts → src/routing/services/index.ts} +0 -0
  291. /package/{types/src/sse/index.d.ts → src/sse/index.ts} +0 -0
  292. /package/{types/src/testing/index.d.ts → src/testing/index.ts} +0 -0
@@ -1,391 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "FastifyProvider", {
6
- enumerable: true,
7
- get: function() {
8
- return FastifyProvider;
9
- }
10
- });
11
- const _nodecrypto = require("node:crypto");
12
- const _promises = require("node:fs/promises");
13
- const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
14
- const _nodestream = require("node:stream");
15
- const _decorators = require("@decohere/crucible/decorators");
16
- const _scribe = require("@decohere/scribe");
17
- const _compress = /*#__PURE__*/ _interop_require_default(require("@fastify/compress"));
18
- const _cors = /*#__PURE__*/ _interop_require_default(require("@fastify/cors"));
19
- const _helmet = /*#__PURE__*/ _interop_require_default(require("@fastify/helmet"));
20
- const _ratelimit = /*#__PURE__*/ _interop_require_default(require("@fastify/rate-limit"));
21
- const _static = /*#__PURE__*/ _interop_require_default(require("@fastify/static"));
22
- const _fastify = /*#__PURE__*/ _interop_require_default(require("fastify"));
23
- const _const = require("../const.js");
24
- const _httpsecurity = require("../http/http.security.js");
25
- const _httpcompression = require("../http/http.compression.js");
26
- const _httpstatic = require("../http/http.static.js");
27
- const _openapigenerator = require("../openapi/openapi.generator.js");
28
- const _anviltypes = require("../routing/anvil.types.js");
29
- const _exceptionfilterhost = require("../routing/filters/exception-filter-host.js");
30
- const _requestid = require("../routing/middleware/request-id.js");
31
- const _pathutil = require("../routing/path.util.js");
32
- const _sseencoder = require("../sse/sse.encoder.js");
33
- const _ssetypes = require("../sse/sse.types.js");
34
- function _interop_require_default(obj) {
35
- return obj && obj.__esModule ? obj : {
36
- default: obj
37
- };
38
- }
39
- function _ts_decorate(decorators, target, key, desc) {
40
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
41
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
42
- else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
43
- return c > 3 && r && Object.defineProperty(target, key, r), r;
44
- }
45
- function _ts_metadata(k, v) {
46
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
47
- }
48
- function _ts_param(paramIndex, decorator) {
49
- return function(target, key) {
50
- decorator(target, key, paramIndex);
51
- };
52
- }
53
- class FastifyProvider {
54
- provider;
55
- router;
56
- routeRegistry;
57
- httpOptions;
58
- exceptionFilterHost;
59
- closing;
60
- logger = _scribe.scribe.logger(FastifyProvider.name);
61
- /** One per live `@Sse()` request, so stopping the app can end the streams. */ openStreams = new Set();
62
- constructor(routeRegistry, router, httpOptions, exceptionFilterHost){
63
- this.routeRegistry = routeRegistry;
64
- this.router = router;
65
- this.httpOptions = httpOptions;
66
- this.exceptionFilterHost = exceptionFilterHost;
67
- this.provider = (0, _fastify.default)({
68
- logger: false,
69
- requestIdHeader: _requestid.REQUEST_ID_HEADER,
70
- genReqId: ()=>(0, _nodecrypto.randomUUID)(),
71
- bodyLimit: httpOptions?.bodyLimit ?? _httpsecurity.DEFAULT_HTTP_BODY_LIMIT
72
- });
73
- }
74
- async start(options) {
75
- const statics = await this.resolveStatics();
76
- await this.registerHelmet(this.httpOptions?.helmet);
77
- await this.registerRateLimit(this.httpOptions?.rateLimit, statics);
78
- await this.registerCors(this.httpOptions?.cors);
79
- await this.registerCompression(this.httpOptions?.compression);
80
- this.registerOpenApi(this.httpOptions);
81
- this.registerAllRoutes(this.routeRegistry);
82
- for (const mount of statics){
83
- await this.registerStatic(mount, statics);
84
- }
85
- this.closeConnectionsWhileStopping();
86
- await this.provider.listen(options);
87
- return {
88
- addresses: this.addresses
89
- };
90
- }
91
- async stop() {
92
- // App stops HTTP before disposing the container, which calls this again.
93
- // A stream never finishes on its own, and close() waits for open responses, so end them first.
94
- for (const stream of this.openStreams){
95
- stream.abort(new Error("The app is stopping"));
96
- }
97
- this.closing ??= this.provider.close();
98
- await this.closing;
99
- return {
100
- message: "closed"
101
- };
102
- }
103
- async [Symbol.asyncDispose]() {
104
- await this.stop();
105
- }
106
- /**
107
- * Once stopping, tell clients to drop the connection after their response. Otherwise
108
- * a request that was in flight leaves a kept-alive socket that holds `close()` open
109
- * until Node's keep-alive timeout (5s).
110
- */ closeConnectionsWhileStopping() {
111
- this.provider.addHook("onSend", async (_request, reply, payload)=>{
112
- if (this.closing) reply.header("connection", "close");
113
- return payload;
114
- });
115
- }
116
- get addresses() {
117
- return this.provider.addresses().filter((a)=>a.family === "IPv4").map((a)=>`http://${a.address}:${a.port}`);
118
- }
119
- async registerHelmet(helmetOptions) {
120
- if (helmetOptions === false) {
121
- return;
122
- }
123
- await this.provider.register(_helmet.default, helmetOptions ?? {});
124
- }
125
- async registerRateLimit(rateLimitOptions, statics) {
126
- if (rateLimitOptions === false) {
127
- return;
128
- }
129
- // Static files are outside the rate limit (ADR 0007). Matched on the route the request
130
- // resolved to, not on its URL, so only requests the static plugin serves are exempt.
131
- const staticRoutes = new Set(statics.map((mount)=>`${mount.prefix}*`));
132
- const userAllowList = rateLimitOptions?.allowList;
133
- await this.provider.register(_ratelimit.default, {
134
- ..._httpsecurity.DEFAULT_HTTP_RATE_LIMIT,
135
- ...rateLimitOptions,
136
- allowList: (request, key)=>{
137
- if (staticRoutes.has(request.routeOptions.url ?? "")) return true;
138
- if (typeof userAllowList === "function") return userAllowList(request, key);
139
- return Array.isArray(userAllowList) && userAllowList.includes(key);
140
- }
141
- });
142
- }
143
- async resolveStatics() {
144
- return _httpstatic.staticFiles.resolve(this.httpOptions?.statics ?? [], {
145
- nodeEnv: process.env.NODE_ENV,
146
- directoryExists: async (directory)=>(await (0, _promises.stat)(directory).catch(()=>undefined))?.isDirectory() === true,
147
- warn: (message)=>this.logger.warn(message)
148
- });
149
- }
150
- /**
151
- * One encapsulated context per mount: its not-found handler (the SPA fallback) applies to
152
- * paths under the prefix and nowhere else, so an API 404 is never answered with the UI.
153
- */ async registerStatic(mount, mounts) {
154
- const withoutSlash = mount.prefix.slice(0, -1);
155
- const isRoot = mount.prefix === "/";
156
- const excluded = _httpstatic.staticFiles.excluder(mount.exclude);
157
- const belongsToApi = this.apiBoundary();
158
- // Requests that already fell back once: a missing index must be a 404, not a loop.
159
- const fellBack = new WeakSet();
160
- await this.provider.register(async (scope)=>{
161
- await scope.register(_static.default, {
162
- root: mount.root,
163
- prefix: "/",
164
- index: mount.index,
165
- dotfiles: "ignore",
166
- list: false,
167
- redirect: false,
168
- etag: true,
169
- lastModified: true,
170
- // The header is set in setHeaders, per file.
171
- cacheControl: false,
172
- setHeaders: (reply, filePath)=>{
173
- reply.header("cache-control", _httpstatic.staticFiles.cacheControlFor(mount, _nodepath.default.relative(mount.root, filePath)));
174
- }
175
- });
176
- // Excluded paths are not served at all: not as a file, not as the fallback.
177
- scope.addHook("onRequest", async (request, reply)=>{
178
- if (excluded(_httpstatic.staticFiles.normalizePathname(request.url))) {
179
- return reply.callNotFound();
180
- }
181
- });
182
- scope.setNotFoundHandler((request, reply)=>{
183
- const [pathname = "", query] = request.url.split("?");
184
- const readOnly = request.method === "GET" || request.method === "HEAD";
185
- // `/ui` goes to `/ui/`. A request for it reaches the root mount's handler instead
186
- // when there is one, so that handler redirects for the other mounts too.
187
- const redirectTo = mounts.find((other)=>other.prefix !== "/" && other.prefix.slice(0, -1) === pathname && (isRoot || other === mount));
188
- if (readOnly && redirectTo != null) {
189
- return reply.redirect(query == null ? redirectTo.prefix : `${redirectTo.prefix}?${query}`, 301);
190
- }
191
- const normalized = _httpstatic.staticFiles.normalizePathname(request.url);
192
- if (mount.spaFallback && !fellBack.has(request) && !excluded(normalized) && _httpstatic.staticFiles.shouldFallBack({
193
- method: request.method,
194
- pathname: normalized,
195
- accept: request.headers.accept
196
- }, belongsToApi)) {
197
- fellBack.add(request);
198
- return reply.sendFile(mount.index);
199
- }
200
- return reply.code(404).send({
201
- message: `Route ${request.method}:${pathname} not found`,
202
- error: "Not Found",
203
- statusCode: 404
204
- });
205
- });
206
- }, {
207
- prefix: withoutSlash
208
- });
209
- }
210
- /** What the static fallback leaves to the API: the global prefix, else the registered routes. ADR 0007. */ apiBoundary() {
211
- return _httpstatic.staticFiles.apiBoundary({
212
- globalPrefix: (0, _pathutil.normalizePrefix)(this.httpOptions?.globalPrefix),
213
- openApiPath: this.httpOptions?.openapi == null ? undefined : (0, _openapigenerator.resolveOpenApiPath)(this.httpOptions.openapi),
214
- routePaths: [
215
- ...this.routeRegistry.routes
216
- ].map((route)=>route.path)
217
- });
218
- }
219
- /**
220
- * `@fastify/compress` with best-practice defaults; the plugin owns the policy. Registered
221
- * before any route so every response goes through it, errors and static files included.
222
- * `providerOptions` is merged last, so a user can override any plugin option.
223
- */ async registerCompression(compressionOptions) {
224
- if (compressionOptions === false) {
225
- return;
226
- }
227
- await this.provider.register(_compress.default, {
228
- global: true,
229
- threshold: compressionOptions?.threshold ?? _httpcompression.DEFAULT_COMPRESSION_THRESHOLD,
230
- encodings: [
231
- ...compressionOptions?.encodings ?? _httpcompression.DEFAULT_COMPRESSION_ENCODINGS
232
- ],
233
- ...compressionOptions?.types != null && {
234
- customTypes: compressionOptions.types
235
- },
236
- ...compressionOptions?.providerOptions
237
- });
238
- }
239
- async registerCors(corsOptions) {
240
- if (corsOptions == null) {
241
- return;
242
- }
243
- await this.provider.register(_cors.default, {
244
- origin: corsOptions.origin,
245
- methods: corsOptions.methods,
246
- allowedHeaders: corsOptions.allowedHeaders,
247
- exposedHeaders: corsOptions.exposedHeaders,
248
- credentials: corsOptions.credentials,
249
- maxAge: corsOptions.maxAge
250
- });
251
- }
252
- registerOpenApi(httpOptions) {
253
- const openapi = httpOptions?.openapi;
254
- if (openapi == null) {
255
- return;
256
- }
257
- const path = (0, _openapigenerator.resolveOpenApiPath)(openapi);
258
- const document = (0, _openapigenerator.generateOpenApiDocument)(this.routeRegistry, openapi);
259
- this.provider.get(path, async (_request, reply)=>{
260
- reply.header("content-type", "application/json; charset=utf-8");
261
- return document;
262
- });
263
- }
264
- registerAllRoutes(registry) {
265
- for (const route of registry.routes){
266
- const { key, method, path } = route;
267
- this.provider.route({
268
- method,
269
- url: path,
270
- handler: (request, reply)=>this.routeRequest(route, key, request, reply)
271
- });
272
- }
273
- }
274
- async routeRequest(route, key, request, reply) {
275
- const httpRequest = this.fastifyToRequest(request);
276
- const disconnect = this.abortOnDisconnect(reply, httpRequest, route.sse != null);
277
- try {
278
- const response = await this.router.route(key, httpRequest);
279
- if (route.httpCode != null) {
280
- reply.status(route.httpCode);
281
- }
282
- if (route.headers != null) {
283
- for (const [name, value] of Object.entries(route.headers)){
284
- reply.header(name, value);
285
- }
286
- }
287
- this.applyResponseHeaders(reply, httpRequest);
288
- if (response instanceof _ssetypes.SseResponse) {
289
- return this.sendEventStream(route, reply, response, disconnect);
290
- }
291
- return response;
292
- } catch (error) {
293
- const result = this.exceptionFilterHost ? await this.exceptionFilterHost.handle(error, {
294
- request: httpRequest
295
- }) : new _exceptionfilterhost.DefaultHttpExceptionFilter().catch(error, {
296
- request: httpRequest
297
- });
298
- if (result.headers) {
299
- for (const [name, value] of Object.entries(result.headers)){
300
- reply.header(name, value);
301
- }
302
- }
303
- this.applyResponseHeaders(reply, httpRequest);
304
- // Returned so Fastify waits for the async onSend hooks instead of sending an empty reply of its own.
305
- return reply.status(result.status).send(result.body);
306
- }
307
- }
308
- /**
309
- * Gives the handler a signal that aborts when the client goes away before the response
310
- * finished. Streams are also tracked so that stopping the app can abort them.
311
- */ abortOnDisconnect(reply, httpRequest, isStream) {
312
- const controller = new AbortController();
313
- httpRequest.signal = controller.signal;
314
- if (isStream) {
315
- if (this.closing) controller.abort(new Error("The app is stopping"));
316
- this.openStreams.add(controller);
317
- }
318
- reply.raw.once("close", ()=>{
319
- this.openStreams.delete(controller);
320
- if (!reply.raw.writableFinished) {
321
- controller.abort(new Error("The client disconnected"));
322
- }
323
- });
324
- return controller;
325
- }
326
- sendEventStream(route, reply, response, disconnect) {
327
- let started = false;
328
- const release = ()=>{
329
- response.release().catch((error)=>{
330
- this.logger.error("Releasing the request scope of an SSE stream failed", error);
331
- });
332
- };
333
- const frames = (0, _sseencoder.encodeSse)(response.events, {
334
- signal: disconnect.signal,
335
- heartbeatMs: route.sse?.heartbeatMs ?? _ssetypes.DEFAULT_SSE_HEARTBEAT_MS,
336
- onProducerStopped: (stopped)=>void stopped.then(release)
337
- });
338
- const guarded = async function*() {
339
- started = true;
340
- yield* frames;
341
- }();
342
- // Never pulled if the client left first, in which case nothing else would release the scope.
343
- reply.raw.once("close", ()=>{
344
- if (!started) release();
345
- });
346
- return reply.status(route.httpCode ?? 200).header("content-type", "text/event-stream; charset=utf-8").header("cache-control", "no-cache, no-transform").header("x-accel-buffering", "no")// A stream owns its connection; closing it afterwards lets app stop finish at once instead of waiting out keep-alive.
347
- .header("connection", "close").send(_nodestream.Readable.from(guarded));
348
- }
349
- applyResponseHeaders(reply, httpRequest) {
350
- reply.header(_requestid.REQUEST_ID_HEADER, httpRequest.requestId);
351
- for (const [name, value] of Object.entries(httpRequest.responseHeaders)){
352
- reply.header(name, value);
353
- }
354
- }
355
- fastifyToRequest(fastifyRequest) {
356
- const method = fastifyRequest.method;
357
- const url = fastifyRequest.url;
358
- const params = fastifyRequest.params;
359
- const query = fastifyRequest.query;
360
- const body = fastifyRequest.body ?? {};
361
- const headers = {};
362
- for (const [key, value] of Object.entries(fastifyRequest.headers)){
363
- if (typeof value === "string") {
364
- headers[key.toLowerCase()] = value;
365
- }
366
- }
367
- const requestId = (0, _requestid.resolveRequestId)(headers, String(fastifyRequest.id ?? ""));
368
- return new _anviltypes.HttpRequest({
369
- requestId,
370
- url,
371
- method,
372
- params,
373
- query,
374
- body,
375
- headers
376
- });
377
- }
378
- }
379
- FastifyProvider = _ts_decorate([
380
- _ts_param(0, (0, _decorators.inject)(_const.RouteRegistryToken)),
381
- _ts_param(1, (0, _decorators.inject)(_const.RouterToken)),
382
- _ts_param(2, (0, _decorators.inject)(_const.HttpOptionsToken)),
383
- _ts_param(3, (0, _decorators.inject)(_const.ExceptionFilterHostToken)),
384
- _ts_metadata("design:type", Function),
385
- _ts_metadata("design:paramtypes", [
386
- typeof RouteRegistry === "undefined" ? Object : RouteRegistry,
387
- typeof Router === "undefined" ? Object : Router,
388
- typeof HttpOptions === "undefined" ? Object : HttpOptions,
389
- typeof ExceptionFilterHost === "undefined" ? Object : ExceptionFilterHost
390
- ])
391
- ], FastifyProvider);
@@ -1,18 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- _export_star(require("./fastify.provider.js"), exports);
6
- function _export_star(from, to) {
7
- Object.keys(from).forEach(function(k) {
8
- if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
9
- Object.defineProperty(to, k, {
10
- enumerable: true,
11
- get: function() {
12
- return from[k];
13
- }
14
- });
15
- }
16
- });
17
- return from;
18
- }
@@ -1,99 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- function _export(target, all) {
6
- for(var name in all)Object.defineProperty(target, name, {
7
- enumerable: true,
8
- get: Object.getOwnPropertyDescriptor(all, name).get
9
- });
10
- }
11
- _export(exports, {
12
- get ForbiddenError () {
13
- return ForbiddenError;
14
- },
15
- get HttpError () {
16
- return HttpError;
17
- },
18
- get HttpRequest () {
19
- return HttpRequest;
20
- },
21
- get InjectedParts () {
22
- return InjectedParts;
23
- },
24
- get RequestParts () {
25
- return RequestParts;
26
- },
27
- get RouteConfigurationError () {
28
- return RouteConfigurationError;
29
- },
30
- get RoutingError () {
31
- return RoutingError;
32
- },
33
- get TransformError () {
34
- return TransformError;
35
- },
36
- get UnauthorizedError () {
37
- return UnauthorizedError;
38
- },
39
- get ValidationError () {
40
- return ValidationError;
41
- }
42
- });
43
- const RequestParts = {
44
- query: "query",
45
- params: "params",
46
- body: "body",
47
- principal: "principal",
48
- signal: "signal",
49
- lastEventId: "lastEventId"
50
- };
51
- const InjectedParts = [
52
- "principal",
53
- "signal",
54
- "lastEventId"
55
- ];
56
- class HttpRequest {
57
- constructor(data){
58
- Object.assign(this, data);
59
- }
60
- requestId = "";
61
- url;
62
- method;
63
- params = {};
64
- query = {};
65
- body;
66
- headers = {};
67
- /** Aborts when the client disconnects or the app stops. Never aborts if the provider cannot tell. */ signal = new AbortController().signal;
68
- /** Outbound headers set by middleware; applied by the HTTP provider. */ responseHeaders = {};
69
- }
70
- class RouteConfigurationError extends Error {
71
- }
72
- class RoutingError extends Error {
73
- }
74
- class TransformError extends Error {
75
- }
76
- class HttpError extends Error {
77
- status;
78
- constructor(message, status){
79
- super(message);
80
- this.status = status;
81
- }
82
- }
83
- class UnauthorizedError extends HttpError {
84
- constructor(message = "Unauthorized"){
85
- super(message, 401);
86
- }
87
- }
88
- class ForbiddenError extends HttpError {
89
- constructor(message = "Forbidden"){
90
- super(message, 403);
91
- }
92
- }
93
- class ValidationError extends HttpError {
94
- errors;
95
- constructor(message = "Bad Request", errors = []){
96
- super(message, 400);
97
- this.errors = errors;
98
- }
99
- }
@@ -1,49 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- function _export(target, all) {
6
- for(var name in all)Object.defineProperty(target, name, {
7
- enumerable: true,
8
- get: Object.getOwnPropertyDescriptor(all, name).get
9
- });
10
- }
11
- _export(exports, {
12
- get Argument () {
13
- return Argument;
14
- },
15
- get Param () {
16
- return Param;
17
- },
18
- get Query () {
19
- return Query;
20
- }
21
- });
22
- const _reflection = require("@decohere/core/reflection");
23
- const _const = require("../../const.js");
24
- const _anviltypes = require("../anvil.types.js");
25
- function Query(key) {
26
- return Argument({
27
- mappingKey: key,
28
- argumentLocation: _anviltypes.RequestParts.query
29
- });
30
- }
31
- function Param(key) {
32
- return Argument({
33
- mappingKey: key,
34
- argumentLocation: _anviltypes.RequestParts.params
35
- });
36
- }
37
- function Argument(options) {
38
- return (target, functionName, argumentIndex)=>{
39
- const { mappingKey, argumentLocation, pipes } = options;
40
- const func = target[functionName];
41
- const meta = {
42
- argumentIndex,
43
- mappingKey,
44
- argumentLocation,
45
- pipes
46
- };
47
- (0, _reflection.defineArgumentMeta)(_const.MetadataKeys.parameter, meta, func, argumentIndex);
48
- };
49
- }
@@ -1,70 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- function _export(target, all) {
6
- for(var name in all)Object.defineProperty(target, name, {
7
- enumerable: true,
8
- get: Object.getOwnPropertyDescriptor(all, name).get
9
- });
10
- }
11
- _export(exports, {
12
- get AllowAnonymous () {
13
- return AllowAnonymous;
14
- },
15
- get Authorize () {
16
- return Authorize;
17
- },
18
- get CurrentUser () {
19
- return CurrentUser;
20
- },
21
- get getAuthorizeOptions () {
22
- return getAuthorizeOptions;
23
- },
24
- get isAllowAnonymous () {
25
- return isAllowAnonymous;
26
- }
27
- });
28
- const _reflection = require("@decohere/core/reflection");
29
- const _const = require("../../const.js");
30
- function Authorize(action, subject = "all") {
31
- const options = {
32
- action,
33
- subject
34
- };
35
- return (target, methodName, _descriptor)=>{
36
- if (methodName) {
37
- const fn = target[methodName];
38
- (0, _reflection.defineMeta)(_const.MetadataKeys.authorize, options, fn);
39
- } else {
40
- (0, _reflection.defineMeta)(_const.MetadataKeys.authorize, options, target.prototype);
41
- }
42
- };
43
- }
44
- function AllowAnonymous() {
45
- return (target, methodName, _descriptor)=>{
46
- const fn = target[methodName];
47
- (0, _reflection.defineMeta)(_const.MetadataKeys.allowAnonymous, true, fn);
48
- };
49
- }
50
- function getAuthorizeOptions(controllerPrototype, handlerFn) {
51
- // Method-level overrides class-level
52
- const methodLevel = (0, _reflection.getMeta)(_const.MetadataKeys.authorize, handlerFn);
53
- if (methodLevel !== undefined) return methodLevel;
54
- return (0, _reflection.getMeta)(_const.MetadataKeys.authorize, controllerPrototype);
55
- }
56
- function isAllowAnonymous(_controllerPrototype, handlerFn) {
57
- return (0, _reflection.getMeta)(_const.MetadataKeys.allowAnonymous, handlerFn) === true;
58
- }
59
- function CurrentUser() {
60
- return (target, propertyKey, parameterIndex)=>{
61
- const fn = target[propertyKey];
62
- const meta = {
63
- argumentIndex: parameterIndex,
64
- argumentLocation: "principal",
65
- mappingKey: undefined,
66
- pipes: []
67
- };
68
- (0, _reflection.defineArgumentMeta)(_const.MetadataKeys.parameter, meta, fn, parameterIndex);
69
- };
70
- }
@@ -1,16 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "Controller", {
6
- enumerable: true,
7
- get: function() {
8
- return Controller;
9
- }
10
- });
11
- const _const = require("../../const.js");
12
- function Controller(options) {
13
- return (target)=>{
14
- Reflect.defineMetadata(_const.MetadataKeys.controller, options, target.prototype);
15
- };
16
- }
@@ -1,25 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "Field", {
6
- enumerable: true,
7
- get: function() {
8
- return Field;
9
- }
10
- });
11
- const _reflection = require("@decohere/core/reflection");
12
- const _const = require("../../const.js");
13
- function Field(fieldLocation, mappingKey) {
14
- return (target, fieldName)=>{
15
- const fieldMap = (0, _reflection.getMeta)(_const.MetadataKeys.fieldMap, target) ?? new Map();
16
- const fieldType = (0, _reflection.getFieldType)(target, fieldName);
17
- fieldMap.set(fieldName, {
18
- fieldKey: fieldName,
19
- mappingKey,
20
- fieldType,
21
- fieldLocation
22
- });
23
- (0, _reflection.defineMeta)(_const.MetadataKeys.fieldMap, fieldMap, target);
24
- };
25
- }