{"_id":"@asenajs/asena-openapi","_rev":"6-f019c7a956c296fb6306ea997deca228","name":"@asenajs/asena-openapi","dist-tags":{"latest":"3.1.0"},"versions":{"1.0.0":{"name":"@asenajs/asena-openapi","version":"1.0.0","author":{"name":"LibirSoft"},"license":"MIT","_id":"@asenajs/asena-openapi@1.0.0","maintainers":[{"name":"libir","email":"libirsoft@gmail.com"}],"homepage":"https://github.com/AsenaJs/asena-openapi#readme","bugs":{"url":"https://github.com/AsenaJs/asena-openapi/issues"},"dist":{"shasum":"e208fd44acc76f269330d47e21a05f27f9df56c3","tarball":"https://registry.npmjs.org/@asenajs/asena-openapi/-/asena-openapi-1.0.0.tgz","fileCount":30,"integrity":"sha512-sRKPNqAjGeirgvbmTFfVdPPV7AiyW7OBYBa1/oZtzELdwt95ynpFY8iOQrXUkskMm8KVbnUlleSQ8NOeQXbM0A==","signatures":[{"sig":"MEUCIGOXLw0V3V+Jjxdw0aJUNhDSI5i82Oi7G4X11Feb2KDiAiEA6V4cjzeUr27kLPLNpo9T/ew+aowsYO3MlKTxB/AbLgE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":68350},"main":"dist/index.js","types":"dist/index.d.ts","module":"index.ts","gitHead":"03021402f467b6458f2454ef54eb643b69482998","scripts":{"lint":"eslint . --ext .ts","test":"bun test","build":"bun run clean && tsc","check":"bun run lint && bun run format:check","clean":"rm -rf dist","format":"prettier --write .","release":"bun run build && changeset publish","version":"changeset version","lint:fix":"eslint . --ext .ts --fix","changeset":"changeset","check:fix":"bun run lint:fix && bun run format","test:watch":"bun test --watch","format:check":"prettier --check .","test:coverage":"bun test --coverage","prepublishOnly":"bun run build"},"_npmUser":{"name":"libir","email":"libirsoft@gmail.com"},"repository":{"url":"git+https://github.com/AsenaJs/asena-openapi.git","type":"git"},"_npmVersion":"11.12.1","description":"OpenAPI 3.1 spec generation for AsenaJS - automatic schema extraction from validators and route decorators","directories":{},"_nodeVersion":"25.9.0","_hasShrinkwrap":false,"devDependencies":{"zod":"^4.3.6","eslint":"^8.57.1","prettier":"^3.5.3","@types/bun":"latest","typescript":"^5.9.3","@asenajs/asena":"^0.7.0","@changesets/cli":"^2.29.3","eslint-plugin-n":"^16.6.2","reflect-metadata":"^0.2.2","eslint-config-alloy":"^5.1.2","eslint-plugin-alloy":"^1.2.1","eslint-plugin-import":"^2.31.0","eslint-plugin-promise":"^6.6.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.4.0","@typescript-eslint/eslint-plugin":"^6.21.0"},"peerDependencies":{"zod":"^4.3.6","@asenajs/asena":"^0.7.0","reflect-metadata":"^0.2.2"},"_npmOperationalInternal":{"tmp":"tmp/asena-openapi_1.0.0_1775597481579_0.26748364745472086","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@asenajs/asena-openapi","version":"1.1.0","author":{"name":"LibirSoft"},"license":"MIT","_id":"@asenajs/asena-openapi@1.1.0","maintainers":[{"name":"libir","email":"libirsoft@gmail.com"}],"homepage":"https://github.com/AsenaJs/asena-openapi#readme","bugs":{"url":"https://github.com/AsenaJs/asena-openapi/issues"},"dist":{"shasum":"e5f263f89e7dd2e4d5e846f085bfd66c5921ed3e","tarball":"https://registry.npmjs.org/@asenajs/asena-openapi/-/asena-openapi-1.1.0.tgz","fileCount":30,"integrity":"sha512-EfHsT9iun6Zq4sIklIsqKv4YrrfNAV3nMeqB8SQBwCTVubTzYiScZLvpi4mpswEQ4sdKvOYxYS4lFqBjDXsi9Q==","signatures":[{"sig":"MEQCIAX1TRgTZWeVA/TWW0FFPKZR291Pt9voVyxeG0caQFLrAiBbtWcphXUFvfYkQCWbz7LYk54zvqjt9GQehbJSRAl7OA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":70067},"main":"dist/index.js","types":"dist/index.d.ts","module":"index.ts","engines":{"bun":">=1.3.12"},"gitHead":"dac943acb3e87f2c655d140cec4b204fee657d1b","scripts":{"lint":"eslint . --ext .ts","test":"bun test","build":"bun run clean && tsc","check":"bun run lint && bun run format:check","clean":"rm -rf dist","format":"prettier --write .","release":"bun run build && changeset publish","version":"changeset version","lint:fix":"eslint . --ext .ts --fix","changeset":"changeset","check:fix":"bun run lint:fix && bun run format","typecheck":"tsc -p tsconfig.typecheck.json","test:watch":"bun test --watch","format:check":"prettier --check .","test:coverage":"bun test --coverage","prepublishOnly":"bun run build"},"_npmUser":{"name":"libir","email":"libirsoft@gmail.com"},"repository":{"url":"git+https://github.com/AsenaJs/asena-openapi.git","type":"git"},"_npmVersion":"12.0.1","description":"OpenAPI 3.1 spec generation for AsenaJS - automatic schema extraction from validators and route decorators","directories":{},"_nodeVersion":"26.5.0","_hasShrinkwrap":false,"devDependencies":{"zod":"^4.3.6","eslint":"^8.57.1","prettier":"^3.5.3","@types/bun":"latest","typescript":"^5.9.3","@asenajs/asena":"^0.9.0","@changesets/cli":"^2.29.3","eslint-plugin-n":"^16.6.2","reflect-metadata":"^0.2.2","eslint-config-alloy":"^5.1.2","eslint-plugin-alloy":"^1.2.1","eslint-plugin-import":"^2.31.0","eslint-plugin-promise":"^6.6.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.4.0","@typescript-eslint/eslint-plugin":"^6.21.0"},"peerDependencies":{"zod":"^4.3.6","@asenajs/asena":"^0.9.0","reflect-metadata":"^0.2.2"},"_npmOperationalInternal":{"tmp":"tmp/asena-openapi_1.1.0_1785197546707_0.7730220514223833","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@asenajs/asena-openapi","version":"2.0.0","author":{"name":"LibirSoft"},"license":"MIT","_id":"@asenajs/asena-openapi@2.0.0","maintainers":[{"name":"libir","email":"libirsoft@gmail.com"}],"homepage":"https://github.com/AsenaJs/asena-openapi#readme","bugs":{"url":"https://github.com/AsenaJs/asena-openapi/issues"},"dist":{"shasum":"df48114423d34fe5440af9373f1c992abae6bb73","tarball":"https://registry.npmjs.org/@asenajs/asena-openapi/-/asena-openapi-2.0.0.tgz","fileCount":30,"integrity":"sha512-HYIuc1OJDGvagmF6DjgAAhtknvoj1ffQ6G/dRQ8w4+ZMUKZHPV572LON01tvFqx0o2nB50wUBGym16SWFYNplg==","signatures":[{"sig":"MEUCIQDhBgqwRr2ezaD3P++mnaQHBzXoTYUYUnlHdfoy/NPMhQIgCeNp8rYRqwnqha7REaX1xZEFT2yJBf7gUYVuqjYZeqY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":70070},"main":"dist/index.js","types":"dist/index.d.ts","module":"index.ts","engines":{"bun":">=1.3.12"},"gitHead":"eb581778dd00db076e2792d29cc85eb85e126633","scripts":{"lint":"eslint . --ext .ts","test":"bun test","build":"bun run clean && tsc","check":"bun run lint && bun run format:check","clean":"rm -rf dist","format":"prettier --write .","release":"bun run build && changeset publish","version":"changeset version","lint:fix":"eslint . --ext .ts --fix","changeset":"changeset","check:fix":"bun run lint:fix && bun run format","typecheck":"tsc -p tsconfig.typecheck.json","test:watch":"bun test --watch","format:check":"prettier --check .","test:coverage":"bun test --coverage","prepublishOnly":"bun run build"},"_npmUser":{"name":"libir","email":"libirsoft@gmail.com"},"repository":{"url":"git+https://github.com/AsenaJs/asena-openapi.git","type":"git"},"_npmVersion":"12.0.1","description":"OpenAPI 3.1 spec generation for AsenaJS - automatic schema extraction from validators and route decorators","directories":{},"_nodeVersion":"26.5.0","_hasShrinkwrap":false,"devDependencies":{"zod":"^4.3.6","eslint":"^8.57.1","prettier":"^3.5.3","@types/bun":"latest","typescript":"^5.9.3","@asenajs/asena":"^0.10.0","@changesets/cli":"^2.29.3","eslint-plugin-n":"^16.6.2","reflect-metadata":"^0.2.2","eslint-config-alloy":"^5.1.2","eslint-plugin-alloy":"^1.2.1","eslint-plugin-import":"^2.31.0","eslint-plugin-promise":"^6.6.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.4.0","@typescript-eslint/eslint-plugin":"^6.21.0"},"peerDependencies":{"zod":"^4.3.6","@asenajs/asena":"^0.10.0","reflect-metadata":"^0.2.2"},"_npmOperationalInternal":{"tmp":"tmp/asena-openapi_2.0.0_1785354343101_0.7948734006216702","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@asenajs/asena-openapi","version":"2.1.0","author":{"name":"LibirSoft"},"license":"MIT","_id":"@asenajs/asena-openapi@2.1.0","maintainers":[{"name":"libir","email":"libirsoft@gmail.com"}],"homepage":"https://github.com/AsenaJs/asena-openapi#readme","bugs":{"url":"https://github.com/AsenaJs/asena-openapi/issues"},"dist":{"shasum":"de5d1716efd207a4a87642a2ba941290603a51ef","tarball":"https://registry.npmjs.org/@asenajs/asena-openapi/-/asena-openapi-2.1.0.tgz","fileCount":33,"integrity":"sha512-c1beMKzgtq7hOKFtIon5Ws29unvRDFD33eEGD+v1tkXazSZ8PGrsrpupN2srWpdRuLv37Ku1RXQ9WoE/1BKT4A==","signatures":[{"sig":"MEYCIQCt+SvqHfljP+XpmmMP+tvq0jkyq6gbATmNPbea1WEeSQIhAKoOaD54YyGoC1bs19K8UMmnlbE/bZInveGZl5oaCc+K","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":65711},"main":"dist/index.js","types":"dist/index.d.ts","module":"index.ts","engines":{"bun":">=1.3.12"},"gitHead":"5bcc392b4bd59560b85523ceb72f7b17a859845c","scripts":{"lint":"eslint . --ext .ts","test":"bun test","build":"bun run clean && tsc","check":"bun run lint && bun run format:check","clean":"rm -rf dist","format":"prettier --write .","release":"bun run build && changeset publish","version":"changeset version","lint:fix":"eslint . --ext .ts --fix","changeset":"changeset","check:fix":"bun run lint:fix && bun run format","typecheck":"tsc -p tsconfig.typecheck.json","test:watch":"bun test --watch","format:check":"prettier --check .","test:coverage":"bun test --coverage","prepublishOnly":"bun run build"},"_npmUser":{"name":"libir","email":"libirsoft@gmail.com"},"repository":{"url":"git+https://github.com/AsenaJs/asena-openapi.git","type":"git"},"_npmVersion":"12.0.2","description":"OpenAPI 3.1 spec generation for AsenaJS - automatic schema extraction from validators and route decorators","directories":{},"_nodeVersion":"26.7.0","_hasShrinkwrap":false,"devDependencies":{"zod":"^4.3.6","eslint":"^8.57.1","prettier":"^3.5.3","@types/bun":"latest","typescript":"^5.9.3","@asenajs/asena":"^0.10.0","@changesets/cli":"^2.29.3","eslint-plugin-n":"^16.6.2","reflect-metadata":"^0.2.2","eslint-config-alloy":"^5.1.2","eslint-plugin-alloy":"^1.2.1","eslint-plugin-import":"^2.31.0","eslint-plugin-promise":"^6.6.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.4.0","@typescript-eslint/eslint-plugin":"^6.21.0"},"peerDependencies":{"zod":"^4.3.6","@asenajs/asena":"^0.10.0","reflect-metadata":"^0.2.2"},"_npmOperationalInternal":{"tmp":"tmp/asena-openapi_2.1.0_1787159713770_0.6386564098880807","host":"s3://npm-registry-packages-npm-production"}},"3.0.0":{"name":"@asenajs/asena-openapi","version":"3.0.0","author":{"name":"LibirSoft"},"license":"MIT","_id":"@asenajs/asena-openapi@3.0.0","maintainers":[{"name":"libir","email":"libirsoft@gmail.com"}],"homepage":"https://github.com/AsenaJs/asena-openapi#readme","bugs":{"url":"https://github.com/AsenaJs/asena-openapi/issues"},"dist":{"shasum":"b938ea321ef21edbfa7085a2e7ce01e3a74cfa81","tarball":"https://registry.npmjs.org/@asenajs/asena-openapi/-/asena-openapi-3.0.0.tgz","fileCount":33,"integrity":"sha512-D0LbXHTODJKexCJky+xJJfCtmdpP8kArvHpm3xdkql33PWHNbfDxOUiUDcWjJ2f1Kv5TlN+PFoglPdgtK2/Qog==","signatures":[{"sig":"MEUCIBaJC3E3omDpEyQ/GfDspk8Rxub2d72dxxL4HqObE/L4AiEA/63xa/uQdmHlGkE0HGEPwduOBhpnZjmd5U6XVEWm5bU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":65704},"main":"dist/index.js","types":"dist/index.d.ts","module":"index.ts","engines":{"bun":">=1.4.0"},"gitHead":"a3b13b3691065660ec2e5235e46c74cf25cf0abf","scripts":{"lint":"eslint . --ext .ts","test":"bun test","build":"bun run clean && tsc","check":"bun run lint && bun run format:check","clean":"rm -rf dist","format":"prettier --write .","release":"bun run build && changeset publish","version":"changeset version","lint:fix":"eslint . --ext .ts --fix","changeset":"changeset","check:fix":"bun run lint:fix && bun run format","typecheck":"tsc -p tsconfig.typecheck.json","test:watch":"bun test --watch","format:check":"prettier --check .","test:coverage":"bun test --coverage","prepublishOnly":"bun run build"},"_npmUser":{"name":"libir","email":"libirsoft@gmail.com"},"repository":{"url":"git+https://github.com/AsenaJs/asena-openapi.git","type":"git"},"_npmVersion":"12.0.2","description":"OpenAPI 3.1 spec generation for AsenaJS - automatic schema extraction from validators and route decorators","directories":{},"_nodeVersion":"26.7.0","_hasShrinkwrap":false,"devDependencies":{"zod":"^4.4.3","eslint":"^8.57.1","prettier":"^3.9.6","@types/bun":"latest","typescript":"^5.9.3","@asenajs/asena":"^0.11.0","@changesets/cli":"^2.31.1","eslint-plugin-n":"^16.6.2","reflect-metadata":"^0.2.2","eslint-config-alloy":"^5.1.2","eslint-plugin-alloy":"^1.2.1","eslint-plugin-import":"^2.32.0","eslint-plugin-promise":"^6.6.0","eslint-config-prettier":"^9.1.2","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/eslint-plugin":"^6.21.0"},"peerDependencies":{"zod":"^4.3.6","@asenajs/asena":"^0.11.0","reflect-metadata":"^0.2.2"},"_npmOperationalInternal":{"tmp":"tmp/asena-openapi_3.0.0_1787695824289_0.2799239069642936","host":"s3://npm-registry-packages-npm-production"}},"3.1.0":{"name":"@asenajs/asena-openapi","version":"3.1.0","author":{"name":"LibirSoft"},"description":"OpenAPI 3.1 spec generation for AsenaJS - automatic schema extraction from validators and route decorators","main":"dist/index.js","module":"index.ts","types":"dist/index.d.ts","license":"MIT","engines":{"bun":">=1.4.0"},"repository":{"type":"git","url":"git+https://github.com/AsenaJs/asena-openapi.git"},"scripts":{"test":"bun test","test:watch":"bun test --watch","test:coverage":"bun test --coverage","build":"bun run clean && tsc","typecheck":"tsc -p tsconfig.typecheck.json","clean":"rm -rf dist","prepublishOnly":"bun run build","lint":"eslint . --ext .ts","lint:fix":"eslint . --ext .ts --fix","format":"prettier --write .","format:check":"prettier --check .","check":"bun run lint && bun run format:check","check:fix":"bun run lint:fix && bun run format","changeset":"changeset","version":"changeset version","release":"bun run build && changeset publish"},"devDependencies":{"@asenajs/asena":"^0.11.0","@changesets/cli":"^2.31.1","@types/bun":"latest","@typescript-eslint/eslint-plugin":"^6.21.0","typescript":"^5.9.3","eslint":"^8.57.1","eslint-config-alloy":"^5.1.2","eslint-config-prettier":"^9.1.2","eslint-plugin-alloy":"^1.2.1","eslint-plugin-import":"^2.32.0","eslint-plugin-n":"^16.6.2","eslint-plugin-prettier":"^5.5.6","eslint-plugin-promise":"^6.6.0","prettier":"^3.9.6","reflect-metadata":"^0.2.2","zod":"^4.4.3"},"peerDependencies":{"@asenajs/asena":"^0.11.0","reflect-metadata":"^0.2.2","zod":"^4.3.6"},"gitHead":"961a9c16ad29ad0cd8a83ba38431a9b0ee31ab34","_id":"@asenajs/asena-openapi@3.1.0","bugs":{"url":"https://github.com/AsenaJs/asena-openapi/issues"},"homepage":"https://github.com/AsenaJs/asena-openapi#readme","_nodeVersion":"26.7.0","_npmVersion":"12.0.2","dist":{"integrity":"sha512-j3pAIe3TtIKh/RJdEFeubIygG5/rRCuEgGGG4aLqhkU+OrRBf9HdMyOarGHwUkX1vOwkXg5yIZinFnF+BzwY3A==","shasum":"bab972ca0939c850ef2e8ea330274b323ec3934c","tarball":"https://registry.npmjs.org/@asenajs/asena-openapi/-/asena-openapi-3.1.0.tgz","fileCount":36,"unpackedSize":73100,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDBlZbkD4KI48IjVc3QUR0SREdswAvsJkGYyrFQ9s7tXQIhANc+DMTjQsM+FmC670nN9F2rKElUinz6PMeucR8kdjEA"}]},"_npmUser":{"name":"libir","email":"libirsoft@gmail.com"},"directories":{},"maintainers":[{"name":"libir","email":"libirsoft@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/asena-openapi_3.1.0_1787845808743_0.6906939091278825"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-07T21:31:21.485Z","modified":"2026-08-27T15:50:09.005Z","1.0.0":"2026-04-07T21:31:21.726Z","1.1.0":"2026-07-28T00:12:26.872Z","2.0.0":"2026-07-29T19:45:43.292Z","2.1.0":"2026-08-19T17:15:13.932Z","3.0.0":"2026-08-25T22:10:24.442Z","3.1.0":"2026-08-27T15:50:08.871Z"},"bugs":{"url":"https://github.com/AsenaJs/asena-openapi/issues"},"author":{"name":"LibirSoft"},"license":"MIT","homepage":"https://github.com/AsenaJs/asena-openapi#readme","repository":{"type":"git","url":"git+https://github.com/AsenaJs/asena-openapi.git"},"description":"OpenAPI 3.1 spec generation for AsenaJS - automatic schema extraction from validators and route decorators","maintainers":[{"name":"libir","email":"libirsoft@gmail.com"}],"readme":"<p width=\"%100\" align=\"center\">\n  <img src=\"https://avatars.githubusercontent.com/u/179836938?s=200&v=4\" width=\"150\" align=\"center\"/>\n</p>\n\n# @asenajs/asena-openapi\n\n[![Version](https://img.shields.io/badge/version-3.1.0-blue.svg)](https://github.com/AsenaJs/asena-openapi)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)\n[![Bun Version](https://img.shields.io/badge/Bun-1.4%2B-blueviolet)](https://bun.sh)\n\nAutomatic OpenAPI 3.1 spec generation for AsenaJS — zero config, uses your existing validators.\n\nYour existing `@Controller` routes and validator schemas (`json()`, `query()`, `param()`, `response()`) are automatically converted to a full OpenAPI specification. No extra annotations needed.\n\n## Features\n\n- **Zero Config** - Extracts schemas from existing validators, no extra annotations needed\n- **OpenAPI 3.1** - Generates JSON Schema draft-2020-12 compatible spec\n- **Zero Runtime Dependencies** - Only peer deps (asena, reflect-metadata, zod)\n- **Built-in API Docs UIs** - Swagger UI or Scalar, CDN-based, no npm install required\n- **@Hidden Decorator** - Class and method level exclusion from spec\n- **Zod v4 Native** - Uses `z.toJSONSchema()` for accurate conversion\n- **Pluggable Converters** - `SchemaConverter` interface for custom schema types\n- **IoC Integrated** - PostProcessor pattern, auto-discovers controllers during bootstrap\n\n## Requirements\n\n- [Bun](https://bun.sh) v1.4 or higher\n- [@asenajs/asena](https://github.com/AsenaJs/Asena) v0.11.0 or higher\n- [Zod](https://zod.dev) v4.3 or higher\n\n## Installation\n\n```bash\nbun add @asenajs/asena-openapi\n```\n\n## Quick Start\n\n```typescript\nimport { OpenApi, OpenApiPostProcessor } from '@asenajs/asena-openapi';\n\n@OpenApi({\n  info: { title: 'My API', version: '1.0.0' },\n  path: '/api/openapi',\n  ui: 'scalar', // or true / 'swagger' — API docs UI at /api/openapi/ui\n})\nexport class AppOpenApi extends OpenApiPostProcessor {}\n```\n\nAsena automatically discovers it — that's it.\n\nNow:\n\n- `GET /api/openapi` → OpenAPI 3.1 JSON spec\n- `GET /api/openapi/ui` → API docs UI (Swagger UI or Scalar)\n\n## How It Works\n\nThe `OpenApiPostProcessor` automatically:\n\n1. **Intercepts** every `@Controller` during IoC setup\n2. **Extracts** route metadata (`@Get`, `@Post`, `@Put`, `@Delete`)\n3. **Resolves** validators and converts their Zod schemas to JSON Schema\n4. **Generates** a complete OpenAPI 3.1 spec\n5. **Registers** GET endpoints on the adapter for spec and the docs UI\n\nYour existing validators do double duty — they validate requests AND generate documentation:\n\n```typescript\n@Middleware({ validator: true })\nexport class CreateUserValidator extends ValidationService {\n  // → requestBody (application/json)\n  json() {\n    return z.object({\n      name: z.string().min(1),\n      email: z.string().email(),\n    });\n  }\n\n  // → query parameters\n  query() {\n    return z.object({\n      page: z.coerce.number().optional(),\n    });\n  }\n\n  // → path parameters (only for segments the path template declares)\n  param() {\n    return z.object({\n      id: z.string().uuid(),\n    });\n  }\n\n  // → response schemas by status code\n  response() {\n    return {\n      201: z.object({ id: z.string(), name: z.string() }),\n      400: { schema: z.object({ error: z.string() }), description: 'Validation error' },\n    };\n  }\n}\n```\n\n### Path Parameters\n\nEvery variable in a route path is documented, whether or not a `param()` validator describes it.\n`@Get('/:id')` on its own emits `id` as a required `string`; a `param()` schema replaces that\ndefault with its own definition. A `param()` field the path template does not mention is dropped —\nOpenAPI has nowhere to put it.\n\n### Routes That Cannot Be Documented\n\n- **`@All` and `@Connect` are skipped.** `all` and `connect` are not OpenAPI Path Item fields, so\n  emitting them produces a spec that fails validation.\n- **Two routes claiming the same path and method throw.** Generation stops and the error names both\n  controllers, rather than letting the second writer silently overwrite the first. With\n  `OpenApiPostProcessor` this surfaces on the first request to the spec endpoint, not at boot.\n- **`operationId` collisions get a numeric suffix.** Two controller classes sharing a name would\n  otherwise produce duplicate ids, which OpenAPI forbids. The first occurrence keeps the bare id.\n\n## @Hidden\n\nHide controllers or individual routes from the spec:\n\n```typescript\n// Hide entire controller\n@Hidden()\n@Controller('/internal')\nexport class InternalController { ... }\n\n// Hide single route\n@Controller('/api')\nexport class ApiController {\n  @Hidden()\n  @Get('/health')\n  healthCheck() {}\n\n  @Get('/users')  // this route IS in the spec\n  listUsers() {}\n}\n```\n\n## Configuration\n\n### OpenApiDecoratorOptions\n\n```typescript\n@OpenApi({\n  info: {\n    title: 'My API', // Required\n    version: '1.0.0', // Required\n    description: 'My app', // Optional\n  },\n  path: '/api/openapi', // Default: '/openapi'\n  ui: 'scalar', // Default: none — 'swagger' (or true), 'scalar', or { provider, configuration }\n  servers: [\n    // Optional\n    { url: 'https://api.example.com', description: 'Production' },\n  ],\n  converters: [\n    // Default: [ZodSchemaConverter]\n    new ZodSchemaConverter(),\n  ],\n})\nexport class AppOpenApi extends OpenApiPostProcessor {}\n```\n\n## API Docs UI\n\nSet `ui` to serve an API documentation page at `{path}/ui`. Both providers load from\nCDN — zero npm dependencies:\n\n| Value                         | UI served                                                                 |\n| ----------------------------- | ------------------------------------------------------------------------- |\n| `true` / `'swagger'`          | Swagger UI (`swagger-ui-dist@5` from unpkg)                               |\n| `'scalar'`                    | Scalar API Reference (`@scalar/api-reference@1` from jsdelivr)            |\n| `{ provider, configuration }` | Either provider, with raw provider configuration merged over the defaults |\n| `false` / unset               | None                                                                      |\n\n`configuration` is passed straight through: for Scalar it lands in\n`Scalar.createApiReference`, for Swagger in `SwaggerUIBundle`. Its keys override the\ndefaults — including `url`, if you want the UI to read a spec from somewhere else.\n\n```typescript\n@OpenApi({\n  info: { title: 'My API', version: '1.0.0' },\n  ui: {\n    provider: 'scalar',\n    configuration: { theme: 'purple', darkMode: true },\n  },\n})\nexport class AppOpenApi extends OpenApiPostProcessor {}\n```\n\nAn unknown provider fails at boot with a clear error instead of serving a broken page.\n\n## OpenApiGenerator (Legacy)\n\nFor manual spec generation without the PostProcessor:\n\n```typescript\nimport { OpenApiGenerator, ZodSchemaConverter } from '@asenajs/asena-openapi';\n\nconst generator = new OpenApiGenerator({\n  info: { title: 'My API', version: '1.0.0' },\n  converters: [new ZodSchemaConverter()],\n});\n\nconst spec = await generator.generate(server.coreContainer.container);\n```\n\nBoth builders share one internal operation builder, so a route documents identically either way.\n\n## Contributing\n\nContributions are welcome! Please follow these guidelines:\n\n1. Maintain test coverage for critical paths\n2. Follow existing code style and linting rules\n3. Test with both Hono and Ergenecore adapters\n\nSubmit a Pull Request on [GitHub](https://github.com/AsenaJs/asena-openapi).\n\n## License\n\nMIT\n\n## Support\n\nIssues or questions? Open an issue on [GitHub](https://github.com/AsenaJs/asena-openapi/issues).\n","readmeFilename":"README.md"}