[{"data":1,"prerenderedAt":2140},["ShallowReactive",2],{"navigation_docs":3,"-docs-recipes-upload-a-file":493,"-docs-recipes-upload-a-file-surround":2135},[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":432,"body":495,"description":2128,"extension":2129,"links":2130,"meta":2131,"navigation":2132,"path":433,"seo":2133,"stem":434,"__hash__":2134},"docs\u002Fdocs\u002F09.recipes\u002F02.upload-a-file.md",{"type":496,"value":497,"toc":2120},"minimark",[498,520,525,558,858,1091,1095,1109,1601,1619,1623,1632,1764,1768,1829,1833,1852,2036,2067,2071,2116],[499,500,501,502,506,507,511,512,515,516,519],"p",{},"Accept a file over HTTP and put it somewhere durable, without coupling the controller to ",[503,504,505],"em",{},"where",". A route\ndeclares its file fields with ",[508,509,510],"code",{},".files({...})","; the server validates and hands you a typed ",[508,513,514],{},"File","; you\nstream it into the abstract ",[508,517,518],{},"BlobStore"," port, which a module binds to memory today and S3 or R2 later\nwith no other change.",[521,522,524],"h2",{"id":523},"_1-declare-the-upload-contract","1. Declare the upload contract",[499,526,527,529,530,533,534,537,538,541,542,545,546,549,550,553,554,557],{},[508,528,510],{}," marks a route ",[508,531,532],{},"multipart\u002Fform-data"," and records each field's constraints — ",[508,535,536],{},"maxBytes",",\n",[508,539,540],{},"accept"," (an exact type, a ",[508,543,544],{},"type\u002F*"," wildcard, or a ",[508,547,548],{},".extension","). A scalar field like ",[508,551,552],{},"caption"," is still\nvalidated by ",[508,555,556],{},".body(...)",":",[559,560,566],"pre",{"className":561,"code":562,"filename":563,"language":564,"meta":565,"style":565},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { Contract, Route } from \"@heximon\u002Fcontract\";\nimport { uploadFields, uploadResult } from \".\u002Fupload.schema\";\n\nexport class UploadsApi extends Contract({\n  prefix: \"\u002Fapi\u002Fuploads\",\n  routes: {\n    create: Route.post(\"\u002F\")\n      .files({ file: { maxBytes: 524_288, accept: [\"image\u002F*\", \"application\u002Fpdf\", \".txt\"] } })\n      .body(uploadFields)\n      .responses({ 201: uploadResult }),\n  },\n}) {}\n","src\u002Fuploads\u002Fupload.api.ts","ts","",[508,567,568,610,638,645,671,689,700,728,803,814,841,847],{"__ignoreMap":565},[569,570,573,577,581,585,588,591,594,597,600,604,607],"span",{"class":571,"line":572},"line",1,[569,574,576],{"class":575},"s7zQu","import",[569,578,580],{"class":579},"sMK4o"," {",[569,582,584],{"class":583},"sTEyZ"," Contract",[569,586,587],{"class":579},",",[569,589,590],{"class":583}," Route",[569,592,593],{"class":579}," }",[569,595,596],{"class":575}," from",[569,598,599],{"class":579}," \"",[569,601,603],{"class":602},"sfazB","@heximon\u002Fcontract",[569,605,606],{"class":579},"\"",[569,608,609],{"class":579},";\n",[569,611,613,615,617,620,622,625,627,629,631,634,636],{"class":571,"line":612},2,[569,614,576],{"class":575},[569,616,580],{"class":579},[569,618,619],{"class":583}," uploadFields",[569,621,587],{"class":579},[569,623,624],{"class":583}," uploadResult",[569,626,593],{"class":579},[569,628,596],{"class":575},[569,630,599],{"class":579},[569,632,633],{"class":602},".\u002Fupload.schema",[569,635,606],{"class":579},[569,637,609],{"class":579},[569,639,641],{"class":571,"line":640},3,[569,642,644],{"emptyLinePlaceholder":643},true,"\n",[569,646,648,651,655,659,662,665,668],{"class":571,"line":647},4,[569,649,650],{"class":575},"export",[569,652,654],{"class":653},"spNyl"," class",[569,656,658],{"class":657},"sBMFI"," UploadsApi",[569,660,661],{"class":653}," extends",[569,663,584],{"class":664},"s2Zo4",[569,666,667],{"class":583},"(",[569,669,670],{"class":579},"{\n",[569,672,674,678,680,682,685,687],{"class":571,"line":673},5,[569,675,677],{"class":676},"swJcz","  prefix",[569,679,557],{"class":579},[569,681,599],{"class":579},[569,683,684],{"class":602},"\u002Fapi\u002Fuploads",[569,686,606],{"class":579},[569,688,537],{"class":579},[569,690,692,695,697],{"class":571,"line":691},6,[569,693,694],{"class":676},"  routes",[569,696,557],{"class":579},[569,698,699],{"class":579}," {\n",[569,701,703,706,708,710,713,716,718,720,723,725],{"class":571,"line":702},7,[569,704,705],{"class":676},"    create",[569,707,557],{"class":579},[569,709,590],{"class":583},[569,711,712],{"class":579},".",[569,714,715],{"class":664},"post",[569,717,667],{"class":583},[569,719,606],{"class":579},[569,721,722],{"class":602},"\u002F",[569,724,606],{"class":579},[569,726,727],{"class":583},")\n",[569,729,731,734,737,739,742,745,747,749,752,754,758,760,763,765,768,770,773,775,777,779,782,784,786,788,791,793,796,799,801],{"class":571,"line":730},8,[569,732,733],{"class":579},"      .",[569,735,736],{"class":664},"files",[569,738,667],{"class":583},[569,740,741],{"class":579},"{",[569,743,744],{"class":676}," file",[569,746,557],{"class":579},[569,748,580],{"class":579},[569,750,751],{"class":676}," maxBytes",[569,753,557],{"class":579},[569,755,757],{"class":756},"sbssI"," 524_288",[569,759,587],{"class":579},[569,761,762],{"class":676}," accept",[569,764,557],{"class":579},[569,766,767],{"class":583}," [",[569,769,606],{"class":579},[569,771,772],{"class":602},"image\u002F*",[569,774,606],{"class":579},[569,776,587],{"class":579},[569,778,599],{"class":579},[569,780,781],{"class":602},"application\u002Fpdf",[569,783,606],{"class":579},[569,785,587],{"class":579},[569,787,599],{"class":579},[569,789,790],{"class":602},".txt",[569,792,606],{"class":579},[569,794,795],{"class":583},"] ",[569,797,798],{"class":579},"}",[569,800,593],{"class":579},[569,802,727],{"class":583},[569,804,806,808,811],{"class":571,"line":805},9,[569,807,733],{"class":579},[569,809,810],{"class":664},"body",[569,812,813],{"class":583},"(uploadFields)\n",[569,815,817,819,822,824,826,829,831,834,836,839],{"class":571,"line":816},10,[569,818,733],{"class":579},[569,820,821],{"class":664},"responses",[569,823,667],{"class":583},[569,825,741],{"class":579},[569,827,828],{"class":756}," 201",[569,830,557],{"class":579},[569,832,833],{"class":583}," uploadResult ",[569,835,798],{"class":579},[569,837,838],{"class":583},")",[569,840,537],{"class":579},[569,842,844],{"class":571,"line":843},11,[569,845,846],{"class":579},"  },\n",[569,848,850,852,855],{"class":571,"line":849},12,[569,851,798],{"class":579},[569,853,854],{"class":583},") ",[569,856,857],{"class":579},"{}\n",[559,859,862],{"className":561,"code":860,"filename":861,"language":564,"meta":565,"style":565},"import { z } from \"zod\";\n\nexport const uploadFields = z.object({\n  caption: z.string().max(280).optional(),\n});\n\nexport const uploadResult = z.object({\n  key: z.string(),\n  filename: z.string(),\n  contentType: z.string(),\n  size: z.number().int(),\n  caption: z.string().optional(),\n});\n","src\u002Fuploads\u002Fupload.schema.ts",[508,863,864,886,890,914,952,960,964,984,1001,1018,1035,1060,1082],{"__ignoreMap":565},[569,865,866,868,870,873,875,877,879,882,884],{"class":571,"line":572},[569,867,576],{"class":575},[569,869,580],{"class":579},[569,871,872],{"class":583}," z",[569,874,593],{"class":579},[569,876,596],{"class":575},[569,878,599],{"class":579},[569,880,881],{"class":602},"zod",[569,883,606],{"class":579},[569,885,609],{"class":579},[569,887,888],{"class":571,"line":612},[569,889,644],{"emptyLinePlaceholder":643},[569,891,892,894,897,900,903,905,907,910,912],{"class":571,"line":640},[569,893,650],{"class":575},[569,895,896],{"class":653}," const",[569,898,899],{"class":583}," uploadFields ",[569,901,902],{"class":579},"=",[569,904,872],{"class":583},[569,906,712],{"class":579},[569,908,909],{"class":664},"object",[569,911,667],{"class":583},[569,913,670],{"class":579},[569,915,916,919,921,923,925,928,931,933,936,938,941,943,945,948,950],{"class":571,"line":647},[569,917,918],{"class":676},"  caption",[569,920,557],{"class":579},[569,922,872],{"class":583},[569,924,712],{"class":579},[569,926,927],{"class":664},"string",[569,929,930],{"class":583},"()",[569,932,712],{"class":579},[569,934,935],{"class":664},"max",[569,937,667],{"class":583},[569,939,940],{"class":756},"280",[569,942,838],{"class":583},[569,944,712],{"class":579},[569,946,947],{"class":664},"optional",[569,949,930],{"class":583},[569,951,537],{"class":579},[569,953,954,956,958],{"class":571,"line":673},[569,955,798],{"class":579},[569,957,838],{"class":583},[569,959,609],{"class":579},[569,961,962],{"class":571,"line":691},[569,963,644],{"emptyLinePlaceholder":643},[569,965,966,968,970,972,974,976,978,980,982],{"class":571,"line":702},[569,967,650],{"class":575},[569,969,896],{"class":653},[569,971,833],{"class":583},[569,973,902],{"class":579},[569,975,872],{"class":583},[569,977,712],{"class":579},[569,979,909],{"class":664},[569,981,667],{"class":583},[569,983,670],{"class":579},[569,985,986,989,991,993,995,997,999],{"class":571,"line":730},[569,987,988],{"class":676},"  key",[569,990,557],{"class":579},[569,992,872],{"class":583},[569,994,712],{"class":579},[569,996,927],{"class":664},[569,998,930],{"class":583},[569,1000,537],{"class":579},[569,1002,1003,1006,1008,1010,1012,1014,1016],{"class":571,"line":805},[569,1004,1005],{"class":676},"  filename",[569,1007,557],{"class":579},[569,1009,872],{"class":583},[569,1011,712],{"class":579},[569,1013,927],{"class":664},[569,1015,930],{"class":583},[569,1017,537],{"class":579},[569,1019,1020,1023,1025,1027,1029,1031,1033],{"class":571,"line":816},[569,1021,1022],{"class":676},"  contentType",[569,1024,557],{"class":579},[569,1026,872],{"class":583},[569,1028,712],{"class":579},[569,1030,927],{"class":664},[569,1032,930],{"class":583},[569,1034,537],{"class":579},[569,1036,1037,1040,1042,1044,1046,1049,1051,1053,1056,1058],{"class":571,"line":843},[569,1038,1039],{"class":676},"  size",[569,1041,557],{"class":579},[569,1043,872],{"class":583},[569,1045,712],{"class":579},[569,1047,1048],{"class":664},"number",[569,1050,930],{"class":583},[569,1052,712],{"class":579},[569,1054,1055],{"class":664},"int",[569,1057,930],{"class":583},[569,1059,537],{"class":579},[569,1061,1062,1064,1066,1068,1070,1072,1074,1076,1078,1080],{"class":571,"line":849},[569,1063,918],{"class":676},[569,1065,557],{"class":579},[569,1067,872],{"class":583},[569,1069,712],{"class":579},[569,1071,927],{"class":664},[569,1073,930],{"class":583},[569,1075,712],{"class":579},[569,1077,947],{"class":664},[569,1079,930],{"class":583},[569,1081,537],{"class":579},[569,1083,1085,1087,1089],{"class":571,"line":1084},13,[569,1086,798],{"class":579},[569,1088,838],{"class":583},[569,1090,609],{"class":579},[521,1092,1094],{"id":1093},"_2-stream-the-file-into-a-blobstore","2. Stream the file into a BlobStore",[499,1096,1097,1098,1100,1101,1104,1105,1108],{},"Each declared field arrives as a web ",[508,1099,514],{},", so it pipes straight into ",[508,1102,1103],{},"BlobStore.put()"," via\n",[508,1106,1107],{},"file.stream()"," — the whole body is never buffered in your handler:",[559,1110,1113],{"className":561,"code":1111,"filename":1112,"language":564,"meta":565,"style":565},"import type { BlobStore } from \"@heximon\u002Fblob\";\nimport type { Action, Controller } from \"@heximon\u002Fhttp\";\nimport type { UploadsApi } from \".\u002Fupload.api\";\n\nexport class UploadsController implements Controller\u003CUploadsApi> {\n  public constructor(private readonly blobs: BlobStore) {}\n\n  public async create(action: Action\u003CUploadsApi, \"create\">) {\n    const { fields, files } = await action.request.readValidatedFormData();\n    const file = files.file; \u002F\u002F typed `File`\n    const key = crypto.randomUUID();\n\n    await this.blobs.put(key, file.stream(), {\n      contentType: file.type,\n      size: file.size,\n      custom: { filename: file.name },\n    });\n\n    return action.respond(201, {\n      key,\n      filename: file.name,\n      contentType: file.type,\n      size: file.size,\n      caption: fields.caption,\n    });\n  }\n}\n","src\u002Fuploads\u002Fuploads.controller.ts",[508,1114,1115,1140,1169,1192,1196,1221,1250,1254,1291,1331,1353,1374,1378,1414,1431,1448,1473,1483,1488,1510,1518,1534,1549,1564,1580,1589,1595],{"__ignoreMap":565},[569,1116,1117,1119,1122,1124,1127,1129,1131,1133,1136,1138],{"class":571,"line":572},[569,1118,576],{"class":575},[569,1120,1121],{"class":575}," type",[569,1123,580],{"class":579},[569,1125,1126],{"class":583}," BlobStore",[569,1128,593],{"class":579},[569,1130,596],{"class":575},[569,1132,599],{"class":579},[569,1134,1135],{"class":602},"@heximon\u002Fblob",[569,1137,606],{"class":579},[569,1139,609],{"class":579},[569,1141,1142,1144,1146,1148,1151,1153,1156,1158,1160,1162,1165,1167],{"class":571,"line":612},[569,1143,576],{"class":575},[569,1145,1121],{"class":575},[569,1147,580],{"class":579},[569,1149,1150],{"class":583}," Action",[569,1152,587],{"class":579},[569,1154,1155],{"class":583}," Controller",[569,1157,593],{"class":579},[569,1159,596],{"class":575},[569,1161,599],{"class":579},[569,1163,1164],{"class":602},"@heximon\u002Fhttp",[569,1166,606],{"class":579},[569,1168,609],{"class":579},[569,1170,1171,1173,1175,1177,1179,1181,1183,1185,1188,1190],{"class":571,"line":640},[569,1172,576],{"class":575},[569,1174,1121],{"class":575},[569,1176,580],{"class":579},[569,1178,658],{"class":583},[569,1180,593],{"class":579},[569,1182,596],{"class":575},[569,1184,599],{"class":579},[569,1186,1187],{"class":602},".\u002Fupload.api",[569,1189,606],{"class":579},[569,1191,609],{"class":579},[569,1193,1194],{"class":571,"line":647},[569,1195,644],{"emptyLinePlaceholder":643},[569,1197,1198,1200,1202,1205,1208,1210,1213,1216,1219],{"class":571,"line":673},[569,1199,650],{"class":575},[569,1201,654],{"class":653},[569,1203,1204],{"class":657}," UploadsController",[569,1206,1207],{"class":653}," implements",[569,1209,1155],{"class":657},[569,1211,1212],{"class":579},"\u003C",[569,1214,1215],{"class":657},"UploadsApi",[569,1217,1218],{"class":579},">",[569,1220,699],{"class":579},[569,1222,1223,1226,1229,1231,1234,1237,1241,1243,1245,1247],{"class":571,"line":691},[569,1224,1225],{"class":653},"  public",[569,1227,1228],{"class":653}," constructor",[569,1230,667],{"class":579},[569,1232,1233],{"class":653},"private",[569,1235,1236],{"class":653}," readonly",[569,1238,1240],{"class":1239},"sHdIc"," blobs",[569,1242,557],{"class":579},[569,1244,1126],{"class":657},[569,1246,838],{"class":579},[569,1248,1249],{"class":579}," {}\n",[569,1251,1252],{"class":571,"line":702},[569,1253,644],{"emptyLinePlaceholder":643},[569,1255,1256,1258,1261,1264,1266,1269,1271,1273,1275,1277,1279,1281,1284,1286,1289],{"class":571,"line":730},[569,1257,1225],{"class":653},[569,1259,1260],{"class":653}," async",[569,1262,1263],{"class":676}," create",[569,1265,667],{"class":579},[569,1267,1268],{"class":1239},"action",[569,1270,557],{"class":579},[569,1272,1150],{"class":657},[569,1274,1212],{"class":579},[569,1276,1215],{"class":657},[569,1278,587],{"class":579},[569,1280,599],{"class":579},[569,1282,1283],{"class":602},"create",[569,1285,606],{"class":579},[569,1287,1288],{"class":579},">)",[569,1290,699],{"class":579},[569,1292,1293,1296,1298,1301,1303,1306,1308,1311,1314,1317,1319,1322,1324,1327,1329],{"class":571,"line":805},[569,1294,1295],{"class":653},"    const",[569,1297,580],{"class":579},[569,1299,1300],{"class":583}," fields",[569,1302,587],{"class":579},[569,1304,1305],{"class":583}," files",[569,1307,593],{"class":579},[569,1309,1310],{"class":579}," =",[569,1312,1313],{"class":575}," await",[569,1315,1316],{"class":583}," action",[569,1318,712],{"class":579},[569,1320,1321],{"class":583},"request",[569,1323,712],{"class":579},[569,1325,1326],{"class":664},"readValidatedFormData",[569,1328,930],{"class":676},[569,1330,609],{"class":579},[569,1332,1333,1335,1337,1339,1341,1343,1346,1349],{"class":571,"line":816},[569,1334,1295],{"class":653},[569,1336,744],{"class":583},[569,1338,1310],{"class":579},[569,1340,1305],{"class":583},[569,1342,712],{"class":579},[569,1344,1345],{"class":583},"file",[569,1347,1348],{"class":579},";",[569,1350,1352],{"class":1351},"sHwdD"," \u002F\u002F typed `File`\n",[569,1354,1355,1357,1360,1362,1365,1367,1370,1372],{"class":571,"line":843},[569,1356,1295],{"class":653},[569,1358,1359],{"class":583}," key",[569,1361,1310],{"class":579},[569,1363,1364],{"class":583}," crypto",[569,1366,712],{"class":579},[569,1368,1369],{"class":664},"randomUUID",[569,1371,930],{"class":676},[569,1373,609],{"class":579},[569,1375,1376],{"class":571,"line":849},[569,1377,644],{"emptyLinePlaceholder":643},[569,1379,1380,1383,1386,1389,1391,1394,1396,1399,1401,1403,1405,1408,1410,1412],{"class":571,"line":1084},[569,1381,1382],{"class":575},"    await",[569,1384,1385],{"class":579}," this.",[569,1387,1388],{"class":583},"blobs",[569,1390,712],{"class":579},[569,1392,1393],{"class":664},"put",[569,1395,667],{"class":676},[569,1397,1398],{"class":583},"key",[569,1400,587],{"class":579},[569,1402,744],{"class":583},[569,1404,712],{"class":579},[569,1406,1407],{"class":664},"stream",[569,1409,930],{"class":676},[569,1411,587],{"class":579},[569,1413,699],{"class":579},[569,1415,1417,1420,1422,1424,1426,1429],{"class":571,"line":1416},14,[569,1418,1419],{"class":676},"      contentType",[569,1421,557],{"class":579},[569,1423,744],{"class":583},[569,1425,712],{"class":579},[569,1427,1428],{"class":583},"type",[569,1430,537],{"class":579},[569,1432,1434,1437,1439,1441,1443,1446],{"class":571,"line":1433},15,[569,1435,1436],{"class":676},"      size",[569,1438,557],{"class":579},[569,1440,744],{"class":583},[569,1442,712],{"class":579},[569,1444,1445],{"class":583},"size",[569,1447,537],{"class":579},[569,1449,1451,1454,1456,1458,1461,1463,1465,1467,1470],{"class":571,"line":1450},16,[569,1452,1453],{"class":676},"      custom",[569,1455,557],{"class":579},[569,1457,580],{"class":579},[569,1459,1460],{"class":676}," filename",[569,1462,557],{"class":579},[569,1464,744],{"class":583},[569,1466,712],{"class":579},[569,1468,1469],{"class":583},"name",[569,1471,1472],{"class":579}," },\n",[569,1474,1476,1479,1481],{"class":571,"line":1475},17,[569,1477,1478],{"class":579},"    }",[569,1480,838],{"class":676},[569,1482,609],{"class":579},[569,1484,1486],{"class":571,"line":1485},18,[569,1487,644],{"emptyLinePlaceholder":643},[569,1489,1491,1494,1496,1498,1501,1503,1506,1508],{"class":571,"line":1490},19,[569,1492,1493],{"class":575},"    return",[569,1495,1316],{"class":583},[569,1497,712],{"class":579},[569,1499,1500],{"class":664},"respond",[569,1502,667],{"class":676},[569,1504,1505],{"class":756},"201",[569,1507,587],{"class":579},[569,1509,699],{"class":579},[569,1511,1513,1516],{"class":571,"line":1512},20,[569,1514,1515],{"class":583},"      key",[569,1517,537],{"class":579},[569,1519,1521,1524,1526,1528,1530,1532],{"class":571,"line":1520},21,[569,1522,1523],{"class":676},"      filename",[569,1525,557],{"class":579},[569,1527,744],{"class":583},[569,1529,712],{"class":579},[569,1531,1469],{"class":583},[569,1533,537],{"class":579},[569,1535,1537,1539,1541,1543,1545,1547],{"class":571,"line":1536},22,[569,1538,1419],{"class":676},[569,1540,557],{"class":579},[569,1542,744],{"class":583},[569,1544,712],{"class":579},[569,1546,1428],{"class":583},[569,1548,537],{"class":579},[569,1550,1552,1554,1556,1558,1560,1562],{"class":571,"line":1551},23,[569,1553,1436],{"class":676},[569,1555,557],{"class":579},[569,1557,744],{"class":583},[569,1559,712],{"class":579},[569,1561,1445],{"class":583},[569,1563,537],{"class":579},[569,1565,1567,1570,1572,1574,1576,1578],{"class":571,"line":1566},24,[569,1568,1569],{"class":676},"      caption",[569,1571,557],{"class":579},[569,1573,1300],{"class":583},[569,1575,712],{"class":579},[569,1577,552],{"class":583},[569,1579,537],{"class":579},[569,1581,1583,1585,1587],{"class":571,"line":1582},25,[569,1584,1478],{"class":579},[569,1586,838],{"class":676},[569,1588,609],{"class":579},[569,1590,1592],{"class":571,"line":1591},26,[569,1593,1594],{"class":579},"  }\n",[569,1596,1598],{"class":571,"line":1597},27,[569,1599,1600],{"class":579},"}\n",[499,1602,1603,1604,1607,1608,1610,1611,1614,1615,1618],{},"A disallowed content type or a missing required file fails with a ",[508,1605,1606],{},"400","; a file over ",[508,1609,536],{}," fails\nwith a ",[508,1612,1613],{},"413"," — both enforced by ",[508,1616,1617],{},"readValidatedFormData()"," before your handler body runs.",[521,1620,1622],{"id":1621},"_3-bind-the-blobstore","3. Bind the BlobStore",[499,1624,1625,1627,1628,1631],{},[508,1626,518],{}," is an abstract DI token. ",[508,1629,1630],{},"MemoryBlobStore"," extends it and is the only satisfier in scope, so a\nbare provider resolves the token — no compiler plugin, no boot seam. Start with the in-memory driver, which\nneeds no setup and is ideal for tests:",[559,1633,1636],{"className":561,"code":1634,"filename":1635,"language":564,"meta":565,"style":565},"import { MemoryBlobStore } from \"@heximon\u002Fblob\u002Fmemory\";\nimport { Module } from \"@heximon\u002Fruntime\";\nimport { UploadsController } from \".\u002Fuploads.controller\";\n\nexport class UploadsModule extends Module({\n  providers: [MemoryBlobStore],\n  http: { controllers: [UploadsController] },\n}) {}\n","src\u002Fuploads\u002Fuploads.module.ts",[508,1637,1638,1660,1682,1703,1707,1724,1736,1756],{"__ignoreMap":565},[569,1639,1640,1642,1644,1647,1649,1651,1653,1656,1658],{"class":571,"line":572},[569,1641,576],{"class":575},[569,1643,580],{"class":579},[569,1645,1646],{"class":583}," MemoryBlobStore",[569,1648,593],{"class":579},[569,1650,596],{"class":575},[569,1652,599],{"class":579},[569,1654,1655],{"class":602},"@heximon\u002Fblob\u002Fmemory",[569,1657,606],{"class":579},[569,1659,609],{"class":579},[569,1661,1662,1664,1666,1669,1671,1673,1675,1678,1680],{"class":571,"line":612},[569,1663,576],{"class":575},[569,1665,580],{"class":579},[569,1667,1668],{"class":583}," Module",[569,1670,593],{"class":579},[569,1672,596],{"class":575},[569,1674,599],{"class":579},[569,1676,1677],{"class":602},"@heximon\u002Fruntime",[569,1679,606],{"class":579},[569,1681,609],{"class":579},[569,1683,1684,1686,1688,1690,1692,1694,1696,1699,1701],{"class":571,"line":640},[569,1685,576],{"class":575},[569,1687,580],{"class":579},[569,1689,1204],{"class":583},[569,1691,593],{"class":579},[569,1693,596],{"class":575},[569,1695,599],{"class":579},[569,1697,1698],{"class":602},".\u002Fuploads.controller",[569,1700,606],{"class":579},[569,1702,609],{"class":579},[569,1704,1705],{"class":571,"line":647},[569,1706,644],{"emptyLinePlaceholder":643},[569,1708,1709,1711,1713,1716,1718,1720,1722],{"class":571,"line":673},[569,1710,650],{"class":575},[569,1712,654],{"class":653},[569,1714,1715],{"class":657}," UploadsModule",[569,1717,661],{"class":653},[569,1719,1668],{"class":664},[569,1721,667],{"class":583},[569,1723,670],{"class":579},[569,1725,1726,1729,1731,1734],{"class":571,"line":691},[569,1727,1728],{"class":676},"  providers",[569,1730,557],{"class":579},[569,1732,1733],{"class":583}," [MemoryBlobStore]",[569,1735,537],{"class":579},[569,1737,1738,1741,1743,1745,1748,1750,1753],{"class":571,"line":702},[569,1739,1740],{"class":676},"  http",[569,1742,557],{"class":579},[569,1744,580],{"class":579},[569,1746,1747],{"class":676}," controllers",[569,1749,557],{"class":579},[569,1751,1752],{"class":583}," [UploadsController] ",[569,1754,1755],{"class":579},"},\n",[569,1757,1758,1760,1762],{"class":571,"line":730},[569,1759,798],{"class":579},[569,1761,854],{"class":583},[569,1763,857],{"class":579},[521,1765,1767],{"id":1766},"_4-run-it","4. Run it",[559,1769,1774],{"className":1770,"code":1771,"filename":1772,"language":1773,"meta":565,"style":565},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","curl -s -X POST localhost:3024\u002Fapi\u002Fuploads \\\n  -F 'file=@.\u002Fphoto.jpg;type=image\u002Fjpeg' \\\n  -F 'caption=Team offsite'\n# → 201 { \"key\": \"…\", \"filename\": \"photo.jpg\", \"contentType\": \"image\u002Fjpeg\", \"size\": 48213, \"caption\": \"Team offsite\" }\n","terminal","bash",[508,1775,1776,1796,1812,1824],{"__ignoreMap":565},[569,1777,1778,1781,1784,1787,1790,1793],{"class":571,"line":572},[569,1779,1780],{"class":657},"curl",[569,1782,1783],{"class":602}," -s",[569,1785,1786],{"class":602}," -X",[569,1788,1789],{"class":602}," POST",[569,1791,1792],{"class":602}," localhost:3024\u002Fapi\u002Fuploads",[569,1794,1795],{"class":583}," \\\n",[569,1797,1798,1801,1804,1807,1810],{"class":571,"line":612},[569,1799,1800],{"class":602},"  -F",[569,1802,1803],{"class":579}," '",[569,1805,1806],{"class":602},"file=@.\u002Fphoto.jpg;type=image\u002Fjpeg",[569,1808,1809],{"class":579},"'",[569,1811,1795],{"class":583},[569,1813,1814,1816,1818,1821],{"class":571,"line":640},[569,1815,1800],{"class":602},[569,1817,1803],{"class":579},[569,1819,1820],{"class":602},"caption=Team offsite",[569,1822,1823],{"class":579},"'\n",[569,1825,1826],{"class":571,"line":647},[569,1827,1828],{"class":1351},"# → 201 { \"key\": \"…\", \"filename\": \"photo.jpg\", \"contentType\": \"image\u002Fjpeg\", \"size\": 48213, \"caption\": \"Team offsite\" }\n",[521,1830,1832],{"id":1831},"go-to-production","Go to production",[499,1834,1835,1836,1839,1840,1843,1844,1847,1848,1851],{},"Swap the binding, not the controller — ",[508,1837,1838],{},"FileSystemBlobStore",", ",[508,1841,1842],{},"S3BlobStore",", and ",[508,1845,1846],{},"R2BlobStore"," all\n",[508,1849,1850],{},"extend BlobStore",", so any of them satisfies the same token:",[559,1853,1855],{"className":561,"code":1854,"filename":1635,"language":564,"meta":565,"style":565},"import { BlobStore } from \"@heximon\u002Fblob\";\nimport { FileSystemBlobStore } from \"@heximon\u002Fblob\u002Ffilesystem\";\nimport { Module } from \"@heximon\u002Fruntime\";\nimport { UploadsController } from \".\u002Fuploads.controller\";\n\nexport class UploadsModule extends Module({\n  providers: [{ provide: BlobStore, useFactory: () => new FileSystemBlobStore(\"\u002Fvar\u002Fdata\u002Fblobs\") }],\n  http: { controllers: [UploadsController] },\n}) {}\n",[508,1856,1857,1877,1899,1919,1939,1943,1959,2012,2028],{"__ignoreMap":565},[569,1858,1859,1861,1863,1865,1867,1869,1871,1873,1875],{"class":571,"line":572},[569,1860,576],{"class":575},[569,1862,580],{"class":579},[569,1864,1126],{"class":583},[569,1866,593],{"class":579},[569,1868,596],{"class":575},[569,1870,599],{"class":579},[569,1872,1135],{"class":602},[569,1874,606],{"class":579},[569,1876,609],{"class":579},[569,1878,1879,1881,1883,1886,1888,1890,1892,1895,1897],{"class":571,"line":612},[569,1880,576],{"class":575},[569,1882,580],{"class":579},[569,1884,1885],{"class":583}," FileSystemBlobStore",[569,1887,593],{"class":579},[569,1889,596],{"class":575},[569,1891,599],{"class":579},[569,1893,1894],{"class":602},"@heximon\u002Fblob\u002Ffilesystem",[569,1896,606],{"class":579},[569,1898,609],{"class":579},[569,1900,1901,1903,1905,1907,1909,1911,1913,1915,1917],{"class":571,"line":640},[569,1902,576],{"class":575},[569,1904,580],{"class":579},[569,1906,1668],{"class":583},[569,1908,593],{"class":579},[569,1910,596],{"class":575},[569,1912,599],{"class":579},[569,1914,1677],{"class":602},[569,1916,606],{"class":579},[569,1918,609],{"class":579},[569,1920,1921,1923,1925,1927,1929,1931,1933,1935,1937],{"class":571,"line":647},[569,1922,576],{"class":575},[569,1924,580],{"class":579},[569,1926,1204],{"class":583},[569,1928,593],{"class":579},[569,1930,596],{"class":575},[569,1932,599],{"class":579},[569,1934,1698],{"class":602},[569,1936,606],{"class":579},[569,1938,609],{"class":579},[569,1940,1941],{"class":571,"line":673},[569,1942,644],{"emptyLinePlaceholder":643},[569,1944,1945,1947,1949,1951,1953,1955,1957],{"class":571,"line":691},[569,1946,650],{"class":575},[569,1948,654],{"class":653},[569,1950,1715],{"class":657},[569,1952,661],{"class":653},[569,1954,1668],{"class":664},[569,1956,667],{"class":583},[569,1958,670],{"class":579},[569,1960,1961,1963,1965,1967,1969,1972,1974,1976,1978,1981,1983,1986,1989,1992,1994,1996,1998,2001,2003,2005,2007,2010],{"class":571,"line":702},[569,1962,1728],{"class":676},[569,1964,557],{"class":579},[569,1966,767],{"class":583},[569,1968,741],{"class":579},[569,1970,1971],{"class":676}," provide",[569,1973,557],{"class":579},[569,1975,1126],{"class":583},[569,1977,587],{"class":579},[569,1979,1980],{"class":664}," useFactory",[569,1982,557],{"class":579},[569,1984,1985],{"class":579}," ()",[569,1987,1988],{"class":653}," =>",[569,1990,1991],{"class":579}," new",[569,1993,1885],{"class":664},[569,1995,667],{"class":583},[569,1997,606],{"class":579},[569,1999,2000],{"class":602},"\u002Fvar\u002Fdata\u002Fblobs",[569,2002,606],{"class":579},[569,2004,854],{"class":583},[569,2006,798],{"class":579},[569,2008,2009],{"class":583},"]",[569,2011,537],{"class":579},[569,2013,2014,2016,2018,2020,2022,2024,2026],{"class":571,"line":730},[569,2015,1740],{"class":676},[569,2017,557],{"class":579},[569,2019,580],{"class":579},[569,2021,1747],{"class":676},[569,2023,557],{"class":579},[569,2025,1752],{"class":583},[569,2027,1755],{"class":579},[569,2029,2030,2032,2034],{"class":571,"line":805},[569,2031,798],{"class":579},[569,2033,854],{"class":583},[569,2035,857],{"class":579},[2037,2038,2040,2046,2047,2050,2051,2053,2054,854,2056,2059,2060,2063,2064,2066],"callout",{"type":2039},"warning",[2041,2042,2043,2045],"strong",{},[508,2044,536],{}," is checked after the body is parsed."," Reading multipart input buffers and parses the whole\nbody via the web ",[508,2048,2049],{},"Request.formData()"," before any ",[508,2052,514],{}," is available, so an over-limit file is rejected\n(",[508,2055,1613],{},[503,2057,2058],{},"after"," it's in memory, not mid-stream. The default-on ",[508,2061,2062],{},"Content-Length"," body guard (1 MiB) catches\noversized requests before parsing, but only when the client sends a ",[508,2065,2062],{}," — for a hard ceiling\non untrusted uploads, also cap the body at the transport or proxy layer.",[521,2068,2070],{"id":2069},"see-also","See also",[2072,2073,2074,2089,2101],"ul",{},[2075,2076,2077,2081,2082,2085,2086,2088],"li",{},[2078,2079,62],"a",{"href":2080},"\u002Fdocs\u002Fessentials\u002Fvalidation#upload-files-multipart"," — the multipart\nvalidation mechanics in full: field constraints, the self-client's automatic ",[508,2083,2084],{},"FormData"," encoding, and\nthe ",[508,2087,536],{},"-vs-body-guard ordering.",[2075,2090,2091,2093,2094,2096,2097,2100],{},[2078,2092,170],{"href":171}," — the full ",[508,2095,518],{}," port (streaming, byte ranges), all four\ndrivers, and the ",[508,2098,2099],{},"SignedUrlBlobStore"," capability for direct-to-storage presigned URLs.",[2075,2102,2103,2111,2112,2115],{},[2078,2104,2108],{"href":2105,"rel":2106},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fgap\u002Fstorage-blob-adapters",[2107],"nofollow",[2041,2109,2110],{},"the gap's storage & blob adapters example","\n— this upload feature alongside presigned-URL issuance and an env-driven ",[508,2113,2114],{},"Storage"," binding, with an\nin-process test proving a streamed round-trip.",[2117,2118,2119],"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 .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}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}",{"title":565,"searchDepth":612,"depth":612,"links":2121},[2122,2123,2124,2125,2126,2127],{"id":523,"depth":612,"text":524},{"id":1093,"depth":612,"text":1094},{"id":1621,"depth":612,"text":1622},{"id":1766,"depth":612,"text":1767},{"id":1831,"depth":612,"text":1832},{"id":2069,"depth":612,"text":2070},"Accept a multipart\u002Fform-data upload validated by Route.files(), read it with readValidatedFormData(), and stream it into the abstract BlobStore DI token — swappable for filesystem, S3, or R2 with no controller change.","md",null,{},{"icon":435},{"title":432,"description":2128},"y51YoXLGZwpA4XStAuVkAODTVTAcBHtV-c66vvLJvQE",[2136,2138],{"title":427,"path":428,"stem":429,"description":2137,"icon":430,"children":-1},"Inject the abstract EmailTransport DI token, compose an EmailMessage with Mustache placeholders, and send it — with a capture transport for dev\u002Ftests and ResendTransport as the one-line production swap.",{"title":437,"path":438,"stem":439,"description":2139,"icon":24,"children":-1},"Wrap a slow read with the Cache facade's getOrSet over a Storage tier, tag the entry for invalidation, and bust it after a write — MemoryStorage, Cache.invalidate, tags.",1783300761903]