adorn-api 1.0.22 → 1.0.24

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 (380) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc.cjs +30 -0
  3. package/README.md +375 -531
  4. package/dist/core/express-adapter.d.ts +27 -0
  5. package/dist/core/express-adapter.d.ts.map +1 -0
  6. package/dist/core/express-adapter.js +146 -0
  7. package/dist/core/express-adapter.js.map +1 -0
  8. package/dist/core/http-error.d.ts +7 -0
  9. package/dist/core/http-error.d.ts.map +1 -0
  10. package/dist/core/http-error.js +14 -0
  11. package/dist/core/http-error.js.map +1 -0
  12. package/dist/decorators/controller.decorator.d.ts +2 -0
  13. package/dist/decorators/controller.decorator.d.ts.map +1 -0
  14. package/dist/decorators/controller.decorator.js +26 -0
  15. package/dist/decorators/controller.decorator.js.map +1 -0
  16. package/dist/decorators/create.decorator.d.ts +8 -0
  17. package/dist/decorators/create.decorator.d.ts.map +1 -0
  18. package/dist/decorators/create.decorator.js +67 -0
  19. package/dist/decorators/create.decorator.js.map +1 -0
  20. package/dist/decorators/http-method.decorator.d.ts +16 -0
  21. package/dist/decorators/http-method.decorator.d.ts.map +1 -0
  22. package/dist/decorators/http-method.decorator.js +117 -0
  23. package/dist/decorators/http-method.decorator.js.map +1 -0
  24. package/dist/decorators/http-params.d.ts +17 -0
  25. package/dist/decorators/http-params.d.ts.map +1 -0
  26. package/dist/decorators/http-params.js +26 -0
  27. package/dist/decorators/http-params.js.map +1 -0
  28. package/dist/decorators/index.d.ts +10 -5
  29. package/dist/decorators/index.d.ts.map +1 -1
  30. package/dist/decorators/index.js +14 -0
  31. package/dist/decorators/index.js.map +1 -0
  32. package/dist/decorators/list.decorator.d.ts +18 -0
  33. package/dist/decorators/list.decorator.d.ts.map +1 -0
  34. package/dist/decorators/list.decorator.js +99 -0
  35. package/dist/decorators/list.decorator.js.map +1 -0
  36. package/dist/decorators/middleware.decorator.d.ts +4 -0
  37. package/dist/decorators/middleware.decorator.d.ts.map +1 -0
  38. package/dist/decorators/middleware.decorator.js +34 -0
  39. package/dist/decorators/middleware.decorator.js.map +1 -0
  40. package/dist/decorators/response.decorator.d.ts +8 -0
  41. package/dist/decorators/response.decorator.d.ts.map +1 -0
  42. package/dist/decorators/response.decorator.js +44 -0
  43. package/dist/decorators/response.decorator.js.map +1 -0
  44. package/dist/decorators/route-options.d.ts +14 -0
  45. package/dist/decorators/route-options.d.ts.map +1 -0
  46. package/dist/decorators/route-options.js +22 -0
  47. package/dist/decorators/route-options.js.map +1 -0
  48. package/dist/decorators/schema.decorator.d.ts +82 -0
  49. package/dist/decorators/schema.decorator.d.ts.map +1 -0
  50. package/dist/decorators/schema.decorator.js +123 -0
  51. package/dist/decorators/schema.decorator.js.map +1 -0
  52. package/dist/decorators/update.decorator.d.ts +8 -0
  53. package/dist/decorators/update.decorator.d.ts.map +1 -0
  54. package/dist/decorators/update.decorator.js +63 -0
  55. package/dist/decorators/update.decorator.js.map +1 -0
  56. package/dist/index.d.ts +11 -13
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +18 -637
  59. package/dist/index.js.map +1 -1
  60. package/dist/metadata/metadata-storage.d.ts +38 -0
  61. package/dist/metadata/metadata-storage.d.ts.map +1 -0
  62. package/dist/metadata/metadata-storage.js +102 -0
  63. package/dist/metadata/metadata-storage.js.map +1 -0
  64. package/dist/metal-orm-integration/dto-helper.d.ts +5 -0
  65. package/dist/metal-orm-integration/dto-helper.d.ts.map +1 -0
  66. package/dist/metal-orm-integration/dto-helper.js +48 -0
  67. package/dist/metal-orm-integration/dto-helper.js.map +1 -0
  68. package/dist/metal-orm-integration/dto-response.decorator.d.ts +4 -0
  69. package/dist/metal-orm-integration/dto-response.decorator.d.ts.map +1 -0
  70. package/dist/metal-orm-integration/dto-response.decorator.js +69 -0
  71. package/dist/metal-orm-integration/dto-response.decorator.js.map +1 -0
  72. package/dist/metal-orm-integration/entity-schema-builder.d.ts +20 -0
  73. package/dist/metal-orm-integration/entity-schema-builder.d.ts.map +1 -0
  74. package/dist/metal-orm-integration/entity-schema-builder.js +356 -0
  75. package/dist/metal-orm-integration/entity-schema-builder.js.map +1 -0
  76. package/dist/metal-orm-integration/index.d.ts +5 -0
  77. package/dist/metal-orm-integration/index.d.ts.map +1 -0
  78. package/dist/metal-orm-integration/index.js +5 -0
  79. package/dist/metal-orm-integration/index.js.map +1 -0
  80. package/dist/metal-orm-integration/schema-modifier.d.ts +11 -0
  81. package/dist/metal-orm-integration/schema-modifier.d.ts.map +1 -0
  82. package/dist/metal-orm-integration/schema-modifier.js +62 -0
  83. package/dist/metal-orm-integration/schema-modifier.js.map +1 -0
  84. package/dist/openapi/index.d.ts +4 -0
  85. package/dist/openapi/index.d.ts.map +1 -0
  86. package/dist/openapi/index.js +4 -0
  87. package/dist/openapi/index.js.map +1 -0
  88. package/dist/openapi/openapi-generator.d.ts +22 -0
  89. package/dist/openapi/openapi-generator.d.ts.map +1 -0
  90. package/dist/openapi/openapi-generator.js +428 -0
  91. package/dist/openapi/openapi-generator.js.map +1 -0
  92. package/dist/openapi/swagger-ui.d.ts +11 -0
  93. package/dist/openapi/swagger-ui.d.ts.map +1 -0
  94. package/dist/openapi/swagger-ui.js +20 -0
  95. package/dist/openapi/swagger-ui.js.map +1 -0
  96. package/dist/openapi/zod-to-openapi.d.ts +4 -0
  97. package/dist/openapi/zod-to-openapi.d.ts.map +1 -0
  98. package/dist/openapi/zod-to-openapi.js +184 -0
  99. package/dist/openapi/zod-to-openapi.js.map +1 -0
  100. package/dist/types/common.d.ts +4 -0
  101. package/dist/types/common.d.ts.map +1 -0
  102. package/dist/types/common.js +2 -0
  103. package/dist/types/common.js.map +1 -0
  104. package/dist/types/controller.d.ts +14 -0
  105. package/dist/types/controller.d.ts.map +1 -0
  106. package/dist/types/controller.js +2 -0
  107. package/dist/types/controller.js.map +1 -0
  108. package/dist/types/metadata.d.ts +48 -0
  109. package/dist/types/metadata.d.ts.map +1 -0
  110. package/dist/types/metadata.js +2 -0
  111. package/dist/types/metadata.js.map +1 -0
  112. package/dist/types/openapi.d.ts +30 -0
  113. package/dist/types/openapi.d.ts.map +1 -0
  114. package/dist/types/openapi.js +2 -0
  115. package/dist/types/openapi.js.map +1 -0
  116. package/dist/validation/zod-adapter.d.ts +15 -0
  117. package/dist/validation/zod-adapter.d.ts.map +1 -0
  118. package/dist/validation/zod-adapter.js +61 -0
  119. package/dist/validation/zod-adapter.js.map +1 -0
  120. package/examples/basic/app.ts +15 -0
  121. package/examples/basic/index.ts +6 -0
  122. package/examples/basic/user.controller.ts +35 -0
  123. package/examples/basic/user.dtos.ts +23 -0
  124. package/examples/metal-orm-sqlite/app.ts +18 -0
  125. package/examples/metal-orm-sqlite/db.ts +90 -0
  126. package/examples/metal-orm-sqlite/index.ts +6 -0
  127. package/examples/metal-orm-sqlite/post.controller.ts +209 -0
  128. package/examples/metal-orm-sqlite/post.dtos.ts +78 -0
  129. package/examples/metal-orm-sqlite/post.entity.ts +24 -0
  130. package/examples/metal-orm-sqlite/user.controller.helpers.ts +305 -0
  131. package/examples/metal-orm-sqlite/user.controller.ts +231 -0
  132. package/examples/metal-orm-sqlite/user.dtos.ts +88 -0
  133. package/examples/metal-orm-sqlite/user.entity.ts +21 -0
  134. package/examples/metal-orm-sqlite-music/album.controller.ts +278 -0
  135. package/examples/metal-orm-sqlite-music/album.dtos.ts +85 -0
  136. package/examples/metal-orm-sqlite-music/album.entity.ts +28 -0
  137. package/examples/metal-orm-sqlite-music/app.ts +19 -0
  138. package/examples/metal-orm-sqlite-music/artist.controller.ts +272 -0
  139. package/examples/metal-orm-sqlite-music/artist.dtos.ts +68 -0
  140. package/examples/metal-orm-sqlite-music/artist.entity.ts +27 -0
  141. package/examples/metal-orm-sqlite-music/db.ts +148 -0
  142. package/examples/metal-orm-sqlite-music/index.ts +6 -0
  143. package/examples/metal-orm-sqlite-music/track.controller.ts +221 -0
  144. package/examples/metal-orm-sqlite-music/track.dtos.ts +82 -0
  145. package/examples/metal-orm-sqlite-music/track.entity.ts +27 -0
  146. package/examples/openapi/health.controller.ts +11 -0
  147. package/examples/openapi/health.dto.ts +7 -0
  148. package/examples/openapi/index.ts +12 -0
  149. package/examples/restful/app.ts +15 -0
  150. package/examples/restful/index.ts +9 -0
  151. package/examples/restful/task.controller.ts +118 -0
  152. package/examples/restful/task.dtos.ts +66 -0
  153. package/examples/restful/task.store.ts +95 -0
  154. package/examples/tsconfig.json +8 -0
  155. package/examples/utils/start-server.ts +56 -0
  156. package/package.json +33 -97
  157. package/scripts/run-example.js +29 -0
  158. package/src/adapter/express.ts +589 -0
  159. package/src/adapter/metal-orm/convention-overrides.ts +115 -0
  160. package/src/adapter/metal-orm/crud-dtos.ts +141 -0
  161. package/src/adapter/metal-orm/dto.ts +20 -0
  162. package/src/adapter/metal-orm/error-dtos.ts +51 -0
  163. package/src/adapter/metal-orm/field-builder.ts +185 -0
  164. package/src/adapter/metal-orm/filters.ts +52 -0
  165. package/src/adapter/metal-orm/index.ts +66 -0
  166. package/src/adapter/metal-orm/paged-dtos.ts +94 -0
  167. package/src/adapter/metal-orm/pagination.ts +28 -0
  168. package/src/adapter/metal-orm/types.ts +250 -0
  169. package/src/adapter/metal-orm/utils.ts +36 -0
  170. package/src/adapter/metal-orm.test.ts +439 -0
  171. package/src/core/__tests__/coerce.test.ts +39 -0
  172. package/src/core/__tests__/dto-compose.test.ts +68 -0
  173. package/src/core/__tests__/schema-builder.test.ts +82 -0
  174. package/src/core/coerce.ts +190 -0
  175. package/src/core/decorators.ts +645 -0
  176. package/src/core/errors.ts +55 -0
  177. package/src/core/metadata.ts +110 -0
  178. package/src/core/openapi.ts +282 -0
  179. package/src/core/schema-builder.ts +287 -0
  180. package/src/core/schema.ts +400 -0
  181. package/src/core/types.ts +14 -0
  182. package/src/e2e/http-error.e2e.test.ts +52 -0
  183. package/src/e2e/sqlite-metal-orm.e2e.test.ts +174 -0
  184. package/src/e2e/sqlite.e2e.test.ts +126 -0
  185. package/src/index.ts +8 -0
  186. package/tsconfig.eslint.json +7 -0
  187. package/tsconfig.json +18 -0
  188. package/vitest.config.ts +8 -0
  189. package/dist/adapter/express/auth.d.ts +0 -13
  190. package/dist/adapter/express/auth.d.ts.map +0 -1
  191. package/dist/adapter/express/bootstrap.d.ts +0 -40
  192. package/dist/adapter/express/bootstrap.d.ts.map +0 -1
  193. package/dist/adapter/express/coercion.d.ts +0 -102
  194. package/dist/adapter/express/coercion.d.ts.map +0 -1
  195. package/dist/adapter/express/index.d.ts +0 -6
  196. package/dist/adapter/express/index.d.ts.map +0 -1
  197. package/dist/adapter/express/merge.d.ts +0 -45
  198. package/dist/adapter/express/merge.d.ts.map +0 -1
  199. package/dist/adapter/express/openapi.d.ts +0 -66
  200. package/dist/adapter/express/openapi.d.ts.map +0 -1
  201. package/dist/adapter/express/router.d.ts +0 -10
  202. package/dist/adapter/express/router.d.ts.map +0 -1
  203. package/dist/adapter/express/swagger.d.ts +0 -18
  204. package/dist/adapter/express/swagger.d.ts.map +0 -1
  205. package/dist/adapter/express/types.d.ts +0 -110
  206. package/dist/adapter/express/types.d.ts.map +0 -1
  207. package/dist/adapter/express/validation.d.ts +0 -27
  208. package/dist/adapter/express/validation.d.ts.map +0 -1
  209. package/dist/cli/progress.d.ts +0 -122
  210. package/dist/cli/progress.d.ts.map +0 -1
  211. package/dist/cli.cjs +0 -4390
  212. package/dist/cli.cjs.map +0 -1
  213. package/dist/cli.d.ts +0 -3
  214. package/dist/cli.d.ts.map +0 -1
  215. package/dist/cli.js +0 -4371
  216. package/dist/cli.js.map +0 -1
  217. package/dist/compiler/analyze/index.d.ts +0 -5
  218. package/dist/compiler/analyze/index.d.ts.map +0 -1
  219. package/dist/compiler/analyze/scanControllers.d.ts +0 -88
  220. package/dist/compiler/analyze/scanControllers.d.ts.map +0 -1
  221. package/dist/compiler/cache/isStale.d.ts +0 -46
  222. package/dist/compiler/cache/isStale.d.ts.map +0 -1
  223. package/dist/compiler/cache/loadArtifacts.d.ts +0 -149
  224. package/dist/compiler/cache/loadArtifacts.d.ts.map +0 -1
  225. package/dist/compiler/cache/schema.d.ts +0 -32
  226. package/dist/compiler/cache/schema.d.ts.map +0 -1
  227. package/dist/compiler/cache/writeCache.d.ts +0 -14
  228. package/dist/compiler/cache/writeCache.d.ts.map +0 -1
  229. package/dist/compiler/gems.d.ts +0 -75
  230. package/dist/compiler/gems.d.ts.map +0 -1
  231. package/dist/compiler/generator/index.d.ts +0 -7
  232. package/dist/compiler/generator/index.d.ts.map +0 -1
  233. package/dist/compiler/generator/manifest.d.ts +0 -23
  234. package/dist/compiler/generator/manifest.d.ts.map +0 -1
  235. package/dist/compiler/generator/openapi.d.ts +0 -118
  236. package/dist/compiler/generator/openapi.d.ts.map +0 -1
  237. package/dist/compiler/graph/builder.d.ts +0 -24
  238. package/dist/compiler/graph/builder.d.ts.map +0 -1
  239. package/dist/compiler/graph/index.d.ts +0 -7
  240. package/dist/compiler/graph/index.d.ts.map +0 -1
  241. package/dist/compiler/graph/schemaGraph.d.ts +0 -67
  242. package/dist/compiler/graph/schemaGraph.d.ts.map +0 -1
  243. package/dist/compiler/graph/types.d.ts +0 -203
  244. package/dist/compiler/graph/types.d.ts.map +0 -1
  245. package/dist/compiler/index.d.ts +0 -12
  246. package/dist/compiler/index.d.ts.map +0 -1
  247. package/dist/compiler/ir/index.d.ts +0 -7
  248. package/dist/compiler/ir/index.d.ts.map +0 -1
  249. package/dist/compiler/ir/pipeline.d.ts +0 -82
  250. package/dist/compiler/ir/pipeline.d.ts.map +0 -1
  251. package/dist/compiler/ir/stages.d.ts +0 -40
  252. package/dist/compiler/ir/stages.d.ts.map +0 -1
  253. package/dist/compiler/ir/visitor.d.ts +0 -98
  254. package/dist/compiler/ir/visitor.d.ts.map +0 -1
  255. package/dist/compiler/manifest/emit.d.ts +0 -21
  256. package/dist/compiler/manifest/emit.d.ts.map +0 -1
  257. package/dist/compiler/manifest/format.d.ts +0 -119
  258. package/dist/compiler/manifest/format.d.ts.map +0 -1
  259. package/dist/compiler/manifest/index.d.ts +0 -6
  260. package/dist/compiler/manifest/index.d.ts.map +0 -1
  261. package/dist/compiler/runner/createProgram.d.ts +0 -24
  262. package/dist/compiler/runner/createProgram.d.ts.map +0 -1
  263. package/dist/compiler/runner/index.d.ts +0 -5
  264. package/dist/compiler/runner/index.d.ts.map +0 -1
  265. package/dist/compiler/schema/extractAnnotations.d.ts +0 -57
  266. package/dist/compiler/schema/extractAnnotations.d.ts.map +0 -1
  267. package/dist/compiler/schema/index.d.ts +0 -8
  268. package/dist/compiler/schema/index.d.ts.map +0 -1
  269. package/dist/compiler/schema/intersectionHandler.d.ts +0 -44
  270. package/dist/compiler/schema/intersectionHandler.d.ts.map +0 -1
  271. package/dist/compiler/schema/objectHandler.d.ts +0 -146
  272. package/dist/compiler/schema/objectHandler.d.ts.map +0 -1
  273. package/dist/compiler/schema/openapi.d.ts +0 -71
  274. package/dist/compiler/schema/openapi.d.ts.map +0 -1
  275. package/dist/compiler/schema/parameters.d.ts +0 -90
  276. package/dist/compiler/schema/parameters.d.ts.map +0 -1
  277. package/dist/compiler/schema/partitioner.d.ts +0 -85
  278. package/dist/compiler/schema/partitioner.d.ts.map +0 -1
  279. package/dist/compiler/schema/primitives.d.ts +0 -68
  280. package/dist/compiler/schema/primitives.d.ts.map +0 -1
  281. package/dist/compiler/schema/queryBuilderAnalyzer.d.ts +0 -76
  282. package/dist/compiler/schema/queryBuilderAnalyzer.d.ts.map +0 -1
  283. package/dist/compiler/schema/queryBuilderSchemaBuilder.d.ts +0 -13
  284. package/dist/compiler/schema/queryBuilderSchemaBuilder.d.ts.map +0 -1
  285. package/dist/compiler/schema/splitOpenapi.d.ts +0 -46
  286. package/dist/compiler/schema/splitOpenapi.d.ts.map +0 -1
  287. package/dist/compiler/schema/typeToJsonSchema.d.ts +0 -26
  288. package/dist/compiler/schema/typeToJsonSchema.d.ts.map +0 -1
  289. package/dist/compiler/schema/types.d.ts +0 -70
  290. package/dist/compiler/schema/types.d.ts.map +0 -1
  291. package/dist/compiler/schema/unionHandler.d.ts +0 -70
  292. package/dist/compiler/schema/unionHandler.d.ts.map +0 -1
  293. package/dist/compiler/transform/dedup.d.ts +0 -35
  294. package/dist/compiler/transform/dedup.d.ts.map +0 -1
  295. package/dist/compiler/transform/flatten.d.ts +0 -50
  296. package/dist/compiler/transform/flatten.d.ts.map +0 -1
  297. package/dist/compiler/transform/index.d.ts +0 -7
  298. package/dist/compiler/transform/index.d.ts.map +0 -1
  299. package/dist/compiler/transform/inline.d.ts +0 -46
  300. package/dist/compiler/transform/inline.d.ts.map +0 -1
  301. package/dist/compiler/validation/emitPrecompiledValidators.d.ts +0 -62
  302. package/dist/compiler/validation/emitPrecompiledValidators.d.ts.map +0 -1
  303. package/dist/compiler/validation/index.d.ts +0 -5
  304. package/dist/compiler/validation/index.d.ts.map +0 -1
  305. package/dist/decorators/Auth.d.ts +0 -22
  306. package/dist/decorators/Auth.d.ts.map +0 -1
  307. package/dist/decorators/Controller.d.ts +0 -17
  308. package/dist/decorators/Controller.d.ts.map +0 -1
  309. package/dist/decorators/Public.d.ts +0 -15
  310. package/dist/decorators/Public.d.ts.map +0 -1
  311. package/dist/decorators/Use.d.ts +0 -23
  312. package/dist/decorators/Use.d.ts.map +0 -1
  313. package/dist/decorators/methods.d.ts +0 -26
  314. package/dist/decorators/methods.d.ts.map +0 -1
  315. package/dist/express.cjs +0 -1186
  316. package/dist/express.cjs.map +0 -1
  317. package/dist/express.d.ts +0 -8
  318. package/dist/express.d.ts.map +0 -1
  319. package/dist/express.js +0 -1150
  320. package/dist/express.js.map +0 -1
  321. package/dist/http.d.ts +0 -33
  322. package/dist/http.d.ts.map +0 -1
  323. package/dist/index.cjs +0 -724
  324. package/dist/index.cjs.map +0 -1
  325. package/dist/metal/applyListQuery.d.ts +0 -100
  326. package/dist/metal/applyListQuery.d.ts.map +0 -1
  327. package/dist/metal/index.cjs +0 -278
  328. package/dist/metal/index.cjs.map +0 -1
  329. package/dist/metal/index.d.ts +0 -15
  330. package/dist/metal/index.d.ts.map +0 -1
  331. package/dist/metal/index.js +0 -243
  332. package/dist/metal/index.js.map +0 -1
  333. package/dist/metal/listQuery.d.ts +0 -26
  334. package/dist/metal/listQuery.d.ts.map +0 -1
  335. package/dist/metal/queryOptions.d.ts +0 -16
  336. package/dist/metal/queryOptions.d.ts.map +0 -1
  337. package/dist/metal/readMetalBag.d.ts +0 -69
  338. package/dist/metal/readMetalBag.d.ts.map +0 -1
  339. package/dist/metal/registerMetalEntities.d.ts +0 -26
  340. package/dist/metal/registerMetalEntities.d.ts.map +0 -1
  341. package/dist/metal/schemaFromEntity.d.ts +0 -41
  342. package/dist/metal/schemaFromEntity.d.ts.map +0 -1
  343. package/dist/metal/searchWhere.d.ts +0 -97
  344. package/dist/metal/searchWhere.d.ts.map +0 -1
  345. package/dist/metal/symbolMetadata.d.ts +0 -8
  346. package/dist/metal/symbolMetadata.d.ts.map +0 -1
  347. package/dist/runtime/auth/runtime.d.ts +0 -183
  348. package/dist/runtime/auth/runtime.d.ts.map +0 -1
  349. package/dist/runtime/metadata/bucket.d.ts +0 -2
  350. package/dist/runtime/metadata/bucket.d.ts.map +0 -1
  351. package/dist/runtime/metadata/key.d.ts +0 -2
  352. package/dist/runtime/metadata/key.d.ts.map +0 -1
  353. package/dist/runtime/metadata/read.d.ts +0 -2
  354. package/dist/runtime/metadata/read.d.ts.map +0 -1
  355. package/dist/runtime/metadata/types.d.ts +0 -95
  356. package/dist/runtime/metadata/types.d.ts.map +0 -1
  357. package/dist/runtime/polyfill.d.ts +0 -2
  358. package/dist/runtime/polyfill.d.ts.map +0 -1
  359. package/dist/runtime/upload.d.ts +0 -44
  360. package/dist/runtime/upload.d.ts.map +0 -1
  361. package/dist/runtime/validation/ajv.d.ts +0 -120
  362. package/dist/runtime/validation/ajv.d.ts.map +0 -1
  363. package/dist/runtime/validation/index.d.ts +0 -11
  364. package/dist/runtime/validation/index.d.ts.map +0 -1
  365. package/dist/schema/decorators.d.ts +0 -37
  366. package/dist/schema/decorators.d.ts.map +0 -1
  367. package/dist/schema/index.cjs +0 -214
  368. package/dist/schema/index.cjs.map +0 -1
  369. package/dist/schema/index.d.ts +0 -2
  370. package/dist/schema/index.d.ts.map +0 -1
  371. package/dist/schema/index.js +0 -163
  372. package/dist/schema/index.js.map +0 -1
  373. package/dist/scripts/adorn-example.cjs +0 -404
  374. package/dist/scripts/adorn-example.cjs.map +0 -1
  375. package/dist/utils/operationId.d.ts +0 -2
  376. package/dist/utils/operationId.d.ts.map +0 -1
  377. package/dist/utils/path.d.ts +0 -2
  378. package/dist/utils/path.d.ts.map +0 -1
  379. package/dist/utils/port.d.ts +0 -9
  380. package/dist/utils/port.d.ts.map +0 -1
