[{"data":1,"prerenderedAt":3901},["ShallowReactive",2],{"navigation_docs":3,"-docs-essentials-controllers":493,"-docs-essentials-controllers-surround":3896},[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":57,"body":495,"description":3889,"extension":3890,"links":3891,"meta":3892,"navigation":3893,"path":58,"seo":3894,"stem":59,"__hash__":3895},"docs\u002Fdocs\u002F02.essentials\u002F02.controllers.md",{"type":496,"value":497,"toc":3875},"minimark",[498,512,766,780,795,798,803,843,1295,1346,1373,1409,1417,1421,1431,1583,1596,1600,1625,1775,1794,1798,1825,1840,2134,2148,2402,2409,2423,2427,2445,2672,2683,2700,2704,2728,3047,3074,3077,3087,3309,3315,3328,3332,3345,3529,3539,3543,3558,3596,3602,3606,3625,3791,3795,3871],[499,500,501,502,506,507,511],"p",{},"A controller is where your HTTP routes live: a class that injects what it needs through its constructor\nand exposes one handler method per route. There's no route-config list to keep in sync — each route is\ndeclared by the ",[503,504,505],"strong",{},"type of its handler's action parameter"," (",[508,509,510],"code",{},"Get\u003C\"\u002F:id\">","), which the compiler reads\nstraight from your source.",[513,514,520],"pre",{"className":515,"code":516,"filename":517,"language":518,"meta":519,"style":519},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import type { Controller, Get } from \"@heximon\u002Fhttp\";\nimport { type User, UsersRepository } from \".\u002Fusers.repository\";\n\nexport class UsersController implements Controller\u003C\"\u002Fusers\"> {\n  constructor(private readonly users: UsersRepository) {}\n\n  \u002F\u002F GET \u002Fusers — the route is the method's parameter type, not a separate config.\n  public async list(_action: Get\u003C\"\u002F\">): Promise\u003CUser[]> {\n    return this.users.list();\n  }\n}\n","src\u002Fusers\u002Fusers.controller.ts","ts","",[508,521,522,567,597,604,639,669,674,681,730,754,760],{"__ignoreMap":519},[523,524,527,531,534,538,542,545,548,551,554,557,561,564],"span",{"class":525,"line":526},"line",1,[523,528,530],{"class":529},"s7zQu","import",[523,532,533],{"class":529}," type",[523,535,537],{"class":536},"sMK4o"," {",[523,539,541],{"class":540},"sTEyZ"," Controller",[523,543,544],{"class":536},",",[523,546,547],{"class":540}," Get",[523,549,550],{"class":536}," }",[523,552,553],{"class":529}," from",[523,555,556],{"class":536}," \"",[523,558,560],{"class":559},"sfazB","@heximon\u002Fhttp",[523,562,563],{"class":536},"\"",[523,565,566],{"class":536},";\n",[523,568,570,572,574,576,579,581,584,586,588,590,593,595],{"class":525,"line":569},2,[523,571,530],{"class":529},[523,573,537],{"class":536},[523,575,533],{"class":529},[523,577,578],{"class":540}," User",[523,580,544],{"class":536},[523,582,583],{"class":540}," UsersRepository",[523,585,550],{"class":536},[523,587,553],{"class":529},[523,589,556],{"class":536},[523,591,592],{"class":559},".\u002Fusers.repository",[523,594,563],{"class":536},[523,596,566],{"class":536},[523,598,600],{"class":525,"line":599},3,[523,601,603],{"emptyLinePlaceholder":602},true,"\n",[523,605,607,610,614,618,621,623,626,628,631,633,636],{"class":525,"line":606},4,[523,608,609],{"class":529},"export",[523,611,613],{"class":612},"spNyl"," class",[523,615,617],{"class":616},"sBMFI"," UsersController",[523,619,620],{"class":612}," implements",[523,622,541],{"class":616},[523,624,625],{"class":536},"\u003C",[523,627,563],{"class":536},[523,629,630],{"class":559},"\u002Fusers",[523,632,563],{"class":536},[523,634,635],{"class":536},">",[523,637,638],{"class":536}," {\n",[523,640,642,645,648,651,654,658,661,663,666],{"class":525,"line":641},5,[523,643,644],{"class":612},"  constructor",[523,646,647],{"class":536},"(",[523,649,650],{"class":612},"private",[523,652,653],{"class":612}," readonly",[523,655,657],{"class":656},"sHdIc"," users",[523,659,660],{"class":536},":",[523,662,583],{"class":616},[523,664,665],{"class":536},")",[523,667,668],{"class":536}," {}\n",[523,670,672],{"class":525,"line":671},6,[523,673,603],{"emptyLinePlaceholder":602},[523,675,677],{"class":525,"line":676},7,[523,678,680],{"class":679},"sHwdD","  \u002F\u002F GET \u002Fusers — the route is the method's parameter type, not a separate config.\n",[523,682,684,687,690,694,696,699,701,703,705,707,710,712,715,718,720,723,726,728],{"class":525,"line":683},8,[523,685,686],{"class":612},"  public",[523,688,689],{"class":612}," async",[523,691,693],{"class":692},"swJcz"," list",[523,695,647],{"class":536},[523,697,698],{"class":656},"_action",[523,700,660],{"class":536},[523,702,547],{"class":616},[523,704,625],{"class":536},[523,706,563],{"class":536},[523,708,709],{"class":559},"\u002F",[523,711,563],{"class":536},[523,713,714],{"class":536},">):",[523,716,717],{"class":616}," Promise",[523,719,625],{"class":536},[523,721,722],{"class":616},"User",[523,724,725],{"class":540},"[]",[523,727,635],{"class":536},[523,729,638],{"class":536},[523,731,733,736,739,742,745,749,752],{"class":525,"line":732},9,[523,734,735],{"class":529},"    return",[523,737,738],{"class":536}," this.",[523,740,741],{"class":540},"users",[523,743,744],{"class":536},".",[523,746,748],{"class":747},"s2Zo4","list",[523,750,751],{"class":692},"()",[523,753,566],{"class":536},[523,755,757],{"class":525,"line":756},10,[523,758,759],{"class":536},"  }\n",[523,761,763],{"class":525,"line":762},11,[523,764,765],{"class":536},"}\n",[499,767,768,771,772,775,776,779],{},[508,769,770],{},"Controller"," is a marker, not behavior: its ",[503,773,774],{},"type argument"," carries the wiring — a ",[508,777,778],{},"\"\u002Fprefix\""," string in\ninline mode, a contract in contract mode — and it contributes nothing at runtime.",[499,781,782,783,786,787,790,791,794],{},"That's why ",[508,784,785],{},"implements"," is the natural clause: with a type-only import the marker vanishes from your\nruntime module graph, and your single ",[508,788,789],{},"extends"," slot stays free for a base class of your own.\n",[508,792,793],{},"extends Controller\u003C\"\u002Fusers\">"," works exactly the same (the compiler reads the type argument from either\nclause) — use it when you want your own class hierarchy on top.",[499,796,797],{},"Constructor parameters are the dependency declaration: no tokens, no decorators. The DI graph resolves each\nby class identity, so listing the controller under a module is the whole registration step.",[799,800,802],"h2",{"id":801},"declare-routes-by-parameter-type","Declare routes by parameter type",[499,804,805,806,809,810,812,813,816,817,816,820,816,823,816,826,829,830,816,833,506,836,839,840,744],{},"In ",[503,807,808],{},"inline mode"," the controller carries a ",[508,811,778],{}," string, and each handler declares its own route\nthrough its action parameter type — the verb shorthands ",[508,814,815],{},"Get"," \u002F ",[508,818,819],{},"Post",[508,821,822],{},"Put",[508,824,825],{},"Patch",[508,827,828],{},"Delete"," \u002F\n",[508,831,832],{},"Head",[508,834,835],{},"Options",[508,837,838],{},"\u003CPath, Schemas?>","), or the general ",[508,841,842],{},"Route\u003CMethod, Path, Schemas?>",[513,844,846],{"className":515,"code":845,"filename":517,"language":518,"meta":519,"style":519},"import type { Controller, Get, Post } from \"@heximon\u002Fhttp\";\nimport { CreatedUser, CreateUser } from \".\u002Fuser.schema\";\nimport { type User, UsersRepository } from \".\u002Fusers.repository\";\n\nexport class UsersController implements Controller\u003C\"\u002Fusers\"> {\n  constructor(private readonly users: UsersRepository) {}\n\n  \u002F\u002F GET \u002Fusers\n  public async list(_action: Get\u003C\"\u002F\">): Promise\u003CUser[]> {\n    return this.users.list();\n  }\n\n  \u002F\u002F GET \u002Fusers\u002F:id — pathParams.id is typed `string` from the path literal\n  public async get(action: Get\u003C\"\u002F:id\">): Promise\u003CUser> {\n    return this.users.getById(action.request.pathParams.id);\n  }\n\n  \u002F\u002F POST \u002Fusers — body validated against `CreateUser` before the handler runs\n  public async create(\n    action: Post\u003C\"\u002F\", { body: CreateUser; responses: { 200: CreatedUser } }>,\n  ): Promise\u003CUser> {\n    const body = await action.request.readValidatedBody();\n    return this.users.create(body);\n  }\n}\n",[508,847,848,879,906,932,936,960,980,984,989,1027,1043,1047,1052,1058,1098,1135,1140,1145,1151,1164,1216,1232,1262,1285,1290],{"__ignoreMap":519},[523,849,850,852,854,856,858,860,862,864,867,869,871,873,875,877],{"class":525,"line":526},[523,851,530],{"class":529},[523,853,533],{"class":529},[523,855,537],{"class":536},[523,857,541],{"class":540},[523,859,544],{"class":536},[523,861,547],{"class":540},[523,863,544],{"class":536},[523,865,866],{"class":540}," Post",[523,868,550],{"class":536},[523,870,553],{"class":529},[523,872,556],{"class":536},[523,874,560],{"class":559},[523,876,563],{"class":536},[523,878,566],{"class":536},[523,880,881,883,885,888,890,893,895,897,899,902,904],{"class":525,"line":569},[523,882,530],{"class":529},[523,884,537],{"class":536},[523,886,887],{"class":540}," CreatedUser",[523,889,544],{"class":536},[523,891,892],{"class":540}," CreateUser",[523,894,550],{"class":536},[523,896,553],{"class":529},[523,898,556],{"class":536},[523,900,901],{"class":559},".\u002Fuser.schema",[523,903,563],{"class":536},[523,905,566],{"class":536},[523,907,908,910,912,914,916,918,920,922,924,926,928,930],{"class":525,"line":599},[523,909,530],{"class":529},[523,911,537],{"class":536},[523,913,533],{"class":529},[523,915,578],{"class":540},[523,917,544],{"class":536},[523,919,583],{"class":540},[523,921,550],{"class":536},[523,923,553],{"class":529},[523,925,556],{"class":536},[523,927,592],{"class":559},[523,929,563],{"class":536},[523,931,566],{"class":536},[523,933,934],{"class":525,"line":606},[523,935,603],{"emptyLinePlaceholder":602},[523,937,938,940,942,944,946,948,950,952,954,956,958],{"class":525,"line":641},[523,939,609],{"class":529},[523,941,613],{"class":612},[523,943,617],{"class":616},[523,945,620],{"class":612},[523,947,541],{"class":616},[523,949,625],{"class":536},[523,951,563],{"class":536},[523,953,630],{"class":559},[523,955,563],{"class":536},[523,957,635],{"class":536},[523,959,638],{"class":536},[523,961,962,964,966,968,970,972,974,976,978],{"class":525,"line":671},[523,963,644],{"class":612},[523,965,647],{"class":536},[523,967,650],{"class":612},[523,969,653],{"class":612},[523,971,657],{"class":656},[523,973,660],{"class":536},[523,975,583],{"class":616},[523,977,665],{"class":536},[523,979,668],{"class":536},[523,981,982],{"class":525,"line":676},[523,983,603],{"emptyLinePlaceholder":602},[523,985,986],{"class":525,"line":683},[523,987,988],{"class":679},"  \u002F\u002F GET \u002Fusers\n",[523,990,991,993,995,997,999,1001,1003,1005,1007,1009,1011,1013,1015,1017,1019,1021,1023,1025],{"class":525,"line":732},[523,992,686],{"class":612},[523,994,689],{"class":612},[523,996,693],{"class":692},[523,998,647],{"class":536},[523,1000,698],{"class":656},[523,1002,660],{"class":536},[523,1004,547],{"class":616},[523,1006,625],{"class":536},[523,1008,563],{"class":536},[523,1010,709],{"class":559},[523,1012,563],{"class":536},[523,1014,714],{"class":536},[523,1016,717],{"class":616},[523,1018,625],{"class":536},[523,1020,722],{"class":616},[523,1022,725],{"class":540},[523,1024,635],{"class":536},[523,1026,638],{"class":536},[523,1028,1029,1031,1033,1035,1037,1039,1041],{"class":525,"line":756},[523,1030,735],{"class":529},[523,1032,738],{"class":536},[523,1034,741],{"class":540},[523,1036,744],{"class":536},[523,1038,748],{"class":747},[523,1040,751],{"class":692},[523,1042,566],{"class":536},[523,1044,1045],{"class":525,"line":762},[523,1046,759],{"class":536},[523,1048,1050],{"class":525,"line":1049},12,[523,1051,603],{"emptyLinePlaceholder":602},[523,1053,1055],{"class":525,"line":1054},13,[523,1056,1057],{"class":679},"  \u002F\u002F GET \u002Fusers\u002F:id — pathParams.id is typed `string` from the path literal\n",[523,1059,1061,1063,1065,1068,1070,1073,1075,1077,1079,1081,1084,1086,1088,1090,1092,1094,1096],{"class":525,"line":1060},14,[523,1062,686],{"class":612},[523,1064,689],{"class":612},[523,1066,1067],{"class":692}," get",[523,1069,647],{"class":536},[523,1071,1072],{"class":656},"action",[523,1074,660],{"class":536},[523,1076,547],{"class":616},[523,1078,625],{"class":536},[523,1080,563],{"class":536},[523,1082,1083],{"class":559},"\u002F:id",[523,1085,563],{"class":536},[523,1087,714],{"class":536},[523,1089,717],{"class":616},[523,1091,625],{"class":536},[523,1093,722],{"class":616},[523,1095,635],{"class":536},[523,1097,638],{"class":536},[523,1099,1101,1103,1105,1107,1109,1112,1114,1116,1118,1121,1123,1126,1128,1131,1133],{"class":525,"line":1100},15,[523,1102,735],{"class":529},[523,1104,738],{"class":536},[523,1106,741],{"class":540},[523,1108,744],{"class":536},[523,1110,1111],{"class":747},"getById",[523,1113,647],{"class":692},[523,1115,1072],{"class":540},[523,1117,744],{"class":536},[523,1119,1120],{"class":540},"request",[523,1122,744],{"class":536},[523,1124,1125],{"class":540},"pathParams",[523,1127,744],{"class":536},[523,1129,1130],{"class":540},"id",[523,1132,665],{"class":692},[523,1134,566],{"class":536},[523,1136,1138],{"class":525,"line":1137},16,[523,1139,759],{"class":536},[523,1141,1143],{"class":525,"line":1142},17,[523,1144,603],{"emptyLinePlaceholder":602},[523,1146,1148],{"class":525,"line":1147},18,[523,1149,1150],{"class":679},"  \u002F\u002F POST \u002Fusers — body validated against `CreateUser` before the handler runs\n",[523,1152,1154,1156,1158,1161],{"class":525,"line":1153},19,[523,1155,686],{"class":612},[523,1157,689],{"class":612},[523,1159,1160],{"class":692}," create",[523,1162,1163],{"class":536},"(\n",[523,1165,1167,1170,1172,1174,1176,1178,1180,1182,1184,1186,1189,1191,1193,1196,1199,1201,1203,1207,1209,1211,1213],{"class":525,"line":1166},20,[523,1168,1169],{"class":656},"    action",[523,1171,660],{"class":536},[523,1173,866],{"class":616},[523,1175,625],{"class":536},[523,1177,563],{"class":536},[523,1179,709],{"class":559},[523,1181,563],{"class":536},[523,1183,544],{"class":536},[523,1185,537],{"class":536},[523,1187,1188],{"class":692}," body",[523,1190,660],{"class":536},[523,1192,892],{"class":616},[523,1194,1195],{"class":536},";",[523,1197,1198],{"class":692}," responses",[523,1200,660],{"class":536},[523,1202,537],{"class":536},[523,1204,1206],{"class":1205},"sbssI"," 200",[523,1208,660],{"class":536},[523,1210,887],{"class":616},[523,1212,550],{"class":536},[523,1214,1215],{"class":536}," }>,\n",[523,1217,1219,1222,1224,1226,1228,1230],{"class":525,"line":1218},21,[523,1220,1221],{"class":536},"  ):",[523,1223,717],{"class":616},[523,1225,625],{"class":536},[523,1227,722],{"class":616},[523,1229,635],{"class":536},[523,1231,638],{"class":536},[523,1233,1235,1238,1240,1243,1246,1249,1251,1253,1255,1258,1260],{"class":525,"line":1234},22,[523,1236,1237],{"class":612},"    const",[523,1239,1188],{"class":540},[523,1241,1242],{"class":536}," =",[523,1244,1245],{"class":529}," await",[523,1247,1248],{"class":540}," action",[523,1250,744],{"class":536},[523,1252,1120],{"class":540},[523,1254,744],{"class":536},[523,1256,1257],{"class":747},"readValidatedBody",[523,1259,751],{"class":692},[523,1261,566],{"class":536},[523,1263,1265,1267,1269,1271,1273,1276,1278,1281,1283],{"class":525,"line":1264},23,[523,1266,735],{"class":529},[523,1268,738],{"class":536},[523,1270,741],{"class":540},[523,1272,744],{"class":536},[523,1274,1275],{"class":747},"create",[523,1277,647],{"class":692},[523,1279,1280],{"class":540},"body",[523,1282,665],{"class":692},[523,1284,566],{"class":536},[523,1286,1288],{"class":525,"line":1287},24,[523,1289,759],{"class":536},[523,1291,1293],{"class":525,"line":1292},25,[523,1294,765],{"class":536},[499,1296,1297,1298,1301,1302,1305,1306,1309,1310,1313,1314,1317,1318,1321,1322,1325,1326,1329,1330,1333,1334,1337,1338,1341,1342,1345],{},"The ",[508,1299,1300],{},"Schemas"," slot of a verb type carries ",[508,1303,1304],{},"{ body, query, pathParams, headers }"," (each a DTO class\nreference, or ",[508,1307,1308],{},"typeof aSchema"," for a plain runtime constant), ",[508,1311,1312],{},"{ responses: { 200: X, 404: Y } }",",\n",[508,1315,1316],{},"{ scopes: [\"read\"] }",", ",[508,1319,1320],{},"{ cors: true }"," (or a ",[508,1323,1324],{},"CorsOptions"," object), and ",[508,1327,1328],{},"{ cache: { maxAge: 60 } }"," (a\n",[508,1331,1332],{},"CacheOptions"," object). A path literal like ",[508,1335,1336],{},"\"\u002F:id\""," types ",[508,1339,1340],{},"action.request.pathParams"," as ",[508,1343,1344],{},"{ id: string }",",\nso the handler reads the id with no schema and no cast.",[499,1347,1348,1350,1351,1354,1355,1358,1359,1362,1363,1366,1367,1369,1370,1372],{},[508,1349,1320],{}," opts a route into CORS handling — the compiler emits an extra ",[508,1352,1353],{},"OPTIONS"," row answered by\nh3's ",[508,1356,1357],{},"handleCors"," before any middleware runs, plus an implicit ",[508,1360,1361],{},"HEAD"," row for every ",[508,1364,1365],{},"GET"," (plain ",[508,1368,1365],{},"\nroutes get that ",[508,1371,1361],{}," row regardless).",[499,1374,1375,1378,1379,506,1381,709,1384,709,1387,709,1390,709,1393,1396,1397,1400,1401,1404,1405,1408],{},[508,1376,1377],{},"{ cache: { maxAge, … } }"," declares the same ",[508,1380,1332],{},[508,1382,1383],{},"maxAge",[508,1385,1386],{},"staleMaxAge",[508,1388,1389],{},"swr",[508,1391,1392],{},"vary",[508,1394,1395],{},"tags",") a\ncontract route's ",[508,1398,1399],{},"Route(...).cache({...})"," carries; the h3 layer emits the matching headers and\n",[508,1402,1403],{},"@heximon\u002Fcache","'s ",[508,1406,1407],{},"CacheMiddleware"," caches the response.",[499,1410,1411,1412,1416],{},"The handler method name ",[1413,1414,1415],"em",{},"is"," the route. Rename the method and you've renamed the route — there's no\nseparate route list to drift from. That's the point of declaring the route on the parameter: the type and\nthe runtime route can't disagree.",[799,1418,1420],{"id":1419},"receive-the-httpaction","Receive the HttpAction",[499,1422,1423,1424,1427,1428,744],{},"Every handler receives a single ",[508,1425,1426],{},"HttpAction\u003CDescriptor>"," object, generic over the route's descriptor so\neverything on it is typed per route. The request side is input-only, the response side is output-only, and\nthe terminal builders produce a response typed against the route's declared ",[508,1429,1430],{},"responses",[1432,1433,1434,1448],"table",{},[1435,1436,1437],"thead",{},[1438,1439,1440,1445],"tr",{},[1441,1442,1444],"th",{"align":1443},"left","Member",[1441,1446,1447],{"align":1443},"What it is",[1449,1450,1451,1477,1508,1525,1537,1550,1563,1573],"tbody",{},[1438,1452,1453,1459],{},[1454,1455,1456],"td",{"align":1443},[508,1457,1458],{},"action.request",[1454,1460,1461,1462,1465,1466,1469,1470,1472,1473,1476],{"align":1443},"An ",[508,1463,1464],{},"HttpRequest"," — ",[503,1467,1468],{},"input only",": typed ",[508,1471,1125],{},", validated accessors, and the full native ",[508,1474,1475],{},"Request"," surface",[1438,1478,1479,1484],{},[1454,1480,1481],{"align":1443},[508,1482,1483],{},"action.response",[1454,1485,1461,1486,1465,1489,1492,1493,816,1496,816,1499,1317,1502,816,1505],{"align":1443},[508,1487,1488],{},"OutgoingResponse",[503,1490,1491],{},"output config only",", no body: ",[508,1494,1495],{},"status",[508,1497,1498],{},"statusText",[508,1500,1501],{},"headers",[508,1503,1504],{},"setCookie",[508,1506,1507],{},"deleteCookie",[1438,1509,1510,1515],{},[1454,1511,1512],{"align":1443},[508,1513,1514],{},"action.json(data)",[1454,1516,1517,1518,1521,1522],{"align":1443},"Terminal builder → ",[508,1519,1520],{},"200",", returns a ",[508,1523,1524],{},"TypedResponse",[1438,1526,1527,1532],{},[1454,1528,1529],{"align":1443},[508,1530,1531],{},"action.respond(status, data)",[1454,1533,1534,1535],{"align":1443},"Terminal builder → a chosen status from the route's ",[508,1536,1430],{},[1438,1538,1539,1544],{},[1454,1540,1541],{"align":1443},[508,1542,1543],{},"action.redirect(location, status?)",[1454,1545,1546,1547,665],{"align":1443},"Redirect (default ",[508,1548,1549],{},"302",[1438,1551,1552,1557],{},[1454,1553,1554],{"align":1443},[508,1555,1556],{},"action.noContent(status?)",[1454,1558,1559,1560,665],{"align":1443},"Empty body (default ",[508,1561,1562],{},"204",[1438,1564,1565,1570],{},[1454,1566,1567],{"align":1443},[508,1568,1569],{},"action.html(markup, init?)",[1454,1571,1572],{"align":1443},"HTML response",[1438,1574,1575,1580],{},[1454,1576,1577],{"align":1443},[508,1578,1579],{},"action.scopes",[1454,1581,1582],{"align":1443},"The route's resolved authorization scopes (the module ∪ controller ∪ route union baked by the compiler)",[499,1584,1585,1586,1588,1589,1592,1593,1595],{},"The split is enforced: the body never goes on ",[508,1587,1483],{},", which holds only status, headers, and\ncookies — return a body through a terminal builder instead. A raw ",[508,1590,1591],{},"HttpResponse"," is intentionally not\nassignable into a typed-response slot, so a \"not assignable to ",[508,1594,1524],{},"\" error means you built the\nresponse by hand instead of through the action.",[799,1597,1599],{"id":1598},"read-the-request","Read the request",[499,1601,1602,1603,1605,1606,1613,1614,1616,1617,1620,1621,1624],{},"Beyond the native ",[508,1604,1475],{}," members (delegated to ",[1607,1608,1612],"a",{"href":1609,"rel":1610},"https:\u002F\u002Fh3.dev\u002F",[1611],"nofollow","h3","), ",[508,1615,1464],{}," adds typed path\nparameters and validated accessors. Each accessor throws ",[508,1618,1619],{},"RequestValidationError"," on failure — no\ntry\u002Fcatch needed; it renders as a ",[508,1622,1623],{},"400 application\u002Fproblem+json"," through the error envelope.",[513,1626,1629],{"className":515,"code":1627,"filename":1628,"language":518,"meta":519,"style":519},"\u002F\u002F Typed path params from the route literal — no schema needed for the string shape.\nconst id = action.request.pathParams.id;\n\n\u002F\u002F Validated accessors — the schema is optional when the route declares one, required otherwise.\nconst body = await action.request.readValidatedBody();\nconst query = await action.request.getValidatedQuery();\nconst params = await action.request.getValidatedPathParams();\nconst headers = await action.request.getValidatedHeaders();\n","reading-requests.ts",[508,1630,1631,1636,1663,1667,1672,1697,1723,1749],{"__ignoreMap":519},[523,1632,1633],{"class":525,"line":526},[523,1634,1635],{"class":679},"\u002F\u002F Typed path params from the route literal — no schema needed for the string shape.\n",[523,1637,1638,1641,1644,1647,1649,1651,1653,1655,1657,1659,1661],{"class":525,"line":569},[523,1639,1640],{"class":612},"const",[523,1642,1643],{"class":540}," id ",[523,1645,1646],{"class":536},"=",[523,1648,1248],{"class":540},[523,1650,744],{"class":536},[523,1652,1120],{"class":540},[523,1654,744],{"class":536},[523,1656,1125],{"class":540},[523,1658,744],{"class":536},[523,1660,1130],{"class":540},[523,1662,566],{"class":536},[523,1664,1665],{"class":525,"line":599},[523,1666,603],{"emptyLinePlaceholder":602},[523,1668,1669],{"class":525,"line":606},[523,1670,1671],{"class":679},"\u002F\u002F Validated accessors — the schema is optional when the route declares one, required otherwise.\n",[523,1673,1674,1676,1679,1681,1683,1685,1687,1689,1691,1693,1695],{"class":525,"line":641},[523,1675,1640],{"class":612},[523,1677,1678],{"class":540}," body ",[523,1680,1646],{"class":536},[523,1682,1245],{"class":529},[523,1684,1248],{"class":540},[523,1686,744],{"class":536},[523,1688,1120],{"class":540},[523,1690,744],{"class":536},[523,1692,1257],{"class":747},[523,1694,751],{"class":540},[523,1696,566],{"class":536},[523,1698,1699,1701,1704,1706,1708,1710,1712,1714,1716,1719,1721],{"class":525,"line":671},[523,1700,1640],{"class":612},[523,1702,1703],{"class":540}," query ",[523,1705,1646],{"class":536},[523,1707,1245],{"class":529},[523,1709,1248],{"class":540},[523,1711,744],{"class":536},[523,1713,1120],{"class":540},[523,1715,744],{"class":536},[523,1717,1718],{"class":747},"getValidatedQuery",[523,1720,751],{"class":540},[523,1722,566],{"class":536},[523,1724,1725,1727,1730,1732,1734,1736,1738,1740,1742,1745,1747],{"class":525,"line":676},[523,1726,1640],{"class":612},[523,1728,1729],{"class":540}," params ",[523,1731,1646],{"class":536},[523,1733,1245],{"class":529},[523,1735,1248],{"class":540},[523,1737,744],{"class":536},[523,1739,1120],{"class":540},[523,1741,744],{"class":536},[523,1743,1744],{"class":747},"getValidatedPathParams",[523,1746,751],{"class":540},[523,1748,566],{"class":536},[523,1750,1751,1753,1756,1758,1760,1762,1764,1766,1768,1771,1773],{"class":525,"line":683},[523,1752,1640],{"class":612},[523,1754,1755],{"class":540}," headers ",[523,1757,1646],{"class":536},[523,1759,1245],{"class":529},[523,1761,1248],{"class":540},[523,1763,744],{"class":536},[523,1765,1120],{"class":540},[523,1767,744],{"class":536},[523,1769,1770],{"class":747},"getValidatedHeaders",[523,1772,751],{"class":540},[523,1774,566],{"class":536},[499,1776,1777,1778,1317,1781,1317,1784,1317,1787,1317,1790,1793],{},"The request-scoped h3 helpers (",[508,1779,1780],{},"readBody",[508,1782,1783],{},"getQuery",[508,1785,1786],{},"getRouterParams",[508,1788,1789],{},"parseCookies",[508,1791,1792],{},"getRequestIP",",\nand more) are also available as instance methods, so the native and validated surfaces sit on one object.",[799,1795,1797],{"id":1796},"add-middleware","Add middleware",[499,1799,1800,1801,1804,1805,1808,1809,1812,1813,1816,1817,1820,1821,1824],{},"A ",[508,1802,1803],{},"Middleware"," is a singleton onion layer — ",[503,1806,1807],{},"one instance per worker",", so per-request state lives in the\nambient ",[508,1810,1811],{},"Context",", never an instance field (a singleton would leak one request's data into the next). Its\n",[508,1814,1815],{},"handle(action, next)"," either calls ",[508,1818,1819],{},"next()"," to continue the chain (and finally the handler) or returns a\n",[508,1822,1823],{},"Response"," to short-circuit.",[499,1826,1827,1828,1831,1832,1835,1836,1839],{},"Declaring ",[508,1829,1830],{},"implements Middleware"," makes the compiler recognize the class when a controller or module names\nit, and makes TypeScript require the ",[508,1833,1834],{},"handle"," method — an empty middleware can't slip through.\n(",[508,1837,1838],{},"extends Middleware"," works the same; use it for your own base class.)",[513,1841,1844],{"className":515,"code":1842,"filename":1843,"language":518,"meta":519,"style":519},"import type { HttpAction, Middleware, Next } from \"@heximon\u002Fhttp\";\n\nexport class RequestLoggerMiddleware implements Middleware {\n  public async handle(action: HttpAction, next: Next): Promise\u003CResponse> {\n    const startedAt = Date.now();\n    const { method } = action.request;\n    const { pathname } = new URL(action.request.url);\n\n    const response = await next(); \u002F\u002F continue the chain; return a Response to short-circuit\n\n    console.log(`${method} ${pathname} -> ${response.status} (${Date.now() - startedAt}ms)`);\n    return response;\n  }\n}\n","src\u002Fcommon\u002Frequest-logger.middleware.ts",[508,1845,1846,1879,1883,1898,1937,1958,1979,2015,2019,2039,2043,2118,2126,2130],{"__ignoreMap":519},[523,1847,1848,1850,1852,1854,1857,1859,1862,1864,1867,1869,1871,1873,1875,1877],{"class":525,"line":526},[523,1849,530],{"class":529},[523,1851,533],{"class":529},[523,1853,537],{"class":536},[523,1855,1856],{"class":540}," HttpAction",[523,1858,544],{"class":536},[523,1860,1861],{"class":540}," Middleware",[523,1863,544],{"class":536},[523,1865,1866],{"class":540}," Next",[523,1868,550],{"class":536},[523,1870,553],{"class":529},[523,1872,556],{"class":536},[523,1874,560],{"class":559},[523,1876,563],{"class":536},[523,1878,566],{"class":536},[523,1880,1881],{"class":525,"line":569},[523,1882,603],{"emptyLinePlaceholder":602},[523,1884,1885,1887,1889,1892,1894,1896],{"class":525,"line":599},[523,1886,609],{"class":529},[523,1888,613],{"class":612},[523,1890,1891],{"class":616}," RequestLoggerMiddleware",[523,1893,620],{"class":612},[523,1895,1861],{"class":616},[523,1897,638],{"class":536},[523,1899,1900,1902,1904,1907,1909,1911,1913,1915,1917,1920,1922,1924,1927,1929,1931,1933,1935],{"class":525,"line":606},[523,1901,686],{"class":612},[523,1903,689],{"class":612},[523,1905,1906],{"class":692}," handle",[523,1908,647],{"class":536},[523,1910,1072],{"class":656},[523,1912,660],{"class":536},[523,1914,1856],{"class":616},[523,1916,544],{"class":536},[523,1918,1919],{"class":656}," next",[523,1921,660],{"class":536},[523,1923,1866],{"class":616},[523,1925,1926],{"class":536},"):",[523,1928,717],{"class":616},[523,1930,625],{"class":536},[523,1932,1823],{"class":616},[523,1934,635],{"class":536},[523,1936,638],{"class":536},[523,1938,1939,1941,1944,1946,1949,1951,1954,1956],{"class":525,"line":641},[523,1940,1237],{"class":612},[523,1942,1943],{"class":540}," startedAt",[523,1945,1242],{"class":536},[523,1947,1948],{"class":540}," Date",[523,1950,744],{"class":536},[523,1952,1953],{"class":747},"now",[523,1955,751],{"class":692},[523,1957,566],{"class":536},[523,1959,1960,1962,1964,1967,1969,1971,1973,1975,1977],{"class":525,"line":671},[523,1961,1237],{"class":612},[523,1963,537],{"class":536},[523,1965,1966],{"class":540}," method",[523,1968,550],{"class":536},[523,1970,1242],{"class":536},[523,1972,1248],{"class":540},[523,1974,744],{"class":536},[523,1976,1120],{"class":540},[523,1978,566],{"class":536},[523,1980,1981,1983,1985,1988,1990,1992,1995,1998,2000,2002,2004,2006,2008,2011,2013],{"class":525,"line":676},[523,1982,1237],{"class":612},[523,1984,537],{"class":536},[523,1986,1987],{"class":540}," pathname",[523,1989,550],{"class":536},[523,1991,1242],{"class":536},[523,1993,1994],{"class":536}," new",[523,1996,1997],{"class":747}," URL",[523,1999,647],{"class":692},[523,2001,1072],{"class":540},[523,2003,744],{"class":536},[523,2005,1120],{"class":540},[523,2007,744],{"class":536},[523,2009,2010],{"class":540},"url",[523,2012,665],{"class":692},[523,2014,566],{"class":536},[523,2016,2017],{"class":525,"line":683},[523,2018,603],{"emptyLinePlaceholder":602},[523,2020,2021,2023,2026,2028,2030,2032,2034,2036],{"class":525,"line":732},[523,2022,1237],{"class":612},[523,2024,2025],{"class":540}," response",[523,2027,1242],{"class":536},[523,2029,1245],{"class":529},[523,2031,1919],{"class":747},[523,2033,751],{"class":692},[523,2035,1195],{"class":536},[523,2037,2038],{"class":679}," \u002F\u002F continue the chain; return a Response to short-circuit\n",[523,2040,2041],{"class":525,"line":756},[523,2042,603],{"emptyLinePlaceholder":602},[523,2044,2045,2048,2050,2053,2055,2058,2061,2064,2067,2070,2072,2075,2078,2081,2083,2085,2087,2089,2091,2094,2096,2098,2101,2104,2106,2108,2111,2114,2116],{"class":525,"line":762},[523,2046,2047],{"class":540},"    console",[523,2049,744],{"class":536},[523,2051,2052],{"class":747},"log",[523,2054,647],{"class":692},[523,2056,2057],{"class":536},"`${",[523,2059,2060],{"class":540},"method",[523,2062,2063],{"class":536},"}",[523,2065,2066],{"class":536}," ${",[523,2068,2069],{"class":540},"pathname",[523,2071,2063],{"class":536},[523,2073,2074],{"class":559}," -> ",[523,2076,2077],{"class":536},"${",[523,2079,2080],{"class":540},"response",[523,2082,744],{"class":536},[523,2084,1495],{"class":540},[523,2086,2063],{"class":536},[523,2088,506],{"class":559},[523,2090,2077],{"class":536},[523,2092,2093],{"class":540},"Date",[523,2095,744],{"class":536},[523,2097,1953],{"class":747},[523,2099,2100],{"class":540},"() ",[523,2102,2103],{"class":536},"-",[523,2105,1943],{"class":540},[523,2107,2063],{"class":536},[523,2109,2110],{"class":559},"ms)",[523,2112,2113],{"class":536},"`",[523,2115,665],{"class":692},[523,2117,566],{"class":536},[523,2119,2120,2122,2124],{"class":525,"line":1049},[523,2121,735],{"class":529},[523,2123,2025],{"class":540},[523,2125,566],{"class":536},[523,2127,2128],{"class":525,"line":1054},[523,2129,759],{"class":536},[523,2131,2132],{"class":525,"line":1060},[523,2133,765],{"class":536},[499,2135,2136,2137,2140,2141,2144,2145,660],{},"To apply a middleware to one controller, declare the controller with a ",[503,2138,2139],{},"config type argument"," — a\n",[508,2142,2143],{},"{ prefix, middlewares, errorFilters, scopes }"," object literal — and list it under ",[508,2146,2147],{},"middlewares",[513,2149,2151],{"className":515,"code":2150,"filename":517,"language":518,"meta":519,"style":519},"import type { Controller, Get } from \"@heximon\u002Fhttp\";\nimport { AuthMiddleware } from \"..\u002Fcommon\u002Fauth.middleware\";\nimport { RequestLoggerMiddleware } from \"..\u002Fcommon\u002Frequest-logger.middleware\";\nimport { type User, UsersRepository } from \".\u002Fusers.repository\";\n\nexport class UsersController implements Controller\u003C{\n  prefix: \"\u002Fusers\";\n  middlewares: [AuthMiddleware, RequestLoggerMiddleware];\n}> {\n  constructor(private readonly users: UsersRepository) {}\n\n  \u002F\u002F Routes are still declared by the handler's parameter type — only the prefix\u002Fmiddlewares moved up.\n  public async list(_action: Get\u003C\"\u002F\">): Promise\u003CUser[]> {\n    return this.users.list();\n  }\n}\n",[508,2152,2153,2179,2201,2222,2248,2252,2267,2282,2304,2311,2331,2335,2340,2378,2394,2398],{"__ignoreMap":519},[523,2154,2155,2157,2159,2161,2163,2165,2167,2169,2171,2173,2175,2177],{"class":525,"line":526},[523,2156,530],{"class":529},[523,2158,533],{"class":529},[523,2160,537],{"class":536},[523,2162,541],{"class":540},[523,2164,544],{"class":536},[523,2166,547],{"class":540},[523,2168,550],{"class":536},[523,2170,553],{"class":529},[523,2172,556],{"class":536},[523,2174,560],{"class":559},[523,2176,563],{"class":536},[523,2178,566],{"class":536},[523,2180,2181,2183,2185,2188,2190,2192,2194,2197,2199],{"class":525,"line":569},[523,2182,530],{"class":529},[523,2184,537],{"class":536},[523,2186,2187],{"class":540}," AuthMiddleware",[523,2189,550],{"class":536},[523,2191,553],{"class":529},[523,2193,556],{"class":536},[523,2195,2196],{"class":559},"..\u002Fcommon\u002Fauth.middleware",[523,2198,563],{"class":536},[523,2200,566],{"class":536},[523,2202,2203,2205,2207,2209,2211,2213,2215,2218,2220],{"class":525,"line":599},[523,2204,530],{"class":529},[523,2206,537],{"class":536},[523,2208,1891],{"class":540},[523,2210,550],{"class":536},[523,2212,553],{"class":529},[523,2214,556],{"class":536},[523,2216,2217],{"class":559},"..\u002Fcommon\u002Frequest-logger.middleware",[523,2219,563],{"class":536},[523,2221,566],{"class":536},[523,2223,2224,2226,2228,2230,2232,2234,2236,2238,2240,2242,2244,2246],{"class":525,"line":606},[523,2225,530],{"class":529},[523,2227,537],{"class":536},[523,2229,533],{"class":529},[523,2231,578],{"class":540},[523,2233,544],{"class":536},[523,2235,583],{"class":540},[523,2237,550],{"class":536},[523,2239,553],{"class":529},[523,2241,556],{"class":536},[523,2243,592],{"class":559},[523,2245,563],{"class":536},[523,2247,566],{"class":536},[523,2249,2250],{"class":525,"line":641},[523,2251,603],{"emptyLinePlaceholder":602},[523,2253,2254,2256,2258,2260,2262,2264],{"class":525,"line":671},[523,2255,609],{"class":529},[523,2257,613],{"class":612},[523,2259,617],{"class":616},[523,2261,620],{"class":612},[523,2263,541],{"class":747},[523,2265,2266],{"class":536},"\u003C{\n",[523,2268,2269,2272,2274,2276,2278,2280],{"class":525,"line":676},[523,2270,2271],{"class":692},"  prefix",[523,2273,660],{"class":536},[523,2275,556],{"class":536},[523,2277,630],{"class":559},[523,2279,563],{"class":536},[523,2281,566],{"class":536},[523,2283,2284,2287,2289,2292,2295,2297,2299,2302],{"class":525,"line":683},[523,2285,2286],{"class":692},"  middlewares",[523,2288,660],{"class":536},[523,2290,2291],{"class":540}," [",[523,2293,2294],{"class":616},"AuthMiddleware",[523,2296,544],{"class":536},[523,2298,1891],{"class":616},[523,2300,2301],{"class":540},"]",[523,2303,566],{"class":536},[523,2305,2306,2309],{"class":525,"line":732},[523,2307,2308],{"class":536},"}>",[523,2310,638],{"class":536},[523,2312,2313,2315,2317,2319,2321,2323,2325,2327,2329],{"class":525,"line":756},[523,2314,644],{"class":612},[523,2316,647],{"class":536},[523,2318,650],{"class":612},[523,2320,653],{"class":612},[523,2322,657],{"class":656},[523,2324,660],{"class":536},[523,2326,583],{"class":616},[523,2328,665],{"class":536},[523,2330,668],{"class":536},[523,2332,2333],{"class":525,"line":762},[523,2334,603],{"emptyLinePlaceholder":602},[523,2336,2337],{"class":525,"line":1049},[523,2338,2339],{"class":679},"  \u002F\u002F Routes are still declared by the handler's parameter type — only the prefix\u002Fmiddlewares moved up.\n",[523,2341,2342,2344,2346,2348,2350,2352,2354,2356,2358,2360,2362,2364,2366,2368,2370,2372,2374,2376],{"class":525,"line":1054},[523,2343,686],{"class":612},[523,2345,689],{"class":612},[523,2347,693],{"class":692},[523,2349,647],{"class":536},[523,2351,698],{"class":656},[523,2353,660],{"class":536},[523,2355,547],{"class":616},[523,2357,625],{"class":536},[523,2359,563],{"class":536},[523,2361,709],{"class":559},[523,2363,563],{"class":536},[523,2365,714],{"class":536},[523,2367,717],{"class":616},[523,2369,625],{"class":536},[523,2371,722],{"class":616},[523,2373,725],{"class":540},[523,2375,635],{"class":536},[523,2377,638],{"class":536},[523,2379,2380,2382,2384,2386,2388,2390,2392],{"class":525,"line":1060},[523,2381,735],{"class":529},[523,2383,738],{"class":536},[523,2385,741],{"class":540},[523,2387,744],{"class":536},[523,2389,748],{"class":747},[523,2391,751],{"class":692},[523,2393,566],{"class":536},[523,2395,2396],{"class":525,"line":1100},[523,2397,759],{"class":536},[523,2399,2400],{"class":525,"line":1137},[523,2401,765],{"class":536},[499,2403,2404,2405,2408],{},"To apply a middleware app-wide instead, declare it module-wide under ",[508,2406,2407],{},"http: { middlewares: [...] }",", where it\npropagates down the import graph as the broadest layer of every inherited route.",[499,2410,2411,2412,2415,2416,2418,2419,2422],{},"Middleware runs ",[503,2413,2414],{},"broad → narrow",": inherited module layers first, then the controller's own innermost.\nBecause each middleware sees ",[508,2417,1579],{},", a module-wide guard can self-skip a route tagged ",[508,2420,2421],{},"public"," —\nthe opt-out is data on the route, not a separate registration.",[1612,2424,2426],{"id":2425},"apply-a-middleware-to-one-route","Apply a middleware to one route",[499,2428,2429,2430,2433,2434,2437,2438,2441,2442,2444],{},"A middleware can guard a ",[503,2431,2432],{},"single route"," — the innermost onion layer, composed\n",[503,2435,2436],{},"module → controller → route",". It is declared in the ",[503,2439,2440],{},"type"," layer the compiler reads, so it stays\nserver-side (middleware are classes \u002F DI tokens and never reach the FE-safe contract). Inline routes carry a\n",[508,2443,2147],{}," tuple on the action type:",[513,2446,2449],{"className":515,"code":2447,"filename":2448,"language":518,"meta":519,"style":519},"import type { Controller, Get } from \"@heximon\u002Fhttp\";\nimport { AdminGuard } from \"..\u002Fcommon\u002Fadmin-guard.middleware\";\nimport { AuthMiddleware } from \"..\u002Fcommon\u002Fauth.middleware\";\n\nexport class ItemsController implements Controller\u003C{ prefix: \"\u002Fitems\"; middlewares: [AuthMiddleware] }> {\n  \u002F\u002F AuthMiddleware (controller) runs, THEN AdminGuard (route) — outer to inner.\n  public async stats(_action: Get\u003C\"\u002Fstats\", { middlewares: [AdminGuard] }>) { \u002F* … *\u002F }\n\n  \u002F\u002F A sibling route with no `middlewares` runs only the controller + module layers.\n  public async list(_action: Get\u003C\"\u002F\">) { \u002F* … *\u002F }\n}\n","src\u002Fitems\u002Fitems.controller.ts",[508,2450,2451,2477,2499,2519,2523,2569,2574,2626,2630,2635,2668],{"__ignoreMap":519},[523,2452,2453,2455,2457,2459,2461,2463,2465,2467,2469,2471,2473,2475],{"class":525,"line":526},[523,2454,530],{"class":529},[523,2456,533],{"class":529},[523,2458,537],{"class":536},[523,2460,541],{"class":540},[523,2462,544],{"class":536},[523,2464,547],{"class":540},[523,2466,550],{"class":536},[523,2468,553],{"class":529},[523,2470,556],{"class":536},[523,2472,560],{"class":559},[523,2474,563],{"class":536},[523,2476,566],{"class":536},[523,2478,2479,2481,2483,2486,2488,2490,2492,2495,2497],{"class":525,"line":569},[523,2480,530],{"class":529},[523,2482,537],{"class":536},[523,2484,2485],{"class":540}," AdminGuard",[523,2487,550],{"class":536},[523,2489,553],{"class":529},[523,2491,556],{"class":536},[523,2493,2494],{"class":559},"..\u002Fcommon\u002Fadmin-guard.middleware",[523,2496,563],{"class":536},[523,2498,566],{"class":536},[523,2500,2501,2503,2505,2507,2509,2511,2513,2515,2517],{"class":525,"line":599},[523,2502,530],{"class":529},[523,2504,537],{"class":536},[523,2506,2187],{"class":540},[523,2508,550],{"class":536},[523,2510,553],{"class":529},[523,2512,556],{"class":536},[523,2514,2196],{"class":559},[523,2516,563],{"class":536},[523,2518,566],{"class":536},[523,2520,2521],{"class":525,"line":606},[523,2522,603],{"emptyLinePlaceholder":602},[523,2524,2525,2527,2529,2532,2534,2536,2539,2542,2544,2546,2549,2551,2553,2556,2558,2560,2562,2565,2567],{"class":525,"line":641},[523,2526,609],{"class":529},[523,2528,613],{"class":612},[523,2530,2531],{"class":616}," ItemsController",[523,2533,620],{"class":612},[523,2535,541],{"class":616},[523,2537,2538],{"class":536},"\u003C{",[523,2540,2541],{"class":692}," prefix",[523,2543,660],{"class":536},[523,2545,556],{"class":536},[523,2547,2548],{"class":559},"\u002Fitems",[523,2550,563],{"class":536},[523,2552,1195],{"class":536},[523,2554,2555],{"class":692}," middlewares",[523,2557,660],{"class":536},[523,2559,2291],{"class":540},[523,2561,2294],{"class":616},[523,2563,2564],{"class":540},"] ",[523,2566,2308],{"class":536},[523,2568,638],{"class":536},[523,2570,2571],{"class":525,"line":671},[523,2572,2573],{"class":679},"  \u002F\u002F AuthMiddleware (controller) runs, THEN AdminGuard (route) — outer to inner.\n",[523,2575,2576,2578,2580,2583,2585,2587,2589,2591,2593,2595,2598,2600,2602,2604,2606,2608,2610,2613,2615,2618,2620,2623],{"class":525,"line":676},[523,2577,686],{"class":612},[523,2579,689],{"class":612},[523,2581,2582],{"class":692}," stats",[523,2584,647],{"class":536},[523,2586,698],{"class":656},[523,2588,660],{"class":536},[523,2590,547],{"class":616},[523,2592,625],{"class":536},[523,2594,563],{"class":536},[523,2596,2597],{"class":559},"\u002Fstats",[523,2599,563],{"class":536},[523,2601,544],{"class":536},[523,2603,537],{"class":536},[523,2605,2555],{"class":692},[523,2607,660],{"class":536},[523,2609,2291],{"class":540},[523,2611,2612],{"class":616},"AdminGuard",[523,2614,2564],{"class":540},[523,2616,2617],{"class":536},"}>)",[523,2619,537],{"class":536},[523,2621,2622],{"class":679}," \u002F* … *\u002F",[523,2624,2625],{"class":536}," }\n",[523,2627,2628],{"class":525,"line":683},[523,2629,603],{"emptyLinePlaceholder":602},[523,2631,2632],{"class":525,"line":732},[523,2633,2634],{"class":679},"  \u002F\u002F A sibling route with no `middlewares` runs only the controller + module layers.\n",[523,2636,2637,2639,2641,2643,2645,2647,2649,2651,2653,2655,2657,2659,2662,2664,2666],{"class":525,"line":756},[523,2638,686],{"class":612},[523,2640,689],{"class":612},[523,2642,693],{"class":692},[523,2644,647],{"class":536},[523,2646,698],{"class":656},[523,2648,660],{"class":536},[523,2650,547],{"class":616},[523,2652,625],{"class":536},[523,2654,563],{"class":536},[523,2656,709],{"class":559},[523,2658,563],{"class":536},[523,2660,2661],{"class":536},">)",[523,2663,537],{"class":536},[523,2665,2622],{"class":679},[523,2667,2625],{"class":536},[523,2669,2670],{"class":525,"line":762},[523,2671,765],{"class":536},[499,2673,2674,2675,2678,2679,744],{},"A contract-mode handler carries the same per-route layer as an optional third argument on\n",[508,2676,2677],{},"Action\u003CApi, \"key\", { middlewares: [...] }>"," — see ",[1607,2680,2682],{"href":2681},"#bind-a-contract","Bind a contract",[499,2684,1297,2685,2688,2689,2691,2692,2695,2696,2699],{},[508,2686,2687],{},"middlewares: [...]"," reference ",[503,2690,1415],{}," the declaration — the compiler resolves each entry, reads its\nconstructor signature, and DI-constructs it; a route-level middleware needs ",[503,2693,2694],{},"no"," ",[508,2697,2698],{},"providers"," entry of its\nown, only a provider for its constructor dependencies in the module's injectable scope.",[799,2701,2703],{"id":2702},"handle-errors-with-filters","Handle errors with filters",[499,2705,2706,2709,2710,2712,2713,2715,2716,2719,2720,2723,2724,2727],{},[508,2707,2708],{},"ErrorFilter"," lives in ",[508,2711,560],{},", alongside ",[508,2714,1803],{},", because a filter's ",[508,2717,2718],{},"catch"," receives\nthe route's ",[508,2721,2722],{},"HttpAction",". It attaches through a controller's or module's ",[508,2725,2726],{},"http"," options. A handler just\nthrows the domain error; the filter maps it to a response, so the happy path stays free of error-shaping\ncode.",[513,2729,2732],{"className":515,"code":2730,"filename":2731,"language":518,"meta":519,"style":519},"import { type ErrorFilter, type HttpAction, HttpResponse } from \"@heximon\u002Fhttp\";\nimport { UserNotFoundError } from \".\u002Fuser-errors\";\n\nexport class UserNotFoundErrorFilter implements ErrorFilter\u003CUserNotFoundError> {\n  public catch(error: UserNotFoundError, action: HttpAction): Response {\n    const { pathname } = new URL(action.request.url);\n    return HttpResponse.json(\n      {\n        type: \"https:\u002F\u002Fexample.com\u002Fproblems\u002Fuser-not-found\",\n        status: 404,\n        title: \"User Not Found\",\n        detail: error.message,\n        instance: pathname,\n        userId: error.userId,\n      },\n      { status: 404, headers: { \"content-type\": \"application\u002Fproblem+json\" } },\n    );\n  }\n}\n","src\u002Fusers\u002Fuser-error.filter.ts",[508,2733,2734,2768,2790,2794,2816,2847,2879,2892,2897,2913,2925,2941,2958,2969,2985,2990,3032,3039,3043],{"__ignoreMap":519},[523,2735,2736,2738,2740,2742,2745,2747,2749,2751,2753,2756,2758,2760,2762,2764,2766],{"class":525,"line":526},[523,2737,530],{"class":529},[523,2739,537],{"class":536},[523,2741,533],{"class":529},[523,2743,2744],{"class":540}," ErrorFilter",[523,2746,544],{"class":536},[523,2748,533],{"class":529},[523,2750,1856],{"class":540},[523,2752,544],{"class":536},[523,2754,2755],{"class":540}," HttpResponse",[523,2757,550],{"class":536},[523,2759,553],{"class":529},[523,2761,556],{"class":536},[523,2763,560],{"class":559},[523,2765,563],{"class":536},[523,2767,566],{"class":536},[523,2769,2770,2772,2774,2777,2779,2781,2783,2786,2788],{"class":525,"line":569},[523,2771,530],{"class":529},[523,2773,537],{"class":536},[523,2775,2776],{"class":540}," UserNotFoundError",[523,2778,550],{"class":536},[523,2780,553],{"class":529},[523,2782,556],{"class":536},[523,2784,2785],{"class":559},".\u002Fuser-errors",[523,2787,563],{"class":536},[523,2789,566],{"class":536},[523,2791,2792],{"class":525,"line":599},[523,2793,603],{"emptyLinePlaceholder":602},[523,2795,2796,2798,2800,2803,2805,2807,2809,2812,2814],{"class":525,"line":606},[523,2797,609],{"class":529},[523,2799,613],{"class":612},[523,2801,2802],{"class":616}," UserNotFoundErrorFilter",[523,2804,620],{"class":612},[523,2806,2744],{"class":616},[523,2808,625],{"class":536},[523,2810,2811],{"class":616},"UserNotFoundError",[523,2813,635],{"class":536},[523,2815,638],{"class":536},[523,2817,2818,2820,2823,2825,2828,2830,2832,2834,2836,2838,2840,2842,2845],{"class":525,"line":641},[523,2819,686],{"class":612},[523,2821,2822],{"class":692}," catch",[523,2824,647],{"class":536},[523,2826,2827],{"class":656},"error",[523,2829,660],{"class":536},[523,2831,2776],{"class":616},[523,2833,544],{"class":536},[523,2835,1248],{"class":656},[523,2837,660],{"class":536},[523,2839,1856],{"class":616},[523,2841,1926],{"class":536},[523,2843,2844],{"class":616}," Response",[523,2846,638],{"class":536},[523,2848,2849,2851,2853,2855,2857,2859,2861,2863,2865,2867,2869,2871,2873,2875,2877],{"class":525,"line":671},[523,2850,1237],{"class":612},[523,2852,537],{"class":536},[523,2854,1987],{"class":540},[523,2856,550],{"class":536},[523,2858,1242],{"class":536},[523,2860,1994],{"class":536},[523,2862,1997],{"class":747},[523,2864,647],{"class":692},[523,2866,1072],{"class":540},[523,2868,744],{"class":536},[523,2870,1120],{"class":540},[523,2872,744],{"class":536},[523,2874,2010],{"class":540},[523,2876,665],{"class":692},[523,2878,566],{"class":536},[523,2880,2881,2883,2885,2887,2890],{"class":525,"line":676},[523,2882,735],{"class":529},[523,2884,2755],{"class":540},[523,2886,744],{"class":536},[523,2888,2889],{"class":747},"json",[523,2891,1163],{"class":692},[523,2893,2894],{"class":525,"line":683},[523,2895,2896],{"class":536},"      {\n",[523,2898,2899,2902,2904,2906,2909,2911],{"class":525,"line":732},[523,2900,2901],{"class":692},"        type",[523,2903,660],{"class":536},[523,2905,556],{"class":536},[523,2907,2908],{"class":559},"https:\u002F\u002Fexample.com\u002Fproblems\u002Fuser-not-found",[523,2910,563],{"class":536},[523,2912,1313],{"class":536},[523,2914,2915,2918,2920,2923],{"class":525,"line":756},[523,2916,2917],{"class":692},"        status",[523,2919,660],{"class":536},[523,2921,2922],{"class":1205}," 404",[523,2924,1313],{"class":536},[523,2926,2927,2930,2932,2934,2937,2939],{"class":525,"line":762},[523,2928,2929],{"class":692},"        title",[523,2931,660],{"class":536},[523,2933,556],{"class":536},[523,2935,2936],{"class":559},"User Not Found",[523,2938,563],{"class":536},[523,2940,1313],{"class":536},[523,2942,2943,2946,2948,2951,2953,2956],{"class":525,"line":1049},[523,2944,2945],{"class":692},"        detail",[523,2947,660],{"class":536},[523,2949,2950],{"class":540}," error",[523,2952,744],{"class":536},[523,2954,2955],{"class":540},"message",[523,2957,1313],{"class":536},[523,2959,2960,2963,2965,2967],{"class":525,"line":1054},[523,2961,2962],{"class":692},"        instance",[523,2964,660],{"class":536},[523,2966,1987],{"class":540},[523,2968,1313],{"class":536},[523,2970,2971,2974,2976,2978,2980,2983],{"class":525,"line":1060},[523,2972,2973],{"class":692},"        userId",[523,2975,660],{"class":536},[523,2977,2950],{"class":540},[523,2979,744],{"class":536},[523,2981,2982],{"class":540},"userId",[523,2984,1313],{"class":536},[523,2986,2987],{"class":525,"line":1100},[523,2988,2989],{"class":536},"      },\n",[523,2991,2992,2995,2998,3000,3002,3004,3007,3009,3011,3013,3016,3018,3020,3022,3025,3027,3029],{"class":525,"line":1137},[523,2993,2994],{"class":536},"      {",[523,2996,2997],{"class":692}," status",[523,2999,660],{"class":536},[523,3001,2922],{"class":1205},[523,3003,544],{"class":536},[523,3005,3006],{"class":692}," headers",[523,3008,660],{"class":536},[523,3010,537],{"class":536},[523,3012,556],{"class":536},[523,3014,3015],{"class":692},"content-type",[523,3017,563],{"class":536},[523,3019,660],{"class":536},[523,3021,556],{"class":536},[523,3023,3024],{"class":559},"application\u002Fproblem+json",[523,3026,563],{"class":536},[523,3028,550],{"class":536},[523,3030,3031],{"class":536}," },\n",[523,3033,3034,3037],{"class":525,"line":1142},[523,3035,3036],{"class":692},"    )",[523,3038,566],{"class":536},[523,3040,3041],{"class":525,"line":1147},[523,3042,759],{"class":536},[523,3044,3045],{"class":525,"line":1153},[523,3046,765],{"class":536},[499,3048,1297,3049,3052,3053,3056,3057,3060,3061,3063,3064,3066,3067,3069,3070,3073],{},[508,3050,3051],{},"ErrorFilter\u003CUserNotFoundError>"," type argument declares the error this filter catches (and its\nsubclasses); when a handler throws, the first filter that matches by ",[508,3054,3055],{},"instanceof"," wins. Filters run\n",[503,3058,3059],{},"narrow → broad"," (the controller's own first, then inherited reversed). A thrown ",[508,3062,1619],{},"\nmaps to a ",[508,3065,1623],{}," automatically; a custom ",[508,3068,2708],{}," attached via\n",[508,3071,3072],{},"http: { errorFilters }"," still dispatches first.",[799,3075,2682],{"id":3076},"bind-a-contract",[499,3078,3079,3080,3083,3084,660],{},"Inline mode is enough for a private API. The moment a second consumer shows up — a frontend, another\nservice, a generated OpenAPI spec — bind the controller to a shared ",[508,3081,3082],{},"Contract"," instead, by naming it as\nthe type argument. The contract then owns the prefix and the handler names, and each handler is typed per\nroute with ",[508,3085,3086],{},"Action\u003CApi, \"key\">",[513,3088,3091],{"className":515,"code":3089,"filename":3090,"language":518,"meta":519,"style":519},"import type { Action, Controller } from \"@heximon\u002Fhttp\";\nimport { ProductsApi } from \".\u002Fproduct.api\";\nimport { ProductsRepository } from \".\u002Fproducts.repository\";\n\nexport class ProductsController implements Controller\u003CProductsApi> {\n  constructor(private readonly products: ProductsRepository) {}\n\n  public async find(action: Action\u003CProductsApi, \"find\">) {\n    const { sku } = await action.request.getValidatedPathParams();\n    return this.products.getBySku(sku);\n  }\n}\n","src\u002Fproducts\u002Fproducts.controller.ts",[508,3092,3093,3120,3142,3164,3168,3190,3211,3215,3249,3278,3301,3305],{"__ignoreMap":519},[523,3094,3095,3097,3099,3101,3104,3106,3108,3110,3112,3114,3116,3118],{"class":525,"line":526},[523,3096,530],{"class":529},[523,3098,533],{"class":529},[523,3100,537],{"class":536},[523,3102,3103],{"class":540}," Action",[523,3105,544],{"class":536},[523,3107,541],{"class":540},[523,3109,550],{"class":536},[523,3111,553],{"class":529},[523,3113,556],{"class":536},[523,3115,560],{"class":559},[523,3117,563],{"class":536},[523,3119,566],{"class":536},[523,3121,3122,3124,3126,3129,3131,3133,3135,3138,3140],{"class":525,"line":569},[523,3123,530],{"class":529},[523,3125,537],{"class":536},[523,3127,3128],{"class":540}," ProductsApi",[523,3130,550],{"class":536},[523,3132,553],{"class":529},[523,3134,556],{"class":536},[523,3136,3137],{"class":559},".\u002Fproduct.api",[523,3139,563],{"class":536},[523,3141,566],{"class":536},[523,3143,3144,3146,3148,3151,3153,3155,3157,3160,3162],{"class":525,"line":599},[523,3145,530],{"class":529},[523,3147,537],{"class":536},[523,3149,3150],{"class":540}," ProductsRepository",[523,3152,550],{"class":536},[523,3154,553],{"class":529},[523,3156,556],{"class":536},[523,3158,3159],{"class":559},".\u002Fproducts.repository",[523,3161,563],{"class":536},[523,3163,566],{"class":536},[523,3165,3166],{"class":525,"line":606},[523,3167,603],{"emptyLinePlaceholder":602},[523,3169,3170,3172,3174,3177,3179,3181,3183,3186,3188],{"class":525,"line":641},[523,3171,609],{"class":529},[523,3173,613],{"class":612},[523,3175,3176],{"class":616}," ProductsController",[523,3178,620],{"class":612},[523,3180,541],{"class":616},[523,3182,625],{"class":536},[523,3184,3185],{"class":616},"ProductsApi",[523,3187,635],{"class":536},[523,3189,638],{"class":536},[523,3191,3192,3194,3196,3198,3200,3203,3205,3207,3209],{"class":525,"line":671},[523,3193,644],{"class":612},[523,3195,647],{"class":536},[523,3197,650],{"class":612},[523,3199,653],{"class":612},[523,3201,3202],{"class":656}," products",[523,3204,660],{"class":536},[523,3206,3150],{"class":616},[523,3208,665],{"class":536},[523,3210,668],{"class":536},[523,3212,3213],{"class":525,"line":676},[523,3214,603],{"emptyLinePlaceholder":602},[523,3216,3217,3219,3221,3224,3226,3228,3230,3232,3234,3236,3238,3240,3243,3245,3247],{"class":525,"line":683},[523,3218,686],{"class":612},[523,3220,689],{"class":612},[523,3222,3223],{"class":692}," find",[523,3225,647],{"class":536},[523,3227,1072],{"class":656},[523,3229,660],{"class":536},[523,3231,3103],{"class":616},[523,3233,625],{"class":536},[523,3235,3185],{"class":616},[523,3237,544],{"class":536},[523,3239,556],{"class":536},[523,3241,3242],{"class":559},"find",[523,3244,563],{"class":536},[523,3246,2661],{"class":536},[523,3248,638],{"class":536},[523,3250,3251,3253,3255,3258,3260,3262,3264,3266,3268,3270,3272,3274,3276],{"class":525,"line":732},[523,3252,1237],{"class":612},[523,3254,537],{"class":536},[523,3256,3257],{"class":540}," sku",[523,3259,550],{"class":536},[523,3261,1242],{"class":536},[523,3263,1245],{"class":529},[523,3265,1248],{"class":540},[523,3267,744],{"class":536},[523,3269,1120],{"class":540},[523,3271,744],{"class":536},[523,3273,1744],{"class":747},[523,3275,751],{"class":692},[523,3277,566],{"class":536},[523,3279,3280,3282,3284,3287,3289,3292,3294,3297,3299],{"class":525,"line":756},[523,3281,735],{"class":529},[523,3283,738],{"class":536},[523,3285,3286],{"class":540},"products",[523,3288,744],{"class":536},[523,3290,3291],{"class":747},"getBySku",[523,3293,647],{"class":692},[523,3295,3296],{"class":540},"sku",[523,3298,665],{"class":692},[523,3300,566],{"class":536},[523,3302,3303],{"class":525,"line":762},[523,3304,759],{"class":536},[523,3306,3307],{"class":525,"line":1049},[523,3308,765],{"class":536},[499,3310,1297,3311,3314],{},[508,3312,3313],{},"implements Controller\u003CProductsApi>"," clause both binds the class to the contract and makes TypeScript\ncheck that every contract route has a matching handler — one declaration of method, path, and schemas\nmeans the served API and its spec can never drift.",[499,3316,3317,3318,3320,3321,3323,3324,3327],{},"See ",[1607,3319,67],{"href":68}," for authoring the ",[508,3322,3185],{}," value, the config-object form\nfor controller-level middlewares (",[508,3325,3326],{},"Controller\u003C{ contract; middlewares }>","), and the exhaustiveness\u002Fprefix\nrules contract mode adds.",[799,3329,3331],{"id":3330},"wire-the-module","Wire the module",[499,3333,3334,3335,3337,3338,3340,3341,3344],{},"Controllers are discovered under the ",[508,3336,2726],{}," namespace; their constructor dependencies — repositories, and\nany middleware or error-filter classes you reference — go in ",[508,3339,2698],{},". Both arrays are ",[503,3342,3343],{},"static"," so the\ncompiler reads them from source.",[513,3346,3349],{"className":515,"code":3347,"filename":3348,"language":518,"meta":519,"style":519},"import { Module } from \"@heximon\u002Fruntime\";\nimport { RequestLoggerMiddleware } from \"..\u002Fcommon\u002Frequest-logger.middleware\";\nimport { UserNotFoundErrorFilter } from \".\u002Fuser-error.filter\";\nimport { UsersController } from \".\u002Fusers.controller\";\nimport { UsersRepository } from \".\u002Fusers.repository\";\n\nexport class UsersModule extends Module({\n  providers: [UsersRepository, RequestLoggerMiddleware, UserNotFoundErrorFilter],\n  http: { controllers: [UsersController] },\n}) {}\n","src\u002Fusers\u002Fusers.module.ts",[508,3350,3351,3373,3393,3414,3435,3455,3459,3478,3499,3519],{"__ignoreMap":519},[523,3352,3353,3355,3357,3360,3362,3364,3366,3369,3371],{"class":525,"line":526},[523,3354,530],{"class":529},[523,3356,537],{"class":536},[523,3358,3359],{"class":540}," Module",[523,3361,550],{"class":536},[523,3363,553],{"class":529},[523,3365,556],{"class":536},[523,3367,3368],{"class":559},"@heximon\u002Fruntime",[523,3370,563],{"class":536},[523,3372,566],{"class":536},[523,3374,3375,3377,3379,3381,3383,3385,3387,3389,3391],{"class":525,"line":569},[523,3376,530],{"class":529},[523,3378,537],{"class":536},[523,3380,1891],{"class":540},[523,3382,550],{"class":536},[523,3384,553],{"class":529},[523,3386,556],{"class":536},[523,3388,2217],{"class":559},[523,3390,563],{"class":536},[523,3392,566],{"class":536},[523,3394,3395,3397,3399,3401,3403,3405,3407,3410,3412],{"class":525,"line":599},[523,3396,530],{"class":529},[523,3398,537],{"class":536},[523,3400,2802],{"class":540},[523,3402,550],{"class":536},[523,3404,553],{"class":529},[523,3406,556],{"class":536},[523,3408,3409],{"class":559},".\u002Fuser-error.filter",[523,3411,563],{"class":536},[523,3413,566],{"class":536},[523,3415,3416,3418,3420,3422,3424,3426,3428,3431,3433],{"class":525,"line":606},[523,3417,530],{"class":529},[523,3419,537],{"class":536},[523,3421,617],{"class":540},[523,3423,550],{"class":536},[523,3425,553],{"class":529},[523,3427,556],{"class":536},[523,3429,3430],{"class":559},".\u002Fusers.controller",[523,3432,563],{"class":536},[523,3434,566],{"class":536},[523,3436,3437,3439,3441,3443,3445,3447,3449,3451,3453],{"class":525,"line":641},[523,3438,530],{"class":529},[523,3440,537],{"class":536},[523,3442,583],{"class":540},[523,3444,550],{"class":536},[523,3446,553],{"class":529},[523,3448,556],{"class":536},[523,3450,592],{"class":559},[523,3452,563],{"class":536},[523,3454,566],{"class":536},[523,3456,3457],{"class":525,"line":671},[523,3458,603],{"emptyLinePlaceholder":602},[523,3460,3461,3463,3465,3468,3471,3473,3475],{"class":525,"line":676},[523,3462,609],{"class":529},[523,3464,613],{"class":612},[523,3466,3467],{"class":616}," UsersModule",[523,3469,3470],{"class":612}," extends",[523,3472,3359],{"class":747},[523,3474,647],{"class":540},[523,3476,3477],{"class":536},"{\n",[523,3479,3480,3483,3485,3488,3490,3492,3494,3497],{"class":525,"line":683},[523,3481,3482],{"class":692},"  providers",[523,3484,660],{"class":536},[523,3486,3487],{"class":540}," [UsersRepository",[523,3489,544],{"class":536},[523,3491,1891],{"class":540},[523,3493,544],{"class":536},[523,3495,3496],{"class":540}," UserNotFoundErrorFilter]",[523,3498,1313],{"class":536},[523,3500,3501,3504,3506,3508,3511,3513,3516],{"class":525,"line":732},[523,3502,3503],{"class":692},"  http",[523,3505,660],{"class":536},[523,3507,537],{"class":536},[523,3509,3510],{"class":692}," controllers",[523,3512,660],{"class":536},[523,3514,3515],{"class":540}," [UsersController] ",[523,3517,3518],{"class":536},"},\n",[523,3520,3521,3523,3526],{"class":525,"line":756},[523,3522,2063],{"class":536},[523,3524,3525],{"class":540},") ",[523,3527,3528],{"class":536},"{}\n",[499,3530,3531,3532,3535,3536,3538],{},"The controller's options say ",[1413,3533,3534],{},"where"," a middleware or filter applies; listing it in ",[508,3537,2698],{}," makes it\npart of the construction graph so the runtime resolves it by identity. Forget the provider and it's a build\nerror — the compiler reports a controller-named middleware with no provider in scope instead of letting the\nrequest fail.",[799,3540,3542],{"id":3541},"how-a-request-reaches-your-handler","How a request reaches your handler",[499,3544,3545,3546,3549,3550,3553,3554,3557],{},"You never write a server entry. The ",[508,3547,3548],{},"heximon()"," Vite plugin compiles your controllers into plain JavaScript\nwiring and a ",[508,3551,3552],{},"fetch"," boot, and ",[508,3555,3556],{},"pnpm dev"," serves it. From there a request flows through three things you\ncan observe but never hand-write:",[3559,3560,3561,3575,3587],"ul",{},[3562,3563,3564,3567,3568,3571,3572,3574],"li",{},[503,3565,3566],{},"A route table"," — one ",[508,3569,3570],{},"{ method, path, handler }"," row per route, already baking in its ",[508,3573,1811],{}," frame,\nroute descriptor, CORS\u002Fcache policy, middleware chain, controller resolution, and error filters. Inline\nand contract routes merge into this one table at boot, which is why both modes get identical semantics.",[3562,3576,3577,3582,3583,3586],{},[503,3578,1800,3579,3581],{},[508,3580,3552],{}," boot entry"," — builds the DI graph directly (there is no runtime container) and exports\n",[508,3584,3585],{},"fetch(request)",". This is what a host loads.",[3562,3588,3589,3592,3593,3595],{},[503,3590,3591],{},"The request dispatcher"," — on a hit it runs the middleware onion, invokes your handler on its resolved\ncontroller, and converts the return to a ",[508,3594,1823],{},"; a thrown error threads through the filters\nmost-specific-first.",[499,3597,3598,3599,3601],{},"Each app gets its own route table, so two apps in one process stay fully isolated. In production another\ntransport usually owns the live server while HTTP supplies the routes — WebSockets and SSE mount their\nupgrade routes onto the same server, and Durable Objects ride the same boot. See\n",[1607,3600,346],{"href":347}," for choosing a host.",[799,3603,3605],{"id":3604},"test-in-process","Test in-process",[499,3607,3608,3611,3612,3614,3615,3617,3618,3620,3621,3624],{},[508,3609,3610],{},"@heximon\u002Fhttp\u002Ftesting"," drives a real ",[508,3613,1475],{}," through the generated ",[508,3616,3552],{}," — the same route chain\na live host serves, with a per-request ",[508,3619,1811],{},", no network. Overriding a provider at ",[508,3622,3623],{},"createApp"," swaps a\ndependency (a recording mailer, an in-memory store) without touching the controller.",[513,3626,3629],{"className":515,"code":3627,"filename":3628,"language":518,"meta":519,"style":519},"import { createTestClient } from \"@heximon\u002Fhttp\u002Ftesting\";\nimport { createTestApp } from \"@heximon\u002Ftesting\";\n\nconst app = await createTestApp({ root });\nconst client = createTestClient(app);\n\nconst response = await client.request(new Request(\"http:\u002F\u002Flocalhost\u002Fusers\"));\nexpect(response.status).toBe(200);\n","users.test.ts",[508,3630,3631,3652,3674,3678,3705,3721,3725,3765],{"__ignoreMap":519},[523,3632,3633,3635,3637,3640,3642,3644,3646,3648,3650],{"class":525,"line":526},[523,3634,530],{"class":529},[523,3636,537],{"class":536},[523,3638,3639],{"class":540}," createTestClient",[523,3641,550],{"class":536},[523,3643,553],{"class":529},[523,3645,556],{"class":536},[523,3647,3610],{"class":559},[523,3649,563],{"class":536},[523,3651,566],{"class":536},[523,3653,3654,3656,3658,3661,3663,3665,3667,3670,3672],{"class":525,"line":569},[523,3655,530],{"class":529},[523,3657,537],{"class":536},[523,3659,3660],{"class":540}," createTestApp",[523,3662,550],{"class":536},[523,3664,553],{"class":529},[523,3666,556],{"class":536},[523,3668,3669],{"class":559},"@heximon\u002Ftesting",[523,3671,563],{"class":536},[523,3673,566],{"class":536},[523,3675,3676],{"class":525,"line":599},[523,3677,603],{"emptyLinePlaceholder":602},[523,3679,3680,3682,3685,3687,3689,3691,3693,3696,3699,3701,3703],{"class":525,"line":606},[523,3681,1640],{"class":612},[523,3683,3684],{"class":540}," app ",[523,3686,1646],{"class":536},[523,3688,1245],{"class":529},[523,3690,3660],{"class":747},[523,3692,647],{"class":540},[523,3694,3695],{"class":536},"{",[523,3697,3698],{"class":540}," root ",[523,3700,2063],{"class":536},[523,3702,665],{"class":540},[523,3704,566],{"class":536},[523,3706,3707,3709,3712,3714,3716,3719],{"class":525,"line":641},[523,3708,1640],{"class":612},[523,3710,3711],{"class":540}," client ",[523,3713,1646],{"class":536},[523,3715,3639],{"class":747},[523,3717,3718],{"class":540},"(app)",[523,3720,566],{"class":536},[523,3722,3723],{"class":525,"line":671},[523,3724,603],{"emptyLinePlaceholder":602},[523,3726,3727,3729,3732,3734,3736,3739,3741,3743,3745,3748,3751,3753,3755,3758,3760,3763],{"class":525,"line":676},[523,3728,1640],{"class":612},[523,3730,3731],{"class":540}," response ",[523,3733,1646],{"class":536},[523,3735,1245],{"class":529},[523,3737,3738],{"class":540}," client",[523,3740,744],{"class":536},[523,3742,1120],{"class":747},[523,3744,647],{"class":540},[523,3746,3747],{"class":536},"new",[523,3749,3750],{"class":747}," Request",[523,3752,647],{"class":540},[523,3754,563],{"class":536},[523,3756,3757],{"class":559},"http:\u002F\u002Flocalhost\u002Fusers",[523,3759,563],{"class":536},[523,3761,3762],{"class":540},"))",[523,3764,566],{"class":536},[523,3766,3767,3770,3773,3775,3778,3780,3783,3785,3787,3789],{"class":525,"line":683},[523,3768,3769],{"class":747},"expect",[523,3771,3772],{"class":540},"(response",[523,3774,744],{"class":536},[523,3776,3777],{"class":540},"status)",[523,3779,744],{"class":536},[523,3781,3782],{"class":747},"toBe",[523,3784,647],{"class":540},[523,3786,1520],{"class":1205},[523,3788,665],{"class":540},[523,3790,566],{"class":536},[799,3792,3794],{"id":3793},"see-also","See also",[3559,3796,3797,3809,3818,3824,3829,3843,3858],{},[3562,3798,3799,3801,3802,3804,3805,3808],{},[1607,3800,67],{"href":68}," — author a shared ",[508,3803,3082],{}," with the ",[508,3806,3807],{},"Route"," builder for\ncontract-mode controllers, then bind a server and a client to the same value.",[3562,3810,3811,3813,3814,3817],{},[1607,3812,62],{"href":63}," — validate bodies, queries, params, and headers against any\nStandard Schema at the boundary, with the built-in ",[508,3815,3816],{},"400 problem+json"," on failure.",[3562,3819,3820,3823],{},[1607,3821,3822],{"href":73},"REST client"," — call the same contract from the frontend with a fully typed client.",[3562,3825,3826,3828],{},[1607,3827,199],{"href":200}," — generate an OpenAPI 3.1 document and serve Scalar \u002F Swagger UI from a\ncontract.",[3562,3830,3831,3836,3837,3839,3840,3842],{},[1607,3832,3835],{"href":3833,"rel":3834},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fladder\u002FL01-minimal",[1611],"the ladder's L01 — Minimal"," — an inline\ncontroller plus a module-level middleware that reads ",[508,3838,1579],{}," to skip a ",[508,3841,2421],{}," route.",[3562,3844,3845,3850,3851,816,3854,3857],{},[1607,3846,3849],{"href":3847,"rel":3848},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fladder\u002FL02-http-validation",[1611],"the ladder's L02 — HTTP & validation","\n— inline routes with a validated body, controller-scoped middleware, and error filters that map domain\nerrors to ",[508,3852,3853],{},"404",[508,3855,3856],{},"409"," problem+json.",[3562,3859,3860,3867,3868,3870],{},[1607,3861,3864],{"href":3862,"rel":3863},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fgap\u002Fopenapi-mcp",[1611],[503,3865,3866],{},"OpenAPI + MCP"," — a\ncontract-mode controller bound to a ",[508,3869,3082],{}," that also drives the generated OpenAPI document.",[3872,3873,3874],"style",{},"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 .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}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 .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 .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":519,"searchDepth":569,"depth":569,"links":3876},[3877,3878,3879,3880,3883,3884,3885,3886,3887,3888],{"id":801,"depth":569,"text":802},{"id":1419,"depth":569,"text":1420},{"id":1598,"depth":569,"text":1599},{"id":1796,"depth":569,"text":1797,"children":3881},[3882],{"id":2425,"depth":599,"text":2426},{"id":2702,"depth":569,"text":2703},{"id":3076,"depth":569,"text":2682},{"id":3330,"depth":569,"text":3331},{"id":3541,"depth":569,"text":3542},{"id":3604,"depth":569,"text":3605},{"id":3793,"depth":569,"text":3794},"Build the HTTP surface with Controllers — routes declared by the handler's action parameter type, the HttpAction dispatcher, middleware, error filters, and contract binding.","md",null,{},{"icon":60},{"title":57,"description":3889},"bIOpqJN0U7TShI-AmlVBUPb9YVDON4GfUHPLWo4rhEc",[3897,3899],{"title":53,"path":54,"stem":55,"description":3898,"icon":48,"children":-1},"Group a feature with Module(...), wire providers with constructor injection, share them via imports and exports, and let the editor catch a broken DI graph before you build.",{"title":62,"path":63,"stem":64,"description":3900,"icon":65,"children":-1},"Request bodies, query, path params, and headers are validated automatically at dispatch — the handler receives typed data without calling an accessor. Declare the shape once with SchemaObject and any Standard Schema validator.",1783300761903]