[{"data":1,"prerenderedAt":3409},["ShallowReactive",2],{"navigation_docs":3,"-docs-realtime-websockets":493,"-docs-realtime-websockets-surround":3404},[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":267,"body":495,"description":3397,"extension":3398,"links":3399,"meta":3400,"navigation":3401,"path":268,"seo":3402,"stem":269,"__hash__":3403},"docs\u002Fdocs\u002F05.realtime\u002F02.websockets.md",{"type":496,"value":497,"toc":3385},"minimark",[498,511,518,523,538,609,749,870,874,897,911,1201,1211,1215,1222,1323,1327,1346,1632,1646,1650,1679,1825,1828,1955,1959,1964,2055,2085,2091,2095,2104,2122,2143,2172,2855,2870,3063,3075,3100,3123,3250,3254,3264,3277,3291,3306,3309,3313,3316,3326,3330,3381],[499,500,501,502,506,507,510],"p",{},"WebSockets give you bidirectional realtime: the connection stays open, and either side pushes a frame whenever\nit has something to say. You author a ",[503,504,505],"code",{},"WebSocketHandler\u003C\"\u002Fpath\">",", implement the connection lifecycle, and the\ncompiler mounts the upgrade onto your existing HTTP host. The path is the type argument — just like a\n",[503,508,509],{},"Controller\u003C\"\u002Fusers\">"," prefix — so the type that names the route also types its path params.",[499,512,513,514,517],{},"A socket handler isn't its own server. It rides whatever ",[503,515,516],{},"fetch"," host already serves your HTTP routes — the\ndev server, Nitro, or Workers — which is why the WebSocket plugin requires the HTTP plugin: one process, one\nport, one boot.",[519,520,522],"h2",{"id":521},"set-up-the-plugin","Set up the plugin",[499,524,525,526,529,530,533,534,537],{},"Install the package and register ",[503,527,528],{},"WebSocketPlugin"," alongside the HTTP plugin. Listing the WebSocket plugin is\nwhat attaches the dev server's upgrade adapter, so ",[503,531,532],{},"ws:\u002F\u002F"," routes connect under ",[503,535,536],{},"pnpm dev",".",[539,540,541,566,581,595],"code-group",{},[542,543,549],"pre",{"className":544,"code":545,"filename":546,"language":547,"meta":548,"style":548},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm add @heximon\u002Fwebsocket\n","pnpm","bash","",[503,550,551],{"__ignoreMap":548},[552,553,556,559,563],"span",{"class":554,"line":555},"line",1,[552,557,546],{"class":558},"sBMFI",[552,560,562],{"class":561},"sfazB"," add",[552,564,565],{"class":561}," @heximon\u002Fwebsocket\n",[542,567,570],{"className":544,"code":568,"filename":569,"language":547,"meta":548,"style":548},"npm install @heximon\u002Fwebsocket\n","npm",[503,571,572],{"__ignoreMap":548},[552,573,574,576,579],{"class":554,"line":555},[552,575,569],{"class":558},[552,577,578],{"class":561}," install",[552,580,565],{"class":561},[542,582,585],{"className":544,"code":583,"filename":584,"language":547,"meta":548,"style":548},"yarn add @heximon\u002Fwebsocket\n","yarn",[503,586,587],{"__ignoreMap":548},[552,588,589,591,593],{"class":554,"line":555},[552,590,584],{"class":558},[552,592,562],{"class":561},[552,594,565],{"class":561},[542,596,599],{"className":544,"code":597,"filename":598,"language":547,"meta":548,"style":548},"bun add @heximon\u002Fwebsocket\n","bun",[503,600,601],{"__ignoreMap":548},[552,602,603,605,607],{"class":554,"line":555},[552,604,598],{"class":558},[552,606,562],{"class":561},[552,608,565],{"class":561},[542,610,615],{"className":611,"code":612,"filename":613,"language":614,"meta":548,"style":548},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import heximon from \"@heximon\u002Fbuild\u002Fvite\";\nimport { defineConfig } from \"vite-plus\";\n\nexport default defineConfig({\n  plugins: [heximon()],\n  server: { port: 3000 },\n});\n","vite.config.ts","ts",[503,616,617,643,669,676,694,716,738],{"__ignoreMap":548},[552,618,619,623,627,630,634,637,640],{"class":554,"line":555},[552,620,622],{"class":621},"s7zQu","import",[552,624,626],{"class":625},"sTEyZ"," heximon ",[552,628,629],{"class":621},"from",[552,631,633],{"class":632},"sMK4o"," \"",[552,635,636],{"class":561},"@heximon\u002Fbuild\u002Fvite",[552,638,639],{"class":632},"\"",[552,641,642],{"class":632},";\n",[552,644,646,648,651,654,657,660,662,665,667],{"class":554,"line":645},2,[552,647,622],{"class":621},[552,649,650],{"class":632}," {",[552,652,653],{"class":625}," defineConfig",[552,655,656],{"class":632}," }",[552,658,659],{"class":621}," from",[552,661,633],{"class":632},[552,663,664],{"class":561},"vite-plus",[552,666,639],{"class":632},[552,668,642],{"class":632},[552,670,672],{"class":554,"line":671},3,[552,673,675],{"emptyLinePlaceholder":674},true,"\n",[552,677,679,682,685,688,691],{"class":554,"line":678},4,[552,680,681],{"class":621},"export",[552,683,684],{"class":621}," default",[552,686,653],{"class":687},"s2Zo4",[552,689,690],{"class":625},"(",[552,692,693],{"class":632},"{\n",[552,695,697,701,704,707,710,713],{"class":554,"line":696},5,[552,698,700],{"class":699},"swJcz","  plugins",[552,702,703],{"class":632},":",[552,705,706],{"class":625}," [",[552,708,709],{"class":687},"heximon",[552,711,712],{"class":625},"()]",[552,714,715],{"class":632},",\n",[552,717,719,722,724,726,729,731,735],{"class":554,"line":718},6,[552,720,721],{"class":699},"  server",[552,723,703],{"class":632},[552,725,650],{"class":632},[552,727,728],{"class":699}," port",[552,730,703],{"class":632},[552,732,734],{"class":733},"sbssI"," 3000",[552,736,737],{"class":632}," },\n",[552,739,741,744,747],{"class":554,"line":740},7,[552,742,743],{"class":632},"}",[552,745,746],{"class":625},")",[552,748,642],{"class":632},[542,750,753],{"className":611,"code":751,"filename":752,"language":614,"meta":548,"style":548},"import { defineHeximonConfig } from \"@heximon\u002Fbuild\";\nimport { HttpPlugin } from \"@heximon\u002Fhttp\u002Fcompiler\";\nimport { WebSocketPlugin } from \"@heximon\u002Fwebsocket\u002Fcompiler\";\n\nexport default defineHeximonConfig({ plugins: [new HttpPlugin(), new WebSocketPlugin()] });\n","heximon.config.ts",[503,754,755,777,799,821,825],{"__ignoreMap":548},[552,756,757,759,761,764,766,768,770,773,775],{"class":554,"line":555},[552,758,622],{"class":621},[552,760,650],{"class":632},[552,762,763],{"class":625}," defineHeximonConfig",[552,765,656],{"class":632},[552,767,659],{"class":621},[552,769,633],{"class":632},[552,771,772],{"class":561},"@heximon\u002Fbuild",[552,774,639],{"class":632},[552,776,642],{"class":632},[552,778,779,781,783,786,788,790,792,795,797],{"class":554,"line":645},[552,780,622],{"class":621},[552,782,650],{"class":632},[552,784,785],{"class":625}," HttpPlugin",[552,787,656],{"class":632},[552,789,659],{"class":621},[552,791,633],{"class":632},[552,793,794],{"class":561},"@heximon\u002Fhttp\u002Fcompiler",[552,796,639],{"class":632},[552,798,642],{"class":632},[552,800,801,803,805,808,810,812,814,817,819],{"class":554,"line":671},[552,802,622],{"class":621},[552,804,650],{"class":632},[552,806,807],{"class":625}," WebSocketPlugin",[552,809,656],{"class":632},[552,811,659],{"class":621},[552,813,633],{"class":632},[552,815,816],{"class":561},"@heximon\u002Fwebsocket\u002Fcompiler",[552,818,639],{"class":632},[552,820,642],{"class":632},[552,822,823],{"class":554,"line":678},[552,824,675],{"emptyLinePlaceholder":674},[552,826,827,829,831,833,835,838,841,843,845,848,850,853,856,859,861,864,866,868],{"class":554,"line":696},[552,828,681],{"class":621},[552,830,684],{"class":621},[552,832,763],{"class":687},[552,834,690],{"class":625},[552,836,837],{"class":632},"{",[552,839,840],{"class":699}," plugins",[552,842,703],{"class":632},[552,844,706],{"class":625},[552,846,847],{"class":632},"new",[552,849,785],{"class":687},[552,851,852],{"class":625},"()",[552,854,855],{"class":632},",",[552,857,858],{"class":632}," new",[552,860,807],{"class":687},[552,862,863],{"class":625},"()] ",[552,865,743],{"class":632},[552,867,746],{"class":625},[552,869,642],{"class":632},[519,871,873],{"id":872},"author-a-handler","Author a handler",[499,875,876,877,880,881,884,885,888,889,892,893,896],{},"A handler implements three lifecycle methods — ",[503,878,879],{},"open(connection)",", ",[503,882,883],{},"message(connection, inbound)",", and\n",[503,886,887],{},"close(connection)"," — plus an optional ",[503,890,891],{},"error(connection, error)",". The upgrade path is the ",[503,894,895],{},"\u003CPath>"," type\nargument; a duplicate path across handlers is a build error, because exactly one handler serves each route.",[499,898,899,900,903,904,907,908,703],{},"The ",[503,901,902],{},"WebSocketConnection"," you receive is the same object for the lifetime of the socket — use it to write\nframes, close the connection, and subscribe to topics. Here a minimal echo socket greets on ",[503,905,906],{},"open"," and sends\nback whatever arrives on ",[503,909,910],{},"message",[542,912,915],{"className":611,"code":913,"filename":914,"language":614,"meta":548,"style":548},"import type { Inbound, WebSocketConnection, WebSocketHandler } from \"@heximon\u002Fwebsocket\";\n\nexport class EchoSocket implements WebSocketHandler\u003C\"\u002Fws\u002Fecho\"> {\n  public async open(connection: WebSocketConnection): Promise\u003Cvoid> {\n    connection.send(JSON.stringify({ type: \"welcome\", id: connection.id }));\n  }\n\n  public async message(connection: WebSocketConnection, inbound: Inbound): Promise\u003Cvoid> {\n    connection.send(inbound.text());\n  }\n\n  public async close(_connection: WebSocketConnection): Promise\u003Cvoid> {}\n}\n","src\u002Fecho\u002Fecho.socket.ts",[503,916,917,952,956,988,1024,1081,1086,1090,1129,1153,1158,1163,1195],{"__ignoreMap":548},[552,918,919,921,924,926,929,931,934,936,939,941,943,945,948,950],{"class":554,"line":555},[552,920,622],{"class":621},[552,922,923],{"class":621}," type",[552,925,650],{"class":632},[552,927,928],{"class":625}," Inbound",[552,930,855],{"class":632},[552,932,933],{"class":625}," WebSocketConnection",[552,935,855],{"class":632},[552,937,938],{"class":625}," WebSocketHandler",[552,940,656],{"class":632},[552,942,659],{"class":621},[552,944,633],{"class":632},[552,946,947],{"class":561},"@heximon\u002Fwebsocket",[552,949,639],{"class":632},[552,951,642],{"class":632},[552,953,954],{"class":554,"line":645},[552,955,675],{"emptyLinePlaceholder":674},[552,957,958,960,964,967,970,972,975,977,980,982,985],{"class":554,"line":671},[552,959,681],{"class":621},[552,961,963],{"class":962},"spNyl"," class",[552,965,966],{"class":558}," EchoSocket",[552,968,969],{"class":962}," implements",[552,971,938],{"class":558},[552,973,974],{"class":632},"\u003C",[552,976,639],{"class":632},[552,978,979],{"class":561},"\u002Fws\u002Fecho",[552,981,639],{"class":632},[552,983,984],{"class":632},">",[552,986,987],{"class":632}," {\n",[552,989,990,993,996,999,1001,1005,1007,1009,1012,1015,1017,1020,1022],{"class":554,"line":678},[552,991,992],{"class":962},"  public",[552,994,995],{"class":962}," async",[552,997,998],{"class":699}," open",[552,1000,690],{"class":632},[552,1002,1004],{"class":1003},"sHdIc","connection",[552,1006,703],{"class":632},[552,1008,933],{"class":558},[552,1010,1011],{"class":632},"):",[552,1013,1014],{"class":558}," Promise",[552,1016,974],{"class":632},[552,1018,1019],{"class":558},"void",[552,1021,984],{"class":632},[552,1023,987],{"class":632},[552,1025,1026,1029,1031,1034,1036,1039,1041,1044,1046,1048,1050,1052,1054,1057,1059,1061,1064,1066,1069,1071,1074,1076,1079],{"class":554,"line":696},[552,1027,1028],{"class":625},"    connection",[552,1030,537],{"class":632},[552,1032,1033],{"class":687},"send",[552,1035,690],{"class":699},[552,1037,1038],{"class":625},"JSON",[552,1040,537],{"class":632},[552,1042,1043],{"class":687},"stringify",[552,1045,690],{"class":699},[552,1047,837],{"class":632},[552,1049,923],{"class":699},[552,1051,703],{"class":632},[552,1053,633],{"class":632},[552,1055,1056],{"class":561},"welcome",[552,1058,639],{"class":632},[552,1060,855],{"class":632},[552,1062,1063],{"class":699}," id",[552,1065,703],{"class":632},[552,1067,1068],{"class":625}," connection",[552,1070,537],{"class":632},[552,1072,1073],{"class":625},"id",[552,1075,656],{"class":632},[552,1077,1078],{"class":699},"))",[552,1080,642],{"class":632},[552,1082,1083],{"class":554,"line":718},[552,1084,1085],{"class":632},"  }\n",[552,1087,1088],{"class":554,"line":740},[552,1089,675],{"emptyLinePlaceholder":674},[552,1091,1093,1095,1097,1100,1102,1104,1106,1108,1110,1113,1115,1117,1119,1121,1123,1125,1127],{"class":554,"line":1092},8,[552,1094,992],{"class":962},[552,1096,995],{"class":962},[552,1098,1099],{"class":699}," message",[552,1101,690],{"class":632},[552,1103,1004],{"class":1003},[552,1105,703],{"class":632},[552,1107,933],{"class":558},[552,1109,855],{"class":632},[552,1111,1112],{"class":1003}," inbound",[552,1114,703],{"class":632},[552,1116,928],{"class":558},[552,1118,1011],{"class":632},[552,1120,1014],{"class":558},[552,1122,974],{"class":632},[552,1124,1019],{"class":558},[552,1126,984],{"class":632},[552,1128,987],{"class":632},[552,1130,1132,1134,1136,1138,1140,1143,1145,1148,1151],{"class":554,"line":1131},9,[552,1133,1028],{"class":625},[552,1135,537],{"class":632},[552,1137,1033],{"class":687},[552,1139,690],{"class":699},[552,1141,1142],{"class":625},"inbound",[552,1144,537],{"class":632},[552,1146,1147],{"class":687},"text",[552,1149,1150],{"class":699},"())",[552,1152,642],{"class":632},[552,1154,1156],{"class":554,"line":1155},10,[552,1157,1085],{"class":632},[552,1159,1161],{"class":554,"line":1160},11,[552,1162,675],{"emptyLinePlaceholder":674},[552,1164,1166,1168,1170,1173,1175,1178,1180,1182,1184,1186,1188,1190,1192],{"class":554,"line":1165},12,[552,1167,992],{"class":962},[552,1169,995],{"class":962},[552,1171,1172],{"class":699}," close",[552,1174,690],{"class":632},[552,1176,1177],{"class":1003},"_connection",[552,1179,703],{"class":632},[552,1181,933],{"class":558},[552,1183,1011],{"class":632},[552,1185,1014],{"class":558},[552,1187,974],{"class":632},[552,1189,1019],{"class":558},[552,1191,984],{"class":632},[552,1193,1194],{"class":632}," {}\n",[552,1196,1198],{"class":554,"line":1197},13,[552,1199,1200],{"class":632},"}\n",[499,1202,1203,1206,1207,1210],{},[503,1204,1205],{},"WebSocketHandler"," is a marker: the type argument carries the config, the class body carries the lifecycle,\nand with a type-only import the marker never enters your runtime module graph. ",[503,1208,1209],{},"extends WebSocketHandler\u003C\"\u002Fws\u002Fecho\">"," works the same — use it when you want your own base class hierarchy on top.",[519,1212,1214],{"id":1213},"declare-it-in-a-module","Declare it in a module",[499,1216,1217,1218,1221],{},"A handler is discovered one way: by being listed under the ",[503,1219,1220],{},"websocket"," namespace in its module's config. That\nlist is the whole registration — the compiler constructs it through the DI graph and dispatches upgrades to it,\nexactly like a controller.",[542,1223,1226],{"className":611,"code":1224,"filename":1225,"language":614,"meta":548,"style":548},"import { Module } from \"@heximon\u002Fruntime\";\nimport { EchoSocket } from \".\u002Fecho.socket\";\n\nexport class EchoModule extends Module({\n  websocket: { handlers: [EchoSocket] },\n}) {}\n","src\u002Fecho\u002Fecho.module.ts",[503,1227,1228,1250,1271,1275,1293,1313],{"__ignoreMap":548},[552,1229,1230,1232,1234,1237,1239,1241,1243,1246,1248],{"class":554,"line":555},[552,1231,622],{"class":621},[552,1233,650],{"class":632},[552,1235,1236],{"class":625}," Module",[552,1238,656],{"class":632},[552,1240,659],{"class":621},[552,1242,633],{"class":632},[552,1244,1245],{"class":561},"@heximon\u002Fruntime",[552,1247,639],{"class":632},[552,1249,642],{"class":632},[552,1251,1252,1254,1256,1258,1260,1262,1264,1267,1269],{"class":554,"line":645},[552,1253,622],{"class":621},[552,1255,650],{"class":632},[552,1257,966],{"class":625},[552,1259,656],{"class":632},[552,1261,659],{"class":621},[552,1263,633],{"class":632},[552,1265,1266],{"class":561},".\u002Fecho.socket",[552,1268,639],{"class":632},[552,1270,642],{"class":632},[552,1272,1273],{"class":554,"line":671},[552,1274,675],{"emptyLinePlaceholder":674},[552,1276,1277,1279,1281,1284,1287,1289,1291],{"class":554,"line":678},[552,1278,681],{"class":621},[552,1280,963],{"class":962},[552,1282,1283],{"class":558}," EchoModule",[552,1285,1286],{"class":962}," extends",[552,1288,1236],{"class":687},[552,1290,690],{"class":625},[552,1292,693],{"class":632},[552,1294,1295,1298,1300,1302,1305,1307,1310],{"class":554,"line":696},[552,1296,1297],{"class":699},"  websocket",[552,1299,703],{"class":632},[552,1301,650],{"class":632},[552,1303,1304],{"class":699}," handlers",[552,1306,703],{"class":632},[552,1308,1309],{"class":625}," [EchoSocket] ",[552,1311,1312],{"class":632},"},\n",[552,1314,1315,1317,1320],{"class":554,"line":718},[552,1316,743],{"class":632},[552,1318,1319],{"class":625},") ",[552,1321,1322],{"class":632},"{}\n",[519,1324,1326],{"id":1325},"validate-inbound-frames","Validate inbound frames",[499,1328,1329,1330,1333,1334,1337,1338,1341,1342,1345],{},"An incoming frame is untrusted input — validate it at the boundary. Declare the message schema in the\n",[503,1331,1332],{},"message()"," parameter type as ",[503,1335,1336],{},"Inbound\u003C{ body: Schema }>"," — a bare class reference (",[503,1339,1340],{},"typeof Schema"," works\ntoo, if you'd rather spell it as a type) — and ",[503,1343,1344],{},"inbound.body()"," runs the decoded JSON through it before\nhanding it to you.",[542,1347,1350],{"className":611,"code":1348,"filename":1349,"language":614,"meta":548,"style":548},"import type { Inbound, WebSocketConnection, WebSocketHandler } from \"@heximon\u002Fwebsocket\";\nimport { ChatMessage } from \".\u002Fchat-message\"; \u002F\u002F a SchemaObject class (any Standard Schema value works)\n\nexport class ChatSocket implements WebSocketHandler\u003C\"\u002Fws\u002Fchat\"> {\n  public async open(connection: WebSocketConnection): Promise\u003Cvoid> {}\n\n  public async message(\n    connection: WebSocketConnection,\n    inbound: Inbound\u003C{ body: ChatMessage }>,\n  ): Promise\u003Cvoid> {\n    const body = await inbound.body(); \u002F\u002F typed; throws InvalidMessageError on a schema failure\n    connection.send(JSON.stringify({ echoed: body }));\n  }\n\n  public async close(connection: WebSocketConnection): Promise\u003Cvoid> {}\n}\n","src\u002Fchat\u002Fchat.socket.ts",[503,1351,1352,1382,1409,1413,1439,1467,1471,1482,1492,1514,1529,1556,1589,1593,1598,1627],{"__ignoreMap":548},[552,1353,1354,1356,1358,1360,1362,1364,1366,1368,1370,1372,1374,1376,1378,1380],{"class":554,"line":555},[552,1355,622],{"class":621},[552,1357,923],{"class":621},[552,1359,650],{"class":632},[552,1361,928],{"class":625},[552,1363,855],{"class":632},[552,1365,933],{"class":625},[552,1367,855],{"class":632},[552,1369,938],{"class":625},[552,1371,656],{"class":632},[552,1373,659],{"class":621},[552,1375,633],{"class":632},[552,1377,947],{"class":561},[552,1379,639],{"class":632},[552,1381,642],{"class":632},[552,1383,1384,1386,1388,1391,1393,1395,1397,1400,1402,1405],{"class":554,"line":645},[552,1385,622],{"class":621},[552,1387,650],{"class":632},[552,1389,1390],{"class":625}," ChatMessage",[552,1392,656],{"class":632},[552,1394,659],{"class":621},[552,1396,633],{"class":632},[552,1398,1399],{"class":561},".\u002Fchat-message",[552,1401,639],{"class":632},[552,1403,1404],{"class":632},";",[552,1406,1408],{"class":1407},"sHwdD"," \u002F\u002F a SchemaObject class (any Standard Schema value works)\n",[552,1410,1411],{"class":554,"line":671},[552,1412,675],{"emptyLinePlaceholder":674},[552,1414,1415,1417,1419,1422,1424,1426,1428,1430,1433,1435,1437],{"class":554,"line":678},[552,1416,681],{"class":621},[552,1418,963],{"class":962},[552,1420,1421],{"class":558}," ChatSocket",[552,1423,969],{"class":962},[552,1425,938],{"class":558},[552,1427,974],{"class":632},[552,1429,639],{"class":632},[552,1431,1432],{"class":561},"\u002Fws\u002Fchat",[552,1434,639],{"class":632},[552,1436,984],{"class":632},[552,1438,987],{"class":632},[552,1440,1441,1443,1445,1447,1449,1451,1453,1455,1457,1459,1461,1463,1465],{"class":554,"line":696},[552,1442,992],{"class":962},[552,1444,995],{"class":962},[552,1446,998],{"class":699},[552,1448,690],{"class":632},[552,1450,1004],{"class":1003},[552,1452,703],{"class":632},[552,1454,933],{"class":558},[552,1456,1011],{"class":632},[552,1458,1014],{"class":558},[552,1460,974],{"class":632},[552,1462,1019],{"class":558},[552,1464,984],{"class":632},[552,1466,1194],{"class":632},[552,1468,1469],{"class":554,"line":718},[552,1470,675],{"emptyLinePlaceholder":674},[552,1472,1473,1475,1477,1479],{"class":554,"line":740},[552,1474,992],{"class":962},[552,1476,995],{"class":962},[552,1478,1099],{"class":699},[552,1480,1481],{"class":632},"(\n",[552,1483,1484,1486,1488,1490],{"class":554,"line":1092},[552,1485,1028],{"class":1003},[552,1487,703],{"class":632},[552,1489,933],{"class":558},[552,1491,715],{"class":632},[552,1493,1494,1497,1499,1501,1504,1507,1509,1511],{"class":554,"line":1131},[552,1495,1496],{"class":1003},"    inbound",[552,1498,703],{"class":632},[552,1500,928],{"class":558},[552,1502,1503],{"class":632},"\u003C{",[552,1505,1506],{"class":699}," body",[552,1508,703],{"class":632},[552,1510,1390],{"class":558},[552,1512,1513],{"class":632}," }>,\n",[552,1515,1516,1519,1521,1523,1525,1527],{"class":554,"line":1155},[552,1517,1518],{"class":632},"  ):",[552,1520,1014],{"class":558},[552,1522,974],{"class":632},[552,1524,1019],{"class":558},[552,1526,984],{"class":632},[552,1528,987],{"class":632},[552,1530,1531,1534,1536,1539,1542,1544,1546,1549,1551,1553],{"class":554,"line":1160},[552,1532,1533],{"class":962},"    const",[552,1535,1506],{"class":625},[552,1537,1538],{"class":632}," =",[552,1540,1541],{"class":621}," await",[552,1543,1112],{"class":625},[552,1545,537],{"class":632},[552,1547,1548],{"class":687},"body",[552,1550,852],{"class":699},[552,1552,1404],{"class":632},[552,1554,1555],{"class":1407}," \u002F\u002F typed; throws InvalidMessageError on a schema failure\n",[552,1557,1558,1560,1562,1564,1566,1568,1570,1572,1574,1576,1579,1581,1583,1585,1587],{"class":554,"line":1165},[552,1559,1028],{"class":625},[552,1561,537],{"class":632},[552,1563,1033],{"class":687},[552,1565,690],{"class":699},[552,1567,1038],{"class":625},[552,1569,537],{"class":632},[552,1571,1043],{"class":687},[552,1573,690],{"class":699},[552,1575,837],{"class":632},[552,1577,1578],{"class":699}," echoed",[552,1580,703],{"class":632},[552,1582,1506],{"class":625},[552,1584,656],{"class":632},[552,1586,1078],{"class":699},[552,1588,642],{"class":632},[552,1590,1591],{"class":554,"line":1197},[552,1592,1085],{"class":632},[552,1594,1596],{"class":554,"line":1595},14,[552,1597,675],{"emptyLinePlaceholder":674},[552,1599,1601,1603,1605,1607,1609,1611,1613,1615,1617,1619,1621,1623,1625],{"class":554,"line":1600},15,[552,1602,992],{"class":962},[552,1604,995],{"class":962},[552,1606,1172],{"class":699},[552,1608,690],{"class":632},[552,1610,1004],{"class":1003},[552,1612,703],{"class":632},[552,1614,933],{"class":558},[552,1616,1011],{"class":632},[552,1618,1014],{"class":558},[552,1620,974],{"class":632},[552,1622,1019],{"class":558},[552,1624,984],{"class":632},[552,1626,1194],{"class":632},[552,1628,1630],{"class":554,"line":1629},16,[552,1631,1200],{"class":632},[499,1633,1634,1635,1637,1638,1641,1642,1645],{},"The validated value is fully typed with nothing to annotate. With no declared schema, ",[503,1636,1344],{}," returns\nthe raw decoded JSON, and ",[503,1639,1640],{},"inbound.text()"," \u002F ",[503,1643,1644],{},"inbound.json\u003CT>()"," give you raw access.",[519,1647,1649],{"id":1648},"authenticate-at-upgrade","Authenticate at upgrade",[499,1651,1652,1653,1656,1657,1663,1664,1667,1668,1671,1672,1675,1676,537],{},"Auth belongs at the handshake, not on every frame. Name HTTP ",[503,1654,1655],{},"Middleware"," classes in the handler's config type\nargument; they run ",[1658,1659,1660,1661],"strong",{},"once at ",[503,1662,906],{}," over the upgrade ",[503,1665,1666],{},"Request",". A middleware that returns a ",[503,1669,1670],{},"Response","\nwithout calling ",[503,1673,1674],{},"next"," aborts the upgrade, and the connection is closed with code ",[503,1677,1678],{},"1008",[542,1680,1682],{"className":611,"code":1681,"filename":1349,"language":614,"meta":548,"style":548},"export class ChatSocket implements WebSocketHandler\u003C{ path: \"\u002Fws\u002Fchat\"; middlewares: [AuthMiddleware] }> {\n  public async open(connection: WebSocketConnection): Promise\u003Cvoid> {}\n  public async message(connection: WebSocketConnection, inbound: Inbound): Promise\u003Cvoid> {}\n  public async close(connection: WebSocketConnection): Promise\u003Cvoid> {}\n}\n",[503,1683,1684,1729,1757,1793,1821],{"__ignoreMap":548},[552,1685,1686,1688,1690,1692,1694,1696,1698,1701,1703,1705,1707,1709,1711,1714,1716,1718,1721,1724,1727],{"class":554,"line":555},[552,1687,681],{"class":621},[552,1689,963],{"class":962},[552,1691,1421],{"class":558},[552,1693,969],{"class":962},[552,1695,938],{"class":558},[552,1697,1503],{"class":632},[552,1699,1700],{"class":699}," path",[552,1702,703],{"class":632},[552,1704,633],{"class":632},[552,1706,1432],{"class":561},[552,1708,639],{"class":632},[552,1710,1404],{"class":632},[552,1712,1713],{"class":699}," middlewares",[552,1715,703],{"class":632},[552,1717,706],{"class":625},[552,1719,1720],{"class":558},"AuthMiddleware",[552,1722,1723],{"class":625},"] ",[552,1725,1726],{"class":632},"}>",[552,1728,987],{"class":632},[552,1730,1731,1733,1735,1737,1739,1741,1743,1745,1747,1749,1751,1753,1755],{"class":554,"line":645},[552,1732,992],{"class":962},[552,1734,995],{"class":962},[552,1736,998],{"class":699},[552,1738,690],{"class":632},[552,1740,1004],{"class":1003},[552,1742,703],{"class":632},[552,1744,933],{"class":558},[552,1746,1011],{"class":632},[552,1748,1014],{"class":558},[552,1750,974],{"class":632},[552,1752,1019],{"class":558},[552,1754,984],{"class":632},[552,1756,1194],{"class":632},[552,1758,1759,1761,1763,1765,1767,1769,1771,1773,1775,1777,1779,1781,1783,1785,1787,1789,1791],{"class":554,"line":671},[552,1760,992],{"class":962},[552,1762,995],{"class":962},[552,1764,1099],{"class":699},[552,1766,690],{"class":632},[552,1768,1004],{"class":1003},[552,1770,703],{"class":632},[552,1772,933],{"class":558},[552,1774,855],{"class":632},[552,1776,1112],{"class":1003},[552,1778,703],{"class":632},[552,1780,928],{"class":558},[552,1782,1011],{"class":632},[552,1784,1014],{"class":558},[552,1786,974],{"class":632},[552,1788,1019],{"class":558},[552,1790,984],{"class":632},[552,1792,1194],{"class":632},[552,1794,1795,1797,1799,1801,1803,1805,1807,1809,1811,1813,1815,1817,1819],{"class":554,"line":678},[552,1796,992],{"class":962},[552,1798,995],{"class":962},[552,1800,1172],{"class":699},[552,1802,690],{"class":632},[552,1804,1004],{"class":1003},[552,1806,703],{"class":632},[552,1808,933],{"class":558},[552,1810,1011],{"class":632},[552,1812,1014],{"class":558},[552,1814,974],{"class":632},[552,1816,1019],{"class":558},[552,1818,984],{"class":632},[552,1820,1194],{"class":632},[552,1822,1823],{"class":554,"line":696},[552,1824,1200],{"class":632},[499,1826,1827],{},"The middleware is an ordinary provider, declared in the same module:",[542,1829,1832],{"className":611,"code":1830,"filename":1831,"language":614,"meta":548,"style":548},"import { Module } from \"@heximon\u002Fruntime\";\nimport { AuthMiddleware } from \".\u002Fauth.middleware\";\nimport { ChatSocket } from \".\u002Fchat.socket\";\n\nexport class ChatModule extends Module({\n  providers: [AuthMiddleware],\n  websocket: { handlers: [ChatSocket] },\n}) {}\n","src\u002Fchat\u002Fchat.module.ts",[503,1833,1834,1854,1876,1897,1901,1918,1930,1947],{"__ignoreMap":548},[552,1835,1836,1838,1840,1842,1844,1846,1848,1850,1852],{"class":554,"line":555},[552,1837,622],{"class":621},[552,1839,650],{"class":632},[552,1841,1236],{"class":625},[552,1843,656],{"class":632},[552,1845,659],{"class":621},[552,1847,633],{"class":632},[552,1849,1245],{"class":561},[552,1851,639],{"class":632},[552,1853,642],{"class":632},[552,1855,1856,1858,1860,1863,1865,1867,1869,1872,1874],{"class":554,"line":645},[552,1857,622],{"class":621},[552,1859,650],{"class":632},[552,1861,1862],{"class":625}," AuthMiddleware",[552,1864,656],{"class":632},[552,1866,659],{"class":621},[552,1868,633],{"class":632},[552,1870,1871],{"class":561},".\u002Fauth.middleware",[552,1873,639],{"class":632},[552,1875,642],{"class":632},[552,1877,1878,1880,1882,1884,1886,1888,1890,1893,1895],{"class":554,"line":671},[552,1879,622],{"class":621},[552,1881,650],{"class":632},[552,1883,1421],{"class":625},[552,1885,656],{"class":632},[552,1887,659],{"class":621},[552,1889,633],{"class":632},[552,1891,1892],{"class":561},".\u002Fchat.socket",[552,1894,639],{"class":632},[552,1896,642],{"class":632},[552,1898,1899],{"class":554,"line":678},[552,1900,675],{"emptyLinePlaceholder":674},[552,1902,1903,1905,1907,1910,1912,1914,1916],{"class":554,"line":696},[552,1904,681],{"class":621},[552,1906,963],{"class":962},[552,1908,1909],{"class":558}," ChatModule",[552,1911,1286],{"class":962},[552,1913,1236],{"class":687},[552,1915,690],{"class":625},[552,1917,693],{"class":632},[552,1919,1920,1923,1925,1928],{"class":554,"line":718},[552,1921,1922],{"class":699},"  providers",[552,1924,703],{"class":632},[552,1926,1927],{"class":625}," [AuthMiddleware]",[552,1929,715],{"class":632},[552,1931,1932,1934,1936,1938,1940,1942,1945],{"class":554,"line":740},[552,1933,1297],{"class":699},[552,1935,703],{"class":632},[552,1937,650],{"class":632},[552,1939,1304],{"class":699},[552,1941,703],{"class":632},[552,1943,1944],{"class":625}," [ChatSocket] ",[552,1946,1312],{"class":632},[552,1948,1949,1951,1953],{"class":554,"line":1092},[552,1950,743],{"class":632},[552,1952,1319],{"class":625},[552,1954,1322],{"class":632},[519,1956,1958],{"id":1957},"the-connection-port","The connection port",[499,1960,1961,1963],{},[503,1962,902],{}," is an abstract class, which makes it both a uniform API and a DI token your providers can\ninject. Its surface is cross-platform — the same code runs on Node and Cloudflare; only the implementation\ndiffers.",[1965,1966,1967,1981],"table",{},[1968,1969,1970],"thead",{},[1971,1972,1973,1978],"tr",{},[1974,1975,1977],"th",{"align":1976},"left","Member",[1974,1979,1980],{"align":1976},"Purpose",[1982,1983,1984,2003,2013,2026,2039],"tbody",{},[1971,1985,1986,1997],{},[1987,1988,1989,880,1991,880,1994],"td",{"align":1976},[503,1990,1073],{},[503,1992,1993],{},"request",[503,1995,1996],{},"topics",[1987,1998,1999,2000,2002],{"align":1976},"Connection identity, the upgrade ",[503,2001,1666],{},", the topics it's subscribed to",[1971,2004,2005,2010],{},[1987,2006,2007],{"align":1976},[503,2008,2009],{},"data: Map",[1987,2011,2012],{"align":1976},"A connection-stable bag, re-seeded into each frame so an upgrade-time principal is readable later",[1971,2014,2015,2020],{},[1987,2016,2017],{"align":1976},[503,2018,2019],{},"pathParams",[1987,2021,899,2022,2025],{"align":1976},[503,2023,2024],{},":segments"," of the upgrade path matched against the concrete request URL",[1971,2027,2028,2036],{},[1987,2029,2030,1641,2033],{"align":1976},[503,2031,2032],{},"send(data)",[503,2034,2035],{},"close(code?, reason?)",[1987,2037,2038],{"align":1976},"Write a frame \u002F close the socket",[1971,2040,2041,2052],{},[1987,2042,2043,1641,2046,1641,2049],{"align":1976},[503,2044,2045],{},"subscribe(topic)",[503,2047,2048],{},"unsubscribe(topic)",[503,2050,2051],{},"publish(topic, data)",[1987,2053,2054],{"align":1976},"Topic pub\u002Fsub fan-out",[499,2056,2057,2059,2060,1641,2062,1641,2064,2067,2068,2070,2071,2074,2075,2078,2079,2082,2083,537],{},[503,2058,2019],{}," is bound by the dispatcher before any lifecycle method runs, so ",[503,2061,906],{},[503,2063,910],{},[503,2065,2066],{},"close"," read\nit directly instead of hand-parsing ",[503,2069,1993],{},"'s URL — ",[503,2072,2073],{},"{ eventId: \"evt-42\" }"," for a ",[503,2076,2077],{},"\u002Fws\u002Fevents\u002F:eventId\u002Fseats","\nupgrade path, empty for a static path. On a contract-bound handler, ",[503,2080,2081],{},"TypedConnection.pathParams"," narrows this\nto the record of the contract's declared ",[503,2084,2024],{},[499,2086,2087,2088,2090],{},"It survives a Cloudflare hibernation wake too: each woken frame builds a fresh connection wrapper, and\n",[503,2089,2019],{}," is rebound against that frame's concrete request URL every time — so a keyed handler reads real\npath parameters on any frame, not only the one that opened it.",[519,2092,2094],{"id":2093},"broadcast-across-isolates-with-durable-objects","Broadcast across isolates with Durable Objects",[499,2096,2097,2099,2100,2103],{},[503,2098,2051],{}," fans out to every connection on that topic — but only within the isolate handling the\nsocket. On Cloudflare a single isolate can't reach connections in another, so a plain ",[503,2101,2102],{},"publish"," never broadcasts\nacross them.",[499,2105,2106,2107,2110,2111,2118,2119,2121],{},"To fan out for real, host the socket ",[1658,2108,2109],{},"inside"," a Durable Object via the\n",[2112,2113,2117],"a",{"href":2114,"rel":2115},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Fdurable-objects\u002Fapi\u002Fwebsockets\u002F",[2116],"nofollow","Hibernation API",": the DO becomes the one\nplace every connection in a room lives, so ",[503,2120,2102],{}," reaches all of them. Hibernation lets the DO be evicted\nbetween frames and rehydrated on the next, so an idle room costs nothing.",[2123,2124,2126,2127,2130,2131,2134,2135,2137,2138,2140,2141,537],"callout",{"type":2125},"info","A durable-hosted handler needs one more plugin: add ",[503,2128,2129],{},"new DurablePlugin()"," (from\n",[503,2132,2133],{},"@heximon\u002Fdurable\u002Fcompiler",") next to ",[503,2136,528],{}," in ",[503,2139,752],{}," — see\n",[2112,2142,289],{"href":290},[499,2144,2145,2146,2149,2150,2153,2154,2156,2157,2160,2161,2164,2165,2167,2168,2171],{},"Name ",[503,2147,2148],{},"durable: SomeDO"," in the config type argument, and supply a ",[503,2151,2152],{},"key"," selector — which DO instance the upgrade\nforwards into — as a ",[503,2155,2152],{}," class field. Here it returns ",[503,2158,2159],{},"roomId",", so each room is its own DO with isolated\nstate and its own pub\u002Fsub registry. The exported ",[503,2162,2163],{},"WebSocketKeySelector\u003CConfig>"," type annotates the field, so\nthe arrow's ",[503,2166,1993],{}," is typed from the config's ",[503,2169,2170],{},"path"," literal.",[542,2173,2175],{"className":611,"code":2174,"filename":1349,"language":614,"meta":548,"style":548},"import type {\n  Inbound,\n  WebSocketConnection,\n  WebSocketHandler,\n  WebSocketKeySelector,\n} from \"@heximon\u002Fwebsocket\";\nimport { ChatRoom } from \".\u002Fchat.room\";\n\nexport class ChatSocket implements WebSocketHandler\u003C{\n  path: \"\u002Fws\u002Fchat\u002F:roomId\";\n  durable: ChatRoom;\n}> {\n  \u002F\u002F `request.pathParams.roomId` is a typed string from the config's \u003Cpath>; one DO per room.\n  public readonly key: WebSocketKeySelector\u003C{ path: \"\u002Fws\u002Fchat\u002F:roomId\"; durable: ChatRoom }> = (\n    request,\n  ) => request.pathParams.roomId;\n\n  private static readonly topic: string = \"\u002Fws\u002Fchat\u002F:roomId\";\n\n  public async open(connection: WebSocketConnection): Promise\u003Cvoid> {\n    connection.subscribe(ChatSocket.topic);\n    connection.send(JSON.stringify({ type: \"welcome\", id: connection.id }));\n    connection.publish(ChatSocket.topic, JSON.stringify({ type: \"join\", id: connection.id }));\n  }\n\n  public async message(connection: WebSocketConnection, inbound: Inbound): Promise\u003Cvoid> {\n    const text = inbound.text();\n    connection.send(JSON.stringify({ type: \"echo\", text }));\n    connection.publish(ChatSocket.topic, JSON.stringify({ type: \"message\", id: connection.id, text }));\n  }\n\n  public async close(connection: WebSocketConnection): Promise\u003Cvoid> {\n    connection.publish(ChatSocket.topic, JSON.stringify({ type: \"leave\", id: connection.id }));\n  }\n}\n",[503,2176,2177,2185,2192,2199,2206,2213,2227,2249,2253,2268,2284,2295,2301,2306,2350,2357,2378,2383,2412,2417,2446,2470,2519,2578,2583,2588,2625,2645,2687,2748,2753,2758,2787,2845,2850],{"__ignoreMap":548},[552,2178,2179,2181,2183],{"class":554,"line":555},[552,2180,622],{"class":621},[552,2182,923],{"class":621},[552,2184,987],{"class":632},[552,2186,2187,2190],{"class":554,"line":645},[552,2188,2189],{"class":625},"  Inbound",[552,2191,715],{"class":632},[552,2193,2194,2197],{"class":554,"line":671},[552,2195,2196],{"class":625},"  WebSocketConnection",[552,2198,715],{"class":632},[552,2200,2201,2204],{"class":554,"line":678},[552,2202,2203],{"class":625},"  WebSocketHandler",[552,2205,715],{"class":632},[552,2207,2208,2211],{"class":554,"line":696},[552,2209,2210],{"class":625},"  WebSocketKeySelector",[552,2212,715],{"class":632},[552,2214,2215,2217,2219,2221,2223,2225],{"class":554,"line":718},[552,2216,743],{"class":632},[552,2218,659],{"class":621},[552,2220,633],{"class":632},[552,2222,947],{"class":561},[552,2224,639],{"class":632},[552,2226,642],{"class":632},[552,2228,2229,2231,2233,2236,2238,2240,2242,2245,2247],{"class":554,"line":740},[552,2230,622],{"class":621},[552,2232,650],{"class":632},[552,2234,2235],{"class":625}," ChatRoom",[552,2237,656],{"class":632},[552,2239,659],{"class":621},[552,2241,633],{"class":632},[552,2243,2244],{"class":561},".\u002Fchat.room",[552,2246,639],{"class":632},[552,2248,642],{"class":632},[552,2250,2251],{"class":554,"line":1092},[552,2252,675],{"emptyLinePlaceholder":674},[552,2254,2255,2257,2259,2261,2263,2265],{"class":554,"line":1131},[552,2256,681],{"class":621},[552,2258,963],{"class":962},[552,2260,1421],{"class":558},[552,2262,969],{"class":962},[552,2264,938],{"class":687},[552,2266,2267],{"class":632},"\u003C{\n",[552,2269,2270,2273,2275,2277,2280,2282],{"class":554,"line":1155},[552,2271,2272],{"class":699},"  path",[552,2274,703],{"class":632},[552,2276,633],{"class":632},[552,2278,2279],{"class":561},"\u002Fws\u002Fchat\u002F:roomId",[552,2281,639],{"class":632},[552,2283,642],{"class":632},[552,2285,2286,2289,2291,2293],{"class":554,"line":1160},[552,2287,2288],{"class":699},"  durable",[552,2290,703],{"class":632},[552,2292,2235],{"class":558},[552,2294,642],{"class":632},[552,2296,2297,2299],{"class":554,"line":1165},[552,2298,1726],{"class":632},[552,2300,987],{"class":632},[552,2302,2303],{"class":554,"line":1197},[552,2304,2305],{"class":1407},"  \u002F\u002F `request.pathParams.roomId` is a typed string from the config's \u003Cpath>; one DO per room.\n",[552,2307,2308,2310,2313,2316,2318,2321,2323,2325,2327,2329,2331,2333,2335,2338,2340,2342,2345,2347],{"class":554,"line":1595},[552,2309,992],{"class":962},[552,2311,2312],{"class":962}," readonly",[552,2314,2315],{"class":699}," key",[552,2317,703],{"class":632},[552,2319,2320],{"class":558}," WebSocketKeySelector",[552,2322,1503],{"class":632},[552,2324,1700],{"class":699},[552,2326,703],{"class":632},[552,2328,633],{"class":632},[552,2330,2279],{"class":561},[552,2332,639],{"class":632},[552,2334,1404],{"class":632},[552,2336,2337],{"class":699}," durable",[552,2339,703],{"class":632},[552,2341,2235],{"class":558},[552,2343,2344],{"class":632}," }>",[552,2346,1538],{"class":632},[552,2348,2349],{"class":625}," (\n",[552,2351,2352,2355],{"class":554,"line":1600},[552,2353,2354],{"class":1003},"    request",[552,2356,715],{"class":632},[552,2358,2359,2362,2365,2368,2370,2372,2374,2376],{"class":554,"line":1629},[552,2360,2361],{"class":625},"  ) ",[552,2363,2364],{"class":962},"=>",[552,2366,2367],{"class":625}," request",[552,2369,537],{"class":632},[552,2371,2019],{"class":625},[552,2373,537],{"class":632},[552,2375,2159],{"class":625},[552,2377,642],{"class":632},[552,2379,2381],{"class":554,"line":2380},17,[552,2382,675],{"emptyLinePlaceholder":674},[552,2384,2386,2389,2392,2394,2397,2399,2402,2404,2406,2408,2410],{"class":554,"line":2385},18,[552,2387,2388],{"class":962},"  private",[552,2390,2391],{"class":962}," static",[552,2393,2312],{"class":962},[552,2395,2396],{"class":699}," topic",[552,2398,703],{"class":632},[552,2400,2401],{"class":558}," string",[552,2403,1538],{"class":632},[552,2405,633],{"class":632},[552,2407,2279],{"class":561},[552,2409,639],{"class":632},[552,2411,642],{"class":632},[552,2413,2415],{"class":554,"line":2414},19,[552,2416,675],{"emptyLinePlaceholder":674},[552,2418,2420,2422,2424,2426,2428,2430,2432,2434,2436,2438,2440,2442,2444],{"class":554,"line":2419},20,[552,2421,992],{"class":962},[552,2423,995],{"class":962},[552,2425,998],{"class":699},[552,2427,690],{"class":632},[552,2429,1004],{"class":1003},[552,2431,703],{"class":632},[552,2433,933],{"class":558},[552,2435,1011],{"class":632},[552,2437,1014],{"class":558},[552,2439,974],{"class":632},[552,2441,1019],{"class":558},[552,2443,984],{"class":632},[552,2445,987],{"class":632},[552,2447,2449,2451,2453,2456,2458,2461,2463,2466,2468],{"class":554,"line":2448},21,[552,2450,1028],{"class":625},[552,2452,537],{"class":632},[552,2454,2455],{"class":687},"subscribe",[552,2457,690],{"class":699},[552,2459,2460],{"class":625},"ChatSocket",[552,2462,537],{"class":632},[552,2464,2465],{"class":625},"topic",[552,2467,746],{"class":699},[552,2469,642],{"class":632},[552,2471,2473,2475,2477,2479,2481,2483,2485,2487,2489,2491,2493,2495,2497,2499,2501,2503,2505,2507,2509,2511,2513,2515,2517],{"class":554,"line":2472},22,[552,2474,1028],{"class":625},[552,2476,537],{"class":632},[552,2478,1033],{"class":687},[552,2480,690],{"class":699},[552,2482,1038],{"class":625},[552,2484,537],{"class":632},[552,2486,1043],{"class":687},[552,2488,690],{"class":699},[552,2490,837],{"class":632},[552,2492,923],{"class":699},[552,2494,703],{"class":632},[552,2496,633],{"class":632},[552,2498,1056],{"class":561},[552,2500,639],{"class":632},[552,2502,855],{"class":632},[552,2504,1063],{"class":699},[552,2506,703],{"class":632},[552,2508,1068],{"class":625},[552,2510,537],{"class":632},[552,2512,1073],{"class":625},[552,2514,656],{"class":632},[552,2516,1078],{"class":699},[552,2518,642],{"class":632},[552,2520,2522,2524,2526,2528,2530,2532,2534,2536,2538,2541,2543,2545,2547,2549,2551,2553,2555,2558,2560,2562,2564,2566,2568,2570,2572,2574,2576],{"class":554,"line":2521},23,[552,2523,1028],{"class":625},[552,2525,537],{"class":632},[552,2527,2102],{"class":687},[552,2529,690],{"class":699},[552,2531,2460],{"class":625},[552,2533,537],{"class":632},[552,2535,2465],{"class":625},[552,2537,855],{"class":632},[552,2539,2540],{"class":625}," JSON",[552,2542,537],{"class":632},[552,2544,1043],{"class":687},[552,2546,690],{"class":699},[552,2548,837],{"class":632},[552,2550,923],{"class":699},[552,2552,703],{"class":632},[552,2554,633],{"class":632},[552,2556,2557],{"class":561},"join",[552,2559,639],{"class":632},[552,2561,855],{"class":632},[552,2563,1063],{"class":699},[552,2565,703],{"class":632},[552,2567,1068],{"class":625},[552,2569,537],{"class":632},[552,2571,1073],{"class":625},[552,2573,656],{"class":632},[552,2575,1078],{"class":699},[552,2577,642],{"class":632},[552,2579,2581],{"class":554,"line":2580},24,[552,2582,1085],{"class":632},[552,2584,2586],{"class":554,"line":2585},25,[552,2587,675],{"emptyLinePlaceholder":674},[552,2589,2591,2593,2595,2597,2599,2601,2603,2605,2607,2609,2611,2613,2615,2617,2619,2621,2623],{"class":554,"line":2590},26,[552,2592,992],{"class":962},[552,2594,995],{"class":962},[552,2596,1099],{"class":699},[552,2598,690],{"class":632},[552,2600,1004],{"class":1003},[552,2602,703],{"class":632},[552,2604,933],{"class":558},[552,2606,855],{"class":632},[552,2608,1112],{"class":1003},[552,2610,703],{"class":632},[552,2612,928],{"class":558},[552,2614,1011],{"class":632},[552,2616,1014],{"class":558},[552,2618,974],{"class":632},[552,2620,1019],{"class":558},[552,2622,984],{"class":632},[552,2624,987],{"class":632},[552,2626,2628,2630,2633,2635,2637,2639,2641,2643],{"class":554,"line":2627},27,[552,2629,1533],{"class":962},[552,2631,2632],{"class":625}," text",[552,2634,1538],{"class":632},[552,2636,1112],{"class":625},[552,2638,537],{"class":632},[552,2640,1147],{"class":687},[552,2642,852],{"class":699},[552,2644,642],{"class":632},[552,2646,2648,2650,2652,2654,2656,2658,2660,2662,2664,2666,2668,2670,2672,2675,2677,2679,2681,2683,2685],{"class":554,"line":2647},28,[552,2649,1028],{"class":625},[552,2651,537],{"class":632},[552,2653,1033],{"class":687},[552,2655,690],{"class":699},[552,2657,1038],{"class":625},[552,2659,537],{"class":632},[552,2661,1043],{"class":687},[552,2663,690],{"class":699},[552,2665,837],{"class":632},[552,2667,923],{"class":699},[552,2669,703],{"class":632},[552,2671,633],{"class":632},[552,2673,2674],{"class":561},"echo",[552,2676,639],{"class":632},[552,2678,855],{"class":632},[552,2680,2632],{"class":625},[552,2682,656],{"class":632},[552,2684,1078],{"class":699},[552,2686,642],{"class":632},[552,2688,2690,2692,2694,2696,2698,2700,2702,2704,2706,2708,2710,2712,2714,2716,2718,2720,2722,2724,2726,2728,2730,2732,2734,2736,2738,2740,2742,2744,2746],{"class":554,"line":2689},29,[552,2691,1028],{"class":625},[552,2693,537],{"class":632},[552,2695,2102],{"class":687},[552,2697,690],{"class":699},[552,2699,2460],{"class":625},[552,2701,537],{"class":632},[552,2703,2465],{"class":625},[552,2705,855],{"class":632},[552,2707,2540],{"class":625},[552,2709,537],{"class":632},[552,2711,1043],{"class":687},[552,2713,690],{"class":699},[552,2715,837],{"class":632},[552,2717,923],{"class":699},[552,2719,703],{"class":632},[552,2721,633],{"class":632},[552,2723,910],{"class":561},[552,2725,639],{"class":632},[552,2727,855],{"class":632},[552,2729,1063],{"class":699},[552,2731,703],{"class":632},[552,2733,1068],{"class":625},[552,2735,537],{"class":632},[552,2737,1073],{"class":625},[552,2739,855],{"class":632},[552,2741,2632],{"class":625},[552,2743,656],{"class":632},[552,2745,1078],{"class":699},[552,2747,642],{"class":632},[552,2749,2751],{"class":554,"line":2750},30,[552,2752,1085],{"class":632},[552,2754,2756],{"class":554,"line":2755},31,[552,2757,675],{"emptyLinePlaceholder":674},[552,2759,2761,2763,2765,2767,2769,2771,2773,2775,2777,2779,2781,2783,2785],{"class":554,"line":2760},32,[552,2762,992],{"class":962},[552,2764,995],{"class":962},[552,2766,1172],{"class":699},[552,2768,690],{"class":632},[552,2770,1004],{"class":1003},[552,2772,703],{"class":632},[552,2774,933],{"class":558},[552,2776,1011],{"class":632},[552,2778,1014],{"class":558},[552,2780,974],{"class":632},[552,2782,1019],{"class":558},[552,2784,984],{"class":632},[552,2786,987],{"class":632},[552,2788,2790,2792,2794,2796,2798,2800,2802,2804,2806,2808,2810,2812,2814,2816,2818,2820,2822,2825,2827,2829,2831,2833,2835,2837,2839,2841,2843],{"class":554,"line":2789},33,[552,2791,1028],{"class":625},[552,2793,537],{"class":632},[552,2795,2102],{"class":687},[552,2797,690],{"class":699},[552,2799,2460],{"class":625},[552,2801,537],{"class":632},[552,2803,2465],{"class":625},[552,2805,855],{"class":632},[552,2807,2540],{"class":625},[552,2809,537],{"class":632},[552,2811,1043],{"class":687},[552,2813,690],{"class":699},[552,2815,837],{"class":632},[552,2817,923],{"class":699},[552,2819,703],{"class":632},[552,2821,633],{"class":632},[552,2823,2824],{"class":561},"leave",[552,2826,639],{"class":632},[552,2828,855],{"class":632},[552,2830,1063],{"class":699},[552,2832,703],{"class":632},[552,2834,1068],{"class":625},[552,2836,537],{"class":632},[552,2838,1073],{"class":625},[552,2840,656],{"class":632},[552,2842,1078],{"class":699},[552,2844,642],{"class":632},[552,2846,2848],{"class":554,"line":2847},34,[552,2849,1085],{"class":632},[552,2851,2853],{"class":554,"line":2852},35,[552,2854,1200],{"class":632},[499,2856,2857,2858,2861,2862,2865,2866,2869],{},"The host DO declares ",[503,2859,2860],{},"DurableObject\u003C{ hibernate: true }>"," and ",[1658,2863,2864],{},"no"," ",[503,2867,2868],{},"webSocket*"," method of its own —\nthe compiler-emitted glue routes hibernation frames straight onto your handler. The DO is free to keep its own\nstate and RPC surface (here a connection count) for code that queries the room directly.",[542,2871,2874],{"className":611,"code":2872,"filename":2873,"language":614,"meta":548,"style":548},"import type { DurableObject } from \"@heximon\u002Fdurable\";\n\nexport class ChatRoom implements DurableObject\u003C{ hibernate: true }> {\n  private opened = 0;\n\n  public join(): Promise\u003Cnumber> {\n    this.opened += 1;\n    return Promise.resolve(this.opened);\n  }\n\n  public roomSize(): Promise\u003Cnumber> {\n    return Promise.resolve(this.opened);\n  }\n}\n","src\u002Fchat\u002Fchat.room.ts",[503,2875,2876,2900,2904,2930,2944,2948,2969,2985,3008,3012,3016,3035,3055,3059],{"__ignoreMap":548},[552,2877,2878,2880,2882,2884,2887,2889,2891,2893,2896,2898],{"class":554,"line":555},[552,2879,622],{"class":621},[552,2881,923],{"class":621},[552,2883,650],{"class":632},[552,2885,2886],{"class":625}," DurableObject",[552,2888,656],{"class":632},[552,2890,659],{"class":621},[552,2892,633],{"class":632},[552,2894,2895],{"class":561},"@heximon\u002Fdurable",[552,2897,639],{"class":632},[552,2899,642],{"class":632},[552,2901,2902],{"class":554,"line":645},[552,2903,675],{"emptyLinePlaceholder":674},[552,2905,2906,2908,2910,2912,2914,2916,2918,2921,2923,2926,2928],{"class":554,"line":671},[552,2907,681],{"class":621},[552,2909,963],{"class":962},[552,2911,2235],{"class":558},[552,2913,969],{"class":962},[552,2915,2886],{"class":558},[552,2917,1503],{"class":632},[552,2919,2920],{"class":699}," hibernate",[552,2922,703],{"class":632},[552,2924,2925],{"class":558}," true",[552,2927,2344],{"class":632},[552,2929,987],{"class":632},[552,2931,2932,2934,2937,2939,2942],{"class":554,"line":678},[552,2933,2388],{"class":962},[552,2935,2936],{"class":699}," opened",[552,2938,1538],{"class":632},[552,2940,2941],{"class":733}," 0",[552,2943,642],{"class":632},[552,2945,2946],{"class":554,"line":696},[552,2947,675],{"emptyLinePlaceholder":674},[552,2949,2950,2952,2955,2958,2960,2962,2965,2967],{"class":554,"line":718},[552,2951,992],{"class":962},[552,2953,2954],{"class":699}," join",[552,2956,2957],{"class":632},"():",[552,2959,1014],{"class":558},[552,2961,974],{"class":632},[552,2963,2964],{"class":558},"number",[552,2966,984],{"class":632},[552,2968,987],{"class":632},[552,2970,2971,2974,2977,2980,2983],{"class":554,"line":740},[552,2972,2973],{"class":632},"    this.",[552,2975,2976],{"class":625},"opened",[552,2978,2979],{"class":632}," +=",[552,2981,2982],{"class":733}," 1",[552,2984,642],{"class":632},[552,2986,2987,2990,2992,2994,2997,2999,3002,3004,3006],{"class":554,"line":1092},[552,2988,2989],{"class":621},"    return",[552,2991,1014],{"class":558},[552,2993,537],{"class":632},[552,2995,2996],{"class":687},"resolve",[552,2998,690],{"class":699},[552,3000,3001],{"class":632},"this.",[552,3003,2976],{"class":625},[552,3005,746],{"class":699},[552,3007,642],{"class":632},[552,3009,3010],{"class":554,"line":1131},[552,3011,1085],{"class":632},[552,3013,3014],{"class":554,"line":1155},[552,3015,675],{"emptyLinePlaceholder":674},[552,3017,3018,3020,3023,3025,3027,3029,3031,3033],{"class":554,"line":1160},[552,3019,992],{"class":962},[552,3021,3022],{"class":699}," roomSize",[552,3024,2957],{"class":632},[552,3026,1014],{"class":558},[552,3028,974],{"class":632},[552,3030,2964],{"class":558},[552,3032,984],{"class":632},[552,3034,987],{"class":632},[552,3036,3037,3039,3041,3043,3045,3047,3049,3051,3053],{"class":554,"line":1165},[552,3038,2989],{"class":621},[552,3040,1014],{"class":558},[552,3042,537],{"class":632},[552,3044,2996],{"class":687},[552,3046,690],{"class":699},[552,3048,3001],{"class":632},[552,3050,2976],{"class":625},[552,3052,746],{"class":699},[552,3054,642],{"class":632},[552,3056,3057],{"class":554,"line":1197},[552,3058,1085],{"class":632},[552,3060,3061],{"class":554,"line":1595},[552,3062,1200],{"class":632},[499,3064,899,3065,3067,3068,3071,3072,3074],{},[503,3066,2976],{}," counter is illustrative — a plain field resets when a hibernating DO is evicted and\nrehydrated. State that must survive hibernation goes through ",[503,3069,3070],{},"DurableContext.storage"," (the pattern\n",[2112,3073,289],{"href":290}," shows).",[499,3076,3077,3078,3081,3082,3085,3086,3089,3090,3092,3093,3095,3096,3099],{},"Declare the DO under ",[503,3079,3080],{},"durable: { objects }"," and the socket under ",[503,3083,3084],{},"websocket: { handlers }"," in the same module.\nThe compiler sees that the socket names ",[503,3087,3088],{},"ChatRoom",", threads the hibernation host into the room, and keys the\nupgrade by ",[503,3091,2159],{}," — and because a hibernating socket needs a hibernating host, it rejects the wiring at build\ntime if ",[503,3094,3088],{}," is missing ",[503,3097,3098],{},"hibernate: true"," rather than emitting a host that can't accept the socket.",[499,3101,899,3102,3105,3106,3108,3109,3111,3112,3115,3116,3119,3120,3122],{},[503,3103,3104],{},"durable:"," target must be a discovered ",[503,3107,2860],{},", checked when you compile; the\n",[503,3110,2152],{}," selector field is a runtime value the durable forward reads off the resolved handler instance (on an\n",[503,3113,3114],{},"extends WebSocketHandler\u003C…>"," handler, the same selector is passed via ",[503,3117,3118],{},"super((request) => …)"," instead —\nthere the arrow's ",[503,3121,1993],{}," is typed with no annotation).",[542,3124,3126],{"className":611,"code":3125,"filename":1831,"language":614,"meta":548,"style":548},"import { Module } from \"@heximon\u002Fruntime\";\nimport { ChatRoom } from \".\u002Fchat.room\";\nimport { ChatSocket } from \".\u002Fchat.socket\";\n\nexport class ChatModule extends Module({\n  durable: { objects: [ChatRoom] },\n  websocket: { handlers: [ChatSocket] },\n}) {}\n",[503,3127,3128,3148,3168,3188,3192,3208,3226,3242],{"__ignoreMap":548},[552,3129,3130,3132,3134,3136,3138,3140,3142,3144,3146],{"class":554,"line":555},[552,3131,622],{"class":621},[552,3133,650],{"class":632},[552,3135,1236],{"class":625},[552,3137,656],{"class":632},[552,3139,659],{"class":621},[552,3141,633],{"class":632},[552,3143,1245],{"class":561},[552,3145,639],{"class":632},[552,3147,642],{"class":632},[552,3149,3150,3152,3154,3156,3158,3160,3162,3164,3166],{"class":554,"line":645},[552,3151,622],{"class":621},[552,3153,650],{"class":632},[552,3155,2235],{"class":625},[552,3157,656],{"class":632},[552,3159,659],{"class":621},[552,3161,633],{"class":632},[552,3163,2244],{"class":561},[552,3165,639],{"class":632},[552,3167,642],{"class":632},[552,3169,3170,3172,3174,3176,3178,3180,3182,3184,3186],{"class":554,"line":671},[552,3171,622],{"class":621},[552,3173,650],{"class":632},[552,3175,1421],{"class":625},[552,3177,656],{"class":632},[552,3179,659],{"class":621},[552,3181,633],{"class":632},[552,3183,1892],{"class":561},[552,3185,639],{"class":632},[552,3187,642],{"class":632},[552,3189,3190],{"class":554,"line":678},[552,3191,675],{"emptyLinePlaceholder":674},[552,3193,3194,3196,3198,3200,3202,3204,3206],{"class":554,"line":696},[552,3195,681],{"class":621},[552,3197,963],{"class":962},[552,3199,1909],{"class":558},[552,3201,1286],{"class":962},[552,3203,1236],{"class":687},[552,3205,690],{"class":625},[552,3207,693],{"class":632},[552,3209,3210,3212,3214,3216,3219,3221,3224],{"class":554,"line":718},[552,3211,2288],{"class":699},[552,3213,703],{"class":632},[552,3215,650],{"class":632},[552,3217,3218],{"class":699}," objects",[552,3220,703],{"class":632},[552,3222,3223],{"class":625}," [ChatRoom] ",[552,3225,1312],{"class":632},[552,3227,3228,3230,3232,3234,3236,3238,3240],{"class":554,"line":740},[552,3229,1297],{"class":699},[552,3231,703],{"class":632},[552,3233,650],{"class":632},[552,3235,1304],{"class":699},[552,3237,703],{"class":632},[552,3239,1944],{"class":625},[552,3241,1312],{"class":632},[552,3243,3244,3246,3248],{"class":554,"line":1092},[552,3245,743],{"class":632},[552,3247,1319],{"class":625},[552,3249,1322],{"class":632},[519,3251,3253],{"id":3252},"platform-behavior","Platform behavior",[499,3255,3256,3257,1641,3259,1641,3261,3263],{},"The same handler runs on Node and Cloudflare; the realtime semantics differ only where the runtime forces them.\nNode development has no Durable Objects, so a durable-backed socket degrades to an in-process connection:\n",[503,3258,906],{},[503,3260,910],{},[503,3262,2066],{}," still run, with no DO round-trip.",[499,3265,3266,3267,3270,3271,3273,3274,3276],{},"The per-room key, the ",[503,3268,3269],{},":roomId"," route, and the live echo all work under ",[503,3272,536],{}," — only true cross-isolate\nhibernation is Cloudflare-only, so reach for ",[503,3275,3104],{}," exactly when a room needs to broadcast for real.",[499,3278,3279,3280,3283,3284,3287,3288,3290],{},"A Cloudflare app (",[503,3281,3282],{},"platform: new CloudflareWorkersStrategy()",") skips that degradation entirely: its ",[503,3285,3286],{},"vp dev","\nruns the worker in real workerd (Miniflare), and the dev server splices each ",[503,3289,532],{}," upgrade byte-for-byte onto\nthe worker's own socket — a durable-backed room connects to its real Durable Object in dev, hibernation\nsemantics included, before any deploy.",[499,3292,3293,3294,3297,3298,3301,3302,3305],{},"On the Cloudflare hibernation path, connection-stable state rides the socket's platform ",[1658,3295,3296],{},"attachment"," (the\none thing that survives DO eviction): ",[503,3299,3300],{},"connection.request"," carries the concrete upgrade URL on every frame\n(so a keyed handler reads its real path parameters, same as on Node), and the string-keyed ",[503,3303,3304],{},"connection.data","\nentries persist across frames and eviction.",[499,3307,3308],{},"Two platform limits apply — symbol-keyed entries stay process-local (they cannot serialize), and Cloudflare\ncaps the serialized attachment at 2 KiB, so stash small lookups (an id, a capacity, a principal), not documents.",[519,3310,3312],{"id":3311},"add-a-typed-contract-when-the-shape-matters","Add a typed contract when the shape matters",[499,3314,3315],{},"A plain handler sends and receives untyped frames — every example above works exactly as shown, with no\ncontract at all.",[499,3317,3318,3319,3321,3322,3325],{},"Once your protocol has a fixed vocabulary of message kinds worth getting wrong at compile time instead of in\nproduction, ",[2112,3320,271],{"href":272}," layers a validated, typed protocol on\ntop: a ",[503,3323,3324],{},"WebSocketContract"," class, per-kind method dispatch, and a browser client that validates against the\nsame schemas.",[519,3327,3329],{"id":3328},"see-also","See also",[3331,3332,3333,3342,3351,3356,3361],"ul",{},[3334,3335,3336,3338,3339,3341],"li",{},[2112,3337,271],{"href":272}," — the opt-in typed layer: a ",[503,3340,3324],{},"\nclass, per-kind message dispatch, broadcast transports, and the typed browser client.",[3334,3343,3344,3346,3347,3350],{},[2112,3345,289],{"href":290}," — author the ",[503,3348,3349],{},"DurableObject"," that hosts a hibernating socket\nand addresses a room by name over its RPC surface.",[3334,3352,3353,3355],{},[2112,3354,275],{"href":276}," — when you only need a one-way push channel, stream\ntyped events instead of opening a duplex socket.",[3334,3357,3358,3360],{},[2112,3359,332],{"href":333}," — ship the generated upgrade routes to a Nitro host or Cloudflare Workers.",[3334,3362,3363,3370,3371,3373,3374,3376,3377,3380],{},[2112,3364,3367],{"href":3365,"rel":3366},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fflagship\u002Fapps\u002Fcloudflare",[2116],[1658,3368,3369],{},"The flagship Cloudflare app","\n— a seat-map socket hosted on a Durable Object, broadcasting across connections under ",[503,3372,536],{},", bound to a\n",[503,3375,3324],{},", with a ",[503,3378,3379],{},"WsClient"," driver test proving the typed round-trip and both validation failure\npaths.",[3382,3383,3384],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}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 pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}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":548,"searchDepth":645,"depth":645,"links":3386},[3387,3388,3389,3390,3391,3392,3393,3394,3395,3396],{"id":521,"depth":645,"text":522},{"id":872,"depth":645,"text":873},{"id":1213,"depth":645,"text":1214},{"id":1325,"depth":645,"text":1326},{"id":1648,"depth":645,"text":1649},{"id":1957,"depth":645,"text":1958},{"id":2093,"depth":645,"text":2094},{"id":3252,"depth":645,"text":3253},{"id":3311,"depth":645,"text":3312},{"id":3328,"depth":645,"text":3329},"Bidirectional realtime over a WebSocketHandler whose upgrade path is its type argument, with inbound-frame validation, auth-at-upgrade middleware, and Durable Object hibernation for cross-isolate broadcast.","md",null,{},{"icon":129},{"title":267,"description":3397},"7Y7XuU2hq0_IEb5JZoeyHfOh2Dr2_RIbEi7UqripXlU",[3405,3407],{"title":142,"path":264,"stem":265,"description":3406,"icon":259,"children":-1},"Choosing between EventStream server-sent events and a WebSocketHandler socket, the opt-in typed layer over WebSocketContract and ServerSentEvents classes, and when Durable Objects earn their keep for cross-isolate broadcast.",{"title":271,"path":272,"stem":273,"description":3408,"icon":70,"children":-1},"A typed WebSocketContract class with per-kind WebSocketMessage dispatch, TypedConnection validation, broadcast transports, and a matching browser client, layered on top of a plain WebSocketHandler.",1783300761903]