{"id":20111995,"url":"https://github.com/vexcited/tcp-websocket","last_synced_at":"2025-05-06T11:31:31.609Z","repository":{"id":194103451,"uuid":"689775535","full_name":"Vexcited/tcp-websocket","owner":"Vexcited","description":"A WebSocket client-only class made with TCP streams.","archived":false,"fork":false,"pushed_at":"2023-09-18T19:10:24.000Z","size":36,"stargazers_count":7,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-04-13T16:13:51.085Z","etag":null,"topics":["bun","tcp","websocket"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/tcp-websocket","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Vexcited.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null},"funding":{"github":"Vexcited","liberapay":"Vexcited"}},"created_at":"2023-09-10T21:21:37.000Z","updated_at":"2024-04-14T17:54:40.934Z","dependencies_parsed_at":"2024-04-19T11:31:28.004Z","dependency_job_id":null,"html_url":"https://github.com/Vexcited/tcp-websocket","commit_stats":null,"previous_names":["vexcited/bun-tcp-websocket","vexcited/tcp-websocket"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Vexcited%2Ftcp-websocket","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Vexcited%2Ftcp-websocket/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Vexcited%2Ftcp-websocket/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Vexcited%2Ftcp-websocket/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Vexcited","download_url":"https://codeload.github.com/Vexcited/tcp-websocket/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224500110,"owners_count":17321641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bun","tcp","websocket"],"created_at":"2024-11-13T18:18:30.248Z","updated_at":"2025-05-06T11:31:31.600Z","avatar_url":"https://github.com/Vexcited.png","language":"TypeScript","funding_links":["https://github.com/sponsors/Vexcited","https://liberapay.com/Vexcited"],"categories":[],"sub_categories":[],"readme":"# `tcp-websocket`\n\n\u003e Was originally made to resolve this [Bun](https://bun.sh/) issue: \u003chttps://github.com/oven-sh/bun/issues/4529\u003e.\n\nInstead of using built-in `WebSocket`, we re-use the `WebSocket` from [`undici`](https://github.com/nodejs/undici) and export it correctly in this package with the typings needed.\n\n## Why not directly use `undici` ?\n\nBun patches `undici` imports under the hood, resulting in it being broken and useless to resolve the issue, see\n[`undici.js`](https://github.com/oven-sh/bun/blob/b124ba056cfdafad7828f86a852a83722f17f8a5/src/js/thirdparty/undici.js) and [`Undici.cpp`](https://github.com/oven-sh/bun/blob/b124ba056cfdafad7828f86a852a83722f17f8a5/src/bun.js/bindings/Undici.cpp).\n\nFor example, if we write the following code with Bun (so, using the native `WebSocket` implementation) :\n\n```typescript\nconst ws = new WebSocket(\"ws://localhost:8080\", {\n  headers: {\n    \"User-Agent\": \"hello\",\n    \"X-My-HeADeR\": \"world\",\n    authorization: \"Bearer Hello!\",\n    origin: \"http://localhost:8080\"\n  }\n});\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eServer code (uses Node.js and \u003ccode\u003ews\u003c/code\u003e package)\u003c/summary\u003e\n\n```typescript\nimport { createServer } from 'http';\nimport { WebSocketServer } from 'ws';\n\nconst server = createServer();\n\nconst wss = new WebSocketServer({ server });\nwss.on('connection', (_, req) =\u003e {\n  const headers = {};\n  // We use `req.rawHeaders` to see the case-sensitive headers.\n  for (let i = 0; i \u003c req.rawHeaders.length; i += 2) {\n    headers[req.rawHeaders[i]] = req.rawHeaders[i + 1];\n  }\n\n  console.log(headers);\n});\n\nserver.listen(8080);\n```\n\n\u003c/details\u003e\n\nWe get the following headers on the server :\n\n```typescript\n{\n  Host: 'localhost:8080',\n  Connection: 'Upgrade',\n  Upgrade: 'websocket',\n  'Sec-WebSocket-Version': '13',\n  'Sec-WebSocket-Key': 'RzRIg/gRTDCqu0rhOXe6OQ==',\n  Authorization: 'Bearer Hello!',\n  Origin: 'http://localhost:8080',\n  'User-Agent': 'hello',\n  'X-My-HeADeR': 'world'\n}\n```\n\nAs you can see, the headers are not the same as the ones we provided. `authorization` and `origin` got uppercased.\n\nLet's now try to use `WebSocket` from `undici` directly :\n\n```typescript\nimport { WebSocket } from \"undici\";\n\nconst ws = new WebSocket(\"ws://localhost:8080\", {\n  headers: {\n    \"User-Agent\": \"hello\",\n    \"X-My-HeADeR\": \"world\",\n    authorization: \"Bearer Hello!\",\n    origin: \"http://localhost:8080\"\n  }\n});\n```\n\nWe get the following headers on the server :\n\n```typescript\n{\n  Host: 'localhost:8080',\n  Connection: 'Upgrade',\n  Upgrade: 'websocket',\n  'Sec-WebSocket-Version': '13',\n  'Sec-WebSocket-Key': 'ND4bTHtlQ/e/CwzhJp6mFA==',\n  Authorization: 'Bearer Hello!',\n  Origin: 'http://localhost:8080',\n  'User-Agent': 'hello',\n  'X-My-HeADeR': 'world'\n}\n```\n\nWe get exactly the same output !\nThis is because [Bun internally redirects the `WebSocket` import from `undici` to the native `WebSocket` implementation](https://github.com/oven-sh/bun/blob/b124ba056cfdafad7828f86a852a83722f17f8a5/src/bun.js/bindings/Undici.cpp#L59-L61).\n\nLet's prevent this by tweaking the imports :\n\n```typescript\nimport { WebSocket } from \"undici/lib/web/websocket/websocket.js\";\n\nconst ws = new WebSocket(\"ws://localhost:8080\", {\n  headers: {\n    \"User-Agent\": \"hello\",\n    \"X-My-HeADeR\": \"world\",\n    authorization: \"Bearer Hello!\",\n    origin: \"http://localhost:8080\"\n  }\n});\n```\n\nNow, we get the following headers on the server :\n\n```typescript\n{\n  host: 'localhost:8080',\n  connection: 'upgrade',\n  upgrade: 'websocket',\n  'User-Agent': 'hello',\n  'X-My-HeADeR': 'world',\n  authorization: 'Bearer Hello!',\n  origin: 'http://localhost:8080',\n  'sec-websocket-key': 'K8JDPp71F1TYDKXujpqoxw==',\n  'sec-websocket-version': '13',\n  'sec-websocket-extensions': 'permessage-deflate; client_max_window_bits',\n  accept: '*/*',\n  'accept-language': '*',\n  'sec-fetch-mode': 'websocket',\n  pragma: 'no-cache',\n  'cache-control': 'no-cache',\n  'accept-encoding': 'gzip, deflate'\n}\n```\n\nIt works as expected !\n\nNow the issue is that we're missing typings, this is what this package is for.\n\n## Usage\n\n```bash\nnpm add tcp-websocket\nyarn add tcp-websocket\npnpm add tcp-websocket\nbun add tcp-websocket\n```\n\n```typescript\nimport WebSocket from \"tcp-websocket\";\n\nconst ws = new WebSocket(\"wss://ws.postman-echo.com/raw\");\nconsole.info(\"connecting...\")\n\nws.onopen = () =\u003e {\n  console.info(\"[open]: connected\");\n  ws.send(\"hello world!\");\n\n  console.info(\"[open]: will close in 5 seconds...\");\n  setTimeout(() =\u003e ws.close(), 5_000);\n};\n\nws.onmessage = (event) =\u003e {\n  console.info(\"[message]:\", event.data);\n};\n\nws.onclose = (event) =\u003e {\n  console.info(`[close(${event.code})]: ${event.reason || \"[no reason provided]\"}`);\n};\n```\n\n## Why not use those libraries instead ?\n\nPackages using `node:http` or `node:https` to make the request handshake\nwill fail in Bun since their implementation also tweaks the headers.\n\n| Package name on NPM | Issues with Bun |\n| ------------------- | --------------- |\n| [`ws`](https://www.npmjs.com/package/ws) | Uses the `node:http` and `node:https` to make the request handshake. [Source](https://github.com/websockets/ws/blob/7460049ff0a61bef8d5eda4b1d5c8170bc7d6b6f/lib/websocket.js#L715) |\n| [`websocket`](https://www.npmjs.com/package/websocket) | Uses the `node:http` and `node:https` to make the request handshake. [Source](https://github.com/theturtle32/WebSocket-Node/blob/cce6d468986dd356a52af5630fd8ed5726ba5b7a/lib/WebSocketClient.js#L254) |\n| [`websocket-stream`](https://www.npmjs.com/package/websocket-stream) | Uses the `ws` package internally, see `ws`. [Source](https://github.com/maxogden/websocket-stream/blob/feeb372ff530621d6df85cb85d4bee03b879c54d/stream.js#L5) |\n| [`websocket-driver`](https://www.npmjs.com/package/websocket-driver) | Works as expected with Bun and others, but last update was 3 years ago with no TS declarations and ES5 syntax. |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvexcited%2Ftcp-websocket","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvexcited%2Ftcp-websocket","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvexcited%2Ftcp-websocket/lists"}