crossws 0.2.3 → 0.3.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 (66) hide show
  1. package/README.md +8 -9
  2. package/adapters/cloudflare-durable.d.ts +2 -0
  3. package/adapters/sse.d.ts +2 -0
  4. package/dist/adapters/bun.d.mts +25 -6
  5. package/dist/adapters/bun.d.ts +25 -6
  6. package/dist/adapters/bun.mjs +54 -62
  7. package/dist/adapters/cloudflare-durable.d.mts +20 -0
  8. package/dist/adapters/cloudflare-durable.d.ts +20 -0
  9. package/dist/adapters/cloudflare-durable.mjs +132 -0
  10. package/dist/adapters/cloudflare.d.mts +5 -5
  11. package/dist/adapters/cloudflare.d.ts +5 -5
  12. package/dist/adapters/cloudflare.mjs +56 -49
  13. package/dist/adapters/deno.d.mts +10 -3
  14. package/dist/adapters/deno.d.ts +10 -3
  15. package/dist/adapters/deno.mjs +54 -45
  16. package/dist/adapters/node.d.mts +4 -2
  17. package/dist/adapters/node.d.ts +4 -2
  18. package/dist/adapters/node.mjs +108 -71
  19. package/dist/adapters/sse.d.mts +12 -0
  20. package/dist/adapters/sse.d.ts +12 -0
  21. package/dist/adapters/sse.mjs +120 -0
  22. package/dist/adapters/uws.d.mts +45 -9
  23. package/dist/adapters/uws.d.ts +45 -9
  24. package/dist/adapters/uws.mjs +148 -124
  25. package/dist/index.d.mts +136 -4
  26. package/dist/index.d.ts +136 -4
  27. package/dist/index.mjs +1 -10
  28. package/dist/shared/crossws.B4sHId41.mjs +42 -0
  29. package/dist/shared/crossws.By9qWDAI.mjs +8 -0
  30. package/dist/shared/crossws.ChIJSJVK.d.mts +297 -0
  31. package/dist/shared/crossws.ChIJSJVK.d.ts +297 -0
  32. package/dist/shared/crossws.DTY7a69w.mjs +315 -0
  33. package/dist/shared/{crossws.a5db571c.mjs → crossws.YgHWLi0G.mjs} +284 -115
  34. package/dist/websocket/{index.d.mts → native.d.mts} +1 -1
  35. package/dist/websocket/{node.d.cts → native.d.ts} +1 -1
  36. package/dist/websocket/native.mjs +3 -0
  37. package/dist/websocket/node.d.mts +1 -1
  38. package/dist/websocket/node.d.ts +1 -1
  39. package/dist/websocket/node.mjs +1 -1
  40. package/dist/websocket/sse.d.mts +41 -0
  41. package/dist/websocket/sse.d.ts +41 -0
  42. package/dist/websocket/sse.mjs +127 -0
  43. package/package.json +78 -75
  44. package/dist/adapters/bun.cjs +0 -95
  45. package/dist/adapters/bun.d.cts +0 -17
  46. package/dist/adapters/cloudflare.cjs +0 -65
  47. package/dist/adapters/cloudflare.d.cts +0 -12
  48. package/dist/adapters/deno.cjs +0 -63
  49. package/dist/adapters/deno.d.cts +0 -15
  50. package/dist/adapters/node.cjs +0 -726
  51. package/dist/adapters/node.d.cts +0 -298
  52. package/dist/adapters/uws.cjs +0 -153
  53. package/dist/adapters/uws.d.cts +0 -19
  54. package/dist/index.cjs +0 -17
  55. package/dist/index.d.cts +0 -6
  56. package/dist/shared/crossws.2ed26345.cjs +0 -3931
  57. package/dist/shared/crossws.34cfc8d0.mjs +0 -139
  58. package/dist/shared/crossws.a2e5c71e.d.cts +0 -112
  59. package/dist/shared/crossws.a2e5c71e.d.mts +0 -112
  60. package/dist/shared/crossws.a2e5c71e.d.ts +0 -112
  61. package/dist/shared/crossws.c13afbe7.cjs +0 -146
  62. package/dist/websocket/index.cjs +0 -5
  63. package/dist/websocket/index.d.cts +0 -10
  64. package/dist/websocket/index.d.ts +0 -10
  65. package/dist/websocket/index.mjs +0 -3
  66. package/dist/websocket/node.cjs +0 -17
