[{"data":1,"prerenderedAt":3946},["ShallowReactive",2],{"navigation_docs":3,"-docs-realtime-server-sent-events":493,"-docs-realtime-server-sent-events-surround":3941},[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":275,"body":495,"description":3934,"extension":3935,"links":3936,"meta":3937,"navigation":3938,"path":276,"seo":3939,"stem":277,"__hash__":3940},"docs\u002Fdocs\u002F05.realtime\u002F04.server-sent-events.md",{"type":496,"value":497,"toc":3917},"minimark",[498,520,724,727,732,796,803,807,821,928,962,966,984,1461,1558,1568,1572,1586,1669,1684,1695,1699,1708,1711,1716,1738,1893,1904,1908,1934,1948,1970,2091,2100,2186,2210,2214,2239,2255,2610,2637,2643,2954,2958,2978,3211,3222,3226,3240,3526,3530,3548,3749,3782,3786,3809,3813,3837,3847,3851,3913],[499,500,501,502,506,507,510,511,514,515,519],"p",{},"Server-Sent Events are server→client streaming over plain HTTP: one long-lived ",[503,504,505],"code",{},"GET"," response on which\nthe server keeps writing typed, named events as they happen. No WebSocket upgrade, no second protocol —\na ",[503,508,509],{},"text\u002Fevent-stream"," body that browsers consume with the built-in ",[503,512,513],{},"EventSource",". Heximon gives you that\nas a value you simply ",[516,517,518],"strong",{},"return"," from a controller.",[521,522,527],"pre",{"className":523,"code":524,"language":525,"meta":526,"style":526},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","public stream(_action: Get\u003C\"\u002Fstream\">): EventStream\u003CNotificationEvents> {\n  return EventStream.from(\n    async (channel) => {\n      await channel.send(\"notification\", { id: \"1\", message: \"hello\" });\n    },\n    { events: NotificationEvents.events, context: this.context },\n  );\n}\n","ts","",[503,528,529,574,580,603,671,677,712,718],{"__ignoreMap":526},[530,531,534,538,542,545,549,552,556,558,561,564,566,569,571],"span",{"class":532,"line":533},"line",1,[530,535,537],{"class":536},"sTEyZ","public ",[530,539,541],{"class":540},"s2Zo4","stream",[530,543,544],{"class":536},"(_action: Get",[530,546,548],{"class":547},"sMK4o","\u003C",[530,550,551],{"class":547},"\"",[530,553,555],{"class":554},"sfazB","\u002Fstream",[530,557,551],{"class":547},[530,559,560],{"class":547},">",[530,562,563],{"class":536},"): EventStream",[530,565,548],{"class":547},[530,567,568],{"class":536},"NotificationEvents",[530,570,560],{"class":547},[530,572,573],{"class":547}," {\n",[530,575,577],{"class":532,"line":576},2,[530,578,579],{"class":536},"  return EventStream.from(\n",[530,581,583,587,590,594,597,601],{"class":532,"line":582},3,[530,584,586],{"class":585},"swJcz","    async",[530,588,589],{"class":547}," (",[530,591,593],{"class":592},"sHdIc","channel",[530,595,596],{"class":547},")",[530,598,600],{"class":599},"spNyl"," =>",[530,602,573],{"class":547},[530,604,606,610,613,616,619,622,624,627,629,632,635,638,641,644,647,649,651,654,656,658,661,663,666,668],{"class":532,"line":605},4,[530,607,609],{"class":608},"sBMFI","      await",[530,611,612],{"class":608}," channel",[530,614,615],{"class":547},".",[530,617,618],{"class":608},"send",[530,620,621],{"class":536},"(",[530,623,551],{"class":547},[530,625,626],{"class":554},"notification",[530,628,551],{"class":547},[530,630,631],{"class":547},",",[530,633,634],{"class":547}," {",[530,636,637],{"class":585}," id",[530,639,640],{"class":547},":",[530,642,643],{"class":547}," \"",[530,645,646],{"class":554},"1",[530,648,551],{"class":547},[530,650,631],{"class":547},[530,652,653],{"class":585}," message",[530,655,640],{"class":547},[530,657,643],{"class":547},[530,659,660],{"class":554},"hello",[530,662,551],{"class":547},[530,664,665],{"class":547}," }",[530,667,596],{"class":536},[530,669,670],{"class":547},";\n",[530,672,674],{"class":532,"line":673},5,[530,675,676],{"class":547},"    },\n",[530,678,680,683,686,688,691,693,696,698,701,703,706,709],{"class":532,"line":679},6,[530,681,682],{"class":547},"    {",[530,684,685],{"class":608}," events",[530,687,640],{"class":547},[530,689,690],{"class":536}," NotificationEvents",[530,692,615],{"class":547},[530,694,695],{"class":536},"events",[530,697,631],{"class":547},[530,699,700],{"class":608}," context",[530,702,640],{"class":547},[530,704,705],{"class":547}," this.",[530,707,708],{"class":536},"context",[530,710,711],{"class":547}," },\n",[530,713,715],{"class":532,"line":714},7,[530,716,717],{"class":536},"  );\n",[530,719,721],{"class":532,"line":720},8,[530,722,723],{"class":547},"}\n",[499,725,726],{},"That's the whole story for a single server. There's a second tier — a durable-backed channel for fanning\none event out to clients spread across isolates — but reach for it only when you need cross-isolate\nbroadcast. Start here.",[728,729,731],"h2",{"id":730},"install-the-package","Install the package",[733,734,735,753,768,782],"code-group",{},[521,736,741],{"className":737,"code":738,"filename":739,"language":740,"meta":526,"style":526},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm add @heximon\u002Fsse\n","pnpm","bash",[503,742,743],{"__ignoreMap":526},[530,744,745,747,750],{"class":532,"line":533},[530,746,739],{"class":608},[530,748,749],{"class":554}," add",[530,751,752],{"class":554}," @heximon\u002Fsse\n",[521,754,757],{"className":737,"code":755,"filename":756,"language":740,"meta":526,"style":526},"npm install @heximon\u002Fsse\n","npm",[503,758,759],{"__ignoreMap":526},[530,760,761,763,766],{"class":532,"line":533},[530,762,756],{"class":608},[530,764,765],{"class":554}," install",[530,767,752],{"class":554},[521,769,772],{"className":737,"code":770,"filename":771,"language":740,"meta":526,"style":526},"yarn add @heximon\u002Fsse\n","yarn",[503,773,774],{"__ignoreMap":526},[530,775,776,778,780],{"class":532,"line":533},[530,777,771],{"class":608},[530,779,749],{"class":554},[530,781,752],{"class":554},[521,783,786],{"className":737,"code":784,"filename":785,"language":740,"meta":526,"style":526},"bun add @heximon\u002Fsse\n","bun",[503,787,788],{"__ignoreMap":526},[530,789,790,792,794],{"class":532,"line":533},[530,791,785],{"class":608},[530,793,749],{"class":554},[530,795,752],{"class":554},[499,797,798,799,802],{},"For a single-server stream that's all you need: ",[503,800,801],{},"EventStream"," is an ordinary HTTP response, so the HTTP\nplugin you already have discovers the route. The durable tier later adds one compiler plugin; nothing\nhere does.",[728,804,806],{"id":805},"declare-a-typed-event-map","Declare a typed event map",[499,808,809,810,812,813,820],{},"Name each event your stream can emit and give it a schema. The map is the contract: a ",[503,811,618],{}," to an\nunlisted name won't type-check, and a payload that fails its schema is rejected before a single byte\nreaches the wire. Any ",[814,815,819],"a",{"href":816,"rel":817},"https:\u002F\u002Fstandardschema.dev\u002F",[818],"nofollow","Standard Schema"," validator works — Zod, Valibot, or\na hand-rolled one.",[521,822,825],{"className":523,"code":823,"filename":824,"language":525,"meta":526,"style":526},"import { ServerSentEvents } from \"@heximon\u002Fsse\";\nimport { notificationSchema } from \".\u002Fnotification.schema\"; \u002F\u002F any StandardSchemaV1\n\nexport class NotificationEvents extends ServerSentEvents({\n  notification: notificationSchema,\n}) {}\n","src\u002Fnotifications\u002Fnotification.events.ts",[503,826,827,852,879,885,905,917],{"__ignoreMap":526},[530,828,829,833,835,838,840,843,845,848,850],{"class":532,"line":533},[530,830,832],{"class":831},"s7zQu","import",[530,834,634],{"class":547},[530,836,837],{"class":536}," ServerSentEvents",[530,839,665],{"class":547},[530,841,842],{"class":831}," from",[530,844,643],{"class":547},[530,846,847],{"class":554},"@heximon\u002Fsse",[530,849,551],{"class":547},[530,851,670],{"class":547},[530,853,854,856,858,861,863,865,867,870,872,875],{"class":532,"line":576},[530,855,832],{"class":831},[530,857,634],{"class":547},[530,859,860],{"class":536}," notificationSchema",[530,862,665],{"class":547},[530,864,842],{"class":831},[530,866,643],{"class":547},[530,868,869],{"class":554},".\u002Fnotification.schema",[530,871,551],{"class":547},[530,873,874],{"class":547},";",[530,876,878],{"class":877},"sHwdD"," \u002F\u002F any StandardSchemaV1\n",[530,880,881],{"class":532,"line":582},[530,882,884],{"emptyLinePlaceholder":883},true,"\n",[530,886,887,890,893,895,898,900,902],{"class":532,"line":605},[530,888,889],{"class":831},"export",[530,891,892],{"class":599}," class",[530,894,690],{"class":608},[530,896,897],{"class":599}," extends",[530,899,837],{"class":540},[530,901,621],{"class":536},[530,903,904],{"class":547},"{\n",[530,906,907,910,912,914],{"class":532,"line":673},[530,908,909],{"class":585},"  notification",[530,911,640],{"class":547},[530,913,860],{"class":536},[530,915,916],{"class":547},",\n",[530,918,919,922,925],{"class":532,"line":679},[530,920,921],{"class":547},"}",[530,923,924],{"class":536},") ",[530,926,927],{"class":547},"{}\n",[499,929,930,933,934,937,938,589,941,944,945,948,949,589,952,954,955,957,958,961],{},[503,931,932],{},"ServerSentEvents({...})"," is the same config-factory base the durable tier uses below — declared with no\n",[503,935,936],{},"path",", it's just the event-schema map, now as a class: both a ",[516,939,940],{},"value",[503,942,943],{},"NotificationEvents.events",", the\nmap ",[503,946,947],{},"EventStream.from"," validates against) and a ",[516,950,951],{},"type",[503,953,568],{},", dropped straight into the\n",[503,956,801],{}," generic with no ",[503,959,960],{},"typeof",").",[728,963,965],{"id":964},"return-the-stream-from-a-controller","Return the stream from a controller",[499,967,968,969,972,973,976,977,980,981,983],{},"A handler returns ",[503,970,971],{},"EventStream.from(producer, options)",". The ",[503,974,975],{},"producer"," is an async function handed an\n",[503,978,979],{},"EventChannel"," — call ",[503,982,618],{}," as events occur, and Heximon frames each one onto the response.",[521,985,988],{"className":523,"code":986,"filename":987,"language":525,"meta":526,"style":526},"import { Context } from \"@heximon\u002Fruntime\";\nimport type { Controller, Get } from \"@heximon\u002Fhttp\";\nimport { TimeSpan } from \"@heximon\u002Fprimitives\";\nimport { EventStream } from \"@heximon\u002Fsse\";\nimport { NotificationEvents } from \".\u002Fnotification.events\";\n\nexport class NotificationsController implements Controller\u003C\"\u002Fnotifications\"> {\n  constructor(private readonly context: Context) {}\n\n  public stream(_action: Get\u003C\"\u002Fstream\">): EventStream\u003CNotificationEvents> {\n    return EventStream.from(\n      async (channel) => {\n        channel.heartbeat(new TimeSpan(50, \"ms\")); \u002F\u002F a \":\" comment line every 50ms, keeps proxies from idling out\n\n        for (let index = 1; index \u003C= 3; index += 1) {\n          await channel.send(\"notification\", { id: String(index), message: `hello ${index}` });\n        }\n        \u002F\u002F Return void to close the stream; return a teardown function to keep it\n        \u002F\u002F open (event-driven) and clean up on disconnect.\n      },\n      { events: NotificationEvents.events, context: this.context },\n    );\n  }\n}\n","src\u002Fnotifications\u002Fnotifications.controller.ts",[503,989,990,1012,1042,1064,1085,1106,1110,1137,1161,1166,1205,1221,1237,1278,1283,1326,1390,1396,1402,1408,1414,1442,1450,1456],{"__ignoreMap":526},[530,991,992,994,996,999,1001,1003,1005,1008,1010],{"class":532,"line":533},[530,993,832],{"class":831},[530,995,634],{"class":547},[530,997,998],{"class":536}," Context",[530,1000,665],{"class":547},[530,1002,842],{"class":831},[530,1004,643],{"class":547},[530,1006,1007],{"class":554},"@heximon\u002Fruntime",[530,1009,551],{"class":547},[530,1011,670],{"class":547},[530,1013,1014,1016,1019,1021,1024,1026,1029,1031,1033,1035,1038,1040],{"class":532,"line":576},[530,1015,832],{"class":831},[530,1017,1018],{"class":831}," type",[530,1020,634],{"class":547},[530,1022,1023],{"class":536}," Controller",[530,1025,631],{"class":547},[530,1027,1028],{"class":536}," Get",[530,1030,665],{"class":547},[530,1032,842],{"class":831},[530,1034,643],{"class":547},[530,1036,1037],{"class":554},"@heximon\u002Fhttp",[530,1039,551],{"class":547},[530,1041,670],{"class":547},[530,1043,1044,1046,1048,1051,1053,1055,1057,1060,1062],{"class":532,"line":582},[530,1045,832],{"class":831},[530,1047,634],{"class":547},[530,1049,1050],{"class":536}," TimeSpan",[530,1052,665],{"class":547},[530,1054,842],{"class":831},[530,1056,643],{"class":547},[530,1058,1059],{"class":554},"@heximon\u002Fprimitives",[530,1061,551],{"class":547},[530,1063,670],{"class":547},[530,1065,1066,1068,1070,1073,1075,1077,1079,1081,1083],{"class":532,"line":605},[530,1067,832],{"class":831},[530,1069,634],{"class":547},[530,1071,1072],{"class":536}," EventStream",[530,1074,665],{"class":547},[530,1076,842],{"class":831},[530,1078,643],{"class":547},[530,1080,847],{"class":554},[530,1082,551],{"class":547},[530,1084,670],{"class":547},[530,1086,1087,1089,1091,1093,1095,1097,1099,1102,1104],{"class":532,"line":673},[530,1088,832],{"class":831},[530,1090,634],{"class":547},[530,1092,690],{"class":536},[530,1094,665],{"class":547},[530,1096,842],{"class":831},[530,1098,643],{"class":547},[530,1100,1101],{"class":554},".\u002Fnotification.events",[530,1103,551],{"class":547},[530,1105,670],{"class":547},[530,1107,1108],{"class":532,"line":679},[530,1109,884],{"emptyLinePlaceholder":883},[530,1111,1112,1114,1116,1119,1122,1124,1126,1128,1131,1133,1135],{"class":532,"line":714},[530,1113,889],{"class":831},[530,1115,892],{"class":599},[530,1117,1118],{"class":608}," NotificationsController",[530,1120,1121],{"class":599}," implements",[530,1123,1023],{"class":608},[530,1125,548],{"class":547},[530,1127,551],{"class":547},[530,1129,1130],{"class":554},"\u002Fnotifications",[530,1132,551],{"class":547},[530,1134,560],{"class":547},[530,1136,573],{"class":547},[530,1138,1139,1142,1144,1147,1150,1152,1154,1156,1158],{"class":532,"line":720},[530,1140,1141],{"class":599},"  constructor",[530,1143,621],{"class":547},[530,1145,1146],{"class":599},"private",[530,1148,1149],{"class":599}," readonly",[530,1151,700],{"class":592},[530,1153,640],{"class":547},[530,1155,998],{"class":608},[530,1157,596],{"class":547},[530,1159,1160],{"class":547}," {}\n",[530,1162,1164],{"class":532,"line":1163},9,[530,1165,884],{"emptyLinePlaceholder":883},[530,1167,1169,1172,1175,1177,1180,1182,1184,1186,1188,1190,1192,1195,1197,1199,1201,1203],{"class":532,"line":1168},10,[530,1170,1171],{"class":599},"  public",[530,1173,1174],{"class":585}," stream",[530,1176,621],{"class":547},[530,1178,1179],{"class":592},"_action",[530,1181,640],{"class":547},[530,1183,1028],{"class":608},[530,1185,548],{"class":547},[530,1187,551],{"class":547},[530,1189,555],{"class":554},[530,1191,551],{"class":547},[530,1193,1194],{"class":547},">):",[530,1196,1072],{"class":608},[530,1198,548],{"class":547},[530,1200,568],{"class":608},[530,1202,560],{"class":547},[530,1204,573],{"class":547},[530,1206,1208,1211,1213,1215,1218],{"class":532,"line":1207},11,[530,1209,1210],{"class":831},"    return",[530,1212,1072],{"class":536},[530,1214,615],{"class":547},[530,1216,1217],{"class":540},"from",[530,1219,1220],{"class":585},"(\n",[530,1222,1224,1227,1229,1231,1233,1235],{"class":532,"line":1223},12,[530,1225,1226],{"class":599},"      async",[530,1228,589],{"class":547},[530,1230,593],{"class":592},[530,1232,596],{"class":547},[530,1234,600],{"class":599},[530,1236,573],{"class":547},[530,1238,1240,1243,1245,1248,1250,1253,1255,1257,1261,1263,1265,1268,1270,1273,1275],{"class":532,"line":1239},13,[530,1241,1242],{"class":536},"        channel",[530,1244,615],{"class":547},[530,1246,1247],{"class":540},"heartbeat",[530,1249,621],{"class":585},[530,1251,1252],{"class":547},"new",[530,1254,1050],{"class":540},[530,1256,621],{"class":585},[530,1258,1260],{"class":1259},"sbssI","50",[530,1262,631],{"class":547},[530,1264,643],{"class":547},[530,1266,1267],{"class":554},"ms",[530,1269,551],{"class":547},[530,1271,1272],{"class":585},"))",[530,1274,874],{"class":547},[530,1276,1277],{"class":877}," \u002F\u002F a \":\" comment line every 50ms, keeps proxies from idling out\n",[530,1279,1281],{"class":532,"line":1280},14,[530,1282,884],{"emptyLinePlaceholder":883},[530,1284,1286,1289,1291,1294,1297,1300,1303,1305,1307,1310,1313,1315,1317,1320,1322,1324],{"class":532,"line":1285},15,[530,1287,1288],{"class":831},"        for",[530,1290,589],{"class":585},[530,1292,1293],{"class":599},"let",[530,1295,1296],{"class":536}," index",[530,1298,1299],{"class":547}," =",[530,1301,1302],{"class":1259}," 1",[530,1304,874],{"class":547},[530,1306,1296],{"class":536},[530,1308,1309],{"class":547}," \u003C=",[530,1311,1312],{"class":1259}," 3",[530,1314,874],{"class":547},[530,1316,1296],{"class":536},[530,1318,1319],{"class":547}," +=",[530,1321,1302],{"class":1259},[530,1323,924],{"class":585},[530,1325,904],{"class":547},[530,1327,1329,1332,1334,1336,1338,1340,1342,1344,1346,1348,1350,1352,1354,1357,1359,1362,1364,1366,1368,1370,1373,1376,1379,1381,1384,1386,1388],{"class":532,"line":1328},16,[530,1330,1331],{"class":831},"          await",[530,1333,612],{"class":536},[530,1335,615],{"class":547},[530,1337,618],{"class":540},[530,1339,621],{"class":585},[530,1341,551],{"class":547},[530,1343,626],{"class":554},[530,1345,551],{"class":547},[530,1347,631],{"class":547},[530,1349,634],{"class":547},[530,1351,637],{"class":585},[530,1353,640],{"class":547},[530,1355,1356],{"class":540}," String",[530,1358,621],{"class":585},[530,1360,1361],{"class":536},"index",[530,1363,596],{"class":585},[530,1365,631],{"class":547},[530,1367,653],{"class":585},[530,1369,640],{"class":547},[530,1371,1372],{"class":547}," `",[530,1374,1375],{"class":554},"hello ",[530,1377,1378],{"class":547},"${",[530,1380,1361],{"class":536},[530,1382,1383],{"class":547},"}`",[530,1385,665],{"class":547},[530,1387,596],{"class":585},[530,1389,670],{"class":547},[530,1391,1393],{"class":532,"line":1392},17,[530,1394,1395],{"class":547},"        }\n",[530,1397,1399],{"class":532,"line":1398},18,[530,1400,1401],{"class":877},"        \u002F\u002F Return void to close the stream; return a teardown function to keep it\n",[530,1403,1405],{"class":532,"line":1404},19,[530,1406,1407],{"class":877},"        \u002F\u002F open (event-driven) and clean up on disconnect.\n",[530,1409,1411],{"class":532,"line":1410},20,[530,1412,1413],{"class":547},"      },\n",[530,1415,1417,1420,1422,1424,1426,1428,1430,1432,1434,1436,1438,1440],{"class":532,"line":1416},21,[530,1418,1419],{"class":547},"      {",[530,1421,685],{"class":585},[530,1423,640],{"class":547},[530,1425,690],{"class":536},[530,1427,615],{"class":547},[530,1429,695],{"class":536},[530,1431,631],{"class":547},[530,1433,700],{"class":585},[530,1435,640],{"class":547},[530,1437,705],{"class":547},[530,1439,708],{"class":536},[530,1441,711],{"class":547},[530,1443,1445,1448],{"class":532,"line":1444},22,[530,1446,1447],{"class":585},"    )",[530,1449,670],{"class":547},[530,1451,1453],{"class":532,"line":1452},23,[530,1454,1455],{"class":547},"  }\n",[530,1457,1459],{"class":532,"line":1458},24,[530,1460,723],{"class":547},[521,1462,1465],{"className":523,"code":1463,"filename":1464,"language":525,"meta":526,"style":526},"import { Module } from \"@heximon\u002Fruntime\";\nimport { NotificationsController } from \".\u002Fnotifications.controller\";\n\nexport class NotificationsModule extends Module({\n  http: { controllers: [NotificationsController] },\n}) {}\n","src\u002Fnotifications\u002Fnotifications.module.ts",[503,1466,1467,1488,1509,1513,1530,1550],{"__ignoreMap":526},[530,1468,1469,1471,1473,1476,1478,1480,1482,1484,1486],{"class":532,"line":533},[530,1470,832],{"class":831},[530,1472,634],{"class":547},[530,1474,1475],{"class":536}," Module",[530,1477,665],{"class":547},[530,1479,842],{"class":831},[530,1481,643],{"class":547},[530,1483,1007],{"class":554},[530,1485,551],{"class":547},[530,1487,670],{"class":547},[530,1489,1490,1492,1494,1496,1498,1500,1502,1505,1507],{"class":532,"line":576},[530,1491,832],{"class":831},[530,1493,634],{"class":547},[530,1495,1118],{"class":536},[530,1497,665],{"class":547},[530,1499,842],{"class":831},[530,1501,643],{"class":547},[530,1503,1504],{"class":554},".\u002Fnotifications.controller",[530,1506,551],{"class":547},[530,1508,670],{"class":547},[530,1510,1511],{"class":532,"line":582},[530,1512,884],{"emptyLinePlaceholder":883},[530,1514,1515,1517,1519,1522,1524,1526,1528],{"class":532,"line":605},[530,1516,889],{"class":831},[530,1518,892],{"class":599},[530,1520,1521],{"class":608}," NotificationsModule",[530,1523,897],{"class":599},[530,1525,1475],{"class":540},[530,1527,621],{"class":536},[530,1529,904],{"class":547},[530,1531,1532,1535,1537,1539,1542,1544,1547],{"class":532,"line":673},[530,1533,1534],{"class":585},"  http",[530,1536,640],{"class":547},[530,1538,634],{"class":547},[530,1540,1541],{"class":585}," controllers",[530,1543,640],{"class":547},[530,1545,1546],{"class":536}," [NotificationsController] ",[530,1548,1549],{"class":547},"},\n",[530,1551,1552,1554,1556],{"class":532,"line":679},[530,1553,921],{"class":547},[530,1555,924],{"class":536},[530,1557,927],{"class":547},[499,1559,1560,1563,1564,1567],{},[503,1561,1562],{},"Context"," is required, not ceremony. There's no ambient ",[503,1565,1566],{},"Context.current()"," — the channel's lifetime is\ntied to the context you pass, which is what keeps the producer running after the handler returns\nsynchronously on Cloudflare. Inject it, pass it.",[728,1569,1571],{"id":1570},"send-beat-resume","Send, beat, resume",[499,1573,1574,1575,1577,1578,1581,1582,1585],{},"The ",[503,1576,979],{}," your producer receives is the entire surface, and it's web-standard only —\n",[503,1579,1580],{},"TextEncoder"," + ",[503,1583,1584],{},"ReadableStream"," — so the same code streams on Node and on Cloudflare.",[1587,1588,1589,1603],"table",{},[1590,1591,1592],"thead",{},[1593,1594,1595,1600],"tr",{},[1596,1597,1599],"th",{"align":1598},"left","Call",[1596,1601,1602],{"align":1598},"Effect",[1604,1605,1606,1617,1631,1649,1659],"tbody",{},[1593,1607,1608,1614],{},[1609,1610,1611],"td",{"align":1598},[503,1612,1613],{},"channel.send(eventName, data)",[1609,1615,1616],{"align":1598},"Named, typed, schema-validated event.",[1593,1618,1619,1624],{},[1609,1620,1621],{"align":1598},[503,1622,1623],{},"channel.send(data)",[1609,1625,1626,1627,1630],{"align":1598},"Unnamed event (the default ",[503,1628,1629],{},"message","), untyped.",[1593,1632,1633,1638],{},[1609,1634,1635],{"align":1598},[503,1636,1637],{},"channel.heartbeat(interval)",[1609,1639,1640,1641,1644,1645,1648],{"align":1598},"A ",[503,1642,1643],{},": ping"," comment every interval (a ",[503,1646,1647],{},"TimeSpan",") — keeps idle connections alive.",[1593,1650,1651,1656],{},[1609,1652,1653],{"align":1598},[503,1654,1655],{},"channel.close()",[1609,1657,1658],{"align":1598},"Ends the stream (idempotent).",[1593,1660,1661,1666],{},[1609,1662,1663],{"align":1598},[503,1664,1665],{},"channel.lastEventId",[1609,1667,1668],{"align":1598},"The id the next event will carry (introspection\u002Ftests).",[499,1670,1671,1672,1675,1676,1679,1683],{},"Validation is ",[516,1673,1674],{},"fail-closed",": a payload that doesn't match its schema throws ",[503,1677,1678],{},"EventValidationError",[1680,1681,1682],"em",{},"before"," the id counter advances or any frame is written, so a rejected event can never half-write the\nstream and desync a client mid-event.",[499,1685,1686,1687,1690,1691,1694],{},"When a dropped client reconnects, its browser replays the last id it saw in a ",[503,1688,1689],{},"Last-Event-ID"," header;\nHeximon reads it and exposes it as ",[503,1692,1693],{},"resumeFrom"," on the producer's second argument, so you pick up exactly\nwhere the connection broke.",[728,1696,1698],{"id":1697},"fan-out-across-isolates-with-a-durable-channel","Fan out across isolates with a durable channel",[499,1700,1701,1702,1704,1705,1707],{},"The single-server stream above lives in one process: the producer and every client share memory, so a\n",[503,1703,618],{}," reaches them all. On the edge that breaks down — clients of the same topic land on different\nisolates, and one isolate's ",[503,1706,593],{}," can't see another's connections.",[499,1709,1710],{},"To broadcast one event to all of them you need a single owner for the topic's live streams. That owner is a\nCloudflare Durable Object: one DO per topic holds every client of that topic, regardless of which isolate\nthey connected through.",[1712,1713,1715],"h3",{"id":1714},"register-the-sse-and-durable-plugins","Register the SSE and durable plugins",[499,1717,1718,1719,1722,1723,1726,1727,1730,1731,1733,1734,1737],{},"The single-server tier needed no compiler plugin. The durable tier does — register\n",[503,1720,1721],{},"ServerSentEventsPlugin"," alongside ",[503,1724,1725],{},"DurablePlugin"," and the ",[503,1728,1729],{},"HttpPlugin"," the connecting ",[503,1732,505],{}," rides on, in\nthe ",[503,1735,1736],{},"heximon.config.ts"," config file.",[521,1739,1741],{"className":523,"code":1740,"filename":1736,"language":525,"meta":526,"style":526},"import { defineHeximonConfig } from \"@heximon\u002Fbuild\";\nimport { DurablePlugin } from \"@heximon\u002Fdurable\u002Fcompiler\";\nimport { HttpPlugin } from \"@heximon\u002Fhttp\u002Fcompiler\";\nimport { ServerSentEventsPlugin } from \"@heximon\u002Fsse\u002Fcompiler\";\n\nexport default defineHeximonConfig({\n  plugins: [new HttpPlugin(), new DurablePlugin(), new ServerSentEventsPlugin()],\n});\n",[503,1742,1743,1765,1787,1809,1831,1835,1848,1885],{"__ignoreMap":526},[530,1744,1745,1747,1749,1752,1754,1756,1758,1761,1763],{"class":532,"line":533},[530,1746,832],{"class":831},[530,1748,634],{"class":547},[530,1750,1751],{"class":536}," defineHeximonConfig",[530,1753,665],{"class":547},[530,1755,842],{"class":831},[530,1757,643],{"class":547},[530,1759,1760],{"class":554},"@heximon\u002Fbuild",[530,1762,551],{"class":547},[530,1764,670],{"class":547},[530,1766,1767,1769,1771,1774,1776,1778,1780,1783,1785],{"class":532,"line":576},[530,1768,832],{"class":831},[530,1770,634],{"class":547},[530,1772,1773],{"class":536}," DurablePlugin",[530,1775,665],{"class":547},[530,1777,842],{"class":831},[530,1779,643],{"class":547},[530,1781,1782],{"class":554},"@heximon\u002Fdurable\u002Fcompiler",[530,1784,551],{"class":547},[530,1786,670],{"class":547},[530,1788,1789,1791,1793,1796,1798,1800,1802,1805,1807],{"class":532,"line":582},[530,1790,832],{"class":831},[530,1792,634],{"class":547},[530,1794,1795],{"class":536}," HttpPlugin",[530,1797,665],{"class":547},[530,1799,842],{"class":831},[530,1801,643],{"class":547},[530,1803,1804],{"class":554},"@heximon\u002Fhttp\u002Fcompiler",[530,1806,551],{"class":547},[530,1808,670],{"class":547},[530,1810,1811,1813,1815,1818,1820,1822,1824,1827,1829],{"class":532,"line":605},[530,1812,832],{"class":831},[530,1814,634],{"class":547},[530,1816,1817],{"class":536}," ServerSentEventsPlugin",[530,1819,665],{"class":547},[530,1821,842],{"class":831},[530,1823,643],{"class":547},[530,1825,1826],{"class":554},"@heximon\u002Fsse\u002Fcompiler",[530,1828,551],{"class":547},[530,1830,670],{"class":547},[530,1832,1833],{"class":532,"line":673},[530,1834,884],{"emptyLinePlaceholder":883},[530,1836,1837,1839,1842,1844,1846],{"class":532,"line":679},[530,1838,889],{"class":831},[530,1840,1841],{"class":831}," default",[530,1843,1751],{"class":540},[530,1845,621],{"class":536},[530,1847,904],{"class":547},[530,1849,1850,1853,1855,1858,1860,1862,1865,1867,1870,1872,1874,1876,1878,1880,1883],{"class":532,"line":714},[530,1851,1852],{"class":585},"  plugins",[530,1854,640],{"class":547},[530,1856,1857],{"class":536}," [",[530,1859,1252],{"class":547},[530,1861,1795],{"class":540},[530,1863,1864],{"class":536},"()",[530,1866,631],{"class":547},[530,1868,1869],{"class":547}," new",[530,1871,1773],{"class":540},[530,1873,1864],{"class":536},[530,1875,631],{"class":547},[530,1877,1869],{"class":547},[530,1879,1817],{"class":540},[530,1881,1882],{"class":536},"()]",[530,1884,916],{"class":547},[530,1886,1887,1889,1891],{"class":532,"line":720},[530,1888,921],{"class":547},[530,1890,596],{"class":536},[530,1892,670],{"class":547},[499,1894,1895,1896,1899,1900,1903],{},"The two plugins resolve each other by inspecting each other's output, not by importing, so your SSE\nsource imports no durable code. You do need ",[503,1897,1898],{},"@heximon\u002Fdurable"," installed, because the channel\nnames a ",[503,1901,1902],{},"DurableObject"," as its host.",[1712,1905,1907],{"id":1906},"declare-the-broadcast-events-as-a-class","Declare the broadcast events as a class",[499,1909,1910,1911,1914,1915,1918,1919,1922,1923,1926,1927,1930,1931,1933],{},"The durable tier's event map is a ",[516,1912,1913],{},"class",", declared like a ",[503,1916,1917],{},"Contract",": ",[503,1920,1921],{},"class FeedEvents extends ServerSentEvents({ path, events })",". That call is one of the few that sit in an ",[503,1924,1925],{},"extends"," clause, and what it\nreturns is ",[516,1928,1929],{},"both a value and a type"," — the same trick a ",[503,1932,1917],{}," uses.",[499,1935,1936,1937,1940,1941,1944,1945,1947],{},"As a value, ",[503,1938,1939],{},"FeedEvents.events"," is the schema map the compiler imports and the broadcaster registers; as a\ntype, ",[503,1942,1943],{},"FeedEvents"," drives the SSE generics directly, with no ",[503,1946,960],{},". The subclass body is free for domain\nmethods over the typed publisher.",[499,1949,1950,1951,1955,1956,1959,1960,1962,1963,1966,1967,615],{},"The events class can also declare the stream's ",[516,1952,1953],{},[503,1954,936],{}," — the canonical form. When it does, the events\nclass is the single owner of the feed's address: the handler below binds ",[503,1957,1958],{},"events: FeedEvents"," and declares\nno ",[503,1961,936],{}," of its own, and ",[503,1964,1965],{},"connection.pathParams"," types itself off the declared ",[503,1968,1969],{},":segments",[521,1971,1974],{"className":523,"code":1972,"filename":1973,"language":525,"meta":526,"style":526},"import { ServerSentEvents } from \"@heximon\u002Fsse\";\nimport { announceSchema } from \".\u002Fannounce.schema\"; \u002F\u002F any StandardSchemaV1 for { message: string }\n\nexport class FeedEvents extends ServerSentEvents({\n  path: \"\u002Ffeed\u002F:topicId\",\n  events: {\n    announce: announceSchema,\n  },\n}) {}\n","src\u002Ffeed\u002Ffeed.events.ts",[503,1975,1976,1996,2021,2025,2042,2058,2067,2078,2083],{"__ignoreMap":526},[530,1977,1978,1980,1982,1984,1986,1988,1990,1992,1994],{"class":532,"line":533},[530,1979,832],{"class":831},[530,1981,634],{"class":547},[530,1983,837],{"class":536},[530,1985,665],{"class":547},[530,1987,842],{"class":831},[530,1989,643],{"class":547},[530,1991,847],{"class":554},[530,1993,551],{"class":547},[530,1995,670],{"class":547},[530,1997,1998,2000,2002,2005,2007,2009,2011,2014,2016,2018],{"class":532,"line":576},[530,1999,832],{"class":831},[530,2001,634],{"class":547},[530,2003,2004],{"class":536}," announceSchema",[530,2006,665],{"class":547},[530,2008,842],{"class":831},[530,2010,643],{"class":547},[530,2012,2013],{"class":554},".\u002Fannounce.schema",[530,2015,551],{"class":547},[530,2017,874],{"class":547},[530,2019,2020],{"class":877}," \u002F\u002F any StandardSchemaV1 for { message: string }\n",[530,2022,2023],{"class":532,"line":582},[530,2024,884],{"emptyLinePlaceholder":883},[530,2026,2027,2029,2031,2034,2036,2038,2040],{"class":532,"line":605},[530,2028,889],{"class":831},[530,2030,892],{"class":599},[530,2032,2033],{"class":608}," FeedEvents",[530,2035,897],{"class":599},[530,2037,837],{"class":540},[530,2039,621],{"class":536},[530,2041,904],{"class":547},[530,2043,2044,2047,2049,2051,2054,2056],{"class":532,"line":673},[530,2045,2046],{"class":585},"  path",[530,2048,640],{"class":547},[530,2050,643],{"class":547},[530,2052,2053],{"class":554},"\u002Ffeed\u002F:topicId",[530,2055,551],{"class":547},[530,2057,916],{"class":547},[530,2059,2060,2063,2065],{"class":532,"line":679},[530,2061,2062],{"class":585},"  events",[530,2064,640],{"class":547},[530,2066,573],{"class":547},[530,2068,2069,2072,2074,2076],{"class":532,"line":714},[530,2070,2071],{"class":585},"    announce",[530,2073,640],{"class":547},[530,2075,2004],{"class":536},[530,2077,916],{"class":547},[530,2079,2080],{"class":532,"line":720},[530,2081,2082],{"class":547},"  },\n",[530,2084,2085,2087,2089],{"class":532,"line":1163},[530,2086,921],{"class":547},[530,2088,924],{"class":536},[530,2090,927],{"class":547},[499,2092,2093,2095,2096,2099],{},[503,2094,936],{}," is optional. Omit it — ",[503,2097,2098],{},"ServerSentEvents({ name: schema })",", the flat form — when a handler should\ndeclare its own path instead, e.g. one events class mounted at several handler-declared paths:",[521,2101,2103],{"className":523,"code":2102,"filename":1973,"language":525,"meta":526,"style":526},"import { ServerSentEvents } from \"@heximon\u002Fsse\";\nimport { announceSchema } from \".\u002Fannounce.schema\"; \u002F\u002F any StandardSchemaV1 for { message: string }\n\nexport class FeedEvents extends ServerSentEvents({\n  announce: announceSchema,\n}) {}\n",[503,2104,2105,2125,2147,2151,2167,2178],{"__ignoreMap":526},[530,2106,2107,2109,2111,2113,2115,2117,2119,2121,2123],{"class":532,"line":533},[530,2108,832],{"class":831},[530,2110,634],{"class":547},[530,2112,837],{"class":536},[530,2114,665],{"class":547},[530,2116,842],{"class":831},[530,2118,643],{"class":547},[530,2120,847],{"class":554},[530,2122,551],{"class":547},[530,2124,670],{"class":547},[530,2126,2127,2129,2131,2133,2135,2137,2139,2141,2143,2145],{"class":532,"line":576},[530,2128,832],{"class":831},[530,2130,634],{"class":547},[530,2132,2004],{"class":536},[530,2134,665],{"class":547},[530,2136,842],{"class":831},[530,2138,643],{"class":547},[530,2140,2013],{"class":554},[530,2142,551],{"class":547},[530,2144,874],{"class":547},[530,2146,2020],{"class":877},[530,2148,2149],{"class":532,"line":582},[530,2150,884],{"emptyLinePlaceholder":883},[530,2152,2153,2155,2157,2159,2161,2163,2165],{"class":532,"line":605},[530,2154,889],{"class":831},[530,2156,892],{"class":599},[530,2158,2033],{"class":608},[530,2160,897],{"class":599},[530,2162,837],{"class":540},[530,2164,621],{"class":536},[530,2166,904],{"class":547},[530,2168,2169,2172,2174,2176],{"class":532,"line":673},[530,2170,2171],{"class":585},"  announce",[530,2173,640],{"class":547},[530,2175,2004],{"class":536},[530,2177,916],{"class":547},[530,2179,2180,2182,2184],{"class":532,"line":679},[530,2181,921],{"class":547},[530,2183,924],{"class":536},[530,2185,927],{"class":547},[2187,2188,2190,2193,2194,2196,2197,2200,2201,2204,2205,2207,2208,961],"callout",{"type":2189},"warning",[516,2191,2192],{},"One source of truth for the address."," Declaring ",[503,2195,936],{}," on the handler's ",[503,2198,2199],{},"ServerSentEventsHandler\u003C{ path }>","\nconfig AND on its bound events class's ",[503,2202,2203],{},"ServerSentEvents({ path, events })"," is a build error — even when both\ndeclare the same string. Pick one owner: the events class (drop the handler's ",[503,2206,936],{},") or the handler (drop the\nevents class's ",[503,2209,936],{},[1712,2211,2213],{"id":2212},"author-the-durable-channel","Author the durable channel",[499,2215,1640,2216,2219,2220,2223,2224,2227,2228,2230,2231,2234,2235,2238],{},[503,2217,2218],{},"ServerSentEventsHandler\u003CConfig>"," reads its stream path, host DO, and events class from a single type\nargument — the same way a ",[503,2221,2222],{},"Controller\u003C\"\u002Fusers\">"," reads its prefix — declared via ",[503,2225,2226],{},"implements"," (or ",[503,2229,1925],{},",\nwhich works the same). SSE is unidirectional, so a connection has nothing to receive: the only method is\n",[503,2232,2233],{},"stream(connection)",", which runs once per client. Cross-client fan-out comes from ",[503,2236,2237],{},"publish",", not from here.",[499,2240,2241,2242,2244,2245,2247,2248,2251,2252,2254],{},"When the bound ",[503,2243,695],{}," class already declares a ",[503,2246,936],{}," (the form above), the handler declares none — the\naddress comes from the events class, and both the ",[503,2249,2250],{},"key"," selector and ",[503,2253,1965],{},"\ntype themselves off it:",[521,2256,2259],{"className":523,"code":2257,"filename":2258,"language":525,"meta":526,"style":526},"import type {\n  EventStreamConnection,\n  ServerSentEventsHandler,\n  ServerSentEventsKeySelector,\n} from \"@heximon\u002Fsse\";\nimport { FeedEvents } from \".\u002Ffeed.events\"; \u002F\u002F declares path: \"\u002Ffeed\u002F:topicId\"\nimport { FeedRoom } from \".\u002Ffeed.room\";\n\nexport class FeedChannel implements ServerSentEventsHandler\u003C{\n  durable: FeedRoom; \u002F\u002F the DurableObject holding the live streams\n  events: FeedEvents; \u002F\u002F owns the stream's address — the handler declares no path\n}> {\n  \u002F\u002F Key the hosting DO per topic — `request` is typed from the events class's declared path.\n  public readonly key: ServerSentEventsKeySelector\u003C{\n    durable: FeedRoom;\n    events: FeedEvents;\n  }> = (request) => request.pathParams.topicId;\n\n  public async stream(connection: EventStreamConnection\u003CFeedEvents>): Promise\u003Cvoid> {\n    \u002F\u002F connection.pathParams is typed off FeedEvents's declared \":topicId\" segment.\n    const topicId = connection.pathParams.topicId;\n    await connection.channel.send({ type: \"welcome\", id: connection.id, topicId });\n  }\n}\n","src\u002Ffeed\u002Ffeed.channel.ts",[503,2260,2261,2269,2276,2283,2290,2304,2328,2350,2354,2371,2385,2398,2405,2410,2426,2437,2448,2479,2483,2520,2525,2548,2602,2606],{"__ignoreMap":526},[530,2262,2263,2265,2267],{"class":532,"line":533},[530,2264,832],{"class":831},[530,2266,1018],{"class":831},[530,2268,573],{"class":547},[530,2270,2271,2274],{"class":532,"line":576},[530,2272,2273],{"class":536},"  EventStreamConnection",[530,2275,916],{"class":547},[530,2277,2278,2281],{"class":532,"line":582},[530,2279,2280],{"class":536},"  ServerSentEventsHandler",[530,2282,916],{"class":547},[530,2284,2285,2288],{"class":532,"line":605},[530,2286,2287],{"class":536},"  ServerSentEventsKeySelector",[530,2289,916],{"class":547},[530,2291,2292,2294,2296,2298,2300,2302],{"class":532,"line":673},[530,2293,921],{"class":547},[530,2295,842],{"class":831},[530,2297,643],{"class":547},[530,2299,847],{"class":554},[530,2301,551],{"class":547},[530,2303,670],{"class":547},[530,2305,2306,2308,2310,2312,2314,2316,2318,2321,2323,2325],{"class":532,"line":679},[530,2307,832],{"class":831},[530,2309,634],{"class":547},[530,2311,2033],{"class":536},[530,2313,665],{"class":547},[530,2315,842],{"class":831},[530,2317,643],{"class":547},[530,2319,2320],{"class":554},".\u002Ffeed.events",[530,2322,551],{"class":547},[530,2324,874],{"class":547},[530,2326,2327],{"class":877}," \u002F\u002F declares path: \"\u002Ffeed\u002F:topicId\"\n",[530,2329,2330,2332,2334,2337,2339,2341,2343,2346,2348],{"class":532,"line":714},[530,2331,832],{"class":831},[530,2333,634],{"class":547},[530,2335,2336],{"class":536}," FeedRoom",[530,2338,665],{"class":547},[530,2340,842],{"class":831},[530,2342,643],{"class":547},[530,2344,2345],{"class":554},".\u002Ffeed.room",[530,2347,551],{"class":547},[530,2349,670],{"class":547},[530,2351,2352],{"class":532,"line":720},[530,2353,884],{"emptyLinePlaceholder":883},[530,2355,2356,2358,2360,2363,2365,2368],{"class":532,"line":1163},[530,2357,889],{"class":831},[530,2359,892],{"class":599},[530,2361,2362],{"class":608}," FeedChannel",[530,2364,1121],{"class":599},[530,2366,2367],{"class":540}," ServerSentEventsHandler",[530,2369,2370],{"class":547},"\u003C{\n",[530,2372,2373,2376,2378,2380,2382],{"class":532,"line":1168},[530,2374,2375],{"class":585},"  durable",[530,2377,640],{"class":547},[530,2379,2336],{"class":608},[530,2381,874],{"class":547},[530,2383,2384],{"class":877}," \u002F\u002F the DurableObject holding the live streams\n",[530,2386,2387,2389,2391,2393,2395],{"class":532,"line":1207},[530,2388,2062],{"class":585},[530,2390,640],{"class":547},[530,2392,2033],{"class":608},[530,2394,874],{"class":547},[530,2396,2397],{"class":877}," \u002F\u002F owns the stream's address — the handler declares no path\n",[530,2399,2400,2403],{"class":532,"line":1223},[530,2401,2402],{"class":547},"}>",[530,2404,573],{"class":547},[530,2406,2407],{"class":532,"line":1239},[530,2408,2409],{"class":877},"  \u002F\u002F Key the hosting DO per topic — `request` is typed from the events class's declared path.\n",[530,2411,2412,2414,2416,2419,2421,2424],{"class":532,"line":1280},[530,2413,1171],{"class":599},[530,2415,1149],{"class":599},[530,2417,2418],{"class":585}," key",[530,2420,640],{"class":547},[530,2422,2423],{"class":608}," ServerSentEventsKeySelector",[530,2425,2370],{"class":547},[530,2427,2428,2431,2433,2435],{"class":532,"line":1285},[530,2429,2430],{"class":585},"    durable",[530,2432,640],{"class":547},[530,2434,2336],{"class":608},[530,2436,670],{"class":547},[530,2438,2439,2442,2444,2446],{"class":532,"line":1328},[530,2440,2441],{"class":585},"    events",[530,2443,640],{"class":547},[530,2445,2033],{"class":608},[530,2447,670],{"class":547},[530,2449,2450,2453,2455,2457,2460,2462,2464,2467,2469,2472,2474,2477],{"class":532,"line":1392},[530,2451,2452],{"class":547},"  }>",[530,2454,1299],{"class":547},[530,2456,589],{"class":547},[530,2458,2459],{"class":592},"request",[530,2461,596],{"class":547},[530,2463,600],{"class":599},[530,2465,2466],{"class":536}," request",[530,2468,615],{"class":547},[530,2470,2471],{"class":536},"pathParams",[530,2473,615],{"class":547},[530,2475,2476],{"class":536},"topicId",[530,2478,670],{"class":547},[530,2480,2481],{"class":532,"line":1398},[530,2482,884],{"emptyLinePlaceholder":883},[530,2484,2485,2487,2490,2492,2494,2497,2499,2502,2504,2506,2508,2511,2513,2516,2518],{"class":532,"line":1404},[530,2486,1171],{"class":599},[530,2488,2489],{"class":599}," async",[530,2491,1174],{"class":585},[530,2493,621],{"class":547},[530,2495,2496],{"class":592},"connection",[530,2498,640],{"class":547},[530,2500,2501],{"class":608}," EventStreamConnection",[530,2503,548],{"class":547},[530,2505,1943],{"class":608},[530,2507,1194],{"class":547},[530,2509,2510],{"class":608}," Promise",[530,2512,548],{"class":547},[530,2514,2515],{"class":608},"void",[530,2517,560],{"class":547},[530,2519,573],{"class":547},[530,2521,2522],{"class":532,"line":1410},[530,2523,2524],{"class":877},"    \u002F\u002F connection.pathParams is typed off FeedEvents's declared \":topicId\" segment.\n",[530,2526,2527,2530,2533,2535,2538,2540,2542,2544,2546],{"class":532,"line":1416},[530,2528,2529],{"class":599},"    const",[530,2531,2532],{"class":536}," topicId",[530,2534,1299],{"class":547},[530,2536,2537],{"class":536}," connection",[530,2539,615],{"class":547},[530,2541,2471],{"class":536},[530,2543,615],{"class":547},[530,2545,2476],{"class":536},[530,2547,670],{"class":547},[530,2549,2550,2553,2555,2557,2559,2561,2563,2565,2568,2570,2572,2574,2577,2579,2581,2583,2585,2587,2589,2592,2594,2596,2598,2600],{"class":532,"line":1444},[530,2551,2552],{"class":831},"    await",[530,2554,2537],{"class":536},[530,2556,615],{"class":547},[530,2558,593],{"class":536},[530,2560,615],{"class":547},[530,2562,618],{"class":540},[530,2564,621],{"class":585},[530,2566,2567],{"class":547},"{",[530,2569,1018],{"class":585},[530,2571,640],{"class":547},[530,2573,643],{"class":547},[530,2575,2576],{"class":554},"welcome",[530,2578,551],{"class":547},[530,2580,631],{"class":547},[530,2582,637],{"class":585},[530,2584,640],{"class":547},[530,2586,2537],{"class":536},[530,2588,615],{"class":547},[530,2590,2591],{"class":536},"id",[530,2593,631],{"class":547},[530,2595,2532],{"class":536},[530,2597,665],{"class":547},[530,2599,596],{"class":585},[530,2601,670],{"class":547},[530,2603,2604],{"class":532,"line":1452},[530,2605,1455],{"class":547},[530,2607,2608],{"class":532,"line":1458},[530,2609,723],{"class":547},[499,2611,1574,2612,2614,2615,2617,2618,2621,2622,2625,2626,2629,2630,2633,2634,2636],{},[503,2613,2250],{}," selector field decides which Durable Object a connection forwards into: returning ",[503,2616,2476],{},"\nmeans every client of ",[503,2619,2620],{},"\u002Ffeed\u002Flobby"," shares one DO, while ",[503,2623,2624],{},"\u002Ffeed\u002Fprivate"," lands on a different DO entirely. That\nkeyed split is what makes broadcast scoped to a topic. (On an ",[503,2627,2628],{},"extends ServerSentEventsHandler\u003C…>"," handler,\nthe same selector is passed via ",[503,2631,2632],{},"super((request) => …)"," — there the arrow's ",[503,2635,2459],{}," needs no annotation.)",[499,2638,2639,2640,2642],{},"The alternative — a flat (path-less) events class bound to a handler that declares its own ",[503,2641,936],{}," — is fully\nsupported, and is the right shape when one events class is mounted at several different handler paths:",[521,2644,2646],{"className":523,"code":2645,"filename":2258,"language":525,"meta":526,"style":526},"import type {\n  EventStreamConnection,\n  ServerSentEventsHandler,\n  ServerSentEventsKeySelector,\n} from \"@heximon\u002Fsse\";\nimport { FeedEvents } from \".\u002Ffeed.events\"; \u002F\u002F flat form — no declared path\nimport { FeedRoom } from \".\u002Ffeed.room\";\n\nexport class FeedChannel implements ServerSentEventsHandler\u003C{\n  path: \"\u002Ffeed\u002F:topicId\";\n  durable: FeedRoom;\n  events: FeedEvents;\n}> {\n  public readonly key: ServerSentEventsKeySelector\u003C{\n    path: \"\u002Ffeed\u002F:topicId\";\n    durable: FeedRoom;\n    events: FeedEvents;\n  }> = (request) => request.pathParams.topicId;\n\n  public async stream(connection: EventStreamConnection\u003CFeedEvents>): Promise\u003Cvoid> {\n    await connection.channel.send({ type: \"welcome\", id: connection.id });\n  }\n}\n",[503,2647,2648,2656,2662,2668,2674,2688,2711,2731,2735,2749,2763,2773,2783,2789,2803,2818,2828,2838,2864,2868,2900,2946,2950],{"__ignoreMap":526},[530,2649,2650,2652,2654],{"class":532,"line":533},[530,2651,832],{"class":831},[530,2653,1018],{"class":831},[530,2655,573],{"class":547},[530,2657,2658,2660],{"class":532,"line":576},[530,2659,2273],{"class":536},[530,2661,916],{"class":547},[530,2663,2664,2666],{"class":532,"line":582},[530,2665,2280],{"class":536},[530,2667,916],{"class":547},[530,2669,2670,2672],{"class":532,"line":605},[530,2671,2287],{"class":536},[530,2673,916],{"class":547},[530,2675,2676,2678,2680,2682,2684,2686],{"class":532,"line":673},[530,2677,921],{"class":547},[530,2679,842],{"class":831},[530,2681,643],{"class":547},[530,2683,847],{"class":554},[530,2685,551],{"class":547},[530,2687,670],{"class":547},[530,2689,2690,2692,2694,2696,2698,2700,2702,2704,2706,2708],{"class":532,"line":679},[530,2691,832],{"class":831},[530,2693,634],{"class":547},[530,2695,2033],{"class":536},[530,2697,665],{"class":547},[530,2699,842],{"class":831},[530,2701,643],{"class":547},[530,2703,2320],{"class":554},[530,2705,551],{"class":547},[530,2707,874],{"class":547},[530,2709,2710],{"class":877}," \u002F\u002F flat form — no declared path\n",[530,2712,2713,2715,2717,2719,2721,2723,2725,2727,2729],{"class":532,"line":714},[530,2714,832],{"class":831},[530,2716,634],{"class":547},[530,2718,2336],{"class":536},[530,2720,665],{"class":547},[530,2722,842],{"class":831},[530,2724,643],{"class":547},[530,2726,2345],{"class":554},[530,2728,551],{"class":547},[530,2730,670],{"class":547},[530,2732,2733],{"class":532,"line":720},[530,2734,884],{"emptyLinePlaceholder":883},[530,2736,2737,2739,2741,2743,2745,2747],{"class":532,"line":1163},[530,2738,889],{"class":831},[530,2740,892],{"class":599},[530,2742,2362],{"class":608},[530,2744,1121],{"class":599},[530,2746,2367],{"class":540},[530,2748,2370],{"class":547},[530,2750,2751,2753,2755,2757,2759,2761],{"class":532,"line":1168},[530,2752,2046],{"class":585},[530,2754,640],{"class":547},[530,2756,643],{"class":547},[530,2758,2053],{"class":554},[530,2760,551],{"class":547},[530,2762,670],{"class":547},[530,2764,2765,2767,2769,2771],{"class":532,"line":1207},[530,2766,2375],{"class":585},[530,2768,640],{"class":547},[530,2770,2336],{"class":608},[530,2772,670],{"class":547},[530,2774,2775,2777,2779,2781],{"class":532,"line":1223},[530,2776,2062],{"class":585},[530,2778,640],{"class":547},[530,2780,2033],{"class":608},[530,2782,670],{"class":547},[530,2784,2785,2787],{"class":532,"line":1239},[530,2786,2402],{"class":547},[530,2788,573],{"class":547},[530,2790,2791,2793,2795,2797,2799,2801],{"class":532,"line":1280},[530,2792,1171],{"class":599},[530,2794,1149],{"class":599},[530,2796,2418],{"class":585},[530,2798,640],{"class":547},[530,2800,2423],{"class":608},[530,2802,2370],{"class":547},[530,2804,2805,2808,2810,2812,2814,2816],{"class":532,"line":1285},[530,2806,2807],{"class":585},"    path",[530,2809,640],{"class":547},[530,2811,643],{"class":547},[530,2813,2053],{"class":554},[530,2815,551],{"class":547},[530,2817,670],{"class":547},[530,2819,2820,2822,2824,2826],{"class":532,"line":1328},[530,2821,2430],{"class":585},[530,2823,640],{"class":547},[530,2825,2336],{"class":608},[530,2827,670],{"class":547},[530,2829,2830,2832,2834,2836],{"class":532,"line":1392},[530,2831,2441],{"class":585},[530,2833,640],{"class":547},[530,2835,2033],{"class":608},[530,2837,670],{"class":547},[530,2839,2840,2842,2844,2846,2848,2850,2852,2854,2856,2858,2860,2862],{"class":532,"line":1398},[530,2841,2452],{"class":547},[530,2843,1299],{"class":547},[530,2845,589],{"class":547},[530,2847,2459],{"class":592},[530,2849,596],{"class":547},[530,2851,600],{"class":599},[530,2853,2466],{"class":536},[530,2855,615],{"class":547},[530,2857,2471],{"class":536},[530,2859,615],{"class":547},[530,2861,2476],{"class":536},[530,2863,670],{"class":547},[530,2865,2866],{"class":532,"line":1404},[530,2867,884],{"emptyLinePlaceholder":883},[530,2869,2870,2872,2874,2876,2878,2880,2882,2884,2886,2888,2890,2892,2894,2896,2898],{"class":532,"line":1410},[530,2871,1171],{"class":599},[530,2873,2489],{"class":599},[530,2875,1174],{"class":585},[530,2877,621],{"class":547},[530,2879,2496],{"class":592},[530,2881,640],{"class":547},[530,2883,2501],{"class":608},[530,2885,548],{"class":547},[530,2887,1943],{"class":608},[530,2889,1194],{"class":547},[530,2891,2510],{"class":608},[530,2893,548],{"class":547},[530,2895,2515],{"class":608},[530,2897,560],{"class":547},[530,2899,573],{"class":547},[530,2901,2902,2904,2906,2908,2910,2912,2914,2916,2918,2920,2922,2924,2926,2928,2930,2932,2934,2936,2938,2940,2942,2944],{"class":532,"line":1416},[530,2903,2552],{"class":831},[530,2905,2537],{"class":536},[530,2907,615],{"class":547},[530,2909,593],{"class":536},[530,2911,615],{"class":547},[530,2913,618],{"class":540},[530,2915,621],{"class":585},[530,2917,2567],{"class":547},[530,2919,1018],{"class":585},[530,2921,640],{"class":547},[530,2923,643],{"class":547},[530,2925,2576],{"class":554},[530,2927,551],{"class":547},[530,2929,631],{"class":547},[530,2931,637],{"class":585},[530,2933,640],{"class":547},[530,2935,2537],{"class":536},[530,2937,615],{"class":547},[530,2939,2591],{"class":536},[530,2941,665],{"class":547},[530,2943,596],{"class":585},[530,2945,670],{"class":547},[530,2947,2948],{"class":532,"line":1444},[530,2949,1455],{"class":547},[530,2951,2952],{"class":532,"line":1452},[530,2953,723],{"class":547},[1712,2955,2957],{"id":2956},"hold-the-streams-in-a-durable-object-and-broadcast","Hold the streams in a Durable Object and broadcast",[499,2959,2960,2961,2964,2965,2967,2968,2970,2971,2974,2975,2977],{},"The host DO injects ",[503,2962,2963],{},"ServerSentEventsBroadcaster"," and exposes a ",[503,2966,2237],{}," RPC that frames the event once\nand writes it to every stream it holds for the topic. Annotating ",[503,2969,2237],{}," with the events class\n(",[503,2972,2973],{},"ServerSentEvent\u003CFeedEvents>"," — no ",[503,2976,960],{},", since the class is a type) makes a wrong event name or a mismatched\npayload a compile error.",[521,2979,2982],{"className":523,"code":2980,"filename":2981,"language":525,"meta":526,"style":526},"import type { DurableObject } from \"@heximon\u002Fdurable\";\nimport { ServerSentEventsBroadcaster, type ServerSentEvent } from \"@heximon\u002Fsse\";\nimport { FeedEvents } from \".\u002Ffeed.events\";\n\nexport class FeedRoom implements DurableObject {\n  private static readonly topic = \"\u002Ffeed\u002F:topicId\"; \u002F\u002F the stream's static dispatch path\n\n  \u002F\u002F The broadcaster is injected by class identity off this DO's app.\n  constructor(private readonly broadcaster: ServerSentEventsBroadcaster) {}\n\n  public async publish(event: ServerSentEvent\u003CFeedEvents>): Promise\u003Cnumber> {\n    return this.broadcaster.publish(FeedRoom.topic, event); \u002F\u002F fans out to every held stream\n  }\n}\n","src\u002Ffeed\u002Ffeed.room.ts",[503,2983,2984,3007,3035,3055,3059,3073,3099,3103,3108,3129,3133,3168,3203,3207],{"__ignoreMap":526},[530,2985,2986,2988,2990,2992,2995,2997,2999,3001,3003,3005],{"class":532,"line":533},[530,2987,832],{"class":831},[530,2989,1018],{"class":831},[530,2991,634],{"class":547},[530,2993,2994],{"class":536}," DurableObject",[530,2996,665],{"class":547},[530,2998,842],{"class":831},[530,3000,643],{"class":547},[530,3002,1898],{"class":554},[530,3004,551],{"class":547},[530,3006,670],{"class":547},[530,3008,3009,3011,3013,3016,3018,3020,3023,3025,3027,3029,3031,3033],{"class":532,"line":576},[530,3010,832],{"class":831},[530,3012,634],{"class":547},[530,3014,3015],{"class":536}," ServerSentEventsBroadcaster",[530,3017,631],{"class":547},[530,3019,1018],{"class":831},[530,3021,3022],{"class":536}," ServerSentEvent",[530,3024,665],{"class":547},[530,3026,842],{"class":831},[530,3028,643],{"class":547},[530,3030,847],{"class":554},[530,3032,551],{"class":547},[530,3034,670],{"class":547},[530,3036,3037,3039,3041,3043,3045,3047,3049,3051,3053],{"class":532,"line":582},[530,3038,832],{"class":831},[530,3040,634],{"class":547},[530,3042,2033],{"class":536},[530,3044,665],{"class":547},[530,3046,842],{"class":831},[530,3048,643],{"class":547},[530,3050,2320],{"class":554},[530,3052,551],{"class":547},[530,3054,670],{"class":547},[530,3056,3057],{"class":532,"line":605},[530,3058,884],{"emptyLinePlaceholder":883},[530,3060,3061,3063,3065,3067,3069,3071],{"class":532,"line":673},[530,3062,889],{"class":831},[530,3064,892],{"class":599},[530,3066,2336],{"class":608},[530,3068,1121],{"class":599},[530,3070,2994],{"class":608},[530,3072,573],{"class":547},[530,3074,3075,3078,3081,3083,3086,3088,3090,3092,3094,3096],{"class":532,"line":679},[530,3076,3077],{"class":599},"  private",[530,3079,3080],{"class":599}," static",[530,3082,1149],{"class":599},[530,3084,3085],{"class":585}," topic",[530,3087,1299],{"class":547},[530,3089,643],{"class":547},[530,3091,2053],{"class":554},[530,3093,551],{"class":547},[530,3095,874],{"class":547},[530,3097,3098],{"class":877}," \u002F\u002F the stream's static dispatch path\n",[530,3100,3101],{"class":532,"line":714},[530,3102,884],{"emptyLinePlaceholder":883},[530,3104,3105],{"class":532,"line":720},[530,3106,3107],{"class":877},"  \u002F\u002F The broadcaster is injected by class identity off this DO's app.\n",[530,3109,3110,3112,3114,3116,3118,3121,3123,3125,3127],{"class":532,"line":1163},[530,3111,1141],{"class":599},[530,3113,621],{"class":547},[530,3115,1146],{"class":599},[530,3117,1149],{"class":599},[530,3119,3120],{"class":592}," broadcaster",[530,3122,640],{"class":547},[530,3124,3015],{"class":608},[530,3126,596],{"class":547},[530,3128,1160],{"class":547},[530,3130,3131],{"class":532,"line":1168},[530,3132,884],{"emptyLinePlaceholder":883},[530,3134,3135,3137,3139,3142,3144,3147,3149,3151,3153,3155,3157,3159,3161,3164,3166],{"class":532,"line":1207},[530,3136,1171],{"class":599},[530,3138,2489],{"class":599},[530,3140,3141],{"class":585}," publish",[530,3143,621],{"class":547},[530,3145,3146],{"class":592},"event",[530,3148,640],{"class":547},[530,3150,3022],{"class":608},[530,3152,548],{"class":547},[530,3154,1943],{"class":608},[530,3156,1194],{"class":547},[530,3158,2510],{"class":608},[530,3160,548],{"class":547},[530,3162,3163],{"class":608},"number",[530,3165,560],{"class":547},[530,3167,573],{"class":547},[530,3169,3170,3172,3174,3177,3179,3181,3183,3186,3188,3191,3193,3196,3198,3200],{"class":532,"line":1223},[530,3171,1210],{"class":831},[530,3173,705],{"class":547},[530,3175,3176],{"class":536},"broadcaster",[530,3178,615],{"class":547},[530,3180,2237],{"class":540},[530,3182,621],{"class":585},[530,3184,3185],{"class":536},"FeedRoom",[530,3187,615],{"class":547},[530,3189,3190],{"class":536},"topic",[530,3192,631],{"class":547},[530,3194,3195],{"class":536}," event",[530,3197,596],{"class":585},[530,3199,874],{"class":547},[530,3201,3202],{"class":877}," \u002F\u002F fans out to every held stream\n",[530,3204,3205],{"class":532,"line":1239},[530,3206,1455],{"class":547},[530,3208,3209],{"class":532,"line":1280},[530,3210,723],{"class":547},[499,3212,3213,3214,3217,3218,3221],{},"Unlike a WebSocket room, the host DO does ",[516,3215,3216],{},"not"," declare ",[503,3219,3220],{},"hibernate: true",". SSE has no hibernation API —\nthe streams are live in DO memory, so the DO stays resident exactly as long as a client is connected.\nThat's the cost of a push channel browsers speak natively, and it's the right trade-off when connections\nare bounded per topic.",[1712,3223,3225],{"id":3224},"trigger-a-broadcast","Trigger a broadcast",[499,3227,3228,3229,3232,3233,3236,3237,3239],{},"A producer injects a ",[503,3230,3231],{},"DurableFactory\u003CFeedRoom>",", resolves the DO by topic name with ",[503,3234,3235],{},"getByName",", and\ncalls its ",[503,3238,2237],{}," RPC — a plain DI dependency like any other.",[521,3241,3244],{"className":523,"code":3242,"filename":3243,"language":525,"meta":526,"style":526},"import type { Controller, Post } from \"@heximon\u002Fhttp\";\nimport { FeedFactory } from \".\u002Ffeed.factory\"; \u002F\u002F extends DurableFactory\u003CFeedRoom>\n\nexport class FeedPublisher implements Controller\u003C\"\u002Ffeed\"> {\n  constructor(private readonly feeds: FeedFactory) {}\n\n  public async announce(action: Post\u003C\"\u002F:topicId\u002Fannounce\">): Promise\u003C{ delivered: number }> {\n    const topicId = action.request.pathParams.topicId;\n    const delivered = await this.feeds\n      .getByName(topicId)\n      .publish({ event: \"announce\", data: { message: `hello ${topicId}` } });\n    return { delivered };\n  }\n}\n","src\u002Ffeed\u002Ffeed.publisher.ts",[503,3245,3246,3273,3298,3302,3328,3349,3353,3400,3425,3441,3455,3507,3518,3522],{"__ignoreMap":526},[530,3247,3248,3250,3252,3254,3256,3258,3261,3263,3265,3267,3269,3271],{"class":532,"line":533},[530,3249,832],{"class":831},[530,3251,1018],{"class":831},[530,3253,634],{"class":547},[530,3255,1023],{"class":536},[530,3257,631],{"class":547},[530,3259,3260],{"class":536}," Post",[530,3262,665],{"class":547},[530,3264,842],{"class":831},[530,3266,643],{"class":547},[530,3268,1037],{"class":554},[530,3270,551],{"class":547},[530,3272,670],{"class":547},[530,3274,3275,3277,3279,3282,3284,3286,3288,3291,3293,3295],{"class":532,"line":576},[530,3276,832],{"class":831},[530,3278,634],{"class":547},[530,3280,3281],{"class":536}," FeedFactory",[530,3283,665],{"class":547},[530,3285,842],{"class":831},[530,3287,643],{"class":547},[530,3289,3290],{"class":554},".\u002Ffeed.factory",[530,3292,551],{"class":547},[530,3294,874],{"class":547},[530,3296,3297],{"class":877}," \u002F\u002F extends DurableFactory\u003CFeedRoom>\n",[530,3299,3300],{"class":532,"line":582},[530,3301,884],{"emptyLinePlaceholder":883},[530,3303,3304,3306,3308,3311,3313,3315,3317,3319,3322,3324,3326],{"class":532,"line":605},[530,3305,889],{"class":831},[530,3307,892],{"class":599},[530,3309,3310],{"class":608}," FeedPublisher",[530,3312,1121],{"class":599},[530,3314,1023],{"class":608},[530,3316,548],{"class":547},[530,3318,551],{"class":547},[530,3320,3321],{"class":554},"\u002Ffeed",[530,3323,551],{"class":547},[530,3325,560],{"class":547},[530,3327,573],{"class":547},[530,3329,3330,3332,3334,3336,3338,3341,3343,3345,3347],{"class":532,"line":673},[530,3331,1141],{"class":599},[530,3333,621],{"class":547},[530,3335,1146],{"class":599},[530,3337,1149],{"class":599},[530,3339,3340],{"class":592}," feeds",[530,3342,640],{"class":547},[530,3344,3281],{"class":608},[530,3346,596],{"class":547},[530,3348,1160],{"class":547},[530,3350,3351],{"class":532,"line":679},[530,3352,884],{"emptyLinePlaceholder":883},[530,3354,3355,3357,3359,3362,3364,3367,3369,3371,3373,3375,3378,3380,3382,3384,3387,3390,3392,3395,3398],{"class":532,"line":714},[530,3356,1171],{"class":599},[530,3358,2489],{"class":599},[530,3360,3361],{"class":585}," announce",[530,3363,621],{"class":547},[530,3365,3366],{"class":592},"action",[530,3368,640],{"class":547},[530,3370,3260],{"class":608},[530,3372,548],{"class":547},[530,3374,551],{"class":547},[530,3376,3377],{"class":554},"\u002F:topicId\u002Fannounce",[530,3379,551],{"class":547},[530,3381,1194],{"class":547},[530,3383,2510],{"class":608},[530,3385,3386],{"class":547},"\u003C{",[530,3388,3389],{"class":585}," delivered",[530,3391,640],{"class":547},[530,3393,3394],{"class":608}," number",[530,3396,3397],{"class":547}," }>",[530,3399,573],{"class":547},[530,3401,3402,3404,3406,3408,3411,3413,3415,3417,3419,3421,3423],{"class":532,"line":720},[530,3403,2529],{"class":599},[530,3405,2532],{"class":536},[530,3407,1299],{"class":547},[530,3409,3410],{"class":536}," action",[530,3412,615],{"class":547},[530,3414,2459],{"class":536},[530,3416,615],{"class":547},[530,3418,2471],{"class":536},[530,3420,615],{"class":547},[530,3422,2476],{"class":536},[530,3424,670],{"class":547},[530,3426,3427,3429,3431,3433,3436,3438],{"class":532,"line":1163},[530,3428,2529],{"class":599},[530,3430,3389],{"class":536},[530,3432,1299],{"class":547},[530,3434,3435],{"class":831}," await",[530,3437,705],{"class":547},[530,3439,3440],{"class":536},"feeds\n",[530,3442,3443,3446,3448,3450,3452],{"class":532,"line":1168},[530,3444,3445],{"class":547},"      .",[530,3447,3235],{"class":540},[530,3449,621],{"class":585},[530,3451,2476],{"class":536},[530,3453,3454],{"class":585},")\n",[530,3456,3457,3459,3461,3463,3465,3467,3469,3471,3474,3476,3478,3481,3483,3485,3487,3489,3491,3493,3495,3497,3499,3501,3503,3505],{"class":532,"line":1207},[530,3458,3445],{"class":547},[530,3460,2237],{"class":540},[530,3462,621],{"class":585},[530,3464,2567],{"class":547},[530,3466,3195],{"class":585},[530,3468,640],{"class":547},[530,3470,643],{"class":547},[530,3472,3473],{"class":554},"announce",[530,3475,551],{"class":547},[530,3477,631],{"class":547},[530,3479,3480],{"class":585}," data",[530,3482,640],{"class":547},[530,3484,634],{"class":547},[530,3486,653],{"class":585},[530,3488,640],{"class":547},[530,3490,1372],{"class":547},[530,3492,1375],{"class":554},[530,3494,1378],{"class":547},[530,3496,2476],{"class":536},[530,3498,1383],{"class":547},[530,3500,665],{"class":547},[530,3502,665],{"class":547},[530,3504,596],{"class":585},[530,3506,670],{"class":547},[530,3508,3509,3511,3513,3515],{"class":532,"line":1223},[530,3510,1210],{"class":831},[530,3512,634],{"class":547},[530,3514,3389],{"class":536},[530,3516,3517],{"class":547}," };\n",[530,3519,3520],{"class":532,"line":1239},[530,3521,1455],{"class":547},[530,3523,3524],{"class":532,"line":1280},[530,3525,723],{"class":547},[1712,3527,3529],{"id":3528},"wire-the-durable-module","Wire the durable module",[499,3531,3532,3533,3536,3537,3540,3541,3544,3545,615],{},"Each concept goes under the namespace its plugin owns: the DO and its factory under ",[503,3534,3535],{},"durable",", the\nchannel under ",[503,3538,3539],{},"sse",", the producer controller under ",[503,3542,3543],{},"http",". None of them belong in ",[503,3546,3547],{},"providers",[521,3549,3552],{"className":523,"code":3550,"filename":3551,"language":525,"meta":526,"style":526},"import { Module } from \"@heximon\u002Fruntime\";\nimport { FeedChannel } from \".\u002Ffeed.channel\";\nimport { FeedFactory } from \".\u002Ffeed.factory\";\nimport { FeedPublisher } from \".\u002Ffeed.publisher\";\nimport { FeedRoom } from \".\u002Ffeed.room\";\n\nexport class FeedModule extends Module({\n  durable: { objects: [FeedRoom], factories: [FeedFactory] },\n  sse: { handlers: [FeedChannel] },\n  http: { controllers: [FeedPublisher] },\n}) {}\n","src\u002Ffeed\u002Ffeed.module.ts",[503,3553,3554,3574,3595,3615,3636,3656,3660,3677,3705,3724,3741],{"__ignoreMap":526},[530,3555,3556,3558,3560,3562,3564,3566,3568,3570,3572],{"class":532,"line":533},[530,3557,832],{"class":831},[530,3559,634],{"class":547},[530,3561,1475],{"class":536},[530,3563,665],{"class":547},[530,3565,842],{"class":831},[530,3567,643],{"class":547},[530,3569,1007],{"class":554},[530,3571,551],{"class":547},[530,3573,670],{"class":547},[530,3575,3576,3578,3580,3582,3584,3586,3588,3591,3593],{"class":532,"line":576},[530,3577,832],{"class":831},[530,3579,634],{"class":547},[530,3581,2362],{"class":536},[530,3583,665],{"class":547},[530,3585,842],{"class":831},[530,3587,643],{"class":547},[530,3589,3590],{"class":554},".\u002Ffeed.channel",[530,3592,551],{"class":547},[530,3594,670],{"class":547},[530,3596,3597,3599,3601,3603,3605,3607,3609,3611,3613],{"class":532,"line":582},[530,3598,832],{"class":831},[530,3600,634],{"class":547},[530,3602,3281],{"class":536},[530,3604,665],{"class":547},[530,3606,842],{"class":831},[530,3608,643],{"class":547},[530,3610,3290],{"class":554},[530,3612,551],{"class":547},[530,3614,670],{"class":547},[530,3616,3617,3619,3621,3623,3625,3627,3629,3632,3634],{"class":532,"line":605},[530,3618,832],{"class":831},[530,3620,634],{"class":547},[530,3622,3310],{"class":536},[530,3624,665],{"class":547},[530,3626,842],{"class":831},[530,3628,643],{"class":547},[530,3630,3631],{"class":554},".\u002Ffeed.publisher",[530,3633,551],{"class":547},[530,3635,670],{"class":547},[530,3637,3638,3640,3642,3644,3646,3648,3650,3652,3654],{"class":532,"line":673},[530,3639,832],{"class":831},[530,3641,634],{"class":547},[530,3643,2336],{"class":536},[530,3645,665],{"class":547},[530,3647,842],{"class":831},[530,3649,643],{"class":547},[530,3651,2345],{"class":554},[530,3653,551],{"class":547},[530,3655,670],{"class":547},[530,3657,3658],{"class":532,"line":679},[530,3659,884],{"emptyLinePlaceholder":883},[530,3661,3662,3664,3666,3669,3671,3673,3675],{"class":532,"line":714},[530,3663,889],{"class":831},[530,3665,892],{"class":599},[530,3667,3668],{"class":608}," FeedModule",[530,3670,897],{"class":599},[530,3672,1475],{"class":540},[530,3674,621],{"class":536},[530,3676,904],{"class":547},[530,3678,3679,3681,3683,3685,3688,3690,3693,3695,3698,3700,3703],{"class":532,"line":720},[530,3680,2375],{"class":585},[530,3682,640],{"class":547},[530,3684,634],{"class":547},[530,3686,3687],{"class":585}," objects",[530,3689,640],{"class":547},[530,3691,3692],{"class":536}," [FeedRoom]",[530,3694,631],{"class":547},[530,3696,3697],{"class":585}," factories",[530,3699,640],{"class":547},[530,3701,3702],{"class":536}," [FeedFactory] ",[530,3704,1549],{"class":547},[530,3706,3707,3710,3712,3714,3717,3719,3722],{"class":532,"line":1163},[530,3708,3709],{"class":585},"  sse",[530,3711,640],{"class":547},[530,3713,634],{"class":547},[530,3715,3716],{"class":585}," handlers",[530,3718,640],{"class":547},[530,3720,3721],{"class":536}," [FeedChannel] ",[530,3723,1549],{"class":547},[530,3725,3726,3728,3730,3732,3734,3736,3739],{"class":532,"line":1168},[530,3727,1534],{"class":585},[530,3729,640],{"class":547},[530,3731,634],{"class":547},[530,3733,1541],{"class":585},[530,3735,640],{"class":547},[530,3737,3738],{"class":536}," [FeedPublisher] ",[530,3740,1549],{"class":547},[530,3742,3743,3745,3747],{"class":532,"line":1207},[530,3744,921],{"class":547},[530,3746,924],{"class":536},[530,3748,927],{"class":547},[2187,3750,3751,3763],{"type":2189},[499,3752,3753,3758,3759,3762],{},[516,3754,3755,3756,615],{},"Don't list the durable factory in ",[503,3757,3547],{}," ",[503,3760,3761],{},"FeedFactory extends DurableFactory\u003CFeedRoom>"," is\nalready constructed by the framework from the Durable Object binding — listing it again shadows that real\nprovider, and the factory you inject won't be wired to the DO namespace.",[499,3764,3765,3766,3769,3770,3773,3774,3777,3778,3781],{},"Declare it only under ",[503,3767,3768],{},"durable: { factories }",", and resolve instances with ",[503,3771,3772],{},"getByName(topicId)"," (a human\ntopic name), never ",[503,3775,3776],{},"get(string)"," — Cloudflare's ",[503,3779,3780],{},"get"," requires a 64-character hex id, so a topic name passed\nto it fails at runtime.",[728,3783,3785],{"id":3784},"where-it-runs","Where it runs",[499,3787,3788,3789,3791,3792,3794,3795,3798,3799,3802,3803,3806,3807,615],{},"The whole stack touches only ",[503,3790,1580],{},", ",[503,3793,1584],{},", and ",[503,3796,3797],{},"JSON",", never a ",[503,3800,3801],{},"node:"," API, so the\nidentical code runs on Node and on Cloudflare. On Node there's no platform DO to forward into, so the\ndurable path degrades to an in-process per-topic registry: the same fan-out code, no round-trip. One\nstream path may have exactly one handler — a duplicate path is a build error — and every ",[503,3804,3805],{},"durable:","\ntarget must be a real, discovered ",[503,3808,1902],{},[728,3810,3812],{"id":3811},"connect-from-the-browser","Connect from the browser",[499,3814,1640,3815,3817,3818,3821,3822,3825,3826,3828,3829,3831,3832,3834,3835,615],{},[503,3816,932],{}," class is more than a server-side declaration — the same class types a browser\nclient. The ",[503,3819,3820],{},"@heximon\u002Fclient\u002Fsse"," subpath gives you an ",[503,3823,3824],{},"SseClient"," that connects to any ",[503,3827,509],{},"\nendpoint and validates every received frame against that schema, with no ",[503,3830,3801],{}," imports and no runtime\ndependency on ",[503,3833,1007],{}," or ",[503,3836,847],{},[499,3838,3839,3840,3843,3844,3846],{},"See ",[814,3841,3842],{"href":281},"Typed clients"," for the full guide — subscribing, streaming, resuming with\n",[503,3845,1689],{},", and testing with a mock transport.",[728,3848,3850],{"id":3849},"see-also","See also",[3852,3853,3854,3860,3872,3884,3890],"ul",{},[3855,3856,3857,3859],"li",{},[814,3858,267],{"href":268}," — switch to a full-duplex channel when clients need to send as well\nas receive; the durable host pattern is the same.",[3855,3861,3862,3864,3865,3867,3868,3871],{},[814,3863,289],{"href":290}," — the ",[503,3866,1902],{}," and ",[503,3869,3870],{},"DurableFactory"," the broadcast\ntier holds its live streams in.",[3855,3873,3874,3876,3877,3879,3880,3883],{},[814,3875,3842],{"href":281}," — the browser-safe ",[503,3878,3824],{}," for a ",[503,3881,3882],{},"ServerSentEvents"," class, and\nits WebSocket counterpart.",[3855,3885,3886,3889],{},[814,3887,3888],{"href":333},"Nitro deployment"," — host the single-server stream, or ship the durable channel to\nCloudflare Workers.",[3855,3891,3892,3899,3900,3902,3903,3791,3906,3908,3909,3912],{},[814,3893,3896],{"href":3894,"rel":3895},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fflagship\u002Fapps\u002Fcloudflare",[818],[516,3897,3898],{},"The flagship Cloudflare app","\n— a durable-backed remaining-seats feed channel with cross-isolate broadcast fan-out, plus a typed\n",[503,3901,3824],{}," test that consumes the live server frames (",[503,3904,3905],{},"subscribe",[503,3907,541],{},", resume via ",[503,3910,3911],{},"lastEventId",",\nand the error-handling paths).",[3914,3915,3916],"style",{},"html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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 .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 .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":526,"searchDepth":576,"depth":576,"links":3918},[3919,3920,3921,3922,3923,3931,3932,3933],{"id":730,"depth":576,"text":731},{"id":805,"depth":576,"text":806},{"id":964,"depth":576,"text":965},{"id":1570,"depth":576,"text":1571},{"id":1697,"depth":576,"text":1698,"children":3924},[3925,3926,3927,3928,3929,3930],{"id":1714,"depth":582,"text":1715},{"id":1906,"depth":582,"text":1907},{"id":2212,"depth":582,"text":2213},{"id":2956,"depth":582,"text":2957},{"id":3224,"depth":582,"text":3225},{"id":3528,"depth":582,"text":3529},{"id":3784,"depth":576,"text":3785},{"id":3811,"depth":576,"text":3812},{"id":3849,"depth":576,"text":3850},"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.","md",null,{},{"icon":278},{"title":275,"description":3934},"jdq9hmCsel5bav9S2fLiXUzi1ecFN8gP0vuzGR85HxA",[3942,3944],{"title":271,"path":272,"stem":273,"description":3943,"icon":70,"children":-1},"A typed WebSocketContract class with per-kind WebSocketMessage dispatch, TypedConnection validation, broadcast transports, and a matching browser client, layered on top of a plain WebSocketHandler.",{"title":280,"path":281,"stem":282,"description":3945,"icon":283,"children":-1},"Browser and edge-safe clients for realtime transports — WsClient over a WebSocketContract and SseClient over a ServerSentEvents class, with WsTransport\u002FSseTransport native\u002Ffetch\u002FeventSource\u002Fmock modes, reconnect policies, and typed validation errors.",1783300761903]