crossws 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +155 -56
  2. package/adapters/bun.d.ts +1 -0
  3. package/adapters/cloudflare.d.ts +1 -0
  4. package/adapters/deno.d.ts +1 -0
  5. package/adapters/node.d.ts +1 -0
  6. package/dist/adapters/bun.cjs +44 -29
  7. package/dist/adapters/bun.d.cts +5 -35
  8. package/dist/adapters/bun.d.mts +5 -35
  9. package/dist/adapters/bun.d.ts +5 -35
  10. package/dist/adapters/bun.mjs +44 -26
  11. package/dist/adapters/cloudflare.cjs +52 -0
  12. package/dist/adapters/cloudflare.d.cts +12 -0
  13. package/dist/adapters/cloudflare.d.mts +12 -0
  14. package/dist/adapters/cloudflare.d.ts +12 -0
  15. package/dist/adapters/cloudflare.mjs +50 -0
  16. package/dist/adapters/deno.cjs +20 -29
  17. package/dist/adapters/deno.d.cts +2 -10
  18. package/dist/adapters/deno.d.mts +2 -10
  19. package/dist/adapters/deno.d.ts +2 -10
  20. package/dist/adapters/deno.mjs +20 -26
  21. package/dist/adapters/node.cjs +57 -3891
  22. package/dist/adapters/node.d.cts +36 -37
  23. package/dist/adapters/node.d.mts +36 -37
  24. package/dist/adapters/node.d.ts +36 -37
  25. package/dist/adapters/node.mjs +48 -3872
  26. package/dist/index.cjs +5 -5
  27. package/dist/index.d.cts +40 -10
  28. package/dist/index.d.mts +40 -10
  29. package/dist/index.d.ts +40 -10
  30. package/dist/index.mjs +4 -4
  31. package/dist/shared/{crossws.002417e5.cjs → crossws.21e14e0d.cjs} +7 -4
  32. package/dist/shared/crossws.2ed26345.cjs +3931 -0
  33. package/dist/shared/{crossws.08866fa6.mjs → crossws.9536f626.mjs} +7 -4
  34. package/dist/shared/crossws.a5db571c.mjs +3910 -0
  35. package/dist/websocket/index.cjs +5 -0
  36. package/dist/websocket/index.d.cts +10 -0
  37. package/dist/websocket/index.d.mts +10 -0
  38. package/dist/websocket/index.d.ts +10 -0
  39. package/dist/websocket/index.mjs +3 -0
  40. package/dist/websocket/node.cjs +17 -0
  41. package/dist/websocket/node.d.cts +10 -0
  42. package/dist/websocket/node.d.mts +10 -0
  43. package/dist/websocket/node.d.ts +10 -0
  44. package/dist/websocket/node.mjs +15 -0
  45. package/package.json +44 -17
  46. package/websocket.d.ts +1 -0
package/README.md CHANGED
@@ -7,17 +7,20 @@
7
7
 
8
8
  <!-- [![Codecov][codecov-src]][codecov-href] -->
9
9
 
