[{"data":1,"prerenderedAt":3308},["ShallowReactive",2],{"navigation_docs":3,"-docs-capabilities-openapi":493,"-docs-capabilities-openapi-surround":3303},[4,46,135,218,257,292,355,377,420],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":45},"Getting Started","i-lucide-rocket","\u002Fdocs\u002Fgetting-started","docs\u002F01.getting-started",[10,15,20,25,30,35,40],{"title":11,"path":12,"stem":13,"icon":14},"What is Heximon?","\u002Fdocs\u002Fgetting-started\u002Fintroduction","docs\u002F01.getting-started\u002F01.introduction","i-lucide-info",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","docs\u002F01.getting-started\u002F02.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Quick Start","\u002Fdocs\u002Fgetting-started\u002Fquick-start","docs\u002F01.getting-started\u002F03.quick-start","i-lucide-zap",{"title":26,"path":27,"stem":28,"icon":29},"Core Concepts","\u002Fdocs\u002Fgetting-started\u002Fcore-concepts","docs\u002F01.getting-started\u002F04.core-concepts","i-lucide-shapes",{"title":31,"path":32,"stem":33,"icon":34},"Examples","\u002Fdocs\u002Fgetting-started\u002Fexamples","docs\u002F01.getting-started\u002F05.examples","i-lucide-library",{"title":36,"path":37,"stem":38,"icon":39},"Where Next?","\u002Fdocs\u002Fgetting-started\u002Fwhere-next","docs\u002F01.getting-started\u002F06.where-next","i-lucide-signpost",{"title":41,"path":42,"stem":43,"icon":44},"Troubleshooting","\u002Fdocs\u002Fgetting-started\u002Ftroubleshooting","docs\u002F01.getting-started\u002F07.troubleshooting","i-lucide-life-buoy",false,{"title":47,"icon":48,"path":49,"stem":50,"children":51,"page":45},"Essentials","i-lucide-boxes","\u002Fdocs\u002Fessentials","docs\u002F02.essentials",[52,56,61,66,71,76,81,86,91,96,101,105,110,115,120,125,130],{"title":53,"path":54,"stem":55,"icon":48},"Modules & DI","\u002Fdocs\u002Fessentials\u002Fmodules-and-di","docs\u002F02.essentials\u002F01.modules-and-di",{"title":57,"path":58,"stem":59,"icon":60},"Controllers","\u002Fdocs\u002Fessentials\u002Fcontrollers","docs\u002F02.essentials\u002F02.controllers","i-lucide-route",{"title":62,"path":63,"stem":64,"icon":65},"Validation & DTOs","\u002Fdocs\u002Fessentials\u002Fvalidation","docs\u002F02.essentials\u002F03.validation","i-lucide-shield-check",{"title":67,"path":68,"stem":69,"icon":70},"Contracts","\u002Fdocs\u002Fessentials\u002Fcontracts","docs\u002F02.essentials\u002F04.contracts","i-lucide-file-signature",{"title":72,"path":73,"stem":74,"icon":75},"REST Client","\u002Fdocs\u002Fessentials\u002Fclient","docs\u002F02.essentials\u002F05.client","i-lucide-plug",{"title":77,"path":78,"stem":79,"icon":80},"Database","\u002Fdocs\u002Fessentials\u002Fdatabase","docs\u002F02.essentials\u002F06.database","i-lucide-database",{"title":82,"path":83,"stem":84,"icon":85},"Drizzle ORM","\u002Fdocs\u002Fessentials\u002Fdrizzle","docs\u002F02.essentials\u002F07.drizzle","i-lucide-database-zap",{"title":87,"path":88,"stem":89,"icon":90},"Persistence & Transactions","\u002Fdocs\u002Fessentials\u002Fpersistence","docs\u002F02.essentials\u002F08.persistence","i-lucide-database-backup",{"title":92,"path":93,"stem":94,"icon":95},"Migrations","\u002Fdocs\u002Fessentials\u002Fmigrations","docs\u002F02.essentials\u002F09.migrations","i-lucide-git-branch",{"title":97,"path":98,"stem":99,"icon":100},"Authentication","\u002Fdocs\u002Fessentials\u002Fauthentication","docs\u002F02.essentials\u002F10.authentication","i-lucide-key-round",{"title":102,"path":103,"stem":104,"icon":65},"Permissions & scopes","\u002Fdocs\u002Fessentials\u002Fpermissions","docs\u002F02.essentials\u002F11.permissions",{"title":106,"path":107,"stem":108,"icon":109},"Error Handling","\u002Fdocs\u002Fessentials\u002Ferrors","docs\u002F02.essentials\u002F12.errors","i-lucide-triangle-alert",{"title":111,"path":112,"stem":113,"icon":114},"Testing a Heximon App","\u002Fdocs\u002Fessentials\u002Ftesting","docs\u002F02.essentials\u002F13.testing","i-lucide-flask-conical",{"title":116,"path":117,"stem":118,"icon":119},"Test app harness","\u002Fdocs\u002Fessentials\u002Fcreatetestapp","docs\u002F02.essentials\u002F14.createtestapp","i-lucide-test-tube",{"title":121,"path":122,"stem":123,"icon":124},"Logging & Observability","\u002Fdocs\u002Fessentials\u002Flogging","docs\u002F02.essentials\u002F17.logging","i-lucide-scroll-text",{"title":126,"path":127,"stem":128,"icon":129},"Context & Lifecycle","\u002Fdocs\u002Fessentials\u002Fcontext-and-lifecycle","docs\u002F02.essentials\u002F18.context-and-lifecycle","i-lucide-radio",{"title":131,"path":132,"stem":133,"icon":134},"Project Structure","\u002Fdocs\u002Fessentials\u002Fproject-structure","docs\u002F02.essentials\u002F19.project-structure","i-lucide-folder-tree",{"title":136,"icon":137,"path":138,"stem":139,"children":140,"page":45},"Capabilities","i-lucide-sparkles","\u002Fdocs\u002Fcapabilities","docs\u002F03.capabilities",[141,145,150,155,160,164,169,174,179,184,188,193,198,203,208,213],{"title":142,"path":143,"stem":144,"icon":137},"Overview","\u002Fdocs\u002Fcapabilities\u002Foverview","docs\u002F03.capabilities\u002F01.overview",{"title":146,"path":147,"stem":148,"icon":149},"Events","\u002Fdocs\u002Fcapabilities\u002Fevents","docs\u002F03.capabilities\u002F02.events","i-lucide-radio-tower",{"title":151,"path":152,"stem":153,"icon":154},"Queue","\u002Fdocs\u002Fcapabilities\u002Fbackground-jobs","docs\u002F03.capabilities\u002F03.background-jobs","i-lucide-list-ordered",{"title":156,"path":157,"stem":158,"icon":159},"Scheduled Work","\u002Fdocs\u002Fcapabilities\u002Fscheduled-jobs","docs\u002F03.capabilities\u002F04.scheduled-jobs","i-lucide-clock",{"title":161,"path":162,"stem":163,"icon":85},"Caching","\u002Fdocs\u002Fcapabilities\u002Fcaching","docs\u002F03.capabilities\u002F05.caching",{"title":165,"path":166,"stem":167,"icon":168},"Key\u002FValue Storage","\u002Fdocs\u002Fcapabilities\u002Fkey-value","docs\u002F03.capabilities\u002F06.key-value","i-lucide-hard-drive",{"title":170,"path":171,"stem":172,"icon":173},"Blob Storage","\u002Fdocs\u002Fcapabilities\u002Fblob","docs\u002F03.capabilities\u002F07.blob","i-lucide-folder-open",{"title":175,"path":176,"stem":177,"icon":178},"Health & Readiness","\u002Fdocs\u002Fcapabilities\u002Fhealth","docs\u002F03.capabilities\u002F08.health","i-lucide-heart-pulse",{"title":180,"path":181,"stem":182,"icon":183},"Notifications","\u002Fdocs\u002Fcapabilities\u002Fnotifications","docs\u002F03.capabilities\u002F09.notifications","i-lucide-mail",{"title":185,"path":186,"stem":187,"icon":65},"Idempotency","\u002Fdocs\u002Fcapabilities\u002Fidempotency","docs\u002F03.capabilities\u002F10.idempotency",{"title":189,"path":190,"stem":191,"icon":192},"Security Hardening","\u002Fdocs\u002Fcapabilities\u002Fsecurity","docs\u002F03.capabilities\u002F11.security","i-lucide-shield",{"title":194,"path":195,"stem":196,"icon":197},"Observability & Tracing","\u002Fdocs\u002Fcapabilities\u002Fobservability","docs\u002F03.capabilities\u002F12.observability","i-lucide-activity",{"title":199,"path":200,"stem":201,"icon":202},"OpenAPI","\u002Fdocs\u002Fcapabilities\u002Fopenapi","docs\u002F03.capabilities\u002F13.openapi","i-lucide-book-marked",{"title":204,"path":205,"stem":206,"icon":207},"MCP Server","\u002Fdocs\u002Fcapabilities\u002Fmcp","docs\u002F03.capabilities\u002F14.mcp","i-lucide-bot",{"title":209,"path":210,"stem":211,"icon":212},"Schema DTOs","\u002Fdocs\u002Fcapabilities\u002Fschema","docs\u002F03.capabilities\u002F15.schema","i-lucide-file-json",{"title":214,"path":215,"stem":216,"icon":217},"Branded IDs","\u002Fdocs\u002Fcapabilities\u002Fbranded-ids","docs\u002F03.capabilities\u002F16.branded-ids","i-lucide-tag",{"title":219,"icon":220,"path":221,"stem":222,"children":223,"page":45},"Architecture","i-lucide-building","\u002Fdocs\u002Farchitecture","docs\u002F04.architecture",[224,227,232,237,242,247,252],{"title":142,"path":225,"stem":226,"icon":220},"\u002Fdocs\u002Farchitecture\u002Foverview","docs\u002F04.architecture\u002F01.overview",{"title":228,"path":229,"stem":230,"icon":231},"Domain-Driven Design","\u002Fdocs\u002Farchitecture\u002Fddd","docs\u002F04.architecture\u002F02.ddd","i-lucide-hexagon",{"title":233,"path":234,"stem":235,"icon":236},"CQRS","\u002Fdocs\u002Farchitecture\u002Fcqrs","docs\u002F04.architecture\u002F03.cqrs","i-lucide-split",{"title":238,"path":239,"stem":240,"icon":241},"Repository Pattern","\u002Fdocs\u002Farchitecture\u002Frepository-pattern","docs\u002F04.architecture\u002F04.repository-pattern","i-lucide-archive",{"title":243,"path":244,"stem":245,"icon":246},"Integration Events","\u002Fdocs\u002Farchitecture\u002Fintegration-events","docs\u002F04.architecture\u002F05.integration-events","i-lucide-send-horizontal",{"title":248,"path":249,"stem":250,"icon":251},"Saga Orchestration","\u002Fdocs\u002Farchitecture\u002Fsagas","docs\u002F04.architecture\u002F06.sagas","i-lucide-hourglass",{"title":253,"path":254,"stem":255,"icon":256},"Durable Workflows","\u002Fdocs\u002Farchitecture\u002Fworkflows","docs\u002F04.architecture\u002F07.workflows","i-lucide-workflow",{"title":258,"icon":259,"path":260,"stem":261,"children":262,"page":45},"Realtime","i-lucide-cable","\u002Fdocs\u002Frealtime","docs\u002F05.realtime",[263,266,270,274,279,284,288],{"title":142,"path":264,"stem":265,"icon":259},"\u002Fdocs\u002Frealtime\u002Foverview","docs\u002F05.realtime\u002F01.overview",{"title":267,"path":268,"stem":269,"icon":129},"WebSockets","\u002Fdocs\u002Frealtime\u002Fwebsockets","docs\u002F05.realtime\u002F02.websockets",{"title":271,"path":272,"stem":273,"icon":70},"WebSocket Contracts","\u002Fdocs\u002Frealtime\u002Fwebsocket-contracts","docs\u002F05.realtime\u002F03.websocket-contracts",{"title":275,"path":276,"stem":277,"icon":278},"Server-Sent Events","\u002Fdocs\u002Frealtime\u002Fserver-sent-events","docs\u002F05.realtime\u002F04.server-sent-events","i-lucide-rss",{"title":280,"path":281,"stem":282,"icon":283},"Typed Clients","\u002Fdocs\u002Frealtime\u002Ftyped-clients","docs\u002F05.realtime\u002F05.typed-clients","i-lucide-plug-zap",{"title":285,"path":286,"stem":287,"icon":259},"AsyncAPI","\u002Fdocs\u002Frealtime\u002Fasyncapi","docs\u002F05.realtime\u002F06.asyncapi",{"title":289,"path":290,"stem":291,"icon":80},"Durable Objects","\u002Fdocs\u002Frealtime\u002Fdurable-objects","docs\u002F05.realtime\u002F07.durable-objects",{"title":293,"icon":294,"path":295,"stem":296,"children":297,"page":45},"Deploy","i-lucide-cloud-upload","\u002Fdocs\u002Fdeploy","docs\u002F06.deploy",[298,301,306,311,316,321,326,331,336,341,345,350],{"title":142,"path":299,"stem":300,"icon":6},"\u002Fdocs\u002Fdeploy\u002Foverview","docs\u002F06.deploy\u002F01.overview",{"title":302,"path":303,"stem":304,"icon":305},"Node","\u002Fdocs\u002Fdeploy\u002Fnode","docs\u002F06.deploy\u002F02.node","la:node-js",{"title":307,"path":308,"stem":309,"icon":310},"Cloudflare","\u002Fdocs\u002Fdeploy\u002Fcloudflare","docs\u002F06.deploy\u002F03.cloudflare","simple-icons:cloudflare",{"title":312,"path":313,"stem":314,"icon":315},"Vercel","\u002Fdocs\u002Fdeploy\u002Fvercel","docs\u002F06.deploy\u002F04.vercel","ri:vercel-fill",{"title":317,"path":318,"stem":319,"icon":320},"Netlify","\u002Fdocs\u002Fdeploy\u002Fnetlify","docs\u002F06.deploy\u002F05.netlify","i-simple-icons-netlify",{"title":322,"path":323,"stem":324,"icon":325},"AWS Lambda","\u002Fdocs\u002Fdeploy\u002Faws-lambda","docs\u002F06.deploy\u002F06.aws-lambda","hugeicons:aws-lambda",{"title":327,"path":328,"stem":329,"icon":330},"Deno Deploy","\u002Fdocs\u002Fdeploy\u002Fdeno-deploy","docs\u002F06.deploy\u002F07.deno-deploy","simple-icons:denodeploy",{"title":332,"path":333,"stem":334,"icon":335},"Nitro","\u002Fdocs\u002Fdeploy\u002Fnitro","docs\u002F06.deploy\u002F08.nitro","i-unjs-nitro",{"title":337,"path":338,"stem":339,"icon":340},"Nuxt","\u002Fdocs\u002Fdeploy\u002Fnuxt","docs\u002F06.deploy\u002F09.nuxt","i-devicon-nuxt",{"title":342,"path":343,"stem":344,"icon":236},"Mixed-runtime split","\u002Fdocs\u002Fdeploy\u002Fmixed-runtime-split","docs\u002F06.deploy\u002F10.mixed-runtime-split",{"title":346,"path":347,"stem":348,"icon":349},"Hosts","\u002Fdocs\u002Fdeploy\u002Fhosts","docs\u002F06.deploy\u002F11.hosts","i-lucide-map",{"title":351,"path":352,"stem":353,"icon":354},"Cross-service events","\u002Fdocs\u002Fdeploy\u002Fcross-service-events","docs\u002F06.deploy\u002F12.cross-service-events","i-lucide-network",{"title":356,"icon":357,"path":358,"stem":359,"children":360,"page":45},"CLI","i-lucide-terminal","\u002Fdocs\u002Fcli","docs\u002F07.cli",[361,365,369,373],{"title":362,"path":363,"stem":364,"icon":357},"The heximon CLI","\u002Fdocs\u002Fcli\u002Foverview","docs\u002F07.cli\u002F01.overview",{"title":366,"path":367,"stem":368,"icon":137},"Create a New App","\u002Fdocs\u002Fcli\u002Fcreate","docs\u002F07.cli\u002F02.create",{"title":370,"path":371,"stem":372,"icon":95},"Migrations from the CLI","\u002Fdocs\u002Fcli\u002Fmigrations","docs\u002F07.cli\u002F03.migrations",{"title":374,"path":375,"stem":376,"icon":6},"Deploying a Heximon app","\u002Fdocs\u002Fcli\u002Fdeploy","docs\u002F07.cli\u002F04.deploy",{"title":378,"icon":379,"path":380,"stem":381,"children":382,"page":45},"Under the Hood","i-lucide-wrench","\u002Fdocs\u002Funder-the-hood","docs\u002F08.under-the-hood",[383,388,393,397,402,407,411,415],{"title":384,"path":385,"stem":386,"icon":387},"How the Compiler Works","\u002Fdocs\u002Funder-the-hood\u002Fhow-the-compiler-works","docs\u002F08.under-the-hood\u002F01.how-the-compiler-works","i-lucide-cog",{"title":389,"path":390,"stem":391,"icon":392},"Compiler","\u002Fdocs\u002Funder-the-hood\u002Fextending-the-compiler","docs\u002F08.under-the-hood\u002F02.extending-the-compiler","i-lucide-cpu",{"title":394,"path":395,"stem":396,"icon":357},"Compiler Diagnostics","\u002Fdocs\u002Funder-the-hood\u002Fcompiler-diagnostics","docs\u002F08.under-the-hood\u002F03.compiler-diagnostics",{"title":398,"path":399,"stem":400,"icon":401},"Library Mode","\u002Fdocs\u002Funder-the-hood\u002Flibrary-mode","docs\u002F08.under-the-hood\u002F04.library-mode","i-lucide-package",{"title":403,"path":404,"stem":405,"icon":406},"Other bundlers","\u002Fdocs\u002Funder-the-hood\u002Falternate-bundlers","docs\u002F08.under-the-hood\u002F05.alternate-bundlers","i-lucide-blocks",{"title":408,"path":409,"stem":410,"icon":401},"Packages","\u002Fdocs\u002Funder-the-hood\u002Fpackages","docs\u002F08.under-the-hood\u002F06.packages",{"title":412,"path":413,"stem":414,"icon":207},"AI Skills","\u002Fdocs\u002Funder-the-hood\u002Fai-and-agents","docs\u002F08.under-the-hood\u002F07.ai-and-agents",{"title":416,"path":417,"stem":418,"icon":419},"Advanced DI","\u002Fdocs\u002Funder-the-hood\u002Fadvanced-di","docs\u002F08.under-the-hood\u002F08.advanced-di","i-lucide-git-fork",{"title":421,"icon":422,"path":423,"stem":424,"children":425,"page":45},"Recipes","i-lucide-book-open-check","\u002Fdocs\u002Frecipes","docs\u002F09.recipes",[426,431,436,440,444,448,452,456,460,465,469,474,479,484,488],{"title":427,"path":428,"stem":429,"icon":430},"Send an Email","\u002Fdocs\u002Frecipes\u002Fsend-an-email","docs\u002F09.recipes\u002F01.send-an-email","i-lucide-send",{"title":432,"path":433,"stem":434,"icon":435},"Upload a File","\u002Fdocs\u002Frecipes\u002Fupload-a-file","docs\u002F09.recipes\u002F02.upload-a-file","i-lucide-upload",{"title":437,"path":438,"stem":439,"icon":24},"Cache an Expensive Call","\u002Fdocs\u002Frecipes\u002Fcache-an-expensive-call","docs\u002F09.recipes\u002F03.cache-an-expensive-call",{"title":441,"path":442,"stem":443,"icon":154},"Paginate a List","\u002Fdocs\u002Frecipes\u002Fpaginate-a-list","docs\u002F09.recipes\u002F04.paginate-a-list",{"title":445,"path":446,"stem":447,"icon":65},"Protect Admin Routes","\u002Fdocs\u002Frecipes\u002Fprotect-admin-routes","docs\u002F09.recipes\u002F05.protect-admin-routes",{"title":449,"path":450,"stem":451,"icon":178},"Health Probes and a Nightly Job","\u002Fdocs\u002Frecipes\u002Fhealth-probes-and-a-nightly-job","docs\u002F09.recipes\u002F06.health-probes-and-a-nightly-job",{"title":453,"path":454,"stem":455,"icon":80},"Build a CRUD API","\u002Fdocs\u002Frecipes\u002Fcrud-api","docs\u002F09.recipes\u002F07.crud-api",{"title":457,"path":458,"stem":459,"icon":100},"Add Authentication","\u002Fdocs\u002Frecipes\u002Fadd-authentication","docs\u002F09.recipes\u002F08.add-authentication",{"title":461,"path":462,"stem":463,"icon":464},"Background Jobs","\u002Fdocs\u002Frecipes\u002Fbackground-jobs","docs\u002F09.recipes\u002F09.background-jobs","i-lucide-timer",{"title":466,"path":467,"stem":468,"icon":149},"Decouple Features with Events","\u002Fdocs\u002Frecipes\u002Fevent-driven-module","docs\u002F09.recipes\u002F10.event-driven-module",{"title":470,"path":471,"stem":472,"icon":473},"Build a DDD Aggregate","\u002Fdocs\u002Frecipes\u002Fddd-aggregate","docs\u002F09.recipes\u002F11.ddd-aggregate","i-lucide-box",{"title":475,"path":476,"stem":477,"icon":478},"Publish a Feature Package","\u002Fdocs\u002Frecipes\u002Ffeature-package","docs\u002F09.recipes\u002F12.feature-package","i-lucide-package-open",{"title":480,"path":481,"stem":482,"icon":483},"Reliable Integration Events","\u002Fdocs\u002Frecipes\u002Freliable-integration-events","docs\u002F09.recipes\u002F13.reliable-integration-events","i-lucide-inbox",{"title":485,"path":486,"stem":487,"icon":95},"Reactive Choreography","\u002Fdocs\u002Frecipes\u002Freactive-choreography","docs\u002F09.recipes\u002F14.reactive-choreography",{"title":489,"path":490,"stem":491,"icon":492},"Build a Production CRUD API","\u002Fdocs\u002Frecipes\u002Fproduction-crud-api","docs\u002F09.recipes\u002F15.production-crud-api","i-lucide-layers",{"id":494,"title":199,"body":495,"description":3296,"extension":3297,"links":3298,"meta":3299,"navigation":3300,"path":200,"seo":3301,"stem":201,"__hash__":3302},"docs\u002Fdocs\u002F03.capabilities\u002F13.openapi.md",{"type":496,"value":497,"toc":3280},"minimark",[498,510,522,527,598,605,609,646,1130,1139,1143,1146,1281,1300,1305,1329,1468,1495,1522,1529,1546,1560,1667,1674,1678,1709,1900,1918,1922,1931,2192,2206,2218,2222,2229,2293,2299,2629,2646,2673,2677,2686,2903,2906,3003,3007,3030,3157,3182,3186,3204,3227,3231,3276],[499,500,501,502,509],"p",{},"You already described your API once — the ",[503,504,505],"a",{"href":68},[506,507,508],"code",{},"Contract"," carries every route's\nmethod, path, schemas, and scopes. Point a generator at it and you get a standards-compliant OpenAPI 3.1\ndocument that reads the same schemas validating incoming requests, so your docs can't drift from what the\nserver actually accepts.",[499,511,512,513,517,518,521],{},"Generation happens at ",[514,515,516],"strong",{},"runtime",": ",[506,519,520],{},"OpenApiGenerator"," is a plain class you construct and call from a\ncontroller — there's no plugin to register.",[523,524,526],"h2",{"id":525},"install-the-package","Install the package",[528,529,530,555,570,584],"code-group",{},[531,532,538],"pre",{"className":533,"code":534,"filename":535,"language":536,"meta":537,"style":537},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm add @heximon\u002Fopenapi\n","pnpm","bash","",[506,539,540],{"__ignoreMap":537},[541,542,545,548,552],"span",{"class":543,"line":544},"line",1,[541,546,535],{"class":547},"sBMFI",[541,549,551],{"class":550},"sfazB"," add",[541,553,554],{"class":550}," @heximon\u002Fopenapi\n",[531,556,559],{"className":533,"code":557,"filename":558,"language":536,"meta":537,"style":537},"npm install @heximon\u002Fopenapi\n","npm",[506,560,561],{"__ignoreMap":537},[541,562,563,565,568],{"class":543,"line":544},[541,564,558],{"class":547},[541,566,567],{"class":550}," install",[541,569,554],{"class":550},[531,571,574],{"className":533,"code":572,"filename":573,"language":536,"meta":537,"style":537},"yarn add @heximon\u002Fopenapi\n","yarn",[506,575,576],{"__ignoreMap":537},[541,577,578,580,582],{"class":543,"line":544},[541,579,573],{"class":547},[541,581,551],{"class":550},[541,583,554],{"class":550},[531,585,588],{"className":533,"code":586,"filename":587,"language":536,"meta":537,"style":537},"bun add @heximon\u002Fopenapi\n","bun",[506,589,590],{"__ignoreMap":537},[541,591,592,594,596],{"class":543,"line":544},[541,593,587],{"class":547},[541,595,551],{"class":550},[541,597,554],{"class":550},[499,599,600,601,604],{},"There's nothing to add to ",[506,602,603],{},"vite.config.ts",". The Scalar and Swagger UI renderers load their JavaScript\nfrom a CDN at view time, so you don't install a UI SDK either.",[523,606,608],{"id":607},"generate-the-document","Generate the document",[499,610,611,614,615,618,619,622,623,626,627,626,630,633,634,637,638,641,642,645],{},[506,612,613],{},"new OpenApiGenerator(options)"," takes ",[506,616,617],{},"info"," (title + version), the ",[506,620,621],{},"contracts"," to document, and optional\n",[506,624,625],{},"servers",", ",[506,628,629],{},"security",[506,631,632],{},"setOperationId",", and ",[506,635,636],{},"jsonQuery",". Call ",[506,639,640],{},"generate()"," to build the document — it\nbuilds ",[514,643,644],{},"once"," and returns the same memoized object on every subsequent call.",[531,647,652],{"className":648,"code":649,"filename":650,"language":651,"meta":537,"style":537},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { OpenApiGenerator, Scalar, SecurityScheme } from \"@heximon\u002Fopenapi\";\nimport { ProductsApi } from \"..\u002Fproducts\u002Fproduct.api\";\n\nexport const documentPath = \"\u002Fopenapi.json\";\n\nexport const openApiGenerator = new OpenApiGenerator({\n  info: {\n    title: \"Products API\",\n    version: \"1.0.0\",\n    description: \"A small Heximon API documented with OpenAPI 3.1.\",\n  },\n  servers: [{ url: \"http:\u002F\u002Flocalhost:3000\", description: \"Local dev server\" }],\n  contracts: [ProductsApi],\n  security: {\n    schemes: [SecurityScheme.bearer(\"bearerAuth\", { bearerFormat: \"JWT\" })],\n    default: { scheme: \"bearerAuth\" },\n    \u002F\u002F GET routes are public; everything else requires the bearer token.\n    requirementFor: (route) => (route.method === \"GET\" ? [] : undefined),\n  },\n});\n\nexport const docsUserInterface = new Scalar({\n  title: \"Products API — Reference\",\n  documentPath,\n});\n","src\u002Fdocs\u002Fopenapi.ts","ts",[506,653,654,697,720,727,752,757,781,794,812,829,846,852,899,912,922,971,995,1002,1056,1061,1071,1076,1096,1113,1121],{"__ignoreMap":537},[541,655,656,660,664,668,671,674,676,679,682,685,688,691,694],{"class":543,"line":544},[541,657,659],{"class":658},"s7zQu","import",[541,661,663],{"class":662},"sMK4o"," {",[541,665,667],{"class":666},"sTEyZ"," OpenApiGenerator",[541,669,670],{"class":662},",",[541,672,673],{"class":666}," Scalar",[541,675,670],{"class":662},[541,677,678],{"class":666}," SecurityScheme",[541,680,681],{"class":662}," }",[541,683,684],{"class":658}," from",[541,686,687],{"class":662}," \"",[541,689,690],{"class":550},"@heximon\u002Fopenapi",[541,692,693],{"class":662},"\"",[541,695,696],{"class":662},";\n",[541,698,700,702,704,707,709,711,713,716,718],{"class":543,"line":699},2,[541,701,659],{"class":658},[541,703,663],{"class":662},[541,705,706],{"class":666}," ProductsApi",[541,708,681],{"class":662},[541,710,684],{"class":658},[541,712,687],{"class":662},[541,714,715],{"class":550},"..\u002Fproducts\u002Fproduct.api",[541,717,693],{"class":662},[541,719,696],{"class":662},[541,721,723],{"class":543,"line":722},3,[541,724,726],{"emptyLinePlaceholder":725},true,"\n",[541,728,730,733,737,740,743,745,748,750],{"class":543,"line":729},4,[541,731,732],{"class":658},"export",[541,734,736],{"class":735},"spNyl"," const",[541,738,739],{"class":666}," documentPath ",[541,741,742],{"class":662},"=",[541,744,687],{"class":662},[541,746,747],{"class":550},"\u002Fopenapi.json",[541,749,693],{"class":662},[541,751,696],{"class":662},[541,753,755],{"class":543,"line":754},5,[541,756,726],{"emptyLinePlaceholder":725},[541,758,760,762,764,767,769,772,775,778],{"class":543,"line":759},6,[541,761,732],{"class":658},[541,763,736],{"class":735},[541,765,766],{"class":666}," openApiGenerator ",[541,768,742],{"class":662},[541,770,771],{"class":662}," new",[541,773,667],{"class":774},"s2Zo4",[541,776,777],{"class":666},"(",[541,779,780],{"class":662},"{\n",[541,782,784,788,791],{"class":543,"line":783},7,[541,785,787],{"class":786},"swJcz","  info",[541,789,790],{"class":662},":",[541,792,793],{"class":662}," {\n",[541,795,797,800,802,804,807,809],{"class":543,"line":796},8,[541,798,799],{"class":786},"    title",[541,801,790],{"class":662},[541,803,687],{"class":662},[541,805,806],{"class":550},"Products API",[541,808,693],{"class":662},[541,810,811],{"class":662},",\n",[541,813,815,818,820,822,825,827],{"class":543,"line":814},9,[541,816,817],{"class":786},"    version",[541,819,790],{"class":662},[541,821,687],{"class":662},[541,823,824],{"class":550},"1.0.0",[541,826,693],{"class":662},[541,828,811],{"class":662},[541,830,832,835,837,839,842,844],{"class":543,"line":831},10,[541,833,834],{"class":786},"    description",[541,836,790],{"class":662},[541,838,687],{"class":662},[541,840,841],{"class":550},"A small Heximon API documented with OpenAPI 3.1.",[541,843,693],{"class":662},[541,845,811],{"class":662},[541,847,849],{"class":543,"line":848},11,[541,850,851],{"class":662},"  },\n",[541,853,855,858,860,863,866,869,871,873,876,878,880,883,885,887,890,892,894,897],{"class":543,"line":854},12,[541,856,857],{"class":786},"  servers",[541,859,790],{"class":662},[541,861,862],{"class":666}," [",[541,864,865],{"class":662},"{",[541,867,868],{"class":786}," url",[541,870,790],{"class":662},[541,872,687],{"class":662},[541,874,875],{"class":550},"http:\u002F\u002Flocalhost:3000",[541,877,693],{"class":662},[541,879,670],{"class":662},[541,881,882],{"class":786}," description",[541,884,790],{"class":662},[541,886,687],{"class":662},[541,888,889],{"class":550},"Local dev server",[541,891,693],{"class":662},[541,893,681],{"class":662},[541,895,896],{"class":666},"]",[541,898,811],{"class":662},[541,900,902,905,907,910],{"class":543,"line":901},13,[541,903,904],{"class":786},"  contracts",[541,906,790],{"class":662},[541,908,909],{"class":666}," [ProductsApi]",[541,911,811],{"class":662},[541,913,915,918,920],{"class":543,"line":914},14,[541,916,917],{"class":786},"  security",[541,919,790],{"class":662},[541,921,793],{"class":662},[541,923,925,928,930,933,936,939,941,943,946,948,950,952,955,957,959,962,964,966,969],{"class":543,"line":924},15,[541,926,927],{"class":786},"    schemes",[541,929,790],{"class":662},[541,931,932],{"class":666}," [SecurityScheme",[541,934,935],{"class":662},".",[541,937,938],{"class":774},"bearer",[541,940,777],{"class":666},[541,942,693],{"class":662},[541,944,945],{"class":550},"bearerAuth",[541,947,693],{"class":662},[541,949,670],{"class":662},[541,951,663],{"class":662},[541,953,954],{"class":786}," bearerFormat",[541,956,790],{"class":662},[541,958,687],{"class":662},[541,960,961],{"class":550},"JWT",[541,963,693],{"class":662},[541,965,681],{"class":662},[541,967,968],{"class":666},")]",[541,970,811],{"class":662},[541,972,974,977,979,981,984,986,988,990,992],{"class":543,"line":973},16,[541,975,976],{"class":786},"    default",[541,978,790],{"class":662},[541,980,663],{"class":662},[541,982,983],{"class":786}," scheme",[541,985,790],{"class":662},[541,987,687],{"class":662},[541,989,945],{"class":550},[541,991,693],{"class":662},[541,993,994],{"class":662}," },\n",[541,996,998],{"class":543,"line":997},17,[541,999,1001],{"class":1000},"sHwdD","    \u002F\u002F GET routes are public; everything else requires the bearer token.\n",[541,1003,1005,1008,1010,1013,1017,1020,1023,1026,1028,1031,1034,1036,1039,1041,1044,1047,1049,1052,1054],{"class":543,"line":1004},18,[541,1006,1007],{"class":774},"    requirementFor",[541,1009,790],{"class":662},[541,1011,1012],{"class":662}," (",[541,1014,1016],{"class":1015},"sHdIc","route",[541,1018,1019],{"class":662},")",[541,1021,1022],{"class":735}," =>",[541,1024,1025],{"class":666}," (route",[541,1027,935],{"class":662},[541,1029,1030],{"class":666},"method ",[541,1032,1033],{"class":662},"===",[541,1035,687],{"class":662},[541,1037,1038],{"class":550},"GET",[541,1040,693],{"class":662},[541,1042,1043],{"class":662}," ?",[541,1045,1046],{"class":666}," [] ",[541,1048,790],{"class":662},[541,1050,1051],{"class":662}," undefined",[541,1053,1019],{"class":666},[541,1055,811],{"class":662},[541,1057,1059],{"class":543,"line":1058},19,[541,1060,851],{"class":662},[541,1062,1064,1067,1069],{"class":543,"line":1063},20,[541,1065,1066],{"class":662},"}",[541,1068,1019],{"class":666},[541,1070,696],{"class":662},[541,1072,1074],{"class":543,"line":1073},21,[541,1075,726],{"emptyLinePlaceholder":725},[541,1077,1079,1081,1083,1086,1088,1090,1092,1094],{"class":543,"line":1078},22,[541,1080,732],{"class":658},[541,1082,736],{"class":735},[541,1084,1085],{"class":666}," docsUserInterface ",[541,1087,742],{"class":662},[541,1089,771],{"class":662},[541,1091,673],{"class":774},[541,1093,777],{"class":666},[541,1095,780],{"class":662},[541,1097,1099,1102,1104,1106,1109,1111],{"class":543,"line":1098},23,[541,1100,1101],{"class":786},"  title",[541,1103,790],{"class":662},[541,1105,687],{"class":662},[541,1107,1108],{"class":550},"Products API — Reference",[541,1110,693],{"class":662},[541,1112,811],{"class":662},[541,1114,1116,1119],{"class":543,"line":1115},24,[541,1117,1118],{"class":666},"  documentPath",[541,1120,811],{"class":662},[541,1122,1124,1126,1128],{"class":543,"line":1123},25,[541,1125,1066],{"class":662},[541,1127,1019],{"class":666},[541,1129,696],{"class":662},[499,1131,1132,1134,1135,1138],{},[506,1133,640],{}," emits one operation per route, deriving parameters, request body, and responses from that\nroute's schemas. A duplicate ",[506,1136,1137],{},"method + path"," across contracts throws the first time you serve it.",[523,1140,1142],{"id":1141},"map-contract-fields-to-the-spec","Map contract fields to the spec",[499,1144,1145],{},"The generator reads everything straight off the contract routes — there's no extra annotation layer:",[1147,1148,1149,1163],"table",{},[1150,1151,1152],"thead",{},[1153,1154,1155,1160],"tr",{},[1156,1157,1159],"th",{"align":1158},"left","Contract field",[1156,1161,1162],{"align":1158},"Becomes",[1164,1165,1166,1175,1196,1211,1221,1241,1254,1267],"tbody",{},[1153,1167,1168,1172],{},[1169,1170,1171],"td",{"align":1158},"Route path + method",[1169,1173,1174],{"align":1158},"An OpenAPI path item + operation",[1153,1176,1177,1193],{},[1169,1178,1179,1182,1183,1182,1186,1182,1189,1192],{"align":1158},[506,1180,1181],{},"body"," \u002F ",[506,1184,1185],{},"query",[506,1187,1188],{},"pathParams",[506,1190,1191],{},"headers"," schemas",[1169,1194,1195],{"align":1158},"The request body + parameters",[1153,1197,1198,1204],{},[1169,1199,1200,1203],{"align":1158},[506,1201,1202],{},".files(...)"," file fields",[1169,1205,1206,1207,1210],{"align":1158},"A ",[506,1208,1209],{},"multipart\u002Fform-data"," request body with a binary file leaf per field",[1153,1212,1213,1218],{},[1169,1214,1215,1192],{"align":1158},[506,1216,1217],{},"responses[status]",[1169,1219,1220],{"align":1158},"The response schemas, keyed by status",[1153,1222,1223,1234],{},[1169,1224,1225,1182,1228,1182,1231],{"align":1158},[506,1226,1227],{},".download()",[506,1229,1230],{},".html()",[506,1232,1233],{},".responseContentType(type)",[1169,1235,1236,1237],{"align":1158},"A non-JSON 2xx response body (binary \u002F text), see ",[503,1238,1240],{"href":1239},"#non-json-and-binary-responses","below",[1153,1242,1243,1251],{},[1169,1244,1245,1182,1248],{"align":1158},[506,1246,1247],{},"summary",[506,1249,1250],{},"description",[1169,1252,1253],{"align":1158},"Operation metadata",[1153,1255,1256,1261],{},[1169,1257,1258],{"align":1158},[506,1259,1260],{},".deprecated()",[1169,1262,1263,1266],{"align":1158},[506,1264,1265],{},"deprecated: true"," on the operation",[1153,1268,1269,1274],{},[1169,1270,1271],{"align":1158},[506,1272,1273],{},"scopes(...)",[1169,1275,1276,1277,1280],{"align":1158},"OAuth2 scope lists — the ",[514,1278,1279],{},"only"," inferred security field",[499,1282,1283,1284,1287,1288,1291,1292,1295,1296,1299],{},"Schemas embed as JSON Schema. A schema that carries a JSON-Schema conversion (SchemaObject fields via the\n",[506,1285,1286],{},"@heximon\u002Fschema\u002Fvalibot"," converter, Zod v4 standalone values, the framework's own response envelopes)\nembeds at full fidelity; one that can't be converted degrades to a permissive ",[506,1289,1290],{},"{}"," rather than failing\nthe whole document. A ",[506,1293,1294],{},"SchemaObject"," still renders its object shape — property keys and ",[506,1297,1298],{},"required"," list —\neven when the leaves are empty.",[1301,1302,1304],"h3",{"id":1303},"full-valibot-leaf-types","Full valibot leaf types",[499,1306,1307,1308,626,1311,1314,1315,1317,1318,1321,1322,1325,1326,1328],{},"Raw valibot schemas (",[506,1309,1310],{},"v.string()",[506,1312,1313],{},"v.object()",", …) do not carry the Standard-Schema JSON Schema\nsister-spec, so they degrade to ",[506,1316,1290],{}," by default. Pass a custom ",[506,1319,1320],{},"converter"," that chains\n",[506,1323,1324],{},"valibotJsonSchema"," from ",[506,1327,1286],{}," before the standard path to get full-fidelity output:",[531,1330,1333],{"className":648,"code":1331,"filename":1332,"language":651,"meta":537,"style":537},"import { valibotJsonSchema } from \"@heximon\u002Fschema\u002Fvalibot\";\nimport { OpenApiGenerator, StandardSchemaConverter } from \"@heximon\u002Fopenapi\";\n\nnew OpenApiGenerator({\n  contracts: [ProductsApi],\n  converter: (schema, direction) =>\n    valibotJsonSchema(schema, direction) ?? StandardSchemaConverter.convert(schema, direction),\n});\n","src\u002Fdocs\u002Fdocs.module.ts",[506,1334,1335,1356,1381,1385,1396,1406,1428,1460],{"__ignoreMap":537},[541,1336,1337,1339,1341,1344,1346,1348,1350,1352,1354],{"class":543,"line":544},[541,1338,659],{"class":658},[541,1340,663],{"class":662},[541,1342,1343],{"class":666}," valibotJsonSchema",[541,1345,681],{"class":662},[541,1347,684],{"class":658},[541,1349,687],{"class":662},[541,1351,1286],{"class":550},[541,1353,693],{"class":662},[541,1355,696],{"class":662},[541,1357,1358,1360,1362,1364,1366,1369,1371,1373,1375,1377,1379],{"class":543,"line":699},[541,1359,659],{"class":658},[541,1361,663],{"class":662},[541,1363,667],{"class":666},[541,1365,670],{"class":662},[541,1367,1368],{"class":666}," StandardSchemaConverter",[541,1370,681],{"class":662},[541,1372,684],{"class":658},[541,1374,687],{"class":662},[541,1376,690],{"class":550},[541,1378,693],{"class":662},[541,1380,696],{"class":662},[541,1382,1383],{"class":543,"line":722},[541,1384,726],{"emptyLinePlaceholder":725},[541,1386,1387,1390,1392,1394],{"class":543,"line":729},[541,1388,1389],{"class":662},"new",[541,1391,667],{"class":774},[541,1393,777],{"class":666},[541,1395,780],{"class":662},[541,1397,1398,1400,1402,1404],{"class":543,"line":754},[541,1399,904],{"class":786},[541,1401,790],{"class":662},[541,1403,909],{"class":666},[541,1405,811],{"class":662},[541,1407,1408,1411,1413,1415,1418,1420,1423,1425],{"class":543,"line":759},[541,1409,1410],{"class":774},"  converter",[541,1412,790],{"class":662},[541,1414,1012],{"class":662},[541,1416,1417],{"class":1015},"schema",[541,1419,670],{"class":662},[541,1421,1422],{"class":1015}," direction",[541,1424,1019],{"class":662},[541,1426,1427],{"class":735}," =>\n",[541,1429,1430,1433,1436,1438,1441,1444,1446,1448,1451,1453,1455,1458],{"class":543,"line":783},[541,1431,1432],{"class":774},"    valibotJsonSchema",[541,1434,1435],{"class":666},"(schema",[541,1437,670],{"class":662},[541,1439,1440],{"class":666}," direction) ",[541,1442,1443],{"class":662},"??",[541,1445,1368],{"class":666},[541,1447,935],{"class":662},[541,1449,1450],{"class":774},"convert",[541,1452,1435],{"class":666},[541,1454,670],{"class":662},[541,1456,1457],{"class":666}," direction)",[541,1459,811],{"class":662},[541,1461,1462,1464,1466],{"class":543,"line":796},[541,1463,1066],{"class":662},[541,1465,1019],{"class":666},[541,1467,696],{"class":662},[499,1469,1470,1472,1473,1476,1477,1480,1481,1484,1485,1487,1488,811,1491,1494],{},[506,1471,1324],{}," guards on ",[506,1474,1475],{},"~standard.vendor === \"valibot\""," and calls ",[506,1478,1479],{},"@valibot\u002Fto-json-schema","\ninternally. It returns ",[506,1482,1483],{},"undefined"," on any conversion failure, so the fallback to ",[506,1486,1290],{}," is preserved for\nschemas that cannot be represented in JSON Schema. Pipe actions that add constraints (e.g. ",[506,1489,1490],{},"v.email()",[506,1492,1493],{},"v.minLength()",") are included where supported; purely custom transforms are silently omitted.",[499,1496,1497,1498,1500,1501,1503,1504,1507,1508,1511,1512,1515,1516,1518,1519,935],{},"A route that declares file fields with ",[506,1499,1202],{}," emits a ",[506,1502,1209],{}," request body instead of\n",[506,1505,1506],{},"application\u002Fjson",": each file field becomes a ",[506,1509,1510],{},"{ type: \"string\", format: \"binary\" }"," leaf (an array of them\nfor a ",[506,1513,1514],{},"multiple"," field), merged with any scalar ",[506,1517,1181],{}," fields, and a field is required unless it declares\n",[506,1520,1521],{},"required: false",[523,1523,1525,1526],{"id":1524},"reuse-a-schema-with-ref","Reuse a schema with ",[506,1527,1528],{},"$ref",[499,1530,1531,1532,1537,1538,1541,1542,1545],{},"A schema that carries a ",[514,1533,1534],{},[506,1535,1536],{},"title"," is promoted into ",[506,1539,1540],{},"components.schemas"," (under that title) and every\nrequest\u002Fresponse occurrence becomes a ",[506,1543,1544],{},"{ $ref: \"#\u002Fcomponents\u002Fschemas\u002F\u003Ctitle>\" }"," — so a DTO shared across\nroutes appears once, not duplicated inline.",[499,1547,1548,1549,1551,1552,1555,1556,1559],{},"Within one ",[506,1550,640],{}," a schema reused across routes is also\ndeduplicated by object identity. An ",[514,1553,1554],{},"untitled"," schema inlines exactly as before. To force an untitled\nschema into components (or override the promoted name), pass a ",[506,1557,1558],{},"schemaName(schema, fallback)"," resolver:",[531,1561,1563],{"className":648,"code":1562,"filename":650,"language":651,"meta":537,"style":537},"new OpenApiGenerator({\n  info: { title: \"Products API\", version: \"1.0.0\" },\n  contracts: [ProductsApi],\n  \u002F\u002F Promote a normally-inline schema under an explicit name; return undefined to keep the default.\n  schemaName: (_schema, fallback) => fallback ?? \"AnonymousResult\",\n});\n",[506,1564,1565,1575,1609,1619,1624,1659],{"__ignoreMap":537},[541,1566,1567,1569,1571,1573],{"class":543,"line":544},[541,1568,1389],{"class":662},[541,1570,667],{"class":774},[541,1572,777],{"class":666},[541,1574,780],{"class":662},[541,1576,1577,1579,1581,1583,1586,1588,1590,1592,1594,1596,1599,1601,1603,1605,1607],{"class":543,"line":699},[541,1578,787],{"class":786},[541,1580,790],{"class":662},[541,1582,663],{"class":662},[541,1584,1585],{"class":786}," title",[541,1587,790],{"class":662},[541,1589,687],{"class":662},[541,1591,806],{"class":550},[541,1593,693],{"class":662},[541,1595,670],{"class":662},[541,1597,1598],{"class":786}," version",[541,1600,790],{"class":662},[541,1602,687],{"class":662},[541,1604,824],{"class":550},[541,1606,693],{"class":662},[541,1608,994],{"class":662},[541,1610,1611,1613,1615,1617],{"class":543,"line":722},[541,1612,904],{"class":786},[541,1614,790],{"class":662},[541,1616,909],{"class":666},[541,1618,811],{"class":662},[541,1620,1621],{"class":543,"line":729},[541,1622,1623],{"class":1000},"  \u002F\u002F Promote a normally-inline schema under an explicit name; return undefined to keep the default.\n",[541,1625,1626,1629,1631,1633,1636,1638,1641,1643,1645,1648,1650,1652,1655,1657],{"class":543,"line":754},[541,1627,1628],{"class":774},"  schemaName",[541,1630,790],{"class":662},[541,1632,1012],{"class":662},[541,1634,1635],{"class":1015},"_schema",[541,1637,670],{"class":662},[541,1639,1640],{"class":1015}," fallback",[541,1642,1019],{"class":662},[541,1644,1022],{"class":735},[541,1646,1647],{"class":666}," fallback ",[541,1649,1443],{"class":662},[541,1651,687],{"class":662},[541,1653,1654],{"class":550},"AnonymousResult",[541,1656,693],{"class":662},[541,1658,811],{"class":662},[541,1660,1661,1663,1665],{"class":543,"line":759},[541,1662,1066],{"class":662},[541,1664,1019],{"class":666},[541,1666,696],{"class":662},[499,1668,1669,1670,1673],{},"There's no auto-counter: two structurally-distinct schemas resolving the same name throw a collision error,\nso give each a unique title (or ",[506,1671,1672],{},"schemaName",").",[523,1675,1677],{"id":1676},"non-json-and-binary-responses","Non-JSON and binary responses",[499,1679,1680,1681,1684,1685,1687,1688,1182,1690,1692,1693,1695,1696,1699,1700,1703,1704,790],{},"A route whose ",[514,1682,1683],{},"2xx"," body is binary or text — a file download, an HTML page — declares its wire type with\n",[506,1686,1233],{},", or the ",[506,1689,1227],{},[506,1691,1230],{}," sugar. The generator emits that media type for\nthe 2xx response (",[506,1694,1510],{}," for binary, ",[506,1697,1698],{},"{ type: \"string\" }"," for ",[506,1701,1702],{},"text\u002F*","),\nwhile ",[514,1705,1706,1707],{},"declared error statuses stay ",[506,1708,1506],{},[531,1710,1713],{"className":648,"code":1711,"filename":1712,"language":651,"meta":537,"style":537},"import { Contract, Route } from \"@heximon\u002Fcontract\";\nimport { keyParams, notFound } from \".\u002Fupload.schema\";\n\nexport class UploadsApi extends Contract({\n  prefix: \"\u002Fapi\u002Fuploads\",\n  routes: {\n    \u002F\u002F 200 is application\u002Foctet-stream (a Blob); the declared 404 stays JSON.\n    download: Route.get(\"\u002F:key\").pathParams(keyParams).download().responses({ 404: notFound }),\n  },\n}) {}\n","src\u002Fuploads\u002Fupload.api.ts",[506,1714,1715,1742,1769,1773,1792,1808,1817,1822,1886,1890],{"__ignoreMap":537},[541,1716,1717,1719,1721,1724,1726,1729,1731,1733,1735,1738,1740],{"class":543,"line":544},[541,1718,659],{"class":658},[541,1720,663],{"class":662},[541,1722,1723],{"class":666}," Contract",[541,1725,670],{"class":662},[541,1727,1728],{"class":666}," Route",[541,1730,681],{"class":662},[541,1732,684],{"class":658},[541,1734,687],{"class":662},[541,1736,1737],{"class":550},"@heximon\u002Fcontract",[541,1739,693],{"class":662},[541,1741,696],{"class":662},[541,1743,1744,1746,1748,1751,1753,1756,1758,1760,1762,1765,1767],{"class":543,"line":699},[541,1745,659],{"class":658},[541,1747,663],{"class":662},[541,1749,1750],{"class":666}," keyParams",[541,1752,670],{"class":662},[541,1754,1755],{"class":666}," notFound",[541,1757,681],{"class":662},[541,1759,684],{"class":658},[541,1761,687],{"class":662},[541,1763,1764],{"class":550},".\u002Fupload.schema",[541,1766,693],{"class":662},[541,1768,696],{"class":662},[541,1770,1771],{"class":543,"line":722},[541,1772,726],{"emptyLinePlaceholder":725},[541,1774,1775,1777,1780,1783,1786,1788,1790],{"class":543,"line":729},[541,1776,732],{"class":658},[541,1778,1779],{"class":735}," class",[541,1781,1782],{"class":547}," UploadsApi",[541,1784,1785],{"class":735}," extends",[541,1787,1723],{"class":774},[541,1789,777],{"class":666},[541,1791,780],{"class":662},[541,1793,1794,1797,1799,1801,1804,1806],{"class":543,"line":754},[541,1795,1796],{"class":786},"  prefix",[541,1798,790],{"class":662},[541,1800,687],{"class":662},[541,1802,1803],{"class":550},"\u002Fapi\u002Fuploads",[541,1805,693],{"class":662},[541,1807,811],{"class":662},[541,1809,1810,1813,1815],{"class":543,"line":759},[541,1811,1812],{"class":786},"  routes",[541,1814,790],{"class":662},[541,1816,793],{"class":662},[541,1818,1819],{"class":543,"line":783},[541,1820,1821],{"class":1000},"    \u002F\u002F 200 is application\u002Foctet-stream (a Blob); the declared 404 stays JSON.\n",[541,1823,1824,1827,1829,1831,1833,1836,1838,1840,1843,1845,1847,1849,1851,1854,1856,1859,1862,1864,1867,1869,1871,1875,1877,1880,1882,1884],{"class":543,"line":796},[541,1825,1826],{"class":786},"    download",[541,1828,790],{"class":662},[541,1830,1728],{"class":666},[541,1832,935],{"class":662},[541,1834,1835],{"class":774},"get",[541,1837,777],{"class":666},[541,1839,693],{"class":662},[541,1841,1842],{"class":550},"\u002F:key",[541,1844,693],{"class":662},[541,1846,1019],{"class":666},[541,1848,935],{"class":662},[541,1850,1188],{"class":774},[541,1852,1853],{"class":666},"(keyParams)",[541,1855,935],{"class":662},[541,1857,1858],{"class":774},"download",[541,1860,1861],{"class":666},"()",[541,1863,935],{"class":662},[541,1865,1866],{"class":774},"responses",[541,1868,777],{"class":666},[541,1870,865],{"class":662},[541,1872,1874],{"class":1873},"sbssI"," 404",[541,1876,790],{"class":662},[541,1878,1879],{"class":666}," notFound ",[541,1881,1066],{"class":662},[541,1883,1019],{"class":666},[541,1885,811],{"class":662},[541,1887,1888],{"class":543,"line":814},[541,1889,851],{"class":662},[541,1891,1892,1894,1897],{"class":543,"line":831},[541,1893,1066],{"class":662},[541,1895,1896],{"class":666},") ",[541,1898,1899],{"class":662},"{}\n",[499,1901,1902,1903,1906,1907,1910,1911,1914,1915,1917],{},"The matching ",[503,1904,1905],{"href":73},"client"," typing decodes the 2xx body to a ",[506,1908,1909],{},"Blob"," (binary) or ",[506,1912,1913],{},"string","\n(",[506,1916,1702],{},") and skips response-schema validation for it — a 2xx schema declared on a non-JSON route is\ninformational (OpenAPI only), never run.",[523,1919,1921],{"id":1920},"deprecate-a-route","Deprecate a route",[499,1923,1924,1925,1927,1928,1930],{},"Mark a route as deprecated with ",[506,1926,1260],{}," on the builder. The OpenAPI generator reads the flag and\nemits ",[506,1929,1265],{}," on the operation — tooling that renders the spec (Scalar, Swagger UI) will\nvisually strike through or dim it. The route remains fully operational; the flag is a signal to callers\nthat the route will be removed in a future API version.",[531,1932,1935],{"className":648,"code":1933,"filename":1934,"language":651,"meta":537,"style":537},"import { Contract, Route } from \"@heximon\u002Fcontract\";\nimport { productList } from \".\u002Fproduct.schema\";\n\nexport class ProductsApi extends Contract({\n  prefix: \"\u002Fapi\u002Fproducts\",\n  routes: {\n    \u002F\u002F Replacement route — callers should migrate to this one.\n    list: Route.get(\"\u002Fv2\")\n      .summary(\"List products\")\n      .responses({ 200: productList }),\n\n    \u002F\u002F Deprecated: kept for backwards compatibility, will be removed.\n    listLegacy: Route.get(\"\u002F\")\n      .deprecated()\n      .summary(\"List products (deprecated — use \u002Fv2 instead)\")\n      .responses({ 200: productList }),\n  },\n}) {}\n","src\u002Fproducts\u002Fproduct.api.ts",[506,1936,1937,1961,1983,1987,2003,2018,2026,2031,2056,2074,2098,2102,2107,2131,2141,2158,2180,2184],{"__ignoreMap":537},[541,1938,1939,1941,1943,1945,1947,1949,1951,1953,1955,1957,1959],{"class":543,"line":544},[541,1940,659],{"class":658},[541,1942,663],{"class":662},[541,1944,1723],{"class":666},[541,1946,670],{"class":662},[541,1948,1728],{"class":666},[541,1950,681],{"class":662},[541,1952,684],{"class":658},[541,1954,687],{"class":662},[541,1956,1737],{"class":550},[541,1958,693],{"class":662},[541,1960,696],{"class":662},[541,1962,1963,1965,1967,1970,1972,1974,1976,1979,1981],{"class":543,"line":699},[541,1964,659],{"class":658},[541,1966,663],{"class":662},[541,1968,1969],{"class":666}," productList",[541,1971,681],{"class":662},[541,1973,684],{"class":658},[541,1975,687],{"class":662},[541,1977,1978],{"class":550},".\u002Fproduct.schema",[541,1980,693],{"class":662},[541,1982,696],{"class":662},[541,1984,1985],{"class":543,"line":722},[541,1986,726],{"emptyLinePlaceholder":725},[541,1988,1989,1991,1993,1995,1997,1999,2001],{"class":543,"line":729},[541,1990,732],{"class":658},[541,1992,1779],{"class":735},[541,1994,706],{"class":547},[541,1996,1785],{"class":735},[541,1998,1723],{"class":774},[541,2000,777],{"class":666},[541,2002,780],{"class":662},[541,2004,2005,2007,2009,2011,2014,2016],{"class":543,"line":754},[541,2006,1796],{"class":786},[541,2008,790],{"class":662},[541,2010,687],{"class":662},[541,2012,2013],{"class":550},"\u002Fapi\u002Fproducts",[541,2015,693],{"class":662},[541,2017,811],{"class":662},[541,2019,2020,2022,2024],{"class":543,"line":759},[541,2021,1812],{"class":786},[541,2023,790],{"class":662},[541,2025,793],{"class":662},[541,2027,2028],{"class":543,"line":783},[541,2029,2030],{"class":1000},"    \u002F\u002F Replacement route — callers should migrate to this one.\n",[541,2032,2033,2036,2038,2040,2042,2044,2046,2048,2051,2053],{"class":543,"line":796},[541,2034,2035],{"class":786},"    list",[541,2037,790],{"class":662},[541,2039,1728],{"class":666},[541,2041,935],{"class":662},[541,2043,1835],{"class":774},[541,2045,777],{"class":666},[541,2047,693],{"class":662},[541,2049,2050],{"class":550},"\u002Fv2",[541,2052,693],{"class":662},[541,2054,2055],{"class":666},")\n",[541,2057,2058,2061,2063,2065,2067,2070,2072],{"class":543,"line":814},[541,2059,2060],{"class":662},"      .",[541,2062,1247],{"class":774},[541,2064,777],{"class":666},[541,2066,693],{"class":662},[541,2068,2069],{"class":550},"List products",[541,2071,693],{"class":662},[541,2073,2055],{"class":666},[541,2075,2076,2078,2080,2082,2084,2087,2089,2092,2094,2096],{"class":543,"line":831},[541,2077,2060],{"class":662},[541,2079,1866],{"class":774},[541,2081,777],{"class":666},[541,2083,865],{"class":662},[541,2085,2086],{"class":1873}," 200",[541,2088,790],{"class":662},[541,2090,2091],{"class":666}," productList ",[541,2093,1066],{"class":662},[541,2095,1019],{"class":666},[541,2097,811],{"class":662},[541,2099,2100],{"class":543,"line":848},[541,2101,726],{"emptyLinePlaceholder":725},[541,2103,2104],{"class":543,"line":854},[541,2105,2106],{"class":1000},"    \u002F\u002F Deprecated: kept for backwards compatibility, will be removed.\n",[541,2108,2109,2112,2114,2116,2118,2120,2122,2124,2127,2129],{"class":543,"line":901},[541,2110,2111],{"class":786},"    listLegacy",[541,2113,790],{"class":662},[541,2115,1728],{"class":666},[541,2117,935],{"class":662},[541,2119,1835],{"class":774},[541,2121,777],{"class":666},[541,2123,693],{"class":662},[541,2125,2126],{"class":550},"\u002F",[541,2128,693],{"class":662},[541,2130,2055],{"class":666},[541,2132,2133,2135,2138],{"class":543,"line":914},[541,2134,2060],{"class":662},[541,2136,2137],{"class":774},"deprecated",[541,2139,2140],{"class":666},"()\n",[541,2142,2143,2145,2147,2149,2151,2154,2156],{"class":543,"line":924},[541,2144,2060],{"class":662},[541,2146,1247],{"class":774},[541,2148,777],{"class":666},[541,2150,693],{"class":662},[541,2152,2153],{"class":550},"List products (deprecated — use \u002Fv2 instead)",[541,2155,693],{"class":662},[541,2157,2055],{"class":666},[541,2159,2160,2162,2164,2166,2168,2170,2172,2174,2176,2178],{"class":543,"line":973},[541,2161,2060],{"class":662},[541,2163,1866],{"class":774},[541,2165,777],{"class":666},[541,2167,865],{"class":662},[541,2169,2086],{"class":1873},[541,2171,790],{"class":662},[541,2173,2091],{"class":666},[541,2175,1066],{"class":662},[541,2177,1019],{"class":666},[541,2179,811],{"class":662},[541,2181,2182],{"class":543,"line":997},[541,2183,851],{"class":662},[541,2185,2186,2188,2190],{"class":543,"line":1004},[541,2187,1066],{"class":662},[541,2189,1896],{"class":666},[541,2191,1899],{"class":662},[499,2193,2194,2195,2198,2199,626,2202,2205],{},"The recommended approach for introducing breaking changes is ",[514,2196,2197],{},"prefix-versioning",": serve the next\nversion of an API surface under a new path prefix (",[506,2200,2201],{},"\u002Fv2\u002F",[506,2203,2204],{},"\u002Fapi\u002Fv2\u002F","), keep the old prefix alive and\ndeprecated while clients migrate, then remove the old routes once adoption is complete.",[499,2207,1206,2208,2210,2211,2214,2215,2217],{},[506,2209,508],{},"\nmaps cleanly to a versioned prefix — author a ",[506,2212,2213],{},"v2"," contract with the updated routes and register both\ncontracts with the same ",[506,2216,520],{}," to document both in one spec.",[523,2219,2221],{"id":2220},"register-security-manually","Register security manually",[499,2223,2224,2225,2228],{},"Security schemes are ",[514,2226,2227],{},"never inferred from your middleware"," — you register each scheme explicitly:",[1147,2230,2231,2241],{},[1150,2232,2233],{},[1153,2234,2235,2238],{},[1156,2236,2237],{"align":1158},"Factory method",[1156,2239,2240],{"align":1158},"Scheme",[1164,2242,2243,2253,2263,2273,2283],{},[1153,2244,2245,2250],{},[1169,2246,2247],{"align":1158},[506,2248,2249],{},"SecurityScheme.bearer(name, { bearerFormat?, description? })",[1169,2251,2252],{"align":1158},"HTTP bearer token",[1153,2254,2255,2260],{},[1169,2256,2257],{"align":1158},[506,2258,2259],{},"SecurityScheme.basic(name, { description? })",[1169,2261,2262],{"align":1158},"HTTP basic auth",[1153,2264,2265,2270],{},[1169,2266,2267],{"align":1158},[506,2268,2269],{},"SecurityScheme.apiKey(name, { in, name, description? })",[1169,2271,2272],{"align":1158},"API key in a header, query, or cookie",[1153,2274,2275,2280],{},[1169,2276,2277],{"align":1158},[506,2278,2279],{},"SecurityScheme.oauth2(name, { flows, description? })",[1169,2281,2282],{"align":1158},"OAuth2 (carries scopes)",[1153,2284,2285,2290],{},[1169,2286,2287],{"align":1158},[506,2288,2289],{},"SecurityScheme.openIdConnect(name, { openIdConnectUrl, description? })",[1169,2291,2292],{"align":1158},"OpenID Connect (carries scopes)",[499,2294,2295,2296,2298],{},"A route's ",[506,2297,1273],{}," is the single field the generator infers. Only OAuth2 and OpenID Connect schemes\ncan carry scopes, so they flow into those operations' required-scope lists; every other scheme type gets\nan empty scope list.",[531,2300,2302],{"className":648,"code":2301,"filename":650,"language":651,"meta":537,"style":537},"import { OpenApiGenerator, SecurityScheme } from \"@heximon\u002Fopenapi\";\nimport { UsersApi } from \"..\u002Fusers\u002Fusers.api\";\n\nconst document = new OpenApiGenerator({\n  info: { title: \"Users API\", version: \"1.0.0\" },\n  contracts: [UsersApi],\n  security: {\n    schemes: [\n      SecurityScheme.oauth2(\"oauth2\", {\n        flows: {\n          authorizationCode: {\n            authorizationUrl: \"https:\u002F\u002Fauth.example.com\u002Fauthorize\",\n            tokenUrl: \"https:\u002F\u002Fauth.example.com\u002Ftoken\",\n            scopes: { \"users.read\": \"Read users\", \"users.write\": \"Write users\" },\n          },\n        },\n      }),\n    ],\n    default: { scheme: \"oauth2\" },\n  },\n}).generate();\n\n\u002F\u002F A route that declared .scopes(\"users.read\") now requires it:\n\u002F\u002F document.paths[\"\u002Fapi\u002Fusers\u002F{id}\"].get.security === [{ oauth2: [\"users.read\"] }]\n",[506,2303,2304,2328,2350,2354,2372,2405,2416,2424,2433,2455,2464,2473,2489,2505,2550,2555,2560,2569,2576,2596,2600,2615,2619,2624],{"__ignoreMap":537},[541,2305,2306,2308,2310,2312,2314,2316,2318,2320,2322,2324,2326],{"class":543,"line":544},[541,2307,659],{"class":658},[541,2309,663],{"class":662},[541,2311,667],{"class":666},[541,2313,670],{"class":662},[541,2315,678],{"class":666},[541,2317,681],{"class":662},[541,2319,684],{"class":658},[541,2321,687],{"class":662},[541,2323,690],{"class":550},[541,2325,693],{"class":662},[541,2327,696],{"class":662},[541,2329,2330,2332,2334,2337,2339,2341,2343,2346,2348],{"class":543,"line":699},[541,2331,659],{"class":658},[541,2333,663],{"class":662},[541,2335,2336],{"class":666}," UsersApi",[541,2338,681],{"class":662},[541,2340,684],{"class":658},[541,2342,687],{"class":662},[541,2344,2345],{"class":550},"..\u002Fusers\u002Fusers.api",[541,2347,693],{"class":662},[541,2349,696],{"class":662},[541,2351,2352],{"class":543,"line":722},[541,2353,726],{"emptyLinePlaceholder":725},[541,2355,2356,2359,2362,2364,2366,2368,2370],{"class":543,"line":729},[541,2357,2358],{"class":735},"const",[541,2360,2361],{"class":666}," document ",[541,2363,742],{"class":662},[541,2365,771],{"class":662},[541,2367,667],{"class":774},[541,2369,777],{"class":666},[541,2371,780],{"class":662},[541,2373,2374,2376,2378,2380,2382,2384,2386,2389,2391,2393,2395,2397,2399,2401,2403],{"class":543,"line":754},[541,2375,787],{"class":786},[541,2377,790],{"class":662},[541,2379,663],{"class":662},[541,2381,1585],{"class":786},[541,2383,790],{"class":662},[541,2385,687],{"class":662},[541,2387,2388],{"class":550},"Users API",[541,2390,693],{"class":662},[541,2392,670],{"class":662},[541,2394,1598],{"class":786},[541,2396,790],{"class":662},[541,2398,687],{"class":662},[541,2400,824],{"class":550},[541,2402,693],{"class":662},[541,2404,994],{"class":662},[541,2406,2407,2409,2411,2414],{"class":543,"line":759},[541,2408,904],{"class":786},[541,2410,790],{"class":662},[541,2412,2413],{"class":666}," [UsersApi]",[541,2415,811],{"class":662},[541,2417,2418,2420,2422],{"class":543,"line":783},[541,2419,917],{"class":786},[541,2421,790],{"class":662},[541,2423,793],{"class":662},[541,2425,2426,2428,2430],{"class":543,"line":796},[541,2427,927],{"class":786},[541,2429,790],{"class":662},[541,2431,2432],{"class":666}," [\n",[541,2434,2435,2438,2440,2443,2445,2447,2449,2451,2453],{"class":543,"line":814},[541,2436,2437],{"class":666},"      SecurityScheme",[541,2439,935],{"class":662},[541,2441,2442],{"class":774},"oauth2",[541,2444,777],{"class":666},[541,2446,693],{"class":662},[541,2448,2442],{"class":550},[541,2450,693],{"class":662},[541,2452,670],{"class":662},[541,2454,793],{"class":662},[541,2456,2457,2460,2462],{"class":543,"line":831},[541,2458,2459],{"class":786},"        flows",[541,2461,790],{"class":662},[541,2463,793],{"class":662},[541,2465,2466,2469,2471],{"class":543,"line":848},[541,2467,2468],{"class":786},"          authorizationCode",[541,2470,790],{"class":662},[541,2472,793],{"class":662},[541,2474,2475,2478,2480,2482,2485,2487],{"class":543,"line":854},[541,2476,2477],{"class":786},"            authorizationUrl",[541,2479,790],{"class":662},[541,2481,687],{"class":662},[541,2483,2484],{"class":550},"https:\u002F\u002Fauth.example.com\u002Fauthorize",[541,2486,693],{"class":662},[541,2488,811],{"class":662},[541,2490,2491,2494,2496,2498,2501,2503],{"class":543,"line":901},[541,2492,2493],{"class":786},"            tokenUrl",[541,2495,790],{"class":662},[541,2497,687],{"class":662},[541,2499,2500],{"class":550},"https:\u002F\u002Fauth.example.com\u002Ftoken",[541,2502,693],{"class":662},[541,2504,811],{"class":662},[541,2506,2507,2510,2512,2514,2516,2519,2521,2523,2525,2528,2530,2532,2534,2537,2539,2541,2543,2546,2548],{"class":543,"line":914},[541,2508,2509],{"class":786},"            scopes",[541,2511,790],{"class":662},[541,2513,663],{"class":662},[541,2515,687],{"class":662},[541,2517,2518],{"class":786},"users.read",[541,2520,693],{"class":662},[541,2522,790],{"class":662},[541,2524,687],{"class":662},[541,2526,2527],{"class":550},"Read users",[541,2529,693],{"class":662},[541,2531,670],{"class":662},[541,2533,687],{"class":662},[541,2535,2536],{"class":786},"users.write",[541,2538,693],{"class":662},[541,2540,790],{"class":662},[541,2542,687],{"class":662},[541,2544,2545],{"class":550},"Write users",[541,2547,693],{"class":662},[541,2549,994],{"class":662},[541,2551,2552],{"class":543,"line":924},[541,2553,2554],{"class":662},"          },\n",[541,2556,2557],{"class":543,"line":973},[541,2558,2559],{"class":662},"        },\n",[541,2561,2562,2565,2567],{"class":543,"line":997},[541,2563,2564],{"class":662},"      }",[541,2566,1019],{"class":666},[541,2568,811],{"class":662},[541,2570,2571,2574],{"class":543,"line":1004},[541,2572,2573],{"class":666},"    ]",[541,2575,811],{"class":662},[541,2577,2578,2580,2582,2584,2586,2588,2590,2592,2594],{"class":543,"line":1058},[541,2579,976],{"class":786},[541,2581,790],{"class":662},[541,2583,663],{"class":662},[541,2585,983],{"class":786},[541,2587,790],{"class":662},[541,2589,687],{"class":662},[541,2591,2442],{"class":550},[541,2593,693],{"class":662},[541,2595,994],{"class":662},[541,2597,2598],{"class":543,"line":1063},[541,2599,851],{"class":662},[541,2601,2602,2604,2606,2608,2611,2613],{"class":543,"line":1073},[541,2603,1066],{"class":662},[541,2605,1019],{"class":666},[541,2607,935],{"class":662},[541,2609,2610],{"class":774},"generate",[541,2612,1861],{"class":666},[541,2614,696],{"class":662},[541,2616,2617],{"class":543,"line":1078},[541,2618,726],{"emptyLinePlaceholder":725},[541,2620,2621],{"class":543,"line":1098},[541,2622,2623],{"class":1000},"\u002F\u002F A route that declared .scopes(\"users.read\") now requires it:\n",[541,2625,2626],{"class":543,"line":1115},[541,2627,2628],{"class":1000},"\u002F\u002F document.paths[\"\u002Fapi\u002Fusers\u002F{id}\"].get.security === [{ oauth2: [\"users.read\"] }]\n",[499,2630,2631,2634,2635,2638,2639,2642,2643,2645],{},[506,2632,2633],{},"security.default"," applies one scheme to every operation. ",[506,2636,2637],{},"requirementFor(route, ctx)"," overrides per\noperation — return an explicit requirement list, ",[506,2640,2641],{},"[]"," to mark the operation public, or ",[506,2644,1483],{}," to\nfall back to the default.",[2647,2648,2650,2656,2657,2660,2661,2664,2665,2668,2669,2672],"callout",{"type":2649},"warning",[514,2651,1206,2652,2655],{},[506,2653,2654],{},"default"," scheme must be registered first."," ",[506,2658,2659],{},"security.default.scheme"," names one of the schemes you\npassed in ",[506,2662,2663],{},"schemes"," — if the name doesn't match, the constructor throws\n",[506,2666,2667],{},"[heximon-openapi] security.default references unregistered scheme \"X\" — add it to security.schemes.","\nReference it by the same ",[506,2670,2671],{},"name"," you gave the helper.",[523,2674,2676],{"id":2675},"serve-the-document-and-a-ui","Serve the document and a UI",[499,2678,2679,2680,2685],{},"The document is data and a renderer returns a self-contained HTML string, so a plain inline\n",[503,2681,2682],{"href":58},[506,2683,2684],{},"Controller"," serves both — the generator and renderer are module-level values\nyou call, not DI providers.",[531,2687,2690],{"className":648,"code":2688,"filename":2689,"language":651,"meta":537,"style":537},"import type { Controller, Get } from \"@heximon\u002Fhttp\";\nimport { docsUserInterface, openApiGenerator } from \".\u002Fopenapi\";\n\nexport class DocsController implements Controller {\n  \u002F\u002F GET \u002Fopenapi.json — the OpenAPI 3.1 document (built once, then memoized)\n  public async document(action: Get\u003C\"\u002Fopenapi.json\">) {\n    return openApiGenerator.generate();\n  }\n\n  \u002F\u002F GET \u002Fdocs — a Scalar reference page (text\u002Fhtml) that fetches \u002Fopenapi.json\n  public async ui(action: Get\u003C\"\u002Fdocs\">) {\n    return action.html(docsUserInterface.render());\n  }\n}\n","src\u002Fdocs\u002Fdocs.controller.ts",[506,2691,2692,2722,2749,2753,2769,2774,2808,2823,2828,2832,2837,2867,2894,2898],{"__ignoreMap":537},[541,2693,2694,2696,2699,2701,2704,2706,2709,2711,2713,2715,2718,2720],{"class":543,"line":544},[541,2695,659],{"class":658},[541,2697,2698],{"class":658}," type",[541,2700,663],{"class":662},[541,2702,2703],{"class":666}," Controller",[541,2705,670],{"class":662},[541,2707,2708],{"class":666}," Get",[541,2710,681],{"class":662},[541,2712,684],{"class":658},[541,2714,687],{"class":662},[541,2716,2717],{"class":550},"@heximon\u002Fhttp",[541,2719,693],{"class":662},[541,2721,696],{"class":662},[541,2723,2724,2726,2728,2731,2733,2736,2738,2740,2742,2745,2747],{"class":543,"line":699},[541,2725,659],{"class":658},[541,2727,663],{"class":662},[541,2729,2730],{"class":666}," docsUserInterface",[541,2732,670],{"class":662},[541,2734,2735],{"class":666}," openApiGenerator",[541,2737,681],{"class":662},[541,2739,684],{"class":658},[541,2741,687],{"class":662},[541,2743,2744],{"class":550},".\u002Fopenapi",[541,2746,693],{"class":662},[541,2748,696],{"class":662},[541,2750,2751],{"class":543,"line":722},[541,2752,726],{"emptyLinePlaceholder":725},[541,2754,2755,2757,2759,2762,2765,2767],{"class":543,"line":729},[541,2756,732],{"class":658},[541,2758,1779],{"class":735},[541,2760,2761],{"class":547}," DocsController",[541,2763,2764],{"class":735}," implements",[541,2766,2703],{"class":547},[541,2768,793],{"class":662},[541,2770,2771],{"class":543,"line":754},[541,2772,2773],{"class":1000},"  \u002F\u002F GET \u002Fopenapi.json — the OpenAPI 3.1 document (built once, then memoized)\n",[541,2775,2776,2779,2782,2785,2787,2790,2792,2794,2797,2799,2801,2803,2806],{"class":543,"line":759},[541,2777,2778],{"class":735},"  public",[541,2780,2781],{"class":735}," async",[541,2783,2784],{"class":786}," document",[541,2786,777],{"class":662},[541,2788,2789],{"class":1015},"action",[541,2791,790],{"class":662},[541,2793,2708],{"class":547},[541,2795,2796],{"class":662},"\u003C",[541,2798,693],{"class":662},[541,2800,747],{"class":550},[541,2802,693],{"class":662},[541,2804,2805],{"class":662},">)",[541,2807,793],{"class":662},[541,2809,2810,2813,2815,2817,2819,2821],{"class":543,"line":783},[541,2811,2812],{"class":658},"    return",[541,2814,2735],{"class":666},[541,2816,935],{"class":662},[541,2818,2610],{"class":774},[541,2820,1861],{"class":786},[541,2822,696],{"class":662},[541,2824,2825],{"class":543,"line":796},[541,2826,2827],{"class":662},"  }\n",[541,2829,2830],{"class":543,"line":814},[541,2831,726],{"emptyLinePlaceholder":725},[541,2833,2834],{"class":543,"line":831},[541,2835,2836],{"class":1000},"  \u002F\u002F GET \u002Fdocs — a Scalar reference page (text\u002Fhtml) that fetches \u002Fopenapi.json\n",[541,2838,2839,2841,2843,2846,2848,2850,2852,2854,2856,2858,2861,2863,2865],{"class":543,"line":848},[541,2840,2778],{"class":735},[541,2842,2781],{"class":735},[541,2844,2845],{"class":786}," ui",[541,2847,777],{"class":662},[541,2849,2789],{"class":1015},[541,2851,790],{"class":662},[541,2853,2708],{"class":547},[541,2855,2796],{"class":662},[541,2857,693],{"class":662},[541,2859,2860],{"class":550},"\u002Fdocs",[541,2862,693],{"class":662},[541,2864,2805],{"class":662},[541,2866,793],{"class":662},[541,2868,2869,2871,2874,2876,2879,2881,2884,2886,2889,2892],{"class":543,"line":854},[541,2870,2812],{"class":658},[541,2872,2873],{"class":666}," action",[541,2875,935],{"class":662},[541,2877,2878],{"class":774},"html",[541,2880,777],{"class":786},[541,2882,2883],{"class":666},"docsUserInterface",[541,2885,935],{"class":662},[541,2887,2888],{"class":774},"render",[541,2890,2891],{"class":786},"())",[541,2893,696],{"class":662},[541,2895,2896],{"class":543,"line":901},[541,2897,2827],{"class":662},[541,2899,2900],{"class":543,"line":914},[541,2901,2902],{"class":662},"}\n",[499,2904,2905],{},"The owning module declares only the controller:",[531,2907,2909],{"className":648,"code":2908,"filename":1332,"language":651,"meta":537,"style":537},"import { Module } from \"@heximon\u002Fruntime\";\nimport { DocsController } from \".\u002Fdocs.controller\";\n\nexport class DocsModule extends Module({\n  http: { controllers: [DocsController] },\n}) {}\n",[506,2910,2911,2933,2954,2958,2975,2995],{"__ignoreMap":537},[541,2912,2913,2915,2917,2920,2922,2924,2926,2929,2931],{"class":543,"line":544},[541,2914,659],{"class":658},[541,2916,663],{"class":662},[541,2918,2919],{"class":666}," Module",[541,2921,681],{"class":662},[541,2923,684],{"class":658},[541,2925,687],{"class":662},[541,2927,2928],{"class":550},"@heximon\u002Fruntime",[541,2930,693],{"class":662},[541,2932,696],{"class":662},[541,2934,2935,2937,2939,2941,2943,2945,2947,2950,2952],{"class":543,"line":699},[541,2936,659],{"class":658},[541,2938,663],{"class":662},[541,2940,2761],{"class":666},[541,2942,681],{"class":662},[541,2944,684],{"class":658},[541,2946,687],{"class":662},[541,2948,2949],{"class":550},".\u002Fdocs.controller",[541,2951,693],{"class":662},[541,2953,696],{"class":662},[541,2955,2956],{"class":543,"line":722},[541,2957,726],{"emptyLinePlaceholder":725},[541,2959,2960,2962,2964,2967,2969,2971,2973],{"class":543,"line":729},[541,2961,732],{"class":658},[541,2963,1779],{"class":735},[541,2965,2966],{"class":547}," DocsModule",[541,2968,1785],{"class":735},[541,2970,2919],{"class":774},[541,2972,777],{"class":666},[541,2974,780],{"class":662},[541,2976,2977,2980,2982,2984,2987,2989,2992],{"class":543,"line":754},[541,2978,2979],{"class":786},"  http",[541,2981,790],{"class":662},[541,2983,663],{"class":662},[541,2985,2986],{"class":786}," controllers",[541,2988,790],{"class":662},[541,2990,2991],{"class":666}," [DocsController] ",[541,2993,2994],{"class":662},"},\n",[541,2996,2997,2999,3001],{"class":543,"line":759},[541,2998,1066],{"class":662},[541,3000,1896],{"class":666},[541,3002,1899],{"class":662},[523,3004,3006],{"id":3005},"choose-a-renderer","Choose a renderer",[499,3008,3009,3010,3013,3014,3017,3018,3021,3022,3025,3026,3029],{},"Both ",[506,3011,3012],{},"Scalar"," and ",[506,3015,3016],{},"SwaggerUserInterface"," take ",[506,3019,3020],{},"{ title, documentPath }"," and return ",[506,3023,3024],{},"render()"," — an HTML\npage that fetches the document from ",[506,3027,3028],{},"documentPath",". Swap one line to switch UIs:",[531,3031,3034],{"className":648,"code":3032,"filename":3033,"language":651,"meta":537,"style":537},"import { Scalar, SwaggerUserInterface } from \"@heximon\u002Fopenapi\";\n\nconst scalar = new Scalar({ title: \"My API\", documentPath: \"\u002Fopenapi.json\" });\nconst swagger = new SwaggerUserInterface({ title: \"My API\", documentPath: \"\u002Fopenapi.json\" });\n","src\u002Fdocs\u002Frenderers.ts",[506,3035,3036,3061,3065,3112],{"__ignoreMap":537},[541,3037,3038,3040,3042,3044,3046,3049,3051,3053,3055,3057,3059],{"class":543,"line":544},[541,3039,659],{"class":658},[541,3041,663],{"class":662},[541,3043,673],{"class":666},[541,3045,670],{"class":662},[541,3047,3048],{"class":666}," SwaggerUserInterface",[541,3050,681],{"class":662},[541,3052,684],{"class":658},[541,3054,687],{"class":662},[541,3056,690],{"class":550},[541,3058,693],{"class":662},[541,3060,696],{"class":662},[541,3062,3063],{"class":543,"line":699},[541,3064,726],{"emptyLinePlaceholder":725},[541,3066,3067,3069,3072,3074,3076,3078,3080,3082,3084,3086,3088,3091,3093,3095,3098,3100,3102,3104,3106,3108,3110],{"class":543,"line":722},[541,3068,2358],{"class":735},[541,3070,3071],{"class":666}," scalar ",[541,3073,742],{"class":662},[541,3075,771],{"class":662},[541,3077,673],{"class":774},[541,3079,777],{"class":666},[541,3081,865],{"class":662},[541,3083,1585],{"class":786},[541,3085,790],{"class":662},[541,3087,687],{"class":662},[541,3089,3090],{"class":550},"My API",[541,3092,693],{"class":662},[541,3094,670],{"class":662},[541,3096,3097],{"class":786}," documentPath",[541,3099,790],{"class":662},[541,3101,687],{"class":662},[541,3103,747],{"class":550},[541,3105,693],{"class":662},[541,3107,681],{"class":662},[541,3109,1019],{"class":666},[541,3111,696],{"class":662},[541,3113,3114,3116,3119,3121,3123,3125,3127,3129,3131,3133,3135,3137,3139,3141,3143,3145,3147,3149,3151,3153,3155],{"class":543,"line":729},[541,3115,2358],{"class":735},[541,3117,3118],{"class":666}," swagger ",[541,3120,742],{"class":662},[541,3122,771],{"class":662},[541,3124,3048],{"class":774},[541,3126,777],{"class":666},[541,3128,865],{"class":662},[541,3130,1585],{"class":786},[541,3132,790],{"class":662},[541,3134,687],{"class":662},[541,3136,3090],{"class":550},[541,3138,693],{"class":662},[541,3140,670],{"class":662},[541,3142,3097],{"class":786},[541,3144,790],{"class":662},[541,3146,687],{"class":662},[541,3148,747],{"class":550},[541,3150,693],{"class":662},[541,3152,681],{"class":662},[541,3154,1019],{"class":666},[541,3156,696],{"class":662},[499,3158,3159,3160,1182,3163,3166,3167,3170,3171,3173,3174,3177,3178,3181],{},"Each renderer loads its script from a CDN (jsDelivr); override ",[506,3161,3162],{},"scriptPath",[506,3164,3165],{},"assetPath"," to self-host it\ninstead. For a fully custom page, extend the abstract ",[506,3168,3169],{},"OpenApiUserInterface"," and implement ",[506,3172,3024],{},",\nthen return its HTML with ",[506,3175,3176],{},"action.html(...)",", which sets the ",[506,3179,3180],{},"text\u002Fhtml"," content type for you.",[523,3183,3185],{"id":3184},"document-the-right-routes","Document the right routes",[499,3187,3188,3189,3191,3192,3195,3196,3200,3201,935],{},"The generator reads contracts, not controllers, so only routes declared on a ",[506,3190,508],{}," appear in the\nspec — an inline controller's routes (",[506,3193,3194],{},"Get\u003C\"\u002F:id\">",") leave nothing to iterate at runtime. To document a\nroute, declare it on a shared ",[503,3197,3198],{"href":68},[506,3199,508],{}," and bind the controller with ",[506,3202,3203],{},"implements Controller\u003CSomeApi>",[2647,3205,3206,3212],{"type":2649},[499,3207,3208,3211],{},[514,3209,3210],{},"Inline routes are silently absent — there's no warning."," The generator is a runtime value that reads\ncontract route tables; an inline route's metadata exists only at compile time, so the generator can't see\nit and emits no diagnostic. The route is simply missing from the spec.",[499,3213,3214,3215,3013,3218,3223,3224,3226],{},"The same is true of the\n",[503,3216,3217],{"href":73},"REST client",[503,3219,3220],{"href":205},[506,3221,3222],{},"@heximon\u002Fmcp"," — all three read the contract, not\ninline metadata. Author the routes on a ",[506,3225,508],{}," to make every contract-driven tool see them.",[523,3228,3230],{"id":3229},"see-also","See also",[3232,3233,3234,3247,3252,3257,3262],"ul",{},[3235,3236,3237,3239,3240,3242,3243,3246],"li",{},[503,3238,67],{"href":68}," — the ",[506,3241,508],{}," + ",[506,3244,3245],{},"Route"," builder that the document is generated\nfrom; the one source of truth shared by the server, the client, and the spec.",[3235,3248,3249,3251],{},[503,3250,57],{"href":58}," — bind a controller to a contract so the document describes the\nroutes you actually serve.",[3235,3253,3254,3256],{},[503,3255,62],{"href":63}," — author each schema once; it both validates requests and\nembeds into the spec as JSON Schema.",[3235,3258,3259,3261],{},[503,3260,72],{"href":73}," — the typed client and the OpenAPI spec are two views of the same\ncontract value.",[3235,3263,3264,3272,3273,3275],{},[503,3265,3269],{"href":3266,"rel":3267},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fgap\u002Fopenapi-mcp",[3268],"nofollow",[514,3270,3271],{},"OpenAPI + MCP"," — a\ncontract-mode products API, a manually registered bearer scheme with ",[506,3274,1038],{}," routes marked public, and a\nScalar reference page served from a plain controller.",[3277,3278,3279],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":537,"searchDepth":699,"depth":699,"links":3281},[3282,3283,3284,3287,3289,3290,3291,3292,3293,3294,3295],{"id":525,"depth":699,"text":526},{"id":607,"depth":699,"text":608},{"id":1141,"depth":699,"text":1142,"children":3285},[3286],{"id":1303,"depth":722,"text":1304},{"id":1524,"depth":699,"text":3288},"Reuse a schema with $ref",{"id":1676,"depth":699,"text":1677},{"id":1920,"depth":699,"text":1921},{"id":2220,"depth":699,"text":2221},{"id":2675,"depth":699,"text":2676},{"id":3005,"depth":699,"text":3006},{"id":3184,"depth":699,"text":3185},{"id":3229,"depth":699,"text":3230},"Generate an OpenAPI 3.1 document from your Contract route tables at runtime with OpenApiGenerator, register security schemes manually with the SecurityScheme factory, and serve Scalar or Swagger UI inline.","md",null,{},{"icon":202},{"title":199,"description":3296},"pW0k9gxwHSJru-BM7QtGv52jGCdNH-ZC8nnlmrV7XFE",[3304,3306],{"title":194,"path":195,"stem":196,"description":3305,"icon":197,"children":-1},"Compile-time, module-attributed OpenTelemetry tracing with OtelPlugin — heximon.module baked onto every HTTP \u002F CommandBus \u002F QueryBus \u002F EventBus span, OTLP export, samplerRatio, batchOptions.",{"title":204,"path":205,"stem":206,"description":3307,"icon":207,"children":-1},"Derive a Model Context Protocol server from a Contract's route table at runtime with McpServerGenerator — per-route .mcp() opt-in, marked or all selection, in-process tool execution, and stateless Streamable HTTP.",1783300761903]