# tcp-websocket

> A re-export of WebSocket from undici for compatibility with Bun.

Latest version **1.0.0** (published 2025-02-28) · MIT license · 0 weekly downloads

> **Deprecated.** This package is deprecated.

## Install

```sh
npm install tcp-websocket
pnpm add tcp-websocket
yarn add tcp-websocket
bun add tcp-websocket
```

## Health

**Score 10/100 (F)** — status: deprecated.

Negative: deprecated.

## Facts

| | |
|---|---|
| Version | 1.0.0 |
| Published | 2025-02-28 |
| First published | 2023-09-15 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 1 |
| Unpacked size | 10.7 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | Mikkel ALMONTE--RINGAUD |
| Maintainers | vexcited |
| Keywords | bun, tcp, client, undici, websocket |

## Links

- npm: https://www.npmjs.com/package/tcp-websocket
- Repository: https://github.com/Vexcited/tcp-websocket
- Homepage: https://github.com/Vexcited/tcp-websocket#readme
- Issues: https://github.com/Vexcited/tcp-websocket/issues
- npm.io page: https://npm.io/package/tcp-websocket

## Dependencies (1)

- [undici](https://npm.io/package/undici.md) ^7.3.0

## Alternatives

- [@opentelemetry/exporter-zipkin](https://npm.io/package/@opentelemetry/exporter-zipkin.md) — 14.8M weekly downloads
- [pusher-js](https://npm.io/package/pusher-js.md) — 2.0M weekly downloads
- [browserify](https://npm.io/package/browserify.md) — 1.7M weekly downloads
- [sqs-consumer](https://npm.io/package/sqs-consumer.md) — 1.7M weekly downloads
- [@sanity/eventsource](https://npm.io/package/@sanity/eventsource.md) — 930.8K weekly downloads

## Recent versions

- 1.0.0 (latest) — 2025-02-28
- 0.3.0 — 2025-02-27
- 0.2.0 — 2024-08-17
- 0.1.1 — 2023-09-18
- 0.1.0 — 2023-09-15

## README

# `tcp-websocket`

> Was originally made to resolve this [Bun](https://bun.sh/) issue: <https://github.com/oven-sh/bun/issues/4529>.

Instead 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.

## Why not directly use `undici` ?

Bun patches `undici` imports under the hood, resulting in it being broken and useless to resolve the issue, see
[`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).

For example, if we write the following code with Bun (so, using the native `WebSocket` implementation) :

```typescript
const ws = new WebSocket("ws://localhost:8080", {
  headers: {
    "User-Agent": "hello",
    "X-My-HeADeR": "world",
    authorization: "Bearer Hello!",
    origin: "http://localhost:8080"
  }
});
```

<details>
<summary>Server code (uses Node.js and <code>ws</code> package)</summary>

```typescript
import { createServer } from 'http';
import { WebSocketServer } from 'ws';

const server = createServer();

const wss = new WebSocketServer({ server });
wss.on('connection', (_, req) => {
  const headers = {};
  // We use `req.rawHeaders` to see the case-sensitive headers.
  for (let i = 0; i < req.rawHeaders.length; i += 2) {
    headers[req.rawHeaders[i]] = req.rawHeaders[i + 1];
  }

  console.log(headers);
});

server.listen(8080);
```

</details>

We get the following headers on the server :

```typescript
{
  Host: 'localhost:8080',
  Connection: 'Upgrade',
  Upgrade: 'websocket',
  'Sec-WebSocket-Version': '13',
  'Sec-WebSocket-Key': 'RzRIg/gRTDCqu0rhOXe6OQ==',
  Authorization: 'Bearer Hello!',
  Origin: 'http://localhost:8080',
  'User-Agent': 'hello',
  'X-My-HeADeR': 'world'
}
```

As you can see, the headers are not the same as the ones we provided. `authorization` and `origin` got uppercased.

Let's now try to use `WebSocket` from `undici` directly :

```typescript
import { WebSocket } from "undici";

const ws = new WebSocket("ws://localhost:8080", {
  headers: {
    "User-Agent": "hello",
    "X-My-HeADeR": "world",
    authorization: "Bearer Hello!",
    origin: "http://localhost:8080"
  }
});
```

We get the following headers on the server :

```typescript
{
  Host: 'localhost:8080',
  Connection: 'Upgrade',
  Upgrade: 'websocket',
  'Sec-WebSocket-Version': '13',
  'Sec-WebSocket-Key': 'ND4bTHtlQ/e/CwzhJp6mFA==',
  Authorization: 'Bearer Hello!',
  Origin: 'http://localhost:8080',
  'User-Agent': 'hello',
  'X-My-HeADeR': 'world'
}
```

We get exactly the same output !
This 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).

Let's prevent this by tweaking the imports :

```typescript
import { WebSocket } from "undici/lib/web/websocket/websocket.js";

const ws = new WebSocket("ws://localhost:8080", {
  headers: {
    "User-Agent": "hello",
    "X-My-HeADeR": "world",
    authorization: "Bearer Hello!",
    origin: "http://localhost:8080"
  }
});
```

Now, we get the following headers on the server :

```typescript
{
  host: 'localhost:8080',
  connection: 'upgrade',
  upgrade: 'websocket',
  'User-Agent': 'hello',
  'X-My-HeADeR': 'world',
  authorization: 'Bearer Hello!',
  origin: 'http://localhost:8080',
  'sec-websocket-key': 'K8JDPp71F1TYDKXujpqoxw==',
  'sec-websocket-version': '13',
  'sec-websocket-extensions': 'permessage-deflate; client_max_window_bits',
  accept: '*/*',
  'accept-language': '*',
  'sec-fetch-mode': 'websocket',
  pragma: 'no-cache',
  'cache-control': 'no-cache',
  'accept-encoding': 'gzip, deflate'
}
```

It works as expected !

Now the issue is that we're missing typings, this is what this package is for.

## Usage

```bash
npm add tcp-websocket
yarn add tcp-websocket
pnpm add tcp-websocket
bun add tcp-websocket
```

```typescript
import WebSocket from "tcp-websocket";

const ws = new WebSocket("wss://ws.postman-echo.com/raw");
console.info("connecting...")

ws.onopen = () => {
  console.info("[open]: connected");
  ws.send("hello world!");

  console.info("[open]: will close in 5 seconds...");
  setTimeout(() => ws.close(), 5_000);
};

ws.onmessage = (event) => {
  console.info("[message]:", event.data);
};

ws.onclose = (event) => {
  console.info(`[close(${event.code})]: ${event.reason || "[no reason provided]"}`);
};
```

## Why not use those libraries instead ?

Packages using `node:http` or `node:https` to make the request handshake
will fail in Bun since their implementation also tweaks the headers.

| Package name on NPM | Issues with Bun |
| ------------------- | --------------- |
| [`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) |
| [`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) |
| [`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) |
| [`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. |

---
_Source: https://npm.io/package/tcp-websocket · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
