[{"data":1,"prerenderedAt":1623},["ShallowReactive",2],{"navigation":3,"-adapters-node":88,"-adapters-node-surround":1618},[4,51],{"title":5,"path":6,"stem":7,"children":8,"icon":10},"Guide","\u002Fguide","1.guide\u002F1.index",[9,11,16,21,26,31,36,41,46],{"title":5,"path":6,"stem":7,"icon":10},"ph:book-open-duotone",{"title":12,"path":13,"stem":14,"icon":15},"Hooks","\u002Fguide\u002Fhooks","1.guide\u002F2.hooks","material-symbols-light:data-object",{"title":17,"path":18,"stem":19,"icon":20},"Peer","\u002Fguide\u002Fpeer","1.guide\u002F3.peer","mynaui:api",{"title":22,"path":23,"stem":24,"icon":25},"Message","\u002Fguide\u002Fmessage","1.guide\u002F4.message","solar:letter-line-duotone",{"title":27,"path":28,"stem":29,"icon":30},"Pub \u002F Sub","\u002Fguide\u002Fpubsub","1.guide\u002F5.pubsub","simple-icons:googlepubsub",{"title":32,"path":33,"stem":34,"icon":35},"Sync Backplane","\u002Fguide\u002Fsync","1.guide\u002F6.sync","tabler:refresh",{"title":37,"path":38,"stem":39,"icon":40},"Resolver API","\u002Fguide\u002Fresolver","1.guide\u002F7.resolver","tabler:route",{"title":42,"path":43,"stem":44,"icon":45},"WebSocket Proxy","\u002Fguide\u002Fproxy","1.guide\u002F8.proxy","tabler:arrows-exchange",{"title":47,"path":48,"stem":49,"icon":50},"WebSocket Client","\u002Fguide\u002Fclient","1.guide\u002F9.client","tabler:plug-connected",{"title":52,"path":53,"stem":54,"children":55,"icon":57},"Adapters","\u002Fadapters","2.adapters\u002F1.index",[56,58,63,68,73,78,83],{"title":52,"path":53,"stem":54,"icon":57},"emojione-monotone:electric-plug",{"title":59,"path":60,"stem":61,"icon":62},"Bun","\u002Fadapters\u002Fbun","2.adapters\u002Fbun","simple-icons:bun",{"title":64,"path":65,"stem":66,"icon":67},"Bunny","\u002Fadapters\u002Fbunny","2.adapters\u002Fbunny","mdi:rabbit",{"title":69,"path":70,"stem":71,"icon":72},"Cloudflare","\u002Fadapters\u002Fcloudflare","2.adapters\u002Fcloudflare","devicon-plain:cloudflareworkers",{"title":74,"path":75,"stem":76,"icon":77},"Deno","\u002Fadapters\u002Fdeno","2.adapters\u002Fdeno","teenyicons:deno-solid",{"title":79,"path":80,"stem":81,"icon":82},"Node.js","\u002Fadapters\u002Fnode","2.adapters\u002Fnode","akar-icons:node-fill",{"title":84,"path":85,"stem":86,"icon":87},"SSE","\u002Fadapters\u002Fsse","2.adapters\u002Fsse","clarity:two-way-arrows-line",{"id":89,"title":79,"body":90,"description":1612,"extension":1613,"meta":1614,"navigation":1615,"path":80,"seo":1616,"stem":81,"__hash__":1617},"content\u002F2.adapters\u002Fnode.md",{"type":91,"value":92,"toc":1605,"icon":82},"minimark",[93,114,133,396,418,427,471,494,563,589,593,616,851,869,880,884,893,900,1115,1133,1137,1163,1578,1589,1601],[94,95,96],"tip",{},[97,98,99,100,104,105,108,109,113],"p",{},"\nYou can use ",[101,102,103],"code",{},"serve"," function from ",[101,106,107],{},"crossws\u002Fserver"," to ",[110,111,112],"strong",{},"automatically"," integrate crossws with Node.js!",[97,115,116,117,120,121,124,125,132],{},"To manually integrate crossws with your Node.js HTTP server, you need to connect the ",[101,118,119],{},"upgrade"," event to the ",[101,122,123],{},"handleUpgrade"," method returned from the adapter. crossws uses a prebundled version of ",[126,127,131],"a",{"href":128,"rel":129},"https:\u002F\u002Fgithub.com\u002Fwebsockets\u002Fws",[130],"nofollow","ws",".",[134,135,140],"pre",{"className":136,"code":137,"language":138,"meta":139,"style":139},"language-ts shiki shiki-themes github-light github-dark github-dark","import { createServer } from \"node:http\";\nimport crossws from \"crossws\u002Fadapters\u002Fnode\";\n\nconst ws = crossws({\n  hooks: {\n    message: console.log,\n  },\n});\n\nconst server = createServer((req, res) => {\n  res.end(\n    `\u003Cscript>new WebSocket(\"ws:\u002F\u002Flocalhost:3000\").addEventListener('open', (e) => e.target.send(\"Hello from client!\"));\u003C\u002Fscript>`,\n  );\n}).listen(3000);\n\nserver.on(\"upgrade\", (req, socket, head) => {\n  if (req.headers.upgrade === \"websocket\") {\n    ws.handleUpgrade(req, socket, head);\n  }\n});\n","ts","",[101,141,142,165,180,187,207,213,219,225,231,236,271,283,292,298,316,321,356,374,385,391],{"__ignoreMap":139},[143,144,147,151,155,158,162],"span",{"class":145,"line":146},"line",1,[143,148,150],{"class":149},"so5gQ","import",[143,152,154],{"class":153},"slsVL"," { createServer } ",[143,156,157],{"class":149},"from",[143,159,161],{"class":160},"sfrk1"," \"node:http\"",[143,163,164],{"class":153},";\n",[143,166,168,170,173,175,178],{"class":145,"line":167},2,[143,169,150],{"class":149},[143,171,172],{"class":153}," crossws ",[143,174,157],{"class":149},[143,176,177],{"class":160}," \"crossws\u002Fadapters\u002Fnode\"",[143,179,164],{"class":153},[143,181,183],{"class":145,"line":182},3,[143,184,186],{"emptyLinePlaceholder":185},true,"\n",[143,188,190,193,197,200,204],{"class":145,"line":189},4,[143,191,192],{"class":149},"const",[143,194,196],{"class":195},"suiK_"," ws",[143,198,199],{"class":149}," =",[143,201,203],{"class":202},"shcOC"," crossws",[143,205,206],{"class":153},"({\n",[143,208,210],{"class":145,"line":209},5,[143,211,212],{"class":153},"  hooks: {\n",[143,214,216],{"class":145,"line":215},6,[143,217,218],{"class":153},"    message: console.log,\n",[143,220,222],{"class":145,"line":221},7,[143,223,224],{"class":153},"  },\n",[143,226,228],{"class":145,"line":227},8,[143,229,230],{"class":153},"});\n",[143,232,234],{"class":145,"line":233},9,[143,235,186],{"emptyLinePlaceholder":185},[143,237,239,241,244,246,249,252,256,259,262,265,268],{"class":145,"line":238},10,[143,240,192],{"class":149},[143,242,243],{"class":195}," server",[143,245,199],{"class":149},[143,247,248],{"class":202}," createServer",[143,250,251],{"class":153},"((",[143,253,255],{"class":254},"sQHwn","req",[143,257,258],{"class":153},", ",[143,260,261],{"class":254},"res",[143,263,264],{"class":153},") ",[143,266,267],{"class":149},"=>",[143,269,270],{"class":153}," {\n",[143,272,274,277,280],{"class":145,"line":273},11,[143,275,276],{"class":153},"  res.",[143,278,279],{"class":202},"end",[143,281,282],{"class":153},"(\n",[143,284,286,289],{"class":145,"line":285},12,[143,287,288],{"class":160},"    `\u003Cscript>new WebSocket(\"ws:\u002F\u002Flocalhost:3000\").addEventListener('open', (e) => e.target.send(\"Hello from client!\"));\u003C\u002Fscript>`",[143,290,291],{"class":153},",\n",[143,293,295],{"class":145,"line":294},13,[143,296,297],{"class":153},"  );\n",[143,299,301,304,307,310,313],{"class":145,"line":300},14,[143,302,303],{"class":153},"}).",[143,305,306],{"class":202},"listen",[143,308,309],{"class":153},"(",[143,311,312],{"class":195},"3000",[143,314,315],{"class":153},");\n",[143,317,319],{"class":145,"line":318},15,[143,320,186],{"emptyLinePlaceholder":185},[143,322,324,327,330,332,335,338,340,342,345,347,350,352,354],{"class":145,"line":323},16,[143,325,326],{"class":153},"server.",[143,328,329],{"class":202},"on",[143,331,309],{"class":153},[143,333,334],{"class":160},"\"upgrade\"",[143,336,337],{"class":153},", (",[143,339,255],{"class":254},[143,341,258],{"class":153},[143,343,344],{"class":254},"socket",[143,346,258],{"class":153},[143,348,349],{"class":254},"head",[143,351,264],{"class":153},[143,353,267],{"class":149},[143,355,270],{"class":153},[143,357,359,362,365,368,371],{"class":145,"line":358},17,[143,360,361],{"class":149},"  if",[143,363,364],{"class":153}," (req.headers.upgrade ",[143,366,367],{"class":149},"===",[143,369,370],{"class":160}," \"websocket\"",[143,372,373],{"class":153},") {\n",[143,375,377,380,382],{"class":145,"line":376},18,[143,378,379],{"class":153},"    ws.",[143,381,123],{"class":202},[143,383,384],{"class":153},"(req, socket, head);\n",[143,386,388],{"class":145,"line":387},19,[143,389,390],{"class":153},"  }\n",[143,392,394],{"class":145,"line":393},20,[143,395,230],{"class":153},[397,398,399],"read-more",{},[97,400,401,402,409,410,417],{},"See ",[126,403,406],{"href":404,"rel":405},"https:\u002F\u002Fgithub.com\u002Fh3js\u002Fcrossws\u002Fblob\u002Fmain\u002Ftest\u002Ffixture\u002Fnode.ts",[130],[101,407,408],{},"test\u002Ffixture\u002Fnode.ts"," for demo and ",[126,411,414],{"href":412,"rel":413},"https:\u002F\u002Fgithub.com\u002Fh3js\u002Fcrossws\u002Fblob\u002Fmain\u002Fsrc\u002Fadapters\u002Fnode.ts",[130],[101,415,416],{},"src\u002Fadapters\u002Fnode.ts"," for implementation.",[419,420,422,423,426],"h2",{"id":421},"detecting-dead-connections-idletimeout","Detecting dead connections (",[101,424,425],{},"idleTimeout",")",[97,428,429,430,433,434,437,438,441,442,444,445,448,449,451,452,455,456,437,458,460,461,464,465,467,468,470],{},"A normal disconnect — closed tab, killed process, ",[101,431,432],{},"socket.destroy()"," — sends a TCP ",[101,435,436],{},"FIN","\u002F",[101,439,440],{},"RST",", so ",[101,443,131],{}," emits ",[101,446,447],{},"close"," within milliseconds and your ",[101,450,447],{}," hook fires. But a ",[110,453,454],{},"half-open"," connection (laptop sleep, NAT\u002Fmobile idle timeout, power loss, a yanked cable) vanishes without ever delivering a ",[101,457,436],{},[101,459,440],{},". The OS socket stays ",[101,462,463],{},"ESTABLISHED"," indefinitely, ",[101,466,131],{}," never emits ",[101,469,447],{},", and the peer — plus anything it owns, such as a proxied upstream connection — leaks.",[97,472,473,474,476,477,482,483,486,487,493],{},"Unlike the native runtimes (Bun, Deno, uWebSockets), Node's ",[101,475,131],{}," does not ping idle connections for you — so crossws emulates it. The adapter pings every peer and terminates any that miss the pong, driven by the shared ",[126,478,480],{"href":479},"\u002Fadapters#idletimeout",[101,481,425],{}," option (in ",[110,484,485],{},"seconds","). It ",[110,488,489,490],{},"defaults to ",[101,491,492],{},"30"," on every runtime, so half-open connections are cleaned up out of the box:",[134,495,497],{"className":136,"code":496,"language":138,"meta":139,"style":139},"import crossws from \"crossws\u002Fadapters\u002Fnode\";\n\nconst ws = crossws({\n  \u002F\u002F Optional — defaults to 30. Lower it to detect dead peers faster, or\n  \u002F\u002F set 0 to disable.\n  idleTimeout: 30,\n  hooks: {\n    message: console.log,\n  },\n});\n",[101,498,499,511,515,527,533,538,547,551,555,559],{"__ignoreMap":139},[143,500,501,503,505,507,509],{"class":145,"line":146},[143,502,150],{"class":149},[143,504,172],{"class":153},[143,506,157],{"class":149},[143,508,177],{"class":160},[143,510,164],{"class":153},[143,512,513],{"class":145,"line":167},[143,514,186],{"emptyLinePlaceholder":185},[143,516,517,519,521,523,525],{"class":145,"line":182},[143,518,192],{"class":149},[143,520,196],{"class":195},[143,522,199],{"class":149},[143,524,203],{"class":202},[143,526,206],{"class":153},[143,528,529],{"class":145,"line":189},[143,530,532],{"class":531},"sCsY4","  \u002F\u002F Optional — defaults to 30. Lower it to detect dead peers faster, or\n",[143,534,535],{"class":145,"line":209},[143,536,537],{"class":531},"  \u002F\u002F set 0 to disable.\n",[143,539,540,543,545],{"class":145,"line":215},[143,541,542],{"class":153},"  idleTimeout: ",[143,544,492],{"class":195},[143,546,291],{"class":153},[143,548,549],{"class":145,"line":221},[143,550,212],{"class":153},[143,552,553],{"class":145,"line":227},[143,554,218],{"class":153},[143,556,557],{"class":145,"line":233},[143,558,224],{"class":153},[143,560,561],{"class":145,"line":238},[143,562,230],{"class":153},[97,564,565,566,568,569,572,573,437,575,578,579,584,585,588],{},"Terminated peers surface through the usual ",[101,567,447],{}," hook (code ",[101,570,571],{},"1006","), so any teardown wired to ",[101,574,447],{},[101,576,577],{},"error"," (including ",[126,580,581],{"href":43},[101,582,583],{},"createWebSocketProxy"," closing its upstream) runs unchanged. Pass ",[101,586,587],{},"idleTimeout: 0"," to opt out. The same option and default apply on the Bun, Deno, and uWebSockets adapters, where it maps to the runtime's native idle timeout.",[419,590,592],{"id":591},"delegating-to-an-existing-nodejs-upgrade-handler","Delegating to an existing Node.js upgrade handler",[97,594,595,596,599,600,258,605,258,608,611,612,615],{},"If you already have a Node.js WebSocket library that exposes a raw ",[101,597,598],{},"(req, socket, head)"," upgrade handler (e.g. ",[126,601,603],{"href":128,"rel":602},[130],[101,604,131],{},[101,606,607],{},"socket.io",[101,609,610],{},"express-ws","), you can route to it through crossws using ",[101,613,614],{},"fromNodeUpgradeHandler",". This lets you keep crossws's upgrade-time request handling while delegating the WebSocket lifecycle to your existing library.",[134,617,619],{"className":136,"code":618,"language":138,"meta":139,"style":139},"import { WebSocketServer } from \"ws\";\nimport { fromNodeUpgradeHandler } from \"crossws\u002Fadapters\u002Fnode\";\nimport { serve } from \"crossws\u002Fserver\u002Fnode\";\n\nconst wss = new WebSocketServer({ noServer: true });\nwss.on(\"connection\", (ws) => {\n  ws.on(\"message\", (data) => ws.send(data));\n});\n\nserve({\n  fetch: () => new Response(\"ok\"),\n  websocket: fromNodeUpgradeHandler((req, socket, head) => {\n    wss.handleUpgrade(req, socket, head, (ws) => {\n      wss.emit(\"connection\", ws, req);\n    });\n  }),\n});\n",[101,620,621,635,648,662,666,690,712,742,746,750,756,779,804,822,837,842,847],{"__ignoreMap":139},[143,622,623,625,628,630,633],{"class":145,"line":146},[143,624,150],{"class":149},[143,626,627],{"class":153}," { WebSocketServer } ",[143,629,157],{"class":149},[143,631,632],{"class":160}," \"ws\"",[143,634,164],{"class":153},[143,636,637,639,642,644,646],{"class":145,"line":167},[143,638,150],{"class":149},[143,640,641],{"class":153}," { fromNodeUpgradeHandler } ",[143,643,157],{"class":149},[143,645,177],{"class":160},[143,647,164],{"class":153},[143,649,650,652,655,657,660],{"class":145,"line":182},[143,651,150],{"class":149},[143,653,654],{"class":153}," { serve } ",[143,656,157],{"class":149},[143,658,659],{"class":160}," \"crossws\u002Fserver\u002Fnode\"",[143,661,164],{"class":153},[143,663,664],{"class":145,"line":189},[143,665,186],{"emptyLinePlaceholder":185},[143,667,668,670,673,675,678,681,684,687],{"class":145,"line":209},[143,669,192],{"class":149},[143,671,672],{"class":195}," wss",[143,674,199],{"class":149},[143,676,677],{"class":149}," new",[143,679,680],{"class":202}," WebSocketServer",[143,682,683],{"class":153},"({ noServer: ",[143,685,686],{"class":195},"true",[143,688,689],{"class":153}," });\n",[143,691,692,695,697,699,702,704,706,708,710],{"class":145,"line":215},[143,693,694],{"class":153},"wss.",[143,696,329],{"class":202},[143,698,309],{"class":153},[143,700,701],{"class":160},"\"connection\"",[143,703,337],{"class":153},[143,705,131],{"class":254},[143,707,264],{"class":153},[143,709,267],{"class":149},[143,711,270],{"class":153},[143,713,714,717,719,721,724,726,729,731,733,736,739],{"class":145,"line":221},[143,715,716],{"class":153},"  ws.",[143,718,329],{"class":202},[143,720,309],{"class":153},[143,722,723],{"class":160},"\"message\"",[143,725,337],{"class":153},[143,727,728],{"class":254},"data",[143,730,264],{"class":153},[143,732,267],{"class":149},[143,734,735],{"class":153}," ws.",[143,737,738],{"class":202},"send",[143,740,741],{"class":153},"(data));\n",[143,743,744],{"class":145,"line":227},[143,745,230],{"class":153},[143,747,748],{"class":145,"line":233},[143,749,186],{"emptyLinePlaceholder":185},[143,751,752,754],{"class":145,"line":238},[143,753,103],{"class":202},[143,755,206],{"class":153},[143,757,758,761,764,766,768,771,773,776],{"class":145,"line":273},[143,759,760],{"class":202},"  fetch",[143,762,763],{"class":153},": () ",[143,765,267],{"class":149},[143,767,677],{"class":149},[143,769,770],{"class":202}," Response",[143,772,309],{"class":153},[143,774,775],{"class":160},"\"ok\"",[143,777,778],{"class":153},"),\n",[143,780,781,784,786,788,790,792,794,796,798,800,802],{"class":145,"line":285},[143,782,783],{"class":153},"  websocket: ",[143,785,614],{"class":202},[143,787,251],{"class":153},[143,789,255],{"class":254},[143,791,258],{"class":153},[143,793,344],{"class":254},[143,795,258],{"class":153},[143,797,349],{"class":254},[143,799,264],{"class":153},[143,801,267],{"class":149},[143,803,270],{"class":153},[143,805,806,809,811,814,816,818,820],{"class":145,"line":294},[143,807,808],{"class":153},"    wss.",[143,810,123],{"class":202},[143,812,813],{"class":153},"(req, socket, head, (",[143,815,131],{"class":254},[143,817,264],{"class":153},[143,819,267],{"class":149},[143,821,270],{"class":153},[143,823,824,827,830,832,834],{"class":145,"line":300},[143,825,826],{"class":153},"      wss.",[143,828,829],{"class":202},"emit",[143,831,309],{"class":153},[143,833,701],{"class":160},[143,835,836],{"class":153},", ws, req);\n",[143,838,839],{"class":145,"line":318},[143,840,841],{"class":153},"    });\n",[143,843,844],{"class":145,"line":323},[143,845,846],{"class":153},"  }),\n",[143,848,849],{"class":145,"line":358},[143,850,230],{"class":153},[97,852,853,854,258,857,258,860,258,862,864,865,868],{},"The underlying handler takes full ownership of the socket, so crossws's other lifecycle hooks (",[101,855,856],{},"open",[101,858,859],{},"message",[101,861,447],{},[101,863,577],{},") are ",[110,866,867],{},"not"," invoked for connections routed through it — manage the WebSocket lifecycle inside your own library as usual.",[870,871,872],"note",{},[97,873,874,876,877,132],{},[101,875,614],{}," only works on the Node.js runtime, and must be used via the crossws node server plugin so the request carries ",[101,878,879],{},"runtime.node.upgrade.{socket, head}",[419,881,883],{"id":882},"uwebsockets","uWebSockets",[97,885,886,887,892],{},"You can alternatively use ",[126,888,891],{"href":889,"rel":890},"https:\u002F\u002Fgithub.com\u002FuNetworking\u002FuWebSockets.js",[130],"uWebSockets.js"," for Node.js servers.",[97,894,895,896,899],{},"First add ",[101,897,898],{},"uNetworking\u002FuWebSockets.js"," as a dependency.",[134,901,903],{"className":136,"code":902,"language":138,"meta":139,"style":139},"import { App } from \"uWebSockets.js\";\nimport crossws from \"crossws\u002Fadapters\u002Fuws\";\n\nconst ws = crossws({\n  hooks: {\n    message: console.log,\n  },\n});\n\nconst server = App().ws(\"\u002F*\", ws.websocket);\n\nserver.get(\"\u002F*\", (res, req) => {\n  res.writeStatus(\"200 OK\").writeHeader(\"Content-Type\", \"text\u002Fhtml\");\n  res.end(\n    `\u003Cscript>new WebSocket(\"ws:\u002F\u002Flocalhost:3000\").addEventListener('open', (e) => e.target.send(\"Hello from client!\"));\u003C\u002Fscript>`,\n  );\n});\n\nserver.listen(3001, () => {\n  console.log(\"Listening to port 3001\");\n});\n",[101,904,905,919,932,936,948,952,956,960,964,968,992,996,1021,1051,1059,1065,1069,1073,1077,1095,1110],{"__ignoreMap":139},[143,906,907,909,912,914,917],{"class":145,"line":146},[143,908,150],{"class":149},[143,910,911],{"class":153}," { App } ",[143,913,157],{"class":149},[143,915,916],{"class":160}," \"uWebSockets.js\"",[143,918,164],{"class":153},[143,920,921,923,925,927,930],{"class":145,"line":167},[143,922,150],{"class":149},[143,924,172],{"class":153},[143,926,157],{"class":149},[143,928,929],{"class":160}," \"crossws\u002Fadapters\u002Fuws\"",[143,931,164],{"class":153},[143,933,934],{"class":145,"line":182},[143,935,186],{"emptyLinePlaceholder":185},[143,937,938,940,942,944,946],{"class":145,"line":189},[143,939,192],{"class":149},[143,941,196],{"class":195},[143,943,199],{"class":149},[143,945,203],{"class":202},[143,947,206],{"class":153},[143,949,950],{"class":145,"line":209},[143,951,212],{"class":153},[143,953,954],{"class":145,"line":215},[143,955,218],{"class":153},[143,957,958],{"class":145,"line":221},[143,959,224],{"class":153},[143,961,962],{"class":145,"line":227},[143,963,230],{"class":153},[143,965,966],{"class":145,"line":233},[143,967,186],{"emptyLinePlaceholder":185},[143,969,970,972,974,976,979,982,984,986,989],{"class":145,"line":238},[143,971,192],{"class":149},[143,973,243],{"class":195},[143,975,199],{"class":149},[143,977,978],{"class":202}," App",[143,980,981],{"class":153},"().",[143,983,131],{"class":202},[143,985,309],{"class":153},[143,987,988],{"class":160},"\"\u002F*\"",[143,990,991],{"class":153},", ws.websocket);\n",[143,993,994],{"class":145,"line":273},[143,995,186],{"emptyLinePlaceholder":185},[143,997,998,1000,1003,1005,1007,1009,1011,1013,1015,1017,1019],{"class":145,"line":285},[143,999,326],{"class":153},[143,1001,1002],{"class":202},"get",[143,1004,309],{"class":153},[143,1006,988],{"class":160},[143,1008,337],{"class":153},[143,1010,261],{"class":254},[143,1012,258],{"class":153},[143,1014,255],{"class":254},[143,1016,264],{"class":153},[143,1018,267],{"class":149},[143,1020,270],{"class":153},[143,1022,1023,1025,1028,1030,1033,1036,1039,1041,1044,1046,1049],{"class":145,"line":294},[143,1024,276],{"class":153},[143,1026,1027],{"class":202},"writeStatus",[143,1029,309],{"class":153},[143,1031,1032],{"class":160},"\"200 OK\"",[143,1034,1035],{"class":153},").",[143,1037,1038],{"class":202},"writeHeader",[143,1040,309],{"class":153},[143,1042,1043],{"class":160},"\"Content-Type\"",[143,1045,258],{"class":153},[143,1047,1048],{"class":160},"\"text\u002Fhtml\"",[143,1050,315],{"class":153},[143,1052,1053,1055,1057],{"class":145,"line":300},[143,1054,276],{"class":153},[143,1056,279],{"class":202},[143,1058,282],{"class":153},[143,1060,1061,1063],{"class":145,"line":318},[143,1062,288],{"class":160},[143,1064,291],{"class":153},[143,1066,1067],{"class":145,"line":323},[143,1068,297],{"class":153},[143,1070,1071],{"class":145,"line":358},[143,1072,230],{"class":153},[143,1074,1075],{"class":145,"line":376},[143,1076,186],{"emptyLinePlaceholder":185},[143,1078,1079,1081,1083,1085,1088,1091,1093],{"class":145,"line":387},[143,1080,326],{"class":153},[143,1082,306],{"class":202},[143,1084,309],{"class":153},[143,1086,1087],{"class":195},"3001",[143,1089,1090],{"class":153},", () ",[143,1092,267],{"class":149},[143,1094,270],{"class":153},[143,1096,1097,1100,1103,1105,1108],{"class":145,"line":393},[143,1098,1099],{"class":153},"  console.",[143,1101,1102],{"class":202},"log",[143,1104,309],{"class":153},[143,1106,1107],{"class":160},"\"Listening to port 3001\"",[143,1109,315],{"class":153},[143,1111,1113],{"class":145,"line":1112},21,[143,1114,230],{"class":153},[397,1116,1117],{},[97,1118,401,1119,409,1126,417],{},[126,1120,1123],{"href":1121,"rel":1122},"https:\u002F\u002Fgithub.com\u002Fh3js\u002Fcrossws\u002Fblob\u002Fmain\u002Ftest\u002Ffixture\u002Fnode-uws.ts",[130],[101,1124,1125],{},"test\u002Ffixture\u002Fnode-uws.ts",[126,1127,1130],{"href":1128,"rel":1129},"https:\u002F\u002Fgithub.com\u002Fh3js\u002Fcrossws\u002Fblob\u002Fmain\u002Fsrc\u002Fadapters\u002Fnode-uws.ts",[130],[101,1131,1132],{},"src\u002Fadapters\u002Fnode-uws.ts",[419,1134,1136],{"id":1135},"socketio","Socket.IO",[97,1138,1139,1143,1144,1147,1148,1150,1151,1154,1155,1158,1159,1162],{},[126,1140,1136],{"href":1141,"rel":1142},"https:\u002F\u002Fsocket.io\u002F",[130]," speaks two transports: a WebSocket upgrade and an HTTP long-polling fallback served under ",[101,1145,1146],{},"\u002Fsocket.io\u002F",". Both can be routed through crossws by combining ",[101,1149,614],{}," with srvx's ",[101,1152,1153],{},"fetchNodeHandler"," helper to forward the polling path (and the optional client bundle at ",[101,1156,1157],{},"\u002Fsocket.io\u002Fsocket.io.js",") to socket.io's own Node-style ",[101,1160,1161],{},"(req, res)"," listener:",[134,1164,1166],{"className":136,"code":1165,"language":138,"meta":139,"style":139},"import { createServer } from \"node:http\";\nimport { Server as SocketIOServer } from \"socket.io\";\nimport { fromNodeUpgradeHandler } from \"crossws\u002Fadapters\u002Fnode\";\nimport { serve } from \"crossws\u002Fserver\u002Fnode\";\nimport { fetchNodeHandler } from \"srvx\u002Fnode\";\n\n\u002F\u002F Attach socket.io to a throwaway http.Server — it never `.listen()`s,\n\u002F\u002F but the dummy server now holds socket.io's own `request` listener,\n\u002F\u002F which handles the client bundle AND delegates polling to engine.io.\nconst dummyServer = createServer();\nconst io = new SocketIOServer(dummyServer, { serveClient: true });\nconst [socketIoRequestListener] = dummyServer.listeners(\"request\");\n\nio.on(\"connection\", (socket) => {\n  socket.emit(\"welcome\", { id: socket.id });\n  socket.on(\"message\", (text) => io.emit(\"message\", { from: socket.id, text }));\n});\n\nconst server = serve({\n  port: 3000,\n  async fetch(req) {\n    const url = new URL(req.url);\n    if (url.pathname.startsWith(\"\u002Fsocket.io\u002F\")) {\n      return fetchNodeHandler(socketIoRequestListener, req);\n    }\n    return new Response(\"ok\");\n  },\n  websocket: fromNodeUpgradeHandler((req, socket, head) => {\n    io.engine.handleUpgrade(req, socket, head);\n  }),\n});\n\nawait server.ready();\n",[101,1167,1168,1180,1200,1212,1224,1238,1242,1247,1252,1257,1271,1292,1321,1325,1346,1361,1392,1396,1400,1413,1422,1436,1455,1475,1487,1493,1509,1514,1539,1549,1554,1559,1564],{"__ignoreMap":139},[143,1169,1170,1172,1174,1176,1178],{"class":145,"line":146},[143,1171,150],{"class":149},[143,1173,154],{"class":153},[143,1175,157],{"class":149},[143,1177,161],{"class":160},[143,1179,164],{"class":153},[143,1181,1182,1184,1187,1190,1193,1195,1198],{"class":145,"line":167},[143,1183,150],{"class":149},[143,1185,1186],{"class":153}," { Server ",[143,1188,1189],{"class":149},"as",[143,1191,1192],{"class":153}," SocketIOServer } ",[143,1194,157],{"class":149},[143,1196,1197],{"class":160}," \"socket.io\"",[143,1199,164],{"class":153},[143,1201,1202,1204,1206,1208,1210],{"class":145,"line":182},[143,1203,150],{"class":149},[143,1205,641],{"class":153},[143,1207,157],{"class":149},[143,1209,177],{"class":160},[143,1211,164],{"class":153},[143,1213,1214,1216,1218,1220,1222],{"class":145,"line":189},[143,1215,150],{"class":149},[143,1217,654],{"class":153},[143,1219,157],{"class":149},[143,1221,659],{"class":160},[143,1223,164],{"class":153},[143,1225,1226,1228,1231,1233,1236],{"class":145,"line":209},[143,1227,150],{"class":149},[143,1229,1230],{"class":153}," { fetchNodeHandler } ",[143,1232,157],{"class":149},[143,1234,1235],{"class":160}," \"srvx\u002Fnode\"",[143,1237,164],{"class":153},[143,1239,1240],{"class":145,"line":215},[143,1241,186],{"emptyLinePlaceholder":185},[143,1243,1244],{"class":145,"line":221},[143,1245,1246],{"class":531},"\u002F\u002F Attach socket.io to a throwaway http.Server — it never `.listen()`s,\n",[143,1248,1249],{"class":145,"line":227},[143,1250,1251],{"class":531},"\u002F\u002F but the dummy server now holds socket.io's own `request` listener,\n",[143,1253,1254],{"class":145,"line":233},[143,1255,1256],{"class":531},"\u002F\u002F which handles the client bundle AND delegates polling to engine.io.\n",[143,1258,1259,1261,1264,1266,1268],{"class":145,"line":238},[143,1260,192],{"class":149},[143,1262,1263],{"class":195}," dummyServer",[143,1265,199],{"class":149},[143,1267,248],{"class":202},[143,1269,1270],{"class":153},"();\n",[143,1272,1273,1275,1278,1280,1282,1285,1288,1290],{"class":145,"line":273},[143,1274,192],{"class":149},[143,1276,1277],{"class":195}," io",[143,1279,199],{"class":149},[143,1281,677],{"class":149},[143,1283,1284],{"class":202}," SocketIOServer",[143,1286,1287],{"class":153},"(dummyServer, { serveClient: ",[143,1289,686],{"class":195},[143,1291,689],{"class":153},[143,1293,1294,1296,1299,1302,1305,1308,1311,1314,1316,1319],{"class":145,"line":285},[143,1295,192],{"class":149},[143,1297,1298],{"class":153}," [",[143,1300,1301],{"class":195},"socketIoRequestListener",[143,1303,1304],{"class":153},"] ",[143,1306,1307],{"class":149},"=",[143,1309,1310],{"class":153}," dummyServer.",[143,1312,1313],{"class":202},"listeners",[143,1315,309],{"class":153},[143,1317,1318],{"class":160},"\"request\"",[143,1320,315],{"class":153},[143,1322,1323],{"class":145,"line":294},[143,1324,186],{"emptyLinePlaceholder":185},[143,1326,1327,1330,1332,1334,1336,1338,1340,1342,1344],{"class":145,"line":300},[143,1328,1329],{"class":153},"io.",[143,1331,329],{"class":202},[143,1333,309],{"class":153},[143,1335,701],{"class":160},[143,1337,337],{"class":153},[143,1339,344],{"class":254},[143,1341,264],{"class":153},[143,1343,267],{"class":149},[143,1345,270],{"class":153},[143,1347,1348,1351,1353,1355,1358],{"class":145,"line":318},[143,1349,1350],{"class":153},"  socket.",[143,1352,829],{"class":202},[143,1354,309],{"class":153},[143,1356,1357],{"class":160},"\"welcome\"",[143,1359,1360],{"class":153},", { id: socket.id });\n",[143,1362,1363,1365,1367,1369,1371,1373,1376,1378,1380,1383,1385,1387,1389],{"class":145,"line":323},[143,1364,1350],{"class":153},[143,1366,329],{"class":202},[143,1368,309],{"class":153},[143,1370,723],{"class":160},[143,1372,337],{"class":153},[143,1374,1375],{"class":254},"text",[143,1377,264],{"class":153},[143,1379,267],{"class":149},[143,1381,1382],{"class":153}," io.",[143,1384,829],{"class":202},[143,1386,309],{"class":153},[143,1388,723],{"class":160},[143,1390,1391],{"class":153},", { from: socket.id, text }));\n",[143,1393,1394],{"class":145,"line":358},[143,1395,230],{"class":153},[143,1397,1398],{"class":145,"line":376},[143,1399,186],{"emptyLinePlaceholder":185},[143,1401,1402,1404,1406,1408,1411],{"class":145,"line":387},[143,1403,192],{"class":149},[143,1405,243],{"class":195},[143,1407,199],{"class":149},[143,1409,1410],{"class":202}," serve",[143,1412,206],{"class":153},[143,1414,1415,1418,1420],{"class":145,"line":393},[143,1416,1417],{"class":153},"  port: ",[143,1419,312],{"class":195},[143,1421,291],{"class":153},[143,1423,1424,1427,1430,1432,1434],{"class":145,"line":1112},[143,1425,1426],{"class":149},"  async",[143,1428,1429],{"class":202}," fetch",[143,1431,309],{"class":153},[143,1433,255],{"class":254},[143,1435,373],{"class":153},[143,1437,1439,1442,1445,1447,1449,1452],{"class":145,"line":1438},22,[143,1440,1441],{"class":149},"    const",[143,1443,1444],{"class":195}," url",[143,1446,199],{"class":149},[143,1448,677],{"class":149},[143,1450,1451],{"class":202}," URL",[143,1453,1454],{"class":153},"(req.url);\n",[143,1456,1458,1461,1464,1467,1469,1472],{"class":145,"line":1457},23,[143,1459,1460],{"class":149},"    if",[143,1462,1463],{"class":153}," (url.pathname.",[143,1465,1466],{"class":202},"startsWith",[143,1468,309],{"class":153},[143,1470,1471],{"class":160},"\"\u002Fsocket.io\u002F\"",[143,1473,1474],{"class":153},")) {\n",[143,1476,1478,1481,1484],{"class":145,"line":1477},24,[143,1479,1480],{"class":149},"      return",[143,1482,1483],{"class":202}," fetchNodeHandler",[143,1485,1486],{"class":153},"(socketIoRequestListener, req);\n",[143,1488,1490],{"class":145,"line":1489},25,[143,1491,1492],{"class":153},"    }\n",[143,1494,1496,1499,1501,1503,1505,1507],{"class":145,"line":1495},26,[143,1497,1498],{"class":149},"    return",[143,1500,677],{"class":149},[143,1502,770],{"class":202},[143,1504,309],{"class":153},[143,1506,775],{"class":160},[143,1508,315],{"class":153},[143,1510,1512],{"class":145,"line":1511},27,[143,1513,224],{"class":153},[143,1515,1517,1519,1521,1523,1525,1527,1529,1531,1533,1535,1537],{"class":145,"line":1516},28,[143,1518,783],{"class":153},[143,1520,614],{"class":202},[143,1522,251],{"class":153},[143,1524,255],{"class":254},[143,1526,258],{"class":153},[143,1528,344],{"class":254},[143,1530,258],{"class":153},[143,1532,349],{"class":254},[143,1534,264],{"class":153},[143,1536,267],{"class":149},[143,1538,270],{"class":153},[143,1540,1542,1545,1547],{"class":145,"line":1541},29,[143,1543,1544],{"class":153},"    io.engine.",[143,1546,123],{"class":202},[143,1548,384],{"class":153},[143,1550,1552],{"class":145,"line":1551},30,[143,1553,846],{"class":153},[143,1555,1557],{"class":145,"line":1556},31,[143,1558,230],{"class":153},[143,1560,1562],{"class":145,"line":1561},32,[143,1563,186],{"emptyLinePlaceholder":185},[143,1565,1567,1570,1573,1576],{"class":145,"line":1566},33,[143,1568,1569],{"class":149},"await",[143,1571,1572],{"class":153}," server.",[143,1574,1575],{"class":202},"ready",[143,1577,1270],{"class":153},[97,1579,1580,1581,1584,1585,1588],{},"If you only need the WebSocket transport, force the client to skip polling with ",[101,1582,1583],{},"io(url, { transports: [\"websocket\"] })"," and you can drop the ",[101,1586,1587],{},"fetch","-side delegation entirely.",[397,1590,1591],{},[97,1592,401,1593,1600],{},[126,1594,1597],{"href":1595,"rel":1596},"https:\u002F\u002Fgithub.com\u002Fh3js\u002Fcrossws\u002Ftree\u002Fmain\u002Fexamples\u002Fsocket.io",[130],[101,1598,1599],{},"examples\u002Fsocket.io"," for a runnable demo including a browser client.",[1602,1603,1604],"style",{},"html pre.shiki code .so5gQ, html code.shiki .so5gQ{--shiki-light:#D73A49;--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .slsVL, html code.shiki .slsVL{--shiki-light:#24292E;--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .sfrk1, html code.shiki .sfrk1{--shiki-light:#032F62;--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .suiK_, html code.shiki .suiK_{--shiki-light:#005CC5;--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .shcOC, html code.shiki .shcOC{--shiki-light:#6F42C1;--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .sQHwn, html code.shiki .sQHwn{--shiki-light:#E36209;--shiki-default:#FFAB70;--shiki-dark:#FFAB70}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 .sCsY4, html code.shiki .sCsY4{--shiki-light:#6A737D;--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":139,"searchDepth":167,"depth":167,"links":1606},[1607,1609,1610,1611],{"id":421,"depth":167,"text":1608},"Detecting dead connections (idleTimeout)",{"id":591,"depth":167,"text":592},{"id":882,"depth":167,"text":883},{"id":1135,"depth":167,"text":1136},"Integrate crossws with Node.js (manually) or uWebSockets.js","md",{"icon":82},{"icon":82},{"title":79,"description":1612},"l2zWMi9mSng1J8wxvSWf1ZDbUq_qjr_Ra3E0qRtWG5w",[1619,1621],{"title":74,"path":75,"stem":76,"description":1620,"icon":77,"children":-1},"Manually integrate crossws with Deno.",{"title":84,"path":85,"stem":86,"description":1622,"icon":87,"children":-1},"Integrate crossws with server-sent events and fetch-api.",1783073588851]