@@ -0,0 +1,231 @@
1
+ import {
2
+ Body,
3
+ Controller,
4
+ Delete,
5
+ Get,
6
+ HttpError,
7
+ Params,
8
+ Patch,
9
+ Post,
10
+ Put,
11
+ Query,
12
+ Returns,
13
+ parsePagination,
14
+ t,
15
+ type RequestContext
16
+ } from "../../src";
17
+ import { applyFilter, toPagedResponse } from "metal-orm";
18
+ import type { SimpleWhereInput } from "metal-orm";
19
+ import { entityRef, selectFromEntity } from "metal-orm";
20
+ import { createSession } from "./db";
21
+ import {
22
+ CreateUserDto,
23
+ ReplaceUserDto,
24
+ UpdateUserDto,
25
+ UserDto,
26
+ UserErrors,
27
+ UserParamsDto,
28
+ UserWithPostsPagedResponseDto,
29
+ UserQueryDto,
30
+ UserQueryDtoClass
31
+ } from "./user.dtos";
32
+ import { CreateUserPostDto, CreateUserPostDtoClass, PostDto } from "./post.dtos";
33
+ import { User } from "./user.entity";
34
+
35
+ type IntegerOptions = {
36
+ min?: number;
37
+ max?: number;
38
+ clamp?: boolean;
39
+ };
40
+
41
+ function parseInteger(value: unknown, options: IntegerOptions = {}): number | undefined {
42
+ if (typeof value !== "number" || !Number.isInteger(value)) {
43
+ return undefined;
44
+ }
45
+ const result = value;
46
+ if (options.min !== undefined && result < options.min) {
47
+ return options.clamp ? options.min : undefined;
48
+ }
49
+ if (options.max !== undefined && result > options.max) {
50
+ return options.clamp ? options.max : undefined;
51
+ }
52
+ return result;
53
+ }
54
+
55
+ function requireUserId(value: unknown): number {
56
+ const id = parseInteger(value, { min: 1 });
57
+ if (id === undefined) {
58
+ throw new HttpError(400, "Invalid user id.");
59
+ }
60
+ return id;
61
+ }
62
+
63
+ const userRef = entityRef(User);
64
+
65
+ type UserFilter = SimpleWhereInput<typeof User, "name" | "email">;
66
+ type OrmSession = ReturnType<typeof createSession>;
67
+
68
+ async function withSession<T>(handler: (session: OrmSession) => Promise<T>) {
69
+ const session = createSession();
70
+ try {
71
+ return await handler(session);
72
+ } finally {
73
+ await session.dispose();
74
+ }
75
+ }
76
+
77
+ async function getUserOrThrow(session: OrmSession, id: number): Promise<User> {
78
+ const user = await session.find(User, id);
79
+ if (!user) {
80
+ throw new HttpError(404, "User not found.");
81
+ }
82
+ return user;
83
+ }
84
+
85
+ function buildUserFilter(query?: UserQueryDto): UserFilter | undefined {
86
+ if (!query) {
87
+ return undefined;
88
+ }
89
+ const filter: UserFilter = {};
90
+ if (query.nameContains) {
91
+ filter.name = { contains: query.nameContains };
92
+ }
93
+ if (query.emailContains) {
94
+ filter.email = { contains: query.emailContains };
95
+ }
96
+ return Object.keys(filter).length ? filter : undefined;
97
+ }
98
+
99
+ @Controller("/users")
100
+ export class UserController {
101
+ @Get("/")
102
+ @Query(UserQueryDtoClass)
103
+ @Returns(UserWithPostsPagedResponseDto)
104
+ async list(ctx: RequestContext<unknown, UserQueryDto>) {
105
+ const paginationQuery = (ctx.query ?? {}) as Record<string, unknown>;
106
+ const { page, pageSize } = parsePagination(paginationQuery);
107
+ return withSession(async (session) => {
108
+ const filters = buildUserFilter(ctx.query);
109
+ const query = applyFilter(
110
+ selectFromEntity(User)
111
+ .orderBy(userRef.id, "ASC")
112
+ .include("posts", {
113
+ columns: ["id", "title", "body", "userId", "createdAt"]
114
+ }),
115
+ User,
116
+ filters
117
+ );
118
+ const paged = await query.executePaged(session, { page, pageSize });
119
+ return toPagedResponse(paged);
120
+ });
121
+ }
122
+
123
+ @Get("/:id")
124
+ @Params(UserParamsDto)
125
+ @Returns(UserDto)
126
+ @UserErrors
127
+ async getOne(ctx: RequestContext<unknown, undefined, UserParamsDto>) {
128
+ const id = requireUserId(ctx.params.id);
129
+ return withSession(async (session) => {
130
+ const user = await getUserOrThrow(session, id);
131
+ return user as UserDto;
132
+ });
133
+ }
134
+
135
+ @Post("/")
136
+ @Body(CreateUserDto)
137
+ @Returns({ status: 201, schema: UserDto })
138
+ async create(ctx: RequestContext<CreateUserDto>) {
139
+ return withSession(async (session) => {
140
+ const user = new User();
141
+ user.name = ctx.body.name;
142
+ user.email = ctx.body.email ?? null;
143
+ user.createdAt = new Date().toISOString();
144
+ await session.persist(user);
145
+ await session.commit();
146
+ return user as UserDto;
147
+ });
148
+ }
149
+
150
+ @Put("/:id")
151
+ @Params(UserParamsDto)
152
+ @Body(ReplaceUserDto)
153
+ @Returns(UserDto)
154
+ @UserErrors
155
+ async replace(ctx: RequestContext<ReplaceUserDto, undefined, UserParamsDto>) {
156
+ const id = requireUserId(ctx.params.id);
157
+ return withSession(async (session) => {
158
+ const entity = await getUserOrThrow(session, id);
159
+ entity.name = ctx.body.name;
160
+ entity.email = ctx.body.email ?? null;
161
+ await session.commit();
162
+ return entity as UserDto;
163
+ });
164
+ }
165
+
166
+ @Patch("/:id")
167
+ @Params(UserParamsDto)
168
+ @Body(UpdateUserDto)
169
+ @Returns(UserDto)
170
+ @UserErrors
171
+ async update(ctx: RequestContext<UpdateUserDto, undefined, UserParamsDto>) {
172
+ const id = requireUserId(ctx.params.id);
173
+ return withSession(async (session) => {
174
+ const entity = await getUserOrThrow(session, id);
175
+ if (ctx.body.name !== undefined) {
176
+ entity.name = ctx.body.name;
177
+ }
178
+ if (ctx.body.email !== undefined) {
179
+ entity.email = ctx.body.email ?? null;
180
+ }
181
+ await session.commit();
182
+ return entity as UserDto;
183
+ });
184
+ }
185
+
186
+ @Get("/:id/posts")
187
+ @Params(UserParamsDto)
188
+ @Returns(t.array(t.ref(PostDto)))
189
+ @UserErrors
190
+ async listPosts(ctx: RequestContext<unknown, undefined, UserParamsDto>) {
191
+ const id = requireUserId(ctx.params.id);
192
+ return withSession(async (session) => {
193
+ const user = await getUserOrThrow(session, id);
194
+ return (await user.posts.load()) as PostDto[];
195
+ });
196
+ }
197
+
198
+ @Post("/:id/posts")
199
+ @Params(UserParamsDto)
200
+ @Body(CreateUserPostDtoClass)
201
+ @Returns({ status: 201, schema: PostDto })
202
+ @UserErrors
203
+ async createPost(
204
+ ctx: RequestContext<CreateUserPostDto, undefined, UserParamsDto>
205
+ ) {
206
+ const id = requireUserId(ctx.params.id);
207
+ return withSession(async (session) => {
208
+ const user = await getUserOrThrow(session, id);
209
+ const post = user.posts.add({
210
+ title: ctx.body.title,
211
+ body: ctx.body.body ?? null,
212
+ createdAt: new Date().toISOString()
213
+ });
214
+ await session.commit();
215
+ return post as PostDto;
216
+ });
217
+ }
218
+
219
+ @Delete("/:id")
220
+ @Params(UserParamsDto)
221
+ @Returns({ status: 204 })
222
+ @UserErrors
223
+ async remove(ctx: RequestContext<unknown, undefined, UserParamsDto>) {
224
+ const id = requireUserId(ctx.params.id);
225
+ return withSession(async (session) => {
226
+ const user = await getUserOrThrow(session, id);
227
+ await session.remove(user);
228
+ await session.commit();
229
+ });
230
+ }
231
+ }
@@ -0,0 +1,88 @@
1
+ import {
2
+ Dto,
3
+ Field,
4
+ MergeDto,
5
+ Errors,
6
+ createMetalCrudDtoClasses,
7
+ createMetalDtoOverrides,
8
+ createPagedResponseDtoClass,
9
+ createPagedFilterQueryDtoClass,
10
+ SimpleErrorDto,
11
+ t
12
+ } from "../../src";
13
+ import { User } from "./user.entity";
14
+ import { PostDto } from "./post.dtos";
15
+
16
+ const userOverrides = createMetalDtoOverrides(User, {
17
+ overrides: {
18
+ email: t.nullable(t.string({ format: "email" }))
19
+ }
20
+ });
21
+
22
+ const userCrud = createMetalCrudDtoClasses(User, {
23
+ overrides: userOverrides,
24
+ response: { description: "User returned by API." },
25
+ mutationExclude: ["id", "createdAt"]
26
+ });
27
+
28
+ export const {
29
+ response: UserDto,
30
+ create: CreateUserDto,
31
+ replace: ReplaceUserDto,
32
+ update: UpdateUserDto,
33
+ params: UserParamsDto
34
+ } = userCrud;
35
+
36
+ export type UserDto = Omit<User, "posts">;
37
+ type UserMutationDto = Omit<UserDto, "id" | "createdAt">;
38
+ export type CreateUserDto = UserMutationDto;
39
+ export type ReplaceUserDto = UserMutationDto;
40
+ export type UpdateUserDto = Partial<UserMutationDto>;
41
+ export type UserParamsDto = InstanceType<typeof UserParamsDto>;
42
+
43
+ export interface UserWithPostsDto extends UserDto {
44
+ posts: PostDto[];
45
+ }
46
+
47
+ @Dto()
48
+ class UserPostsDto {
49
+ @Field(t.array(t.ref(PostDto)))
50
+ posts!: PostDto[];
51
+ }
52
+
53
+ @MergeDto([UserDto, UserPostsDto], {
54
+ description: "User returned by API with posts."
55
+ })
56
+ export class UserWithPostsDto {}
57
+
58
+ export const UserQueryDtoClass = createPagedFilterQueryDtoClass({
59
+ name: "UserQueryDto",
60
+ filters: {
61
+ nameContains: { schema: t.string({ minLength: 1 }), operator: "contains" },
62
+ emailContains: { schema: t.string({ minLength: 1 }), operator: "contains" }
63
+ }
64
+ });
65
+
66
+ export interface UserQueryDto {
67
+ page?: number;
68
+ pageSize?: number;
69
+ nameContains?: string;
70
+ emailContains?: string;
71
+ }
72
+
73
+ export const UserPagedResponseDto = createPagedResponseDtoClass({
74
+ name: "UserPagedResponseDto",
75
+ itemDto: UserDto,
76
+ description: "Paged user list response."
77
+ });
78
+
79
+ export const UserWithPostsPagedResponseDto = createPagedResponseDtoClass({
80
+ name: "UserWithPostsPagedResponseDto",
81
+ itemDto: UserWithPostsDto,
82
+ description: "Paged user list response with posts."
83
+ });
84
+
85
+ export const UserErrors = Errors(SimpleErrorDto, [
86
+ { status: 400, description: "Invalid user id." },
87
+ { status: 404, description: "User not found." }
88
+ ]);
@@ -0,0 +1,21 @@
1
+ import { Column, Entity, HasMany, PrimaryKey, col } from "metal-orm";
2
+ import type { HasManyCollection } from "metal-orm";
3
+ import { Post } from "./post.entity";
4
+
5
+ @Entity({ tableName: "users" })
6
+ export class User {
7
+ @PrimaryKey(col.autoIncrement(col.int()))
8
+ id!: number;
9
+
10
+ @Column(col.notNull(col.text()))
11
+ name!: string;
12
+
13
+ @Column(col.text())
14
+ email?: string | null;
15
+
16
+ @Column(col.notNull(col.text()))
17
+ createdAt!: string;
18
+
19
+ @HasMany({ target: () => Post, foreignKey: "userId" })
20
+ posts!: HasManyCollection<Post>;
21
+ }
@@ -0,0 +1,278 @@
1
+ import {
2
+ Body,
3
+ Controller,
4
+ Delete,
5
+ Get,
6
+ HttpError,
7
+ Params,
8
+ Patch,
9
+ Post,
10
+ Put,
11
+ Query,
12
+ Returns,
13
+ parsePagination,
14
+ type RequestContext
15
+ } from "../../src";
16
+ import { applyFilter, toPagedResponse } from "metal-orm";
17
+ import type { SimpleWhereInput } from "metal-orm";
18
+ import { entityRef, selectFromEntity } from "metal-orm";
19
+ import { createSession } from "./db";
20
+ import {
21
+ AlbumDto,
22
+ AlbumParamsDto,
23
+ AlbumQueryDto,
24
+ AlbumQueryDtoClass,
25
+ AlbumPagedResponseDto,
26
+ AlbumErrors,
27
+ CreateAlbumDto,
28
+ ReplaceAlbumDto,
29
+ UpdateAlbumDto,
30
+ CreateAlbumTrackDto,
31
+ CreateAlbumTrackDtoClass
32
+ } from "./album.dtos";
33
+ import {
34
+ TrackDto,
35
+ TrackQueryDto,
36
+ TrackQueryDtoClass,
37
+ TrackPagedResponseDto
38
+ } from "./track.dtos";
39
+ import { Album as AlbumEntity } from "./album.entity";
40
+ import { Artist } from "./artist.entity";
41
+ import { Track as TrackEntity } from "./track.entity";
42
+
43
+ type IntegerOptions = {
44
+ min?: number;
45
+ max?: number;
46
+ clamp?: boolean;
47
+ };
48
+
49
+ function parseInteger(value: unknown, options: IntegerOptions = {}): number | undefined {
50
+ if (typeof value !== "number" || !Number.isInteger(value)) {
51
+ return undefined;
52
+ }
53
+ const result = value;
54
+ if (options.min !== undefined && result < options.min) {
55
+ return options.clamp ? options.min : undefined;
56
+ }
57
+ if (options.max !== undefined && result > options.max) {
58
+ return options.clamp ? options.max : undefined;
59
+ }
60
+ return result;
61
+ }
62
+
63
+ function requireAlbumId(value: unknown): number {
64
+ const id = parseInteger(value, { min: 1 });
65
+ if (id === undefined) {
66
+ const message = "Invalid album id.";
67
+ throw new HttpError(
68
+ 400,
69
+ message,
70
+ buildErrorBody(message, "INVALID_ALBUM_ID", [{ field: "id", message }])
71
+ );
72
+ }
73
+ return id;
74
+ }
75
+
76
+ const albumRef = entityRef(AlbumEntity);
77
+ const trackRef = entityRef(TrackEntity);
78
+
79
+ type AlbumFilter = SimpleWhereInput<typeof AlbumEntity, "title" | "releaseYear" | "artistId">;
80
+ type AlbumTrackFilter = SimpleWhereInput<typeof TrackEntity, "albumId">;
81
+ type OrmSession = ReturnType<typeof createSession>;
82
+
83
+ type ErrorDetail = { field: string; message: string };
84
+
85
+ function buildErrorBody(message: string, code: string, errors?: ErrorDetail[]) {
86
+ return { message, code, errors };
87
+ }
88
+
89
+ async function withSession<T>(handler: (session: OrmSession) => Promise<T>) {
90
+ const session = createSession();
91
+ try {
92
+ return await handler(session);
93
+ } finally {
94
+ await session.dispose();
95
+ }
96
+ }
97
+
98
+ async function getAlbumOrThrow(session: OrmSession, id: number): Promise<AlbumEntity> {
99
+ const album = await session.find(AlbumEntity, id);
100
+ if (!album) {
101
+ const message = "Album not found.";
102
+ throw new HttpError(404, message, buildErrorBody(message, "ALBUM_NOT_FOUND"));
103
+ }
104
+ return album;
105
+ }
106
+
107
+ async function getArtistOrThrow(session: OrmSession, id: number): Promise<Artist> {
108
+ const artist = await session.find(Artist, id);
109
+ if (!artist) {
110
+ const message = "Artist not found.";
111
+ throw new HttpError(404, message, buildErrorBody(message, "ARTIST_NOT_FOUND"));
112
+ }
113
+ return artist;
114
+ }
115
+
116
+ function buildAlbumFilter(query?: AlbumQueryDto): AlbumFilter | undefined {
117
+ if (!query) {
118
+ return undefined;
119
+ }
120
+ const filter: AlbumFilter = {};
121
+ if (query.titleContains) {
122
+ filter.title = { contains: query.titleContains };
123
+ }
124
+ if (query.releaseYear !== undefined) {
125
+ (filter as any).releaseYear = { equals: query.releaseYear };
126
+ }
127
+ if (query.artistId !== undefined) {
128
+ filter.artistId = { equals: query.artistId };
129
+ }
130
+ return Object.keys(filter).length ? filter : undefined;
131
+ }
132
+
133
+ @Controller("/albums")
134
+ export class AlbumController {
135
+ @Get("/")
136
+ @Query(AlbumQueryDtoClass)
137
+ @Returns(AlbumPagedResponseDto)
138
+ async list(ctx: RequestContext<unknown, AlbumQueryDto>) {
139
+ const paginationQuery = (ctx.query ?? {}) as Record<string, unknown>;
140
+ const { page, pageSize } = parsePagination(paginationQuery);
141
+ return withSession(async (session) => {
142
+ const filters = buildAlbumFilter(ctx.query);
143
+ const query = applyFilter(
144
+ selectFromEntity(AlbumEntity).orderBy(albumRef.id, "ASC"),
145
+ AlbumEntity,
146
+ filters
147
+ );
148
+ const paged = await query.executePaged(session, { page, pageSize });
149
+ return toPagedResponse(paged);
150
+ });
151
+ }
152
+
153
+ @Get("/:id")
154
+ @Params(AlbumParamsDto)
155
+ @Returns(AlbumDto)
156
+ @AlbumErrors
157
+ async getOne(ctx: RequestContext<unknown, undefined, AlbumParamsDto>) {
158
+ const id = requireAlbumId(ctx.params.id);
159
+ return withSession(async (session) => {
160
+ const album = await getAlbumOrThrow(session, id);
161
+ return album as AlbumDto;
162
+ });
163
+ }
164
+
165
+ @Post("/")
166
+ @Body(CreateAlbumDto)
167
+ @Returns({ status: 201, schema: AlbumDto })
168
+ async create(ctx: RequestContext<CreateAlbumDto>) {
169
+ return withSession(async (session) => {
170
+ await getArtistOrThrow(session, ctx.body.artistId);
171
+ const album = new AlbumEntity();
172
+ album.title = ctx.body.title;
173
+ album.releaseYear = ctx.body.releaseYear ?? null;
174
+ album.artistId = ctx.body.artistId;
175
+ album.createdAt = new Date().toISOString();
176
+ await session.persist(album);
177
+ await session.commit();
178
+ return album as AlbumDto;
179
+ });
180
+ }
181
+
182
+ @Put("/:id")
183
+ @Params(AlbumParamsDto)
184
+ @Body(ReplaceAlbumDto)
185
+ @Returns(AlbumDto)
186
+ @AlbumErrors
187
+ async replace(ctx: RequestContext<ReplaceAlbumDto, undefined, AlbumParamsDto>) {
188
+ const id = requireAlbumId(ctx.params.id);
189
+ return withSession(async (session) => {
190
+ const entity = await getAlbumOrThrow(session, id);
191
+ entity.title = ctx.body.title;
192
+ entity.releaseYear = ctx.body.releaseYear ?? null;
193
+ await session.commit();
194
+ return entity as AlbumDto;
195
+ });
196
+ }
197
+
198
+ @Patch("/:id")
199
+ @Params(AlbumParamsDto)
200
+ @Body(UpdateAlbumDto)
201
+ @Returns(AlbumDto)
202
+ @AlbumErrors
203
+ async update(ctx: RequestContext<UpdateAlbumDto, undefined, AlbumParamsDto>) {
204
+ const id = requireAlbumId(ctx.params.id);
205
+ return withSession(async (session) => {
206
+ const entity = await getAlbumOrThrow(session, id);
207
+ if (ctx.body.title !== undefined) {
208
+ entity.title = ctx.body.title;
209
+ }
210
+ if (ctx.body.releaseYear !== undefined) {
211
+ entity.releaseYear = ctx.body.releaseYear ?? null;
212
+ }
213
+ await session.commit();
214
+ return entity as AlbumDto;
215
+ });
216
+ }
217
+
218
+ @Get("/:id/tracks")
219
+ @Params(AlbumParamsDto)
220
+ @Query(TrackQueryDtoClass)
221
+ @Returns(TrackPagedResponseDto)
222
+ @AlbumErrors
223
+ async listTracks(
224
+ ctx: RequestContext<unknown, TrackQueryDto, AlbumParamsDto>
225
+ ) {
226
+ const id = requireAlbumId(ctx.params.id);
227
+ const paginationQuery = (ctx.query ?? {}) as Record<string, unknown>;
228
+ const { page, pageSize } = parsePagination(paginationQuery);
229
+ return withSession(async (session) => {
230
+ await getAlbumOrThrow(session, id);
231
+ const filters: AlbumTrackFilter = {
232
+ albumId: { equals: id }
233
+ };
234
+ const query = applyFilter(
235
+ selectFromEntity(TrackEntity).orderBy(trackRef.id, "ASC"),
236
+ TrackEntity,
237
+ filters
238
+ );
239
+ const paged = await query.executePaged(session, { page, pageSize });
240
+ return toPagedResponse(paged);
241
+ });
242
+ }
243
+
244
+ @Post("/:id/tracks")
245
+ @Params(AlbumParamsDto)
246
+ @Body(CreateAlbumTrackDtoClass)
247
+ @Returns({ status: 201, schema: TrackDto })
248
+ @AlbumErrors
249
+ async createTrack(
250
+ ctx: RequestContext<CreateAlbumTrackDto, undefined, AlbumParamsDto>
251
+ ) {
252
+ const id = requireAlbumId(ctx.params.id);
253
+ return withSession(async (session) => {
254
+ const album = await getAlbumOrThrow(session, id);
255
+ const track = album.tracks.add({
256
+ title: ctx.body.title,
257
+ durationSeconds: ctx.body.durationSeconds ?? null,
258
+ trackNumber: ctx.body.trackNumber ?? null,
259
+ createdAt: new Date().toISOString()
260
+ });
261
+ await session.commit();
262
+ return track as TrackDto;
263
+ });
264
+ }
265
+
266
+ @Delete("/:id")
267
+ @Params(AlbumParamsDto)
268
+ @Returns({ status: 204 })
269
+ @AlbumErrors
270
+ async remove(ctx: RequestContext<unknown, undefined, AlbumParamsDto>) {
271
+ const id = requireAlbumId(ctx.params.id);
272
+ return withSession(async (session) => {
273
+ const album = await getAlbumOrThrow(session, id);
274
+ await session.remove(album);
275
+ await session.commit();
276
+ });
277
+ }
278
+ }
@@ -0,0 +1,85 @@
1
+ import {
2
+ Errors,
3
+ createMetalCrudDtoClasses,
4
+ createMetalDtoOverrides,
5
+ createPagedResponseDtoClass,
6
+ createNestedCreateDtoClass,
7
+ createPagedFilterQueryDtoClass,
8
+ StandardErrorDto,
9
+ t
10
+ } from "../../src";
11
+ import { Album } from "./album.entity";
12
+ import { CreateAlbumTrackDto, CreateAlbumTrackDtoClass } from "./track.dtos";
13
+
14
+ const albumOverrides = createMetalDtoOverrides(Album, {
15
+ overrides: {
16
+ releaseYear: t.nullable(t.integer({ minimum: 1900, maximum: 9999 }))
17
+ }
18
+ });
19
+
20
+ const albumCrud = createMetalCrudDtoClasses(Album, {
21
+ overrides: albumOverrides,
22
+ response: { description: "Album returned by API." },
23
+ mutationExclude: ["id", "createdAt"],
24
+ immutable: ["artistId"]
25
+ });
26
+
27
+ export const {
28
+ response: AlbumDto,
29
+ create: CreateAlbumDto,
30
+ replace: ReplaceAlbumDto,
31
+ update: UpdateAlbumDto,
32
+ params: AlbumParamsDto
33
+ } = albumCrud;
34
+
35
+ export type AlbumDto = Omit<Album, "artist" | "tracks">;
36
+ type AlbumMutationDto = Omit<AlbumDto, "id" | "createdAt">;
37
+ type AlbumUpdateDto = Omit<AlbumMutationDto, "artistId">;
38
+ export type CreateAlbumDto = AlbumMutationDto;
39
+ export type ReplaceAlbumDto = AlbumUpdateDto;
40
+ export type UpdateAlbumDto = Partial<AlbumUpdateDto>;
41
+ export type AlbumParamsDto = InstanceType<typeof AlbumParamsDto>;
42
+
43
+ export const CreateArtistAlbumDtoClass = createNestedCreateDtoClass(
44
+ Album,
45
+ albumOverrides,
46
+ {
47
+ name: "CreateArtistAlbumDto",
48
+ additionalExclude: ["artistId"]
49
+ }
50
+ );
51
+
52
+ export interface CreateArtistAlbumDto {
53
+ title: string;
54
+ releaseYear?: number | null;
55
+ }
56
+
57
+ export const AlbumQueryDtoClass = createPagedFilterQueryDtoClass({
58
+ name: "AlbumQueryDto",
59
+ filters: {
60
+ titleContains: { schema: t.string({ minLength: 1 }), operator: "contains" },
61
+ releaseYear: { schema: t.integer({ minimum: 1900, maximum: 9999 }), operator: "equals" },
62
+ artistId: { schema: t.integer({ minimum: 1 }), operator: "equals" }
63
+ }
64
+ });
65
+
66
+ export interface AlbumQueryDto {
67
+ page?: number;
68
+ pageSize?: number;
69
+ titleContains?: string;
70
+ releaseYear?: number;
71
+ artistId?: number;
72
+ }
73
+
74
+ export const AlbumPagedResponseDto = createPagedResponseDtoClass({
75
+ name: "AlbumPagedResponseDto",
76
+ itemDto: AlbumDto,
77
+ description: "Paged album list response."
78
+ });
79
+
80
+ export const AlbumErrors = Errors(StandardErrorDto, [
81
+ { status: 400, description: "Invalid album id." },
82
+ { status: 404, description: "Album not found." }
83
+ ]);
84
+
85
+ export { CreateAlbumTrackDto, CreateAlbumTrackDtoClass };