@@ -1,60 +1,69 @@
1
- import { d as defineWebSocketAdapter, M as Message, P as Peer, t as toBufferLike, c as createCrossWS } from '../shared/crossws.34cfc8d0.mjs';
2
- import { WSError } from '../index.mjs';
1
+ import { M as Message, P as Peer, t as toBufferLike } from '../shared/crossws.DTY7a69w.mjs';
2
+ import { d as defineWebSocketAdapter, a as adapterUtils, A as AdapterHookable } from '../shared/crossws.B4sHId41.mjs';
3
+ import { W as WSError } from '../shared/crossws.By9qWDAI.mjs';
4
+ import 'uncrypto';
3
5
 
4
6
  const deno = defineWebSocketAdapter(
5
7
  (options = {}) => {
6
- const crossws = createCrossWS(options);
7
- const handleUpgrade = async (req, info) => {
8
- const { headers } = await crossws.upgrade({
9
- url: req.url,
10
- headers: req.headers
11
- });
12
- const upgrade = Deno.upgradeWebSocket(req, {
13
- // @ts-expect-error https://github.com/denoland/deno/pull/22242
14
- headers
15
- });
16
- const peer = new DenoPeer({
17
- deno: { ws: upgrade.socket, req, info }
18
- });
19
- upgrade.socket.addEventListener("open", () => {
20
- crossws.$callHook("deno:open", peer);
21
- crossws.callHook("open", peer);
22
- });
23
- upgrade.socket.addEventListener("message", (event) => {
24
- crossws.$callHook("deno:message", peer, event);
25
- crossws.callHook("message", peer, new Message(event.data));
26
- });
27
- upgrade.socket.addEventListener("close", () => {
28
- crossws.$callHook("deno:close", peer);
29
- crossws.callHook("close", peer, {});
30
- });
31
- upgrade.socket.addEventListener("error", (error) => {
32
- crossws.$callHook("deno:error", peer, error);
33
- crossws.callHook("error", peer, new WSError(error));
34
- });
35
- return upgrade.response;
36
- };
8
+ const hooks = new AdapterHookable(options);
9
+ const peers = /* @__PURE__ */ new Set();
37
10
  return {
38
- handleUpgrade
11
+ ...adapterUtils(peers),
12
+ handleUpgrade: async (request, info) => {
13
+ const res = await hooks.callHook("upgrade", request);
14
+ if (res instanceof Response) {
15
+ return res;
16
+ }
17
+ const upgrade = Deno.upgradeWebSocket(request, {
18
+ // @ts-expect-error https://github.com/denoland/deno/pull/22242
19
+ headers: res?.headers
20
+ });
21
+ const peer = new DenoPeer({
22
+ ws: upgrade.socket,
23
+ request,
24
+ peers,
25
+ denoInfo: info
26
+ });
27
+ peers.add(peer);
28
+ upgrade.socket.addEventListener("open", () => {
29
+ hooks.callHook("open", peer);
30
+ });
31
+ upgrade.socket.addEventListener("message", (event) => {
32
+ hooks.callHook("message", peer, new Message(event.data, peer, event));
33
+ });
34
+ upgrade.socket.addEventListener("close", () => {
35
+ peers.delete(peer);
36
+ hooks.callHook("close", peer, {});
37
+ });
38
+ upgrade.socket.addEventListener("error", (error) => {
39
+ peers.delete(peer);
40
+ hooks.callHook("error", peer, new WSError(error));
41
+ });
42
+ return upgrade.response;
43
+ }
39
44
  };
40
45
  }
41
46
  );
