@ddd-ts-kit/http 0.0.0-stage → 0.1.1

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 (394) hide show
  1. package/README.md +388 -2
  2. package/dist/criteria/coerce-value.d.ts +8 -0
  3. package/dist/criteria/coerce-value.d.ts.map +1 -0
  4. package/dist/criteria/coerce-value.js +29 -0
  5. package/dist/criteria/coerce-value.js.map +1 -0
  6. package/dist/criteria/criteria-from.d.ts +11 -0
  7. package/dist/criteria/criteria-from.d.ts.map +1 -0
  8. package/dist/criteria/criteria-from.js +17 -0
  9. package/dist/criteria/criteria-from.js.map +1 -0
  10. package/dist/criteria/criteria-parameters.d.ts +10 -0
  11. package/dist/criteria/criteria-parameters.d.ts.map +1 -0
  12. package/dist/criteria/criteria-parameters.js +45 -0
  13. package/dist/criteria/criteria-parameters.js.map +1 -0
  14. package/dist/criteria/criteria-query-options.d.ts +6 -0
  15. package/dist/criteria/criteria-query-options.d.ts.map +1 -0
  16. package/dist/criteria/criteria-query-options.js +2 -0
  17. package/dist/criteria/criteria-query-options.js.map +1 -0
  18. package/dist/criteria/criteria-query-schema.d.ts +10 -0
  19. package/dist/criteria/criteria-query-schema.d.ts.map +1 -0
  20. package/dist/criteria/criteria-query-schema.js +12 -0
  21. package/dist/criteria/criteria-query-schema.js.map +1 -0
  22. package/dist/criteria/filter-entries.d.ts +9 -0
  23. package/dist/criteria/filter-entries.d.ts.map +1 -0
  24. package/dist/criteria/filter-entries.js +28 -0
  25. package/dist/criteria/filter-entries.js.map +1 -0
  26. package/dist/criteria/filter-entry.d.ts +6 -0
  27. package/dist/criteria/filter-entry.d.ts.map +1 -0
  28. package/dist/criteria/filter-entry.js +2 -0
  29. package/dist/criteria/filter-entry.js.map +1 -0
  30. package/dist/criteria/parse-condition.d.ts +7 -0
  31. package/dist/criteria/parse-condition.d.ts.map +1 -0
  32. package/dist/criteria/parse-condition.js +52 -0
  33. package/dist/criteria/parse-condition.js.map +1 -0
  34. package/dist/criteria/parse-criteria-query.d.ts +5 -0
  35. package/dist/criteria/parse-criteria-query.d.ts.map +1 -0
  36. package/dist/criteria/parse-criteria-query.js +31 -0
  37. package/dist/criteria/parse-criteria-query.js.map +1 -0
  38. package/dist/criteria/parse-filters.d.ts +15 -0
  39. package/dist/criteria/parse-filters.d.ts.map +1 -0
  40. package/dist/criteria/parse-filters.js +52 -0
  41. package/dist/criteria/parse-filters.js.map +1 -0
  42. package/dist/criteria/sortable-fields.d.ts +4 -0
  43. package/dist/criteria/sortable-fields.d.ts.map +1 -0
  44. package/dist/criteria/sortable-fields.js +6 -0
  45. package/dist/criteria/sortable-fields.js.map +1 -0
  46. package/dist/criteria/split-list.d.ts +3 -0
  47. package/dist/criteria/split-list.d.ts.map +1 -0
  48. package/dist/criteria/split-list.js +19 -0
  49. package/dist/criteria/split-list.js.map +1 -0
  50. package/dist/docs/docs-page.d.ts +11 -0
  51. package/dist/docs/docs-page.d.ts.map +1 -0
  52. package/dist/docs/docs-page.js +18 -0
  53. package/dist/docs/docs-page.js.map +1 -0
  54. package/dist/docs/docs-ui.d.ts +2 -0
  55. package/dist/docs/docs-ui.d.ts.map +1 -0
  56. package/dist/docs/docs-ui.js +2 -0
  57. package/dist/docs/docs-ui.js.map +1 -0
  58. package/dist/docs/open-api-docs-routes.d.ts +22 -0
  59. package/dist/docs/open-api-docs-routes.d.ts.map +1 -0
  60. package/dist/docs/open-api-docs-routes.js +54 -0
  61. package/dist/docs/open-api-docs-routes.js.map +1 -0
  62. package/dist/docs.d.ts +3 -0
  63. package/dist/docs.d.ts.map +1 -0
  64. package/dist/docs.js +2 -0
  65. package/dist/docs.js.map +1 -0
  66. package/dist/error/create-error-mapper.d.ts +10 -0
  67. package/dist/error/create-error-mapper.d.ts.map +1 -0
  68. package/dist/error/create-error-mapper.js +12 -0
  69. package/dist/error/create-error-mapper.js.map +1 -0
  70. package/dist/error/default-error-mapping.d.ts +3 -0
  71. package/dist/error/default-error-mapping.d.ts.map +1 -0
  72. package/dist/error/default-error-mapping.js +9 -0
  73. package/dist/error/default-error-mapping.js.map +1 -0
  74. package/dist/error/error-mapper.d.ts +12 -0
  75. package/dist/error/error-mapper.d.ts.map +1 -0
  76. package/dist/error/error-mapper.js +8 -0
  77. package/dist/error/error-mapper.js.map +1 -0
  78. package/dist/error/error-mapping.d.ts +3 -0
  79. package/dist/error/error-mapping.d.ts.map +1 -0
  80. package/dist/error/error-mapping.js +2 -0
  81. package/dist/error/error-mapping.js.map +1 -0
  82. package/dist/error/forbidden-error.d.ts +5 -0
  83. package/dist/error/forbidden-error.d.ts.map +1 -0
  84. package/dist/error/forbidden-error.js +5 -0
  85. package/dist/error/forbidden-error.js.map +1 -0
  86. package/dist/error/internal-error-code.d.ts +2 -0
  87. package/dist/error/internal-error-code.d.ts.map +1 -0
  88. package/dist/error/internal-error-code.js +2 -0
  89. package/dist/error/internal-error-code.js.map +1 -0
  90. package/dist/error/problem.d.ts +11 -0
  91. package/dist/error/problem.d.ts.map +1 -0
  92. package/dist/error/problem.js +2 -0
  93. package/dist/error/problem.js.map +1 -0
  94. package/dist/error/response-contract-error.d.ts +7 -0
  95. package/dist/error/response-contract-error.d.ts.map +1 -0
  96. package/dist/error/response-contract-error.js +10 -0
  97. package/dist/error/response-contract-error.js.map +1 -0
  98. package/dist/error/status-title.d.ts +2 -0
  99. package/dist/error/status-title.d.ts.map +1 -0
  100. package/dist/error/status-title.js +28 -0
  101. package/dist/error/status-title.js.map +1 -0
  102. package/dist/error/to-problem.d.ts +4 -0
  103. package/dist/error/to-problem.d.ts.map +1 -0
  104. package/dist/error/to-problem.js +34 -0
  105. package/dist/error/to-problem.js.map +1 -0
  106. package/dist/error/unauthorized-error.d.ts +5 -0
  107. package/dist/error/unauthorized-error.d.ts.map +1 -0
  108. package/dist/error/unauthorized-error.js +5 -0
  109. package/dist/error/unauthorized-error.js.map +1 -0
  110. package/dist/express/mount-express.d.ts +10 -0
  111. package/dist/express/mount-express.d.ts.map +1 -0
  112. package/dist/express/mount-express.js +38 -0
  113. package/dist/express/mount-express.js.map +1 -0
  114. package/dist/express.d.ts +2 -0
  115. package/dist/express.d.ts.map +1 -0
  116. package/dist/express.js +2 -0
  117. package/dist/express.js.map +1 -0
  118. package/dist/fastify/fastify-mount-options.d.ts +3 -0
  119. package/dist/fastify/fastify-mount-options.d.ts.map +1 -0
  120. package/dist/fastify/fastify-mount-options.js +2 -0
  121. package/dist/fastify/fastify-mount-options.js.map +1 -0
  122. package/dist/fastify/fastify-routes.d.ts +6 -0
  123. package/dist/fastify/fastify-routes.d.ts.map +1 -0
  124. package/dist/fastify/fastify-routes.js +10 -0
  125. package/dist/fastify/fastify-routes.js.map +1 -0
  126. package/dist/fastify/mount-fastify.d.ts +6 -0
  127. package/dist/fastify/mount-fastify.d.ts.map +1 -0
  128. package/dist/fastify/mount-fastify.js +19 -0
  129. package/dist/fastify/mount-fastify.js.map +1 -0
  130. package/dist/fastify.d.ts +4 -0
  131. package/dist/fastify.d.ts.map +1 -0
  132. package/dist/fastify.js +3 -0
  133. package/dist/fastify.js.map +1 -0
  134. package/dist/index.d.ts +50 -0
  135. package/dist/index.d.ts.map +1 -0
  136. package/dist/index.js +27 -0
  137. package/dist/index.js.map +1 -0
  138. package/dist/nest/create-routes-module.d.ts +15 -0
  139. package/dist/nest/create-routes-module.d.ts.map +1 -0
  140. package/dist/nest/create-routes-module.js +54 -0
  141. package/dist/nest/create-routes-module.js.map +1 -0
  142. package/dist/nest/routes-factory.d.ts +8 -0
  143. package/dist/nest/routes-factory.d.ts.map +1 -0
  144. package/dist/nest/routes-factory.js +2 -0
  145. package/dist/nest/routes-factory.js.map +1 -0
  146. package/dist/nest/routes-mounter.d.ts +9 -0
  147. package/dist/nest/routes-mounter.d.ts.map +1 -0
  148. package/dist/nest/routes-mounter.js +8 -0
  149. package/dist/nest/routes-mounter.js.map +1 -0
  150. package/dist/nest.d.ts +4 -0
  151. package/dist/nest.d.ts.map +1 -0
  152. package/dist/nest.js +3 -0
  153. package/dist/nest.js.map +1 -0
  154. package/dist/openapi/check-open-api-drift.d.ts +6 -0
  155. package/dist/openapi/check-open-api-drift.d.ts.map +1 -0
  156. package/dist/openapi/check-open-api-drift.js +6 -0
  157. package/dist/openapi/check-open-api-drift.js.map +1 -0
  158. package/dist/openapi/compare.d.ts +3 -0
  159. package/dist/openapi/compare.d.ts.map +1 -0
  160. package/dist/openapi/compare.js +5 -0
  161. package/dist/openapi/compare.js.map +1 -0
  162. package/dist/openapi/definitions.d.ts +14 -0
  163. package/dist/openapi/definitions.d.ts.map +1 -0
  164. package/dist/openapi/definitions.js +61 -0
  165. package/dist/openapi/definitions.js.map +1 -0
  166. package/dist/openapi/generate-open-api.d.ts +21 -0
  167. package/dist/openapi/generate-open-api.d.ts.map +1 -0
  168. package/dist/openapi/generate-open-api.js +43 -0
  169. package/dist/openapi/generate-open-api.js.map +1 -0
  170. package/dist/openapi/open-api-document.d.ts +18 -0
  171. package/dist/openapi/open-api-document.d.ts.map +1 -0
  172. package/dist/openapi/open-api-document.js +2 -0
  173. package/dist/openapi/open-api-document.js.map +1 -0
  174. package/dist/openapi/open-api-info.d.ts +6 -0
  175. package/dist/openapi/open-api-info.d.ts.map +1 -0
  176. package/dist/openapi/open-api-info.js +2 -0
  177. package/dist/openapi/open-api-info.js.map +1 -0
  178. package/dist/openapi/open-api-path.d.ts +2 -0
  179. package/dist/openapi/open-api-path.d.ts.map +1 -0
  180. package/dist/openapi/open-api-path.js +4 -0
  181. package/dist/openapi/open-api-path.js.map +1 -0
  182. package/dist/openapi/open-api-server.d.ts +5 -0
  183. package/dist/openapi/open-api-server.d.ts.map +1 -0
  184. package/dist/openapi/open-api-server.js +2 -0
  185. package/dist/openapi/open-api-server.js.map +1 -0
  186. package/dist/openapi/path-items.d.ts +7 -0
  187. package/dist/openapi/path-items.d.ts.map +1 -0
  188. package/dist/openapi/path-items.js +43 -0
  189. package/dist/openapi/path-items.js.map +1 -0
  190. package/dist/openapi/path-parameters.d.ts +4 -0
  191. package/dist/openapi/path-parameters.d.ts.map +1 -0
  192. package/dist/openapi/path-parameters.js +13 -0
  193. package/dist/openapi/path-parameters.js.map +1 -0
  194. package/dist/openapi/problem-schema.d.ts +3 -0
  195. package/dist/openapi/problem-schema.d.ts.map +1 -0
  196. package/dist/openapi/problem-schema.js +27 -0
  197. package/dist/openapi/problem-schema.js.map +1 -0
  198. package/dist/openapi/requirement.d.ts +3 -0
  199. package/dist/openapi/requirement.d.ts.map +1 -0
  200. package/dist/openapi/requirement.js +5 -0
  201. package/dist/openapi/requirement.js.map +1 -0
  202. package/dist/openapi/responses.d.ts +7 -0
  203. package/dist/openapi/responses.d.ts.map +1 -0
  204. package/dist/openapi/responses.js +50 -0
  205. package/dist/openapi/responses.js.map +1 -0
  206. package/dist/openapi/schema-parameters.d.ts +5 -0
  207. package/dist/openapi/schema-parameters.d.ts.map +1 -0
  208. package/dist/openapi/schema-parameters.js +24 -0
  209. package/dist/openapi/schema-parameters.js.map +1 -0
  210. package/dist/openapi/stringify-open-api.d.ts +4 -0
  211. package/dist/openapi/stringify-open-api.d.ts.map +1 -0
  212. package/dist/openapi/stringify-open-api.js +5 -0
  213. package/dist/openapi/stringify-open-api.js.map +1 -0
  214. package/dist/openapi.d.ts +7 -0
  215. package/dist/openapi.d.ts.map +1 -0
  216. package/dist/openapi.js +4 -0
  217. package/dist/openapi.js.map +1 -0
  218. package/dist/pagination/issue.d.ts +3 -0
  219. package/dist/pagination/issue.d.ts.map +1 -0
  220. package/dist/pagination/issue.js +4 -0
  221. package/dist/pagination/issue.js.map +1 -0
  222. package/dist/pagination/limits-of.d.ts +3 -0
  223. package/dist/pagination/limits-of.d.ts.map +1 -0
  224. package/dist/pagination/limits-of.js +8 -0
  225. package/dist/pagination/limits-of.js.map +1 -0
  226. package/dist/pagination/page-body.d.ts +7 -0
  227. package/dist/pagination/page-body.d.ts.map +1 -0
  228. package/dist/pagination/page-body.js +2 -0
  229. package/dist/pagination/page-body.js.map +1 -0
  230. package/dist/pagination/page-query-options.d.ts +8 -0
  231. package/dist/pagination/page-query-options.d.ts.map +1 -0
  232. package/dist/pagination/page-query-options.js +2 -0
  233. package/dist/pagination/page-query-options.js.map +1 -0
  234. package/dist/pagination/page-query-schema.d.ts +6 -0
  235. package/dist/pagination/page-query-schema.d.ts.map +1 -0
  236. package/dist/pagination/page-query-schema.js +8 -0
  237. package/dist/pagination/page-query-schema.js.map +1 -0
  238. package/dist/pagination/page-request-from.d.ts +9 -0
  239. package/dist/pagination/page-request-from.d.ts.map +1 -0
  240. package/dist/pagination/page-request-from.js +16 -0
  241. package/dist/pagination/page-request-from.js.map +1 -0
  242. package/dist/pagination/page-request-parameters.d.ts +9 -0
  243. package/dist/pagination/page-request-parameters.d.ts.map +1 -0
  244. package/dist/pagination/page-request-parameters.js +30 -0
  245. package/dist/pagination/page-request-parameters.js.map +1 -0
  246. package/dist/pagination/page-response.d.ts +5 -0
  247. package/dist/pagination/page-response.d.ts.map +1 -0
  248. package/dist/pagination/page-response.js +9 -0
  249. package/dist/pagination/page-response.js.map +1 -0
  250. package/dist/pagination/page-schema-of.d.ts +9 -0
  251. package/dist/pagination/page-schema-of.d.ts.map +1 -0
  252. package/dist/pagination/page-schema-of.js +60 -0
  253. package/dist/pagination/page-schema-of.js.map +1 -0
  254. package/dist/pagination/parse-page-query.d.ts +5 -0
  255. package/dist/pagination/parse-page-query.d.ts.map +1 -0
  256. package/dist/pagination/parse-page-query.js +108 -0
  257. package/dist/pagination/parse-page-query.js.map +1 -0
  258. package/dist/pipeline/assert-declared-response.d.ts +4 -0
  259. package/dist/pipeline/assert-declared-response.d.ts.map +1 -0
  260. package/dist/pipeline/assert-declared-response.js +18 -0
  261. package/dist/pipeline/assert-declared-response.js.map +1 -0
  262. package/dist/pipeline/correlation-header.d.ts +2 -0
  263. package/dist/pipeline/correlation-header.d.ts.map +1 -0
  264. package/dist/pipeline/correlation-header.js +2 -0
  265. package/dist/pipeline/correlation-header.js.map +1 -0
  266. package/dist/pipeline/correlation-id-of.d.ts +4 -0
  267. package/dist/pipeline/correlation-id-of.d.ts.map +1 -0
  268. package/dist/pipeline/correlation-id-of.js +7 -0
  269. package/dist/pipeline/correlation-id-of.js.map +1 -0
  270. package/dist/pipeline/error-context.d.ts +15 -0
  271. package/dist/pipeline/error-context.d.ts.map +1 -0
  272. package/dist/pipeline/error-context.js +2 -0
  273. package/dist/pipeline/error-context.js.map +1 -0
  274. package/dist/pipeline/execute-options.d.ts +40 -0
  275. package/dist/pipeline/execute-options.d.ts.map +1 -0
  276. package/dist/pipeline/execute-options.js +2 -0
  277. package/dist/pipeline/execute-options.js.map +1 -0
  278. package/dist/pipeline/execute-route.d.ts +6 -0
  279. package/dist/pipeline/execute-route.d.ts.map +1 -0
  280. package/dist/pipeline/execute-route.js +108 -0
  281. package/dist/pipeline/execute-route.js.map +1 -0
  282. package/dist/pipeline/execution-of-request.d.ts +16 -0
  283. package/dist/pipeline/execution-of-request.d.ts.map +1 -0
  284. package/dist/pipeline/execution-of-request.js +44 -0
  285. package/dist/pipeline/execution-of-request.js.map +1 -0
  286. package/dist/pipeline/raw-request.d.ts +10 -0
  287. package/dist/pipeline/raw-request.d.ts.map +1 -0
  288. package/dist/pipeline/raw-request.js +2 -0
  289. package/dist/pipeline/raw-request.js.map +1 -0
  290. package/dist/pipeline/report-http-errors.d.ts +11 -0
  291. package/dist/pipeline/report-http-errors.d.ts.map +1 -0
  292. package/dist/pipeline/report-http-errors.js +17 -0
  293. package/dist/pipeline/report-http-errors.js.map +1 -0
  294. package/dist/pipeline/route-around.d.ts +5 -0
  295. package/dist/pipeline/route-around.d.ts.map +1 -0
  296. package/dist/pipeline/route-around.js +2 -0
  297. package/dist/pipeline/route-around.js.map +1 -0
  298. package/dist/pipeline/route-response.d.ts +7 -0
  299. package/dist/pipeline/route-response.d.ts.map +1 -0
  300. package/dist/pipeline/route-response.js +2 -0
  301. package/dist/pipeline/route-response.js.map +1 -0
  302. package/dist/pipeline/serialize-body.d.ts +7 -0
  303. package/dist/pipeline/serialize-body.d.ts.map +1 -0
  304. package/dist/pipeline/serialize-body.js +16 -0
  305. package/dist/pipeline/serialize-body.js.map +1 -0
  306. package/dist/pipeline/validate-request.d.ts +8 -0
  307. package/dist/pipeline/validate-request.d.ts.map +1 -0
  308. package/dist/pipeline/validate-request.js +30 -0
  309. package/dist/pipeline/validate-request.js.map +1 -0
  310. package/dist/pipeline/well-formed-identifier.d.ts +3 -0
  311. package/dist/pipeline/well-formed-identifier.d.ts.map +1 -0
  312. package/dist/pipeline/well-formed-identifier.js +6 -0
  313. package/dist/pipeline/well-formed-identifier.js.map +1 -0
  314. package/dist/route/assert-unique-routes.d.ts +3 -0
  315. package/dist/route/assert-unique-routes.d.ts.map +1 -0
  316. package/dist/route/assert-unique-routes.js +22 -0
  317. package/dist/route/assert-unique-routes.js.map +1 -0
  318. package/dist/route/define-route.d.ts +7 -0
  319. package/dist/route/define-route.d.ts.map +1 -0
  320. package/dist/route/define-route.js +17 -0
  321. package/dist/route/define-route.js.map +1 -0
  322. package/dist/route/define-routes.d.ts +4 -0
  323. package/dist/route/define-routes.d.ts.map +1 -0
  324. package/dist/route/define-routes.js +11 -0
  325. package/dist/route/define-routes.js.map +1 -0
  326. package/dist/route/duplicate-route-error.d.ts +4 -0
  327. package/dist/route/duplicate-route-error.d.ts.map +1 -0
  328. package/dist/route/duplicate-route-error.js +7 -0
  329. package/dist/route/duplicate-route-error.js.map +1 -0
  330. package/dist/route/handler-input.d.ts +23 -0
  331. package/dist/route/handler-input.d.ts.map +1 -0
  332. package/dist/route/handler-input.js +2 -0
  333. package/dist/route/handler-input.js.map +1 -0
  334. package/dist/route/handler-result.d.ts +20 -0
  335. package/dist/route/handler-result.d.ts.map +1 -0
  336. package/dist/route/handler-result.js +2 -0
  337. package/dist/route/handler-result.js.map +1 -0
  338. package/dist/route/http-method.d.ts +2 -0
  339. package/dist/route/http-method.d.ts.map +1 -0
  340. package/dist/route/http-method.js +2 -0
  341. package/dist/route/http-method.js.map +1 -0
  342. package/dist/route/prefix-routes.d.ts +4 -0
  343. package/dist/route/prefix-routes.d.ts.map +1 -0
  344. package/dist/route/prefix-routes.js +10 -0
  345. package/dist/route/prefix-routes.js.map +1 -0
  346. package/dist/route/raw-headers.d.ts +2 -0
  347. package/dist/route/raw-headers.d.ts.map +1 -0
  348. package/dist/route/raw-headers.js +2 -0
  349. package/dist/route/raw-headers.js.map +1 -0
  350. package/dist/route/request-context.d.ts +13 -0
  351. package/dist/route/request-context.d.ts.map +1 -0
  352. package/dist/route/request-context.js +2 -0
  353. package/dist/route/request-context.js.map +1 -0
  354. package/dist/route/request-schemas.d.ts +8 -0
  355. package/dist/route/request-schemas.d.ts.map +1 -0
  356. package/dist/route/request-schemas.js +2 -0
  357. package/dist/route/request-schemas.js.map +1 -0
  358. package/dist/route/response-spec.d.ts +6 -0
  359. package/dist/route/response-spec.d.ts.map +1 -0
  360. package/dist/route/response-spec.js +2 -0
  361. package/dist/route/response-spec.js.map +1 -0
  362. package/dist/route/responses-spec.d.ts +5 -0
  363. package/dist/route/responses-spec.d.ts.map +1 -0
  364. package/dist/route/responses-spec.js +2 -0
  365. package/dist/route/responses-spec.js.map +1 -0
  366. package/dist/route/route-handler.d.ts +6 -0
  367. package/dist/route/route-handler.d.ts.map +1 -0
  368. package/dist/route/route-handler.js +2 -0
  369. package/dist/route/route-handler.js.map +1 -0
  370. package/dist/route/route-input.d.ts +10 -0
  371. package/dist/route/route-input.d.ts.map +1 -0
  372. package/dist/route/route-input.js +2 -0
  373. package/dist/route/route-input.js.map +1 -0
  374. package/dist/route/route-output.d.ts +6 -0
  375. package/dist/route/route-output.d.ts.map +1 -0
  376. package/dist/route/route-output.js +2 -0
  377. package/dist/route/route-output.js.map +1 -0
  378. package/dist/route/route-spec.d.ts +23 -0
  379. package/dist/route/route-spec.d.ts.map +1 -0
  380. package/dist/route/route-spec.js +2 -0
  381. package/dist/route/route-spec.js.map +1 -0
  382. package/dist/route/route.d.ts +11 -0
  383. package/dist/route/route.d.ts.map +1 -0
  384. package/dist/route/route.js +2 -0
  385. package/dist/route/route.js.map +1 -0
  386. package/dist/route/without-trailing-slashes.d.ts +3 -0
  387. package/dist/route/without-trailing-slashes.d.ts.map +1 -0
  388. package/dist/route/without-trailing-slashes.js +9 -0
  389. package/dist/route/without-trailing-slashes.js.map +1 -0
  390. package/dist/support/is-record.d.ts +2 -0
  391. package/dist/support/is-record.d.ts.map +1 -0
  392. package/dist/support/is-record.js +4 -0
  393. package/dist/support/is-record.js.map +1 -0
  394. package/package.json +82 -4
