{"_id":"@apvee/azure-functions-openapi","_rev":"9-0a94e75e652530d033a01a032d72f19a","name":"@apvee/azure-functions-openapi","dist-tags":{"latest":"2.0.0","alpha":"2.0.0-alpha.1"},"versions":{"0.0.1":{"name":"@apvee/azure-functions-openapi","version":"0.0.1","keywords":[],"author":{"url":"Apvee Solutions","name":"Fabio Franzini"},"license":"MIT","_id":"@apvee/azure-functions-openapi@0.0.1","maintainers":[{"name":"fabiofranzini","email":"fabio@apvee.com"}],"dist":{"shasum":"59dce900acba4c844a49e9946160363579f92fa3","tarball":"https://registry.npmjs.org/@apvee/azure-functions-openapi/-/azure-functions-openapi-0.0.1.tgz","fileCount":13,"integrity":"sha512-ns4Y4l+oX+JXeI1599Zbnh2efL4smzG6Y8IBEIbWVrzPE8KSjNg9Q+kvxQ4/b0aV1xOmMUQ9aNlXI3nLrBYFnw==","signatures":[{"sig":"MEUCIEs9BFK4OpgbV8nwGuHQ3oCXrsasg77BKmGJFdhqW57FAiEAxNLpLQT3g4vSecsmabzwHb+T4YhYW9Qhdr6m1RIMDjg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":23596},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"296b3ec060e0a5edca03a3ea9a91c3f20b5ae754","scripts":{"build":"tsc","clean":"rimraf dist"},"_npmUser":{"name":"fabiofranzini","email":"fabio@apvee.com"},"_npmVersion":"10.8.3","description":"An extension for Azure Functions V4 that provides support for exporting OpenAPI spec files from annotated Azure Functions.","directories":{},"_nodeVersion":"18.20.2","dependencies":{"@asteasolutions/zod-to-openapi":"^7.1.1"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^5.0.10","typescript":"^5.6.2","@types/node":"^22.5.4"},"peerDependencies":{"zod":"^3.0.0","@azure/functions":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/azure-functions-openapi_0.0.1_1726775774560_0.6188991588539281","host":"s3://npm-registry-packages"}},"0.0.2":{"name":"@apvee/azure-functions-openapi","version":"0.0.2","keywords":["api","azure","azure-functions","openapi","swagger","types","zod"],"author":{"url":"Apvee Solutions","name":"Fabio Franzini"},"license":"MIT","_id":"@apvee/azure-functions-openapi@0.0.2","maintainers":[{"name":"fabiofranzini","email":"fabio@apvee.com"}],"homepage":"https://github.com/apvee/azure-functions-nodejs-monorepo","bugs":{"url":"https://github.com/apvee/azure-functions-nodejs-monorepo/issues"},"dist":{"shasum":"4b2a4808410166b6a15fe7e361eed2c2c0ba1eeb","tarball":"https://registry.npmjs.org/@apvee/azure-functions-openapi/-/azure-functions-openapi-0.0.2.tgz","fileCount":13,"integrity":"sha512-fX8K8L05yVGBwu797+mzYcHMU6lJfsPOIH4G/U8czFxPfBWIlQJHBp0LrWqOSqpNOtLwpnQUzvlNTq25XmQcuQ==","signatures":[{"sig":"MEQCICWbIbgyJuqog65x6uPkukZ5vQA6K/sl+tMqpEn9YgEZAiBsgIyyrelH1ik3ACz9766YO/MlL1l31QBvlfmG/jbe0w==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":23802},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"eac820690973f37b8a1a7773ac29b88768b244a4","scripts":{"build":"tsc","clean":"rimraf dist"},"_npmUser":{"name":"fabiofranzini","email":"fabio@apvee.com"},"repository":{"url":"git+https://github.com/apvee/azure-functions-nodejs-monorepo.git","type":"git"},"_npmVersion":"10.8.3","description":"An extension for Azure Functions V4 that provides support for exporting OpenAPI spec files from annotated Azure Functions.","directories":{},"_nodeVersion":"18.20.2","dependencies":{"@asteasolutions/zod-to-openapi":"^7.1.1"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^5.0.10","typescript":"^5.6.2","@types/node":"^22.5.4"},"peerDependencies":{"zod":"^3.0.0","@azure/functions":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/azure-functions-openapi_0.0.2_1726776882050_0.1751272793374934","host":"s3://npm-registry-packages"}},"0.0.3":{"name":"@apvee/azure-functions-openapi","version":"0.0.3","keywords":["api","azure","azure-functions","openapi","swagger","types","zod"],"author":{"url":"Apvee Solutions","name":"Fabio Franzini"},"license":"MIT","_id":"@apvee/azure-functions-openapi@0.0.3","maintainers":[{"name":"fabiofranzini","email":"fabio@apvee.com"}],"homepage":"https://github.com/apvee/azure-functions-nodejs-monorepo","bugs":{"url":"https://github.com/apvee/azure-functions-nodejs-monorepo/issues"},"dist":{"shasum":"81837bb5d973575132879764a0750dee300d3794","tarball":"https://registry.npmjs.org/@apvee/azure-functions-openapi/-/azure-functions-openapi-0.0.3.tgz","fileCount":23,"integrity":"sha512-j5lrRO7NRJsHBkn0BPwME5XfHBNG+o5jNTAKvh9NynXg5RRfXHuQPX47QYyzT1oeNX73ZOHPYmMx9mujHD7Now==","signatures":[{"sig":"MEYCIQDOXyjBtBr8/xhb643VpZEZe+AQrxgombhXzLG6fkBsHwIhAIMTlPMuHgZevPD6r3JZSum2oRSpjZM0KB7s6I1Snz5A","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":28589},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"678863df5dcbecfbca4e909ca3ce41af6cf87079","scripts":{"build":"tsc","clean":"rimraf dist"},"_npmUser":{"name":"fabiofranzini","email":"fabio@apvee.com"},"repository":{"url":"git+https://github.com/apvee/azure-functions-nodejs-monorepo.git","type":"git"},"_npmVersion":"10.8.3","description":"An extension for Azure Functions V4 that provides support for exporting OpenAPI spec files from annotated Azure Functions.","directories":{},"_nodeVersion":"18.20.2","dependencies":{"@asteasolutions/zod-to-openapi":"^7.1.1"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^5.0.10","typescript":"^5.6.2","@types/node":"^22.5.4"},"peerDependencies":{"zod":"^3.0.0","@azure/functions":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/azure-functions-openapi_0.0.3_1726780383521_0.9277451559866676","host":"s3://npm-registry-packages"}},"1.0.0":{"name":"@apvee/azure-functions-openapi","version":"1.0.0","keywords":["api","azure","azure-functions","openapi","swagger","types","zod"],"author":{"url":"Apvee Solutions","name":"Fabio Franzini"},"license":"MIT","_id":"@apvee/azure-functions-openapi@1.0.0","maintainers":[{"name":"fabiofranzini","email":"fabio@apvee.com"}],"homepage":"https://github.com/apvee/azure-functions-nodejs-monorepo","bugs":{"url":"https://github.com/apvee/azure-functions-nodejs-monorepo/issues"},"dist":{"shasum":"b8472405bc00efb819b88abc562eb1e5e4c54f20","tarball":"https://registry.npmjs.org/@apvee/azure-functions-openapi/-/azure-functions-openapi-1.0.0.tgz","fileCount":27,"integrity":"sha512-xN05guVQmDcx0TCE4iHEu0xy/loPZ9NGPPE6G6T/lTA8LfkRHDKW5ugkVZWxW33/+1ld49UraIu8vA02x+3B0A==","signatures":[{"sig":"MEUCIQCrShZqRhP9mmUWRULKaw67aUotSKqNX+AiOtOWvtajOwIgdoaPuCsedM5u1d+7bbCVk/nvjOXLgYDrqHe8hURg/UI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":65705},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"f353727b75647b7a73dda3bd1bd770a8c390d477","scripts":{"build":"tsc","clean":"rimraf dist"},"_npmUser":{"name":"fabiofranzini","email":"fabio@apvee.com"},"repository":{"url":"git+https://github.com/apvee/azure-functions-nodejs-monorepo.git","type":"git"},"_npmVersion":"10.8.3","description":"An extension for Azure Functions V4 that provides support for exporting OpenAPI spec files from annotated Azure Functions.","directories":{},"_nodeVersion":"18.20.2","dependencies":{"yaml":"^2.5.1","lodash.camelcase":"^4.3.0","lodash.clonedeep":"^4.5.0","@asteasolutions/zod-to-openapi":"^7.1.1"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^5.0.10","typescript":"^5.6.2","@types/node":"^22.5.4","@types/lodash.camelcase":"^4.3.9","@types/lodash.clonedeep":"^4.5.9"},"peerDependencies":{"zod":"^3.0.0","@azure/functions":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/azure-functions-openapi_1.0.0_1728744163811_0.35762288369268025","host":"s3://npm-registry-packages"}},"1.0.1":{"name":"@apvee/azure-functions-openapi","version":"1.0.1","keywords":["api","azure","azure-functions","openapi","swagger","types","zod"],"author":{"url":"Apvee Solutions","name":"Fabio Franzini"},"license":"MIT","_id":"@apvee/azure-functions-openapi@1.0.1","maintainers":[{"name":"fabiofranzini","email":"fabio@apvee.com"}],"homepage":"https://github.com/apvee/azure-functions-nodejs-monorepo","bugs":{"url":"https://github.com/apvee/azure-functions-nodejs-monorepo/issues"},"dist":{"shasum":"27772493d5234ea44193a3549a78248dd617026e","tarball":"https://registry.npmjs.org/@apvee/azure-functions-openapi/-/azure-functions-openapi-1.0.1.tgz","fileCount":27,"integrity":"sha512-XpZ+K/rsQFQJMwyeW67ra+CkazyzWsdWJLOgwhl/4Vl5th6hYx+QKJqJbQupHSfI5blMlFIg8DnWd/Yj7Ji2pg==","signatures":[{"sig":"MEUCIC0IAp6BW8um2Sxk58YaMCjH7ScYRsCdl627GFRvWXT8AiEA1qx5pGWKB8ehPg4oi1PFJILa3QL9tYtixvCug+Lnn0U=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":65690},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"ba4db10ada171717e5160d635706984378fa6e3a","scripts":{"build":"tsc","clean":"rimraf dist"},"_npmUser":{"name":"fabiofranzini","email":"fabio@apvee.com"},"repository":{"url":"git+https://github.com/apvee/azure-functions-nodejs-monorepo.git","type":"git"},"_npmVersion":"11.0.0","description":"An extension for Azure Functions V4 that provides support for exporting OpenAPI spec files from annotated Azure Functions.","directories":{},"_nodeVersion":"20.18.1","dependencies":{"yaml":"^2.5.1","lodash.camelcase":"^4.3.0","lodash.clonedeep":"^4.5.0","@asteasolutions/zod-to-openapi":"^7.1.1"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^5.0.10","typescript":"^5.6.2","@types/node":"^22.5.4","@types/lodash.camelcase":"^4.3.9","@types/lodash.clonedeep":"^4.5.9"},"peerDependencies":{"zod":"^3.0.0","@azure/functions":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/azure-functions-openapi_1.0.1_1743099591704_0.4820417996989834","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@apvee/azure-functions-openapi","version":"1.0.2","keywords":["api","azure","azure-functions","openapi","swagger","types","zod"],"author":{"url":"Apvee Solutions","name":"Fabio Franzini"},"license":"MIT","_id":"@apvee/azure-functions-openapi@1.0.2","maintainers":[{"name":"fabiofranzini","email":"fabio@apvee.com"}],"homepage":"https://github.com/apvee/azure-functions-nodejs-monorepo","bugs":{"url":"https://github.com/apvee/azure-functions-nodejs-monorepo/issues"},"dist":{"shasum":"c739e608ef44c08a8389b2e6d96c5177f8455bb0","tarball":"https://registry.npmjs.org/@apvee/azure-functions-openapi/-/azure-functions-openapi-1.0.2.tgz","fileCount":27,"integrity":"sha512-EMcbhkuOIsvKrP45awluw6CmhS1CY9pYWDBkG5vd1ucHJ7DLbA5LoDC5KiIKoMJYZQ7axF3m3rZ+cxPPnyvNrQ==","signatures":[{"sig":"MEUCIDjpq4Npeep6qRMAKhDKZ/AiPp25pRczSKMwvm9bj0WAAiEApeZk8yP6ckgIFy79aGbkCWrmdh8m9+QXEHIZob7kjt8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":67308},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"2e8fbc53d1b847ba13ebedd2395991a43d7115aa","scripts":{"build":"tsc","clean":"rimraf dist"},"_npmUser":{"name":"fabiofranzini","email":"fabio@apvee.com"},"repository":{"url":"git+https://github.com/apvee/azure-functions-nodejs-monorepo.git","type":"git"},"_npmVersion":"10.8.2","description":"An extension for Azure Functions V4 that provides support for exporting OpenAPI spec files from annotated Azure Functions.","directories":{},"_nodeVersion":"20.19.4","dependencies":{"yaml":"^2.5.1","lodash.camelcase":"^4.3.0","lodash.clonedeep":"^4.5.0","@asteasolutions/zod-to-openapi":"^7.1.1"},"_hasShrinkwrap":false,"devDependencies":{"rimraf":"^5.0.10","typescript":"^5.6.2","@types/node":"^22.5.4","@types/lodash.camelcase":"^4.3.9","@types/lodash.clonedeep":"^4.5.9"},"peerDependencies":{"zod":"^3.0.0","@azure/functions":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/azure-functions-openapi_1.0.2_1755774912488_0.9463518040974144","host":"s3://npm-registry-packages-npm-production"}},"2.0.0-alpha.0":{"name":"@apvee/azure-functions-openapi","version":"2.0.0-alpha.0","keywords":["api","azure","azure-functions","openapi","swagger","types","zod"],"author":{"url":"Apvee Solutions","name":"Fabio Franzini"},"license":"MIT","_id":"@apvee/azure-functions-openapi@2.0.0-alpha.0","maintainers":[{"name":"fabiofranzini","email":"fabio@apvee.com"}],"homepage":"https://github.com/apvee/azure-functions-nodejs-monorepo","bugs":{"url":"https://github.com/apvee/azure-functions-nodejs-monorepo/issues"},"dist":{"shasum":"4325a144bf4b793b148db6fe863204590849e95c","tarball":"https://registry.npmjs.org/@apvee/azure-functions-openapi/-/azure-functions-openapi-2.0.0-alpha.0.tgz","fileCount":48,"integrity":"sha512-gE3V7ZDqr/AW7Wap8gqqcS/ANTOuf8ghWErAqkLvuQKmddRB6o+DFIOPXyaPmVmtT6wZ08UpIFnG/bkhfvqSHg==","signatures":[{"sig":"MEUCIHUdt35SnJucCv9NeTFvOl4S0uA0tIdnlJHHOwZYMQiMAiEA7Bko9QYP2R4dtYTf91LzRldYemripQczK7B+Kpugunw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1852363},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"9467178300f1f99d4b5c02493a6922bb2256bf6d","scripts":{"build":"npm run clean && tsc","clean":"rimraf dist"},"_npmUser":{"name":"fabiofranzini","email":"fabio@apvee.com"},"repository":{"url":"git+https://github.com/apvee/azure-functions-nodejs-monorepo.git","type":"git"},"_npmVersion":"10.9.2","description":"An extension for Azure Functions V4 that provides support for exporting OpenAPI spec files from annotated Azure Functions.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"yaml":"^2.5.1","swagger-ui-dist":"^5.30.2","lodash.camelcase":"^4.3.0","lodash.clonedeep":"^4.5.0","@asteasolutions/zod-to-openapi":"^8.1.0"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"rimraf":"^5.0.10","typescript":"^5.6.2","@types/node":"^22.5.4","@types/lodash.camelcase":"^4.3.9","@types/lodash.clonedeep":"^4.5.9"},"peerDependencies":{"zod":"^4.0.0","@azure/functions":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/azure-functions-openapi_2.0.0-alpha.0_1762427822130_0.02004889883762373","host":"s3://npm-registry-packages-npm-production"}},"2.0.0-alpha.1":{"name":"@apvee/azure-functions-openapi","version":"2.0.0-alpha.1","keywords":["api","azure","azure-functions","openapi","swagger","types","zod"],"author":{"url":"Apvee Solutions","name":"Fabio Franzini"},"license":"MIT","_id":"@apvee/azure-functions-openapi@2.0.0-alpha.1","maintainers":[{"name":"fabiofranzini","email":"fabio@apvee.com"}],"homepage":"https://github.com/apvee/azure-functions-nodejs-monorepo","bugs":{"url":"https://github.com/apvee/azure-functions-nodejs-monorepo/issues"},"dist":{"shasum":"abeb38eb2869f4759e5ab69d399d2dc101ec2760","tarball":"https://registry.npmjs.org/@apvee/azure-functions-openapi/-/azure-functions-openapi-2.0.0-alpha.1.tgz","fileCount":47,"integrity":"sha512-RP8gTi8f2/AvBNE2TWC7vjOROPcGUPJ2mn2hgiyPIZm7T6OgeV2y/MfYl3408V1/NnN69nBwFCFgAkdghenkzA==","signatures":[{"sig":"MEYCIQDwCP4bK8tym745ctryzdUE+kkZGPIzdGvLSjTns7O1fwIhAPCMsLyupcLxSmSRbo/sH8WjU2ZTw8l0h5eKgCI8Hvov","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":439196},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"51433ee4f1c8c99ab3c067e8c43f96855cf18ce6","scripts":{"build":"npm run clean && tsc","clean":"rimraf dist"},"_npmUser":{"name":"fabiofranzini","email":"fabio@apvee.com"},"repository":{"url":"git+https://github.com/apvee/azure-functions-nodejs-monorepo.git","type":"git"},"_npmVersion":"10.9.2","description":"An extension for Azure Functions V4 that provides support for exporting OpenAPI spec files from annotated Azure Functions.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"yaml":"^2.5.1","swagger-ui-dist":"^5.30.2","lodash.camelcase":"^4.3.0","lodash.clonedeep":"^4.5.0","@asteasolutions/zod-to-openapi":"^8.1.0"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"rimraf":"^5.0.10","typescript":"^5.6.2","@types/node":"^22.5.4","@types/lodash.camelcase":"^4.3.9","@types/lodash.clonedeep":"^4.5.9"},"peerDependencies":{"zod":"^4.0.0","@azure/functions":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/azure-functions-openapi_2.0.0-alpha.1_1762428301889_0.12054219382984632","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@apvee/azure-functions-openapi","version":"2.0.0","repository":{"type":"git","url":"git+https://github.com/apvee/azure-functions-nodejs-monorepo.git","directory":"packages/azure-functions-openapi"},"bugs":{"url":"https://github.com/apvee/azure-functions-nodejs-monorepo/issues"},"homepage":"https://github.com/apvee/azure-functions-nodejs-monorepo/tree/main/packages/azure-functions-openapi#readme","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./register":{"types":"./dist/register.d.ts","default":"./dist/register.js"},"./package.json":"./package.json"},"scripts":{"build":"npm run clean && tsc","clean":"rimraf dist","lint":"eslint \"src/**/*.ts\"","format":"prettier --check \"src/**/*.{ts,md,json}\"","format:write":"prettier --write \"src/**/*.{ts,md,json}\"","test":"vitest run","test:watch":"vitest"},"keywords":["api","azure","azure-functions","openapi","swagger","types","zod"],"author":{"name":"Fabio Franzini","url":"Apvee Solutions"},"license":"MIT","description":"An extension for Azure Functions V4 that provides support for exporting OpenAPI spec files from annotated Azure Functions.","engines":{"node":">=18"},"dependencies":{"@asteasolutions/zod-to-openapi":"^8.1.0","lodash.camelcase":"^4.3.0","lodash.clonedeep":"^4.5.0","openapi3-ts":"^4.5.0","swagger-ui-dist":"^5.30.2","yaml":"^2.8.3"},"peerDependencies":{"@azure/functions":"^4.5.2","zod":"^4.0.0"},"devDependencies":{"@types/lodash.camelcase":"^4.3.9","@types/lodash.clonedeep":"^4.5.9","@types/node":"^22.19.19","rimraf":"^5.0.10","typescript":"^5.6.2","vite":"^5.4.21","vitest":"^3.2.4"},"_id":"@apvee/azure-functions-openapi@2.0.0","gitHead":"94878977a05a2ba353e9a65523d2c3654762ba6f","_nodeVersion":"22.14.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-JBb/VflwnGaXzpL32RjraqdRN1yml/Gu1piRuY44nYhEZbOWKylU6DVCT3at6H3UyoU0vIYtKOWQZ8rTMdwe4w==","shasum":"fc954d1360145272f4f4c398b4b4c5b9d53e57ba","tarball":"https://registry.npmjs.org/@apvee/azure-functions-openapi/-/azure-functions-openapi-2.0.0.tgz","fileCount":108,"unpackedSize":573367,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHjVFlX5VLcUgasagojo/xr23wC+raNw0No3P/ELTcj7AiAO17qUgnQK8YwDiZDwhlzYwS9lwS+PWQerbbKYuqgAeA=="}]},"_npmUser":{"name":"fabiofranzini","email":"fabio@apvee.com"},"directories":{},"maintainers":[{"name":"fabiofranzini","email":"fabio@apvee.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/azure-functions-openapi_2.0.0_1779114536680_0.4653017455897057"},"_hasShrinkwrap":false}},"time":{"created":"2024-09-19T19:56:14.456Z","modified":"2026-05-18T14:28:56.952Z","0.0.1":"2024-09-19T19:56:14.778Z","0.0.2":"2024-09-19T20:14:42.219Z","0.0.3":"2024-09-19T21:13:03.721Z","1.0.0":"2024-10-12T14:42:44.060Z","1.0.1":"2025-03-27T18:19:51.889Z","1.0.2":"2025-08-21T11:15:12.680Z","2.0.0-alpha.0":"2025-11-06T11:17:02.380Z","2.0.0-alpha.1":"2025-11-06T11:25:02.098Z","2.0.0":"2026-05-18T14:28:56.830Z"},"bugs":{"url":"https://github.com/apvee/azure-functions-nodejs-monorepo/issues"},"author":{"name":"Fabio Franzini","url":"Apvee Solutions"},"license":"MIT","homepage":"https://github.com/apvee/azure-functions-nodejs-monorepo/tree/main/packages/azure-functions-openapi#readme","keywords":["api","azure","azure-functions","openapi","swagger","types","zod"],"repository":{"type":"git","url":"git+https://github.com/apvee/azure-functions-nodejs-monorepo.git","directory":"packages/azure-functions-openapi"},"description":"An extension for Azure Functions V4 that provides support for exporting OpenAPI spec files from annotated Azure Functions.","maintainers":[{"name":"fabiofranzini","email":"fabio@apvee.com"}],"readme":"# @apvee/azure-functions-openapi\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n> **Version 2.0** - A complete rewrite with improved TypeScript support, automatic type inference, and enhanced Azure integration.\n\n![Apvee Azure Functions OpenAPI v2](https://raw.githubusercontent.com/apvee/azure-functions-nodejs-monorepo/main/packages/azure-functions-openapi/assets/apvee-azure-functions-openapi-v2.png)\n\n## 📖 Overview\n\n`@apvee/azure-functions-openapi` is a powerful extension for **Azure Functions V4** that automatically generates and serves OpenAPI documentation for your serverless APIs. Built on top of `@asteasolutions/zod-to-openapi` and leveraging **Zod schemas** for validation, it ensures your API is always type-safe, well-documented, and easy to explore.\n\n### What Makes v2.0 Special?\n\nThis major release introduces a **modern, ergonomic API** through TypeScript module augmentation, directly extending the `@azure/functions` app object with intuitive methods. Write less boilerplate, get full type inference, and enjoy seamless integration with Azure Functions runtime.\n\n```typescript\nimport \"@apvee/azure-functions-openapi\";\nimport { app } from \"@azure/functions\";\nimport { z } from \"zod\";\n\n// Setup OpenAPI documentation\napp.openAPISetup({\n  info: { title: \"My API\", version: \"1.0.0\" },\n});\n\n// Register a fully typed endpoint\napp.openAPIPath(\"GetUser\", \"Get user by ID\", {\n  typedHandler: async ({ params, context }) => {\n    // params.id is automatically typed as string!\n    const user = await getUser(params.id);\n    return { jsonBody: user };\n  },\n  methods: [\"GET\"],\n  route: \"users/{id}\",\n  params: z.object({ id: z.string().uuid() }),\n  response: UserSchema,\n});\n```\n\n### Key Capabilities\n\n- 🎯 **Automatic Type Inference** - TypeScript infers all parameter types from your Zod schemas\n- 📝 **Multi-Version OpenAPI** - OpenAPI 3.1.0 by default, with opt-in OpenAPI 3.0.3 and Swagger 2.0 output\n- 🎨 **Integrated Swagger UI** - Beautiful, interactive API documentation out of the box\n- 🔒 **Azure Security Built-in** - Native support for Function Keys, EasyAuth, and Azure AD\n- ✅ **Runtime Validation** - Automatic request/response validation with detailed error messages\n- 🌐 **Webhook Support** - Document callback endpoints with OpenAPI 3.1.0 webhooks\n- 🚀 **Zero Config** - Sensible defaults with full customization when needed\n\n---\n\n## ✨ What's New in v2.0\n\nVersion 2.0 is a complete rewrite that brings significant improvements in developer experience, performance, and Azure integration. Here's what changed:\n\n### 🎨 Modern API with Module Augmentation\n\n**Before (v1.x):**\n\n```typescript\nimport {\n  registerFunction,\n  registerOpenAPIHandler,\n} from \"@apvee/azure-functions-openapi\";\n\nregisterOpenAPIHandler(\"anonymous\", config, \"3.1.0\", \"json\");\nregisterFunction(\"GetUser\", \"Get user\", {\n  /* ... */\n});\n```\n\n**Now (v2.x):**\n\n```typescript\nimport \"@apvee/azure-functions-openapi\";\nimport { app } from \"@azure/functions\";\n\napp.openAPISetup({ info: { title: 'My API', version: '1.0.0' } });\napp.openAPIPath('GetUser', 'Get user', { /* ... */ });\n```\n\nThe new API extends Azure Functions natively, providing better IDE support and a more intuitive developer experience.\n\n### 🎯 Automatic Type Inference with Typed Handlers\n\nThe biggest feature in v2.0 is **automatic type inference**. No more manual type assertions!\n\n```typescript\napp.openAPIPath('UpdateUser', 'Update user information', {\n  typedHandler: async ({ params, body, query, context }) => {\n    // All parameters are automatically typed from your schemas!\n    // params.id: string (from UUID schema)\n    // body.name: string\n    // body.email: string\n    // query.notify: boolean | undefined\n\n    await updateUser(params.id, body);\n    return { jsonBody: { success: true } };\n  },\n  methods: [\"PUT\"],\n  route: \"users/{id}\",\n  params: z.object({ id: z.string().uuid() }),\n  body: z.object({\n    name: z.string().min(1),\n    email: z.string().email(),\n  }),\n  query: z.object({\n    notify: z.boolean().optional(),\n  }),\n});\n```\n\n### 🔒 Enhanced Azure Security Support\n\nv2.0 introduces native support for multiple Azure authentication methods:\n\n- **Azure Function Keys** - Built-in support for function, admin, and anonymous auth levels\n- **Azure EasyAuth** - Integration with App Service Authentication (AAD, Google, Facebook, GitHub, etc.)\n- **Azure AD Bearer Token** - Manual JWT validation for Microsoft Entra ID\n- **Azure AD Client Credentials** - Service-to-service authentication with app roles\n- **Custom API Keys** - Flexible header/query/cookie-based authentication\n- **Safe host resolution** - `servers` is never derived from the request `Host` header unless you explicitly opt in with `trustHostHeader`\n\n```typescript\n// Azure EasyAuth example\nconst easyAuth = app.openAPIEasyAuth('aad');\n\napp.openAPIPath('GetProfile', 'Get user profile', {\n  handler: getProfileHandler,\n  methods: [\"GET\"],\n  route: \"profile\",\n  authLevel: \"anonymous\", // Required for EasyAuth\n  security: [easyAuth],\n  response: ProfileSchema,\n});\n```\n\n### 📦 Local Swagger UI Assets\n\nv2.0 serves Swagger UI assets **locally from node_modules** instead of loading from CDN:\n\n- ✅ **Better Performance** - No external dependencies, faster load times\n- ✅ **Offline Support** - Works without internet connection\n- ✅ **Security** - No third-party CDN risks\n- ✅ **Monorepo Support** - Automatically detects local or root node_modules\n- ✅ **Caching** - Built-in ETag support for optimal caching\n\n### 🔄 Zod 4 Compatibility\n\nFully aligned with **Zod 4.x**, ensuring compatibility with the latest validation features and improvements:\n\n```json\n{\n  \"peerDependencies\": {\n    \"@azure/functions\": \"^4.5.2\",\n    \"zod\": \"^4.0.0\"\n  }\n}\n```\n\n### 🌐 OpenAPI 3.1.0 Webhook Support\n\nDocument callback endpoints that your API calls using the new **webhooks** feature:\n\n```typescript\napp.openAPIWebhook('OrderCreated', 'Notify when order is created', {\n  typedHandler: async ({ body, context }) => {\n    context.log(`Webhook received: Order ${body.orderId}`);\n    return { jsonBody: { received: true } };\n  },\n  methods: [\"POST\"],\n  body: OrderEventSchema,\n  responses: [{ httpCode: 200, description: \"Success\" }],\n});\n```\n\n### ⚡ Simplified Configuration\n\nSetup is now **much simpler** with a single `openAPISetup()` call:\n\n```typescript\n// Only OpenAPI 3.1.0 in JSON/YAML is emitted by default.\n// Add 3.0.3 or 2.0 only when your tooling needs them.\napp.openAPISetup({\n  info: { title: 'My API', version: '1.0.0' },\n  routePrefix: 'api',\n  versions: ['3.1.0', '3.0.3', '2.0'], // Optional, defaults to ['3.1.0']\n  formats: ['json', 'yaml'],           // Optional, defaults to ['json', 'yaml']\n  servers: [{ url: 'https://api.example.com' }],\n  trustHostHeader: false,               // Optional, defaults to false\n  swaggerUI: { \n    enabled: true,                     // Optional, defaults to true\n    route: 'docs'                      // Optional, defaults to 'swagger-ui'\n  }\n});\n```\n\n### 🛠️ New Utility Functions\n\nExport powerful utilities for manual validation and parsing:\n\n- `parseRouteParams()` - Validate route parameters\n- `parseQueryParams()` - Validate query strings\n- `parseBody()` - Validate request body\n- `parseHeaders()` - Validate headers\n- `parseEasyAuthPrincipal()` - Decode Azure EasyAuth user info\n- `extractFunctionKey()` - Extract function keys from requests\n- `createTypedHandler()` - Create typed handlers programmatically\n\n### 📋 Breaking Changes Summary\n\nWhile v2.0 brings many improvements, some APIs have changed. See the [Migration Guide](#-migration-guide-v1-v2) below for detailed migration steps.\n\n**Removed APIs:**\n- `registerOpenAPIHandler()` → Use `app.openAPISetup()`\n- `registerSwaggerUIHandler()` → Use `app.openAPISetup()`\n- `registerFunction()` → Use `app.openAPIPath()` or `app.openAPIWebhook()`\n- `registerApiKeySecuritySchema()` → Use `app.openAPIKeySecurity()`\n- `registerTypeSchema()` → Use `app.openAPISchema()`\n\n**Private APIs:**\n\n- `convertHttpRequestParamsToObject()` - Now internal, use `parseRouteParams()` instead\n- `convertURLSearchParamsToObject()` - Now internal, use `parseQueryParams()` instead\n\n---\n\n## 🎯 Why Document APIs with OpenAPI?\n\nOpenAPI documentation is more than just nice-to-have—it's a fundamental part of modern API development that delivers tangible benefits to both API producers and consumers.\n\n### Always Up-to-Date Documentation\n\nBy integrating OpenAPI documentation directly into your codebase, you ensure that your API documentation is **always in sync** with the implementation. No more outdated docs, no more discrepancies between what's documented and what's actually deployed.\n\n```typescript\n// Documentation is generated from the same schemas used for validation\napp.openAPIPath('CreateUser', 'Create a new user', {\n  typedHandler: async ({ body, context }) => {\n    // This schema validates the request AND generates the documentation\n    return { status: 201, jsonBody: await createUser(body) };\n  },\n  methods: [\"POST\"],\n  route: \"users\",\n  body: z.object({\n    name: z.string().min(1).describe(\"User full name\"),\n    email: z.string().email().describe(\"Valid email address\"),\n    age: z.number().int().positive().optional().describe(\"User age\"),\n  }),\n  response: UserSchema,\n});\n```\n\n### Auto-Generated Client Libraries\n\nOpenAPI specifications can be used to automatically generate **type-safe client libraries** for various programming languages:\n\n- **[Kiota](https://github.com/microsoft/kiota)** - Microsoft's OpenAPI-based API client generator\n- **[OpenAPI Generator](https://openapi-generator.tech/)** - Supports 50+ languages\n- **[oazapfts](https://github.com/oazapfts/oazapfts)** - TypeScript-first OpenAPI client generator\n\nThis saves development time and ensures API consumers have reliable, type-safe SDKs without manual coding.\n\n### Enhanced Developer Experience\n\nComprehensive and accurate API documentation makes it easier for developers to understand and use your API:\n\n- **Interactive Testing** - Swagger UI allows developers to test endpoints directly from documentation\n- **Type Definitions** - Clear request/response schemas with examples\n- **Authentication Details** - Security requirements clearly documented\n- **Validation Rules** - Understand constraints before making requests\n\n### Improved API Testing\n\nOpenAPI documentation enables:\n\n- **Contract Testing** - Validate that implementation matches the spec\n- **Mock Servers** - Generate mock APIs for frontend development\n- **Test Case Generation** - Automatically create test scenarios from schemas\n- **Response Validation** - Ensure API responses match documented structure\n\n### Standardization and Interoperability\n\nOpenAPI is a **widely adopted industry standard** supported by thousands of tools and platforms:\n\n- API Gateways (Azure API Management, Kong, AWS API Gateway)\n- Monitoring Tools (Postman, Insomnia, Paw)\n- Testing Frameworks (Dredd, Schemathesis, REST Assured)\n- Documentation Platforms (Redoc, Stoplight, Readme.io)\n\nBy documenting with OpenAPI, your API becomes easily integrable with the entire ecosystem.\n\n---\n\n## 🚀 Key Features\n\n### Module Augmentation API\n\nExtends `@azure/functions` natively with intuitive OpenAPI methods. No separate imports needed—just extend the app object you already use.\n\n```typescript\nimport \"@apvee/azure-functions-openapi\";\nimport { app } from \"@azure/functions\";\n\n// All app.openAPIXxx() methods are now available\napp.openAPISetup({ /* config */ });\napp.openAPIPath('MyEndpoint', 'Description', { /* config */ });\n```\n\n### Automatic Type Inference\n\nTypeScript automatically infers parameter types from Zod schemas. No manual type assertions, no type mismatches.\n\n```typescript\napp.openAPIPath('UpdateTodo', 'Update a todo item', {\n  typedHandler: async ({ params, body }) => {\n    // params.id is automatically string (from UUID schema)\n    // body.title is automatically string\n    // body.completed is automatically boolean\n    const updated = { id: params.id, ...body };\n    return { jsonBody: updated };\n  },\n  methods: [\"PUT\"],\n  route: \"todos/{id}\",\n  params: z.object({ id: z.string().uuid() }),\n  body: z.object({\n    title: z.string(),\n    completed: z.boolean(),\n  }),\n});\n```\n\n### Multi-Version OpenAPI Support\n\nGenerate OpenAPI specifications in multiple versions and formats when your tooling needs them. By default, the library emits OpenAPI 3.1.0 in JSON and YAML only.\n\n- **OpenAPI 3.1.0** - Latest spec with full JSON Schema support and webhooks\n- **OpenAPI 3.0.3** - Widely supported by most tools\n- **OpenAPI 2.0 (Swagger)** - Legacy support for older tools\n\nAdd `versions: [\"3.1.0\", \"3.0.3\", \"2.0\"]` to generate all supported versions simultaneously. Export in **JSON** or **YAML** format to suit your needs.\n\n### Integrated Swagger UI\n\nBeautiful, interactive API documentation served automatically:\n\n```typescript\napp.openAPISetup({\n  info: { title: 'My API', version: '1.0.0' },\n  swaggerUI: {\n    enabled: true,\n    route: \"docs\", // Access at: http://localhost:7071/docs\n  },\n});\n```\n\nSwagger UI assets are served **locally** for better performance, security, and offline support.\n\n### Type-Safe Request Validation\n\nLeverage Zod schemas to validate all aspects of HTTP requests:\n\n- **Route Parameters** - `/users/{id}` with UUID validation\n- **Query Strings** - Pagination, filtering, sorting with type coercion\n- **Request Body** - JSON payloads with nested object validation\n- **Headers** - Custom headers like API keys or tokens\n\nValidation errors automatically return **400 Bad Request** with detailed error messages.\n\n### Comprehensive Azure Security Support\n\nNative integration with Azure authentication mechanisms:\n\n| Security Method        | Use Case                    | Example                       |\n| ---------------------- | --------------------------- | ----------------------------- |\n| **Function Keys**      | Azure native key-based auth | API keys managed by Azure     |\n| **EasyAuth**           | App Service Authentication  | AAD, Google, Facebook, GitHub |\n| **Azure AD Bearer**    | Manual JWT validation       | Entra ID token validation     |\n| **Client Credentials** | Service-to-service auth     | Daemon apps, background jobs  |\n| **Custom API Keys**    | User-implemented auth       | Header/query/cookie keys      |\n\n```typescript\n// Example: Azure AD Bearer Token\nconst adAuth = app.openAPIAzureADBearer({\n  name: 'AzureAD',\n  scopes: ['User.Read', 'Mail.Send']\n});\n\napp.openAPIPath('SendEmail', 'Send email on behalf of user', {\n  handler: sendEmailHandler,\n  methods: [\"POST\"],\n  route: \"send-email\",\n  security: [adAuth],\n  body: EmailSchema,\n});\n```\n\n### OpenAPI 3.1.0 Webhooks\n\nDocument callback endpoints that your API calls using the webhooks feature:\n\n```typescript\napp.openAPIWebhook('PaymentCompleted', 'Notify when payment is completed', {\n  typedHandler: async ({ body, context }) => {\n    context.log(`Payment ${body.paymentId} completed`);\n    return { jsonBody: { acknowledged: true } };\n  },\n  methods: [\"POST\"],\n  body: PaymentEventSchema,\n});\n```\n\n### Advanced Response Configuration\n\nSupport for complex response scenarios:\n\n- **Multiple Status Codes** - Document 200, 201, 400, 404, 500, etc.\n- **Multiple Content Types** - JSON, XML, PDF, CSV in the same endpoint\n- **Response Headers** - Rate limits, pagination info, custom headers\n- **Detailed Descriptions** - Clear documentation for each response\n\n```typescript\napp.openAPIPath('GetReport', 'Get report in multiple formats', {\n  handler: getReportHandler,\n  methods: [\"GET\"],\n  route: \"reports/{id}\",\n  params: z.object({ id: z.string() }),\n  responses: [\n    {\n      httpCode: 200,\n      description: \"Report retrieved successfully\",\n      content: [\n        { mediaType: \"application/json\", schema: JsonReportSchema },\n        { mediaType: \"application/pdf\", schema: z.instanceof(Buffer) },\n        { mediaType: \"text/csv\", schema: z.string() },\n      ],\n    },\n    {\n      httpCode: 404,\n      description: \"Report not found\",\n      schema: ErrorSchema,\n    },\n  ],\n});\n```\n\n### Rich Utility Functions\n\nExport powerful utilities for advanced use cases:\n\n```typescript\nimport {\n  parseRouteParams,\n  parseQueryParams,\n  parseBody,\n  parseHeaders,\n  parseEasyAuthPrincipal,\n  extractFunctionKey,\n  createTypedHandler,\n  ValidationError,\n} from \"@apvee/azure-functions-openapi\";\n```\n\n### Zero Configuration Defaults\n\nGet started quickly with sensible defaults, customize when needed:\n\n```typescript\n// Minimal setup - generates OpenAPI 3.1.0 in JSON/YAML + Swagger UI\napp.openAPISetup({\n  info: { title: 'My API', version: '1.0.0' }\n});\n\n// Or fully customize everything\napp.openAPISetup({\n  info: { /* ... */ },\n  routePrefix: 'api',\n  versions: ['3.1.0', '3.0.3', '2.0'],\n  formats: ['json', 'yaml'],\n  authLevel: 'anonymous',\n  security: [globalSecurityScheme],\n  servers: [{ url: \"https://api.example.com\" }],\n  tags: [{ name: \"Users\", description: \"User management\" }],\n  swaggerUI: {\n    enabled: true,\n    route: \"docs\",\n    authLevel: \"anonymous\",\n  },\n});\n```\n\n---\n\n## 📦 Installation\n\nInstall the package using npm, yarn, or pnpm:\n\n```bash\nnpm install @apvee/azure-functions-openapi\n```\n\n### Peer Dependencies\n\nThis library requires the following peer dependencies:\n\n```bash\nnpm install @azure/functions zod\n```\n\n### Version Compatibility\n\n| Package                          | Version    | Notes                    |\n| -------------------------------- | ---------- | ------------------------ |\n| `@apvee/azure-functions-openapi` | `^2.0.0`   | This library             |\n| `@azure/functions`               | `^4.5.2`   | Azure Functions runtime  |\n| `zod`                            | `^4.0.0`   | Schema validation        |\n| Node.js                          | `>=18.0.0` | Recommended: Node 20 LTS |\n\n### Complete Installation\n\nFor a new Azure Functions project with TypeScript:\n\n```bash\n# Create a new Azure Functions project\nfunc init my-api --typescript\n\n# Navigate to project\ncd my-api\n\n# Install dependencies\nnpm install @azure/functions zod\nnpm install @apvee/azure-functions-openapi\n\n# Install dev dependencies\nnpm install -D typescript @types/node\n```\n\n### Verification\n\nVerify the installation by importing the package:\n\n```typescript\nimport \"@apvee/azure-functions-openapi\";\nimport { app } from \"@azure/functions\";\nimport { z } from \"zod\";\n\nconsole.log(\"✅ Installation successful!\");\n```\n\n---\n\n## 🚀 Quick Start\n\nGet your first OpenAPI-documented Azure Function running in minutes.\n\n### Step 1: Setup OpenAPI\n\nCreate or edit your `src/index.ts` file:\n\n```typescript\nimport \"@apvee/azure-functions-openapi\";\nimport { app } from \"@azure/functions\";\n\n// Configure OpenAPI documentation\napp.openAPISetup({\n  info: {\n    title: \"My First API\",\n    version: \"1.0.0\",\n    description: \"A simple API with OpenAPI documentation\",\n  },\n});\n```\n\n### Step 2: Create Your First Endpoint\n\nAdd a simple GET endpoint:\n\n```typescript\nimport { z } from \"zod\";\n\n// Define response schema\nconst GreetingSchema = z.object({\n  message: z.string(),\n  timestamp: z.string(),\n});\n\n// Register endpoint with OpenAPI documentation\napp.openAPIPath('GetGreeting', 'Get a greeting message', {\n  typedHandler: async ({ query, context }) => {\n    const name = query.name || \"World\";\n\n    context.log(`Greeting requested for: ${name}`);\n\n    return {\n      jsonBody: {\n        message: `Hello, ${name}!`,\n        timestamp: new Date().toISOString(),\n      },\n    };\n  },\n  methods: [\"GET\"],\n  route: \"greet\",\n  query: z.object({\n    name: z.string().optional().describe(\"Name to greet\"),\n  }),\n  response: GreetingSchema,\n});\n```\n\n### Step 3: Run Your Function\n\nStart the Azure Functions runtime:\n\n```bash\nnpm start\n# or\nfunc start\n```\n\n### Step 4: Access Your API Documentation\n\nOpen your browser and navigate to:\n\n- **Swagger UI**: http://localhost:7071/swagger-ui\n- **OpenAPI JSON**: http://localhost:7071/api/openapi/3.1.0.json\n- **OpenAPI YAML**: http://localhost:7071/api/openapi/3.1.0.yaml\n\n### Step 5: Test Your Endpoint\n\nTry your new endpoint:\n\n```bash\n# Without name parameter\ncurl http://localhost:7071/api/greet\n\n# With name parameter\ncurl \"http://localhost:7071/api/greet?name=Azure\"\n```\n\n**Response:**\n\n```json\n{\n  \"message\": \"Hello, Azure!\",\n  \"timestamp\": \"2025-11-06T12:34:56.789Z\"\n}\n```\n\n### Complete Quick Start Example\n\nHere's the full `src/index.ts` file:\n\n```typescript\nimport \"@apvee/azure-functions-openapi\";\nimport { app } from \"@azure/functions\";\nimport { z } from \"zod\";\n\n// Setup OpenAPI\napp.openAPISetup({\n  info: {\n    title: \"My First API\",\n    version: \"1.0.0\",\n    description: \"A simple API with OpenAPI documentation\",\n    contact: {\n      name: \"API Support\",\n      email: \"support@example.com\",\n    },\n  },\n  tags: [{ name: \"Greetings\", description: \"Greeting endpoints\" }],\n});\n\n// Define schemas\nconst GreetingSchema = z.object({\n  message: z.string(),\n  timestamp: z.string(),\n});\n\n// Register endpoint\napp.openAPIPath('GetGreeting', 'Get a personalized greeting', {\n  typedHandler: async ({ query, context }) => {\n    const name = query.name || \"World\";\n    context.log(`Greeting requested for: ${name}`);\n\n    return {\n      jsonBody: {\n        message: `Hello, ${name}!`,\n        timestamp: new Date().toISOString(),\n      },\n    };\n  },\n  methods: [\"GET\"],\n  route: \"greet\",\n  tags: [\"Greetings\"],\n  query: z.object({\n    name: z.string().optional().describe(\"Name to greet (default: World)\"),\n  }),\n  response: GreetingSchema,\n});\n```\n\n🎉 **Congratulations!** You now have a fully documented API with automatic type inference, request validation, and interactive Swagger UI.\n\n---\n\n## 📘 Core Concepts\n\n### Setup OpenAPI Documentation\n\nThe `app.openAPISetup()` method is the entry point for configuring OpenAPI documentation. Call it once in your `src/index.ts` file before registering any endpoints.\n\n#### Basic Setup\n\nMinimal configuration with defaults:\n\n```typescript\nimport \"@apvee/azure-functions-openapi\";\nimport { app } from \"@azure/functions\";\n\napp.openAPISetup({\n  info: {\n    title: \"My API\",\n    version: \"1.0.0\",\n  },\n});\n```\n\nThis generates:\n\n- OpenAPI 3.1.0 in JSON and YAML formats\n- Swagger UI at `/swagger-ui`\n- Documents accessible at `/api/openapi/3.1.0.json` and `/api/openapi/3.1.0.yaml`\n\n#### Complete Configuration\n\nFull example with all available options:\n\n```typescript\napp.openAPISetup({\n  // Required: API metadata\n  info: {\n    title: \"Todo API\",\n    version: \"2.0.0\",\n    description: \"A comprehensive todo management API\",\n    termsOfService: \"https://example.com/terms\",\n    contact: {\n      name: \"API Support Team\",\n      email: \"api-support@example.com\",\n      url: \"https://example.com/support\",\n    },\n    license: {\n      name: \"MIT\",\n      url: \"https://opensource.org/licenses/MIT\",\n    },\n  },\n\n  // Optional: Server configurations\n  // Recommended in production. When omitted, `servers` is not derived from\n  // the request Host header unless `trustHostHeader` is explicitly enabled.\n  servers: [\n    {\n      url: \"https://api.example.com\",\n      description: \"Production server\",\n    },\n    {\n      url: \"https://staging-api.example.com\",\n      description: \"Staging server\",\n    },\n    {\n      url: \"http://localhost:7071\",\n      description: \"Local development\",\n    },\n  ],\n\n  // Optional: Global security requirements\n  security: [\n    { BearerAuth: [] }, // Apply to all endpoints by default\n  ],\n\n  // Optional: External documentation\n  externalDocs: {\n    description: \"Full API Documentation\",\n    url: \"https://docs.example.com/api\",\n  },\n\n  // Optional: Tags for organizing endpoints\n  tags: [\n    {\n      name: \"Todos\",\n      description: \"Todo item operations\",\n      externalDocs: {\n        description: \"Todo guide\",\n        url: \"https://docs.example.com/todos\",\n      },\n    },\n    {\n      name: \"Users\",\n      description: \"User management operations\",\n    },\n  ],\n\n  // Optional: Azure Functions route prefix (default: 'api')\n  routePrefix: \"api\",\n\n  // Optional: Authorization level for OpenAPI endpoints (default: 'anonymous')\n  authLevel: \"anonymous\",\n\n  // Optional: OpenAPI versions to generate (default: ['3.1.0'])\n  versions: [\"3.1.0\", \"3.0.3\", \"2.0\"],\n\n  // Optional: Output formats (default: ['json', 'yaml'])\n  formats: [\"json\", \"yaml\"],\n\n  // Optional: trust the incoming Host header for generated `servers`\n  // (default: false). Prefer explicit `servers` in production.\n  trustHostHeader: false,\n\n  // Optional: host allowlist used only when `trustHostHeader: true`\n  trustedHosts: [\"api.example.com\", \"staging-api.example.com\"],\n\n  // Optional: Swagger UI configuration\n  swaggerUI: {\n    enabled: true, // default: true\n    route: \"docs\", // default: 'swagger-ui'\n    authLevel: \"anonymous\", // default: same as main authLevel\n  },\n});\n```\n\n#### Configuration Options Reference\n\n| Option                | Type                                   | Default                  | Description                                      |\n| --------------------- | -------------------------------------- | ------------------------ | ------------------------------------------------ |\n| `info`                | `InfoObject`                           | **Required**             | API metadata (title, version, description, etc.) |\n| `servers`             | `ServerObject[]`                       | `undefined`              | Server URLs for different environments           |\n| `security`            | `SecurityRequirementObject[]`          | `undefined`              | Global security requirements                     |\n| `externalDocs`        | `ExternalDocumentationObject`          | `undefined`              | Link to external documentation                   |\n| `tags`                | `TagObject[]`                          | `undefined`              | Tags for organizing endpoints                    |\n| `routePrefix`         | `string`                               | `'api'`                  | Azure Functions route prefix                     |\n| `authLevel`           | `'anonymous' \\| 'function' \\| 'admin'` | `'anonymous'`            | Auth level for OpenAPI/Swagger endpoints         |\n| `versions`            | `Array<'2.0' \\| '3.0.3' \\| '3.1.0'>`   | `['3.1.0']`              | OpenAPI versions to generate                     |\n| `formats`             | `Array<'json' \\| 'yaml'>`              | `['json', 'yaml']`       | Output formats                                   |\n| `trustHostHeader`     | `boolean`                              | `false`                  | Derive `servers` from the request `Host` header only when explicitly enabled |\n| `trustedHosts`        | `string[]`                             | `[]`                     | Hostname allowlist used when `trustHostHeader` is enabled |\n| `swaggerUI.enabled`   | `boolean`                              | `true`                   | Enable/disable Swagger UI                        |\n| `swaggerUI.route`     | `string`                               | `'swagger-ui'`           | Swagger UI route                                 |\n| `swaggerUI.authLevel` | `'anonymous' \\| 'function' \\| 'admin'` | Same as main `authLevel` | Auth level for Swagger UI                        |\n\n#### Generated Endpoints\n\nBased on your configuration, the following endpoints are automatically created. With the default `versions: [\"3.1.0\"]`, only the 3.1.0 document routes are registered; 3.0.3 and 2.0 routes are registered only when you add those versions.\n\n**OpenAPI Documents:**\n\n```\nGET /{routePrefix}/openapi/3.1.0.json\nGET /{routePrefix}/openapi/3.1.0.yaml\nGET /{routePrefix}/openapi/3.0.3.json\nGET /{routePrefix}/openapi/3.0.3.yaml\nGET /{routePrefix}/openapi/2.0.json\nGET /{routePrefix}/openapi/2.0.yaml\n```\n\n**Swagger UI:**\n\n```\nGET /{swaggerUI.route}\nGET /{swaggerUI.route}/assets/{file}\n```\n\n#### Example: Multiple Environments\n\nConfigure different servers for different deployment stages:\n\n```typescript\napp.openAPISetup({\n  info: {\n    title: \"Multi-Environment API\",\n    version: \"1.0.0\",\n  },\n  servers: [\n    {\n      url: \"https://{environment}.api.example.com\",\n      description: \"Environment-based server\",\n      variables: {\n        environment: {\n          default: \"prod\",\n          enum: [\"prod\", \"staging\", \"dev\"],\n          description: \"Environment name\",\n        },\n      },\n    },\n  ],\n});\n```\n\n#### Example: Secured Documentation\n\nRequire authentication to view OpenAPI docs and Swagger UI:\n\n```typescript\napp.openAPISetup({\n  info: {\n    title: \"Secured API\",\n    version: \"1.0.0\",\n  },\n  authLevel: \"function\", // Require function key\n  swaggerUI: {\n    enabled: true,\n    route: \"docs\",\n    authLevel: \"admin\", // Require admin key for Swagger UI\n  },\n});\n```\n\nAccess requires a key:\n\n```bash\n# Access OpenAPI with function key\ncurl \"http://localhost:7071/api/openapi/3.1.0.json?code=YOUR_FUNCTION_KEY\"\n\n# Access Swagger UI with admin key\ncurl \"http://localhost:7071/docs?code=YOUR_ADMIN_KEY\"\n```\n\n---\n\n### Registering HTTP Endpoints\n\nThe `app.openAPIPath()` method registers HTTP endpoints with OpenAPI documentation. It replaces the traditional `app.http()` method and automatically handles both Azure Functions registration and OpenAPI documentation generation.\n\n#### Basic Endpoint\n\nSimple GET endpoint without parameters:\n\n```typescript\nimport { z } from \"zod\";\n\nconst StatusSchema = z.object({\n  status: z.string(),\n  timestamp: z.string(),\n  version: z.string(),\n});\n\napp.openAPIPath('GetStatus', 'Get API status', {\n  handler: async (request, context) => {\n    return {\n      jsonBody: {\n        status: \"healthy\",\n        timestamp: new Date().toISOString(),\n        version: \"1.0.0\",\n      },\n    };\n  },\n  methods: [\"GET\"],\n  route: \"status\",\n  response: StatusSchema,\n});\n```\n\n#### Endpoint with Path Parameters\n\nExtract values from the URL path:\n\n```typescript\nconst UserSchema = z.object({\n  id: z.string(),\n  name: z.string(),\n  email: z.string(),\n});\n\napp.openAPIPath('GetUser', 'Get user by ID', {\n  typedHandler: async ({ params, context }) => {\n    // params.id is automatically typed as string and validated as UUID\n    context.log(`Fetching user: ${params.id}`);\n\n    const user = await getUserById(params.id);\n    return { jsonBody: user };\n  },\n  methods: [\"GET\"],\n  route: \"users/{id}\",\n  params: z.object({\n    id: z.string().uuid().describe(\"User unique identifier\"),\n  }),\n  response: UserSchema,\n});\n```\n\n#### Endpoint with Query Parameters\n\nHandle query strings with validation:\n\n```typescript\nconst TodoListSchema = z.object({\n  todos: z.array(TodoSchema),\n  total: z.number(),\n  page: z.number(),\n  pageSize: z.number(),\n});\n\napp.openAPIPath('ListTodos', 'List todos with pagination', {\n  typedHandler: async ({ query, context }) => {\n    // query parameters are automatically typed and coerced\n    const page = query.page || 1;\n    const pageSize = query.pageSize || 10;\n    const status = query.status;\n\n    const todos = await getTodos({ page, pageSize, status });\n\n    return {\n      jsonBody: {\n        todos,\n        total: todos.length,\n        page,\n        pageSize,\n      },\n    };\n  },\n  methods: [\"GET\"],\n  route: \"todos\",\n  query: z.object({\n    page: z.coerce.number().int().positive().default(1).describe(\"Page number\"),\n    pageSize: z.coerce\n      .number()\n      .int()\n      .positive()\n      .max(100)\n      .default(10)\n      .describe(\"Items per page\"),\n    status: z\n      .enum([\"pending\", \"completed\", \"all\"])\n      .optional()\n      .describe(\"Filter by status\"),\n  }),\n  response: TodoListSchema,\n});\n```\n\n#### Endpoint with Request Body\n\nPOST/PUT/PATCH endpoints with JSON body:\n\n```typescript\nconst CreateTodoSchema = z.object({\n  title: z.string().min(1).max(200).describe(\"Todo title\"),\n  description: z.string().optional().describe(\"Todo description\"),\n  dueDate: z.string().datetime().optional().describe(\"Due date in ISO format\"),\n});\n\nconst TodoSchema = z.object({\n  id: z.string().uuid(),\n  title: z.string(),\n  description: z.string().optional(),\n  dueDate: z.string().optional(),\n  completed: z.boolean(),\n  createdAt: z.string(),\n});\n\napp.openAPIPath('CreateTodo', 'Create a new todo', {\n  typedHandler: async ({ body, context }) => {\n    // body is automatically typed and validated\n    context.log(`Creating todo: ${body.title}`);\n\n    const newTodo = await createTodo(body);\n\n    return {\n      status: 201,\n      jsonBody: newTodo,\n    };\n  },\n  methods: [\"POST\"],\n  route: \"todos\",\n  body: CreateTodoSchema,\n  response: TodoSchema,\n});\n```\n\n#### Endpoint with Multiple HTTP Methods\n\nSupport multiple methods on the same route:\n\n```typescript\napp.openAPIPath('ManageTodo', 'Manage todo item', {\n  typedHandler: async ({ request, params, body, context }) => {\n    const method = request.method;\n    const todoId = params.id;\n\n    switch (method) {\n      case \"GET\":\n        return { jsonBody: await getTodo(todoId) };\n      case \"PUT\":\n        return { jsonBody: await updateTodo(todoId, body) };\n      case \"DELETE\":\n        await deleteTodo(todoId);\n        return { status: 204 };\n      default:\n        return { status: 405, body: \"Method not allowed\" };\n    }\n  },\n  methods: [\"GET\", \"PUT\", \"DELETE\"],\n  route: \"todos/{id}\",\n  params: z.object({ id: z.string().uuid() }),\n  body: UpdateTodoSchema,\n  responses: [\n    { httpCode: 200, schema: TodoSchema, description: \"Todo retrieved\" },\n    { httpCode: 204, description: \"Todo deleted\" },\n  ],\n});\n```\n\n#### Endpoint with Custom Headers\n\nValidate custom request headers:\n\n```typescript\napp.openAPIPath('SecureEndpoint', 'Endpoint with custom API key', {\n  typedHandler: async ({ headers, context }) => {\n    // headers are automatically typed and validated\n    const apiKey = headers[\"x-api-key\"];\n\n    context.log(`Request with API key: ${apiKey.substring(0, 8)}...`);\n\n    return { jsonBody: { authenticated: true } };\n  },\n  methods: [\"GET\"],\n  route: \"secure/data\",\n  headers: z.object({\n    \"x-api-key\": z.string().min(32).describe(\"API key for authentication\"),\n    \"x-request-id\": z\n      .string()\n      .uuid()\n      .optional()\n      .describe(\"Optional request tracking ID\"),\n  }),\n  response: z.object({ authenticated: z.boolean() }),\n});\n```\n\n#### Endpoint with Multiple Responses\n\nDocument different response codes:\n\n```typescript\nconst ErrorSchema = z.object({\n  error: z.string(),\n  code: z.string(),\n  details: z.any().optional(),\n});\n\napp.openAPIPath('UpdateTodo', 'Update an existing todo', {\n  typedHandler: async ({ params, body, context }) => {\n    try {\n      const todo = await getTodo(params.id);\n\n      if (!todo) {\n        return {\n          status: 404,\n          jsonBody: {\n            error: \"Todo not found\",\n            code: \"TODO_NOT_FOUND\",\n          },\n        };\n      }\n\n      const updated = await updateTodo(params.id, body);\n\n      return {\n        status: 200,\n        jsonBody: updated,\n      };\n    } catch (error) {\n      return {\n        status: 500,\n        jsonBody: {\n          error: \"Internal server error\",\n          code: \"INTERNAL_ERROR\",\n          details: error.message,\n        },\n      };\n    }\n  },\n  methods: [\"PUT\"],\n  route: \"todos/{id}\",\n  params: z.object({ id: z.string().uuid() }),\n  body: UpdateTodoSchema,\n  responses: [\n    {\n      httpCode: 200,\n      description: \"Todo updated successfully\",\n      schema: TodoSchema,\n    },\n    {\n      httpCode: 404,\n      description: \"Todo not found\",\n      schema: ErrorSchema,\n    },\n    {\n      httpCode: 500,\n      description: \"Internal server error\",\n      schema: ErrorSchema,\n    },\n  ],\n});\n```\n\n#### Configuration Options\n\n| Option         | Type                                   | Required                           | Description                               |\n| -------------- | -------------------------------------- | ---------------------------------- | ----------------------------------------- |\n| `handler`      | `HttpHandler`                          | One of `handler` or `typedHandler` | Traditional Azure Functions handler       |\n| `typedHandler` | `TypedHandler`                         | One of `handler` or `typedHandler` | Typed handler with auto validation        |\n| `methods`      | `HttpMethod[]`                         | ✅ Yes                             | HTTP methods (GET, POST, PUT, etc.)       |\n| `route`        | `string`                               | ✅ Yes                             | Route path (without prefix)               |\n| `params`       | `ZodSchema`                            | No                                 | Route parameters schema                   |\n| `query`        | `ZodSchema`                            | No                                 | Query string parameters schema            |\n| `body`         | `ZodSchema`                            | No                                 | Request body schema (JSON)                |\n| `headers`      | `ZodObject`                            | No                                 | Request headers schema                    |\n| `response`     | `ZodSchema`                            | No                                 | Single response shortcut (assumes 200 OK) |\n| `responses`    | `ResponseConfig[]`                     | No                                 | Multiple response configurations          |\n| `authLevel`    | `'anonymous' \\| 'function' \\| 'admin'` | No                                 | Azure Functions auth level                |\n| `security`     | `SecurityRequirementObject[]`          | No                                 | Security requirements for this endpoint   |\n| `tags`         | `string[]`                             | No                                 | OpenAPI tags for organization             |\n| `description`  | `string`                               | No                                 | Detailed description                      |\n| `deprecated`   | `boolean`                              | No                                 | Mark endpoint as deprecated               |\n| `operationId`  | `string`                               | No                                 | Unique operation ID (defaults to name)    |\n\n#### Traditional Handler vs Typed Handler\n\n**Traditional Handler** - Manual parsing and validation:\n\n```typescript\napp.openAPIPath('CreateUser', 'Create user', {\n  handler: async (request, context) => {\n    // Manual parsing required\n    const body = await request.json();\n    const { name, email } = body;\n\n    // Manual validation\n    if (!name || !email) {\n      return { status: 400, body: \"Missing required fields\" };\n    }\n\n    const user = await createUser({ name, email });\n    return { jsonBody: user };\n  },\n  methods: [\"POST\"],\n  route: \"users\",\n  body: CreateUserSchema,\n});\n```\n\n**Typed Handler** - Automatic parsing and validation:\n\n```typescript\napp.openAPIPath('CreateUser', 'Create user', {\n  typedHandler: async ({ body, context }) => {\n    // body is already parsed, validated, and typed!\n    // No manual checks needed\n    const user = await createUser(body);\n    return { jsonBody: user };\n  },\n  methods: [\"POST\"],\n  route: \"users\",\n  body: CreateUserSchema,\n});\n```\n\n---\n\n### Typed Handlers\n\nThe **Typed Handler** feature is the most powerful addition in v2.0. It provides automatic type inference from Zod schemas, eliminating manual type assertions and reducing boilerplate code.\n\n#### How It Works\n\nWhen you define schemas for `params`, `query`, `body`, or `headers`, TypeScript automatically infers the types for your handler parameters:\n\n```typescript\n// Define schemas\nconst ParamsSchema = z.object({ id: z.string().uuid() });\nconst QuerySchema = z.object({ include: z.string().optional() });\nconst BodySchema = z.object({ title: z.string(), completed: z.boolean() });\n\napp.openAPIPath('UpdateTodo', 'Update todo', {\n  typedHandler: async ({ params, query, body, context }) => {\n    // TypeScript knows:\n    // - params.id is string (from UUID schema)\n    // - query.include is string | undefined\n    // - body.title is string\n    // - body.completed is boolean\n\n    // No type assertions needed! ✨\n    const todo = await updateTodo(params.id, body);\n    return { jsonBody: todo };\n  },\n  methods: [\"PUT\"],\n  route: \"todos/{id}\",\n  params: ParamsSchema,\n  query: QuerySchema,\n  body: BodySchema,\n});\n```\n\n#### Automatic Validation\n\nTyped handlers automatically validate all request data. If validation fails, a **400 Bad Request** response is returned with detailed error information:\n\n```typescript\napp.openAPIPath('CreateUser', 'Create new user', {\n  typedHandler: async ({ body, context }) => {\n    // If we reach here, body is guaranteed to be valid\n    const user = await createUser(body);\n    return { status: 201, jsonBody: user };\n  },\n  methods: [\"POST\"],\n  route: \"users\",\n  body: z.object({\n    name: z.string().min(1).max(100),\n    email: z.string().email(),\n    age: z.number().int().positive().min(18),\n  }),\n});\n```\n\n**Invalid request:**\n\n```bash\ncurl -X POST http://localhost:7071/api/users \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"\",\"email\":\"invalid\",\"age\":15}'\n```\n\n**Automatic error response:**\n\n```json\n{\n  \"error\": \"Request body validation failed\",\n  \"issues\": [\n    {\n      \"path\": [\"name\"],\n      \"message\": \"String must contain at least 1 character(s)\"\n    },\n    {\n      \"path\": [\"email\"],\n      \"message\": \"Invalid email\"\n    },\n    {\n      \"path\": [\"age\"],\n      \"message\": \"Number must be greater than or equal to 18\"\n    }\n  ]\n}\n```\n\n#### Handler Arguments\n\nThe typed handler receives a single object with the following properties:\n\n```typescript\ntype TypedHandlerArgs = {\n  params: /* inferred from params schema */;\n  query: /* inferred from query schema or URLSearchParams */;\n  body: /* inferred from body schema or undefined */;\n  headers: /* inferred from headers schema or raw headers */;\n  request: SafeHttpRequest; // Original request (body methods removed if parsed)\n  context: InvocationContext; // Azure Functions context\n};\n```\n\n**Example with all parameters:**\n\n```typescript\napp.openAPIPath('ComplexEndpoint', 'Example with all parameters', {\n  typedHandler: async ({ params, query, body, headers, request, context }) => {\n    context.log(`Method: ${request.method}`);\n    context.log(`URL: ${request.url}`);\n    context.log(`Params:`, params);\n    context.log(`Query:`, query);\n    context.log(`Body:`, body);\n    context.log(`Headers:`, headers);\n\n    return { jsonBody: { success: true } };\n  },\n  methods: [\"POST\"],\n  route: \"complex/{id}\",\n  params: z.object({ id: z.string() }),\n  query: z.object({ filter: z.string().optional() }),\n  body: z.object({ data: z.any() }),\n  headers: z.object({\n    \"x-api-key\": z.string(),\n  }),\n});\n```\n\n#### Type Inference Examples\n\n**Simple types:**\n\n```typescript\n// String parameter\nparams: z.object({ id: z.string() });\n// → params.id is string\n\n// Number with coercion\nquery: z.object({ page: z.coerce.number() });\n// → query.page is number\n\n// Boolean\nbody: z.object({ enabled: z.boolean() });\n// → body.enabled is boolean\n```\n\n**Optional types:**\n\n```typescript\nquery: z.object({\n  search: z.string().optional(),\n  limit: z.coerce.number().default(10),\n});\n// → query.search is string | undefined\n// → query.limit is number\n```\n\n**Complex types:**\n\n```typescript\nbody: z.object({\n  user: z.object({\n    name: z.string(),\n    email: z.string().email(),\n  }),\n  tags: z.array(z.string()),\n  metadata: z.record(z.string(), z.any()),\n});\n// → body.user is { name: string; email: string }\n// → body.tags is string[]\n// → body.metadata is Record<string, any>\n```\n\n**Enums:**\n\n```typescript\nquery: z.object({\n  status: z.enum([\"active\", \"inactive\", \"pending\"]),\n});\n// → query.status is \"active\" | \"inactive\" | \"pending\"\n```\n\n**Union types:**\n\n```typescript\nbody: z.object({\n  value: z.union([z.string(), z.number()]),\n});\n// → body.value is string | number\n```\n\n#### Using createTypedHandler\n\nFor reusable handlers, use the `createTypedHandler` utility:\n\n```typescript\nimport { createTypedHandler } from \"@apvee/azure-functions-openapi\";\n\n// Define schemas\nconst TodoParamsSchema = z.object({ id: z.string().uuid() });\nconst UpdateTodoBodySchema = z.object({\n  title: z.string().optional(),\n  completed: z.boolean().optional(),\n});\n\n// Create reusable typed handler\nconst updateTodoHandler = createTypedHandler(\n  {\n    params: TodoParamsSchema,\n    body: UpdateTodoBodySchema,\n  },\n  async ({ params, body, context }) => {\n    // Fully typed parameters\n    context.log(`Updating todo ${params.id}`);\n    const updated = await updateTodo(params.id, body);\n    return { jsonBody: updated };\n  }\n);\n\n// Use in endpoint registration\napp.openAPIPath('UpdateTodo', 'Update todo', {\n  handler: updateTodoHandler, // Use as regular handler\n  methods: [\"PATCH\"],\n  route: \"todos/{id}\",\n  params: TodoParamsSchema,\n  body: UpdateTodoBodySchema,\n  response: TodoSchema,\n});\n```\n\n#### Inline vs External Handlers\n\n**Inline Handler** (best for simple logic):\n\n```typescript\napp.openAPIPath('DeleteTodo', 'Delete todo', {\n  typedHandler: async ({ params, context }) => {\n    await deleteTodo(params.id);\n    return { status: 204 };\n  },\n  methods: [\"DELETE\"],\n  route: \"todos/{id}\",\n  params: z.object({ id: z.string().uuid() }),\n});\n```\n\n**External Handler** (best for complex logic):\n\n```typescript\n// In handlers/deleteTodo.ts\nexport const deleteTodoHandler = createTypedHandler(\n  { params: TodoParamsSchema },\n  async ({ params, context }) => {\n    context.log(`Deleting todo: ${params.id}`);\n\n    const todo = await getTodo(params.id);\n    if (!todo) {\n      return { status: 404, jsonBody: { error: \"Todo not found\" } };\n    }\n\n    await deleteTodo(params.id);\n    return { status: 204 };\n  }\n);\n\n// In index.ts\nimport { deleteTodoHandler } from \"./handlers/deleteTodo\";\n\napp.openAPIPath('DeleteTodo', 'Delete todo', {\n  handler: deleteTodoHandler,\n  methods: [\"DELETE\"],\n  route: \"todos/{id}\",\n  params: TodoParamsSchema,\n});\n```\n\n#### Error Handling in Typed Handlers\n\nValidation errors are handled automatically, but you can add custom error handling:\n\n```typescript\napp.openAPIPath('RiskyOperation', 'Operation that might fail', {\n  typedHandler: async ({ body, context }) => {\n    try {\n      const result = await performRiskyOperation(body);\n      return { jsonBody: result };\n    } catch (error) {\n      context.error(\"Operation failed:\", error);\n\n      if (error instanceof DatabaseError) {\n        return {\n          status: 503,\n          jsonBody: { error: \"Service temporarily unavailable\" },\n        };\n      }\n\n      return {\n        status: 500,\n        jsonBody: { error: \"Internal server error\" },\n      };\n    }\n  },\n  methods: [\"POST\"],\n  route: \"risky\",\n  body: OperationSchema,\n  responses: [\n    { httpCode: 200, schema: ResultSchema },\n    { httpCode: 500, schema: ErrorSchema },\n    { httpCode: 503, schema: ErrorSchema },\n  ],\n});\n```\n\n#### Benefits of Typed Handlers\n\n✅ **Type Safety** - Compiler catches type errors before runtime  \n✅ **Less Boilerplate** - No manual parsing or validation code  \n✅ **Better IDE Support** - IntelliSense shows exact types  \n✅ **Automatic Validation** - Invalid requests rejected automatically  \n✅ **Consistent Error Responses** - Standardized validation errors  \n✅ **Self-Documenting** - Schemas serve as both validation and documentation\n\n---\n\n### Registering Reusable Schemas\n\nThe `app.openAPISchema()` method registers Zod schemas as named types in the OpenAPI registry. This promotes reusability and keeps your OpenAPI specification clean by using references instead of inlining schemas everywhere.\n\n#### Basic Schema Registration\n\nRegister a simple schema:\n\n```typescript\nimport { z } from \"zod\";\n\n// Define schema\nconst UserSchema = z.object({\n  id: z.string().uuid().describe(\"Unique user identifier\"),\n  name: z.string().min(1).max(100).describe(\"User full name\"),\n  email: z.string().email().describe(\"User email address\"),\n  createdAt: z.string().datetime().describe(\"Account creation timestamp\"),\n});\n\n// Register schema with a name\napp.openAPISchema('User', UserSchema);\n\n// Now use it in endpoints\napp.openAPIPath('GetUser', 'Get user by ID', {\n  typedHandler: async ({ params }) => {\n    const user = await getUserById(params.id);\n    return { jsonBody: user };\n  },\n  methods: [\"GET\"],\n  route: \"users/{id}\",\n  params: z.object({ id: z.string().uuid() }),\n  response: UserSchema, // References 'User' in OpenAPI spec\n});\n```\n\n#### Nested Schema Registration\n\nRegister complex schemas with nested objects:\n\n```typescript\n// Address schema\nconst AddressSchema = z.object({\n  street: z.string().describe(\"Street address\"),\n  city: z.string().describe(\"City name\"),\n  state: z.string().length(2).describe(\"State code (2 letters)\"),\n  zipCode: z\n    .string()\n    .regex(/^\\d{5}$/)\n    .describe(\"5-digit ZIP code\"),\n});\n\napp.openAPISchema('Address', AddressSchema);\n\n// User profile with nested address\nconst UserProfileSchema = z.object({\n  user: UserSchema,\n  address: AddressSchema,\n  phoneNumber: z.string().optional(),\n  preferences: z.object({\n    newsletter: z.boolean(),\n    notifications: z.boolean(),\n  }),\n});\n\napp.openAPISchema('UserProfile', UserProfileSchema);\n```\n\n#### Array Schemas\n\nRegister schemas for collections:\n\n```typescript\n// Single todo item\nconst TodoSchema = z.object({\n  id: z.string().uuid(),\n  title: z.string(),\n  completed: z.boolean(),\n  createdAt: z.string().datetime(),\n});\n\napp.openAPISchema('Todo', TodoSchema);\n\n// Paginated list\nconst PaginatedTodosSchema = z.object({\n  items: z.array(TodoSchema).describe(\"List of todos\"),\n  total: z.number().int().describe(\"Total number of items\"),\n  page: z.number().int().describe(\"Current page number\"),\n  pageSize: z.number().int().describe(\"Items per page\"),\n  hasMore: z.boolean().describe(\"Whether more pages exist\"),\n});\n\napp.openAPISchema('PaginatedTodos', PaginatedTodosSchema);\n\n// Use in endpoint\napp.openAPIPath('ListTodos', 'List all todos with pagination', {\n  typedHandler: async ({ query }) => {\n    const todos = await getTodos(query);\n    return { jsonBody: todos };\n  },\n  methods: [\"GET\"],\n  route: \"todos\",\n  query: z.object({\n    page: z.coerce.number().default(1),\n    pageSize: z.coerce.number().default(10),\n  }),\n  response: PaginatedTodosSchema,\n});\n```\n\n#### Error Schemas\n\nCreate standardized error responses:\n\n```typescript\n// Base error schema\nconst ErrorSchema = z.object({\n  error: z.string().describe(\"Error message\"),\n  code: z.string().describe(\"Error code\"),\n  timestamp: z.string().datetime().describe(\"When the error occurred\"),\n  path: z.string().optional().describe(\"Request path that caused the error\"),\n  details: z.any().optional().describe(\"Additional error details\"),\n});\n\napp.openAPISchema('Error', ErrorSchema);\n\n// Validation error schema\nconst ValidationErrorSchema = z.object({\n  error: z.string(),\n  code: z.literal(\"VALIDATION_ERROR\"),\n  issues: z.array(\n    z.object({\n      path: z.array(z.union([z.string(), z.number()])),\n      message: z.string(),\n    })\n  ),\n});\n\napp.openAPISchema('ValidationError', ValidationErrorSchema);\n\n// Use in endpoints\napp.openAPIPath('CreateUser', 'Create new user', {\n  typedHandler: async ({ body }) => {\n    const user = await createUser(body);\n    return { status: 201, jsonBody: user };\n  },\n  methods: [\"POST\"],\n  route: \"users\",\n  body: CreateUserSchema,\n  responses: [\n    {\n      httpCode: 201,\n      schema: UserSchema,\n      description: \"User created successfully\",\n    },\n    {\n      httpCode: 400,\n      schema: ValidationErrorSchema,\n      description: \"Invalid request data\",\n    },\n    {\n      httpCode: 500,\n      schema: ErrorSchema,\n      description: \"Internal server error\",\n    },\n  ],\n});\n```\n\n#### Discriminated Union Schemas\n\nRegister schemas with discriminated unions for polymorphic data:\n\n```typescript\n// Base notification\nconst BaseNotificationSchema = z.object({\n  id: z.string().uuid(),\n  createdAt: z.string().datetime(),\n});\n\n// Email notification\nconst EmailNotificationSchema = BaseNotificationSchema.extend({\n  type: z.literal(\"email\"),\n  to: z.string().email(),\n  subject: z.string(),\n  body: z.string(),\n});\n\napp.openAPISchema('EmailNotification', EmailNotificationSchema);\n\n// SMS notification\nconst SmsNotificationSchema = BaseNotificationSchema.extend({\n  type: z.literal(\"sms\"),\n  to: z.string().regex(/^\\+?[1-9]\\d{1,14}$/),\n  message: z.string().max(160),\n});\n\napp.openAPISchema('SmsNotification', SmsNotificationSchema);\n\n// Union of all notification types\nconst NotificationSchema = z.discriminatedUnion(\"type\", [\n  EmailNotificationSchema,\n  SmsNotificationSchema,\n]);\n\napp.openAPISchema('Notification', NotificationSchema);\n```\n\n#### Schema Organization Best Practices\n\n**Organize by domain:**\n\n```typescript\n// schemas/user.ts\nexport const UserSchema = z.object({\n  /* ... */\n});\nexport const CreateUserSchema = z.object({\n  /* ... */\n});\nexport const UpdateUserSchema = z.object({\n  /* ... */\n});\n\n// schemas/todo.ts\nexport const TodoSchema = z.object({\n  /* ... */\n});\nexport const CreateTodoSchema = z.object({\n  /* ... */\n});\nexport const UpdateTodoSchema = z.object({\n  /* ... */\n});\n\n// schemas/index.ts\nimport { UserSchema, CreateUserSchema, UpdateUserSchema } from \"./user\";\nimport { TodoSchema, CreateTodoSchema, UpdateTodoSchema } from \"./todo\";\n\nexport function registerSchemas(app: typeof import(\"@azure/functions\").app) {\n  // User schemas\n  app.openAPISchema('User', UserSchema);\n  app.openAPISchema('CreateUser', CreateUserSchema);\n  app.openAPISchema('UpdateUser', UpdateUserSchema);\n  \n  // Todo schemas\n  app.openAPISchema('Todo', TodoSchema);\n  app.openAPISchema('CreateTodo', CreateTodoSchema);\n  app.openAPISchema('UpdateTodo', UpdateTodoSchema);\n}\n\n// In index.ts\nimport \"@apvee/azure-functions-openapi\";\nimport { app } from \"@azure/functions\";\nimport { registerSchemas } from \"./schemas\";\n\napp.openAPISetup({ /* config */ });\nregisterSchemas(app);\n```\n\n#### Benefits of Schema Registration\n\n✅ **DRY Principle** - Define schemas once, use everywhere  \n✅ **Cleaner OpenAPI Spec** - Uses `$ref` instead of inline schemas  \n✅ **Better Documentation** - Named types are easier to understand  \n✅ **Consistency** - Same schema used for validation and documentation  \n✅ **Type Reusability** - Share types across multiple endpoints  \n✅ **Easier Maintenance** - Update schema in one place\n\n#### When to Register Schemas\n\n**Register schemas when:**\n\n- Used in multiple endpoints\n- Complex nested structures\n- Part of your domain model\n- Error response formats\n- Common request/response patterns\n\n**Inline schemas when:**\n\n- Used only once\n- Very simple structures\n- Endpoint-specific parameters\n- Quick prototyping\n\n**Example - Mixed approach:**\n\n```typescript\n// Register common schemas\napp.openAPISchema('Todo', TodoSchema);\napp.openAPISchema('Error', ErrorSchema);\n\napp.openAPIPath('UpdateTodo', 'Update todo', {\n  typedHandler: async ({ params, body }) => {\n    const todo = await updateTodo(params.id, body);\n    return { jsonBody: todo };\n  },\n  methods: [\"PATCH\"],\n  route: \"todos/{id}\",\n  // Inline simple param schema\n  params: z.object({ id: z.string().uuid() }),\n  // Inline endpoint-specific body schema\n  body: z.object({\n    title: z.string().optional(),\n    completed: z.boolean().optional(),\n  }),\n  // Reference registered schema\n  response: TodoSchema,\n});\n```\n\n---\n\n### Webhooks (OpenAPI 3.1.0)\n\nWebhooks are a new feature in OpenAPI 3.1.0 that allows you to document **callback endpoints** - HTTP requests that your API makes to external URLs. Unlike regular endpoints that clients call, webhooks represent notifications that your API sends to client-provided URLs.\n\n#### Understanding Webhooks\n\n**Regular API Endpoint (Path):**\n\n```\nClient → Your API\nExample: GET /api/orders/123\n```\n\n**Webhook (Callback):**\n\n```\nYour API → Client's URL\nExample: POST https://client.com/webhooks/order-completed\n```\n\n#### Basic Webhook Registration\n\nDocument a simple webhook notification:\n\n```typescript\nimport { z } from \"zod\";\n\n// Define the payload your API will send\nconst OrderCompletedSchema = z.object({\n  orderId: z.string().uuid().describe(\"Order identifier\"),\n  status: z.literal(\"completed\").describe(\"Order status\"),\n  completedAt: z.string().datetime().describe(\"Completion timestamp\"),\n  totalAmount: z.number().describe(\"Total order amount\"),\n});\n\n// Register webhook\napp.openAPIWebhook('OrderCompleted', 'Notifies when an order is completed', {\n  typedHandler: async ({ body, context }) => {\n    // This is the handler that receives the webhook at YOUR endpoint\n    // (for testing or development purposes)\n    context.log(`Webhook received: Order ${body.orderId} completed`);\n    return { jsonBody: { received: true } };\n  },\n  methods: [\"POST\"],\n  body: OrderCompletedSchema,\n  responses: [\n    {\n      httpCode: 200,\n      description: \"Webhook received successfully\",\n      schema: z.object({ received: z.boolean() }),\n    },\n  ],\n});\n```\n\n#### Webhook with Authentication\n\nDocument webhooks that require authentication:\n\n```typescript\n// Register security scheme for webhook signatures\nconst webhookSignature = app.openAPIKeySecurity(\n  'X-Webhook-Signature',\n  'header',\n  'HMAC signature for webhook verification'\n);\n\napp.openAPIWebhook('PaymentProcessed', 'Notifies when payment is processed', {\n  typedHandler: async ({ body, headers, context }) => {\n    // Verify webhook signature\n    const signature = headers[\"x-webhook-signature\"];\n    const isValid = verifyWebhookSignature(body, signature);\n\n    if (!isValid) {\n      return { status: 401, jsonBody: { error: \"Invalid signature\" } };\n    }\n\n    context.log(`Payment processed: ${body.paymentId}`);\n    return { jsonBody: { acknowledged: true } };\n  },\n  methods: [\"POST\"],\n  body: z.object({\n    paymentId: z.string().uuid(),\n    amount: z.number(),\n    currency: z.string().length(3),\n    status: z.enum([\"success\", \"failed\", \"pending\"]),\n  }),\n  headers: z.object({\n    \"x-webhook-signature\": z.string(),\n  }),\n  security: [webhookSignature],\n});\n```\n\n#### Multiple Webhook Events\n\nDocument different webhook events for various scenarios:\n\n```typescript\n// User registration webhook\napp.openAPIWebhook('UserRegistered', 'Notifies when a new user registers', {\n  typedHandler: async ({ body, context }) => {\n    context.log(`New user: ${body.userId}`);\n    return { status: 200 };\n  },\n  methods: [\"POST\"],\n  body: z.object({\n    userId: z.string().uuid(),\n    email: z.string().email(),\n    registeredAt: z.string().datetime(),\n  }),\n  tags: [\"User Events\"],\n});\n\n// User deletion webhook\napp.openAPIWebhook('UserDeleted', 'Notifies when a user is deleted', {\n  typedHandler: async ({ body, context }) => {\n    context.log(`User deleted: ${body.userId}`);\n    return { status: 200 };\n  },\n  methods: [\"POST\"],\n  body: z.object({\n    userId: z.string().uuid(),\n    deletedAt: z.string().datetime(),\n    reason: z.string().optional(),\n  }),\n  tags: [\"User Events\"],\n});\n\n// Subscription events\napp.openAPIWebhook('SubscriptionChanged', 'Notifies when subscription status changes', {\n  typedHandler: async ({ body, context }) => {\n    context.log(`Subscription ${body.subscriptionId} changed to ${body.status}`);\n    return { status: 200 };\n  },\n  methods: ['POST'],\n  body: z.object({\n    subscriptionId: z.string().uuid(),\n    userId: z.string().uuid(),\n    status: z.enum(['active', 'cancelled', 'expired', 'paused']),\n    changedAt: z.string().datetime()\n  }),\n  tags: ['Subscription Events']\n});\n```\n\n#### Webhook Retry Logic Documentation\n\nDocument how your API handles webhook failures:\n\n```typescript\napp.openAPIWebhook('OrderShipped', 'Notifies when order is shipped', {\n  typedHandler: async ({ body, headers, context }) => {\n    // Document retry attempt in description\n    const attemptNumber = headers[\"x-webhook-attempt\"];\n    context.log(`Webhook attempt ${attemptNumber} for order ${body.orderId}`);\n\n    return { status: 200 };\n  },\n  methods: [\"POST\"],\n  description: `\nWebhook sent when an order is shipped.\n\n**Retry Policy:**\n- Initial attempt immediately after shipping\n- Retries after 1 min, 5 min, 15 min, 1 hour, 6 hours\n- Maximum 5 retry attempts\n- Exponential backoff applied\n\n**Request Headers:**\n- \\`X-Webhook-Attempt\\`: Retry attempt number (1-5)\n- \\`X-Webhook-Id\\`: Unique webhook delivery ID\n- \\`X-Webhook-Timestamp\\`: ISO 8601 timestamp\n  `,\n  body: z.object({\n    orderId: z.string().uuid(),\n    trackingNumber: z.string(),\n    carrier: z.string(),\n    shippedAt: z.string().datetime(),\n    estimatedDelivery: z.string().datetime(),\n  }),\n  headers: z.object({\n    \"x-webhook-attempt\": z.coerce.number().int().min(1).max(5),\n    \"x-webhook-id\": z.string().uuid(),\n    \"x-webhook-timestamp\": z.string().datetime(),\n  }),\n  responses: [\n    {\n      httpCode: 200,\n      description: \"Webhook acknowledged successfully\",\n    },\n    {\n      httpCode: 500,\n      description: \"Server error - webhook will be retried\",\n    },\n  ],\n});\n```\n\n#### Real-World Webhook Example: Stripe-style\n\nComprehensive webhook similar to Stripe's approach:\n\n```typescript\n// Register webhook schemas\nconst WebhookEventSchema = z.object({\n  id: z.string().uuid().describe(\"Unique event identifier\"),\n  type: z.string().describe(\"Event type\"),\n  created: z.number().int().describe(\"Unix timestamp\"),\n  data: z.object({\n    object: z.any(),\n  }),\n  livemode: z.boolean().describe(\"Whether in production mode\"),\n});\n\napp.openAPISchema('WebhookEvent', WebhookEventSchema);\n\n// Webhook signature security\nconst stripeSignature = app.openAPIKeySecurity(\n  'Stripe-Signature',\n  'header',\n  'Webhook signature for verification (see Stripe documentation)'\n);\n\napp.openAPIWebhook('StripeWebhook', 'Generic Stripe-style webhook endpoint', {\n  typedHandler: async ({ body, headers, context }) => {\n    const signature = headers[\"stripe-signature\"];\n\n    // Verify signature (pseudo-code)\n    if (!verifyStripeSignature(body, signature, process.env.WEBHOOK_SECRET)) {\n      return { status: 400, jsonBody: { error: \"Invalid signature\" } };\n    }\n\n    // Handle different event types\n    context.log(`Event received: ${body.type}`);\n\n    switch (body.type) {\n      case \"payment_intent.succeeded\":\n        await handlePaymentSuccess(body.data.object);\n        break;\n      case \"payment_intent.payment_failed\":\n        await handlePaymentFailure(body.data.object);\n        break;\n      case \"customer.subscription.created\":\n        await handleSubscriptionCreated(body.data.object);\n        break;\n      default:\n        context.warn(`Unhandled event type: ${body.type}`);\n    }\n\n    return { jsonBody: { received: true } };\n  },\n  methods: [\"POST\"],\n  description: `\nWebhook endpoint for receiving events from Stripe.\n\n**Verification:**\nAll webhook requests include a \\`Stripe-Signature\\` header. Verify this signature\nusing your webhook secret to ensure the request came from Stripe.\n\n**Event Types:**\n- \\`payment_intent.succeeded\\` - Payment completed successfully\n- \\`payment_intent.payment_failed\\` - Payment failed\n- \\`customer.subscription.created\\` - New subscription created\n- \\`customer.subscription.deleted\\` - Subscription cancelled\n- And many more...\n\n**Best Practices:**\n1. Always verify the signature\n2. Return 2xx status code quickly\n3. Process events asynchronously\n4. Handle duplicate events (idempotency)\n  `,\n  body: WebhookEventSchema,\n  headers: z.object({\n    \"stripe-signature\": z.string().describe(\"HMAC signature of the payload\"),\n  }),\n  security: [stripeSignature],\n  responses: [\n    {\n      httpCode: 200,\n      description: \"Event received and queued for processing\",\n      schema: z.object({ received: z.boolean() }),\n    },\n    {\n      httpCode: 400,\n      description: \"Invalid signature or malformed payload\",\n      schema: z.object({ error: z.string() }),\n    },\n  ],\n  tags: [\"Webhooks\"],\n});\n```\n\n#### Webhook Configuration Options\n\nThe `app.openAPIWebhook()` method accepts the same options as `app.openAPIPath()`:\n\n| Option                      | Description                                  |\n| --------------------------- | -------------------------------------------- |\n| `handler` or `typedHandler` | Function to handle webhook (for testing)     |\n| `methods`                   | HTTP methods (usually `['POST']`)            |\n| `body`                      | Expected webhook payload schema              |\n| `headers`                   | Expected headers (signatures, IDs, etc.)     |\n| `security`                  | Security requirements (signatures, API keys) |\n| `responses`                 | Possible response codes                      |\n| `description`               | Detailed webhook documentation               |\n| `tags`                      | Tags for organization                        |\n\n#### Webhook vs Path Differences\n\n**Webhooks:**\n\n- Documented in `webhooks` section of OpenAPI spec\n- Represent outbound requests from your API\n- Typically POST requests\n- Often include signature verification\n- Usually retry on failure\n\n**Paths:**\n\n- Documented in `paths` section of OpenAPI spec\n- Represent inbound requests to your API\n- Support all HTTP methods\n- May require authentication\n- Client handles retries\n\n#### Testing Webhooks Locally\n\nUse tools like ngrok or webhook.site for local testing:\n\n```bash\n# Start ngrok tunnel\nngrok http 7071\n\n# Your webhook URL becomes:\n# https://abc123.ngrok.io/api/webhooks/order-completed\n\n# Test with curl\ncurl -X POST https://abc123.ngrok.io/api/webhooks/order-completed \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"orderId\": \"123e4567-e89b-12d3-a456-426614174000\",\n    \"status\": \"completed\",\n    \"completedAt\": \"2025-11-06T12:00:00Z\",\n    \"totalAmount\": 99.99\n  }'\n```\n\n---\n\n## 🔒 Security Schemas\n\nSecurity is a critical aspect of API development. This library provides native support for multiple Azure authentication methods and custom security schemes, all properly documented in your OpenAPI","readmeFilename":"README.md"}