42
47
  class DenoPeer extends Peer {
43
- get id() {
44
- return this.ctx.deno.ws.remoteAddress;
48
+ get remoteAddress() {
49
+ return this._internal.denoInfo.remoteAddr?.hostname;
45
50
  }
46
- get readyState() {
47
- return this.ctx.deno.ws.readyState;
51
+ send(data) {
52
+ return this._internal.ws.send(toBufferLike(data));
48
53
  }
49
- get url() {
50
- return this.ctx.deno.req.url;
54
+ publish(topic, data) {
55
+ const dataBuff = toBufferLike(data);
56
+ for (const peer of this._internal.peers) {
57
+ if (peer !== this && peer._topics.has(topic)) {
58
+ peer._internal.ws.send(dataBuff);
59
+ }
60
+ }
51
61
  }
52
- get headers() {
53
- return this.ctx.deno.req.headers || new Headers();
62
+ close(code, reason) {
63
+ this._internal.ws.close(code, reason);
54
64
  }
55
- send(message) {
56
- this.ctx.deno.ws.send(toBufferLike(message));
57
- return 0;
65
+ terminate() {
66
+ this._internal.ws.terminate();
58
67
  }
59
68
  }
60
69
 
@@ -1,4 +1,4 @@
1
- import { A as AdapterOptions, b as Adapter } from '../shared/crossws.a2e5c71e.mjs';
1
+ import { AdapterInstance, AdapterOptions, Adapter } from '../index.mjs';
2
2
  import { IncomingMessage as IncomingMessage$1 } from 'node:http';
3
3
  import { Duplex as Duplex$1 } from 'node:stream';
4
4
  import { EventEmitter } from 'events';
@@ -8,6 +8,7 @@ import { Duplex, DuplexOptions } from 'stream';
8
8
  import { SecureContextOptions } from 'tls';
9
9
  import { URL } from 'url';
10
10
  import { ZlibOptions } from 'zlib';
11
+ import '../shared/crossws.ChIJSJVK.mjs';
11
12
 
12
13
  type BufferLike = string | Buffer | DataView | number | ArrayBufferView | Uint8Array | ArrayBuffer | SharedArrayBuffer | readonly any[] | readonly number[] | {
13
14
  valueOf(): ArrayBuffer;
@@ -286,8 +287,9 @@ declare class Server<T extends typeof WebSocket = typeof WebSocket, U extends ty
286
287
  type WebSocketServer = Server;
287
288
  declare function createWebSocketStream(websocket: WebSocket, options?: DuplexOptions): Duplex;
288
289
 
289
- interface NodeAdapter {
290
+ interface NodeAdapter extends AdapterInstance {
290
291
  handleUpgrade(req: IncomingMessage$1, socket: Duplex$1, head: Buffer): void;
292
+ closeAll: (code?: number, data?: string | Buffer) => void;
291
293
  }
292
294
  interface NodeOptions extends AdapterOptions {
293
295
  wss?: WebSocketServer;
@@ -1,4 +1,4 @@
1
- import { A as AdapterOptions, b as Adapter } from '../shared/crossws.a2e5c71e.js';
1
+ import { AdapterInstance, AdapterOptions, Adapter } from '../index.js';
2
2
  import { IncomingMessage as IncomingMessage$1 } from 'node:http';
3
3
  import { Duplex as Duplex$1 } from 'node:stream';
4
4
  import { EventEmitter } from 'events';
@@ -8,6 +8,7 @@ import { Duplex, DuplexOptions } from 'stream';
8
8
  import { SecureContextOptions } from 'tls';
9
9
  import { URL } from 'url';
10
10
  import { ZlibOptions } from 'zlib';
11
+ import '../shared/crossws.ChIJSJVK.js';
11
12
 
12
13
  type BufferLike = string | Buffer | DataView | number | ArrayBufferView | Uint8Array | ArrayBuffer | SharedArrayBuffer | readonly any[] | readonly number[] | {
13
14
  valueOf(): ArrayBuffer;
@@ -286,8 +287,9 @@ declare class Server<T extends typeof WebSocket = typeof WebSocket, U extends ty
286
287
  type WebSocketServer = Server;
287
288
  declare function createWebSocketStream(websocket: WebSocket, options?: DuplexOptions): Duplex;
288
289
 
289
- interface NodeAdapter {
290
+ interface NodeAdapter extends AdapterInstance {
290
291
  handleUpgrade(req: IncomingMessage$1, socket: Duplex$1, head: Buffer): void;
292
+ closeAll: (code?: number, data?: string | Buffer) => void;
291
293
  }
292
294
  interface NodeOptions extends AdapterOptions {
293
295
  wss?: WebSocketServer;
@@ -1,10 +1,12 @@
1
+ import { M as Message, P as Peer, t as toBufferLike } from '../shared/crossws.DTY7a69w.mjs';
2
+ import { d as defineWebSocketAdapter, a as adapterUtils, A as AdapterHookable } from '../shared/crossws.B4sHId41.mjs';
3
+ import { W as WSError } from '../shared/crossws.By9qWDAI.mjs';
1
4
  import 'stream';
2
- import { v as validationExports, g as getDefaultExportFromCjs, e as extension$1, p as permessageDeflate, w as websocket, c as constants } from '../shared/crossws.a5db571c.mjs';
5
+ import { v as validationExports, g as getDefaultExportFromCjs, e as extension$1, p as permessageDeflate, w as websocket, c as constants } from '../shared/crossws.YgHWLi0G.mjs';
3
6
  import require$$0 from 'events';
4
7
  import require$$2 from 'http';
5
8
  import require$$1 from 'crypto';
6
- import { d as defineWebSocketAdapter, M as Message, P as Peer, t as toBufferLike, c as createCrossWS } from '../shared/crossws.34cfc8d0.mjs';
7
- import { WSError } from '../index.mjs';
9
+ import 'uncrypto';
8
10
  import 'https';
9
11
  import 'net';
10
12
  import 'tls';
@@ -73,7 +75,7 @@ function parse(header) {
73
75
 
74
76
  var subprotocol$1 = { parse };
75
77
 
76
- /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex$" }] */
78
+ /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex$", "caughtErrors": "none" }] */
77
79
 
78
80
  const EventEmitter = require$$0;
79
81
  const http = require$$2;
@@ -101,7 +103,7 @@ class WebSocketServer extends EventEmitter {
101
103
  * Create a `WebSocketServer` instance.
102
104
  *
103
105
  * @param {Object} options Configuration options
104
- * @param {Boolean} [options.allowSynchronousEvents=false] Specifies whether
106
+ * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
105
107
  * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
106
108
  * multiple times in the same tick
107
109
  * @param {Boolean} [options.autoPong=true] Specifies whether or not to
@@ -132,7 +134,7 @@ class WebSocketServer extends EventEmitter {
132
134
  super();
133
135
 
134
136
  options = {
135
- allowSynchronousEvents: false,
137
+ allowSynchronousEvents: true,
136
138
  autoPong: true,
137
139
  maxPayload: 100 * 1024 * 1024,
138
140
  skipUTF8Validation: false,
@@ -307,6 +309,7 @@ class WebSocketServer extends EventEmitter {
307
309
  socket.on('error', socketOnError);
308
310
 
309
311
  const key = req.headers['sec-websocket-key'];
312
+ const upgrade = req.headers.upgrade;
310
313
  const version = +req.headers['sec-websocket-version'];
311
314
 
312
315
  if (req.method !== 'GET') {
@@ -315,13 +318,13 @@ class WebSocketServer extends EventEmitter {
315
318
  return;
316
319
  }
317
320
 
318
- if (req.headers.upgrade.toLowerCase() !== 'websocket') {
321
+ if (upgrade === undefined || upgrade.toLowerCase() !== 'websocket') {
319
322
  const message = 'Invalid Upgrade header';
320
323
  abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
321
324
  return;
322
325
  }
323
326
 
324
- if (!key || !keyRegex.test(key)) {
327
+ if (key === undefined || !keyRegex.test(key)) {
325
328
  const message = 'Missing or invalid Sec-WebSocket-Key header';
326
329
  abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
327
330
  return;
@@ -614,105 +617,139 @@ const _WebSocketServer = /*@__PURE__*/getDefaultExportFromCjs(websocketServer);
614
617
 
615
618
  const node = defineWebSocketAdapter(
616
619
  (options = {}) => {
617
- const crossws = createCrossWS(options);
620
+ const hooks = new AdapterHookable(options);
621
+ const peers = /* @__PURE__ */ new Set();
618
622
  const wss = options.wss || new _WebSocketServer({
619
623
  noServer: true,
620
624
  ...options.serverOptions
621
625
  });
622
- wss.on("connection", (ws, req) => {
623
- const peer = new NodePeer({ node: { ws, req, server: wss } });
624
- crossws.callHook("open", peer);
625
- ws.on("message", (data, isBinary) => {
626
- crossws.$callHook("node:message", peer, data, isBinary);
626
+ wss.on("connection", (ws, nodeReq) => {
627
+ const request = new NodeReqProxy(nodeReq);
628
+ const peer = new NodePeer({ ws, request, peers, nodeReq });
629
+ peers.add(peer);
630
+ hooks.callHook("open", peer);
631
+ ws.on("message", (data) => {
627
632
  if (Array.isArray(data)) {
628
633
  data = Buffer.concat(data);
629
634
  }
630
- crossws.callHook("message", peer, new Message(data, isBinary));
635
+ hooks.callHook("message", peer, new Message(data, peer));
631
636
  });
632
637
  ws.on("error", (error) => {
633
- crossws.$callHook("node:error", peer, error);
634
- crossws.callHook("error", peer, new WSError(error));
638
+ peers.delete(peer);
639
+ hooks.callHook("error", peer, new WSError(error));
635
640
  });
636
641
  ws.on("close", (code, reason) => {
637
- crossws.$callHook("node:close", peer, code, reason);
638
- crossws.callHook("close", peer, {
642
+ peers.delete(peer);
643
+ hooks.callHook("close", peer, {
639
644
  code,
640
645
  reason: reason?.toString()
641
646
  });
642
647
  });
643
- ws.on("open", () => {
644
- crossws.$callHook("node:open", peer);
645
- });
646
- ws.on("ping", (data) => {
647
- crossws.$callHook("node:ping", peer, data);
648
- });
649
- ws.on("pong", (data) => {
650
- crossws.$callHook("node:pong", peer, data);
651
- });
652
- ws.on(
653
- "unexpected-response",
654
- (req2, res) => {
655
- crossws.$callHook("node:unexpected-response", peer, req2, res);
656
- }
657
- );
658
- ws.on("upgrade", (req2) => {
659
- crossws.$callHook("node:upgrade", peer, req2);
660
- });
661
648
  });
662
- wss.on("headers", function(outgoingHeaders, req) {
649
+ wss.on("headers", (outgoingHeaders, req) => {
663
650
  const upgradeHeaders = req._upgradeHeaders;
664
651
  if (upgradeHeaders) {
665
- const _headers = new Headers(upgradeHeaders);
666
- for (const [key, value] of _headers) {
652
+ for (const [key, value] of new Headers(upgradeHeaders)) {
667
653
  outgoingHeaders.push(`${key}: ${value}`);
668
654
  }
669
655
  }
670
656
  });
671
657
  return {
672
- handleUpgrade: async (req, socket, head) => {
673
- const { headers } = await crossws.upgrade({
674
- url: req.url || "",
675
- headers: req.headers
676
- });
677
- req._upgradeHeaders = headers;
678
- wss.handleUpgrade(req, socket, head, (ws) => {
679
- wss.emit("connection", ws, req);
658
+ ...adapterUtils(peers),
659
+ handleUpgrade: async (nodeReq, socket, head) => {
660
+ const request = new NodeReqProxy(nodeReq);
661
+ const res = await hooks.callHook("upgrade", request);
662
+ if (res instanceof Response) {
663
+ return sendResponse(socket, res);
664
+ }
665
+ nodeReq._request = request;
666
+ nodeReq._upgradeHeaders = res?.headers;
667
+ wss.handleUpgrade(nodeReq, socket, head, (ws) => {
668
+ wss.emit("connection", ws, nodeReq);
680
669
  });
670
+ },
671
+ closeAll: (code, data) => {
672
+ for (const client of wss.clients) {
673
+ client.close(code, data);
674
+ }
681
675
  }
682
676
  };
683
677
  }
684
678
  );
685
679
  class NodePeer extends Peer {
686
- get id() {
687
- const socket = this.ctx.node.req.socket;
688
- if (!socket) {
689
- return void 0;
690
- }
691
- const headers = this.ctx.node.req.headers;
692
- let addr = headers["x-forwarded-for"] || socket.remoteAddress || "??";
693
- if (addr.includes(":")) {
694
- addr = `[${addr}]`;
680
+ get remoteAddress() {
681
+ return this._internal.nodeReq.socket?.remoteAddress;
682
+ }
683
+ send(data, options) {
684
+ const dataBuff = toBufferLike(data);
685
+ const isBinary = typeof data !== "string";
686
+ this._internal.ws.send(dataBuff, {
687
+ compress: options?.compress,
688
+ binary: isBinary,
689
+ ...options
690
+ });
691
+ return 0;
692
+ }
693
+ publish(topic, data, options) {
694
+ const dataBuff = toBufferLike(data);
695
+ const isBinary = typeof data !== "string";
696
+ const sendOptions = {
697
+ compress: options?.compress,
698
+ binary: isBinary,
699
+ ...options
700
+ };
701
+ for (const peer of this._internal.peers) {
702
+ if (peer !== this && peer._topics.has(topic)) {
703
+ peer._internal.ws.send(dataBuff, sendOptions);
704
+ }
695
705
  }
696
- const port = headers["x-forwarded-port"] || socket.remotePort || "??";
697
- return `${addr}:${port}`;
706
+ }
707
+ close(code, data) {
708
+ this._internal.ws.close(code, data);
709
+ }
710
+ terminate() {
711
+ this._internal.ws.terminate();
712
+ }
713
+ }
714
+ class NodeReqProxy {
715
+ _req;
716
+ _headers;
717
+ _url;
718
+ constructor(req) {
719
+ this._req = req;
698
720
  }
699
721
  get url() {
700
- return this.ctx.node.req.url || "/";
722
+ if (!this._url) {
723
+ const req = this._req;
724
+ const host = req.headers["host"] || "localhost";
725
+ const isSecure = req.socket?.encrypted ?? req.headers["x-forwarded-proto"] === "https";
726
+ this._url = `${isSecure ? "https" : "http"}://${host}${req.url}`;
727
+ }
728
+ return this._url;
701
729
  }
702
730
  get headers() {
703
- return this.ctx.node.req.headers;
704
- }
705
- get readyState() {
706
- return this.ctx.node.ws.readyState;
731
+ if (!this._headers) {
732
+ this._headers = new Headers(this._req.headers);
733
+ }
734
+ return this._headers;
707
735
  }
708
- send(message, options) {
709
- this.ctx.node.ws.send(toBufferLike(message), {
710
- compress: options?.compress,
711
- binary: options?.binary,
712
- ...options
713
- });
714
- return 0;
736
+ }
737
+ async function sendResponse(socket, res) {
738
+ const head = [
739
+ `HTTP/1.1 ${res.status || 200} ${res.statusText || ""}`,
740
+ ...[...res.headers.entries()].map(
741
+ ([key, value]) => `${encodeURIComponent(key)}: ${encodeURIComponent(value)}`
742
+ )
743
+ ];
744
+ socket.write(head.join("\r\n") + "\r\n\r\n");
745
+ if (res.body) {
746
+ for await (const chunk of res.body) {
747
+ socket.write(chunk);
748
+ }
715
749
  }
750
+ return new Promise((resolve) => {
751
+ socket.end(resolve);
752
+ });
716
753
  }
717
754
 
718
755
  export { node as default };
@@ -0,0 +1,12 @@
1
+ import { AdapterInstance, AdapterOptions, Adapter } from '../index.mjs';
2
+ import '../shared/crossws.ChIJSJVK.mjs';
3
+
4
+ interface SSEAdapter extends AdapterInstance {
5
+ fetch(req: Request): Promise<Response>;
6
+ }
7
+ interface SSEOptions extends AdapterOptions {
8
+ bidir?: boolean;
9
+ }
10
+ declare const _default: Adapter<SSEAdapter, SSEOptions>;
11
+
12
+ export { type SSEAdapter, type SSEOptions, _default as default };
@@ -0,0 +1,12 @@
1
+ import { AdapterInstance, AdapterOptions, Adapter } from '../index.js';
2
+ import '../shared/crossws.ChIJSJVK.js';
3
+
4
+ interface SSEAdapter extends AdapterInstance {
5
+ fetch(req: Request): Promise<Response>;
6
+ }
7
+ interface SSEOptions extends AdapterOptions {
8
+ bidir?: boolean;
9
+ }
10
+ declare const _default: Adapter<SSEAdapter, SSEOptions>;
11
+
12
+ export { type SSEAdapter, type SSEOptions, _default as default };
@@ -0,0 +1,120 @@
1
+ import { M as Message, P as Peer, a as toString } from '../shared/crossws.DTY7a69w.mjs';
2
+ import { d as defineWebSocketAdapter, a as adapterUtils, A as AdapterHookable } from '../shared/crossws.B4sHId41.mjs';
3
+ import 'uncrypto';
4
+
5
+ const sse = defineWebSocketAdapter((opts = {}) => {
6
+ const hooks = new AdapterHookable(opts);
7
+ const peers = /* @__PURE__ */ new Set();
8
+ const peersMap = opts.bidir ? /* @__PURE__ */ new Map() : void 0;
9
+ return {
10
+ ...adapterUtils(peers),
11
+ fetch: async (request) => {
12
+ const _res = await hooks.callHook("upgrade", request);
13
+ if (_res instanceof Response) {
14
+ return _res;
15
+ }
16
+ let peer;
17
+ if (opts.bidir && request.body && request.headers.has("x-crossws-id")) {
18
+ const id = request.headers.get("x-crossws-id");
19
+ peer = peersMap?.get(id);
20
+ if (!peer) {
21
+ return new Response("invalid peer id", { status: 400 });
22
+ }
23
+ const stream = request.body.pipeThrough(new TextDecoderStream());
24
+ try {
25
+ for await (const chunk of stream) {
26
+ hooks.callHook("message", peer, new Message(chunk, peer));
27
+ }
28
+ } catch {
29
+ await stream.cancel().catch(() => {
30
+ });
31
+ }
32
+ return new Response(null, {});
33
+ } else {
34
+ const ws = new SSEWebSocketStub();
35
+ peer = new SSEPeer({
36
+ peers,
37
+ peersMap,
38
+ request,
39
+ hooks,
40
+ ws
41
+ });
42
+ peers.add(peer);
43
+ if (opts.bidir) {
44
+ peersMap.set(peer.id, peer);
45
+ peer._sendEvent("crossws-id", peer.id);
46
+ }
47
+ }
48
+ let headers = {
49
+ "Content-Type": "text/event-stream",
50
+ "Cache-Control": "no-cache",
51
+ Connection: "keep-alive"
52
+ };
53
+ if (opts.bidir) {
54
+ headers["x-crossws-id"] = peer.id;
55
+ }
56
+ if (_res?.headers) {
57
+ headers = new Headers(headers);
58
+ for (const [key, value] of new Headers(_res.headers)) {
59
+ headers.set(key, value);
60
+ }
61
+ }
62
+ return new Response(peer._sseStream, { ..._res, headers });
63
+ }
64
+ };
65
+ });
66
+ class SSEPeer extends Peer {
67
+ _sseStream;
68
+ // server -> client
69
+ _sseStreamController;
70
+ constructor(_internal) {
71
+ super(_internal);
72
+ _internal.ws.readyState = 0;
73
+ this._sseStream = new ReadableStream({
74
+ start: (controller) => {
75
+ _internal.ws.readyState = 1;
76
+ this._sseStreamController = controller;
77
+ _internal.hooks.callHook("open", this);
78
+ },
79
+ cancel: () => {
80
+ _internal.ws.readyState = 2;
81
+ _internal.peers.delete(this);
82
+ _internal.peersMap?.delete(this.id);
83
+ Promise.resolve(this._internal.hooks.callHook("close", this)).finally(
84
+ () => {
85
+ _internal.ws.readyState = 3;
86
+ }
87
+ );
88
+ }
89
+ }).pipeThrough(new TextEncoderStream());
90
+ }
91
+ _sendEvent(event, data) {
92
+ const lines = data.split("\n");
93
+ this._sseStreamController?.enqueue(
94
+ `event: ${event}
95
+ ${lines.map((l) => `data: ${l}`)}
96
+
97
+ `
98
+ );
99
+ }
100
+ send(data) {
101
+ this._sendEvent("message", toString(data));
102
+ return 0;
103
+ }
104
+ publish(topic, data) {
105
+ const dataBuff = toString(data);
106
+ for (const peer of this._internal.peers) {
107
+ if (peer !== this && peer._topics.has(topic)) {
108
+ peer._sendEvent("message", dataBuff);
109
+ }
110
+ }
111
+ }
112
+ close() {
113
+ this._sseStreamController?.close();
114
+ }
115
+ }
116
+ class SSEWebSocketStub {
117
+ readyState;
118
+ }
119
+
120
+ export { sse as default };
@@ -1,19 +1,55 @@
1
- import { A as AdapterOptions, b as Adapter } from '../shared/crossws.a2e5c71e.mjs';
2
- import { WebSocketBehavior, HttpRequest, HttpResponse } from 'uWebSockets.js';
1
+ import { AdapterInstance, AdapterOptions, Adapter, Peer } from '../index.mjs';
2
+ import { W as WebSocket } from '../shared/crossws.ChIJSJVK.mjs';
3
+ import uws from 'uWebSockets.js';
3
4
 
4
5
  type UserData = {
5
- _peer?: any;
6
- req: HttpRequest;
7
- res: HttpResponse;
8
- context: any;
6
+ peer?: UWSPeer;
7
+ req: uws.HttpRequest;
8
+ res: uws.HttpResponse;
9
+ protocol: string;
10
+ extensions: string;
9
11
  };
10
- type WebSocketHandler = WebSocketBehavior<UserData>;
11
- interface UWSAdapter {
12
+ type WebSocketHandler = uws.WebSocketBehavior<UserData>;
13
+ interface UWSAdapter extends AdapterInstance {
12
14
  websocket: WebSocketHandler;
13
15
  }
14
16
  interface UWSOptions extends AdapterOptions {
15
- uws?: Exclude<WebSocketBehavior<any>, "close" | "drain" | "message" | "open" | "ping" | "pong" | "subscription" | "upgrade">;
17
+ uws?: Exclude<uws.WebSocketBehavior<any>, "close" | "drain" | "message" | "open" | "ping" | "pong" | "subscription" | "upgrade">;
16
18
  }
17
19
  declare const _default: Adapter<UWSAdapter, UWSOptions>;
18
20
 
21
+ declare class UWSPeer extends Peer<{
22
+ peers: Set<UWSPeer>;
23
+ request: UWSReqProxy;
24
+ uws: uws.WebSocket<UserData>;
25
+ ws: UwsWebSocketProxy;
26
+ uwsData: UserData;
27
+ }> {
28
+ get remoteAddress(): string | undefined;
29
+ send(data: unknown, options?: {
30
+ compress?: boolean;
31
+ }): number;
32
+ subscribe(topic: string): void;
33
+ publish(topic: string, message: string, options?: {
34
+ compress?: boolean;
35
+ }): number;
36
+ close(code?: number, reason?: uws.RecognizedString): void;
37
+ terminate(): void;
38
+ }
39
+ declare class UWSReqProxy {
40
+ private _headers?;
41
+ private _rawHeaders;
42
+ url: string;
43
+ constructor(_req: uws.HttpRequest);
44
+ get headers(): Headers;
45
+ }
46
+ declare class UwsWebSocketProxy implements Partial<WebSocket> {
47
+ private _uws;
48
+ readyState?: number;
49
+ constructor(_uws: uws.WebSocket<UserData>);
50
+ get bufferedAmount(): number;
51
+ get protocol(): string;
52
+ get extensions(): string;
53
+ }
54
+
19
55
  export { type UWSAdapter, type UWSOptions, _default as default };