adorn-api 1.0.23 → 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.
- package/.eslintignore +3 -0
- package/.eslintrc.cjs +30 -0
- package/README.md +375 -531
- package/dist/core/express-adapter.d.ts +27 -0
- package/dist/core/express-adapter.d.ts.map +1 -0
- package/dist/core/express-adapter.js +146 -0
- package/dist/core/express-adapter.js.map +1 -0
- package/dist/core/http-error.d.ts +7 -0
- package/dist/core/http-error.d.ts.map +1 -0
- package/dist/core/http-error.js +14 -0
- package/dist/core/http-error.js.map +1 -0
- package/dist/decorators/controller.decorator.d.ts +2 -0
- package/dist/decorators/controller.decorator.d.ts.map +1 -0
- package/dist/decorators/controller.decorator.js +26 -0
- package/dist/decorators/controller.decorator.js.map +1 -0
- package/dist/decorators/create.decorator.d.ts +8 -0
- package/dist/decorators/create.decorator.d.ts.map +1 -0
- package/dist/decorators/create.decorator.js +67 -0
- package/dist/decorators/create.decorator.js.map +1 -0
- package/dist/decorators/http-method.decorator.d.ts +16 -0
- package/dist/decorators/http-method.decorator.d.ts.map +1 -0
- package/dist/decorators/http-method.decorator.js +117 -0
- package/dist/decorators/http-method.decorator.js.map +1 -0
- package/dist/decorators/http-params.d.ts +17 -0
- package/dist/decorators/http-params.d.ts.map +1 -0
- package/dist/decorators/http-params.js +26 -0
- package/dist/decorators/http-params.js.map +1 -0
- package/dist/decorators/index.d.ts +10 -5
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js +14 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/decorators/list.decorator.d.ts +18 -0
- package/dist/decorators/list.decorator.d.ts.map +1 -0
- package/dist/decorators/list.decorator.js +99 -0
- package/dist/decorators/list.decorator.js.map +1 -0
- package/dist/decorators/middleware.decorator.d.ts +4 -0
- package/dist/decorators/middleware.decorator.d.ts.map +1 -0
- package/dist/decorators/middleware.decorator.js +34 -0
- package/dist/decorators/middleware.decorator.js.map +1 -0
- package/dist/decorators/response.decorator.d.ts +8 -0
- package/dist/decorators/response.decorator.d.ts.map +1 -0
- package/dist/decorators/response.decorator.js +44 -0
- package/dist/decorators/response.decorator.js.map +1 -0
- package/dist/decorators/route-options.d.ts +14 -0
- package/dist/decorators/route-options.d.ts.map +1 -0
- package/dist/decorators/route-options.js +22 -0
- package/dist/decorators/route-options.js.map +1 -0
- package/dist/decorators/schema.decorator.d.ts +82 -0
- package/dist/decorators/schema.decorator.d.ts.map +1 -0
- package/dist/decorators/schema.decorator.js +123 -0
- package/dist/decorators/schema.decorator.js.map +1 -0
- package/dist/decorators/update.decorator.d.ts +8 -0
- package/dist/decorators/update.decorator.d.ts.map +1 -0
- package/dist/decorators/update.decorator.js +63 -0
- package/dist/decorators/update.decorator.js.map +1 -0
- package/dist/index.d.ts +11 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -637
- package/dist/index.js.map +1 -1
- package/dist/metadata/metadata-storage.d.ts +38 -0
- package/dist/metadata/metadata-storage.d.ts.map +1 -0
- package/dist/metadata/metadata-storage.js +102 -0
- package/dist/metadata/metadata-storage.js.map +1 -0
- package/dist/metal-orm-integration/dto-helper.d.ts +5 -0
- package/dist/metal-orm-integration/dto-helper.d.ts.map +1 -0
- package/dist/metal-orm-integration/dto-helper.js +48 -0
- package/dist/metal-orm-integration/dto-helper.js.map +1 -0
- package/dist/metal-orm-integration/dto-response.decorator.d.ts +4 -0
- package/dist/metal-orm-integration/dto-response.decorator.d.ts.map +1 -0
- package/dist/metal-orm-integration/dto-response.decorator.js +69 -0
- package/dist/metal-orm-integration/dto-response.decorator.js.map +1 -0
- package/dist/metal-orm-integration/entity-schema-builder.d.ts +20 -0
- package/dist/metal-orm-integration/entity-schema-builder.d.ts.map +1 -0
- package/dist/metal-orm-integration/entity-schema-builder.js +356 -0
- package/dist/metal-orm-integration/entity-schema-builder.js.map +1 -0
- package/dist/metal-orm-integration/index.d.ts +5 -0
- package/dist/metal-orm-integration/index.d.ts.map +1 -0
- package/dist/metal-orm-integration/index.js +5 -0
- package/dist/metal-orm-integration/index.js.map +1 -0
- package/dist/metal-orm-integration/schema-modifier.d.ts +11 -0
- package/dist/metal-orm-integration/schema-modifier.d.ts.map +1 -0
- package/dist/metal-orm-integration/schema-modifier.js +62 -0
- package/dist/metal-orm-integration/schema-modifier.js.map +1 -0
- package/dist/openapi/index.d.ts +4 -0
- package/dist/openapi/index.d.ts.map +1 -0
- package/dist/openapi/index.js +4 -0
- package/dist/openapi/index.js.map +1 -0
- package/dist/openapi/openapi-generator.d.ts +22 -0
- package/dist/openapi/openapi-generator.d.ts.map +1 -0
- package/dist/openapi/openapi-generator.js +428 -0
- package/dist/openapi/openapi-generator.js.map +1 -0
- package/dist/openapi/swagger-ui.d.ts +11 -0
- package/dist/openapi/swagger-ui.d.ts.map +1 -0
- package/dist/openapi/swagger-ui.js +20 -0
- package/dist/openapi/swagger-ui.js.map +1 -0
- package/dist/openapi/zod-to-openapi.d.ts +4 -0
- package/dist/openapi/zod-to-openapi.d.ts.map +1 -0
- package/dist/openapi/zod-to-openapi.js +184 -0
- package/dist/openapi/zod-to-openapi.js.map +1 -0
- package/dist/types/common.d.ts +4 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/common.js +2 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/controller.d.ts +14 -0
- package/dist/types/controller.d.ts.map +1 -0
- package/dist/types/controller.js +2 -0
- package/dist/types/controller.js.map +1 -0
- package/dist/types/metadata.d.ts +48 -0
- package/dist/types/metadata.d.ts.map +1 -0
- package/dist/types/metadata.js +2 -0
- package/dist/types/metadata.js.map +1 -0
- package/dist/types/openapi.d.ts +30 -0
- package/dist/types/openapi.d.ts.map +1 -0
- package/dist/types/openapi.js +2 -0
- package/dist/types/openapi.js.map +1 -0
- package/dist/validation/zod-adapter.d.ts +15 -0
- package/dist/validation/zod-adapter.d.ts.map +1 -0
- package/dist/validation/zod-adapter.js +61 -0
- package/dist/validation/zod-adapter.js.map +1 -0
- package/examples/basic/app.ts +15 -0
- package/examples/basic/index.ts +6 -0
- package/examples/basic/user.controller.ts +35 -0
- package/examples/basic/user.dtos.ts +23 -0
- package/examples/metal-orm-sqlite/app.ts +18 -0
- package/examples/metal-orm-sqlite/db.ts +90 -0
- package/examples/metal-orm-sqlite/index.ts +6 -0
- package/examples/metal-orm-sqlite/post.controller.ts +209 -0
- package/examples/metal-orm-sqlite/post.dtos.ts +78 -0
- package/examples/metal-orm-sqlite/post.entity.ts +24 -0
- package/examples/metal-orm-sqlite/user.controller.helpers.ts +305 -0
- package/examples/metal-orm-sqlite/user.controller.ts +231 -0
- package/examples/metal-orm-sqlite/user.dtos.ts +88 -0
- package/examples/metal-orm-sqlite/user.entity.ts +21 -0
- package/examples/metal-orm-sqlite-music/album.controller.ts +278 -0
- package/examples/metal-orm-sqlite-music/album.dtos.ts +85 -0
- package/examples/metal-orm-sqlite-music/album.entity.ts +28 -0
- package/examples/metal-orm-sqlite-music/app.ts +19 -0
- package/examples/metal-orm-sqlite-music/artist.controller.ts +272 -0
- package/examples/metal-orm-sqlite-music/artist.dtos.ts +68 -0
- package/examples/metal-orm-sqlite-music/artist.entity.ts +27 -0
- package/examples/metal-orm-sqlite-music/db.ts +148 -0
- package/examples/metal-orm-sqlite-music/index.ts +6 -0
- package/examples/metal-orm-sqlite-music/track.controller.ts +221 -0
- package/examples/metal-orm-sqlite-music/track.dtos.ts +82 -0
- package/examples/metal-orm-sqlite-music/track.entity.ts +27 -0
- package/examples/openapi/health.controller.ts +11 -0
- package/examples/openapi/health.dto.ts +7 -0
- package/examples/openapi/index.ts +12 -0
- package/examples/restful/app.ts +15 -0
- package/examples/restful/index.ts +9 -0
- package/examples/restful/task.controller.ts +118 -0
- package/examples/restful/task.dtos.ts +66 -0
- package/examples/restful/task.store.ts +95 -0
- package/examples/tsconfig.json +8 -0
- package/examples/utils/start-server.ts +56 -0
- package/package.json +33 -97
- package/scripts/run-example.js +29 -0
- package/src/adapter/express.ts +589 -0
- package/src/adapter/metal-orm/convention-overrides.ts +115 -0
- package/src/adapter/metal-orm/crud-dtos.ts +141 -0
- package/src/adapter/metal-orm/dto.ts +20 -0
- package/src/adapter/metal-orm/error-dtos.ts +51 -0
- package/src/adapter/metal-orm/field-builder.ts +185 -0
- package/src/adapter/metal-orm/filters.ts +52 -0
- package/src/adapter/metal-orm/index.ts +66 -0
- package/src/adapter/metal-orm/paged-dtos.ts +94 -0
- package/src/adapter/metal-orm/pagination.ts +28 -0
- package/src/adapter/metal-orm/types.ts +250 -0
- package/src/adapter/metal-orm/utils.ts +36 -0
- package/src/adapter/metal-orm.test.ts +439 -0
- package/src/core/__tests__/coerce.test.ts +39 -0
- package/src/core/__tests__/dto-compose.test.ts +68 -0
- package/src/core/__tests__/schema-builder.test.ts +82 -0
- package/src/core/coerce.ts +190 -0
- package/src/core/decorators.ts +645 -0
- package/src/core/errors.ts +55 -0
- package/src/core/metadata.ts +110 -0
- package/src/core/openapi.ts +282 -0
- package/src/core/schema-builder.ts +287 -0
- package/src/core/schema.ts +400 -0
- package/src/core/types.ts +14 -0
- package/src/e2e/http-error.e2e.test.ts +52 -0
- package/src/e2e/sqlite-metal-orm.e2e.test.ts +174 -0
- package/src/e2e/sqlite.e2e.test.ts +126 -0
- package/src/index.ts +8 -0
- package/tsconfig.eslint.json +7 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +8 -0
- package/dist/adapter/express/auth.d.ts +0 -13
- package/dist/adapter/express/auth.d.ts.map +0 -1
- package/dist/adapter/express/bootstrap.d.ts +0 -40
- package/dist/adapter/express/bootstrap.d.ts.map +0 -1
- package/dist/adapter/express/coercion.d.ts +0 -102
- package/dist/adapter/express/coercion.d.ts.map +0 -1
- package/dist/adapter/express/index.d.ts +0 -6
- package/dist/adapter/express/index.d.ts.map +0 -1
- package/dist/adapter/express/merge.d.ts +0 -45
- package/dist/adapter/express/merge.d.ts.map +0 -1
- package/dist/adapter/express/openapi.d.ts +0 -66
- package/dist/adapter/express/openapi.d.ts.map +0 -1
- package/dist/adapter/express/router.d.ts +0 -10
- package/dist/adapter/express/router.d.ts.map +0 -1
- package/dist/adapter/express/swagger.d.ts +0 -18
- package/dist/adapter/express/swagger.d.ts.map +0 -1
- package/dist/adapter/express/types.d.ts +0 -110
- package/dist/adapter/express/types.d.ts.map +0 -1
- package/dist/adapter/express/validation.d.ts +0 -27
- package/dist/adapter/express/validation.d.ts.map +0 -1
- package/dist/cli/progress.d.ts +0 -124
- package/dist/cli/progress.d.ts.map +0 -1
- package/dist/cli.cjs +0 -4622
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -4603
- package/dist/cli.js.map +0 -1
- package/dist/compiler/analyze/index.d.ts +0 -5
- package/dist/compiler/analyze/index.d.ts.map +0 -1
- package/dist/compiler/analyze/scanControllers.d.ts +0 -88
- package/dist/compiler/analyze/scanControllers.d.ts.map +0 -1
- package/dist/compiler/cache/isStale.d.ts +0 -46
- package/dist/compiler/cache/isStale.d.ts.map +0 -1
- package/dist/compiler/cache/loadArtifacts.d.ts +0 -149
- package/dist/compiler/cache/loadArtifacts.d.ts.map +0 -1
- package/dist/compiler/cache/schema.d.ts +0 -32
- package/dist/compiler/cache/schema.d.ts.map +0 -1
- package/dist/compiler/cache/writeCache.d.ts +0 -14
- package/dist/compiler/cache/writeCache.d.ts.map +0 -1
- package/dist/compiler/gems.d.ts +0 -75
- package/dist/compiler/gems.d.ts.map +0 -1
- package/dist/compiler/generator/index.d.ts +0 -7
- package/dist/compiler/generator/index.d.ts.map +0 -1
- package/dist/compiler/generator/manifest.d.ts +0 -23
- package/dist/compiler/generator/manifest.d.ts.map +0 -1
- package/dist/compiler/generator/openapi.d.ts +0 -118
- package/dist/compiler/generator/openapi.d.ts.map +0 -1
- package/dist/compiler/graph/builder.d.ts +0 -24
- package/dist/compiler/graph/builder.d.ts.map +0 -1
- package/dist/compiler/graph/index.d.ts +0 -7
- package/dist/compiler/graph/index.d.ts.map +0 -1
- package/dist/compiler/graph/schemaGraph.d.ts +0 -67
- package/dist/compiler/graph/schemaGraph.d.ts.map +0 -1
- package/dist/compiler/graph/types.d.ts +0 -203
- package/dist/compiler/graph/types.d.ts.map +0 -1
- package/dist/compiler/index.d.ts +0 -12
- package/dist/compiler/index.d.ts.map +0 -1
- package/dist/compiler/ir/index.d.ts +0 -7
- package/dist/compiler/ir/index.d.ts.map +0 -1
- package/dist/compiler/ir/pipeline.d.ts +0 -82
- package/dist/compiler/ir/pipeline.d.ts.map +0 -1
- package/dist/compiler/ir/stages.d.ts +0 -40
- package/dist/compiler/ir/stages.d.ts.map +0 -1
- package/dist/compiler/ir/visitor.d.ts +0 -98
- package/dist/compiler/ir/visitor.d.ts.map +0 -1
- package/dist/compiler/manifest/emit.d.ts +0 -21
- package/dist/compiler/manifest/emit.d.ts.map +0 -1
- package/dist/compiler/manifest/format.d.ts +0 -119
- package/dist/compiler/manifest/format.d.ts.map +0 -1
- package/dist/compiler/manifest/index.d.ts +0 -6
- package/dist/compiler/manifest/index.d.ts.map +0 -1
- package/dist/compiler/runner/createProgram.d.ts +0 -24
- package/dist/compiler/runner/createProgram.d.ts.map +0 -1
- package/dist/compiler/runner/index.d.ts +0 -5
- package/dist/compiler/runner/index.d.ts.map +0 -1
- package/dist/compiler/schema/extractAnnotations.d.ts +0 -57
- package/dist/compiler/schema/extractAnnotations.d.ts.map +0 -1
- package/dist/compiler/schema/index.d.ts +0 -10
- package/dist/compiler/schema/index.d.ts.map +0 -1
- package/dist/compiler/schema/intersectionHandler.d.ts +0 -44
- package/dist/compiler/schema/intersectionHandler.d.ts.map +0 -1
- package/dist/compiler/schema/objectHandler.d.ts +0 -146
- package/dist/compiler/schema/objectHandler.d.ts.map +0 -1
- package/dist/compiler/schema/openapi.d.ts +0 -71
- package/dist/compiler/schema/openapi.d.ts.map +0 -1
- package/dist/compiler/schema/parameters.d.ts +0 -90
- package/dist/compiler/schema/parameters.d.ts.map +0 -1
- package/dist/compiler/schema/partitioner.d.ts +0 -85
- package/dist/compiler/schema/partitioner.d.ts.map +0 -1
- package/dist/compiler/schema/primitives.d.ts +0 -68
- package/dist/compiler/schema/primitives.d.ts.map +0 -1
- package/dist/compiler/schema/queryBuilderAnalyzer.d.ts +0 -95
- package/dist/compiler/schema/queryBuilderAnalyzer.d.ts.map +0 -1
- package/dist/compiler/schema/queryBuilderSchemaBuilder.d.ts +0 -13
- package/dist/compiler/schema/queryBuilderSchemaBuilder.d.ts.map +0 -1
- package/dist/compiler/schema/serviceCallAnalyzer.d.ts +0 -102
- package/dist/compiler/schema/serviceCallAnalyzer.d.ts.map +0 -1
- package/dist/compiler/schema/splitOpenapi.d.ts +0 -46
- package/dist/compiler/schema/splitOpenapi.d.ts.map +0 -1
- package/dist/compiler/schema/typeToJsonSchema.d.ts +0 -26
- package/dist/compiler/schema/typeToJsonSchema.d.ts.map +0 -1
- package/dist/compiler/schema/types.d.ts +0 -70
- package/dist/compiler/schema/types.d.ts.map +0 -1
- package/dist/compiler/schema/unionHandler.d.ts +0 -70
- package/dist/compiler/schema/unionHandler.d.ts.map +0 -1
- package/dist/compiler/transform/dedup.d.ts +0 -35
- package/dist/compiler/transform/dedup.d.ts.map +0 -1
- package/dist/compiler/transform/flatten.d.ts +0 -50
- package/dist/compiler/transform/flatten.d.ts.map +0 -1
- package/dist/compiler/transform/index.d.ts +0 -7
- package/dist/compiler/transform/index.d.ts.map +0 -1
- package/dist/compiler/transform/inline.d.ts +0 -46
- package/dist/compiler/transform/inline.d.ts.map +0 -1
- package/dist/compiler/validation/emitPrecompiledValidators.d.ts +0 -62
- package/dist/compiler/validation/emitPrecompiledValidators.d.ts.map +0 -1
- package/dist/compiler/validation/index.d.ts +0 -5
- package/dist/compiler/validation/index.d.ts.map +0 -1
- package/dist/decorators/Auth.d.ts +0 -22
- package/dist/decorators/Auth.d.ts.map +0 -1
- package/dist/decorators/Controller.d.ts +0 -17
- package/dist/decorators/Controller.d.ts.map +0 -1
- package/dist/decorators/Public.d.ts +0 -15
- package/dist/decorators/Public.d.ts.map +0 -1
- package/dist/decorators/Use.d.ts +0 -23
- package/dist/decorators/Use.d.ts.map +0 -1
- package/dist/decorators/methods.d.ts +0 -26
- package/dist/decorators/methods.d.ts.map +0 -1
- package/dist/express.cjs +0 -1186
- package/dist/express.cjs.map +0 -1
- package/dist/express.d.ts +0 -8
- package/dist/express.d.ts.map +0 -1
- package/dist/express.js +0 -1150
- package/dist/express.js.map +0 -1
- package/dist/http.d.ts +0 -33
- package/dist/http.d.ts.map +0 -1
- package/dist/index.cjs +0 -724
- package/dist/index.cjs.map +0 -1
- package/dist/metal/applyListQuery.d.ts +0 -100
- package/dist/metal/applyListQuery.d.ts.map +0 -1
- package/dist/metal/index.cjs +0 -278
- package/dist/metal/index.cjs.map +0 -1
- package/dist/metal/index.d.ts +0 -15
- package/dist/metal/index.d.ts.map +0 -1
- package/dist/metal/index.js +0 -243
- package/dist/metal/index.js.map +0 -1
- package/dist/metal/listQuery.d.ts +0 -26
- package/dist/metal/listQuery.d.ts.map +0 -1
- package/dist/metal/queryOptions.d.ts +0 -16
- package/dist/metal/queryOptions.d.ts.map +0 -1
- package/dist/metal/readMetalBag.d.ts +0 -69
- package/dist/metal/readMetalBag.d.ts.map +0 -1
- package/dist/metal/registerMetalEntities.d.ts +0 -26
- package/dist/metal/registerMetalEntities.d.ts.map +0 -1
- package/dist/metal/schemaFromEntity.d.ts +0 -41
- package/dist/metal/schemaFromEntity.d.ts.map +0 -1
- package/dist/metal/searchWhere.d.ts +0 -97
- package/dist/metal/searchWhere.d.ts.map +0 -1
- package/dist/metal/symbolMetadata.d.ts +0 -8
- package/dist/metal/symbolMetadata.d.ts.map +0 -1
- package/dist/runtime/auth/runtime.d.ts +0 -183
- package/dist/runtime/auth/runtime.d.ts.map +0 -1
- package/dist/runtime/metadata/bucket.d.ts +0 -2
- package/dist/runtime/metadata/bucket.d.ts.map +0 -1
- package/dist/runtime/metadata/key.d.ts +0 -2
- package/dist/runtime/metadata/key.d.ts.map +0 -1
- package/dist/runtime/metadata/read.d.ts +0 -2
- package/dist/runtime/metadata/read.d.ts.map +0 -1
- package/dist/runtime/metadata/types.d.ts +0 -95
- package/dist/runtime/metadata/types.d.ts.map +0 -1
- package/dist/runtime/polyfill.d.ts +0 -2
- package/dist/runtime/polyfill.d.ts.map +0 -1
- package/dist/runtime/upload.d.ts +0 -44
- package/dist/runtime/upload.d.ts.map +0 -1
- package/dist/runtime/validation/ajv.d.ts +0 -120
- package/dist/runtime/validation/ajv.d.ts.map +0 -1
- package/dist/runtime/validation/index.d.ts +0 -11
- package/dist/runtime/validation/index.d.ts.map +0 -1
- package/dist/schema/decorators.d.ts +0 -37
- package/dist/schema/decorators.d.ts.map +0 -1
- package/dist/schema/index.cjs +0 -214
- package/dist/schema/index.cjs.map +0 -1
- package/dist/schema/index.d.ts +0 -2
- package/dist/schema/index.d.ts.map +0 -1
- package/dist/schema/index.js +0 -163
- package/dist/schema/index.js.map +0 -1
- package/dist/scripts/adorn-example.cjs +0 -404
- package/dist/scripts/adorn-example.cjs.map +0 -1
- package/dist/utils/operationId.d.ts +0 -2
- package/dist/utils/operationId.d.ts.map +0 -1
- package/dist/utils/path.d.ts +0 -2
- package/dist/utils/path.d.ts.map +0 -1
- package/dist/utils/port.d.ts +0 -9
- package/dist/utils/port.d.ts.map +0 -1
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
import type { SchemaNode, SchemaSource } from "./schema";
|
|
2
|
+
import type { Constructor, DtoConstructor, HttpMethod } from "./types";
|
|
3
|
+
import {
|
|
4
|
+
getAdornMetadata,
|
|
5
|
+
getDtoMeta,
|
|
6
|
+
registerController,
|
|
7
|
+
registerDto,
|
|
8
|
+
type ControllerMeta,
|
|
9
|
+
type DecoratorMetadata,
|
|
10
|
+
type DtoMeta,
|
|
11
|
+
type FieldMeta,
|
|
12
|
+
type InputMeta,
|
|
13
|
+
type ResponseMeta,
|
|
14
|
+
type RouteMeta,
|
|
15
|
+
type RouteMetaInput
|
|
16
|
+
} from "./metadata";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Options for configuring a Data Transfer Object (DTO).
|
|
20
|
+
*/
|
|
21
|
+
export interface DtoOptions {
|
|
22
|
+
/** Custom name for the DTO */
|
|
23
|
+
name?: string;
|
|
24
|
+
/** Description of the DTO */
|
|
25
|
+
description?: string;
|
|
26
|
+
/** Whether to allow additional properties not defined in the schema */
|
|
27
|
+
additionalProperties?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Options for composing DTOs from existing DTOs.
|
|
32
|
+
* @extends DtoOptions
|
|
33
|
+
*/
|
|
34
|
+
export interface DtoComposeOptions extends DtoOptions {
|
|
35
|
+
/** Field overrides for the composed DTO */
|
|
36
|
+
overrides?: Record<string, FieldOverride>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Options for overriding field definitions.
|
|
41
|
+
*/
|
|
42
|
+
export interface FieldOverrideOptions {
|
|
43
|
+
/** Schema definition for the field */
|
|
44
|
+
schema?: SchemaNode;
|
|
45
|
+
/** Whether the field is optional */
|
|
46
|
+
optional?: boolean;
|
|
47
|
+
/** Description of the field */
|
|
48
|
+
description?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Field override definition - can be either a schema node or override options.
|
|
53
|
+
*/
|
|
54
|
+
export type FieldOverride = SchemaNode | FieldOverrideOptions;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Options for defining a field in a DTO.
|
|
58
|
+
*/
|
|
59
|
+
export interface FieldOptions {
|
|
60
|
+
/** Schema definition for the field */
|
|
61
|
+
schema: SchemaNode;
|
|
62
|
+
/** Whether the field is optional */
|
|
63
|
+
optional?: boolean;
|
|
64
|
+
/** Description of the field */
|
|
65
|
+
description?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Options for configuring a controller.
|
|
70
|
+
*/
|
|
71
|
+
export interface ControllerOptions {
|
|
72
|
+
/** Base path for the controller */
|
|
73
|
+
path?: string;
|
|
74
|
+
/** Tags for OpenAPI documentation */
|
|
75
|
+
tags?: string[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Options for documenting routes.
|
|
80
|
+
*/
|
|
81
|
+
export interface DocOptions {
|
|
82
|
+
/** Summary of the route */
|
|
83
|
+
summary?: string;
|
|
84
|
+
/** Detailed description of the route */
|
|
85
|
+
description?: string;
|
|
86
|
+
/** Tags for OpenAPI documentation */
|
|
87
|
+
tags?: string[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Options for input parameters.
|
|
92
|
+
*/
|
|
93
|
+
export interface InputOptions {
|
|
94
|
+
/** Description of the input */
|
|
95
|
+
description?: string;
|
|
96
|
+
/** Whether the input is required */
|
|
97
|
+
required?: boolean;
|
|
98
|
+
/** Content type for the input */
|
|
99
|
+
contentType?: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Options for return responses.
|
|
104
|
+
*/
|
|
105
|
+
export interface ReturnsOptions {
|
|
106
|
+
/** HTTP status code */
|
|
107
|
+
status?: number;
|
|
108
|
+
/** Schema for the response body */
|
|
109
|
+
schema?: SchemaSource;
|
|
110
|
+
/** Description of the response */
|
|
111
|
+
description?: string;
|
|
112
|
+
/** Content type for the response */
|
|
113
|
+
contentType?: string;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Options for error responses.
|
|
118
|
+
* @extends Omit<ReturnsOptions, "schema" | "status">
|
|
119
|
+
*/
|
|
120
|
+
export interface ErrorResponseOptions
|
|
121
|
+
extends Omit<ReturnsOptions, "schema" | "status"> {
|
|
122
|
+
/** HTTP status code for the error */
|
|
123
|
+
status: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Decorator for defining Data Transfer Objects (DTOs).
|
|
128
|
+
* @param options - Configuration options for the DTO
|
|
129
|
+
* @returns Class decorator function
|
|
130
|
+
*/
|
|
131
|
+
export function Dto(options: DtoOptions = {}) {
|
|
132
|
+
return (value: DtoConstructor, context: ClassDecoratorContext): void => {
|
|
133
|
+
const meta = getAdornMetadata(context.metadata as DecoratorMetadata);
|
|
134
|
+
const fields = meta.dtoFields ?? {};
|
|
135
|
+
const dtoMeta: DtoMeta = {
|
|
136
|
+
name: options.name ?? value.name,
|
|
137
|
+
description: options.description,
|
|
138
|
+
fields,
|
|
139
|
+
additionalProperties: options.additionalProperties
|
|
140
|
+
};
|
|
141
|
+
registerDto(value, dtoMeta);
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Creates a new DTO by picking specific fields from an existing DTO.
|
|
147
|
+
* @param dto - Source DTO to pick fields from
|
|
148
|
+
* @param keys - Array of field names to include
|
|
149
|
+
* @param options - Composition options
|
|
150
|
+
* @returns Class decorator function
|
|
151
|
+
*/
|
|
152
|
+
export function PickDto(
|
|
153
|
+
dto: DtoConstructor,
|
|
154
|
+
keys: string[],
|
|
155
|
+
options: DtoComposeOptions = {}
|
|
156
|
+
) {
|
|
157
|
+
return (value: DtoConstructor, _context: ClassDecoratorContext): void => {
|
|
158
|
+
const dtoMeta = getDtoMetaOrThrow(dto);
|
|
159
|
+
const fields = pickFields(dtoMeta.fields, keys);
|
|
160
|
+
const mergedFields = applyOverrides(fields, options.overrides);
|
|
161
|
+
registerDto(value, buildDerivedMeta(value, dtoMeta, mergedFields, options));
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Creates a new DTO by omitting specific fields from an existing DTO.
|
|
167
|
+
* @param dto - Source DTO to omit fields from
|
|
168
|
+
* @param keys - Array of field names to exclude
|
|
169
|
+
* @param options - Composition options
|
|
170
|
+
* @returns Class decorator function
|
|
171
|
+
*/
|
|
172
|
+
export function OmitDto(
|
|
173
|
+
dto: DtoConstructor,
|
|
174
|
+
keys: string[],
|
|
175
|
+
options: DtoComposeOptions = {}
|
|
176
|
+
) {
|
|
177
|
+
return (value: DtoConstructor, _context: ClassDecoratorContext): void => {
|
|
178
|
+
const dtoMeta = getDtoMetaOrThrow(dto);
|
|
179
|
+
const fields = omitFields(dtoMeta.fields, keys);
|
|
180
|
+
const mergedFields = applyOverrides(fields, options.overrides);
|
|
181
|
+
registerDto(value, buildDerivedMeta(value, dtoMeta, mergedFields, options));
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Creates a new DTO by making all fields optional from an existing DTO.
|
|
187
|
+
* @param dto - Source DTO to make fields optional
|
|
188
|
+
* @param options - Composition options
|
|
189
|
+
* @returns Class decorator function
|
|
190
|
+
*/
|
|
191
|
+
export function PartialDto(dto: DtoConstructor, options: DtoComposeOptions = {}) {
|
|
192
|
+
return (value: DtoConstructor, _context: ClassDecoratorContext): void => {
|
|
193
|
+
const dtoMeta = getDtoMetaOrThrow(dto);
|
|
194
|
+
const fields = makeFieldsPartial(dtoMeta.fields);
|
|
195
|
+
const mergedFields = applyOverrides(fields, options.overrides);
|
|
196
|
+
registerDto(value, buildDerivedMeta(value, dtoMeta, mergedFields, options));
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Creates a new DTO by merging multiple existing DTOs.
|
|
202
|
+
* @param dtos - Array of DTOs to merge
|
|
203
|
+
* @param options - Composition options
|
|
204
|
+
* @returns Class decorator function
|
|
205
|
+
*/
|
|
206
|
+
export function MergeDto(dtos: DtoConstructor[], options: DtoComposeOptions = {}) {
|
|
207
|
+
return (value: DtoConstructor, _context: ClassDecoratorContext): void => {
|
|
208
|
+
if (!dtos.length) {
|
|
209
|
+
throw new Error("MergeDto requires at least one DTO.");
|
|
210
|
+
}
|
|
211
|
+
const metas = dtos.map(getDtoMetaOrThrow);
|
|
212
|
+
const fields = mergeFields(metas.map((meta) => meta.fields));
|
|
213
|
+
const mergedFields = applyOverrides(fields, options.overrides);
|
|
214
|
+
registerDto(value, buildDerivedMeta(value, metas[0], mergedFields, options));
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Decorator for defining fields in a DTO.
|
|
220
|
+
* @param schemaOrOptions - Schema definition or field options
|
|
221
|
+
* @returns Property decorator function
|
|
222
|
+
*/
|
|
223
|
+
export function Field(schemaOrOptions: SchemaNode | FieldOptions) {
|
|
224
|
+
return (_value: unknown, context: ClassFieldDecoratorContext): void => {
|
|
225
|
+
if (typeof context.name !== "string") {
|
|
226
|
+
throw new Error("Field decorator only supports string property keys.");
|
|
227
|
+
}
|
|
228
|
+
const meta = getAdornMetadata(context.metadata as DecoratorMetadata);
|
|
229
|
+
const fields = meta.dtoFields ?? (meta.dtoFields = {});
|
|
230
|
+
fields[context.name] = normalizeField(schemaOrOptions);
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Decorator for defining API controllers.
|
|
236
|
+
* @param pathOrOptions - Base path or controller options
|
|
237
|
+
* @returns Class decorator function
|
|
238
|
+
*/
|
|
239
|
+
export function Controller(pathOrOptions: string | ControllerOptions = {}) {
|
|
240
|
+
return (value: Constructor, context: ClassDecoratorContext): void => {
|
|
241
|
+
const options =
|
|
242
|
+
typeof pathOrOptions === "string" ? { path: pathOrOptions } : pathOrOptions;
|
|
243
|
+
const meta = getAdornMetadata(context.metadata as DecoratorMetadata);
|
|
244
|
+
const routes = meta.routes ?? [];
|
|
245
|
+
const controllerMeta: ControllerMeta = {
|
|
246
|
+
basePath: normalizePath(options.path ?? ""),
|
|
247
|
+
controller: value as Constructor,
|
|
248
|
+
routes: routes.map(finalizeRoute),
|
|
249
|
+
tags: options.tags
|
|
250
|
+
};
|
|
251
|
+
registerController(controllerMeta);
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Decorator for adding documentation to route handlers.
|
|
257
|
+
* @param options - Documentation options
|
|
258
|
+
* @returns Method decorator function
|
|
259
|
+
*/
|
|
260
|
+
export function Doc(options: DocOptions) {
|
|
261
|
+
return (_value: unknown, context: ClassMethodDecoratorContext): void => {
|
|
262
|
+
const route = getRoute(context.metadata as DecoratorMetadata, context.name);
|
|
263
|
+
route.summary = options.summary;
|
|
264
|
+
route.description = options.description;
|
|
265
|
+
route.tags = options.tags;
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Decorator for GET HTTP method routes.
|
|
271
|
+
* @param path - Route path
|
|
272
|
+
* @returns Method decorator function
|
|
273
|
+
*/
|
|
274
|
+
export function Get(path = "") {
|
|
275
|
+
return Route("get", path);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Decorator for POST HTTP method routes.
|
|
280
|
+
* @param path - Route path
|
|
281
|
+
* @returns Method decorator function
|
|
282
|
+
*/
|
|
283
|
+
export function Post(path = "") {
|
|
284
|
+
return Route("post", path);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Decorator for PUT HTTP method routes.
|
|
289
|
+
* @param path - Route path
|
|
290
|
+
* @returns Method decorator function
|
|
291
|
+
*/
|
|
292
|
+
export function Put(path = "") {
|
|
293
|
+
return Route("put", path);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Decorator for PATCH HTTP method routes.
|
|
298
|
+
* @param path - Route path
|
|
299
|
+
* @returns Method decorator function
|
|
300
|
+
*/
|
|
301
|
+
export function Patch(path = "") {
|
|
302
|
+
return Route("patch", path);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Decorator for DELETE HTTP method routes.
|
|
307
|
+
* @param path - Route path
|
|
308
|
+
* @returns Method decorator function
|
|
309
|
+
*/
|
|
310
|
+
export function Delete(path = "") {
|
|
311
|
+
return Route("delete", path);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Decorator for defining request body schema.
|
|
316
|
+
* @param schema - Schema for the request body
|
|
317
|
+
* @param options - Input options
|
|
318
|
+
* @returns Method decorator function
|
|
319
|
+
*/
|
|
320
|
+
export function Body(schema: SchemaSource, options: InputOptions = {}) {
|
|
321
|
+
return (_value: unknown, context: ClassMethodDecoratorContext): void => {
|
|
322
|
+
const route = getRoute(context.metadata as DecoratorMetadata, context.name);
|
|
323
|
+
route.body = buildInputMeta(schema, options);
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Decorator for defining query parameter schema.
|
|
329
|
+
* @param schema - Schema for query parameters
|
|
330
|
+
* @param options - Input options
|
|
331
|
+
* @returns Method decorator function
|
|
332
|
+
*/
|
|
333
|
+
export function Query(schema: SchemaSource, options: InputOptions = {}) {
|
|
334
|
+
return (_value: unknown, context: ClassMethodDecoratorContext): void => {
|
|
335
|
+
const route = getRoute(context.metadata as DecoratorMetadata, context.name);
|
|
336
|
+
route.query = buildInputMeta(schema, options);
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Decorator for defining path parameter schema.
|
|
342
|
+
* @param schema - Schema for path parameters
|
|
343
|
+
* @param options - Input options
|
|
344
|
+
* @returns Method decorator function
|
|
345
|
+
*/
|
|
346
|
+
export function Params(schema: SchemaSource, options: InputOptions = {}) {
|
|
347
|
+
return (_value: unknown, context: ClassMethodDecoratorContext): void => {
|
|
348
|
+
const route = getRoute(context.metadata as DecoratorMetadata, context.name);
|
|
349
|
+
route.params = buildInputMeta(schema, options);
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Decorator for defining request header schema.
|
|
355
|
+
* @param schema - Schema for request headers
|
|
356
|
+
* @param options - Input options
|
|
357
|
+
* @returns Method decorator function
|
|
358
|
+
*/
|
|
359
|
+
export function Headers(schema: SchemaSource, options: InputOptions = {}) {
|
|
360
|
+
return (_value: unknown, context: ClassMethodDecoratorContext): void => {
|
|
361
|
+
const route = getRoute(context.metadata as DecoratorMetadata, context.name);
|
|
362
|
+
route.headers = buildInputMeta(schema, options);
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Decorator for defining successful return responses.
|
|
368
|
+
* @param schemaOrOptions - Response schema or options
|
|
369
|
+
* @param options - Additional response options
|
|
370
|
+
* @returns Method decorator function
|
|
371
|
+
*/
|
|
372
|
+
export function Returns(
|
|
373
|
+
schemaOrOptions: SchemaSource | ReturnsOptions = {},
|
|
374
|
+
options: Omit<ReturnsOptions, "schema"> = {}
|
|
375
|
+
){
|
|
376
|
+
return (_value: unknown, context: ClassMethodDecoratorContext): void => {
|
|
377
|
+
const route = getRoute(context.metadata as DecoratorMetadata, context.name);
|
|
378
|
+
const response = normalizeReturns(schemaOrOptions, options);
|
|
379
|
+
route.responses.push(response);
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Decorator for defining error return responses.
|
|
385
|
+
* @param schemaOrOptions - Error response schema or options
|
|
386
|
+
* @param options - Additional response options
|
|
387
|
+
* @returns Method decorator function
|
|
388
|
+
*/
|
|
389
|
+
export function ReturnsError(
|
|
390
|
+
schemaOrOptions: SchemaSource | ReturnsOptions = {},
|
|
391
|
+
options: Omit<ReturnsOptions, "schema"> = {}
|
|
392
|
+
) {
|
|
393
|
+
return (_value: unknown, context: ClassMethodDecoratorContext): void => {
|
|
394
|
+
const route = getRoute(context.metadata as DecoratorMetadata, context.name);
|
|
395
|
+
const response = normalizeReturns(schemaOrOptions, options);
|
|
396
|
+
response.status = response.status >= 400 ? response.status : 400;
|
|
397
|
+
response.error = true;
|
|
398
|
+
route.responses.push(response);
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Decorator for defining multiple error responses.
|
|
404
|
+
* @param schema - Schema for error responses
|
|
405
|
+
* @param responses - Array of error response options
|
|
406
|
+
* @returns Method decorator function
|
|
407
|
+
*/
|
|
408
|
+
export function Errors(schema: SchemaSource, responses: ErrorResponseOptions[]) {
|
|
409
|
+
return (_value: unknown, context: ClassMethodDecoratorContext): void => {
|
|
410
|
+
if (!responses.length) {
|
|
411
|
+
throw new Error("Errors decorator requires at least one response.");
|
|
412
|
+
}
|
|
413
|
+
const route = getRoute(context.metadata as DecoratorMetadata, context.name);
|
|
414
|
+
for (const response of responses) {
|
|
415
|
+
route.responses.push({
|
|
416
|
+
status: response.status,
|
|
417
|
+
schema,
|
|
418
|
+
description: response.description,
|
|
419
|
+
contentType: response.contentType,
|
|
420
|
+
error: true
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function Route(method: HttpMethod, path: string) {
|
|
427
|
+
return (_value: unknown, context: ClassMethodDecoratorContext): void => {
|
|
428
|
+
const route = getRoute(context.metadata as DecoratorMetadata, context.name);
|
|
429
|
+
route.httpMethod = method;
|
|
430
|
+
route.path = path;
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function normalizeField(schemaOrOptions: SchemaNode | FieldOptions): FieldMeta {
|
|
435
|
+
if (isSchemaNode(schemaOrOptions)) {
|
|
436
|
+
return { schema: schemaOrOptions, optional: schemaOrOptions.optional };
|
|
437
|
+
}
|
|
438
|
+
return {
|
|
439
|
+
schema: schemaOrOptions.schema,
|
|
440
|
+
optional: schemaOrOptions.optional ?? schemaOrOptions.schema.optional,
|
|
441
|
+
description: schemaOrOptions.description
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function buildInputMeta(schema: SchemaSource, options: InputOptions): InputMeta {
|
|
446
|
+
return {
|
|
447
|
+
schema,
|
|
448
|
+
description: options.description,
|
|
449
|
+
required: options.required,
|
|
450
|
+
contentType: options.contentType
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function normalizeReturns(
|
|
455
|
+
schemaOrOptions: SchemaSource | ReturnsOptions,
|
|
456
|
+
options: Omit<ReturnsOptions, "schema">
|
|
457
|
+
): ResponseMeta {
|
|
458
|
+
if (isSchemaNode(schemaOrOptions) || isDtoConstructor(schemaOrOptions)) {
|
|
459
|
+
return {
|
|
460
|
+
status: options.status ?? 200,
|
|
461
|
+
schema: schemaOrOptions,
|
|
462
|
+
description: options.description,
|
|
463
|
+
contentType: options.contentType
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
status: schemaOrOptions.status ?? 200,
|
|
468
|
+
schema: schemaOrOptions.schema,
|
|
469
|
+
description: schemaOrOptions.description,
|
|
470
|
+
contentType: schemaOrOptions.contentType
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function getRoute(metadata: DecoratorMetadata, name: string | symbol): RouteMetaInput {
|
|
475
|
+
const meta = getAdornMetadata(metadata);
|
|
476
|
+
const routes = meta.routes ?? (meta.routes = []);
|
|
477
|
+
let route = routes.find((entry) => entry.handlerName === name);
|
|
478
|
+
if (!route) {
|
|
479
|
+
route = {
|
|
480
|
+
handlerName: name,
|
|
481
|
+
responses: []
|
|
482
|
+
};
|
|
483
|
+
routes.push(route);
|
|
484
|
+
}
|
|
485
|
+
return route;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function finalizeRoute(route: RouteMetaInput): RouteMeta {
|
|
489
|
+
if (!route.httpMethod) {
|
|
490
|
+
throw new Error(`Missing HTTP method decorator on route "${String(route.handlerName)}".`);
|
|
491
|
+
}
|
|
492
|
+
if (route.path === undefined) {
|
|
493
|
+
throw new Error(`Missing path for route "${String(route.handlerName)}".`);
|
|
494
|
+
}
|
|
495
|
+
if (!route.responses.length) {
|
|
496
|
+
route.responses.push({ status: 200, description: "OK" });
|
|
497
|
+
}
|
|
498
|
+
return {
|
|
499
|
+
...route,
|
|
500
|
+
httpMethod: route.httpMethod,
|
|
501
|
+
path: route.path,
|
|
502
|
+
responses: route.responses
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function normalizePath(path: string): string {
|
|
507
|
+
if (!path) {
|
|
508
|
+
return "";
|
|
509
|
+
}
|
|
510
|
+
if (!path.startsWith("/")) {
|
|
511
|
+
return `/${path}`;
|
|
512
|
+
}
|
|
513
|
+
return path;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function isSchemaNode(value: unknown): value is SchemaNode {
|
|
517
|
+
return !!value && typeof value === "object" && "kind" in (value as SchemaNode);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function isDtoConstructor(value: unknown): value is DtoConstructor {
|
|
521
|
+
return typeof value === "function";
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function getDtoMetaOrThrow(dto: DtoConstructor): DtoMeta {
|
|
525
|
+
const dtoMeta = getDtoMeta(dto);
|
|
526
|
+
if (!dtoMeta) {
|
|
527
|
+
throw new Error(`DTO "${dto.name}" is missing @Dto decorator.`);
|
|
528
|
+
}
|
|
529
|
+
return dtoMeta;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function buildDerivedMeta(
|
|
533
|
+
value: Constructor,
|
|
534
|
+
baseMeta: DtoMeta,
|
|
535
|
+
fields: Record<string, FieldMeta>,
|
|
536
|
+
options: DtoComposeOptions
|
|
537
|
+
): DtoMeta {
|
|
538
|
+
const name = options.name ?? value.name;
|
|
539
|
+
return {
|
|
540
|
+
name,
|
|
541
|
+
description: options.description ?? baseMeta.description,
|
|
542
|
+
fields,
|
|
543
|
+
additionalProperties: options.additionalProperties ?? baseMeta.additionalProperties
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function pickFields(fields: Record<string, FieldMeta>, keys: string[]): Record<string, FieldMeta> {
|
|
548
|
+
const output: Record<string, FieldMeta> = {};
|
|
549
|
+
const seen = new Set<string>();
|
|
550
|
+
for (const key of keys) {
|
|
551
|
+
if (seen.has(key)) {
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
seen.add(key);
|
|
555
|
+
const field = fields[key];
|
|
556
|
+
if (!field) {
|
|
557
|
+
throw new Error(`DTO field "${key}" does not exist.`);
|
|
558
|
+
}
|
|
559
|
+
output[key] = cloneField(field);
|
|
560
|
+
}
|
|
561
|
+
return output;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function omitFields(fields: Record<string, FieldMeta>, keys: string[]): Record<string, FieldMeta> {
|
|
565
|
+
const omitSet = new Set(keys);
|
|
566
|
+
for (const key of omitSet) {
|
|
567
|
+
if (!fields[key]) {
|
|
568
|
+
throw new Error(`DTO field "${key}" does not exist.`);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
const output: Record<string, FieldMeta> = {};
|
|
572
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
573
|
+
if (!omitSet.has(name)) {
|
|
574
|
+
output[name] = cloneField(field);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return output;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function makeFieldsPartial(fields: Record<string, FieldMeta>): Record<string, FieldMeta> {
|
|
581
|
+
const output: Record<string, FieldMeta> = {};
|
|
582
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
583
|
+
output[name] = { ...cloneField(field), optional: true };
|
|
584
|
+
}
|
|
585
|
+
return output;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function mergeFields(
|
|
589
|
+
sources: Array<Record<string, FieldMeta>>
|
|
590
|
+
): Record<string, FieldMeta> {
|
|
591
|
+
const output: Record<string, FieldMeta> = {};
|
|
592
|
+
for (const fields of sources) {
|
|
593
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
594
|
+
output[name] = cloneField(field);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
return output;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function applyOverrides(
|
|
601
|
+
fields: Record<string, FieldMeta>,
|
|
602
|
+
overrides: Record<string, FieldOverride> | undefined
|
|
603
|
+
): Record<string, FieldMeta> {
|
|
604
|
+
if (!overrides) {
|
|
605
|
+
return fields;
|
|
606
|
+
}
|
|
607
|
+
for (const [name, override] of Object.entries(overrides)) {
|
|
608
|
+
const field = fields[name];
|
|
609
|
+
if (!field) {
|
|
610
|
+
throw new Error(`DTO field "${name}" does not exist.`);
|
|
611
|
+
}
|
|
612
|
+
fields[name] = normalizeOverride(field, override);
|
|
613
|
+
}
|
|
614
|
+
return fields;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function normalizeOverride(field: FieldMeta, override: FieldOverride): FieldMeta {
|
|
618
|
+
if (isSchemaNode(override)) {
|
|
619
|
+
return {
|
|
620
|
+
schema: override,
|
|
621
|
+
optional:
|
|
622
|
+
override.optional ?? field.optional ?? field.schema.optional,
|
|
623
|
+
description: field.description
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
const schema = override.schema ?? field.schema;
|
|
627
|
+
const optional =
|
|
628
|
+
override.optional ??
|
|
629
|
+
schema.optional ??
|
|
630
|
+
field.optional ??
|
|
631
|
+
field.schema.optional;
|
|
632
|
+
return {
|
|
633
|
+
schema,
|
|
634
|
+
optional,
|
|
635
|
+
description: override.description ?? field.description
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function cloneField(field: FieldMeta): FieldMeta {
|
|
640
|
+
return {
|
|
641
|
+
schema: field.schema,
|
|
642
|
+
optional: field.optional,
|
|
643
|
+
description: field.description
|
|
644
|
+
};
|
|
645
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for creating HTTP errors.
|
|
3
|
+
*/
|
|
4
|
+
export interface HttpErrorOptions {
|
|
5
|
+
/** HTTP status code */
|
|
6
|
+
status: number;
|
|
7
|
+
/** Error message */
|
|
8
|
+
message?: string;
|
|
9
|
+
/** Error response body */
|
|
10
|
+
body?: unknown;
|
|
11
|
+
/** Response headers */
|
|
12
|
+
headers?: Record<string, string>;
|
|
13
|
+
/** Original cause of the error */
|
|
14
|
+
cause?: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* HTTP error class for representing HTTP error responses.
|
|
19
|
+
*/
|
|
20
|
+
export class HttpError extends Error {
|
|
21
|
+
/** HTTP status code */
|
|
22
|
+
status: number;
|
|
23
|
+
/** Error response body */
|
|
24
|
+
body?: unknown;
|
|
25
|
+
/** Response headers */
|
|
26
|
+
headers?: Record<string, string>;
|
|
27
|
+
|
|
28
|
+
constructor(status: number, message?: string, body?: unknown, headers?: Record<string, string>);
|
|
29
|
+
constructor(options: HttpErrorOptions);
|
|
30
|
+
constructor(
|
|
31
|
+
statusOrOptions: number | HttpErrorOptions,
|
|
32
|
+
message?: string,
|
|
33
|
+
body?: unknown,
|
|
34
|
+
headers?: Record<string, string>
|
|
35
|
+
) {
|
|
36
|
+
const normalized =
|
|
37
|
+
typeof statusOrOptions === "number"
|
|
38
|
+
? { status: statusOrOptions, message, body, headers }
|
|
39
|
+
: statusOrOptions;
|
|
40
|
+
super(normalized.message ?? "Request failed.", { cause: normalized.cause });
|
|
41
|
+
this.name = "HttpError";
|
|
42
|
+
this.status = normalized.status;
|
|
43
|
+
this.body = normalized.body;
|
|
44
|
+
this.headers = normalized.headers;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Type guard for checking if a value is an HttpError.
|
|
50
|
+
* @param value - Value to check
|
|
51
|
+
* @returns True if the value is an HttpError
|
|
52
|
+
*/
|
|
53
|
+
export function isHttpError(value: unknown): value is HttpError {
|
|
54
|
+
return value instanceof HttpError;
|
|
55
|
+
}
|