[{"data":1,"prerenderedAt":832},["ShallowReactive",2],{"navigation:en":3,"\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fwebsockets:en":311},[4],{"title":5,"path":6,"stem":7,"children":8,"icon":10},"Nuxfire Docs","\u002Fdocs","docs\u002F0.index",[9,11,152],{"title":5,"path":6,"stem":7,"icon":10},"i-lucide-layout-grid",{"title":12,"path":13,"stem":14,"children":15,"icon":151},"SaaS Starter Kit","\u002Fdocs\u002Fsaas-starter-kit","docs\u002F1.saas-starter-kit\u002F0.index",[16,19,63,129],{"title":17,"path":13,"stem":14,"icon":18},"What is the SaaS Starter Kit?","i-lucide-info",{"title":20,"icon":21,"hide":22,"path":23,"stem":24,"children":25,"page":62},"Getting Started","i-lucide-play",true,"\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started","docs\u002F1.saas-starter-kit\u002F1.getting-started",[26,30,34,38,42,46,50,54,58],{"title":27,"path":28,"stem":29},"Quick Start","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fquick-start","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F0.quick-start",{"title":31,"path":32,"stem":33},"Installation","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Finstallation","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F1.installation",{"title":35,"path":36,"stem":37},"Cloudflare Access","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fcloudflare-access","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F2.cloudflare-access",{"title":39,"path":40,"stem":41},"Configuration","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fconfiguration","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F3.configuration",{"title":43,"path":44,"stem":45},"Secrets","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fsecrets","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F4.secrets",{"title":47,"path":48,"stem":49},"Environment Variables","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fenvironment-variables","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F5.environment-variables",{"title":51,"path":52,"stem":53},"Deploy","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fdeploy","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F6.deploy",{"title":55,"path":56,"stem":57},"Local Development","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Flocal-development","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F7.local-development",{"title":59,"path":60,"stem":61},"Project Structure","\u002Fdocs\u002Fsaas-starter-kit\u002Fgetting-started\u002Fproject-structure","docs\u002F1.saas-starter-kit\u002F1.getting-started\u002F8.project-structure",false,{"title":64,"icon":65,"hide":22,"path":66,"stem":67,"children":68,"page":62},"Features","i-lucide-box","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures","docs\u002F1.saas-starter-kit\u002F2.features",[69,73,77,81,85,89,93,97,101,105,109,113,117,121,125],{"title":70,"path":71,"stem":72},"Authentication","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fauth","docs\u002F1.saas-starter-kit\u002F2.features\u002F1.auth",{"title":74,"path":75,"stem":76},"Rate Limiting","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Frate-limiting","docs\u002F1.saas-starter-kit\u002F2.features\u002F10.rate-limiting",{"title":78,"path":79,"stem":80},"WebSockets","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fwebsockets","docs\u002F1.saas-starter-kit\u002F2.features\u002F11.websockets",{"title":82,"path":83,"stem":84},"Blog","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fblog","docs\u002F1.saas-starter-kit\u002F2.features\u002F14.blog",{"title":86,"path":87,"stem":88},"Documentation","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fdocumentation","docs\u002F1.saas-starter-kit\u002F2.features\u002F15.documentation",{"title":90,"path":91,"stem":92},"Analytics","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fanalytics","docs\u002F1.saas-starter-kit\u002F2.features\u002F16.analytics",{"title":94,"path":95,"stem":96},"Platform Admin Console","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fplatform-admin","docs\u002F1.saas-starter-kit\u002F2.features\u002F17.platform-admin",{"title":98,"path":99,"stem":100},"Database","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fdatabase","docs\u002F1.saas-starter-kit\u002F2.features\u002F2.database",{"title":102,"path":103,"stem":104},"Payments & Billing","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fpayments","docs\u002F1.saas-starter-kit\u002F2.features\u002F3.payments",{"title":106,"path":107,"stem":108},"Emails","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Femails","docs\u002F1.saas-starter-kit\u002F2.features\u002F4.emails",{"title":110,"path":111,"stem":112},"Notifications","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fnotifications","docs\u002F1.saas-starter-kit\u002F2.features\u002F5.notifications",{"title":114,"path":115,"stem":116},"Role-Based Access Control","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Frbac","docs\u002F1.saas-starter-kit\u002F2.features\u002F6.rbac",{"title":118,"path":119,"stem":120},"Async Jobs","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fasync-jobs","docs\u002F1.saas-starter-kit\u002F2.features\u002F7.async-jobs",{"title":122,"path":123,"stem":124},"Storage","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Fstorage","docs\u002F1.saas-starter-kit\u002F2.features\u002F8.storage",{"title":126,"path":127,"stem":128},"Internationalization","\u002Fdocs\u002Fsaas-starter-kit\u002Ffeatures\u002Finternationalization","docs\u002F1.saas-starter-kit\u002F2.features\u002F9.internationalization",{"title":130,"icon":131,"path":132,"stem":133,"children":134,"page":62},"Concepts","i-lucide-graduation-cap","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts","docs\u002F1.saas-starter-kit\u002F3.concepts",[135,139,143,147],{"title":136,"path":137,"stem":138},"Technology Stack","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Ftech-stack","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F0.tech-stack",{"title":140,"path":141,"stem":142},"SST.dev","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Fsst","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F1.sst",{"title":144,"path":145,"stem":146},"NuxtHub","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Fnuxt-hub","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F2.nuxt-hub",{"title":148,"path":149,"stem":150},"Design System & Branding","\u002Fdocs\u002Fsaas-starter-kit\u002Fconcepts\u002Fdesign-system","docs\u002F1.saas-starter-kit\u002F3.concepts\u002F3.design-system","i-lucide-zap",{"title":153,"path":154,"stem":155,"children":156,"icon":159},"Fireskills","\u002Fdocs\u002Ffireskills","docs\u002F2.fireskills\u002F0.index",[157,160,219,249,275,293],{"title":158,"path":154,"stem":155,"icon":159},"What is Fireskills?","i-lucide-flame",{"title":20,"icon":21,"hide":22,"path":161,"stem":162,"children":163,"page":62},"\u002Fdocs\u002Ffireskills\u002Fgetting-started","docs\u002F2.fireskills\u002F1.getting-started",[164,167,171,175,179,183,187,191,195,199,203,207,211,215],{"title":27,"path":165,"stem":166},"\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fquick-start","docs\u002F2.fireskills\u002F1.getting-started\u002F00.quick-start",{"title":168,"path":169,"stem":170},"Workflow & Governance","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fworkflow","docs\u002F2.fireskills\u002F1.getting-started\u002F01.workflow",{"title":172,"path":173,"stem":174},"Project Context","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fproject-context","docs\u002F2.fireskills\u002F1.getting-started\u002F02.project-context",{"title":176,"path":177,"stem":178},"Step 0: Setup","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-setup","docs\u002F2.fireskills\u002F1.getting-started\u002F03.step-setup",{"title":180,"path":181,"stem":182},"Step 1: Inbox","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-inbox","docs\u002F2.fireskills\u002F1.getting-started\u002F04.step-inbox",{"title":184,"path":185,"stem":186},"Step 2: Backlog","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-backlog","docs\u002F2.fireskills\u002F1.getting-started\u002F05.step-backlog",{"title":188,"path":189,"stem":190},"Step 3: Specify","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-specify","docs\u002F2.fireskills\u002F1.getting-started\u002F06.step-specify",{"title":192,"path":193,"stem":194},"Step 4: Validate","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-validate","docs\u002F2.fireskills\u002F1.getting-started\u002F07.step-validate",{"title":196,"path":197,"stem":198},"Step 5: Tasks","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-tasks","docs\u002F2.fireskills\u002F1.getting-started\u002F08.step-tasks",{"title":200,"path":201,"stem":202},"Step 6: TDD\u002FBDD","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-tdd-bdd","docs\u002F2.fireskills\u002F1.getting-started\u002F09.step-tdd-bdd",{"title":204,"path":205,"stem":206},"Step 7: Implement","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-implement","docs\u002F2.fireskills\u002F1.getting-started\u002F10.step-implement",{"title":208,"path":209,"stem":210},"Step 8: Documentator","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fstep-documentator","docs\u002F2.fireskills\u002F1.getting-started\u002F11.step-documentator",{"title":212,"path":213,"stem":214},"Late Changes","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Flate-changes","docs\u002F2.fireskills\u002F1.getting-started\u002F12.late-changes",{"title":216,"path":217,"stem":218},"Other Entry Points","\u002Fdocs\u002Ffireskills\u002Fgetting-started\u002Fother-entry-points","docs\u002F2.fireskills\u002F1.getting-started\u002F13.other-entry-points",{"title":220,"icon":221,"hide":22,"path":222,"stem":223,"children":224,"page":62},"Methodology","i-lucide-git-branch","\u002Fdocs\u002Ffireskills\u002Fmethodology","docs\u002F2.fireskills\u002F2.methodology",[225,229,233,237,241,245],{"title":226,"path":227,"stem":228},"The Three Acts","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fthree-acts","docs\u002F2.fireskills\u002F2.methodology\u002F0.three-acts",{"title":230,"path":231,"stem":232},"The Three Gates","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fthree-gates","docs\u002F2.fireskills\u002F2.methodology\u002F1.three-gates",{"title":234,"path":235,"stem":236},"The Canonical Spec","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fcanonical-spec","docs\u002F2.fireskills\u002F2.methodology\u002F2.canonical-spec",{"title":238,"path":239,"stem":240},"Lifecycle States","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Flifecycle-states","docs\u002F2.fireskills\u002F2.methodology\u002F3.lifecycle-states",{"title":242,"path":243,"stem":244},"Effort Scale","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Feffort-scale","docs\u002F2.fireskills\u002F2.methodology\u002F4.effort-scale",{"title":246,"path":247,"stem":248},"Milestones & Governance","\u002Fdocs\u002Ffireskills\u002Fmethodology\u002Fgovernance-and-milestones","docs\u002F2.fireskills\u002F2.methodology\u002F5.governance-and-milestones",{"title":250,"icon":251,"hide":22,"path":252,"stem":253,"children":254,"page":62},"Skills","i-lucide-layers","\u002Fdocs\u002Ffireskills\u002Fskills","docs\u002F2.fireskills\u002F3.skills",[255,259,263,267,271],{"title":256,"path":257,"stem":258},"Catalog","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fcatalog","docs\u002F2.fireskills\u002F3.skills\u002F0.catalog",{"title":260,"path":261,"stem":262},"Act I: Define","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fact-1-define","docs\u002F2.fireskills\u002F3.skills\u002F1.act-1-define",{"title":264,"path":265,"stem":266},"Act II: Design & Prove","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fact-2-design-prove","docs\u002F2.fireskills\u002F3.skills\u002F2.act-2-design-prove",{"title":268,"path":269,"stem":270},"Act III: Deliver","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fact-3-deliver","docs\u002F2.fireskills\u002F3.skills\u002F3.act-3-deliver",{"title":272,"path":273,"stem":274},"Governance & Decisions","\u002Fdocs\u002Ffireskills\u002Fskills\u002Fgovernance-and-decisions","docs\u002F2.fireskills\u002F3.skills\u002F4.governance-and-decisions",{"title":276,"icon":277,"hide":22,"path":278,"stem":279,"children":280,"page":62},"Specialists","i-lucide-sparkles","\u002Fdocs\u002Ffireskills\u002Fspecialists","docs\u002F2.fireskills\u002F4.specialists",[281,285,289],{"title":282,"path":283,"stem":284},"Overview","\u002Fdocs\u002Ffireskills\u002Fspecialists\u002Foverview","docs\u002F2.fireskills\u002F4.specialists\u002F0.overview",{"title":286,"path":287,"stem":288},"Nuxfire Stack","\u002Fdocs\u002Ffireskills\u002Fspecialists\u002Fnuxfire-stack","docs\u002F2.fireskills\u002F4.specialists\u002F1.nuxfire-stack",{"title":290,"path":291,"stem":292},"Engineering Specialists","\u002Fdocs\u002Ffireskills\u002Fspecialists\u002Fengineering-specialists","docs\u002F2.fireskills\u002F4.specialists\u002F2.engineering-specialists",{"title":294,"icon":295,"hide":22,"path":296,"stem":297,"children":298,"page":62},"CLI & Tooling","i-lucide-terminal","\u002Fdocs\u002Ffireskills\u002Fcli","docs\u002F2.fireskills\u002F5.cli",[299,303,307],{"title":300,"path":301,"stem":302},"CLI Overview","\u002Fdocs\u002Ffireskills\u002Fcli\u002Foverview","docs\u002F2.fireskills\u002F5.cli\u002F0.overview",{"title":304,"path":305,"stem":306},"Commands","\u002Fdocs\u002Ffireskills\u002Fcli\u002Fcommands","docs\u002F2.fireskills\u002F5.cli\u002F1.commands",{"title":308,"path":309,"stem":310},"Safety Scripts","\u002Fdocs\u002Ffireskills\u002Fcli\u002Fgovernance-scripts","docs\u002F2.fireskills\u002F5.cli\u002F2.governance-scripts",{"id":312,"title":78,"body":313,"description":827,"extension":828,"meta":829,"navigation":22,"path":79,"seo":830,"stem":80,"__hash__":831},"docs\u002Fdocs\u002F1.saas-starter-kit\u002F2.features\u002F11.websockets.md",{"type":314,"value":315,"toc":818},"minimark",[316,329,334,361,365,370,377,585,588,607,611,617,736,740,759,762,768,811,814],[317,318,319,320,324,325,328],"p",{},"Real-time updates run on Cloudflare Workers and Durable Objects, behind the ",[321,322,323],"code",{},"websockets"," flag in ",[321,326,327],{},"config.ts",".",[330,331,333],"h2",{"id":332},"architecture","Architecture",[335,336,337,349,355],"ul",{},[338,339,340,341,344,345,348],"li",{},"Frontend WebSocket client: ",[321,342,343],{},"apps\u002Fapp\u002Fapp\u002Fplugins\u002Fwebsockets.client.ts"," (a companion plugin, ",[321,346,347],{},"ws-notifications.client.ts",", wires this into the notifications feature)",[338,350,351,352],{},"Backend WebSocket server: ",[321,353,354],{},"apps\u002Ffunctions\u002Fsrc\u002Fwebsockets.js",[338,356,357,358],{},"Infrastructure: ",[321,359,360],{},"infra\u002Fwebsockets.ts",[330,362,364],{"id":363},"usage","Usage",[366,367,369],"h3",{"id":368},"frontend","Frontend",[317,371,372,373,376],{},"The WebSocket client is a Nuxt plugin, available in components via ",[321,374,375],{},"$ws",":",[378,379,384],"pre",{"className":380,"code":381,"language":382,"meta":383,"style":383},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark monokai","\u002F\u002F In your component\nconst { $ws } = useNuxtApp();\n\n\u002F\u002F Subscribe to topics\n$ws.on(\"user.123\", (data) => {\n  \u002F\u002F Handle topic payload\n  console.log(\"Got update:\", data);\n});\n\n\u002F\u002F Cleanup when done\nonUnmounted(() => {\n  $ws.off(\"user.123\");\n});\n","ts","",[321,385,386,395,428,434,440,482,488,518,528,533,539,553,576],{"__ignoreMap":383},[387,388,391],"span",{"class":389,"line":390},"line",1,[387,392,394],{"class":393},"ss7Ak","\u002F\u002F In your component\n",[387,396,398,402,406,410,413,417,421,425],{"class":389,"line":397},2,[387,399,401],{"class":400},"srJo8","const",[387,403,405],{"class":404},"swvn1"," {",[387,407,409],{"class":408},"s91G_"," $ws",[387,411,412],{"class":404}," }",[387,414,416],{"class":415},"sGXK2"," =",[387,418,420],{"class":419},"sD0ED"," useNuxtApp",[387,422,424],{"class":423},"ss--_","()",[387,426,427],{"class":404},";\n",[387,429,431],{"class":389,"line":430},3,[387,432,433],{"emptyLinePlaceholder":22},"\n",[387,435,437],{"class":389,"line":436},4,[387,438,439],{"class":393},"\u002F\u002F Subscribe to topics\n",[387,441,443,445,447,450,453,457,461,463,466,469,473,476,479],{"class":389,"line":442},5,[387,444,375],{"class":423},[387,446,328],{"class":404},[387,448,449],{"class":419},"on",[387,451,452],{"class":423},"(",[387,454,456],{"class":455},"siCPE","\"",[387,458,460],{"class":459},"sLACW","user.123",[387,462,456],{"class":455},[387,464,465],{"class":404},",",[387,467,468],{"class":404}," (",[387,470,472],{"class":471},"sQgqH","data",[387,474,475],{"class":404},")",[387,477,478],{"class":400}," =>",[387,480,481],{"class":404}," {\n",[387,483,485],{"class":389,"line":484},6,[387,486,487],{"class":393},"  \u002F\u002F Handle topic payload\n",[387,489,491,494,496,499,502,504,507,509,511,514,516],{"class":389,"line":490},7,[387,492,493],{"class":423},"  console",[387,495,328],{"class":404},[387,497,498],{"class":419},"log",[387,500,452],{"class":501},"squCx",[387,503,456],{"class":455},[387,505,506],{"class":459},"Got update:",[387,508,456],{"class":455},[387,510,465],{"class":404},[387,512,513],{"class":423}," data",[387,515,475],{"class":501},[387,517,427],{"class":404},[387,519,521,524,526],{"class":389,"line":520},8,[387,522,523],{"class":404},"}",[387,525,475],{"class":423},[387,527,427],{"class":404},[387,529,531],{"class":389,"line":530},9,[387,532,433],{"emptyLinePlaceholder":22},[387,534,536],{"class":389,"line":535},10,[387,537,538],{"class":393},"\u002F\u002F Cleanup when done\n",[387,540,542,545,547,549,551],{"class":389,"line":541},11,[387,543,544],{"class":419},"onUnmounted",[387,546,452],{"class":423},[387,548,424],{"class":404},[387,550,478],{"class":400},[387,552,481],{"class":404},[387,554,556,559,561,564,566,568,570,572,574],{"class":389,"line":555},12,[387,557,558],{"class":423},"  $ws",[387,560,328],{"class":404},[387,562,563],{"class":419},"off",[387,565,452],{"class":501},[387,567,456],{"class":455},[387,569,460],{"class":459},[387,571,456],{"class":455},[387,573,475],{"class":501},[387,575,427],{"class":404},[387,577,579,581,583],{"class":389,"line":578},13,[387,580,523],{"class":404},[387,582,475],{"class":423},[387,584,427],{"class":404},[317,586,587],{},"The client automatically:",[335,589,590,601,604],{},[338,591,592,593,596,597,600],{},"Authenticates using a short-lived WebSocket ticket (",[321,594,595],{},"\u002Fapi\u002Fauth\u002Fws-ticket",") rather than the long-lived session token — the ",[321,598,599],{},"sec-websocket-protocol"," header is the only way to authenticate a browser WebSocket connection, and it's readable by page JS, so the real access token never goes there.",[338,602,603],{},"Reconnects on connection drops.",[338,605,606],{},"Resubscribes to topics after reconnection.",[366,608,610],{"id":609},"backend","Backend",[317,612,613,614,376],{},"Publish messages to topics from a tRPC procedure via ",[321,615,616],{},"ctx.sendWebsocketsEvent",[378,618,622],{"className":619,"code":620,"language":621,"meta":383,"style":383},"language-typescript shiki shiki-themes material-theme-lighter github-light github-dark monokai","async ({ ctx: { user, sendWebsocketsEvent }, input }) => {\n  \u002F\u002F ...create notification in database...\n  await sendWebsocketsEvent(\n    user.id, \u002F\u002F topic\n    { type: \"notification\", payload: notification },\n  );\n};\n","typescript",[321,623,624,662,667,678,693,724,731],{"__ignoreMap":383},[387,625,626,630,633,637,639,641,644,646,649,652,655,658,660],{"class":389,"line":390},[387,627,629],{"class":628},"sTNss","async",[387,631,632],{"class":404}," ({",[387,634,636],{"class":635},"sIDdj"," ctx",[387,638,376],{"class":404},[387,640,405],{"class":404},[387,642,643],{"class":471}," user",[387,645,465],{"class":404},[387,647,648],{"class":471}," sendWebsocketsEvent",[387,650,651],{"class":404}," },",[387,653,654],{"class":471}," input",[387,656,657],{"class":404}," })",[387,659,478],{"class":400},[387,661,481],{"class":404},[387,663,664],{"class":389,"line":397},[387,665,666],{"class":393},"  \u002F\u002F ...create notification in database...\n",[387,668,669,673,675],{"class":389,"line":430},[387,670,672],{"class":671},"sRxSC","  await",[387,674,648],{"class":419},[387,676,677],{"class":501},"(\n",[387,679,680,683,685,688,690],{"class":389,"line":436},[387,681,682],{"class":423},"    user",[387,684,328],{"class":404},[387,686,687],{"class":423},"id",[387,689,465],{"class":404},[387,691,692],{"class":393}," \u002F\u002F topic\n",[387,694,695,698,701,703,706,709,711,713,716,718,721],{"class":389,"line":442},[387,696,697],{"class":404},"    {",[387,699,700],{"class":501}," type",[387,702,376],{"class":404},[387,704,705],{"class":455}," \"",[387,707,708],{"class":459},"notification",[387,710,456],{"class":455},[387,712,465],{"class":404},[387,714,715],{"class":501}," payload",[387,717,376],{"class":404},[387,719,720],{"class":423}," notification",[387,722,723],{"class":404}," },\n",[387,725,726,729],{"class":389,"line":484},[387,727,728],{"class":501},"  )",[387,730,427],{"class":404},[387,732,733],{"class":389,"line":490},[387,734,735],{"class":404},"};\n",[330,737,739],{"id":738},"security","Security",[335,741,742,752],{},[338,743,744,745,748,749,751],{},"Authentication uses a short-lived ticket, signed with a secret shared only between the App Worker and the Websockets Worker (",[321,746,747],{},"WsTicketSecret",", ",[321,750,360],{},") — never the OpenAuth session token.",[338,753,754,755,758],{},"Topic authorization is defined by whatever the app chooses to publish to a given topic — there's no built-in per-topic ACL, so scope topic names (e.g. ",[321,756,757],{},"user.\u003Cid>",") to whoever should legitimately receive them.",[330,760,39],{"id":761},"configuration",[317,763,764,765,767],{},"Enable WebSockets by setting the ",[321,766,323],{}," flag:",[378,769,771],{"className":380,"code":770,"filename":327,"language":382,"meta":383,"style":383},"export const flags = {\n  \u002F\u002F ...\n  websockets: true,\n};\n",[321,772,773,788,793,807],{"__ignoreMap":383},[387,774,775,778,781,784,786],{"class":389,"line":390},[387,776,777],{"class":671},"export",[387,779,780],{"class":400}," const",[387,782,783],{"class":408}," flags",[387,785,416],{"class":415},[387,787,481],{"class":404},[387,789,790],{"class":389,"line":397},[387,791,792],{"class":393},"  \u002F\u002F ...\n",[387,794,795,798,800,804],{"class":389,"line":430},[387,796,797],{"class":501},"  websockets",[387,799,376],{"class":404},[387,801,803],{"class":802},"s8HiA"," true",[387,805,806],{"class":404},",\n",[387,808,809],{"class":389,"line":436},[387,810,735],{"class":404},[317,812,813],{},"This deploys the WebSocket Worker (backed by a Durable Object) and links it into the app.",[815,816,817],"style",{},"html pre.shiki code .ss7Ak, html code.shiki .ss7Ak{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit;--shiki-sepia:#88846F;--shiki-sepia-font-style:inherit}html pre.shiki code .srJo8, html code.shiki .srJo8{--shiki-light:#9C3EDA;--shiki-light-font-style:inherit;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit;--shiki-sepia:#66D9EF;--shiki-sepia-font-style:italic}html pre.shiki code .swvn1, html code.shiki .swvn1{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .s91G_, html code.shiki .s91G_{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#F8F8F2}html pre.shiki code .sGXK2, html code.shiki .sGXK2{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .sD0ED, html code.shiki .sD0ED{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}html pre.shiki code .ss--_, html code.shiki .ss--_{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .siCPE, html code.shiki .siCPE{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html pre.shiki code .sLACW, html code.shiki .sLACW{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html pre.shiki code .sQgqH, html code.shiki .sQgqH{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit;--shiki-sepia:#FD971F;--shiki-sepia-font-style:italic}html pre.shiki code .squCx, html code.shiki .squCx{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}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 .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sTNss, html code.shiki .sTNss{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .sIDdj, html code.shiki .sIDdj{--shiki-light:#E53935;--shiki-default:#E36209;--shiki-dark:#FFAB70;--shiki-sepia:#F8F8F2}html pre.shiki code .sRxSC, html code.shiki .sRxSC{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit;--shiki-sepia:#F92672;--shiki-sepia-font-style:inherit}html pre.shiki code .s8HiA, html code.shiki .s8HiA{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF}",{"title":383,"searchDepth":397,"depth":397,"links":819},[820,821,825,826],{"id":332,"depth":397,"text":333},{"id":363,"depth":397,"text":364,"children":822},[823,824],{"id":368,"depth":430,"text":369},{"id":609,"depth":430,"text":610},{"id":738,"depth":397,"text":739},{"id":761,"depth":397,"text":39},"A pub\u002Fsub WebSockets implementation based on Cloudflare Durable Objects, behind the websockets feature flag.","md",{},{"title":78,"description":827},"QNqks4DIiKyr6VYoxkgBJw2VU4NNmdhuopXW8oG5occ",1790707485288]