[{"data":1,"prerenderedAt":1129},["ShallowReactive",2],{"navigation_docs":3,"-docs-realtime-overview":493,"-docs-realtime-overview-surround":1124},[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":142,"body":495,"description":1117,"extension":1118,"links":1119,"meta":1120,"navigation":1121,"path":264,"seo":1122,"stem":265,"__hash__":1123},"docs\u002Fdocs\u002F05.realtime\u002F01.overview.md",{"type":496,"value":497,"toc":1109},"minimark",[498,502,507,526,851,854,858,861,951,959,969,973,984,987,1008,1012,1030,1033,1040,1044,1057,1061,1105],[499,500,501],"p",{},"Some responses can't be a single JSON payload — the server has more to say after it's already replied, or\nthe client needs to talk back on the same connection. Heximon covers both with a handler class and one\nconfig key: no separate server to run, no Durable Object required to get started.",[503,504,506],"h2",{"id":505},"start-with-plain-node-no-durable-objects-needed","Start with plain Node — no Durable Objects needed",[499,508,509,510,514,515,514,518,521,522,525],{},"A server-sent-events stream is a value you return from a controller, and a WebSocket handler is a class with\n",[511,512,513],"code",{},"open","\u002F",[511,516,517],{},"message",[511,519,520],{},"close"," methods bound to a path. Both run on the same ",[511,523,524],{},"fetch"," host your HTTP routes already\nuse, and both work fully in-process on Node: one server, one port, real bidirectional or push traffic, zero\nplatform-specific concepts.",[527,528,534],"pre",{"className":529,"code":530,"filename":531,"language":532,"meta":533,"style":533},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","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","ts","",[511,535,536,586,593,628,668,729,735,740,779,803,808,813,845],{"__ignoreMap":533},[537,538,541,545,548,552,556,559,562,564,567,570,573,576,580,583],"span",{"class":539,"line":540},"line",1,[537,542,544],{"class":543},"s7zQu","import",[537,546,547],{"class":543}," type",[537,549,551],{"class":550},"sMK4o"," {",[537,553,555],{"class":554},"sTEyZ"," Inbound",[537,557,558],{"class":550},",",[537,560,561],{"class":554}," WebSocketConnection",[537,563,558],{"class":550},[537,565,566],{"class":554}," WebSocketHandler",[537,568,569],{"class":550}," }",[537,571,572],{"class":543}," from",[537,574,575],{"class":550}," \"",[537,577,579],{"class":578},"sfazB","@heximon\u002Fwebsocket",[537,581,582],{"class":550},"\"",[537,584,585],{"class":550},";\n",[537,587,589],{"class":539,"line":588},2,[537,590,592],{"emptyLinePlaceholder":591},true,"\n",[537,594,596,599,603,607,610,612,615,617,620,622,625],{"class":539,"line":595},3,[537,597,598],{"class":543},"export",[537,600,602],{"class":601},"spNyl"," class",[537,604,606],{"class":605},"sBMFI"," EchoSocket",[537,608,609],{"class":601}," implements",[537,611,566],{"class":605},[537,613,614],{"class":550},"\u003C",[537,616,582],{"class":550},[537,618,619],{"class":578},"\u002Fws\u002Fecho",[537,621,582],{"class":550},[537,623,624],{"class":550},">",[537,626,627],{"class":550}," {\n",[537,629,631,634,637,641,644,648,651,653,656,659,661,664,666],{"class":539,"line":630},4,[537,632,633],{"class":601},"  public",[537,635,636],{"class":601}," async",[537,638,640],{"class":639},"swJcz"," open",[537,642,643],{"class":550},"(",[537,645,647],{"class":646},"sHdIc","connection",[537,649,650],{"class":550},":",[537,652,561],{"class":605},[537,654,655],{"class":550},"):",[537,657,658],{"class":605}," Promise",[537,660,614],{"class":550},[537,662,663],{"class":605},"void",[537,665,624],{"class":550},[537,667,627],{"class":550},[537,669,671,674,677,681,683,686,688,691,693,696,698,700,702,705,707,709,712,714,717,719,722,724,727],{"class":539,"line":670},5,[537,672,673],{"class":554},"    connection",[537,675,676],{"class":550},".",[537,678,680],{"class":679},"s2Zo4","send",[537,682,643],{"class":639},[537,684,685],{"class":554},"JSON",[537,687,676],{"class":550},[537,689,690],{"class":679},"stringify",[537,692,643],{"class":639},[537,694,695],{"class":550},"{",[537,697,547],{"class":639},[537,699,650],{"class":550},[537,701,575],{"class":550},[537,703,704],{"class":578},"welcome",[537,706,582],{"class":550},[537,708,558],{"class":550},[537,710,711],{"class":639}," id",[537,713,650],{"class":550},[537,715,716],{"class":554}," connection",[537,718,676],{"class":550},[537,720,721],{"class":554},"id",[537,723,569],{"class":550},[537,725,726],{"class":639},"))",[537,728,585],{"class":550},[537,730,732],{"class":539,"line":731},6,[537,733,734],{"class":550},"  }\n",[537,736,738],{"class":539,"line":737},7,[537,739,592],{"emptyLinePlaceholder":591},[537,741,743,745,747,750,752,754,756,758,760,763,765,767,769,771,773,775,777],{"class":539,"line":742},8,[537,744,633],{"class":601},[537,746,636],{"class":601},[537,748,749],{"class":639}," message",[537,751,643],{"class":550},[537,753,647],{"class":646},[537,755,650],{"class":550},[537,757,561],{"class":605},[537,759,558],{"class":550},[537,761,762],{"class":646}," inbound",[537,764,650],{"class":550},[537,766,555],{"class":605},[537,768,655],{"class":550},[537,770,658],{"class":605},[537,772,614],{"class":550},[537,774,663],{"class":605},[537,776,624],{"class":550},[537,778,627],{"class":550},[537,780,782,784,786,788,790,793,795,798,801],{"class":539,"line":781},9,[537,783,673],{"class":554},[537,785,676],{"class":550},[537,787,680],{"class":679},[537,789,643],{"class":639},[537,791,792],{"class":554},"inbound",[537,794,676],{"class":550},[537,796,797],{"class":679},"text",[537,799,800],{"class":639},"())",[537,802,585],{"class":550},[537,804,806],{"class":539,"line":805},10,[537,807,734],{"class":550},[537,809,811],{"class":539,"line":810},11,[537,812,592],{"emptyLinePlaceholder":591},[537,814,816,818,820,823,825,828,830,832,834,836,838,840,842],{"class":539,"line":815},12,[537,817,633],{"class":601},[537,819,636],{"class":601},[537,821,822],{"class":639}," close",[537,824,643],{"class":550},[537,826,827],{"class":646},"_connection",[537,829,650],{"class":550},[537,831,561],{"class":605},[537,833,655],{"class":550},[537,835,658],{"class":605},[537,837,614],{"class":550},[537,839,663],{"class":605},[537,841,624],{"class":550},[537,843,844],{"class":550}," {}\n",[537,846,848],{"class":539,"line":847},13,[537,849,850],{"class":550},"}\n",[499,852,853],{},"That's the entire mental model for a single-server app: pick a transport, author the handler, list it in\nyour module. Everything past this point is opt-in, and each tier only matters once your app actually needs\nit.",[503,855,857],{"id":856},"sse-or-websockets","SSE or WebSockets?",[499,859,860],{},"The two transports solve different shapes of \"the response isn't one message.\" Pick by whether the client\never needs to talk back on the same connection.",[862,863,864,878],"table",{},[865,866,867],"thead",{},[868,869,870,874,876],"tr",{},[871,872],"th",{"align":873},"left",[871,875,275],{"align":873},[871,877,267],{"align":873},[879,880,881,893,908,926,940],"tbody",{},[868,882,883,887,890],{},[884,885,886],"td",{"align":873},"Direction",[884,888,889],{"align":873},"Server → client only",[884,891,892],{"align":873},"Bidirectional",[868,894,895,898,905],{},[884,896,897],{"align":873},"Protocol",[884,899,900,901,904],{"align":873},"Plain HTTP (",[511,902,903],{},"text\u002Fevent-stream",")",[884,906,907],{"align":873},"Upgraded connection",[868,909,910,913,920],{},[884,911,912],{"align":873},"Browser API",[884,914,915,916,919],{"align":873},"Built-in ",[511,917,918],{},"EventSource",", auto-reconnect",[884,921,922,925],{"align":873},[511,923,924],{},"WebSocket",", you handle reconnect",[868,927,928,931,937],{},[884,929,930],{"align":873},"Resume after a drop",[884,932,933,936],{"align":873},[511,934,935],{},"Last-Event-ID",", built in",[884,938,939],{"align":873},"Nothing built in — reconnect and re-sync yourself",[868,941,942,945,948],{},[884,943,944],{"align":873},"Reach for it when",[884,946,947],{"align":873},"Progress updates, notifications, live feeds",[884,949,950],{"align":873},"Chat, presence, collaborative editing, anything the client sends often",[499,952,953,954,958],{},"If the client only ever ",[955,956,957],"em",{},"receives",", SSE is less to build and gives you resume for free. The moment the\nclient needs to send more than an occasional request, reach for a socket instead.",[499,960,961,962,965,966,968],{},"See ",[963,964,275],"a",{"href":276}," and ",[963,967,267],{"href":268}," for\nthe full handler walkthrough — validation, auth at upgrade, and the module wiring.",[503,970,972],{"id":971},"add-a-typed-contract-when-the-shape-matters","Add a typed contract when the shape matters",[499,974,975,976,979,980,983],{},"A plain socket sends and receives untyped frames; a plain stream sends untyped events. Once your protocol has\na fixed vocabulary of message kinds worth getting wrong at compile time instead of in production, declare it\nas a class — a ",[511,977,978],{},"WebSocketContract({ clientToServer, serverToClient })"," for a socket, or a\n",[511,981,982],{},"ServerSentEvents({ events })"," map for a stream — and both your server and a matching frontend client\nvalidate against the same schemas.",[499,985,986],{},"This is a layer on top, not a prerequisite: every example above works with no contract at all.",[499,988,961,989,991,992,995,996,999,1000,1003,1004,1007],{},[963,990,271],{"href":272}," for binding a ",[511,993,994],{},"WebSocketContract"," to a socket\nhandler, and ",[963,997,998],{"href":281},"Typed clients"," for the frontend-safe ",[511,1001,1002],{},"WsClient"," \u002F ",[511,1005,1006],{},"SseClient"," pair.",[503,1009,1011],{"id":1010},"durable-objects-the-cloudflare-scale-out-story","Durable Objects: the Cloudflare scale-out story",[499,1013,1014,1015,1018,1019,1022,1023,1025,1026,1029],{},"Everything so far runs in one process. That's fine until clients of the same room or topic land on\n",[955,1016,1017],{},"different"," isolates on Cloudflare — at that point no single process holds every connection, so a plain\n",[511,1020,1021],{},"publish"," or broadcast can't reach them all. ",[963,1024,289],{"href":290}," solve that by\ngiving one room a single, addressable, stateful home: a ",[511,1027,1028],{},"DurableObject"," holds every connection for a topic,\nregardless of which isolate accepted them.",[499,1031,1032],{},"You may never need this. It's a Cloudflare-specific mechanism for cross-isolate fan-out, not a requirement\nfor realtime in general — a single Node server never has this problem, because one process already holds\nevery connection. When you don't need cross-isolate broadcast, skip this tier entirely.",[499,1034,1035,1036,1039],{},"When you do, ",[511,1037,1038],{},"pnpm dev"," still runs a faithful in-process shim, so you build and test the whole flow before\nyou ever touch real Cloudflare infrastructure.",[503,1041,1043],{"id":1042},"documenting-it-asyncapi","Documenting it: AsyncAPI",[499,1045,1046,1047,1049,1050,1053,1054,1056],{},"Once you've described your realtime API as a ",[511,1048,994],{}," or a ",[511,1051,1052],{},"ServerSentEvents"," map, that\ndescription doubles as documentation: ",[963,1055,285],{"href":286}," generates a standards-compliant\ndocument straight from the same classes, so the docs can't drift from what the wire actually validates.",[503,1058,1060],{"id":1059},"next-steps","Next steps",[1062,1063,1064,1070,1078,1086,1095,1100],"ul",{},[1065,1066,1067,1069],"li",{},[963,1068,267],{"href":268}," — author a bidirectional handler, validate inbound frames, and\nauthenticate at upgrade.",[1065,1071,1072,1074,1075,1077],{},[963,1073,271],{"href":272}," — the opt-in typed layer: a ",[511,1076,994],{},"\nclass with per-kind message dispatch and broadcast transports.",[1065,1079,1080,1082,1083,1085],{},[963,1081,275],{"href":276}," — stream typed events with heartbeats and\n",[511,1084,935],{}," resume.",[1065,1087,1088,1090,1091,1003,1093,1007],{},[963,1089,998],{"href":281}," — the frontend-safe ",[511,1092,1002],{},[511,1094,1006],{},[1065,1096,1097,1099],{},[963,1098,289],{"href":290}," — the cross-isolate broadcast tier, and its Node dev shim.",[1065,1101,1102,1104],{},[963,1103,285],{"href":286}," — generate a spec document from your existing contracts.",[1106,1107,1108],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}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 .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":533,"searchDepth":588,"depth":588,"links":1110},[1111,1112,1113,1114,1115,1116],{"id":505,"depth":588,"text":506},{"id":856,"depth":588,"text":857},{"id":971,"depth":588,"text":972},{"id":1010,"depth":588,"text":1011},{"id":1042,"depth":588,"text":1043},{"id":1059,"depth":588,"text":1060},"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.","md",null,{},{"icon":259,"title":142},{"title":142,"description":1117},"PsW1POop7hmf9n_6dWMQQmElAs0yxnpQvEHmqWBDGBE",[1125,1127],{"title":253,"path":254,"stem":255,"description":1126,"icon":256,"children":-1},"Workflow, WorkflowFactory, WorkflowStep, NodeWorkflowEngine — a linear imperative procedure that memoizes each step and survives restarts, with step.do durable checkpoints and step.sleep durable suspend\u002Fresume.",{"title":267,"path":268,"stem":269,"description":1128,"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.",1783300759752]