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
package/dist/express.js
DELETED
|
@@ -1,1150 +0,0 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
// src/runtime/polyfill.ts
|
|
9
|
-
Symbol.metadata ??= /* @__PURE__ */ Symbol("Symbol.metadata");
|
|
10
|
-
|
|
11
|
-
// src/adapter/express/router.ts
|
|
12
|
-
import { Router } from "express";
|
|
13
|
-
import { join } from "path";
|
|
14
|
-
|
|
15
|
-
// src/runtime/metadata/key.ts
|
|
16
|
-
var ADORN_META = /* @__PURE__ */ Symbol.for("adorn-api/meta");
|
|
17
|
-
|
|
18
|
-
// src/runtime/metadata/read.ts
|
|
19
|
-
function readAdornBucket(ctor) {
|
|
20
|
-
const metaSym = Symbol.metadata;
|
|
21
|
-
if (!metaSym) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
const classMetadata = ctor[metaSym];
|
|
25
|
-
if (!classMetadata) {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
const bucket = classMetadata[ADORN_META];
|
|
29
|
-
return bucket ?? null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// src/utils/operationId.ts
|
|
33
|
-
function defaultOperationId(controllerName, methodName) {
|
|
34
|
-
return `${controllerName}_${methodName}`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// src/utils/path.ts
|
|
38
|
-
function joinPaths(base, sub) {
|
|
39
|
-
const b = base.endsWith("/") ? base.slice(0, -1) : base;
|
|
40
|
-
const s = sub.startsWith("/") ? sub : `/${sub}`;
|
|
41
|
-
return b + s || "/";
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// src/adapter/express/merge.ts
|
|
45
|
-
import crypto from "crypto";
|
|
46
|
-
var routeCache = /* @__PURE__ */ new Map();
|
|
47
|
-
function convertToExpressPath(path3) {
|
|
48
|
-
return path3.replace(/:([^/]+)/g, ":$1");
|
|
49
|
-
}
|
|
50
|
-
function computeManifestHash(manifest) {
|
|
51
|
-
const ops = manifest.controllers.flatMap((c) => c.operations.map((o) => o.operationId)).sort();
|
|
52
|
-
const data = JSON.stringify({
|
|
53
|
-
ops,
|
|
54
|
-
validation: manifest.validation
|
|
55
|
-
});
|
|
56
|
-
return crypto.createHash("sha256").update(data).digest("hex").slice(0, 16);
|
|
57
|
-
}
|
|
58
|
-
function getControllerUniqueIds(controllers) {
|
|
59
|
-
return controllers.map((c) => `${c.name}:${c.toString()}`);
|
|
60
|
-
}
|
|
61
|
-
function bindRoutes(params) {
|
|
62
|
-
const { controllers, manifest, useCache = true } = params;
|
|
63
|
-
if (useCache) {
|
|
64
|
-
const manifestHash = computeManifestHash(manifest);
|
|
65
|
-
const controllerIds = getControllerUniqueIds(controllers);
|
|
66
|
-
const cacheKey = `${manifestHash}:${controllerIds.join(",")}`;
|
|
67
|
-
const cached = routeCache.get(cacheKey);
|
|
68
|
-
if (cached) {
|
|
69
|
-
return cached.boundRoutes;
|
|
70
|
-
}
|
|
71
|
-
const bound = computeBoundRoutes(controllers, manifest);
|
|
72
|
-
routeCache.set(cacheKey, {
|
|
73
|
-
boundRoutes: bound,
|
|
74
|
-
manifestHash,
|
|
75
|
-
controllerIds
|
|
76
|
-
});
|
|
77
|
-
return bound;
|
|
78
|
-
}
|
|
79
|
-
return computeBoundRoutes(controllers, manifest);
|
|
80
|
-
}
|
|
81
|
-
function computeBoundRoutes(controllers, manifest) {
|
|
82
|
-
const manifestByOpId = /* @__PURE__ */ new Map();
|
|
83
|
-
for (const ctrl of manifest.controllers) {
|
|
84
|
-
for (const op of ctrl.operations) {
|
|
85
|
-
if (manifestByOpId.has(op.operationId)) {
|
|
86
|
-
throw new Error(`Duplicate operationId in manifest: ${op.operationId}`);
|
|
87
|
-
}
|
|
88
|
-
manifestByOpId.set(op.operationId, op);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
const bound = [];
|
|
92
|
-
for (const ctor of controllers) {
|
|
93
|
-
const bucket = readAdornBucket(ctor);
|
|
94
|
-
if (!bucket) {
|
|
95
|
-
console.warn(`No decorator metadata found for ${ctor.name}, skipping`);
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
const basePath = bucket.basePath ?? "/";
|
|
99
|
-
for (const routeOp of bucket.ops) {
|
|
100
|
-
const opId = routeOp.operationId ?? defaultOperationId(ctor.name, routeOp.methodName);
|
|
101
|
-
const manifestOp = manifestByOpId.get(opId);
|
|
102
|
-
if (!manifestOp) {
|
|
103
|
-
throw new Error(
|
|
104
|
-
`No manifest entry for operationId="${opId}. Did you run "adorn-api build"? Are your operationId rules aligned?`
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
if (manifestOp.http.method !== routeOp.httpMethod || manifestOp.http.path !== routeOp.path) {
|
|
108
|
-
throw new Error(
|
|
109
|
-
`Route mismatch for ${opId}. Runtime: ${routeOp.httpMethod} ${routeOp.path}, Manifest: ${manifestOp.http.method} ${manifestOp.http.path}. Rebuild with "adorn-api build".`
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
const expressPath = convertToExpressPath(routeOp.path);
|
|
113
|
-
const fullPath = joinPaths(basePath, expressPath);
|
|
114
|
-
bound.push({
|
|
115
|
-
operationId: opId,
|
|
116
|
-
fullPath,
|
|
117
|
-
httpMethod: manifestOp.http.method,
|
|
118
|
-
controllerCtor: ctor,
|
|
119
|
-
methodName: routeOp.methodName,
|
|
120
|
-
args: manifestOp.args,
|
|
121
|
-
responses: manifestOp.responses,
|
|
122
|
-
use: routeOp.use,
|
|
123
|
-
auth: routeOp.auth,
|
|
124
|
-
controllerUse: bucket.controllerUse
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return bound;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// src/runtime/validation/ajv.ts
|
|
132
|
-
import Ajv from "ajv";
|
|
133
|
-
import addFormats from "ajv-formats";
|
|
134
|
-
var ValidationErrorResponse = class extends Error {
|
|
135
|
-
/**
|
|
136
|
-
* HTTP status code for validation errors.
|
|
137
|
-
*/
|
|
138
|
-
statusCode;
|
|
139
|
-
/**
|
|
140
|
-
* Detailed validation errors.
|
|
141
|
-
*/
|
|
142
|
-
errors;
|
|
143
|
-
/**
|
|
144
|
-
* Creates a new ValidationErrorResponse.
|
|
145
|
-
*
|
|
146
|
-
* @param statusCode - HTTP status code (typically 400)
|
|
147
|
-
* @param errors - Array of validation errors
|
|
148
|
-
*/
|
|
149
|
-
constructor(statusCode, errors) {
|
|
150
|
-
super("Validation failed");
|
|
151
|
-
this.name = "ValidationErrorResponse";
|
|
152
|
-
this.statusCode = statusCode;
|
|
153
|
-
this.errors = errors;
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
function createValidator() {
|
|
157
|
-
const ajv = new Ajv.default({
|
|
158
|
-
allErrors: true,
|
|
159
|
-
coerceTypes: false,
|
|
160
|
-
strict: false,
|
|
161
|
-
validateFormats: true
|
|
162
|
-
});
|
|
163
|
-
addFormats.default(ajv);
|
|
164
|
-
ajv.addFormat("br-phone", /^\(\d{2}\)\s\d{5}-\d{4}$/);
|
|
165
|
-
return ajv;
|
|
166
|
-
}
|
|
167
|
-
function formatValidationErrors(errors) {
|
|
168
|
-
const formatted = {};
|
|
169
|
-
for (const error of errors) {
|
|
170
|
-
const path3 = error.path || "body";
|
|
171
|
-
if (!formatted[path3]) {
|
|
172
|
-
formatted[path3] = [];
|
|
173
|
-
}
|
|
174
|
-
formatted[path3].push(error.message);
|
|
175
|
-
}
|
|
176
|
-
return {
|
|
177
|
-
error: "Validation failed",
|
|
178
|
-
details: formatted
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// src/compiler/cache/loadArtifacts.ts
|
|
183
|
-
import fs from "fs";
|
|
184
|
-
import path from "path";
|
|
185
|
-
import { stat } from "fs/promises";
|
|
186
|
-
var artifactCache = /* @__PURE__ */ new Map();
|
|
187
|
-
async function getMtime(filePath) {
|
|
188
|
-
try {
|
|
189
|
-
const stats = await stat(filePath);
|
|
190
|
-
return stats.mtimeMs;
|
|
191
|
-
} catch {
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
async function loadArtifacts(options) {
|
|
196
|
-
const { outDir } = options;
|
|
197
|
-
const cacheKey = path.resolve(outDir);
|
|
198
|
-
const entry = artifactCache.get(cacheKey);
|
|
199
|
-
const openapiPath = path.join(outDir, "openapi.json");
|
|
200
|
-
const manifestPath = path.join(outDir, "manifest.json");
|
|
201
|
-
const validatorsPath = path.join(outDir, "validators.mjs");
|
|
202
|
-
const openapiMtime = await getMtime(openapiPath);
|
|
203
|
-
const manifestMtime = await getMtime(manifestPath);
|
|
204
|
-
const validatorsMtime = await getMtime(validatorsPath);
|
|
205
|
-
if (entry) {
|
|
206
|
-
const mtimesMatch = entry.mtimes.openapi === openapiMtime && entry.mtimes.manifest === manifestMtime && entry.mtimes.validators === validatorsMtime;
|
|
207
|
-
if (mtimesMatch) {
|
|
208
|
-
if (entry.openapi && entry.manifest) {
|
|
209
|
-
return {
|
|
210
|
-
openapi: entry.openapi,
|
|
211
|
-
manifest: entry.manifest,
|
|
212
|
-
validators: entry.validators
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
const openapiContent = fs.readFileSync(openapiPath, "utf-8");
|
|
218
|
-
const manifestContent = fs.readFileSync(manifestPath, "utf-8");
|
|
219
|
-
const openapi = JSON.parse(openapiContent);
|
|
220
|
-
const manifest = JSON.parse(manifestContent);
|
|
221
|
-
let validators = null;
|
|
222
|
-
if (manifest.validation.mode === "precompiled" && manifest.validation.precompiledModule) {
|
|
223
|
-
try {
|
|
224
|
-
const validatorsModule = await import(path.join(outDir, manifest.validation.precompiledModule));
|
|
225
|
-
validators = validatorsModule;
|
|
226
|
-
} catch (err) {
|
|
227
|
-
console.warn(`Failed to load precompiled validators: ${err}`);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
artifactCache.set(cacheKey, {
|
|
231
|
-
openapi,
|
|
232
|
-
manifest,
|
|
233
|
-
validators,
|
|
234
|
-
mtimes: {
|
|
235
|
-
openapi: openapiMtime,
|
|
236
|
-
manifest: manifestMtime,
|
|
237
|
-
validators: validatorsMtime
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
return {
|
|
241
|
-
openapi,
|
|
242
|
-
manifest,
|
|
243
|
-
validators
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// src/adapter/express/openapi.ts
|
|
248
|
-
function toOpenApiPath(path3) {
|
|
249
|
-
return path3.replace(/:([^/]+)/g, "{$1}");
|
|
250
|
-
}
|
|
251
|
-
function getOpenApiOperation(openapi, route) {
|
|
252
|
-
const pathKey = toOpenApiPath(route.fullPath);
|
|
253
|
-
const pathItem = openapi.paths?.[pathKey];
|
|
254
|
-
if (!pathItem) return null;
|
|
255
|
-
return pathItem[route.httpMethod.toLowerCase()] ?? null;
|
|
256
|
-
}
|
|
257
|
-
function getParamSchemaIndex(operation) {
|
|
258
|
-
const index = /* @__PURE__ */ new Map();
|
|
259
|
-
const params = operation?.parameters ?? [];
|
|
260
|
-
for (const param of params) {
|
|
261
|
-
if (!param?.name || !param?.in) continue;
|
|
262
|
-
if (param.schema) {
|
|
263
|
-
index.set(`${param.in}:${param.name}`, param.schema);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
return index;
|
|
267
|
-
}
|
|
268
|
-
function getParamSchemaFromIndex(index, location, name) {
|
|
269
|
-
return index.get(`${location}:${name}`) ?? null;
|
|
270
|
-
}
|
|
271
|
-
function getRequestBodySchema(operation, contentType) {
|
|
272
|
-
const content = operation?.requestBody?.content;
|
|
273
|
-
if (!content) return null;
|
|
274
|
-
if (contentType && content[contentType]?.schema) {
|
|
275
|
-
return content[contentType].schema;
|
|
276
|
-
}
|
|
277
|
-
const first = Object.values(content)[0];
|
|
278
|
-
return first?.schema ?? null;
|
|
279
|
-
}
|
|
280
|
-
function schemaFromType(schemaType) {
|
|
281
|
-
if (!schemaType) return null;
|
|
282
|
-
return { type: schemaType };
|
|
283
|
-
}
|
|
284
|
-
function resolveSchema(schema, components, seen = /* @__PURE__ */ new Set()) {
|
|
285
|
-
const ref = schema.$ref;
|
|
286
|
-
if (typeof ref !== "string" || !ref.startsWith("#/components/schemas/")) {
|
|
287
|
-
return schema;
|
|
288
|
-
}
|
|
289
|
-
const name = ref.replace("#/components/schemas/", "");
|
|
290
|
-
if (seen.has(name)) return schema;
|
|
291
|
-
const next = components[name];
|
|
292
|
-
if (!next) return schema;
|
|
293
|
-
seen.add(name);
|
|
294
|
-
return resolveSchema(next, components, seen);
|
|
295
|
-
}
|
|
296
|
-
function getSchemaByRef(openapi, ref) {
|
|
297
|
-
if (!ref.startsWith("#/components/schemas/")) return null;
|
|
298
|
-
const schemaName = ref.replace("#/components/schemas/", "");
|
|
299
|
-
return openapi.components.schemas[schemaName] || null;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// src/adapter/express/coercion.ts
|
|
303
|
-
function normalizeCoerceOptions(coerce) {
|
|
304
|
-
return {
|
|
305
|
-
body: coerce?.body ?? false,
|
|
306
|
-
query: coerce?.query ?? false,
|
|
307
|
-
path: coerce?.path ?? false,
|
|
308
|
-
header: coerce?.header ?? false,
|
|
309
|
-
cookie: coerce?.cookie ?? false,
|
|
310
|
-
dateTime: coerce?.dateTime ?? false,
|
|
311
|
-
date: coerce?.date ?? false
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
function getDateCoercionOptions(coerce, location) {
|
|
315
|
-
const enabled = coerce[location];
|
|
316
|
-
return {
|
|
317
|
-
dateTime: enabled && coerce.dateTime,
|
|
318
|
-
date: enabled && coerce.date
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
function coerceDatesWithSchema(value, schema, dateCoercion, components) {
|
|
322
|
-
if (!schema || !dateCoercion.date && !dateCoercion.dateTime) return value;
|
|
323
|
-
return coerceWithSchema(value, schema, dateCoercion, components, { coercePrimitives: false });
|
|
324
|
-
}
|
|
325
|
-
function coerceParamValue(value, schema, dateCoercion, components) {
|
|
326
|
-
if (!schema) return value;
|
|
327
|
-
return coerceWithSchema(value, schema, dateCoercion, components, { coercePrimitives: true });
|
|
328
|
-
}
|
|
329
|
-
function coerceWithSchema(value, schema, dateCoercion, components, options) {
|
|
330
|
-
if (value === void 0 || value === null) return value;
|
|
331
|
-
if (value instanceof Date) return value;
|
|
332
|
-
const resolved = resolveSchema(schema, components);
|
|
333
|
-
const override = resolved["x-adorn-coerce"];
|
|
334
|
-
const allowDateTime = override === true ? true : override === false ? false : dateCoercion.dateTime;
|
|
335
|
-
const allowDate = override === true ? true : override === false ? false : dateCoercion.date;
|
|
336
|
-
const byFormat = coerceDateString(value, resolved, allowDateTime, allowDate);
|
|
337
|
-
if (byFormat !== value) return byFormat;
|
|
338
|
-
const allOf = resolved.allOf;
|
|
339
|
-
if (Array.isArray(allOf)) {
|
|
340
|
-
let out = value;
|
|
341
|
-
for (const entry of allOf) {
|
|
342
|
-
out = coerceWithSchema(out, entry, { dateTime: allowDateTime, date: allowDate }, components, options);
|
|
343
|
-
}
|
|
344
|
-
return out;
|
|
345
|
-
}
|
|
346
|
-
const variants = resolved.oneOf ?? resolved.anyOf;
|
|
347
|
-
if (Array.isArray(variants)) {
|
|
348
|
-
for (const entry of variants) {
|
|
349
|
-
const out = coerceWithSchema(value, entry, { dateTime: allowDateTime, date: allowDate }, components, options);
|
|
350
|
-
if (out !== value) return out;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
const schemaType = resolved.type;
|
|
354
|
-
const types = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
|
|
355
|
-
if ((types.includes("array") || resolved.items) && Array.isArray(value)) {
|
|
356
|
-
const itemSchema = resolved.items ?? {};
|
|
357
|
-
return value.map((item) => coerceWithSchema(item, itemSchema, { dateTime: allowDateTime, date: allowDate }, components, options));
|
|
358
|
-
}
|
|
359
|
-
if ((types.includes("object") || resolved.properties || resolved.additionalProperties) && isPlainObject(value)) {
|
|
360
|
-
const props = resolved.properties;
|
|
361
|
-
const out = { ...value };
|
|
362
|
-
if (props) {
|
|
363
|
-
for (const [key, propSchema] of Object.entries(props)) {
|
|
364
|
-
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
365
|
-
out[key] = coerceWithSchema(value[key], propSchema, { dateTime: allowDateTime, date: allowDate }, components, options);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
const additional = resolved.additionalProperties;
|
|
370
|
-
if (additional && typeof additional === "object") {
|
|
371
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
372
|
-
if (props && Object.prototype.hasOwnProperty.call(props, key)) continue;
|
|
373
|
-
out[key] = coerceWithSchema(entry, additional, { dateTime: allowDateTime, date: allowDate }, components, options);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
return out;
|
|
377
|
-
}
|
|
378
|
-
if (options.coercePrimitives) {
|
|
379
|
-
return coercePrimitiveValue(value, types);
|
|
380
|
-
}
|
|
381
|
-
return value;
|
|
382
|
-
}
|
|
383
|
-
function coerceDateString(value, schema, allowDateTime, allowDate) {
|
|
384
|
-
if (typeof value !== "string") return value;
|
|
385
|
-
const format = schema.format;
|
|
386
|
-
const schemaType = schema.type;
|
|
387
|
-
const types = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
|
|
388
|
-
const allowsString = types.length === 0 || types.includes("string");
|
|
389
|
-
if (format === "date-time" && allowDateTime && allowsString) {
|
|
390
|
-
const parsed = new Date(value);
|
|
391
|
-
if (Number.isNaN(parsed.getTime())) {
|
|
392
|
-
throw new Error(`Invalid date-time: ${value}`);
|
|
393
|
-
}
|
|
394
|
-
return parsed;
|
|
395
|
-
}
|
|
396
|
-
if (format === "date" && allowDate && allowsString) {
|
|
397
|
-
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
398
|
-
throw new Error(`Invalid date: ${value}`);
|
|
399
|
-
}
|
|
400
|
-
const parsed = /* @__PURE__ */ new Date(`${value}T00:00:00.000Z`);
|
|
401
|
-
if (Number.isNaN(parsed.getTime())) {
|
|
402
|
-
throw new Error(`Invalid date: ${value}`);
|
|
403
|
-
}
|
|
404
|
-
return parsed;
|
|
405
|
-
}
|
|
406
|
-
return value;
|
|
407
|
-
}
|
|
408
|
-
function coercePrimitiveValue(value, types) {
|
|
409
|
-
if (value === void 0 || value === null) return value;
|
|
410
|
-
if (types.includes("number") || types.includes("integer")) {
|
|
411
|
-
const num = Number(value);
|
|
412
|
-
if (Number.isNaN(num)) {
|
|
413
|
-
throw new Error(`Invalid number: ${value}`);
|
|
414
|
-
}
|
|
415
|
-
return num;
|
|
416
|
-
}
|
|
417
|
-
if (types.includes("boolean")) {
|
|
418
|
-
if (value === "true") return true;
|
|
419
|
-
if (value === "false") return false;
|
|
420
|
-
if (typeof value === "boolean") return value;
|
|
421
|
-
throw new Error(`Invalid boolean: ${value}`);
|
|
422
|
-
}
|
|
423
|
-
return value;
|
|
424
|
-
}
|
|
425
|
-
function isPlainObject(value) {
|
|
426
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
427
|
-
const proto = Object.getPrototypeOf(value);
|
|
428
|
-
return proto === Object.prototype || proto === null;
|
|
429
|
-
}
|
|
430
|
-
function parseQueryValue(value, param) {
|
|
431
|
-
if (value === void 0 || value === null) return value;
|
|
432
|
-
const isArray = Array.isArray(param.schemaType) ? param.schemaType.includes("array") : param.schemaType === "array";
|
|
433
|
-
if (!isArray) return value;
|
|
434
|
-
const style = param.serialization?.style ?? "form";
|
|
435
|
-
const explode = param.serialization?.explode ?? true;
|
|
436
|
-
if (Array.isArray(value)) {
|
|
437
|
-
return value;
|
|
438
|
-
}
|
|
439
|
-
if (style === "form") {
|
|
440
|
-
if (explode) {
|
|
441
|
-
return value;
|
|
442
|
-
}
|
|
443
|
-
return value.split(",");
|
|
444
|
-
}
|
|
445
|
-
if (style === "spaceDelimited") {
|
|
446
|
-
return value.split(" ");
|
|
447
|
-
}
|
|
448
|
-
if (style === "pipeDelimited") {
|
|
449
|
-
return value.split("|");
|
|
450
|
-
}
|
|
451
|
-
return value;
|
|
452
|
-
}
|
|
453
|
-
function getRawQueryString(req) {
|
|
454
|
-
const url = req.originalUrl ?? req.url ?? "";
|
|
455
|
-
const index = url.indexOf("?");
|
|
456
|
-
if (index === -1) return "";
|
|
457
|
-
return url.slice(index + 1);
|
|
458
|
-
}
|
|
459
|
-
function parseDeepObjectParams(rawQuery, names, maxDepth = 5) {
|
|
460
|
-
const out = {};
|
|
461
|
-
if (!rawQuery || names.size === 0) return out;
|
|
462
|
-
const params = new URLSearchParams(rawQuery);
|
|
463
|
-
for (const [key, value] of params.entries()) {
|
|
464
|
-
const path3 = parseBracketPath(key);
|
|
465
|
-
if (path3.length === 0) continue;
|
|
466
|
-
if (path3.length > maxDepth) {
|
|
467
|
-
throw new Error(`Query parameter nesting depth (${path3.length}) exceeds maximum of ${maxDepth}`);
|
|
468
|
-
}
|
|
469
|
-
const root = path3[0];
|
|
470
|
-
if (!names.has(root)) continue;
|
|
471
|
-
assignDeepValue(out, path3, value);
|
|
472
|
-
}
|
|
473
|
-
return out;
|
|
474
|
-
}
|
|
475
|
-
function parseBracketPath(key) {
|
|
476
|
-
const parts = [];
|
|
477
|
-
let current = "";
|
|
478
|
-
for (let i = 0; i < key.length; i++) {
|
|
479
|
-
const ch = key[i];
|
|
480
|
-
if (ch === "[") {
|
|
481
|
-
if (current) parts.push(current);
|
|
482
|
-
current = "";
|
|
483
|
-
continue;
|
|
484
|
-
}
|
|
485
|
-
if (ch === "]") {
|
|
486
|
-
if (current) parts.push(current);
|
|
487
|
-
current = "";
|
|
488
|
-
continue;
|
|
489
|
-
}
|
|
490
|
-
current += ch;
|
|
491
|
-
}
|
|
492
|
-
if (current) parts.push(current);
|
|
493
|
-
return parts;
|
|
494
|
-
}
|
|
495
|
-
function assignDeepValue(target, path3, value) {
|
|
496
|
-
let cursor = target;
|
|
497
|
-
for (let i = 0; i < path3.length; i++) {
|
|
498
|
-
const key = path3[i];
|
|
499
|
-
if (!key) continue;
|
|
500
|
-
const isLast = i === path3.length - 1;
|
|
501
|
-
if (isLast) {
|
|
502
|
-
const existing = cursor[key];
|
|
503
|
-
if (existing === void 0) {
|
|
504
|
-
cursor[key] = value;
|
|
505
|
-
} else if (Array.isArray(existing)) {
|
|
506
|
-
existing.push(value);
|
|
507
|
-
} else {
|
|
508
|
-
cursor[key] = [existing, value];
|
|
509
|
-
}
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
const next = cursor[key];
|
|
513
|
-
if (!isPlainObject(next)) {
|
|
514
|
-
cursor[key] = {};
|
|
515
|
-
}
|
|
516
|
-
cursor = cursor[key];
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
function parseCookies(cookieHeader) {
|
|
520
|
-
if (!cookieHeader) return {};
|
|
521
|
-
const cookies = {};
|
|
522
|
-
const pairs = cookieHeader.split(";");
|
|
523
|
-
for (const pair of pairs) {
|
|
524
|
-
const [name, ...valueParts] = pair.trim().split("=");
|
|
525
|
-
if (name) {
|
|
526
|
-
cookies[name] = valueParts.join("=");
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
return cookies;
|
|
530
|
-
}
|
|
531
|
-
function normalizeSort(sort) {
|
|
532
|
-
if (Array.isArray(sort)) {
|
|
533
|
-
return sort.map((s) => String(s));
|
|
534
|
-
}
|
|
535
|
-
if (typeof sort === "string") {
|
|
536
|
-
return sort.split(",").map((s) => s.trim()).filter(Boolean);
|
|
537
|
-
}
|
|
538
|
-
return [];
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// src/adapter/express/auth.ts
|
|
542
|
-
function createAuthMiddleware(authConfig, routeAuth, globalSecurity) {
|
|
543
|
-
return async (req, res, next) => {
|
|
544
|
-
const isPublic = routeAuth === "public";
|
|
545
|
-
const hasAuthDecorator = routeAuth && routeAuth !== "public";
|
|
546
|
-
const hasGlobalSecurity = globalSecurity && globalSecurity.length > 0;
|
|
547
|
-
if (!hasAuthDecorator && !hasGlobalSecurity) {
|
|
548
|
-
return next();
|
|
549
|
-
}
|
|
550
|
-
if (isPublic) {
|
|
551
|
-
return next();
|
|
552
|
-
}
|
|
553
|
-
const authMeta = routeAuth;
|
|
554
|
-
const scheme = authMeta.scheme;
|
|
555
|
-
const requiredScopes = authMeta.scopes || [];
|
|
556
|
-
const isOptional = authMeta.optional ?? false;
|
|
557
|
-
const authRuntime = authConfig.schemes[scheme];
|
|
558
|
-
if (!authRuntime) {
|
|
559
|
-
throw new Error(`Auth scheme "${scheme}" not found in auth configuration`);
|
|
560
|
-
}
|
|
561
|
-
const result = await authRuntime.authenticate(req);
|
|
562
|
-
if (!result) {
|
|
563
|
-
if (isOptional) {
|
|
564
|
-
req.auth = null;
|
|
565
|
-
return next();
|
|
566
|
-
}
|
|
567
|
-
return authRuntime.challenge(res);
|
|
568
|
-
}
|
|
569
|
-
req.auth = result.principal;
|
|
570
|
-
if (authRuntime.authorize && requiredScopes.length > 0) {
|
|
571
|
-
if (!authRuntime.authorize(result, requiredScopes)) {
|
|
572
|
-
res.status(403).json({ error: "Forbidden", message: "Insufficient scopes" });
|
|
573
|
-
return;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
next();
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// src/adapter/express/validation.ts
|
|
581
|
-
function validateRequestWithPrecompiled(route, req, validators, maxDepth = 5) {
|
|
582
|
-
const errors = [];
|
|
583
|
-
const deepNames = new Set(route.args.query.filter((q) => q.serialization?.style === "deepObject").map((q) => q.name));
|
|
584
|
-
const deepValues = deepNames.size > 0 ? parseDeepObjectParams(getRawQueryString(req), deepNames, maxDepth) : {};
|
|
585
|
-
if (route.args.body) {
|
|
586
|
-
const validator = validators[route.operationId]?.body;
|
|
587
|
-
if (validator) {
|
|
588
|
-
const valid = validator(req.body);
|
|
589
|
-
if (!valid) {
|
|
590
|
-
const v = validators[route.operationId].body;
|
|
591
|
-
for (const err of v.errors || []) {
|
|
592
|
-
errors.push({
|
|
593
|
-
path: `#/body${err.instancePath}`,
|
|
594
|
-
message: err.message || "Invalid value",
|
|
595
|
-
keyword: err.keyword,
|
|
596
|
-
params: err.params
|
|
597
|
-
});
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
for (const q of route.args.query) {
|
|
603
|
-
let value = q.serialization?.style === "deepObject" ? deepValues[q.name] : req.query[q.name];
|
|
604
|
-
if (q.content === "application/json" && typeof value === "string") {
|
|
605
|
-
try {
|
|
606
|
-
value = JSON.parse(value);
|
|
607
|
-
} catch {
|
|
608
|
-
errors.push({
|
|
609
|
-
path: `#/query/${q.name}`,
|
|
610
|
-
message: "Invalid JSON string",
|
|
611
|
-
keyword: "json",
|
|
612
|
-
params: {}
|
|
613
|
-
});
|
|
614
|
-
continue;
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
return errors.length > 0 ? errors : null;
|
|
619
|
-
}
|
|
620
|
-
function validateRequest(route, req, openapi, validator, maxDepth = 5) {
|
|
621
|
-
const openapiOperation = getOpenApiOperation(openapi, route);
|
|
622
|
-
const paramSchemaIndex = getParamSchemaIndex(openapiOperation);
|
|
623
|
-
const deepNames = new Set(route.args.query.filter((q) => q.serialization?.style === "deepObject").map((q) => q.name));
|
|
624
|
-
const deepValues = deepNames.size > 0 ? parseDeepObjectParams(getRawQueryString(req), deepNames, maxDepth) : {};
|
|
625
|
-
const errors = [];
|
|
626
|
-
if (route.args.body) {
|
|
627
|
-
const bodySchema = getSchemaByRef(openapi, route.args.body.schemaRef);
|
|
628
|
-
if (bodySchema) {
|
|
629
|
-
const validate = validator.compile(bodySchema);
|
|
630
|
-
const valid = validate(req.body);
|
|
631
|
-
if (!valid) {
|
|
632
|
-
for (const err of validate.errors || []) {
|
|
633
|
-
errors.push({
|
|
634
|
-
path: `#/body${err.instancePath}`,
|
|
635
|
-
message: err.message || "Invalid value",
|
|
636
|
-
keyword: err.keyword,
|
|
637
|
-
params: err.params
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
for (const q of route.args.query) {
|
|
644
|
-
let value = q.serialization?.style === "deepObject" ? deepValues[q.name] : req.query[q.name];
|
|
645
|
-
if (q.content === "application/json" && typeof value === "string") {
|
|
646
|
-
try {
|
|
647
|
-
value = JSON.parse(value);
|
|
648
|
-
} catch {
|
|
649
|
-
errors.push({
|
|
650
|
-
path: `#/query/${q.name}`,
|
|
651
|
-
message: "Invalid JSON string",
|
|
652
|
-
keyword: "json",
|
|
653
|
-
params: {}
|
|
654
|
-
});
|
|
655
|
-
continue;
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
const openapiSchema = getParamSchemaFromIndex(paramSchemaIndex, "query", q.name);
|
|
659
|
-
let schema = {};
|
|
660
|
-
if (openapiSchema) {
|
|
661
|
-
schema = resolveSchema(openapiSchema, openapi.components.schemas);
|
|
662
|
-
} else {
|
|
663
|
-
if (q.schemaType) {
|
|
664
|
-
const type = Array.isArray(q.schemaType) ? q.schemaType[0] : q.schemaType;
|
|
665
|
-
schema.type = type;
|
|
666
|
-
}
|
|
667
|
-
if (q.schemaRef && q.schemaRef.includes("Inline")) {
|
|
668
|
-
const inlineSchema = getSchemaByRef(openapi, q.schemaRef);
|
|
669
|
-
if (inlineSchema) {
|
|
670
|
-
Object.assign(schema, inlineSchema);
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
const coerced = coerceParamValue(value, schema, { dateTime: false, date: false }, openapi.components.schemas);
|
|
675
|
-
if (Object.keys(schema).length > 0 && coerced !== void 0) {
|
|
676
|
-
const validate = validator.compile(schema);
|
|
677
|
-
const valid = validate(coerced);
|
|
678
|
-
if (!valid) {
|
|
679
|
-
for (const err of validate.errors || []) {
|
|
680
|
-
errors.push({
|
|
681
|
-
path: `#/query/${q.name}`,
|
|
682
|
-
message: err.message || "Invalid value",
|
|
683
|
-
keyword: err.keyword,
|
|
684
|
-
params: err.params
|
|
685
|
-
});
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
for (const p of route.args.path) {
|
|
691
|
-
const value = req.params[p.name];
|
|
692
|
-
const openapiSchema = getParamSchemaFromIndex(paramSchemaIndex, "path", p.name);
|
|
693
|
-
let schema = {};
|
|
694
|
-
if (openapiSchema) {
|
|
695
|
-
schema = resolveSchema(openapiSchema, openapi.components.schemas);
|
|
696
|
-
} else {
|
|
697
|
-
if (p.schemaType) {
|
|
698
|
-
const type = Array.isArray(p.schemaType) ? p.schemaType[0] : p.schemaType;
|
|
699
|
-
schema.type = type;
|
|
700
|
-
}
|
|
701
|
-
if (p.schemaRef && p.schemaRef.includes("Inline")) {
|
|
702
|
-
const inlineSchema = getSchemaByRef(openapi, p.schemaRef);
|
|
703
|
-
if (inlineSchema) {
|
|
704
|
-
Object.assign(schema, inlineSchema);
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
const coerced = coerceParamValue(value, schema, { dateTime: false, date: false }, openapi.components.schemas);
|
|
709
|
-
if (Object.keys(schema).length > 0 && coerced !== void 0) {
|
|
710
|
-
const validate = validator.compile(schema);
|
|
711
|
-
const valid = validate(coerced);
|
|
712
|
-
if (!valid) {
|
|
713
|
-
for (const err of validate.errors || []) {
|
|
714
|
-
errors.push({
|
|
715
|
-
path: `#/path/${p.name}`,
|
|
716
|
-
message: err.message || "Invalid value",
|
|
717
|
-
keyword: err.keyword,
|
|
718
|
-
params: err.params
|
|
719
|
-
});
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
return errors.length > 0 ? errors : null;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
// src/adapter/express/router.ts
|
|
728
|
-
async function createExpressRouter(options) {
|
|
729
|
-
const { controllers, artifactsDir = ".adorn", middleware = {}, defaultPageSize = 10, queryOptions } = options;
|
|
730
|
-
let manifest;
|
|
731
|
-
let openapi;
|
|
732
|
-
let precompiledValidators = null;
|
|
733
|
-
if (options.manifest && options.openapi) {
|
|
734
|
-
manifest = options.manifest;
|
|
735
|
-
openapi = options.openapi;
|
|
736
|
-
if (manifest.validation.mode === "precompiled" && manifest.validation.precompiledModule) {
|
|
737
|
-
try {
|
|
738
|
-
const validatorPath = join(artifactsDir, manifest.validation.precompiledModule);
|
|
739
|
-
precompiledValidators = __require(validatorPath).validators;
|
|
740
|
-
} catch (err) {
|
|
741
|
-
console.warn(`Failed to load precompiled validators: ${err}`);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
} else {
|
|
745
|
-
const artifacts = await loadArtifacts({ outDir: artifactsDir });
|
|
746
|
-
manifest = artifacts.manifest;
|
|
747
|
-
openapi = artifacts.openapi;
|
|
748
|
-
precompiledValidators = artifacts.validators?.validators ?? null;
|
|
749
|
-
}
|
|
750
|
-
const routes = bindRoutes({ controllers, manifest });
|
|
751
|
-
const validator = precompiledValidators ? null : createValidator();
|
|
752
|
-
const router = Router();
|
|
753
|
-
const instanceCache = /* @__PURE__ */ new Map();
|
|
754
|
-
const coerce = normalizeCoerceOptions(options.coerce);
|
|
755
|
-
function getInstance(Ctor) {
|
|
756
|
-
if (!instanceCache.has(Ctor)) {
|
|
757
|
-
instanceCache.set(Ctor, new Ctor());
|
|
758
|
-
}
|
|
759
|
-
return instanceCache.get(Ctor);
|
|
760
|
-
}
|
|
761
|
-
function resolveMiddleware(items, named = {}) {
|
|
762
|
-
return items.map((item) => {
|
|
763
|
-
if (typeof item === "string") {
|
|
764
|
-
const fn = named[item];
|
|
765
|
-
if (!fn) {
|
|
766
|
-
throw new Error(`Named middleware "${item}" not found in middleware registry`);
|
|
767
|
-
}
|
|
768
|
-
return fn;
|
|
769
|
-
}
|
|
770
|
-
return item;
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
for (const route of routes) {
|
|
774
|
-
const method = route.httpMethod.toLowerCase();
|
|
775
|
-
const openapiOperation = getOpenApiOperation(openapi, route);
|
|
776
|
-
const paramSchemaIndex = getParamSchemaIndex(openapiOperation);
|
|
777
|
-
const bodySchema = getRequestBodySchema(openapiOperation, route.args.body?.contentType) ?? (route.args.body ? getSchemaByRef(openapi, route.args.body.schemaRef) : null);
|
|
778
|
-
const coerceBodyDates = getDateCoercionOptions(coerce, "body");
|
|
779
|
-
const coerceQueryDates = getDateCoercionOptions(coerce, "query");
|
|
780
|
-
const coercePathDates = getDateCoercionOptions(coerce, "path");
|
|
781
|
-
const coerceHeaderDates = getDateCoercionOptions(coerce, "header");
|
|
782
|
-
const coerceCookieDates = getDateCoercionOptions(coerce, "cookie");
|
|
783
|
-
const middlewareChain = [];
|
|
784
|
-
if (middleware.global) {
|
|
785
|
-
middlewareChain.push(...resolveMiddleware(middleware.global, middleware.named || {}));
|
|
786
|
-
}
|
|
787
|
-
if (route.controllerUse) {
|
|
788
|
-
middlewareChain.push(...resolveMiddleware(route.controllerUse, middleware.named || {}));
|
|
789
|
-
}
|
|
790
|
-
if (route.use) {
|
|
791
|
-
middlewareChain.push(...resolveMiddleware(route.use, middleware.named || {}));
|
|
792
|
-
}
|
|
793
|
-
if (options.auth) {
|
|
794
|
-
const authMw = createAuthMiddleware(options.auth, route.auth, openapi.security || []);
|
|
795
|
-
middlewareChain.push(authMw);
|
|
796
|
-
}
|
|
797
|
-
router[method](route.fullPath, ...middlewareChain, async (req, res, next) => {
|
|
798
|
-
try {
|
|
799
|
-
const maxDepth = queryOptions?.maxNestingDepth ?? 5;
|
|
800
|
-
const validationErrors = precompiledValidators ? validateRequestWithPrecompiled(route, req, precompiledValidators, maxDepth) : validateRequest(route, req, openapi, validator, maxDepth);
|
|
801
|
-
if (validationErrors) {
|
|
802
|
-
return res.status(400).json(formatValidationErrors(validationErrors));
|
|
803
|
-
}
|
|
804
|
-
const deepNames = new Set(
|
|
805
|
-
route.args.query.filter((q) => q.serialization?.style === "deepObject").map((q) => q.name)
|
|
806
|
-
);
|
|
807
|
-
const deepValues = deepNames.size > 0 ? parseDeepObjectParams(getRawQueryString(req), deepNames, maxDepth) : {};
|
|
808
|
-
const instance = getInstance(route.controllerCtor);
|
|
809
|
-
const handler = instance[route.methodName];
|
|
810
|
-
if (typeof handler !== "function") {
|
|
811
|
-
throw new Error(`Method ${route.methodName} not found on controller`);
|
|
812
|
-
}
|
|
813
|
-
const args = [];
|
|
814
|
-
if (route.args.body) {
|
|
815
|
-
const coercedBody = (coerceBodyDates.date || coerceBodyDates.dateTime) && bodySchema ? coerceDatesWithSchema(req.body, bodySchema, coerceBodyDates, openapi.components.schemas) : req.body;
|
|
816
|
-
args[route.args.body.index] = coercedBody;
|
|
817
|
-
}
|
|
818
|
-
for (const pathArg of route.args.path) {
|
|
819
|
-
const rawValue = req.params[pathArg.name];
|
|
820
|
-
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "path", pathArg.name) ?? (pathArg.schemaRef ? getSchemaByRef(openapi, pathArg.schemaRef) : null) ?? schemaFromType(pathArg.schemaType);
|
|
821
|
-
const coerced = coerceParamValue(rawValue, paramSchema, coercePathDates, openapi.components.schemas);
|
|
822
|
-
args[pathArg.index] = coerced;
|
|
823
|
-
}
|
|
824
|
-
if (route.args.query.length > 0) {
|
|
825
|
-
const jsonArgs = route.args.query.filter((q) => q.content === "application/json");
|
|
826
|
-
const standardArgs = route.args.query.filter((q) => q.content !== "application/json");
|
|
827
|
-
const queryArgIndex = standardArgs[0]?.index;
|
|
828
|
-
if (queryArgIndex !== void 0) {
|
|
829
|
-
args[queryArgIndex] = {};
|
|
830
|
-
}
|
|
831
|
-
if (jsonArgs.length > 0) {
|
|
832
|
-
for (const q of jsonArgs) {
|
|
833
|
-
const rawValue = q.serialization?.style === "deepObject" ? deepValues[q.name] : req.query[q.name];
|
|
834
|
-
if (rawValue === void 0 || rawValue === null) continue;
|
|
835
|
-
let parsed = rawValue;
|
|
836
|
-
if (typeof rawValue === "string" && rawValue.length > 0) {
|
|
837
|
-
try {
|
|
838
|
-
parsed = JSON.parse(rawValue);
|
|
839
|
-
} catch {
|
|
840
|
-
parsed = rawValue;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "query", q.name) ?? (q.schemaRef ? getSchemaByRef(openapi, q.schemaRef) : null) ?? schemaFromType(q.schemaType);
|
|
844
|
-
const coerced = coerceParamValue(parsed, paramSchema, coerceQueryDates, openapi.components.schemas);
|
|
845
|
-
if (!args[q.index] || typeof args[q.index] !== "object") {
|
|
846
|
-
args[q.index] = {};
|
|
847
|
-
}
|
|
848
|
-
Object.assign(args[q.index], coerced);
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
if (standardArgs.length > 0) {
|
|
852
|
-
for (const q of standardArgs) {
|
|
853
|
-
const rawValue = q.serialization?.style === "deepObject" ? deepValues[q.name] : req.query[q.name];
|
|
854
|
-
if (rawValue === void 0) continue;
|
|
855
|
-
const parsed = parseQueryValue(rawValue, q);
|
|
856
|
-
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "query", q.name) ?? (q.schemaRef ? getSchemaByRef(openapi, q.schemaRef) : null) ?? schemaFromType(q.schemaType);
|
|
857
|
-
const coerced = coerceParamValue(parsed, paramSchema, coerceQueryDates, openapi.components.schemas);
|
|
858
|
-
if (!args[q.index] || typeof args[q.index] !== "object") {
|
|
859
|
-
args[q.index] = {};
|
|
860
|
-
}
|
|
861
|
-
args[q.index][q.name] = coerced;
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
if (queryArgIndex !== void 0 && args[queryArgIndex]) {
|
|
865
|
-
const queryObj = args[queryArgIndex];
|
|
866
|
-
if (queryObj.page === void 0) {
|
|
867
|
-
queryObj.page = 1;
|
|
868
|
-
}
|
|
869
|
-
if (queryObj.pageSize === void 0) {
|
|
870
|
-
queryObj.pageSize = defaultPageSize;
|
|
871
|
-
}
|
|
872
|
-
if (queryObj.sort) {
|
|
873
|
-
queryObj.sort = normalizeSort(queryObj.sort);
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
if (route.args.headers.length > 0) {
|
|
878
|
-
const firstHeaderIndex = route.args.headers[0].index;
|
|
879
|
-
const allSameIndex = route.args.headers.every((h) => h.index === firstHeaderIndex);
|
|
880
|
-
if (allSameIndex) {
|
|
881
|
-
args[firstHeaderIndex] = {};
|
|
882
|
-
for (const h of route.args.headers) {
|
|
883
|
-
const headerValue = req.headers[h.name.toLowerCase()];
|
|
884
|
-
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "header", h.name) ?? (h.schemaRef ? getSchemaByRef(openapi, h.schemaRef) : null) ?? schemaFromType(h.schemaType);
|
|
885
|
-
const coerced = coerceParamValue(headerValue, paramSchema, coerceHeaderDates, openapi.components.schemas);
|
|
886
|
-
args[firstHeaderIndex][h.name] = coerced ?? void 0;
|
|
887
|
-
}
|
|
888
|
-
} else {
|
|
889
|
-
for (const h of route.args.headers) {
|
|
890
|
-
const headerValue = req.headers[h.name.toLowerCase()];
|
|
891
|
-
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "header", h.name) ?? (h.schemaRef ? getSchemaByRef(openapi, h.schemaRef) : null) ?? schemaFromType(h.schemaType);
|
|
892
|
-
const coerced = coerceParamValue(headerValue, paramSchema, coerceHeaderDates, openapi.components.schemas);
|
|
893
|
-
args[h.index] = coerced ?? void 0;
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
if (route.args.cookies.length > 0) {
|
|
898
|
-
const firstCookieIndex = route.args.cookies[0].index;
|
|
899
|
-
const allSameIndex = route.args.cookies.every((c) => c.index === firstCookieIndex);
|
|
900
|
-
const cookies = parseCookies(req.headers.cookie);
|
|
901
|
-
if (allSameIndex) {
|
|
902
|
-
args[firstCookieIndex] = {};
|
|
903
|
-
for (const c of route.args.cookies) {
|
|
904
|
-
const cookieValue = cookies[c.name];
|
|
905
|
-
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "cookie", c.name) ?? (c.schemaRef ? getSchemaByRef(openapi, c.name) : null) ?? schemaFromType(c.schemaType);
|
|
906
|
-
const coerced = coerceParamValue(cookieValue, paramSchema, coerceCookieDates, openapi.components.schemas);
|
|
907
|
-
args[firstCookieIndex][c.name] = coerced;
|
|
908
|
-
}
|
|
909
|
-
} else {
|
|
910
|
-
for (const c of route.args.cookies) {
|
|
911
|
-
const cookieValue = cookies[c.name];
|
|
912
|
-
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "cookie", c.name) ?? (c.schemaRef ? getSchemaByRef(openapi, c.name) : null) ?? schemaFromType(c.schemaType);
|
|
913
|
-
const coerced = coerceParamValue(cookieValue, paramSchema, coerceCookieDates, openapi.components.schemas);
|
|
914
|
-
args[c.index] = coerced;
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
if (args.length === 0) {
|
|
919
|
-
args.push(req);
|
|
920
|
-
}
|
|
921
|
-
const result = await handler.apply(instance, args);
|
|
922
|
-
const primaryResponse = route.responses[0];
|
|
923
|
-
const status = primaryResponse?.status ?? 200;
|
|
924
|
-
res.status(status).json(result);
|
|
925
|
-
} catch (error) {
|
|
926
|
-
next(error);
|
|
927
|
-
}
|
|
928
|
-
});
|
|
929
|
-
}
|
|
930
|
-
return router;
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
// src/adapter/express/swagger.ts
|
|
934
|
-
import { Router as Router2 } from "express";
|
|
935
|
-
import { readFileSync, existsSync } from "fs";
|
|
936
|
-
import { resolve, isAbsolute, join as join2 } from "path";
|
|
937
|
-
import swaggerUi from "swagger-ui-express";
|
|
938
|
-
var DEFAULT_SWAGGER_UI_OPTIONS = {
|
|
939
|
-
docExpansion: "none",
|
|
940
|
-
// Collapse all by default for better performance
|
|
941
|
-
filter: true,
|
|
942
|
-
// Show filter input
|
|
943
|
-
showRequestDuration: true,
|
|
944
|
-
// Show request duration
|
|
945
|
-
displayRequestDuration: true,
|
|
946
|
-
operationsSorter: "alpha",
|
|
947
|
-
// Sort operations alphabetically
|
|
948
|
-
tagsSorter: "alpha",
|
|
949
|
-
// Sort tags alphabetically
|
|
950
|
-
persistAuthorization: true
|
|
951
|
-
// Persist authorization across reloads
|
|
952
|
-
};
|
|
953
|
-
function isSplitMode(openapiPath) {
|
|
954
|
-
const schemasDir = join2(openapiPath, "..", "schemas");
|
|
955
|
-
return existsSync(schemasDir);
|
|
956
|
-
}
|
|
957
|
-
function createSchemaRouter(artifactsDir) {
|
|
958
|
-
const router = Router2();
|
|
959
|
-
const schemasDir = isAbsolute(artifactsDir) ? resolve(artifactsDir, "schemas") : resolve(process.cwd(), artifactsDir, "schemas");
|
|
960
|
-
router.get("/schemas/:filename", (req, res, next) => {
|
|
961
|
-
const { filename } = req.params;
|
|
962
|
-
const filePath = resolve(schemasDir, filename);
|
|
963
|
-
if (!existsSync(filePath)) {
|
|
964
|
-
return res.status(404).json({ error: "Schema file not found" });
|
|
965
|
-
}
|
|
966
|
-
try {
|
|
967
|
-
const content = readFileSync(filePath, "utf-8");
|
|
968
|
-
res.setHeader("Content-Type", "application/json");
|
|
969
|
-
res.send(content);
|
|
970
|
-
} catch (error) {
|
|
971
|
-
next(error);
|
|
972
|
-
}
|
|
973
|
-
});
|
|
974
|
-
return router;
|
|
975
|
-
}
|
|
976
|
-
function setupSwagger(options = {}) {
|
|
977
|
-
const {
|
|
978
|
-
artifactsDir = ".adorn",
|
|
979
|
-
jsonPath = "/docs/openapi.json",
|
|
980
|
-
uiPath = "/docs",
|
|
981
|
-
swaggerOptions = {},
|
|
982
|
-
swaggerUiOptions = {}
|
|
983
|
-
} = options;
|
|
984
|
-
const router = Router2();
|
|
985
|
-
const openApiPath = isAbsolute(artifactsDir) ? resolve(artifactsDir, "openapi.json") : resolve(process.cwd(), artifactsDir, "openapi.json");
|
|
986
|
-
const splitMode = isSplitMode(openApiPath);
|
|
987
|
-
router.get(jsonPath, (req, res) => {
|
|
988
|
-
if (!existsSync(openApiPath)) {
|
|
989
|
-
return res.status(404).json({ error: "OpenAPI spec not found" });
|
|
990
|
-
}
|
|
991
|
-
const content = readFileSync(openApiPath, "utf-8");
|
|
992
|
-
res.setHeader("Content-Type", "application/json");
|
|
993
|
-
res.send(content);
|
|
994
|
-
});
|
|
995
|
-
if (splitMode) {
|
|
996
|
-
router.use("/schemas", createSchemaRouter(artifactsDir));
|
|
997
|
-
}
|
|
998
|
-
const mergedSwaggerOptions = {
|
|
999
|
-
...DEFAULT_SWAGGER_UI_OPTIONS,
|
|
1000
|
-
...swaggerOptions
|
|
1001
|
-
};
|
|
1002
|
-
router.use(uiPath, swaggerUi.serve, swaggerUi.setup(null, {
|
|
1003
|
-
swaggerOptions: {
|
|
1004
|
-
url: jsonPath,
|
|
1005
|
-
...mergedSwaggerOptions,
|
|
1006
|
-
// Add support for external $ref references in split mode
|
|
1007
|
-
supportedSubmitMethods: ["get", "put", "post", "delete", "options", "head", "patch"]
|
|
1008
|
-
},
|
|
1009
|
-
...swaggerUiOptions
|
|
1010
|
-
}));
|
|
1011
|
-
return router;
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
// src/adapter/express/bootstrap.ts
|
|
1015
|
-
import express from "express";
|
|
1016
|
-
import cors from "cors";
|
|
1017
|
-
import path2 from "path";
|
|
1018
|
-
var DEFAULT_CORS_CONFIG = {
|
|
1019
|
-
origin: (origin, callback) => {
|
|
1020
|
-
const allowedOrigins = process.env.CORS_ALLOWED_ORIGINS ? process.env.CORS_ALLOWED_ORIGINS.split(",") : ["http://localhost:*", "http://127.0.0.1:*"];
|
|
1021
|
-
if (!origin) {
|
|
1022
|
-
return callback(null, true);
|
|
1023
|
-
}
|
|
1024
|
-
const isAllowed = allowedOrigins.some((allowed) => {
|
|
1025
|
-
if (allowed === "*") return true;
|
|
1026
|
-
if (allowed.includes("*")) {
|
|
1027
|
-
const regex = new RegExp(allowed.replace("*", ".*"));
|
|
1028
|
-
return regex.test(origin);
|
|
1029
|
-
}
|
|
1030
|
-
return origin === allowed;
|
|
1031
|
-
});
|
|
1032
|
-
callback(null, isAllowed);
|
|
1033
|
-
},
|
|
1034
|
-
methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
|
|
1035
|
-
allowedHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
|
|
1036
|
-
credentials: false,
|
|
1037
|
-
optionsSuccessStatus: 204,
|
|
1038
|
-
maxAge: 86400
|
|
1039
|
-
};
|
|
1040
|
-
function applyCors(app, config) {
|
|
1041
|
-
if (typeof config === "function") {
|
|
1042
|
-
config(app);
|
|
1043
|
-
return;
|
|
1044
|
-
}
|
|
1045
|
-
if (config === true) {
|
|
1046
|
-
app.use(cors(DEFAULT_CORS_CONFIG));
|
|
1047
|
-
return;
|
|
1048
|
-
}
|
|
1049
|
-
app.use(cors(config));
|
|
1050
|
-
}
|
|
1051
|
-
function bootstrap(options) {
|
|
1052
|
-
return new Promise((resolve2, reject) => {
|
|
1053
|
-
const {
|
|
1054
|
-
controllers,
|
|
1055
|
-
port: userPort,
|
|
1056
|
-
host: userHost,
|
|
1057
|
-
artifactsDir: userArtifactsDir = ".adorn",
|
|
1058
|
-
enableSwagger = true,
|
|
1059
|
-
swaggerPath = "/docs",
|
|
1060
|
-
swaggerJsonPath = "/docs/openapi.json",
|
|
1061
|
-
middleware,
|
|
1062
|
-
auth,
|
|
1063
|
-
coerce,
|
|
1064
|
-
cors: cors2
|
|
1065
|
-
} = options;
|
|
1066
|
-
if (controllers.length === 0) {
|
|
1067
|
-
reject(new Error("At least one controller must be provided to bootstrap()."));
|
|
1068
|
-
return;
|
|
1069
|
-
}
|
|
1070
|
-
const envPort = process.env.PORT;
|
|
1071
|
-
const port = userPort ?? (envPort !== void 0 ? Number(envPort) : 3e3);
|
|
1072
|
-
const host = userHost ?? process.env.HOST ?? "0.0.0.0";
|
|
1073
|
-
if (isNaN(port) || port < 0 || port > 65535) {
|
|
1074
|
-
reject(new Error(`Invalid port: ${port}. Port must be between 0 and 65535.`));
|
|
1075
|
-
return;
|
|
1076
|
-
}
|
|
1077
|
-
const absoluteArtifactsDir = path2.isAbsolute(userArtifactsDir) ? userArtifactsDir : path2.resolve(process.cwd(), userArtifactsDir);
|
|
1078
|
-
const app = express();
|
|
1079
|
-
app.use(express.json());
|
|
1080
|
-
if (cors2 !== void 0 && cors2 !== false) {
|
|
1081
|
-
applyCors(app, cors2);
|
|
1082
|
-
}
|
|
1083
|
-
createExpressRouter({
|
|
1084
|
-
controllers,
|
|
1085
|
-
artifactsDir: absoluteArtifactsDir,
|
|
1086
|
-
middleware,
|
|
1087
|
-
auth,
|
|
1088
|
-
coerce
|
|
1089
|
-
}).then((router) => {
|
|
1090
|
-
app.use(router);
|
|
1091
|
-
if (enableSwagger) {
|
|
1092
|
-
const displayHost = host === "0.0.0.0" ? "localhost" : host;
|
|
1093
|
-
const serverUrl = `http://${displayHost}:${port}`;
|
|
1094
|
-
app.use(
|
|
1095
|
-
setupSwagger({
|
|
1096
|
-
artifactsDir: absoluteArtifactsDir,
|
|
1097
|
-
jsonPath: swaggerJsonPath,
|
|
1098
|
-
uiPath: swaggerPath,
|
|
1099
|
-
swaggerOptions: {
|
|
1100
|
-
servers: [{ url: serverUrl }],
|
|
1101
|
-
...options.swaggerOptions
|
|
1102
|
-
},
|
|
1103
|
-
swaggerUiOptions: options.swaggerUiOptions
|
|
1104
|
-
})
|
|
1105
|
-
);
|
|
1106
|
-
}
|
|
1107
|
-
const server = app.listen(port, host, () => {
|
|
1108
|
-
const displayHost = host === "0.0.0.0" ? "localhost" : host;
|
|
1109
|
-
const serverUrl = `http://${displayHost}:${port}`;
|
|
1110
|
-
console.log(`\u{1F680} Server running on ${serverUrl}`);
|
|
1111
|
-
if (enableSwagger) {
|
|
1112
|
-
console.log(`\u{1F4DA} Swagger UI: ${serverUrl}${swaggerPath}`);
|
|
1113
|
-
}
|
|
1114
|
-
const result = {
|
|
1115
|
-
server,
|
|
1116
|
-
app,
|
|
1117
|
-
url: serverUrl,
|
|
1118
|
-
port,
|
|
1119
|
-
host,
|
|
1120
|
-
close: () => new Promise((closeResolve) => {
|
|
1121
|
-
server.close(() => {
|
|
1122
|
-
console.log("Server closed gracefully");
|
|
1123
|
-
closeResolve();
|
|
1124
|
-
});
|
|
1125
|
-
})
|
|
1126
|
-
};
|
|
1127
|
-
resolve2(result);
|
|
1128
|
-
});
|
|
1129
|
-
server.on("error", (error) => {
|
|
1130
|
-
if (error.code === "EADDRINUSE") {
|
|
1131
|
-
reject(new Error(`Port ${port} already in use. Please choose a different port.`));
|
|
1132
|
-
} else if (error.code === "EACCES") {
|
|
1133
|
-
reject(new Error(`Permission denied for port ${port}. Ports below 1024 require root privileges.`));
|
|
1134
|
-
} else {
|
|
1135
|
-
reject(new Error(`Failed to start server: ${error.message}`));
|
|
1136
|
-
}
|
|
1137
|
-
});
|
|
1138
|
-
}).catch(reject);
|
|
1139
|
-
});
|
|
1140
|
-
}
|
|
1141
|
-
export {
|
|
1142
|
-
ValidationErrorResponse,
|
|
1143
|
-
bindRoutes,
|
|
1144
|
-
bootstrap,
|
|
1145
|
-
createExpressRouter,
|
|
1146
|
-
createValidator,
|
|
1147
|
-
formatValidationErrors,
|
|
1148
|
-
setupSwagger
|
|
1149
|
-
};
|
|
1150
|
-
//# sourceMappingURL=express.js.map
|