[{"data":1,"prerenderedAt":2927},["ShallowReactive",2],{"navigation_docs":3,"-docs-essentials-validation":493,"-docs-essentials-validation-surround":2922},[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":62,"body":495,"description":2915,"extension":2916,"links":2917,"meta":2918,"navigation":2919,"path":63,"seo":2920,"stem":64,"__hash__":2921},"docs\u002Fdocs\u002F02.essentials\u002F03.validation.md",{"type":496,"value":497,"toc":2903},"minimark",[498,507,522,527,538,853,873,877,891,1015,1022,1025,1101,1108,1112,1119,1173,1184,1474,1490,1494,1501,1915,1924,1928,1941,2198,2207,2214,2299,2303,2340,2344,2355,2426,2444,2478,2482,2502,2777,2792,2796,2807,2811,2899],[499,500,501,502,506],"p",{},"Declare a schema on a route and validation runs automatically — before the middleware chain, before the\nhandler body. The handler receives already-validated, typed data. Anything that fails the check is\nrejected before your code runs; the caller gets a ",[503,504,505],"code",{},"400 application\u002Fproblem+json"," response.",[499,508,509,510,513,514,521],{},"You describe each shape as a DTO class with ",[503,511,512],{},"SchemaObject",", built over per-field validators from any\n",[515,516,520],"a",{"href":517,"rel":518},"https:\u002F\u002Fstandardschema.dev",[519],"nofollow","Standard Schema"," library. Zod is the default, but Valibot and ArkType work\nidentically — Heximon owns the boundary plumbing and delegates each field to the validator you bring.",[523,524,526],"h2",{"id":525},"author-a-dto-with-schemaobject","Author a DTO with SchemaObject",[499,528,529,532,533,537],{},[503,530,531],{},"SchemaObject(shape)"," turns a record of per-field validators into a class that is, at the same time, the\ninferred TypeScript type ",[534,535,536],"strong",{},"and"," a usable Standard-Schema value. That dual nature is the whole point:\nthe one class you write is what a route validates against, what a handler reads back, and what the type\nchecker holds you to — no separate interface to keep in sync.",[539,540,546],"pre",{"className":541,"code":542,"filename":543,"language":544,"meta":545,"style":545},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { SchemaObject } from \"@heximon\u002Fschema\";\nimport { z } from \"zod\";\n\n\u002F** The POST \u002Fapi\u002Fproducts request body — no server-managed fields. *\u002F\nexport class CreateProduct extends SchemaObject({\n  sku: z.string().min(1),\n  name: z.string().min(1),\n  priceInCents: z.number().int().positive(),\n}) {}\n\n\u002F** A single product as the API returns it. *\u002F\nexport class Product extends SchemaObject({\n  sku: z.string(),\n  name: z.string(),\n  priceInCents: z.number().int(),\n}) {}\n","src\u002Fproducts\u002Fproduct.schema.ts","ts","",[503,547,548,584,607,614,621,647,685,713,746,758,763,769,787,804,821,844],{"__ignoreMap":545},[549,550,553,557,561,565,568,571,574,578,581],"span",{"class":551,"line":552},"line",1,[549,554,556],{"class":555},"s7zQu","import",[549,558,560],{"class":559},"sMK4o"," {",[549,562,564],{"class":563},"sTEyZ"," SchemaObject",[549,566,567],{"class":559}," }",[549,569,570],{"class":555}," from",[549,572,573],{"class":559}," \"",[549,575,577],{"class":576},"sfazB","@heximon\u002Fschema",[549,579,580],{"class":559},"\"",[549,582,583],{"class":559},";\n",[549,585,587,589,591,594,596,598,600,603,605],{"class":551,"line":586},2,[549,588,556],{"class":555},[549,590,560],{"class":559},[549,592,593],{"class":563}," z",[549,595,567],{"class":559},[549,597,570],{"class":555},[549,599,573],{"class":559},[549,601,602],{"class":576},"zod",[549,604,580],{"class":559},[549,606,583],{"class":559},[549,608,610],{"class":551,"line":609},3,[549,611,613],{"emptyLinePlaceholder":612},true,"\n",[549,615,617],{"class":551,"line":616},4,[549,618,620],{"class":619},"sHwdD","\u002F** The POST \u002Fapi\u002Fproducts request body — no server-managed fields. *\u002F\n",[549,622,624,627,631,635,638,641,644],{"class":551,"line":623},5,[549,625,626],{"class":555},"export",[549,628,630],{"class":629},"spNyl"," class",[549,632,634],{"class":633},"sBMFI"," CreateProduct",[549,636,637],{"class":629}," extends",[549,639,564],{"class":640},"s2Zo4",[549,642,643],{"class":563},"(",[549,645,646],{"class":559},"{\n",[549,648,650,654,657,659,662,665,668,670,673,675,679,682],{"class":551,"line":649},6,[549,651,653],{"class":652},"swJcz","  sku",[549,655,656],{"class":559},":",[549,658,593],{"class":563},[549,660,661],{"class":559},".",[549,663,664],{"class":640},"string",[549,666,667],{"class":563},"()",[549,669,661],{"class":559},[549,671,672],{"class":640},"min",[549,674,643],{"class":563},[549,676,678],{"class":677},"sbssI","1",[549,680,681],{"class":563},")",[549,683,684],{"class":559},",\n",[549,686,688,691,693,695,697,699,701,703,705,707,709,711],{"class":551,"line":687},7,[549,689,690],{"class":652},"  name",[549,692,656],{"class":559},[549,694,593],{"class":563},[549,696,661],{"class":559},[549,698,664],{"class":640},[549,700,667],{"class":563},[549,702,661],{"class":559},[549,704,672],{"class":640},[549,706,643],{"class":563},[549,708,678],{"class":677},[549,710,681],{"class":563},[549,712,684],{"class":559},[549,714,716,719,721,723,725,728,730,732,735,737,739,742,744],{"class":551,"line":715},8,[549,717,718],{"class":652},"  priceInCents",[549,720,656],{"class":559},[549,722,593],{"class":563},[549,724,661],{"class":559},[549,726,727],{"class":640},"number",[549,729,667],{"class":563},[549,731,661],{"class":559},[549,733,734],{"class":640},"int",[549,736,667],{"class":563},[549,738,661],{"class":559},[549,740,741],{"class":640},"positive",[549,743,667],{"class":563},[549,745,684],{"class":559},[549,747,749,752,755],{"class":551,"line":748},9,[549,750,751],{"class":559},"}",[549,753,754],{"class":563},") ",[549,756,757],{"class":559},"{}\n",[549,759,761],{"class":551,"line":760},10,[549,762,613],{"emptyLinePlaceholder":612},[549,764,766],{"class":551,"line":765},11,[549,767,768],{"class":619},"\u002F** A single product as the API returns it. *\u002F\n",[549,770,772,774,776,779,781,783,785],{"class":551,"line":771},12,[549,773,626],{"class":555},[549,775,630],{"class":629},[549,777,778],{"class":633}," Product",[549,780,637],{"class":629},[549,782,564],{"class":640},[549,784,643],{"class":563},[549,786,646],{"class":559},[549,788,790,792,794,796,798,800,802],{"class":551,"line":789},13,[549,791,653],{"class":652},[549,793,656],{"class":559},[549,795,593],{"class":563},[549,797,661],{"class":559},[549,799,664],{"class":640},[549,801,667],{"class":563},[549,803,684],{"class":559},[549,805,807,809,811,813,815,817,819],{"class":551,"line":806},14,[549,808,690],{"class":652},[549,810,656],{"class":559},[549,812,593],{"class":563},[549,814,661],{"class":559},[549,816,664],{"class":640},[549,818,667],{"class":563},[549,820,684],{"class":559},[549,822,824,826,828,830,832,834,836,838,840,842],{"class":551,"line":823},15,[549,825,718],{"class":652},[549,827,656],{"class":559},[549,829,593],{"class":563},[549,831,661],{"class":559},[549,833,727],{"class":640},[549,835,667],{"class":563},[549,837,661],{"class":559},[549,839,734],{"class":640},[549,841,667],{"class":563},[549,843,684],{"class":559},[549,845,847,849,851],{"class":551,"line":846},16,[549,848,751],{"class":559},[549,850,754],{"class":563},[549,852,757],{"class":559},[499,854,855,857,858,860,861,864,865,868,869,872],{},[503,856,577],{}," itself imports no validator library — ",[503,859,602],{}," is only there because that's what these docs\ndefault to. Swap the ",[503,862,863],{},"import { z } from \"zod\""," line for ",[503,866,867],{},"valibot"," or ",[503,870,871],{},"arktype"," and nothing else changes,\nbecause the package depends only on the Standard Schema contract every one of them implements.",[523,874,876],{"id":875},"validated-by-default-at-dispatch","Validated by default at dispatch",[499,878,879,880,883,884,887,888,890],{},"When a route declares a schema, the framework validates the matching input ",[534,881,882],{},"at dispatch time"," — before\nthe middleware chain runs, before the handler body executes. The validated typed value is attached to the\nrequest, and any accessor call in the handler returns it immediately from the cache. Invalid input throws\na ",[503,885,886],{},"RequestValidationError",", which the built-in filter maps to a ",[503,889,505],{}," response\nbefore the handler runs.",[539,892,894],{"className":541,"code":893,"language":544,"meta":545,"style":545},"\u002F\u002F Inside a contract-mode controller (`implements Controller\u003CProductsApi>`):\npublic async create(action: Action\u003CProductsApi, \"create\">) {\n  const body = await action.request.readValidatedBody(); \u002F\u002F typed CreateProduct — already validated at dispatch\n  return action.respond(201, await this.products.create(body));\n}\n",[503,895,896,901,934,969,1010],{"__ignoreMap":545},[549,897,898],{"class":551,"line":552},[549,899,900],{"class":619},"\u002F\u002F Inside a contract-mode controller (`implements Controller\u003CProductsApi>`):\n",[549,902,903,906,909,912,915,918,921,923,925,927,930,932],{"class":551,"line":586},[549,904,905],{"class":563},"public async ",[549,907,908],{"class":640},"create",[549,910,911],{"class":563},"(action: Action",[549,913,914],{"class":559},"\u003C",[549,916,917],{"class":563},"ProductsApi",[549,919,920],{"class":559},",",[549,922,573],{"class":559},[549,924,908],{"class":576},[549,926,580],{"class":559},[549,928,929],{"class":559},">",[549,931,754],{"class":563},[549,933,646],{"class":559},[549,935,936,939,942,945,948,951,953,956,958,961,963,966],{"class":551,"line":609},[549,937,938],{"class":629},"  const",[549,940,941],{"class":563}," body",[549,943,944],{"class":559}," =",[549,946,947],{"class":555}," await",[549,949,950],{"class":563}," action",[549,952,661],{"class":559},[549,954,955],{"class":563},"request",[549,957,661],{"class":559},[549,959,960],{"class":640},"readValidatedBody",[549,962,667],{"class":652},[549,964,965],{"class":559},";",[549,967,968],{"class":619}," \u002F\u002F typed CreateProduct — already validated at dispatch\n",[549,970,971,974,976,978,981,983,986,988,990,993,996,998,1000,1002,1005,1008],{"class":551,"line":616},[549,972,973],{"class":555},"  return",[549,975,950],{"class":563},[549,977,661],{"class":559},[549,979,980],{"class":640},"respond",[549,982,643],{"class":652},[549,984,985],{"class":677},"201",[549,987,920],{"class":559},[549,989,947],{"class":555},[549,991,992],{"class":559}," this.",[549,994,995],{"class":563},"products",[549,997,661],{"class":559},[549,999,908],{"class":640},[549,1001,643],{"class":652},[549,1003,1004],{"class":563},"body",[549,1006,1007],{"class":652},"))",[549,1009,583],{"class":559},[549,1011,1012],{"class":551,"line":623},[549,1013,1014],{"class":559},"}\n",[499,1016,1017,1018,1021],{},"The ",[503,1019,1020],{},"readValidatedBody()"," call above is an accessor returning the dispatch-pre-validated value from the\ncache. A handler that never calls the accessor still gets the protection — invalid input is rejected\nbefore the handler body runs.",[499,1023,1024],{},"The four accessor methods remain available as escape-hatches for programmatic access or to pass an\nexplicit override schema:",[1026,1027,1028,1045],"table",{},[1029,1030,1031],"thead",{},[1032,1033,1034,1039,1042],"tr",{},[1035,1036,1038],"th",{"align":1037},"left","Accessor",[1035,1040,1041],{"align":1037},"Reads",[1035,1043,1044],{"align":1037},"Notes",[1046,1047,1048,1061,1073,1089],"tbody",{},[1032,1049,1050,1055,1058],{},[1051,1052,1053],"td",{"align":1037},[503,1054,1020],{},[1051,1056,1057],{"align":1037},"the request body",[1051,1059,1060],{"align":1037},"dispatch pre-validates; returns cache hit",[1032,1062,1063,1068,1071],{},[1051,1064,1065],{"align":1037},[503,1066,1067],{},"getValidatedQuery()",[1051,1069,1070],{"align":1037},"the query string",[1051,1072,1060],{"align":1037},[1032,1074,1075,1080,1087],{},[1051,1076,1077],{"align":1037},[503,1078,1079],{},"getValidatedPathParams()",[1051,1081,1082,1083,1086],{"align":1037},"the ",[503,1084,1085],{},":segments"," of the path",[1051,1088,1060],{"align":1037},[1032,1090,1091,1096,1099],{},[1051,1092,1093],{"align":1037},[503,1094,1095],{},"getValidatedHeaders()",[1051,1097,1098],{"align":1037},"the request headers",[1051,1100,1060],{"align":1037},[499,1102,1103,1104,1107],{},"Pass a schema explicitly — ",[503,1105,1106],{},"readValidatedBody(SomeDto)"," — to bypass the cache and re-validate with a\ndifferent schema. The return type follows from the argument instead.",[523,1109,1111],{"id":1110},"structured-query-parameters","Structured query parameters",[499,1113,1114,1115,1118],{},"A query string is flat text, but a query schema can declare arrays and nested objects. The server expands\nthe ",[534,1116,1117],{},"bracket notation"," a client serializes them with back into structure before validation — so a query\nschema with an array or nested object round-trips by default:",[1026,1120,1121,1131],{},[1029,1122,1123],{},[1032,1124,1125,1128],{},[1035,1126,1127],{"align":1037},"Wire",[1035,1129,1130],{"align":1037},"Parsed",[1046,1132,1133,1149,1161],{},[1032,1134,1135,1144],{},[1051,1136,1137,1140,1141,681],{"align":1037},[503,1138,1139],{},"?tags[0]=a&tags[1]=b"," (or ",[503,1142,1143],{},"?tags[]=a&tags[]=b",[1051,1145,1146],{"align":1037},[503,1147,1148],{},"{ tags: [\"a\", \"b\"] }",[1032,1150,1151,1156],{},[1051,1152,1153],{"align":1037},[503,1154,1155],{},"?filter[status]=active&filter[city]=NYC",[1051,1157,1158],{"align":1037},[503,1159,1160],{},"{ filter: { status: \"active\", city: \"NYC\" } }",[1032,1162,1163,1168],{},[1051,1164,1165],{"align":1037},[503,1166,1167],{},"?q=boots&page=2",[1051,1169,1170],{"align":1037},[503,1171,1172],{},"{ q: \"boots\", page: \"2\" }",[499,1174,1175,1176,1179,1180,1183],{},"This is the exact scheme the ",[503,1177,1178],{},"@heximon\u002Fcontract"," self-client emits, so a structured query a client sends\narrives as the arrays\u002Fobjects the route's ",[503,1181,1182],{},"query"," schema expects:",[539,1185,1187],{"className":541,"code":1186,"language":544,"meta":545,"style":545},"import { Route } from \"@heximon\u002Fcontract\";\nimport { z } from \"zod\";\n\n\u002F\u002F ?q=boots&tags[0]=a&tags[1]=b&filter[status]=active  →  { q, tags: [...], filter: { status } }\nexport const search = Route.get(\"\u002Fsearch\")\n  .query(\n    z.object({\n      q: z.string().optional(),\n      tags: z.array(z.string()).optional(),\n      filter: z.object({ status: z.string(), city: z.string() }).optional(),\n    }),\n  )\n  .responses({ 200: z.object({ ok: z.boolean() }) });\n",[503,1188,1189,1210,1230,1234,1239,1271,1281,1295,1319,1351,1410,1419,1424],{"__ignoreMap":545},[549,1190,1191,1193,1195,1198,1200,1202,1204,1206,1208],{"class":551,"line":552},[549,1192,556],{"class":555},[549,1194,560],{"class":559},[549,1196,1197],{"class":563}," Route",[549,1199,567],{"class":559},[549,1201,570],{"class":555},[549,1203,573],{"class":559},[549,1205,1178],{"class":576},[549,1207,580],{"class":559},[549,1209,583],{"class":559},[549,1211,1212,1214,1216,1218,1220,1222,1224,1226,1228],{"class":551,"line":586},[549,1213,556],{"class":555},[549,1215,560],{"class":559},[549,1217,593],{"class":563},[549,1219,567],{"class":559},[549,1221,570],{"class":555},[549,1223,573],{"class":559},[549,1225,602],{"class":576},[549,1227,580],{"class":559},[549,1229,583],{"class":559},[549,1231,1232],{"class":551,"line":609},[549,1233,613],{"emptyLinePlaceholder":612},[549,1235,1236],{"class":551,"line":616},[549,1237,1238],{"class":619},"\u002F\u002F ?q=boots&tags[0]=a&tags[1]=b&filter[status]=active  →  { q, tags: [...], filter: { status } }\n",[549,1240,1241,1243,1246,1249,1252,1254,1256,1259,1261,1263,1266,1268],{"class":551,"line":623},[549,1242,626],{"class":555},[549,1244,1245],{"class":629}," const",[549,1247,1248],{"class":563}," search ",[549,1250,1251],{"class":559},"=",[549,1253,1197],{"class":563},[549,1255,661],{"class":559},[549,1257,1258],{"class":640},"get",[549,1260,643],{"class":563},[549,1262,580],{"class":559},[549,1264,1265],{"class":576},"\u002Fsearch",[549,1267,580],{"class":559},[549,1269,1270],{"class":563},")\n",[549,1272,1273,1276,1278],{"class":551,"line":649},[549,1274,1275],{"class":559},"  .",[549,1277,1182],{"class":640},[549,1279,1280],{"class":563},"(\n",[549,1282,1283,1286,1288,1291,1293],{"class":551,"line":687},[549,1284,1285],{"class":563},"    z",[549,1287,661],{"class":559},[549,1289,1290],{"class":640},"object",[549,1292,643],{"class":563},[549,1294,646],{"class":559},[549,1296,1297,1300,1302,1304,1306,1308,1310,1312,1315,1317],{"class":551,"line":715},[549,1298,1299],{"class":652},"      q",[549,1301,656],{"class":559},[549,1303,593],{"class":563},[549,1305,661],{"class":559},[549,1307,664],{"class":640},[549,1309,667],{"class":563},[549,1311,661],{"class":559},[549,1313,1314],{"class":640},"optional",[549,1316,667],{"class":563},[549,1318,684],{"class":559},[549,1320,1321,1324,1326,1328,1330,1333,1336,1338,1340,1343,1345,1347,1349],{"class":551,"line":748},[549,1322,1323],{"class":652},"      tags",[549,1325,656],{"class":559},[549,1327,593],{"class":563},[549,1329,661],{"class":559},[549,1331,1332],{"class":640},"array",[549,1334,1335],{"class":563},"(z",[549,1337,661],{"class":559},[549,1339,664],{"class":640},[549,1341,1342],{"class":563},"())",[549,1344,661],{"class":559},[549,1346,1314],{"class":640},[549,1348,667],{"class":563},[549,1350,684],{"class":559},[549,1352,1353,1356,1358,1360,1362,1364,1366,1369,1372,1374,1376,1378,1380,1382,1384,1387,1389,1391,1393,1395,1398,1400,1402,1404,1406,1408],{"class":551,"line":760},[549,1354,1355],{"class":652},"      filter",[549,1357,656],{"class":559},[549,1359,593],{"class":563},[549,1361,661],{"class":559},[549,1363,1290],{"class":640},[549,1365,643],{"class":563},[549,1367,1368],{"class":559},"{",[549,1370,1371],{"class":652}," status",[549,1373,656],{"class":559},[549,1375,593],{"class":563},[549,1377,661],{"class":559},[549,1379,664],{"class":640},[549,1381,667],{"class":563},[549,1383,920],{"class":559},[549,1385,1386],{"class":652}," city",[549,1388,656],{"class":559},[549,1390,593],{"class":563},[549,1392,661],{"class":559},[549,1394,664],{"class":640},[549,1396,1397],{"class":563},"() ",[549,1399,751],{"class":559},[549,1401,681],{"class":563},[549,1403,661],{"class":559},[549,1405,1314],{"class":640},[549,1407,667],{"class":563},[549,1409,684],{"class":559},[549,1411,1412,1415,1417],{"class":551,"line":765},[549,1413,1414],{"class":559},"    }",[549,1416,681],{"class":563},[549,1418,684],{"class":559},[549,1420,1421],{"class":551,"line":771},[549,1422,1423],{"class":563},"  )\n",[549,1425,1426,1428,1431,1433,1435,1438,1440,1442,1444,1446,1448,1450,1453,1455,1457,1459,1462,1464,1466,1468,1470,1472],{"class":551,"line":789},[549,1427,1275],{"class":559},[549,1429,1430],{"class":640},"responses",[549,1432,643],{"class":563},[549,1434,1368],{"class":559},[549,1436,1437],{"class":677}," 200",[549,1439,656],{"class":559},[549,1441,593],{"class":563},[549,1443,661],{"class":559},[549,1445,1290],{"class":640},[549,1447,643],{"class":563},[549,1449,1368],{"class":559},[549,1451,1452],{"class":652}," ok",[549,1454,656],{"class":559},[549,1456,593],{"class":563},[549,1458,661],{"class":559},[549,1460,1461],{"class":640},"boolean",[549,1463,1397],{"class":563},[549,1465,751],{"class":559},[549,1467,754],{"class":563},[549,1469,751],{"class":559},[549,1471,681],{"class":563},[549,1473,583],{"class":559},[499,1475,1476,1477,1480,1481,1484,1485,1487,1488,661],{},"The expansion reads only the key syntax — it never inspects the schema, and it does ",[534,1478,1479],{},"not"," comma-split\nvalues (a ",[503,1482,1483],{},"\"a,b\""," query value stays the string ",[503,1486,1483],{},"; use bracket notation or repeated keys for an array).\nPath parameters and headers are likewise passed through as-is — the contract types each as a ",[503,1489,664],{},[523,1491,1493],{"id":1492},"raw-body-opt-out","Raw-body opt-out",[499,1495,1496,1497,1500],{},"Webhook-signature or streaming routes must receive the body stream unconsumed. Declare ",[503,1498,1499],{},".rawBody()"," on\nthe route builder to opt out of dispatch-time body buffering for that route:",[539,1502,1505],{"className":541,"code":1503,"filename":1504,"language":544,"meta":545,"style":545},"import type { Controller, Post } from \"@heximon\u002Fhttp\";\nimport { Route } from \"@heximon\u002Fcontract\";\nimport { createHmac } from \"node:crypto\";\n\n\u002F\u002F `.rawBody()` opts the route out of dispatch-time body buffering (it works on a `Contract` route too).\nexport const webhookRoute = Route.post(\"\u002Fwebhook\").rawBody();\n\nexport class WebhookController implements Controller\u003C\"\u002Fwebhook\"> {\n  public async receive(action: Post\u003C\"\u002F\", { rawBody: true }>): Promise\u003C{ ok: boolean }> {\n    \u002F\u002F action.request.bodyUsed is false — the dispatcher did not consume the stream, so the raw\n    \u002F\u002F bytes are still available to verify the signature.\n    const raw = await action.request.text();\n    const provided = action.request.headers.get(\"x-signature\") ?? \"\";\n    const expected = createHmac(\"sha256\", process.env[\"WEBHOOK_SECRET\"] ?? \"\").update(raw).digest(\"hex\");\n\n    return { ok: provided === expected };\n  }\n}\n","src\u002Fwebhooks\u002Fwebhook.controller.ts",[503,1506,1507,1537,1557,1579,1583,1588,1626,1630,1657,1720,1725,1730,1757,1800,1879,1883,1904,1910],{"__ignoreMap":545},[549,1508,1509,1511,1514,1516,1519,1521,1524,1526,1528,1530,1533,1535],{"class":551,"line":552},[549,1510,556],{"class":555},[549,1512,1513],{"class":555}," type",[549,1515,560],{"class":559},[549,1517,1518],{"class":563}," Controller",[549,1520,920],{"class":559},[549,1522,1523],{"class":563}," Post",[549,1525,567],{"class":559},[549,1527,570],{"class":555},[549,1529,573],{"class":559},[549,1531,1532],{"class":576},"@heximon\u002Fhttp",[549,1534,580],{"class":559},[549,1536,583],{"class":559},[549,1538,1539,1541,1543,1545,1547,1549,1551,1553,1555],{"class":551,"line":586},[549,1540,556],{"class":555},[549,1542,560],{"class":559},[549,1544,1197],{"class":563},[549,1546,567],{"class":559},[549,1548,570],{"class":555},[549,1550,573],{"class":559},[549,1552,1178],{"class":576},[549,1554,580],{"class":559},[549,1556,583],{"class":559},[549,1558,1559,1561,1563,1566,1568,1570,1572,1575,1577],{"class":551,"line":609},[549,1560,556],{"class":555},[549,1562,560],{"class":559},[549,1564,1565],{"class":563}," createHmac",[549,1567,567],{"class":559},[549,1569,570],{"class":555},[549,1571,573],{"class":559},[549,1573,1574],{"class":576},"node:crypto",[549,1576,580],{"class":559},[549,1578,583],{"class":559},[549,1580,1581],{"class":551,"line":616},[549,1582,613],{"emptyLinePlaceholder":612},[549,1584,1585],{"class":551,"line":623},[549,1586,1587],{"class":619},"\u002F\u002F `.rawBody()` opts the route out of dispatch-time body buffering (it works on a `Contract` route too).\n",[549,1589,1590,1592,1594,1597,1599,1601,1603,1606,1608,1610,1613,1615,1617,1619,1622,1624],{"class":551,"line":649},[549,1591,626],{"class":555},[549,1593,1245],{"class":629},[549,1595,1596],{"class":563}," webhookRoute ",[549,1598,1251],{"class":559},[549,1600,1197],{"class":563},[549,1602,661],{"class":559},[549,1604,1605],{"class":640},"post",[549,1607,643],{"class":563},[549,1609,580],{"class":559},[549,1611,1612],{"class":576},"\u002Fwebhook",[549,1614,580],{"class":559},[549,1616,681],{"class":563},[549,1618,661],{"class":559},[549,1620,1621],{"class":640},"rawBody",[549,1623,667],{"class":563},[549,1625,583],{"class":559},[549,1627,1628],{"class":551,"line":687},[549,1629,613],{"emptyLinePlaceholder":612},[549,1631,1632,1634,1636,1639,1642,1644,1646,1648,1650,1652,1654],{"class":551,"line":715},[549,1633,626],{"class":555},[549,1635,630],{"class":629},[549,1637,1638],{"class":633}," WebhookController",[549,1640,1641],{"class":629}," implements",[549,1643,1518],{"class":633},[549,1645,914],{"class":559},[549,1647,580],{"class":559},[549,1649,1612],{"class":576},[549,1651,580],{"class":559},[549,1653,929],{"class":559},[549,1655,1656],{"class":559}," {\n",[549,1658,1659,1662,1665,1668,1670,1674,1676,1678,1680,1682,1685,1687,1689,1691,1694,1696,1699,1702,1705,1708,1710,1712,1715,1718],{"class":551,"line":748},[549,1660,1661],{"class":629},"  public",[549,1663,1664],{"class":629}," async",[549,1666,1667],{"class":652}," receive",[549,1669,643],{"class":559},[549,1671,1673],{"class":1672},"sHdIc","action",[549,1675,656],{"class":559},[549,1677,1523],{"class":633},[549,1679,914],{"class":559},[549,1681,580],{"class":559},[549,1683,1684],{"class":576},"\u002F",[549,1686,580],{"class":559},[549,1688,920],{"class":559},[549,1690,560],{"class":559},[549,1692,1693],{"class":652}," rawBody",[549,1695,656],{"class":559},[549,1697,1698],{"class":633}," true",[549,1700,1701],{"class":559}," }>):",[549,1703,1704],{"class":633}," Promise",[549,1706,1707],{"class":559},"\u003C{",[549,1709,1452],{"class":652},[549,1711,656],{"class":559},[549,1713,1714],{"class":633}," boolean",[549,1716,1717],{"class":559}," }>",[549,1719,1656],{"class":559},[549,1721,1722],{"class":551,"line":760},[549,1723,1724],{"class":619},"    \u002F\u002F action.request.bodyUsed is false — the dispatcher did not consume the stream, so the raw\n",[549,1726,1727],{"class":551,"line":765},[549,1728,1729],{"class":619},"    \u002F\u002F bytes are still available to verify the signature.\n",[549,1731,1732,1735,1738,1740,1742,1744,1746,1748,1750,1753,1755],{"class":551,"line":771},[549,1733,1734],{"class":629},"    const",[549,1736,1737],{"class":563}," raw",[549,1739,944],{"class":559},[549,1741,947],{"class":555},[549,1743,950],{"class":563},[549,1745,661],{"class":559},[549,1747,955],{"class":563},[549,1749,661],{"class":559},[549,1751,1752],{"class":640},"text",[549,1754,667],{"class":652},[549,1756,583],{"class":559},[549,1758,1759,1761,1764,1766,1768,1770,1772,1774,1777,1779,1781,1783,1785,1788,1790,1792,1795,1798],{"class":551,"line":789},[549,1760,1734],{"class":629},[549,1762,1763],{"class":563}," provided",[549,1765,944],{"class":559},[549,1767,950],{"class":563},[549,1769,661],{"class":559},[549,1771,955],{"class":563},[549,1773,661],{"class":559},[549,1775,1776],{"class":563},"headers",[549,1778,661],{"class":559},[549,1780,1258],{"class":640},[549,1782,643],{"class":652},[549,1784,580],{"class":559},[549,1786,1787],{"class":576},"x-signature",[549,1789,580],{"class":559},[549,1791,754],{"class":652},[549,1793,1794],{"class":559},"??",[549,1796,1797],{"class":559}," \"\"",[549,1799,583],{"class":559},[549,1801,1802,1804,1807,1809,1811,1813,1815,1818,1820,1822,1825,1827,1830,1833,1835,1838,1840,1843,1845,1847,1849,1851,1854,1856,1859,1861,1863,1866,1868,1870,1873,1875,1877],{"class":551,"line":806},[549,1803,1734],{"class":629},[549,1805,1806],{"class":563}," expected",[549,1808,944],{"class":559},[549,1810,1565],{"class":640},[549,1812,643],{"class":652},[549,1814,580],{"class":559},[549,1816,1817],{"class":576},"sha256",[549,1819,580],{"class":559},[549,1821,920],{"class":559},[549,1823,1824],{"class":563}," process",[549,1826,661],{"class":559},[549,1828,1829],{"class":563},"env",[549,1831,1832],{"class":652},"[",[549,1834,580],{"class":559},[549,1836,1837],{"class":576},"WEBHOOK_SECRET",[549,1839,580],{"class":559},[549,1841,1842],{"class":652},"] ",[549,1844,1794],{"class":559},[549,1846,1797],{"class":559},[549,1848,681],{"class":652},[549,1850,661],{"class":559},[549,1852,1853],{"class":640},"update",[549,1855,643],{"class":652},[549,1857,1858],{"class":563},"raw",[549,1860,681],{"class":652},[549,1862,661],{"class":559},[549,1864,1865],{"class":640},"digest",[549,1867,643],{"class":652},[549,1869,580],{"class":559},[549,1871,1872],{"class":576},"hex",[549,1874,580],{"class":559},[549,1876,681],{"class":652},[549,1878,583],{"class":559},[549,1880,1881],{"class":551,"line":823},[549,1882,613],{"emptyLinePlaceholder":612},[549,1884,1885,1888,1890,1892,1894,1896,1899,1901],{"class":551,"line":846},[549,1886,1887],{"class":555},"    return",[549,1889,560],{"class":559},[549,1891,1452],{"class":652},[549,1893,656],{"class":559},[549,1895,1763],{"class":563},[549,1897,1898],{"class":559}," ===",[549,1900,1806],{"class":563},[549,1902,1903],{"class":559}," };\n",[549,1905,1907],{"class":551,"line":1906},17,[549,1908,1909],{"class":559},"  }\n",[549,1911,1913],{"class":551,"line":1912},18,[549,1914,1014],{"class":559},[499,1916,1917,1919,1920,1923],{},[503,1918,1499],{}," is type-gated off GET\u002FHEAD (those carry no request body). The ",[503,1921,1922],{},"readValidated*"," escape-hatch\naccessors still work on raw-body routes — they fall through to the live stream on first call — but there\nis no automatic 400 when the accessor is never called, because the dispatcher did not run the schema.",[523,1925,1927],{"id":1926},"reference-a-dto-from-the-route","Reference a DTO from the route",[499,1929,1930,1931,1933,1934,1936,1937,1940],{},"A DTO is a Standard-Schema value, so it drops straight into a contract route's ",[503,1932,1004],{},", ",[503,1935,1430],{},", or\n",[503,1938,1939],{},"pathParams",". The route is the single declaration the controller binds to, the OpenAPI document reads,\nand the validated accessors inherit — author the shape once, reference it everywhere.",[539,1942,1945],{"className":541,"code":1943,"filename":1944,"language":544,"meta":545,"style":545},"import { Contract, Route } from \"@heximon\u002Fcontract\";\nimport { SchemaArray } from \"@heximon\u002Fschema\";\nimport { CreateProduct, Product } from \".\u002Fproduct.schema\";\n\nclass ProductList extends SchemaArray(Product) {} \u002F\u002F a validating class over Product[]\n\nexport class ProductsApi extends Contract({\n  prefix: \"\u002Fapi\u002Fproducts\",\n  routes: {\n    list: Route.get(\"\u002F\").responses({ 200: ProductList }),\n    create: Route.post(\"\u002F\").body(CreateProduct).responses({ 201: Product }),\n  },\n}) {}\n","src\u002Fproducts\u002Fproduct.api.ts",[503,1946,1947,1972,1993,2018,2022,2043,2047,2064,2080,2089,2133,2185,2190],{"__ignoreMap":545},[549,1948,1949,1951,1953,1956,1958,1960,1962,1964,1966,1968,1970],{"class":551,"line":552},[549,1950,556],{"class":555},[549,1952,560],{"class":559},[549,1954,1955],{"class":563}," Contract",[549,1957,920],{"class":559},[549,1959,1197],{"class":563},[549,1961,567],{"class":559},[549,1963,570],{"class":555},[549,1965,573],{"class":559},[549,1967,1178],{"class":576},[549,1969,580],{"class":559},[549,1971,583],{"class":559},[549,1973,1974,1976,1978,1981,1983,1985,1987,1989,1991],{"class":551,"line":586},[549,1975,556],{"class":555},[549,1977,560],{"class":559},[549,1979,1980],{"class":563}," SchemaArray",[549,1982,567],{"class":559},[549,1984,570],{"class":555},[549,1986,573],{"class":559},[549,1988,577],{"class":576},[549,1990,580],{"class":559},[549,1992,583],{"class":559},[549,1994,1995,1997,1999,2001,2003,2005,2007,2009,2011,2014,2016],{"class":551,"line":609},[549,1996,556],{"class":555},[549,1998,560],{"class":559},[549,2000,634],{"class":563},[549,2002,920],{"class":559},[549,2004,778],{"class":563},[549,2006,567],{"class":559},[549,2008,570],{"class":555},[549,2010,573],{"class":559},[549,2012,2013],{"class":576},".\u002Fproduct.schema",[549,2015,580],{"class":559},[549,2017,583],{"class":559},[549,2019,2020],{"class":551,"line":616},[549,2021,613],{"emptyLinePlaceholder":612},[549,2023,2024,2027,2030,2032,2034,2037,2040],{"class":551,"line":623},[549,2025,2026],{"class":629},"class",[549,2028,2029],{"class":633}," ProductList",[549,2031,637],{"class":629},[549,2033,1980],{"class":640},[549,2035,2036],{"class":563},"(Product) ",[549,2038,2039],{"class":559},"{}",[549,2041,2042],{"class":619}," \u002F\u002F a validating class over Product[]\n",[549,2044,2045],{"class":551,"line":649},[549,2046,613],{"emptyLinePlaceholder":612},[549,2048,2049,2051,2053,2056,2058,2060,2062],{"class":551,"line":687},[549,2050,626],{"class":555},[549,2052,630],{"class":629},[549,2054,2055],{"class":633}," ProductsApi",[549,2057,637],{"class":629},[549,2059,1955],{"class":640},[549,2061,643],{"class":563},[549,2063,646],{"class":559},[549,2065,2066,2069,2071,2073,2076,2078],{"class":551,"line":715},[549,2067,2068],{"class":652},"  prefix",[549,2070,656],{"class":559},[549,2072,573],{"class":559},[549,2074,2075],{"class":576},"\u002Fapi\u002Fproducts",[549,2077,580],{"class":559},[549,2079,684],{"class":559},[549,2081,2082,2085,2087],{"class":551,"line":748},[549,2083,2084],{"class":652},"  routes",[549,2086,656],{"class":559},[549,2088,1656],{"class":559},[549,2090,2091,2094,2096,2098,2100,2102,2104,2106,2108,2110,2112,2114,2116,2118,2120,2122,2124,2127,2129,2131],{"class":551,"line":760},[549,2092,2093],{"class":652},"    list",[549,2095,656],{"class":559},[549,2097,1197],{"class":563},[549,2099,661],{"class":559},[549,2101,1258],{"class":640},[549,2103,643],{"class":563},[549,2105,580],{"class":559},[549,2107,1684],{"class":576},[549,2109,580],{"class":559},[549,2111,681],{"class":563},[549,2113,661],{"class":559},[549,2115,1430],{"class":640},[549,2117,643],{"class":563},[549,2119,1368],{"class":559},[549,2121,1437],{"class":677},[549,2123,656],{"class":559},[549,2125,2126],{"class":563}," ProductList ",[549,2128,751],{"class":559},[549,2130,681],{"class":563},[549,2132,684],{"class":559},[549,2134,2135,2138,2140,2142,2144,2146,2148,2150,2152,2154,2156,2158,2160,2163,2165,2167,2169,2171,2174,2176,2179,2181,2183],{"class":551,"line":765},[549,2136,2137],{"class":652},"    create",[549,2139,656],{"class":559},[549,2141,1197],{"class":563},[549,2143,661],{"class":559},[549,2145,1605],{"class":640},[549,2147,643],{"class":563},[549,2149,580],{"class":559},[549,2151,1684],{"class":576},[549,2153,580],{"class":559},[549,2155,681],{"class":563},[549,2157,661],{"class":559},[549,2159,1004],{"class":640},[549,2161,2162],{"class":563},"(CreateProduct)",[549,2164,661],{"class":559},[549,2166,1430],{"class":640},[549,2168,643],{"class":563},[549,2170,1368],{"class":559},[549,2172,2173],{"class":677}," 201",[549,2175,656],{"class":559},[549,2177,2178],{"class":563}," Product ",[549,2180,751],{"class":559},[549,2182,681],{"class":563},[549,2184,684],{"class":559},[549,2186,2187],{"class":551,"line":771},[549,2188,2189],{"class":559},"  },\n",[549,2191,2192,2194,2196],{"class":551,"line":789},[549,2193,751],{"class":559},[549,2195,754],{"class":563},[549,2197,757],{"class":559},[499,2199,2200,2203,2204,2206],{},[503,2201,2202],{},"SchemaArray(element)"," wraps a single element schema into a validating class for an array of it,\nre-reporting each element's errors under its index — the array veneer to ",[503,2205,512],{},"'s object veneer.",[499,2208,2209,2210,2213],{},"If you author routes inline instead of through a contract, the same DTO goes in the action type as the bare\nclass reference — ",[503,2211,2212],{},"typeof CreateProduct"," resolves identically, if you'd rather spell it as a type — and the\naccessor still takes no argument:",[539,2215,2217],{"className":541,"code":2216,"language":544,"meta":545,"style":545},"public async create(action: Post\u003C\"\u002F\", { body: CreateProduct }>): Promise\u003CProduct> {\n  const body = await action.request.readValidatedBody(); \u002F\u002F inherits CreateProduct\n  return this.products.create(body);\n}\n",[503,2218,2219,2265,2290,2295],{"__ignoreMap":545},[549,2220,2221,2223,2225,2228,2230,2232,2234,2236,2238,2240,2242,2244,2247,2250,2253,2256,2258,2261,2263],{"class":551,"line":552},[549,2222,905],{"class":563},[549,2224,908],{"class":640},[549,2226,2227],{"class":563},"(action: Post",[549,2229,914],{"class":559},[549,2231,580],{"class":559},[549,2233,1684],{"class":576},[549,2235,580],{"class":559},[549,2237,920],{"class":559},[549,2239,560],{"class":559},[549,2241,941],{"class":652},[549,2243,656],{"class":559},[549,2245,2246],{"class":563}," CreateProduct ",[549,2248,2249],{"class":559},"}>",[549,2251,2252],{"class":563},"): ",[549,2254,2255],{"class":633},"Promise",[549,2257,914],{"class":559},[549,2259,2260],{"class":563},"Product",[549,2262,929],{"class":559},[549,2264,1656],{"class":559},[549,2266,2267,2270,2272,2274,2276,2278,2280,2282,2284,2287],{"class":551,"line":586},[549,2268,2269],{"class":563},"  const body ",[549,2271,1251],{"class":559},[549,2273,947],{"class":555},[549,2275,950],{"class":563},[549,2277,661],{"class":559},[549,2279,955],{"class":563},[549,2281,661],{"class":559},[549,2283,960],{"class":640},[549,2285,2286],{"class":563},"(); ",[549,2288,2289],{"class":619},"\u002F\u002F inherits CreateProduct\n",[549,2291,2292],{"class":551,"line":609},[549,2293,2294],{"class":563},"  return this.products.create(body);\n",[549,2296,2297],{"class":551,"line":616},[549,2298,1014],{"class":559},[523,2300,2302],{"id":2301},"upload-files-multipart","Upload files (multipart)",[499,2304,2305,2306,2309,2310,2313,2314,2317,2318,1684,2321,2324,2325,2328,2329,2332,2333,2336,2337,2339],{},"A route can also declare ",[534,2307,2308],{},"file fields"," with ",[503,2311,2312],{},".files({...})",", which marks it ",[503,2315,2316],{},"multipart\u002Fform-data"," and\nvalidates each field's ",[503,2319,2320],{},"maxBytes",[503,2322,2323],{},"accept"," alongside the rest of the ",[503,2326,2327],{},".body(...)"," schema. See\n",[515,2330,2331],{"href":433},"Upload a file"," for the full route-to-",[503,2334,2335],{},"BlobStore"," walkthrough and\n",[515,2338,170],{"href":171}," for the streaming port and driver swap behind it.",[523,2341,2343],{"id":2342},"construct-a-dto-directly","Construct a DTO directly",[499,2345,2346,2347,2350,2351,2354],{},"Because a DTO is a class, ",[503,2348,2349],{},"new CreateProduct({ ... })"," validates a value synchronously — handy in a\nservice, a test, or a seed script where there's no request. You get a plain object of the inferred type\nback, or a ",[503,2352,2353],{},"SchemaError"," carrying the failing issues.",[539,2356,2358],{"className":541,"code":2357,"language":544,"meta":545,"style":545},"const product = new CreateProduct({ sku: \"abc\", name: \"Widget\", priceInCents: 1500 });\n\u002F\u002F product: { sku: string; name: string; priceInCents: number } — a plain value\n",[503,2359,2360,2421],{"__ignoreMap":545},[549,2361,2362,2365,2368,2370,2373,2375,2377,2379,2382,2384,2386,2389,2391,2393,2396,2398,2400,2403,2405,2407,2410,2412,2415,2417,2419],{"class":551,"line":552},[549,2363,2364],{"class":629},"const",[549,2366,2367],{"class":563}," product ",[549,2369,1251],{"class":559},[549,2371,2372],{"class":559}," new",[549,2374,634],{"class":640},[549,2376,643],{"class":563},[549,2378,1368],{"class":559},[549,2380,2381],{"class":652}," sku",[549,2383,656],{"class":559},[549,2385,573],{"class":559},[549,2387,2388],{"class":576},"abc",[549,2390,580],{"class":559},[549,2392,920],{"class":559},[549,2394,2395],{"class":652}," name",[549,2397,656],{"class":559},[549,2399,573],{"class":559},[549,2401,2402],{"class":576},"Widget",[549,2404,580],{"class":559},[549,2406,920],{"class":559},[549,2408,2409],{"class":652}," priceInCents",[549,2411,656],{"class":559},[549,2413,2414],{"class":677}," 1500",[549,2416,567],{"class":559},[549,2418,681],{"class":563},[549,2420,583],{"class":559},[549,2422,2423],{"class":551,"line":586},[549,2424,2425],{"class":619},"\u002F\u002F product: { sku: string; name: string; priceInCents: number } — a plain value\n",[499,2427,2428,2429,2431,2432,2435,2436,2439,2440,2443],{},"A field counts as ",[534,2430,1314],{}," only when its validator accepts ",[503,2433,2434],{},"undefined"," synchronously — a zod field\nending in ",[503,2437,2438],{},".optional()",". That single rule is what populates the ",[503,2441,2442],{},"required"," list in the JSON Schema the\nOpenAPI layer emits, so optionality is read off the validator, never declared twice.",[2445,2446,2448,2451,2452,2455,2456,2458,2459,2461,2462,2465,2466,2468,2469,2473,2474,2477],"callout",{"type":2447},"warning",[534,2449,2450],{},"Constructing a DTO is synchronous; validating a request is not."," ",[503,2453,2454],{},"new Dto(input)"," runs the field\nvalidators inline, so a validator that returns a ",[503,2457,2255],{}," can't be resolved in time — the constructor\nthrows a ",[503,2460,2353],{},": ",[503,2463,2464],{},"\"Schema validation must be synchronous when constructing a value.\"",". The\nrequest-boundary path (",[503,2467,960],{}," and friends) ",[2470,2471,2472],"em",{},"awaits"," each field, so an async validator works\nfine there. The fix: keep async refinements out of DTOs you ",[503,2475,2476],{},"new"," by hand, or only validate them at the\nboundary.",[523,2479,2481],{"id":2480},"derive-a-dto-instead-of-repeating-it","Derive a DTO instead of repeating it",[499,2483,2484,2485,2487,2488,1933,2491,684,2494,2497,2498,2501],{},"A create body, an update body, and a public projection are usually one shape minus or plus a field.\nRather than write three ",[503,2486,512],{},"s, derive them — every DTO class exposes ",[503,2489,2490],{},"partial",[503,2492,2493],{},"pick",[503,2495,2496],{},"omit",", and ",[503,2499,2500],{},"extend",", each rebuilding the field record and chainable.",[539,2503,2505],{"className":541,"code":2504,"language":544,"meta":545,"style":545},"class CreateUser extends SchemaObject({\n  email: z.email(),\n  name: z.string().min(1),\n  age: z.number().optional(),\n}) {}\n\nclass UpdateUser extends CreateUser.partial() {}          \u002F\u002F every field optional\nclass EmailOnly extends CreateUser.pick([\"email\"]) {}     \u002F\u002F keep only email\nclass PublicUser extends CreateUser.omit([\"email\"]) {}    \u002F\u002F drop email\nclass WithRole extends CreateUser.extend({ role: z.string() }) {} \u002F\u002F add a field\n\nclass Patch extends CreateUser.partial().extend({ revision: z.number() }) {} \u002F\u002F chained\n",[503,2506,2507,2522,2540,2566,2589,2597,2601,2623,2655,2685,2726,2730],{"__ignoreMap":545},[549,2508,2509,2511,2514,2516,2518,2520],{"class":551,"line":552},[549,2510,2026],{"class":629},[549,2512,2513],{"class":633}," CreateUser",[549,2515,637],{"class":629},[549,2517,564],{"class":640},[549,2519,643],{"class":563},[549,2521,646],{"class":559},[549,2523,2524,2527,2529,2531,2533,2536,2538],{"class":551,"line":586},[549,2525,2526],{"class":652},"  email",[549,2528,656],{"class":559},[549,2530,593],{"class":563},[549,2532,661],{"class":559},[549,2534,2535],{"class":640},"email",[549,2537,667],{"class":563},[549,2539,684],{"class":559},[549,2541,2542,2544,2546,2548,2550,2552,2554,2556,2558,2560,2562,2564],{"class":551,"line":609},[549,2543,690],{"class":652},[549,2545,656],{"class":559},[549,2547,593],{"class":563},[549,2549,661],{"class":559},[549,2551,664],{"class":640},[549,2553,667],{"class":563},[549,2555,661],{"class":559},[549,2557,672],{"class":640},[549,2559,643],{"class":563},[549,2561,678],{"class":677},[549,2563,681],{"class":563},[549,2565,684],{"class":559},[549,2567,2568,2571,2573,2575,2577,2579,2581,2583,2585,2587],{"class":551,"line":616},[549,2569,2570],{"class":652},"  age",[549,2572,656],{"class":559},[549,2574,593],{"class":563},[549,2576,661],{"class":559},[549,2578,727],{"class":640},[549,2580,667],{"class":563},[549,2582,661],{"class":559},[549,2584,1314],{"class":640},[549,2586,667],{"class":563},[549,2588,684],{"class":559},[549,2590,2591,2593,2595],{"class":551,"line":623},[549,2592,751],{"class":559},[549,2594,754],{"class":563},[549,2596,757],{"class":559},[549,2598,2599],{"class":551,"line":649},[549,2600,613],{"emptyLinePlaceholder":612},[549,2602,2603,2605,2608,2610,2612,2614,2616,2618,2620],{"class":551,"line":687},[549,2604,2026],{"class":629},[549,2606,2607],{"class":633}," UpdateUser",[549,2609,637],{"class":629},[549,2611,2513],{"class":563},[549,2613,661],{"class":559},[549,2615,2490],{"class":640},[549,2617,1397],{"class":563},[549,2619,2039],{"class":559},[549,2621,2622],{"class":619},"          \u002F\u002F every field optional\n",[549,2624,2625,2627,2630,2632,2634,2636,2638,2641,2643,2645,2647,2650,2652],{"class":551,"line":715},[549,2626,2026],{"class":629},[549,2628,2629],{"class":633}," EmailOnly",[549,2631,637],{"class":629},[549,2633,2513],{"class":563},[549,2635,661],{"class":559},[549,2637,2493],{"class":640},[549,2639,2640],{"class":563},"([",[549,2642,580],{"class":559},[549,2644,2535],{"class":576},[549,2646,580],{"class":559},[549,2648,2649],{"class":563},"]) ",[549,2651,2039],{"class":559},[549,2653,2654],{"class":619},"     \u002F\u002F keep only email\n",[549,2656,2657,2659,2662,2664,2666,2668,2670,2672,2674,2676,2678,2680,2682],{"class":551,"line":748},[549,2658,2026],{"class":629},[549,2660,2661],{"class":633}," PublicUser",[549,2663,637],{"class":629},[549,2665,2513],{"class":563},[549,2667,661],{"class":559},[549,2669,2496],{"class":640},[549,2671,2640],{"class":563},[549,2673,580],{"class":559},[549,2675,2535],{"class":576},[549,2677,580],{"class":559},[549,2679,2649],{"class":563},[549,2681,2039],{"class":559},[549,2683,2684],{"class":619},"    \u002F\u002F drop email\n",[549,2686,2687,2689,2692,2694,2696,2698,2700,2702,2704,2707,2709,2711,2713,2715,2717,2719,2721,2723],{"class":551,"line":760},[549,2688,2026],{"class":629},[549,2690,2691],{"class":633}," WithRole",[549,2693,637],{"class":629},[549,2695,2513],{"class":563},[549,2697,661],{"class":559},[549,2699,2500],{"class":640},[549,2701,643],{"class":563},[549,2703,1368],{"class":559},[549,2705,2706],{"class":652}," role",[549,2708,656],{"class":559},[549,2710,593],{"class":563},[549,2712,661],{"class":559},[549,2714,664],{"class":640},[549,2716,1397],{"class":563},[549,2718,751],{"class":559},[549,2720,754],{"class":563},[549,2722,2039],{"class":559},[549,2724,2725],{"class":619}," \u002F\u002F add a field\n",[549,2727,2728],{"class":551,"line":765},[549,2729,613],{"emptyLinePlaceholder":612},[549,2731,2732,2734,2737,2739,2741,2743,2745,2747,2749,2751,2753,2755,2758,2760,2762,2764,2766,2768,2770,2772,2774],{"class":551,"line":771},[549,2733,2026],{"class":629},[549,2735,2736],{"class":633}," Patch",[549,2738,637],{"class":629},[549,2740,2513],{"class":563},[549,2742,661],{"class":559},[549,2744,2490],{"class":640},[549,2746,667],{"class":563},[549,2748,661],{"class":559},[549,2750,2500],{"class":640},[549,2752,643],{"class":563},[549,2754,1368],{"class":559},[549,2756,2757],{"class":652}," revision",[549,2759,656],{"class":559},[549,2761,593],{"class":563},[549,2763,661],{"class":559},[549,2765,727],{"class":640},[549,2767,1397],{"class":563},[549,2769,751],{"class":559},[549,2771,754],{"class":563},[549,2773,2039],{"class":559},[549,2775,2776],{"class":619}," \u002F\u002F chained\n",[499,2778,2779,2780,2783,2784,2787,2788,2791],{},"These operators rebuild the shape without ever inspecting a field's validator, so the derived class\nstays fully introspectable — the OpenAPI emitter sees its real fields, not an opaque blob. For an\nengine-specific refinement that ",[2470,2781,2782],{},"does"," touch the validator (a Zod ",[503,2785,2786],{},".refine",", a Valibot pipe), reach for\n",[503,2789,2790],{},"customize","; that result is deliberately opaque to extraction, so prefer the four derivations whenever\nyou want the derived shape to keep showing up in your generated spec.",[523,2793,2795],{"id":2794},"validated-requests-reviewed-responses","Validated requests, reviewed responses",[499,2797,2798,2799,2802,2803,2806],{},"Requests are validated and rejected; a handler's response is only ",[534,2800,2801],{},"reviewed",". In development,\n",[503,2804,2805],{},"reviewResponse"," logs a warning when a returned body doesn't match the route's declared response schema\nfor that status — a fast signal that your handler and your contract have drifted. It never throws and is\na no-op in production, because a response that's already on the wire is not something to fail a request\nover. That asymmetry is on purpose: you don't trust the client, but you do trust your own code enough to\nship it while you fix the warning.",[523,2808,2810],{"id":2809},"see-also","See also",[2812,2813,2814,2820,2828,2833,2847,2853,2858,2870,2880],"ul",{},[2815,2816,2817,2819],"li",{},[515,2818,2331],{"href":433}," — the shortest path from a multipart route to a stored\nblob, with the production swap to a filesystem\u002FS3\u002FR2 driver.",[2815,2821,2822,2824,2825,2827],{},[515,2823,170],{"href":171}," — the ",[503,2826,2335],{}," port a validated file field streams into,\nplus its memory\u002Ffilesystem\u002FR2\u002FS3 drivers.",[2815,2829,2830,2832],{},[515,2831,57],{"href":58}," — declare routes by action-parameter type and read the\nvalidated body inside a handler.",[2815,2834,2835,2837,2838,2840,2841,2843,2844,2846],{},[515,2836,67],{"href":68}," — reference these DTOs as a route's ",[503,2839,1004],{}," \u002F ",[503,2842,1430],{}," \u002F\n",[503,2845,1939],{}," in one shared, frontend-safe value.",[2815,2848,2849,2852],{},[515,2850,2851],{"href":73},"REST client"," — consume the same contract from the frontend, with opt-in\nresponse-body validation.",[2815,2854,2855,2857],{},[515,2856,199],{"href":200}," — each DTO's JSON Schema embeds into the generated spec at full fidelity.",[2815,2859,2860,2862,2863,2865,2866,2869],{},[515,2861,228],{"href":229}," — wrap a ",[503,2864,512],{}," in a ",[503,2867,2868],{},"ValueObject"," to validate a\ndomain value once at its edge.",[2815,2871,2872,2879],{},[515,2873,2876],{"href":2874,"rel":2875},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fgap\u002Fopenapi-mcp",[519],[534,2877,2878],{},"OpenAPI + MCP"," — a\ncontract of validated routes, a contract-mode controller reading validated input, and an e2e test that\nproves the generated spec matches the served API.",[2815,2881,2882,2889,2890,2892,2893,2896,2897,661],{},[515,2883,2886],{"href":2884,"rel":2885},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fgap\u002Fstorage-blob-adapters",[519],[534,2887,2888],{},"Storage & blob adapters","\n— a ",[503,2891,2316],{}," upload validated by ",[503,2894,2895],{},".files(...)"," and streamed into a swappable ",[503,2898,2335],{},[2900,2901,2902],"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 .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 .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 .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}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 .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}",{"title":545,"searchDepth":586,"depth":586,"links":2904},[2905,2906,2907,2908,2909,2910,2911,2912,2913,2914],{"id":525,"depth":586,"text":526},{"id":875,"depth":586,"text":876},{"id":1110,"depth":586,"text":1111},{"id":1492,"depth":586,"text":1493},{"id":1926,"depth":586,"text":1927},{"id":2301,"depth":586,"text":2302},{"id":2342,"depth":586,"text":2343},{"id":2480,"depth":586,"text":2481},{"id":2794,"depth":586,"text":2795},{"id":2809,"depth":586,"text":2810},"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.","md",null,{},{"icon":65},{"title":62,"description":2915},"m1gk0clOU6_yUXm9_YRcb0KkFp7AgrMMdLnLPkFb1rM",[2923,2925],{"title":57,"path":58,"stem":59,"description":2924,"icon":60,"children":-1},"Build the HTTP surface with Controllers — routes declared by the handler's action parameter type, the HttpAction dispatcher, middleware, error filters, and contract binding.",{"title":67,"path":68,"stem":69,"description":2926,"icon":70,"children":-1},"Describe an HTTP API once with the Contract class and Route builder, then bind the server, the REST client, and OpenAPI to the same route table — Route.get\u002Fpost, body, responses, pathParams, scopes, shared responses, contract-mode controllers.",1783300761903]