10
- > [!WARNING]
11
- > This project and API are under heavy development and trial. Please don't rely on it for production yet. Feedback is more than welcome!
10
+ 👉 Elegant, typed, and simple interface to implement platform-agnostic WebSocket servers
11
+
12
+ 🧩 Seamlessly integrates with, [Node.js](https://nodejs.org/en), [Bun](https://bun.sh/), [Deno](https://deno.com/) and [Cloudflare Workers](https://workers.cloudflare.com/)!
13
+
14
+ 🚀 High-performance server hooks, avoiding heavy per-connection events API ([why](https://bun.sh/docs/api/websockets#lcYFjkFYJC-summary))
12
15
 
13
- Cross-platform WebSocket Servers:
16
+ 📦 No external dependencies, includes [ws](https://github.com/websockets/ws) for Node.js support
14
17
 
15
- - Elegant, typed, and simple interface to define your WebSocket server handlers
16
- - Performant per-server handlers instead of per-connection events API ([why](https://bun.sh/docs/api/websockets#lcYFjkFYJC-summary))
17
- - Zero dependencies with bundled [ws](https://github.com/websockets/ws) types and runtime for [Node.js](https://nodejs.org/) support
18
- - Native integration with [Bun](https://bun.sh/) and [Deno](https://deno.com/) WebSocket API
19
- - Super lightweight tree-shakable packaging
20
- - Developer-Friendly logging
18
+ 💡 Extremely lightweight and tree-shakable packaging with ESM and CJS support
19
+
20
+ 🔍 Developer-friendly object logging
21
+
22
+ > [!WARNING]
23
+ > This project and API are under development.
21
24
 
22
25
  ## Install
23
26
 
@@ -35,9 +38,91 @@ pnpm install crossws
35
38
  bun install crossws
36
39
  ```
37
40
 
38
- ## Integration
41
+ ## Unified Hooks
42
+
43
+ CrossWS provides a cross-platform API to define WebSocket servers. An implementation with these hooks works across runtimes without needing you to go into details of any of them (while you always have the power to control low-level hooks). You can only define the life-cycle hooks that you only need and only those will be called run runtime.
44
+
45
+ > [!NOTE]
46
+ > For type support and IDE auto-completion, you can use `defineWebSocketHooks` utility or `WebSocketHooks` type export from the main.
47
+
48
+ ```ts
49
+ import { defineWebSocketHooks } from "crossws";
50
+
51
+ const websocketHooks = defineWebSocketHooks({
52
+ open(peer) {
53
+ console.log("[ws] open", peer);
54
+ },
55
+
56
+ message(peer, message) {
57
+ console.log("[ws] message", peer, message);
58
+ if (message.text().includes("ping")) {
59
+ peer.send("pong");
60
+ }
61
+ },
62
+
63
+ close(peer, event) {
64
+ console.log("[ws] close", peer, event);
65
+ },
66
+
67
+ error(peer, error) {
68
+ console.log("[ws] error", peer, error);
69
+ },
70
+
71
+ // ... platform hooks such as bun:drain ...
72
+ });
73
+ ```
74
+
75
+ ### Peer Object
76
+
77
+ Websocket hooks always accept a peer instance as the first argument. `peer`, keeps the state of the connected client.
78
+
79
+ **Properties:**
80
+
81
+ - `peer.id?`: The peer address or unique id (might be `undefined`)
82
+ - `peer.readyState`: The connection status ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState))
83
+ - `peer.ctx[name]`: Keeps the state of native client connection
84
+
85
+ **Methods:**
86
+
87
+ - `send(message, compress)`: Send a message to the connected client
88
+
89
+ > [!TIP]
90
+ > You can safely log a peer instance to the console using `console.log` it will be automatically stringified with useful information including the remote address and connection status!
91
+
92
+ ### Message Object
93
+
94
+ on `message` hook, you receive a message object containing an incoming message from the client.
95
+
96
+ **Properties:**
97
+
98
+ - `message.rawData`: Raw message data
99
+ - `message.isBinary`: Indicates if the message is binary (might be `undefined`)
100
+
101
+ **Methods:**
102
+
103
+ - `message.text()`: Get stringified version of the message
104
+
105
+ > [!TIP]
106
+ > You can safely log `message` object to the console using `console.log` it will be automatically stringified!
107
+
108
+ ## Error handling
109
+
110
+ You can catch errors using `error` hook. The second argument is error wrapped into a `WebSocketError` class.
111
+
112
+ ## Universal WebSocket client
113
+
114
+ CrossWS also exposes a universal way to use [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) API in order to connect to the server. For all runtimes, except Node.js, native implementation is used, and for Node.js, a bundled version of [`ws.WebSocket`](https://www.npmjs.com/package/ws) is bundled.
115
+
116
+ ```js
117
+ import WebSocket from "crossws/websocket";
118
+ ```
119
+
120
+ > [!NOTE]
121
+ > Using export conditions, the correct version will be always used so you don't have to worry about picking the right build!
122
+
123
+ ## Integrations
39
124
 
40
- CrossWS allows integrating your WebSocket handlers with different runtimes and platforms using built-in adapters. Each runtime has a specific method of integrating WebSocket. Once integrated, your custom handlers (such as `onMessage`) will work consistently even if you change the runtime!
125
+ CrossWS allows integrating your WebSocket hooks with different runtimes and platforms using built-in adapters. Each runtime has a specific method of integrating WebSocket. Once integrated, your custom hooks (such as `message` and `close`) will work consistently even if you change the runtime!
41
126
 
42
127
  ### Integration with **Node.js**
43
128
 
@@ -53,26 +138,38 @@ const server = createServer((req, res) => {
53
138
  );
54
139
  }).listen(3000);
55
140
 
56
- // Initialize WebSocket Handler
141
+ // Initialize WebSocket Hooks
57
142
  import nodeWSAdapter from "crossws/adapters/node";
58
143
 
59
- const { handleUpgrade } = nodeWSAdapter({ onMessage: console.log });
144
+ const { handleUpgrade } = nodeWSAdapter({ message: console.log });
60
145
  server.on("upgrade", handleUpgrade);
61
146
  ```
62
147
 
148
+ **Node-specific hooks:**
149
+
150
+ - `node:open (peer)`
151
+ - `node:message (peer, data, isBinary)`
152
+ - `node:close (peer, code, reason)`
153
+ - `node:error (peer, error)`
154
+ - `node:ping (peer)`
155
+ - `node:pong (peer)`
156
+ - `node:unexpected-response (peer, req, res)`
157
+ - `node:upgrade (peer, req)`
158
+
63
159
  See [playground/node.ts](./playground/node.ts) for demo and [src/adapters/node.ts](./src/adapters/node.ts) for implementation.
64
160
 
65
- ## Integration with **Bun**
161
+ ### Integration with **Bun**
66
162
 
67
- To integrate crosws with your Bun server, you need to check for `server.upgrade` and also pass the `websocket` object returned from the adapter to server options. CrossWS leverages native Bun WebSocket API.
163
+ To integrate CrossWS with your Bun server, you need to check for `server.upgrade` and also pass the `websocket` object returned from the adapter to server options. CrossWS leverages native Bun WebSocket API.
68
164
 
69
165
  ```ts
70
166
  import bunAdapter from "./dist/adapters/bun";
71
167
 
72
- const { websocket } = bunAdapter({ onMessage: console.log });
168
+ const { websocket } = bunAdapter({ message: console.log });
73
169
 
74
170
  Bun.serve({
75
171
  port: 3000,
172
+ websocket,
76
173
  fetch(req, server) {
77
174
  if (server.upgrade(req)) {
78
175
  return;
@@ -82,20 +179,29 @@ Bun.serve({
82
179
  { headers: { "content-type": "text/html" } },
83
180
  );
84
181
  },
85
- websocket,
86
182
  });
87
183
  ```
88
184
 
185
+ **Bun-specific hooks:**
186
+
187
+ - `bun:message (peer, ws,message)`
188
+ - `bun:open (peer, ws)`
189
+ - `bun:close (peer, ws)`
190
+ - `bun:drain (peer)`
191
+ - `bun:error (peer, ws, error)`
192
+ - `bun:ping (peer, ws, data)`
193
+ - `bun:pong (peer, ws, data)`
194
+
89
195
  See [playground/bun.ts](./playground/bun.ts) for demo and [src/adapters/bun.ts](./src/adapters/bun.ts) for implementation.
90
196
 
91
- ## Integration with **Deno**
197
+ ### Integration with **Deno**
92
198
 
93
199
  To integrate CrossWS with your Deno server, you need to check for the `upgrade` header and then call `handleUpgrade` method from the adapter passing the incoming request object. The returned value is the server upgrade response.
94
200
 
95
201
  ```ts
96
202
  import denoAdapter from "crossws/adapters/deno";
97
203
 
98
- const { handleUpgrade } = denoAdapter({ onMessage: console.log });
204
+ const { handleUpgrade } = denoAdapter({ message: console.log });
99
205
 
100
206
  Deno.serve({ port: 3000 }, (req) => {
101
207
  if (req.headers.get("upgrade") === "websocket") {
@@ -108,63 +214,56 @@ Deno.serve({ port: 3000 }, (req) => {
108
214
  });
109
215
  ```
110
216
 
111
- See [playground/deno.ts](./playground/deno.ts) for demo and [src/adapters/deno.ts](./src/adapters/deno.ts) for implementation.
217
+ **Deno-specific hooks:**
112
218
 
113
- ## Integration with other runtimes
219
+ - `deno:open (peer)`
220
+ - `deno:message (peer, event)`
221
+ - `deno:close (peer)`
222
+ - `deno:error (peer, error)`
114
223
 
115
- You can define your custom adapters using `defineWebSocketAdapter` wrapper.
116
-
117
- See other adapter implementations in [./src/adapters](./src/adapters/) to get and idea how adapters can be implemented and feel free to directly make a Pull Request to support your environment in CrossWS!
118
-
119
- ## Handler API
120
-
121
- Previously you saw in the adapter examples that we pass `onMessage` option.
224
+ See [playground/deno.ts](./playground/deno.ts) for demo and [src/adapters/deno.ts](./src/adapters/deno.ts) for implementation.
122
225
 
123
- First object passed to adapters is a list of global handlers that will get called during lifecycle of a WebSocket connection. You can use `defineWebSocketHandler` utility to make a typed websocket handler object and pass it to the actual adapter when needed.
226
+ ### Integration with **Cloudflare Workers**
124
227
 
125
- **Note: API is subject to change! Feedbacks Welcome!**
228
+ To integrate CrossWS with your Cloudflare Workers, you need to check for the `upgrade` header
126
229
 
127
230
  ```ts
128
- import { defineWebSocketHandler } from "crossws";
231
+ import cloudflareAdapter from "crossws/adapters/cloudflare";
129
232
 
130
- const websocketHandler = defineWebSocketHandler({
131
- onMessage: (peer, message) => {
132
- console.log("message", peer, message);
133
- if (message.text().includes("ping")) {
134
- peer.send("pong");
233
+ const { handleUpgrade } = cloudflareAdapter({ message: console.log });
234
+
235
+ export default {
236
+ async fetch(request, env, context) {
237
+ if (request.headers.get("upgrade") === "websocket") {
238
+ return handleUpgrade(request, env, context);
135
239
  }
240
+ return new Response(
241
+ `<script>new WebSocket("ws://localhost:3000").addEventListener("open", (e) => e.target.send("Hello from client!"));</script>`,
242
+ { headers: { "content-type": "text/html" } },
243
+ );
136
244
  },
137
- onError: (peer, error) => {
138
- console.log("error", peer, error);
139
- },
140
- onOpen: (peer) => {
141
- console.log("open", peer);
142
- },
143
- onClose: (peer, code, reason) => {
144
- console.log("close", peer, code, reason);
145
- },
146
- onEvent: (event, ...args) => {
147
- console.log("event", event);
148
- },
149
- });
245
+ };
150
246
  ```
151
247
 
152
- ### `WebSocketPeer`
248
+ **Cloudflare-specific hooks:**
153
249
 
154
- Websocket handler methods accept a peer instance as first argument. peer is a wrapper over platform natives WebSocket connection instance and alows to send message.
250
+ - `cloudflare:accept(peer)`
251
+ - `cloudflare:message(peer, event)`
252
+ - `cloudflare:error(peer, event)`
253
+ - `cloudflare:close(peer, event)`
155
254
 
156
- **Tip:** You can safely log a peer instance to console using `console.log` it will be automatically stringified with useful information including remote address and connection status!
255
+ See [playground/cloudflare.ts](./playground/cloudflare.ts) for demo and [src/adapters/cloudflare.ts](./src/adapters/cloudflare.ts) for implementation.
157
256
 
158
- ### `WebSocketMessage`
257
+ ### Integration with other runtimes
159
258
 
160
- Second argument to `onMessage` event handler is a message object. You can access raw data using `message.rawData` or stringified message using `message.text()`.
259
+ You can define your custom adapters using `defineWebSocketAdapter` wrapper.
161
260
 
162
- **Tip:** You can safely log `message` object to console using `console.log` it will be automatically stringified!
261
+ See other adapter implementations in [./src/adapters](./src/adapters/) to get an idea of how adapters can be implemented and feel free to directly make a Pull Request to support your environment in CrossWS!
163
262
 
164
263
  ## Development
165
264
 
166
265
  - Clone this repository
167
- - Install latest LTS version of [Node.js](https://nodejs.org/en/)
266
+ - Install the latest LTS version of [Node.js](https://nodejs.org/en/)
168
267
  - Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable`
169
268
  - Install dependencies using `pnpm install`
170
269
  - Run interactive tests using `pnpm dev`
@@ -0,0 +1 @@
1
+ export * from "../dist/adapters/bun";
@@ -0,0 +1 @@
1
+ export * from "../dist/adapters/cloudflare";
@@ -0,0 +1 @@
1
+ export * from "../dist/adapters/deno";
@@ -0,0 +1 @@
1
+ export * from "../dist/adapters/node";
@@ -1,57 +1,72 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
3
+ const peer = require('../shared/crossws.21e14e0d.cjs');
4
4
 
5
- const peer = require('../shared/crossws.002417e5.cjs');
6
-
7
- const WebSocket = globalThis.WebSocket;
8
5
  const bun = peer.defineWebSocketAdapter(
9
- (handler, opts = {}) => {
6
+ (hooks, opts = {}) => {
7
+ const getPeer = (ws) => {
8
+ if (ws.data?._peer) {
9
+ return ws.data._peer;
10
+ }
11
+ const peer = new WebSocketPeer({ bun: { ws } });
12
+ ws.data = ws.data || {};
13
+ ws.data._peer = peer;
14
+ return peer;
15
+ };
10
16
  return {
11
17
  websocket: {
12
18
  message: (ws, message) => {
13
- handler.onEvent?.("bun:message", ws, message);
14
- const peer$1 = new BunWebSocketPeer(ws);
15
- handler.onMessage?.(peer$1, new peer.WebSocketMessage(message));
19
+ const peer$1 = getPeer(ws);
20
+ hooks["bun:message"]?.(peer$1, ws, message);
21
+ hooks.message?.(peer$1, new peer.WebSocketMessage(message));
16
22
  },
17
23
  open: (ws) => {
18
- handler.onEvent?.("bun:open", ws);
19
- const peer = new BunWebSocketPeer(ws);
20
- handler.onOpen?.(peer);
24
+ const peer = getPeer(ws);
25
+ hooks["bun:open"]?.(peer, ws);
26
+ hooks.open?.(peer);
21
27
  },
22
28
  close: (ws) => {
23
- handler.onEvent?.("bun:close", ws);
24
- const peer = new BunWebSocketPeer(ws);
25
- handler.onClose?.(peer, 0, "");
29
+ const peer = getPeer(ws);
30
+ hooks["bun:close"]?.(peer, ws);
31
+ hooks.close?.(peer, {});
32
+ },
33
+ drain: (ws) => {
34
+ const peer = getPeer(ws);
35
+ hooks["bun:drain"]?.(peer);
26
36
  },
37
+ // @ts-expect-error types unavailable but mentioned in docs
27
38
  error: (ws, error) => {
28
- handler.onEvent?.("bun:error", ws, error);
29
- const peer$1 = new BunWebSocketPeer(ws);
30
- handler.onError?.(peer$1, new peer.WebSocketError(error));
39
+ const peer$1 = getPeer(ws);
40
+ hooks["bun:error"]?.(peer$1, ws, error);
41
+ hooks.error?.(peer$1, new peer.WebSocketError(error));
31
42
  },
32
- drain: (ws) => {
33
- handler.onEvent?.("bun:drain", ws);
43
+ ping(ws, data) {
44
+ const peer = getPeer(ws);
45
+ hooks["bun:ping"]?.(peer, ws, data);
46
+ },
47
+ pong(ws, data) {
48
+ const peer = getPeer(ws);
49
+ hooks["bun:pong"]?.(peer, ws, data);
34
50
  }
35
51
  }
36
52
  };
37
53
  }
38
54
  );
39
- class BunWebSocketPeer extends peer.WebSocketPeer {
40
- constructor(_ws) {
41
- super();
42
- this._ws = _ws;
43
- }
55
+ class WebSocketPeer extends peer.WebSocketPeerBase {
44
56
  get id() {
45
- return this._ws.remoteAddress;
57
+ let addr = this.ctx.bun.ws.remoteAddress;
58
+ if (addr.includes(":")) {
59
+ addr = `[${addr}]`;
60
+ }
61
+ return addr;
46
62
  }
47
63
  get readyState() {
48
- return this._ws.readyState;
64
+ return this.ctx.bun.ws.readyState;
49
65
  }
50
66
  send(message) {
51
- this._ws.send(message);
67
+ this.ctx.bun.ws.send(message);
52
68
  return 0;
53
69
  }
54
70
  }
55
71
 
56
- exports.WebSocket = WebSocket;
57
- exports.default = bun;
72
+ module.exports = bun;
@@ -1,43 +1,13 @@
1
1
  import { WebSocketAdapter } from '../index.cjs';
2
2
 
3
- interface BunWSOptions {
4
- message: (ws: BunServerWebSocket, message: string | ArrayBuffer | Uint8Array) => void;
5
- open?: (ws: BunServerWebSocket) => void;
6
- close?: (ws: BunServerWebSocket) => void;
7
- error?: (ws: BunServerWebSocket, error: Error) => void;
8
- drain?: (ws: BunServerWebSocket) => void;
9
- perMessageDeflate?: boolean | {
10
- compress?: boolean | BunWSCompressor;
11
- decompress?: boolean | BunWSCompressor;
12
- };
13
- }
14
- type BunWSCompressor = `"disable"` | `"shared"` | `"dedicated"` | `"3KB"` | `"4KB"` | `"8KB"` | `"16KB"` | `"32KB"` | `"64KB"` | `"128KB"` | `"256KB"`;
15
- interface BunServerWebSocket {
16
- readonly data: any;
17
- readonly readyState: number;
18
- readonly remoteAddress: string;
19
- send(message: string | ArrayBuffer | Uint8Array, compress?: boolean): number;
20
- close(code?: number, reason?: string): void;
21
- subscribe(topic: string): void;
22
- unsubscribe(topic: string): void;
23
- publish(topic: string, message: string | ArrayBuffer | Uint8Array): void;
24
- isSubscribed(topic: string): boolean;
25
- cork(cb: (ws: BunServerWebSocket) => void): void;
26
- }
27
-
28
- declare const WebSocket: {
29
- new (url: string | URL, protocols?: string | string[] | undefined): WebSocket;
30
- prototype: WebSocket;
31
- readonly CONNECTING: 0;
32
- readonly OPEN: 1;
33
- readonly CLOSING: 2;
34
- readonly CLOSED: 3;
35
- };
36
3
  interface AdapterOptions {
37
4
  }
5
+ type WebSocketHooks = Extract<Parameters<typeof Bun.serve<ContextData>>[0], {
6
+ websocket: any;
7
+ }>["websocket"];
38
8
  interface Adapter {
39
- websocket: BunWSOptions;
9
+ websocket: WebSocketHooks;
40
10
  }
41
11
  declare const _default: WebSocketAdapter<Adapter, AdapterOptions>;
42
12
 
43
- export { type Adapter, type AdapterOptions, WebSocket, _default as default };
13
+ export { type Adapter, type AdapterOptions, _default as default };
@@ -1,43 +1,13 @@
1
1
  import { WebSocketAdapter } from '../index.mjs';
2
2
 
3
- interface BunWSOptions {
4
- message: (ws: BunServerWebSocket, message: string | ArrayBuffer | Uint8Array) => void;
5
- open?: (ws: BunServerWebSocket) => void;
6
- close?: (ws: BunServerWebSocket) => void;
7
- error?: (ws: BunServerWebSocket, error: Error) => void;
8
- drain?: (ws: BunServerWebSocket) => void;
9
- perMessageDeflate?: boolean | {
10
- compress?: boolean | BunWSCompressor;
11
- decompress?: boolean | BunWSCompressor;
12
- };
13
- }
14
- type BunWSCompressor = `"disable"` | `"shared"` | `"dedicated"` | `"3KB"` | `"4KB"` | `"8KB"` | `"16KB"` | `"32KB"` | `"64KB"` | `"128KB"` | `"256KB"`;
15
- interface BunServerWebSocket {
16
- readonly data: any;
17
- readonly readyState: number;
18
- readonly remoteAddress: string;
19
- send(message: string | ArrayBuffer | Uint8Array, compress?: boolean): number;
20
- close(code?: number, reason?: string): void;
21
- subscribe(topic: string): void;
22
- unsubscribe(topic: string): void;
23
- publish(topic: string, message: string | ArrayBuffer | Uint8Array): void;
24
- isSubscribed(topic: string): boolean;
25
- cork(cb: (ws: BunServerWebSocket) => void): void;
26
- }
27
-
28
- declare const WebSocket: {
29
- new (url: string | URL, protocols?: string | string[] | undefined): WebSocket;
30
- prototype: WebSocket;
31
- readonly CONNECTING: 0;
32
- readonly OPEN: 1;
33
- readonly CLOSING: 2;
34
- readonly CLOSED: 3;
35
- };
36
3
  interface AdapterOptions {
37
4
  }
5
+ type WebSocketHooks = Extract<Parameters<typeof Bun.serve<ContextData>>[0], {
6
+ websocket: any;
7
+ }>["websocket"];
38
8
  interface Adapter {
39
- websocket: BunWSOptions;
9
+ websocket: WebSocketHooks;
40
10
  }
41
11
  declare const _default: WebSocketAdapter<Adapter, AdapterOptions>;
42
12
 
43
- export { type Adapter, type AdapterOptions, WebSocket, _default as default };
13
+ export { type Adapter, type AdapterOptions, _default as default };
@@ -1,43 +1,13 @@
1
1
  import { WebSocketAdapter } from '../index.js';
2
2
 
3
- interface BunWSOptions {
4
- message: (ws: BunServerWebSocket, message: string | ArrayBuffer | Uint8Array) => void;
5
- open?: (ws: BunServerWebSocket) => void;
6
- close?: (ws: BunServerWebSocket) => void;
7
- error?: (ws: BunServerWebSocket, error: Error) => void;
8
- drain?: (ws: BunServerWebSocket) => void;
9
- perMessageDeflate?: boolean | {
10
- compress?: boolean | BunWSCompressor;
11
- decompress?: boolean | BunWSCompressor;
12
- };
13
- }
14
- type BunWSCompressor = `"disable"` | `"shared"` | `"dedicated"` | `"3KB"` | `"4KB"` | `"8KB"` | `"16KB"` | `"32KB"` | `"64KB"` | `"128KB"` | `"256KB"`;
15
- interface BunServerWebSocket {
16
- readonly data: any;
17
- readonly readyState: number;
18
- readonly remoteAddress: string;
19
- send(message: string | ArrayBuffer | Uint8Array, compress?: boolean): number;
20
- close(code?: number, reason?: string): void;
21
- subscribe(topic: string): void;
22
- unsubscribe(topic: string): void;
23
- publish(topic: string, message: string | ArrayBuffer | Uint8Array): void;
24
- isSubscribed(topic: string): boolean;
25
- cork(cb: (ws: BunServerWebSocket) => void): void;
26
- }
27
-
28
- declare const WebSocket: {
29
- new (url: string | URL, protocols?: string | string[] | undefined): WebSocket;
30
- prototype: WebSocket;
31
- readonly CONNECTING: 0;
32
- readonly OPEN: 1;
33
- readonly CLOSING: 2;
34
- readonly CLOSED: 3;
35
- };
36
3
  interface AdapterOptions {
37
4
  }
5
+ type WebSocketHooks = Extract<Parameters<typeof Bun.serve<ContextData>>[0], {
6
+ websocket: any;
7
+ }>["websocket"];
38
8
  interface Adapter {
39
- websocket: BunWSOptions;
9
+ websocket: WebSocketHooks;
40
10
  }
41
11
  declare const _default: WebSocketAdapter<Adapter, AdapterOptions>;
42
12
 
43
- export { type Adapter, type AdapterOptions, WebSocket, _default as default };
13
+ export { type Adapter, type AdapterOptions, _default as default };
@@ -1,52 +1,70 @@
1
- import { d as defineWebSocketAdapter, a as WebSocketMessage, W as WebSocketError, b as WebSocketPeer } from '../shared/crossws.08866fa6.mjs';
1
+ import { d as defineWebSocketAdapter, a as WebSocketMessage, W as WebSocketError, b as WebSocketPeerBase } from '../shared/crossws.9536f626.mjs';
2
2
 
3
- const WebSocket = globalThis.WebSocket;
4
3
  const bun = defineWebSocketAdapter(
5
- (handler, opts = {}) => {
4
+ (hooks, opts = {}) => {
5
+ const getPeer = (ws) => {
6
+ if (ws.data?._peer) {
7
+ return ws.data._peer;
8
+ }
9
+ const peer = new WebSocketPeer({ bun: { ws } });
10
+ ws.data = ws.data || {};
11
+ ws.data._peer = peer;
12
+ return peer;
13
+ };
6
14
  return {
7
15
  websocket: {
8
16
  message: (ws, message) => {
9
- handler.onEvent?.("bun:message", ws, message);
10
- const peer = new BunWebSocketPeer(ws);
11
- handler.onMessage?.(peer, new WebSocketMessage(message));
17
+ const peer = getPeer(ws);
18
+ hooks["bun:message"]?.(peer, ws, message);
19
+ hooks.message?.(peer, new WebSocketMessage(message));
12
20
  },
13
21
  open: (ws) => {
14
- handler.onEvent?.("bun:open", ws);
15
- const peer = new BunWebSocketPeer(ws);
16
- handler.onOpen?.(peer);
22
+ const peer = getPeer(ws);
23
+ hooks["bun:open"]?.(peer, ws);
24
+ hooks.open?.(peer);
17
25
  },
18
26
  close: (ws) => {
19
- handler.onEvent?.("bun:close", ws);
20
- const peer = new BunWebSocketPeer(ws);
21
- handler.onClose?.(peer, 0, "");
27
+ const peer = getPeer(ws);
28
+ hooks["bun:close"]?.(peer, ws);
29
+ hooks.close?.(peer, {});
30
+ },
31
+ drain: (ws) => {
32
+ const peer = getPeer(ws);
33
+ hooks["bun:drain"]?.(peer);
22
34
  },
35
+ // @ts-expect-error types unavailable but mentioned in docs
23
36
  error: (ws, error) => {
24
- handler.onEvent?.("bun:error", ws, error);
25
- const peer = new BunWebSocketPeer(ws);
26
- handler.onError?.(peer, new WebSocketError(error));
37
+ const peer = getPeer(ws);
38
+ hooks["bun:error"]?.(peer, ws, error);
39
+ hooks.error?.(peer, new WebSocketError(error));
27
40
  },
28
- drain: (ws) => {
29
- handler.onEvent?.("bun:drain", ws);
41
+ ping(ws, data) {
42
+ const peer = getPeer(ws);
43
+ hooks["bun:ping"]?.(peer, ws, data);
44
+ },
45
+ pong(ws, data) {
46
+ const peer = getPeer(ws);
47
+ hooks["bun:pong"]?.(peer, ws, data);
30
48
  }
31
49
  }
32
50
  };
33
51
  }
34
52
  );
35
- class BunWebSocketPeer extends WebSocketPeer {
36
- constructor(_ws) {
37
- super();
38
- this._ws = _ws;
39
- }
53
+ class WebSocketPeer extends WebSocketPeerBase {
40
54
  get id() {
41
- return this._ws.remoteAddress;
55
+ let addr = this.ctx.bun.ws.remoteAddress;
56
+ if (addr.includes(":")) {
57
+ addr = `[${addr}]`;
58
+ }
59
+ return addr;
42
60
  }
43
61
  get readyState() {
44
- return this._ws.readyState;
62
+ return this.ctx.bun.ws.readyState;
45
63
  }
46
64
  send(message) {
47
- this._ws.send(message);
65
+ this.ctx.bun.ws.send(message);
48
66
  return 0;
49
67
  }
50
68
  }
51
69
 
52
- export { WebSocket, bun as default };
70
+ export { bun as default };