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