package/README.md CHANGED
@@ -1,3 +1,389 @@
1
- # Temporary Holding Version
1
+ # @ddd-ts-kit/http
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Declare a route once, without a framework. Mount it on Express 5, Fastify 5 or NestJS, and
4
+ generate the OpenAPI 3.1 contract from the same declaration.
5
+
6
+ Every adapter goes through one pipeline (`executeRoute`): validation, handler, error mapping.
7
+ The behaviour is therefore the same on the three frameworks, and one test suite proves it.
8
+
9
+ ## Declare
10
+
11
+ ```ts
12
+ import { z } from 'zod';
13
+ import { defineRoute, defineRoutes } from '@ddd-ts-kit/http';
14
+ import { zodSchema } from '@ddd-ts-kit/validation/zod';
15
+
16
+ const order = zodSchema(z.object({ id: z.string(), total: z.number() }), { io: 'output' });
17
+
18
+ export const orderRoutes = (orders: OrderQueries) =>
19
+ defineRoutes(
20
+ defineRoute(
21
+ {
22
+ method: 'GET',
23
+ path: '/orders/:orderId',
24
+ operationId: 'getOrder',
25
+ summary: 'Read an order',
26
+ tags: ['orders'],
27
+ request: { params: zodSchema(z.object({ orderId: z.string().uuid() })) },
28
+ responses: { 200: { description: 'The order.', body: order } },
29
+ errors: ['NOT_FOUND'],
30
+ security: ['bearer'],
31
+ },
32
+ async ({ params, context }) => ({
33
+ status: 200, // must be a declared status, and the body must match its schema
34
+ body: await orders.byId(params.orderId, context.principal),
35
+ }),
36
+ ),
37
+ );
38
+ ```
39
+
40
+ - `params`, `query`, `headers` and `body` are typed from the schemas. Any `Schema` of
41
+ `@ddd-ts-kit/validation` works (zod, class-validator, Standard Schema).
42
+ - Paths are literal segments and `:name` parameters, the form Express and Fastify share.
43
+ - Frameworks hand query values over as strings: coerce in the schema (`z.coerce.number()`).
44
+ - `context` is `{ requestId, execution, principal?, raw }`. `execution` is the kernel
45
+ `ExecutionContext` of the request (see "The execution context of a request"); `raw` is the
46
+ framework's request object.
47
+ - `security` is required: the names of the security schemes that apply, or `false` for a route
48
+ anybody may call. A route is never public by omission: leaving it out does not compile, and
49
+ `defineRoute` refuses an empty list, which would say the same as `false` without saying it.
50
+ - `defineRoutes` and every mount refuse a duplicate `operationId` or method and path.
51
+ - `prefixRoutes('/api', routes)` moves routes under a prefix, for the server and the contract.
52
+
53
+ ## Errors
54
+
55
+ A handler throws a `DomainError`; its `code` is mapped to a status in one place and answered as
56
+ `application/problem+json` (RFC 9457):
57
+
58
+ ```json
59
+ { "type": "urn:problem:not-found", "title": "Not Found", "status": 404, "code": "NOT_FOUND",
60
+ "detail": "Order 7 does not exist.", "requestId": "..." }
61
+ ```
62
+
63
+ Defaults: `VALIDATION_FAILED` 422 (with `issues`, paths prefixed by the part:
64
+ `body.items.0.sku`), `NOT_FOUND` 404, `CONFLICT` 409, `INVARIANT_VIOLATION` 422, `UNAUTHORIZED`
65
+ 401, `FORBIDDEN` 403. A domain code that is not mapped answers 422.
66
+
67
+ ```ts
68
+ import { createErrorMapper } from '@ddd-ts-kit/http';
69
+
70
+ const errorMapper = createErrorMapper({ ORDER_ALREADY_SHIPPED: 409, QUOTA_EXCEEDED: 429 });
71
+ ```
72
+
73
+ Anything that is not a `DomainError` answers 500 `Internal Server Error` with no detail: the
74
+ message of an unexpected error never leaves the process. Log it from the `onError` hook, and
75
+ report it to the error tracker with `onError: reportHttpErrors(errorReporter)`: every request
76
+ answered 5xx goes to the kernel `ErrorReporter`, once, with the correlation, actor and tenant
77
+ ids of its execution context (`context.execution`, present once the request got that far); a
78
+ 4xx never does.
79
+
80
+ ## Options shared by the adapters
81
+
82
+ ```ts
83
+ import type { ExecuteOptions } from '@ddd-ts-kit/http';
84
+
85
+ const options: ExecuteOptions = {
86
+ errorMapper,
87
+ resolvePrincipal: (raw) => authenticate(raw), // throw UnauthorizedError to refuse
88
+ onError: (error, { requestId, correlationId, operationId, problem }) => {
89
+ if (problem.status >= 500) {
90
+ logger.error({ requestId, correlationId, operationId, error });
91
+ }
92
+ },
93
+ validateResponses: process.env.NODE_ENV === 'test', // checks answers against the declaration
94
+ around: tracedRoutes(), // of @ddd-ts-kit/tracing: the span of the request, named by its route template
95
+ };
96
+ ```
97
+
98
+ `around(route, respond)` wraps the whole handling of a request, from authentication to the
99
+ answer, refusals included, and is told the route that was matched (`method`, `path` as
100
+ declared, `operationId`). It is where a tracer names a request `POST /orders/:id/lines` instead
101
+ of by its URL; this package depends on no tracing library.
102
+
103
+ The request id is the incoming `x-request-id` when well formed, else a UUID; it is echoed in
104
+ the `x-request-id` response header and in every problem.
105
+
106
+ ## The execution context of a request
107
+
108
+ `executeRoute` builds a kernel `ExecutionContext` for every request and hands it to the handler
109
+ as `context.execution`:
110
+
111
+ | Field | Value |
112
+ |---|---|
113
+ | `correlationId` | the incoming `x-correlation-id` when well formed (letters, digits, `._:-`, at most 128), else the request id; echoed in the `x-correlation-id` response header, on problems too, and given to `onError` |
114
+ | `origin` | `http` |
115
+ | `actor`, `session` | from what `resolvePrincipal` answered, read structurally: a `Session` gives both, an `Actor` gives the actor, anything else is the anonymous actor |
116
+ | `tenantId` | the tenant of the actor |
117
+ | `locale` | the locale of the session, else the preferred language of `Accept-Language` |
118
+ | `deadline` | arrival plus `requestTimeoutMs`, when the option is set |
119
+
120
+ ```ts
121
+ import { Clock, ExecutionScope } from '@ddd-ts-kit/kernel';
122
+
123
+ const options: ExecuteOptions = {
124
+ resolvePrincipal: principalResolver(authenticator), // of @ddd-ts-kit/auth: answers a Session
125
+ executionScope: app.resolve(ExecutionScope), // the scope the application runs its use cases in
126
+ clock: app.resolve(Clock),
127
+ requestTimeoutMs: 10_000,
128
+ };
129
+ ```
130
+
131
+ With `executionScope`, every handler runs inside `scope.run(context.execution, ...)`: loggers,
132
+ audit columns, tenant scoping and outgoing headers read the context of the request with no
133
+ wrapper around the handler, and no route can forget it. `withPrincipal` stays, for a scope of
134
+ your own; it runs inside the execution scope. The pipeline does not interrupt a handler at its
135
+ deadline: a timeout wrapper or an outgoing call reads `deadline` and honours it. `clock` is the
136
+ option that says when a request arrived, the system clock by default.
137
+
138
+ Use cases still receive the actor explicitly: `new PlaceOrder(context.execution.actor, body)`,
139
+ or `actorOf(context.principal)` of `@ddd-ts-kit/auth`, which reads the same actor.
140
+
141
+ ## In the composition root
142
+
143
+ The package binds and resolves nothing: routes are a plain list and `ExecuteOptions` a plain
144
+ object, both built where the server starts. What it takes from the application are ports,
145
+ resolved by their class:
146
+
147
+ ```ts
148
+ import { createApplication, defineContextModule, kernelModule } from '@ddd-ts-kit/di';
149
+ import { ErrorMapper, createErrorMapper, type ExecuteOptions } from '@ddd-ts-kit/http';
150
+ import { Clock, ExecutionScope } from '@ddd-ts-kit/kernel';
151
+
152
+ const httpModule = defineContextModule({
153
+ name: 'http',
154
+ register(builder) {
155
+ builder.bind(ErrorMapper).toValue(createErrorMapper({ ORDER_ALREADY_SHIPPED: 409 }));
156
+ },
157
+ });
158
+
159
+ const app = createApplication({ modules: [kernelModule(), httpModule, orderingModule] });
160
+
161
+ const options: ExecuteOptions = {
162
+ errorMapper: app.resolve(ErrorMapper),
163
+ executionScope: app.resolve(ExecutionScope),
164
+ clock: app.resolve(Clock),
165
+ };
166
+ ```
167
+
168
+ | What | Is | Why |
169
+ |---|---|---|
170
+ | `ErrorMapper` | a port: an abstract class, the class is the token | one per application, and the server and the contract generator must be given the same one; binding it is optional, `createErrorMapper(...)` in a constant works as well |
171
+ | `ExecutionScope`, `Clock` | ports of the kernel | resolved from the application and handed over in the options |
172
+ | `resolvePrincipal`, `withPrincipal`, `onError`, `requestId` | functions of `ExecuteOptions` | hooks of one server, written where it starts; the injectable side of authentication is the `Authenticator` of `@ddd-ts-kit/auth`, which `principalResolver` turns into `resolvePrincipal` |
173
+ | the route list | a value, `readonly Route[]` | the result of `defineRoutes(...)`, passed to the mount and to `generateOpenApi` |
174
+ | `Schema` | an interface of `@ddd-ts-kit/validation` | one value per request part, declared in the route |
175
+
176
+ ## Pagination
177
+
178
+ One query vocabulary and one answer for every search, on top of the kernel `PageRequest` and
179
+ `Page`: `limit`, `offset` or `cursor`, and `sort` (`placedAt,-total`: comma-separated fields, a
180
+ leading `-` for descending).
181
+
182
+ ```ts
183
+ import { defineRoute, pageQuerySchema, pageResponse, pageSchemaOf } from '@ddd-ts-kit/http';
184
+
185
+ const searchOrders = defineRoute(
186
+ {
187
+ method: 'GET',
188
+ path: '/orders',
189
+ operationId: 'searchOrders',
190
+ summary: 'Search orders',
191
+ request: { query: pageQuerySchema({ sortable: ['placedAt', 'total'], maxSize: 50 }) },
192
+ responses: { 200: { description: 'A page of orders.', body: pageSchemaOf(order) } },
193
+ security: ['bearer'],
194
+ },
195
+ async ({ query }) => ({
196
+ status: 200, // query is a PageRequest<'placedAt' | 'total'>
197
+ body: pageResponse(await orders.search(query), toOrderDto),
198
+ }),
199
+ );
200
+ ```
201
+
202
+ | Helper | Does |
203
+ |---|---|
204
+ | `pageRequestFrom(query, options?)` | reads the four parameters, as strings or already coerced, into a `PageRequest`; throws the `ValidationError` of the pipeline, so a wrong parameter answers 422 with one issue per parameter (`query.limit`, `query.sort`...). For a route whose query schema also has filters: declare the four parameters in that schema and call it in the handler |
205
+ | `pageQuerySchema(options?)` | the same reading as a `Schema`, for a route whose whole query is the page |
206
+ | `pageResponse(page, map)` | the JSON body `{ items, total?, nextCursor? }`, each item through `map` |
207
+ | `pageRequestParameters(options?)` | the JSON Schema of the query (one OpenAPI parameter per property); `properties` merges into a wider schema |
208
+ | `pageSchemaOf(itemSchema)` | the `Schema` of a page of items: validates the envelope and each item, describes both in the contract |
209
+
210
+ `options` are the `PageLimits` of the kernel (`defaultSize` 20, `maxSize` 100) plus `sortable`,
211
+ the closed list of fields a client may sort by, and `kind`, the variant of a query that names
212
+ neither `offset` nor `cursor` (`offset` by default). Without `sortable`, a `sort` parameter is
213
+ refused: a client never chooses a column the read port did not offer. `offset` and `cursor`
214
+ together are refused. These helpers depend on no validation library; `pageQuerySchema` and
215
+ `pageSchemaOf` are plain `Schema`s and sit next to zod or class-validator schemas.
216
+
217
+ ## Filters and sorting
218
+
219
+ One query grammar for every search route, on top of the kernel `Criteria`: the read port declares
220
+ what it accepts with `defineCriteria`, and the route reads the query into a criteria that
221
+ definition accepts, or answers 422.
222
+
223
+ ```ts
224
+ import { criteriaQuerySchema, defineRoute, pageResponse, pageSchemaOf } from '@ddd-ts-kit/http';
225
+
226
+ const searchAppointments = defineRoute(
227
+ {
228
+ method: 'GET',
229
+ path: '/appointments',
230
+ operationId: 'searchAppointments',
231
+ summary: 'Search appointments',
232
+ request: { query: criteriaQuerySchema(appointmentCriteria) },
233
+ responses: { 200: { description: 'A page of appointments.', body: pageSchemaOf(appointment) } },
234
+ security: ['bearer'],
235
+ },
236
+ async ({ query, context }) => ({
237
+ status: 200, // query is a Criteria<AppointmentRow>
238
+ body: pageResponse(await queries.execute(new SearchAppointments(context.execution.actor, query)), toAppointmentDto),
239
+ }),
240
+ );
241
+ ```
242
+
243
+ ```
244
+ GET /appointments?filter[status]=in:booked,confirmed&filter[startsAt]=gte:2026-10-01T00:00:00Z
245
+ &filter[or][practitionerId]=eq:p-7&filter[or][remote]=true
246
+ &sort=-startsAt,id&limit=20&cursor=eyJzdGFydHNBdCI6...
247
+ ```
248
+
249
+ | Written | Means |
250
+ |---|---|
251
+ | `filter[<field>]=<operator>:<value>` | one condition. The operators are those of the kernel: `eq`, `ne`, `in`, `gt`, `gte`, `lt`, `lte`, `contains`, `startsWith`, `isNull`, `between` |
252
+ | `filter[<field>]=<value>` | `eq`. The text before the first colon is an operator only when it is the name of one: `filter[title]=10:30 review` is an `eq`, and a value that starts like an operator is written `eq:in:the morning` |
253
+ | several `filter[...]` | joined by AND; the same field may be repeated: `filter[seats]=gte:2&filter[seats]=lte:4` |
254
+ | `in:<a>,<b>,<c>` | a list, separated by commas. A comma inside a value is written `\,`, a backslash `\\`; a colon needs nothing |
255
+ | `between:<from>,<to>` | two values, both bounds included |
256
+ | `isNull:true`, `isNull:false`, `isNull` | without a value, or with one; `isNull` alone means `true` |
257
+ | `contains:<text>`, `startsWith:<text>` | the text as it is, commas included; case sensitive |
258
+ | `filter[or][<field>]=...` | the conditions written under `or` are alternatives; under `not`, the rows that satisfy them all are refused; `and` exists for symmetry |
259
+ | `filter[or][0][<field>]=...&filter[or][0][<other>]=...&filter[or][1][<field>]=...` | an index holds several conditions together inside a group: `or(and(...), ...)`. It counts as one more level of depth, which the definition allows or not (`maxDepth`, 1 by default) |
260
+ | `sort=-startsAt,id` | comma-separated or repeated, a leading `-` for descending, among the fields declared sortable and the tie-breaker |
261
+ | `limit`, `offset`, `cursor` | the page, read by the helpers of "Pagination" with the limits of the definition |
262
+
263
+ A value is read by the type its field declares: a number in plain digits (no exponent), `true`
264
+ or `false`, a decimal kept as written, an instant in ISO 8601 with its zone
265
+ (`2026-10-01T08:00:00Z`, `2026-10-01T10:00:00+02:00`) or a date alone, taken at midnight UTC.
266
+ Percent-encoding is the transport's: `+` in a zone is sent `%2B`. `and`, `or`, `not` and
267
+ numbers are reserved names: a field called so cannot be filtered. Both ways a framework hands
268
+ a query are read: the keys as written (`'filter[status]'`, Express 5 and Fastify by default)
269
+ and already nested (`filter: { status }`, a `qs` parser).
270
+
271
+ Anything the definition does not declare is refused with the 422 problem of the pipeline, one
272
+ issue per mistake, none quoting a value:
273
+
274
+ | `path` | `code` |
275
+ |---|---|
276
+ | `query.filter.<field>` | `unknown_field`, `operator_not_allowed`, `invalid_value`, `in_list_too_long`, `invalid_filter` (not written `filter[<field>]=<operator>:<value>`) |
277
+ | `query.filter` | `too_deep`, `empty_group`, `too_many_filters`, `invalid_filter` |
278
+ | `query.sort` | `unsortable_field`, `invalid_sort` |
279
+ | `query.limit`, `query.offset`, `query.cursor` | `invalid_limit`, `invalid_offset`, `invalid_cursor`, `offset_with_cursor` |
280
+
281
+ | Helper | Does |
282
+ |---|---|
283
+ | `criteriaQuerySchema(definition, { kind? })` | the reading as a `Schema`, for a route whose whole query is the criteria |
284
+ | `criteriaFrom(query, definition, { kind? })` | the same reading in a handler, for a route whose query schema holds other parameters too; throws the `ValidationError` of the pipeline |
285
+ | `criteriaParameters(definition)` | the JSON Schema of the query, one OpenAPI parameter per property: `filter[<field>]` for each field with an operator, with the operators in its description and in `x-filter-operators`, its type in `x-filter-type`; `sort` as the list of the values it accepts; `limit`, `offset` and `cursor` |
286
+
287
+ The scope of the actor is not a parameter: the handler of the query joins it with `scoped`,
288
+ and nothing in a query string can remove it.
289
+
290
+ ## Express 5
291
+
292
+ ```ts
293
+ import express from 'express';
294
+ import { mountExpress } from '@ddd-ts-kit/http/express';
295
+
296
+ const app = express();
297
+ app.use(express.json()); // required: the adapter does not parse bodies
298
+ mountExpress(app, orderRoutes(orders), options); // an express.Router() works too
299
+ ```
300
+
301
+ ## Fastify 5
302
+
303
+ ```ts
304
+ import Fastify from 'fastify';
305
+ import { fastifyRoutes, mountFastify } from '@ddd-ts-kit/http/fastify';
306
+
307
+ const app = Fastify();
308
+ mountFastify(app, orderRoutes(orders), options);
309
+ // or, as a plugin:
310
+ await app.register(fastifyRoutes(orderRoutes(orders), options), { prefix: '/api' });
311
+ ```
312
+
313
+ ## NestJS
314
+
315
+ No decorator per route: a module mounts the routes on the underlying HTTP server at module
316
+ init, whether Nest runs on Express or on Fastify. Handlers get their dependencies by closure.
317
+
318
+ ```ts
319
+ import { Module } from '@nestjs/common';
320
+ import { createRoutesModule } from '@ddd-ts-kit/http/nest';
321
+
322
+ @Module({
323
+ imports: [
324
+ createRoutesModule(
325
+ { inject: [OrderQueries], useFactory: (orders: OrderQueries) => orderRoutes(orders) },
326
+ { ...options, imports: [OrdersModule] }, // the modules exporting the injected tokens
327
+ ),
328
+ ],
329
+ })
330
+ export class AppModule {}
331
+ ```
332
+
333
+ A plain array works as well: `createRoutesModule(routes, options)`. These routes bypass Nest's
334
+ pipes, guards, interceptors, exception filters and global prefix: validation and error mapping
335
+ are those described above. Use `prefixRoutes` for a prefix.
336
+
337
+ ## OpenAPI 3.1
338
+
339
+ ```ts
340
+ import { readFileSync, writeFileSync } from 'node:fs';
341
+ import { checkOpenApiDrift, generateOpenApi, stringifyOpenApi } from '@ddd-ts-kit/http/openapi';
342
+
343
+ const document = generateOpenApi({
344
+ info: { title: 'Orders', version: '1.0.0' },
345
+ servers: [{ url: 'https://api.example.com' }],
346
+ routes: orderRoutes(orders),
347
+ securitySchemes: { bearer: { type: 'http', scheme: 'bearer' } },
348
+ errorMapper, // the one the server runs with
349
+ });
350
+
351
+ writeFileSync('openapi.json', stringifyOpenApi(document));
352
+
353
+ // In CI: regenerate and fail on a difference.
354
+ if (checkOpenApiDrift(document, readFileSync('openapi.json', 'utf8')).drift) {
355
+ process.exit(1);
356
+ }
357
+ ```
358
+
359
+ What is generated: `{param}` paths, one parameter per property of the params, query and headers
360
+ schemas, the request body, the declared responses, the `security` of every operation (an empty
361
+ list for a public route), one `application/problem+json` response per
362
+ status of the declared `errors` (codes listed in the description and in `x-error-codes`), 422
363
+ whenever the route validates input, a shared `Problem` schema. Local `$defs` of the schemas are
364
+ moved to `components.schemas`. Paths, statuses and component keys are sorted: the output does
365
+ not depend on the order of the routes, so the committed file diffs cleanly.
366
+
367
+ ## Serving the contract and its page
368
+
369
+ ```ts
370
+ import { openApiDocsRoutes } from '@ddd-ts-kit/http/docs';
371
+
372
+ const routes = [...orderRoutes(orders), ...openApiDocsRoutes(document, { ui: 'scalar' })];
373
+ ```
374
+
375
+ Two public routes (`security: false`), mounted next to the application routes and absent
376
+ from the contract they display:
377
+ the JSON document (`jsonPath`, default `/openapi.json`) and an interactive page (`uiPath`,
378
+ default `/docs`) rendered by `ui`: `swagger` (default), `scalar` or `redoc`. The page loads the
379
+ assets of the UI from a public CDN pinned to a major version; `assetsUrl` points it at
380
+ self-hosted files when it must work offline or under a strict content security policy. A
381
+ project generated by the CLI mounts them in `src/platform/http/with-documentation.ts`, with
382
+ the UI chosen by `--docs-ui`, unless documentation is switched off by its configuration.
383
+
384
+ ## Limits
385
+
386
+ - A body the framework itself refuses (malformed JSON, payload too large) is answered by the
387
+ framework's own error handler, before the pipeline runs.
388
+ - Routes are JSON first. A handler may answer a string or bytes by setting its own
389
+ `content-type` header; streaming is out of scope.
@@ -0,0 +1,8 @@
1
+ import type { FieldType, FieldValue } from '@ddd-ts-kit/kernel';
2
+ /**
3
+ * A value of a query string as the type of its field reads it, `undefined` when it does not:
4
+ * a number in plain digits, `true` or `false`, an ISO 8601 instant with its zone (or a date,
5
+ * taken at midnight UTC), a decimal kept as written.
6
+ */
7
+ export declare function coerceValue(type: FieldType, text: string): FieldValue | undefined;
8
+ //# sourceMappingURL=coerce-value.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coerce-value.d.ts","sourceRoot":"","sources":["../../src/criteria/coerce-value.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAKhE;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAajF"}
@@ -0,0 +1,29 @@
1
+ const NUMBER = /^-?\d+(?:\.\d+)?$/;
2
+ const INSTANT = /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:\d{2}))?$/;
3
+ /**
4
+ * A value of a query string as the type of its field reads it, `undefined` when it does not:
5
+ * a number in plain digits, `true` or `false`, an ISO 8601 instant with its zone (or a date,
6
+ * taken at midnight UTC), a decimal kept as written.
7
+ */
8
+ export function coerceValue(type, text) {
9
+ switch (type) {
10
+ case 'string':
11
+ return text;
12
+ case 'number':
13
+ return NUMBER.test(text) ? Number(text) : undefined;
14
+ case 'decimal':
15
+ return NUMBER.test(text) ? text : undefined;
16
+ case 'boolean':
17
+ return booleanOf(text);
18
+ case 'instant':
19
+ return instantOf(text);
20
+ }
21
+ }
22
+ function booleanOf(text) {
23
+ return text === 'true' || text === 'false' ? text === 'true' : undefined;
24
+ }
25
+ function instantOf(text) {
26
+ const instant = new Date(text);
27
+ return INSTANT.test(text) && !Number.isNaN(instant.getTime()) ? instant : undefined;
28
+ }
29
+ //# sourceMappingURL=coerce-value.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coerce-value.js","sourceRoot":"","sources":["../../src/criteria/coerce-value.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,GAAG,mBAAmB,CAAC;AACnC,MAAM,OAAO,GAAG,oFAAoF,CAAC;AAErG;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,IAAe,EAAE,IAAY;IACvD,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC;QACd,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACtD,KAAK,SAAS;YACZ,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QAC9C,KAAK,SAAS;YACZ,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;QACzB,KAAK,SAAS;YACZ,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3E,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;IAE/B,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACtF,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Criteria, CriteriaDefinition } from '@ddd-ts-kit/kernel';
2
+ import type { CriteriaQueryOptions } from './criteria-query-options.js';
3
+ /**
4
+ * Reads the criteria of a search route from its query: `filter[<field>]=<operator>:<value>`,
5
+ * groups, and the `sort`, `limit`, `offset` and `cursor` of a page. Anything the definition of
6
+ * the read port does not declare throws the `ValidationError` of the pipeline: a 422 whose
7
+ * issues name `query.filter.<field>` and a stable code. For a route whose query schema holds
8
+ * other parameters too; `criteriaQuerySchema` is the same reading as a schema.
9
+ */
10
+ export declare function criteriaFrom<Row>(query: unknown, definition: CriteriaDefinition<Row>, options?: CriteriaQueryOptions): Criteria<Row>;
11
+ //# sourceMappingURL=criteria-from.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"criteria-from.d.ts","sourceRoot":"","sources":["../../src/criteria/criteria-from.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAEvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGxE;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAC9B,KAAK,EAAE,OAAO,EACd,UAAU,EAAE,kBAAkB,CAAC,GAAG,CAAC,EACnC,OAAO,GAAE,oBAAyB,GACjC,QAAQ,CAAC,GAAG,CAAC,CAWf"}
@@ -0,0 +1,17 @@
1
+ import { ValidationError } from '@ddd-ts-kit/validation';
2
+ import { parseCriteriaQuery } from './parse-criteria-query.js';
3
+ /**
4
+ * Reads the criteria of a search route from its query: `filter[<field>]=<operator>:<value>`,
5
+ * groups, and the `sort`, `limit`, `offset` and `cursor` of a page. Anything the definition of
6
+ * the read port does not declare throws the `ValidationError` of the pipeline: a 422 whose
7
+ * issues name `query.filter.<field>` and a stable code. For a route whose query schema holds
8
+ * other parameters too; `criteriaQuerySchema` is the same reading as a schema.
9
+ */
10
+ export function criteriaFrom(query, definition, options = {}) {
11
+ const parsed = parseCriteriaQuery(query, definition, options);
12
+ if (parsed.ok) {
13
+ return parsed.value;
14
+ }
15
+ throw new ValidationError(parsed.error.map((issue) => ({ ...issue, path: `query.${issue.path}` })), 'The request is not valid.');
16
+ }
17
+ //# sourceMappingURL=criteria-from.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"criteria-from.js","sourceRoot":"","sources":["../../src/criteria/criteria-from.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAc,EACd,UAAmC,EACnC,OAAO,GAAyB,EAAE;IAElC,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IAE9D,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;QACd,OAAO,MAAM,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,MAAM,IAAI,eAAe,CACvB,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,SAAS,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EACxE,2BAA2B,CAC5B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { CriteriaDefinition } from '@ddd-ts-kit/kernel';
2
+ import type { JsonSchema } from '@ddd-ts-kit/validation';
3
+ /**
4
+ * The query parameters of a criteria as a JSON Schema object, one OpenAPI parameter per
5
+ * property: a `filter[<field>]` for each field that can be filtered, its operators in the
6
+ * description and in `x-filter-operators`, its type in `x-filter-type`; `sort` as the list of
7
+ * the values it accepts; `limit`, `offset` and `cursor` as `pageRequestParameters` gives them.
8
+ */
9
+ export declare function criteriaParameters<Row>(definition: CriteriaDefinition<Row>): JsonSchema;
10
+ //# sourceMappingURL=criteria-parameters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"criteria-parameters.d.ts","sourceRoot":"","sources":["../../src/criteria/criteria-parameters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAoB,MAAM,oBAAoB,CAAC;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAazD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,UAAU,EAAE,kBAAkB,CAAC,GAAG,CAAC,GAAG,UAAU,CAgBvF"}
@@ -0,0 +1,45 @@
1
+ import { pageRequestParameters } from '../pagination/page-request-parameters.js';
2
+ import { isRecord } from '../support/is-record.js';
3
+ import { sortableFields } from './sortable-fields.js';
4
+ const WRITTEN = {
5
+ string: 'text',
6
+ number: 'a number',
7
+ boolean: '`true` or `false`',
8
+ instant: 'an ISO 8601 instant with its zone',
9
+ decimal: 'a decimal number',
10
+ };
11
+ /**
12
+ * The query parameters of a criteria as a JSON Schema object, one OpenAPI parameter per
13
+ * property: a `filter[<field>]` for each field that can be filtered, its operators in the
14
+ * description and in `x-filter-operators`, its type in `x-filter-type`; `sort` as the list of
15
+ * the values it accepts; `limit`, `offset` and `cursor` as `pageRequestParameters` gives them.
16
+ */
17
+ export function criteriaParameters(definition) {
18
+ const { defaultPageSize, maxPageSize } = definition.limits;
19
+ const page = pageRequestParameters({ defaultSize: defaultPageSize, maxSize: maxPageSize })['properties'];
20
+ const filters = Object.entries(definition.fields).flatMap(([field, declared]) => declared.filter.length === 0 ? [] : [[`filter[${field}]`, filterParameter(field, declared)]]);
21
+ return {
22
+ type: 'object',
23
+ properties: {
24
+ ...Object.fromEntries(filters),
25
+ sort: sortParameter(sortableFields(definition)),
26
+ ...(isRecord(page) ? page : {}),
27
+ },
28
+ };
29
+ }
30
+ function filterParameter(field, { type, filter }) {
31
+ return {
32
+ type: 'string',
33
+ description: `Filters on \`${field}\`, written \`<operator>:<value>\`; the value alone means \`eq\`. Operators: ${filter.join(', ')}. The value is ${WRITTEN[type]}.`,
34
+ 'x-filter-operators': [...filter],
35
+ 'x-filter-type': type,
36
+ };
37
+ }
38
+ function sortParameter(sortable) {
39
+ return {
40
+ type: 'array',
41
+ items: { type: 'string', enum: sortable.flatMap((field) => [field, `-${field}`]) },
42
+ description: 'The fields to sort by, a leading "-" for descending, repeated or separated by commas.',
43
+ };
44
+ }
45
+ //# sourceMappingURL=criteria-parameters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"criteria-parameters.js","sourceRoot":"","sources":["../../src/criteria/criteria-parameters.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,OAAO,GAAuD;IAClE,MAAM,EAAE,MAAM;IACd,MAAM,EAAE,UAAU;IAClB,OAAO,EAAE,mBAAmB;IAC5B,OAAO,EAAE,mCAAmC;IAC5C,OAAO,EAAE,kBAAkB;CAC5B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAM,UAAmC;IACzE,MAAM,EAAE,eAAe,EAAE,WAAW,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC;IAC3D,MAAM,IAAI,GAAY,qBAAqB,CAAC,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC;IAElH,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,EAAE,CAC9E,QAAQ,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,KAAK,GAAG,EAAE,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAC7F,CAAC;IAEF,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC;YAC9B,IAAI,EAAE,aAAa,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;YAC/C,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;SAChC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,KAAa,EAAE,EAAE,IAAI,EAAE,MAAM,EAAoB;IACxE,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,gBAAgB,KAAK,gFAAgF,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,OAAO,CAAC,IAAI,CAAC,GAAG;QACrK,oBAAoB,EAAE,CAAC,GAAG,MAAM,CAAC;QACjC,eAAe,EAAE,IAAI;KACtB,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,QAA2B;IAChD,OAAO;QACL,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,KAAK,EAAE,CAAC,CAAC,EAAE;QAClF,WAAW,EAAE,uFAAuF;KACrG,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { PageRequest } from '@ddd-ts-kit/kernel';
2
+ export interface CriteriaQueryOptions {
3
+ /** The variant of a query that names neither `offset` nor `cursor`. Default `offset`. */
4
+ readonly kind?: PageRequest['kind'];
5
+ }
6
+ //# sourceMappingURL=criteria-query-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"criteria-query-options.d.ts","sourceRoot":"","sources":["../../src/criteria/criteria-query-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACnC,yFAAyF;IACzF,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACrC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=criteria-query-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"criteria-query-options.js","sourceRoot":"","sources":["../../src/criteria/criteria-query-options.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import type { Criteria, CriteriaDefinition } from '@ddd-ts-kit/kernel';
2
+ import { type Schema } from '@ddd-ts-kit/validation';
3
+ import type { CriteriaQueryOptions } from './criteria-query-options.js';
4
+ /**
5
+ * The query of a search route as a `Schema`: `request: { query: criteriaQuerySchema(definition) }`
6
+ * hands the handler a `Criteria` the read port accepts, and documents every filter, the sort
7
+ * and the page in the contract.
8
+ */
9
+ export declare function criteriaQuerySchema<Row>(definition: CriteriaDefinition<Row>, options?: CriteriaQueryOptions): Schema<Criteria<Row>>;
10
+ //# sourceMappingURL=criteria-query-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"criteria-query-schema.d.ts","sourceRoot":"","sources":["../../src/criteria/criteria-query-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEnE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGxE;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EACrC,UAAU,EAAE,kBAAkB,CAAC,GAAG,CAAC,EACnC,OAAO,GAAE,oBAAyB,GACjC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAEvB"}
@@ -0,0 +1,12 @@
1
+ import { customSchema } from '@ddd-ts-kit/validation';
2
+ import { criteriaParameters } from './criteria-parameters.js';
3
+ import { parseCriteriaQuery } from './parse-criteria-query.js';
4
+ /**
5
+ * The query of a search route as a `Schema`: `request: { query: criteriaQuerySchema(definition) }`
6
+ * hands the handler a `Criteria` the read port accepts, and documents every filter, the sort
7
+ * and the page in the contract.
8
+ */
9
+ export function criteriaQuerySchema(definition, options = {}) {
10
+ return customSchema((query) => parseCriteriaQuery(query, definition, options), criteriaParameters(definition));
11
+ }
12
+ //# sourceMappingURL=criteria-query-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"criteria-query-schema.js","sourceRoot":"","sources":["../../src/criteria/criteria-query-schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAe,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,UAAmC,EACnC,OAAO,GAAyB,EAAE;IAElC,OAAO,YAAY,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,kBAAkB,CAAC,UAAU,CAAC,CAAC,CAAC;AACjH,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { FilterEntry } from './filter-entry.js';
2
+ /**
3
+ * The filter parameters of a query, whichever way the framework hands them: the keys as
4
+ * written (`{ 'filter[status]': 'eq:booked' }`, Express 5 and Fastify by default) or already
5
+ * nested (`{ filter: { status: 'eq:booked' } }`, a `qs` parser). A repeated parameter comes as
6
+ * a list of values.
7
+ */
8
+ export declare function filterEntries(query: Readonly<Record<string, unknown>>): FilterEntry[];
9
+ //# sourceMappingURL=filter-entries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-entries.d.ts","sourceRoot":"","sources":["../../src/criteria/filter-entries.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,WAAW,EAAE,CAQrF"}
@@ -0,0 +1,28 @@
1
+ import { isRecord } from '../support/is-record.js';
2
+ const FLAT = /^filter((?:\[[^[\]]*\])+)$/;
3
+ /**
4
+ * The filter parameters of a query, whichever way the framework hands them: the keys as
5
+ * written (`{ 'filter[status]': 'eq:booked' }`, Express 5 and Fastify by default) or already
6
+ * nested (`{ filter: { status: 'eq:booked' } }`, a `qs` parser). A repeated parameter comes as
7
+ * a list of values.
8
+ */
9
+ export function filterEntries(query) {
10
+ const flat = Object.entries(query).flatMap(([key, value]) => {
11
+ const brackets = FLAT.exec(key)?.[1];
12
+ return brackets === undefined ? [] : [{ path: brackets.slice(1, -1).split(']['), values: listed(value) }];
13
+ });
14
+ return query['filter'] === undefined ? flat : [...flat, ...nested(query['filter'], [])];
15
+ }
16
+ function nested(value, path) {
17
+ if (isRecord(value)) {
18
+ return Object.entries(value).flatMap(([key, inner]) => nested(inner, [...path, key]));
19
+ }
20
+ if (Array.isArray(value) && value.some((item) => isRecord(item))) {
21
+ return value.flatMap((item, index) => nested(item, [...path, String(index)]));
22
+ }
23
+ return [{ path, values: listed(value) }];
24
+ }
25
+ function listed(value) {
26
+ return Array.isArray(value) ? value : [value];
27
+ }
28
+ //# sourceMappingURL=filter-entries.js.map