crossws 0.4.5 → 0.4.7

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 (49) hide show
  1. package/adapters/vercel.d.ts +2 -0
  2. package/dist/THIRD-PARTY-LICENSES.md +1 -1
  3. package/dist/_chunks/_request.mjs +2 -2
  4. package/dist/_chunks/_types.d.mts +8 -8
  5. package/dist/_chunks/adapter.d.mts +458 -4
  6. package/dist/_chunks/adapter.mjs +90 -13
  7. package/dist/_chunks/bun.d.mts +5 -3
  8. package/dist/_chunks/bunny.d.mts +2 -2
  9. package/dist/_chunks/cloudflare.d.mts +3 -3
  10. package/dist/_chunks/deno.d.mts +2 -2
  11. package/dist/_chunks/error.mjs +1 -1
  12. package/dist/_chunks/libs/ws.mjs +53 -4
  13. package/dist/_chunks/node.d.mts +2 -2
  14. package/dist/_chunks/node.mjs +17 -11
  15. package/dist/_chunks/peer.mjs +38 -26
  16. package/dist/_chunks/sse.d.mts +2 -2
  17. package/dist/_chunks/web.d.mts +1 -1
  18. package/dist/adapters/bun.d.mts +1 -1
  19. package/dist/adapters/bun.mjs +18 -9
  20. package/dist/adapters/bunny.d.mts +1 -1
  21. package/dist/adapters/bunny.mjs +8 -6
  22. package/dist/adapters/cloudflare.d.mts +1 -1
  23. package/dist/adapters/cloudflare.mjs +16 -13
  24. package/dist/adapters/deno.d.mts +1 -1
  25. package/dist/adapters/deno.mjs +22 -9
  26. package/dist/adapters/node.d.mts +2 -2
  27. package/dist/adapters/node.mjs +1 -1
  28. package/dist/adapters/sse.d.mts +1 -1
  29. package/dist/adapters/sse.mjs +7 -5
  30. package/dist/adapters/uws.d.mts +5 -4
  31. package/dist/adapters/uws.mjs +17 -10
  32. package/dist/adapters/vercel.d.mts +25 -0
  33. package/dist/adapters/vercel.mjs +48 -0
  34. package/dist/index.d.mts +30 -7
  35. package/dist/index.mjs +78 -35
  36. package/dist/server/bun.d.mts +1 -1
  37. package/dist/server/bunny.d.mts +1 -1
  38. package/dist/server/cloudflare.d.mts +1 -1
  39. package/dist/server/default.d.mts +1 -1
  40. package/dist/server/deno.d.mts +1 -1
  41. package/dist/server/node.d.mts +1 -1
  42. package/dist/server/node.mjs +1 -1
  43. package/dist/sync.d.mts +2 -0
  44. package/dist/sync.mjs +200 -0
  45. package/dist/websocket/node.mjs +1 -1
  46. package/dist/websocket/sse.d.mts +1 -1
  47. package/package.json +23 -35
  48. package/sync.d.ts +2 -0
  49. package/dist/_chunks/rolldown-runtime.mjs +0 -24
@@ -1,4 +1,4 @@
1
- import { d as PeerContext, n as AdapterInstance, r as AdapterOptions, t as Adapter, u as Peer } from "./adapter.mjs";
1
+ import { Adapter, AdapterInstance, AdapterOptions, Peer, PeerContext, SyncDriver } from "./adapter.mjs";
2
2
  import { Server, ServerWebSocket, WebSocketHandler } from "bun";
3
3
  interface BunAdapter extends AdapterInstance {
4
4
  websocket: WebSocketHandler<ContextData>;
@@ -18,13 +18,15 @@ declare class BunPeer extends Peer<{
18
18
  namespace: string;
19
19
  request: Request;
20
20
  peers: Set<BunPeer>;
21
+ sync?: SyncDriver;
21
22
  }> {
22
23
  get remoteAddress(): string;
23
24
  get context(): PeerContext;
25
+ get bufferedAmount(): number;
24
26
  send(data: unknown, options?: {
25
27
  compress?: boolean;
26
28
  }): number;
27
- publish(topic: string, data: unknown, options?: {
29
+ _publish(topic: string, data: unknown, options?: {
28
30
  compress?: boolean;
29
31
  }): number;
30
32
  subscribe(topic: string): void;
@@ -32,4 +34,4 @@ declare class BunPeer extends Peer<{
32
34
  close(code?: number, reason?: string): void;
33
35
  terminate(): void;
34
36
  }
35
- export { BunOptions as n, bunAdapter as r, BunAdapter as t };
37
+ export { BunAdapter, BunOptions, bunAdapter };
@@ -1,4 +1,4 @@
1
- import { n as AdapterInstance, r as AdapterOptions, t as Adapter } from "./adapter.mjs";
1
+ import { Adapter, AdapterInstance, AdapterOptions } from "./adapter.mjs";
2
2
  interface BunnyAdapter extends AdapterInstance {
3
3
  handleUpgrade(req: Request): Promise<Response>;
4
4
  }
@@ -19,4 +19,4 @@ interface BunnyOptions extends AdapterOptions {
19
19
  idleTimeout?: number;
20
20
  }
21
21
  declare const bunnyAdapter: Adapter<BunnyAdapter, BunnyOptions>;
22
- export { BunnyOptions as n, bunnyAdapter as r, BunnyAdapter as t };
22
+ export { BunnyAdapter, BunnyOptions, bunnyAdapter };
@@ -1,5 +1,5 @@
1
- import { n as AdapterInstance, r as AdapterOptions, t as Adapter } from "./adapter.mjs";
2
- import { a as WebSocket$1 } from "./web.mjs";
1
+ import { Adapter, AdapterInstance, AdapterOptions } from "./adapter.mjs";
2
+ import { WebSocket as WebSocket$1 } from "./web.mjs";
3
3
  import { DurableObject } from "cloudflare:workers";
4
4
  import * as CF from "@cloudflare/workers-types";
5
5
  type WSDurableObjectStub = CF.DurableObjectStub & {
@@ -39,4 +39,4 @@ interface CloudflareDurableAdapter extends AdapterInstance {
39
39
  handleDurablePublish: (obj: DurableObject, topic: string, data: unknown, opts: any) => Promise<void>;
40
40
  handleDurableClose(obj: DurableObject, ws: WebSocket | CF.WebSocket | WebSocket$1, code: number, reason: string, wasClean: boolean): Promise<void>;
41
41
  }
42
- export { CloudflareOptions as n, cloudflareAdapter as r, CloudflareDurableAdapter as t };
42
+ export { CloudflareDurableAdapter, CloudflareOptions, cloudflareAdapter };
@@ -1,4 +1,4 @@
1
- import { n as AdapterInstance, r as AdapterOptions, t as Adapter } from "./adapter.mjs";
1
+ import { Adapter, AdapterInstance, AdapterOptions } from "./adapter.mjs";
2
2
  interface DenoAdapter extends AdapterInstance {
3
3
  handleUpgrade(req: Request, info: ServeHandlerInfo): Promise<Response>;
4
4
  }
@@ -11,4 +11,4 @@ type ServeHandlerInfo = {
11
11
  };
12
12
  };
13
13
  declare const denoAdapter: Adapter<DenoAdapter, DenoOptions>;
14
- export { DenoOptions as n, denoAdapter as r, DenoAdapter as t };
14
+ export { DenoAdapter, DenoOptions, denoAdapter };
@@ -4,4 +4,4 @@ var WSError = class extends Error {
4
4
  this.name = "WSError";
5
5
  }
6
6
  };
7
- export { WSError as t };
7
+ export { WSError };
@@ -1,4 +1,26 @@
1
- import { n as __require, r as __toESM, t as __commonJSMin } from "../rolldown-runtime.mjs";
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
2
24
  var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
3
25
  const BINARY_TYPES = [
4
26
  "nodebuffer",
@@ -509,6 +531,8 @@ var require_receiver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
509
531
  this._binaryType = options.binaryType || BINARY_TYPES[0];
510
532
  this._extensions = options.extensions || {};
511
533
  this._isServer = !!options.isServer;
534
+ this._maxBufferedChunks = options.maxBufferedChunks | 0;
535
+ this._maxFragments = options.maxFragments | 0;
512
536
  this._maxPayload = options.maxPayload | 0;
513
537
  this._skipUTF8Validation = !!options.skipUTF8Validation;
514
538
  this[kWebSocket] = void 0;
@@ -530,6 +554,10 @@ var require_receiver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
530
554
  }
531
555
  _write(chunk, encoding, cb) {
532
556
  if (this._opcode === 8 && this._state == GET_INFO) return cb();
557
+ if (this._maxBufferedChunks > 0 && this._buffers.length >= this._maxBufferedChunks) {
558
+ cb(this.createError(RangeError, "Too many buffered chunks", false, 1008, "WS_ERR_TOO_MANY_BUFFERED_PARTS"));
559
+ return;
560
+ }
533
561
  this._bufferedBytes += chunk.length;
534
562
  this._buffers.push(chunk);
535
563
  this.startLoop(cb);
@@ -709,6 +737,10 @@ var require_receiver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
709
737
  return;
710
738
  }
711
739
  if (data.length) {
740
+ if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
741
+ cb(this.createError(RangeError, "Too many message fragments", false, 1008, "WS_ERR_TOO_MANY_BUFFERED_PARTS"));
742
+ return;
743
+ }
712
744
  this._messageLength = this._totalPayloadLength;
713
745
  this._fragments.push(data);
714
746
  }
@@ -723,6 +755,10 @@ var require_receiver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
723
755
  cb(this.createError(RangeError, "Max payload size exceeded", false, 1009, "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"));
724
756
  return;
725
757
  }
758
+ if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
759
+ cb(this.createError(RangeError, "Too many message fragments", false, 1008, "WS_ERR_TOO_MANY_BUFFERED_PARTS"));
760
+ return;
761
+ }
726
762
  this._fragments.push(buf);
727
763
  }
728
764
  this.dataMessage(cb);
@@ -827,6 +863,7 @@ var require_receiver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
827
863
  var require_sender = /* @__PURE__ */ __commonJSMin(((exports, module) => {
828
864
  const { Duplex: Duplex$3 } = __require("stream");
829
865
  const { randomFillSync } = __require("crypto");
866
+ const { types: { isUint8Array } } = __require("util");
830
867
  const PerMessageDeflate = require_permessage_deflate();
831
868
  const { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants();
832
869
  const { isBlob, isValidStatusCode } = require_validation();
@@ -931,7 +968,8 @@ var require_sender = /* @__PURE__ */ __commonJSMin(((exports, module) => {
931
968
  buf = Buffer.allocUnsafe(2 + length);
932
969
  buf.writeUInt16BE(code, 0);
933
970
  if (typeof data === "string") buf.write(data, 2);
934
- else buf.set(data, 2);
971
+ else if (isUint8Array(data)) buf.set(data, 2);
972
+ else throw new TypeError("Second argument must be a string or a Uint8Array");
935
973
  }
936
974
  const options = {
937
975
  [kByteLength]: buf.length,
@@ -1119,7 +1157,8 @@ var require_sender = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1119
1157
  this._state = DEFLATING;
1120
1158
  perMessageDeflate.compress(data, options.fin, (_, buf) => {
1121
1159
  if (this._socket.destroyed) {
1122
- callCallbacks(this, /* @__PURE__ */ new Error("The socket was closed while data was being compressed"), cb);
1160
+ const err = /* @__PURE__ */ new Error("The socket was closed while data was being compressed");
1161
+ callCallbacks(this, err, cb);
1123
1162
  return;
1124
1163
  }
1125
1164
  this._bufferedBytes -= options[kByteLength];
@@ -1507,6 +1546,8 @@ var require_websocket = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1507
1546
  binaryType: this.binaryType,
1508
1547
  extensions: this._extensions,
1509
1548
  isServer: this._isServer,
1549
+ maxBufferedChunks: options.maxBufferedChunks,
1550
+ maxFragments: options.maxFragments,
1510
1551
  maxPayload: options.maxPayload,
1511
1552
  skipUTF8Validation: options.skipUTF8Validation
1512
1553
  });
@@ -1714,6 +1755,8 @@ var require_websocket = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1714
1755
  autoPong: true,
1715
1756
  closeTimeout: CLOSE_TIMEOUT,
1716
1757
  protocolVersion: protocolVersions[1],
1758
+ maxBufferedChunks: 1024 * 1024,
1759
+ maxFragments: 128 * 1024,
1717
1760
  maxPayload: 100 * 1024 * 1024,
1718
1761
  skipUTF8Validation: false,
1719
1762
  perMessageDeflate: true,
@@ -1903,6 +1946,8 @@ var require_websocket = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1903
1946
  websocket.setSocket(socket, head, {
1904
1947
  allowSynchronousEvents: opts.allowSynchronousEvents,
1905
1948
  generateMask: opts.generateMask,
1949
+ maxBufferedChunks: opts.maxBufferedChunks,
1950
+ maxFragments: opts.maxFragments,
1906
1951
  maxPayload: opts.maxPayload,
1907
1952
  skipUTF8Validation: opts.skipUTF8Validation
1908
1953
  });
@@ -2188,6 +2233,8 @@ var require_websocket_server = /* @__PURE__ */ __commonJSMin(((exports, module)
2188
2233
  options = {
2189
2234
  allowSynchronousEvents: true,
2190
2235
  autoPong: true,
2236
+ maxBufferedChunks: 1024 * 1024,
2237
+ maxFragments: 128 * 1024,
2191
2238
  maxPayload: 100 * 1024 * 1024,
2192
2239
  skipUTF8Validation: false,
2193
2240
  perMessageDeflate: false,
@@ -2372,6 +2419,8 @@ var require_websocket_server = /* @__PURE__ */ __commonJSMin(((exports, module)
2372
2419
  socket.removeListener("error", socketOnError);
2373
2420
  ws.setSocket(socket, head, {
2374
2421
  allowSynchronousEvents: this.options.allowSynchronousEvents,
2422
+ maxBufferedChunks: this.options.maxBufferedChunks,
2423
+ maxFragments: this.options.maxFragments,
2375
2424
  maxPayload: this.options.maxPayload,
2376
2425
  skipUTF8Validation: this.options.skipUTF8Validation
2377
2426
  });
@@ -2426,4 +2475,4 @@ require_sender();
2426
2475
  require_subprotocol();
2427
2476
  var import_websocket = /* @__PURE__ */ __toESM(require_websocket(), 1);
2428
2477
  var import_websocket_server = /* @__PURE__ */ __toESM(require_websocket_server(), 1);
2429
- export { import_websocket_server as n, import_websocket as t };
2478
+ export { import_websocket, import_websocket_server };
@@ -1,4 +1,4 @@
1
- import { a as Hooks, n as AdapterInstance, r as AdapterOptions, t as Adapter } from "./adapter.mjs";
1
+ import { Adapter, AdapterInstance, AdapterOptions, Hooks } from "./adapter.mjs";
2
2
  import { EventEmitter } from "events";
3
3
  import { Agent, ClientRequest, ClientRequestArgs, IncomingMessage, OutgoingHttpHeaders, Server } from "node:http";
4
4
  import { Duplex, DuplexOptions } from "node:stream";
@@ -325,4 +325,4 @@ interface NodeOptions extends AdapterOptions {
325
325
  serverOptions?: ServerOptions;
326
326
  }
327
327
  declare const nodeAdapter: Adapter<NodeAdapter, NodeOptions>;
328
- export { fromNodeUpgradeHandler as a, NodeUpgradeHandler as i, NodeOptions as n, nodeAdapter as r, NodeAdapter as t };
328
+ export { NodeAdapter, NodeOptions, NodeUpgradeHandler, fromNodeUpgradeHandler, nodeAdapter };
@@ -1,8 +1,8 @@
1
- import { i as AdapterHookable, r as getPeers, t as adapterUtils } from "./adapter.mjs";
2
- import { n as import_websocket_server } from "./libs/ws.mjs";
3
- import { n as Message, r as toBufferLike, t as Peer } from "./peer.mjs";
4
- import { t as WSError } from "./error.mjs";
5
- import { t as StubRequest } from "./_request.mjs";
1
+ import { import_websocket_server } from "./libs/ws.mjs";
2
+ import { AdapterHookable, adapterUtils, getPeers, toBufferLike } from "./adapter.mjs";
3
+ import { Message, Peer } from "./peer.mjs";
4
+ import { WSError } from "./error.mjs";
5
+ import { StubRequest } from "./_request.mjs";
6
6
  function fromNodeUpgradeHandler(handler) {
7
7
  return { async upgrade(request) {
8
8
  const node = request.runtime?.node;
@@ -15,6 +15,7 @@ const nodeAdapter = (options = {}) => {
15
15
  if ("Deno" in globalThis || "Bun" in globalThis) throw new Error("[crossws] Using Node.js adapter in an incompatible environment.");
16
16
  const hooks = new AdapterHookable(options);
17
17
  const globalPeers = /* @__PURE__ */ new Map();
18
+ const baseUtils = adapterUtils(globalPeers, options);
18
19
  const wss = options.wss || new import_websocket_server.default({
19
20
  noServer: true,
20
21
  handleProtocols: () => false,
@@ -28,7 +29,8 @@ const nodeAdapter = (options = {}) => {
28
29
  request,
29
30
  peers,
30
31
  nodeReq,
31
- namespace: nodeReq._namespace
32
+ namespace: nodeReq._namespace,
33
+ sync: baseUtils.sync
32
34
  });
33
35
  peers.add(peer);
34
36
  hooks.callHook("open", peer);
@@ -41,8 +43,12 @@ const nodeAdapter = (options = {}) => {
41
43
  peers.delete(peer);
42
44
  hooks.callHook("error", peer, new WSError(error));
43
45
  });
46
+ const socket = ws._socket;
47
+ const onDrain = () => hooks.callHook("drain", peer);
48
+ socket?.on("drain", onDrain);
44
49
  ws.on("close", (code, reason) => {
45
50
  peers.delete(peer);
51
+ socket?.off("drain", onDrain);
46
52
  hooks.callHook("close", peer, {
47
53
  code,
48
54
  reason: reason?.toString()
@@ -54,7 +60,7 @@ const nodeAdapter = (options = {}) => {
54
60
  if (upgradeHeaders) for (const [key, value] of new Headers(upgradeHeaders)) outgoingHeaders.push(`${key}: ${value}`);
55
61
  });
56
62
  return {
57
- ...adapterUtils(globalPeers),
63
+ ...baseUtils,
58
64
  handleUpgrade: async (nodeReq, socket, head, webRequest) => {
59
65
  const request = webRequest || new NodeReqProxy(nodeReq);
60
66
  const { upgradeHeaders, endResponse, handled, context, namespace } = await hooks.upgrade(request);
@@ -89,11 +95,11 @@ var NodePeer = class extends Peer {
89
95
  binary: isBinary,
90
96
  ...options
91
97
  });
92
- return 0;
98
+ return this._internal.ws.bufferedAmount;
93
99
  }
94
- publish(topic, data, options) {
100
+ _publish(topic, data, options) {
95
101
  const dataBuff = toBufferLike(data);
96
- const isBinary = typeof data !== "string";
102
+ const isBinary = typeof dataBuff !== "string";
97
103
  const sendOptions = {
98
104
  compress: options?.compress,
99
105
  binary: isBinary,
@@ -126,4 +132,4 @@ async function sendResponse(socket, res) {
126
132
  });
127
133
  });
128
134
  }
129
- export { fromNodeUpgradeHandler as n, nodeAdapter as t };
135
+ export { fromNodeUpgradeHandler, nodeAdapter };
@@ -1,28 +1,4 @@
1
- const kNodeInspect = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
2
- function toBufferLike(val) {
3
- if (val === void 0 || val === null) return "";
4
- const type = typeof val;
5
- if (type === "string") return val;
6
- if (type === "number" || type === "boolean" || type === "bigint") return val.toString();
7
- if (type === "function" || type === "symbol") return "{}";
8
- if (val instanceof Uint8Array || val instanceof ArrayBuffer) return val;
9
- if (isPlainObject(val)) return JSON.stringify(val);
10
- return val;
11
- }
12
- function toString(val) {
13
- if (typeof val === "string") return val;
14
- const data = toBufferLike(val);
15
- if (typeof data === "string") return data;
16
- return `data:application/octet-stream;base64,${btoa(String.fromCharCode(...new Uint8Array(data)))}`;
17
- }
18
- function isPlainObject(value) {
19
- if (value === null || typeof value !== "object") return false;
20
- const prototype = Object.getPrototypeOf(value);
21
- if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) return false;
22
- if (Symbol.iterator in value) return false;
23
- if (Symbol.toStringTag in value) return Object.prototype.toString.call(value) === "[object Module]";
24
- return true;
25
- }
1
+ import { kNodeInspect, serializeMessage } from "./adapter.mjs";
26
2
  var Message = class {
27
3
  event;
28
4
  peer;
@@ -147,6 +123,34 @@ var Peer = class {
147
123
  get topics() {
148
124
  return this._topics;
149
125
  }
126
+ get bufferedAmount() {
127
+ return this._internal.ws?.bufferedAmount ?? 0;
128
+ }
129
+ waitForDrain(opts = {}) {
130
+ const threshold = opts.threshold ?? 0;
131
+ if (this.bufferedAmount <= threshold) return Promise.resolve();
132
+ const signal = opts.signal;
133
+ if (signal?.aborted) return Promise.reject(signal.reason);
134
+ return new Promise((resolve, reject) => {
135
+ const check = () => {
136
+ if (this.bufferedAmount <= threshold || (this.websocket.readyState ?? 1) > 1) {
137
+ cleanup();
138
+ resolve();
139
+ }
140
+ };
141
+ const onAbort = () => {
142
+ cleanup();
143
+ reject(signal.reason);
144
+ };
145
+ const timer = setInterval(check, opts.pollInterval ?? 100);
146
+ timer.unref?.();
147
+ const cleanup = () => {
148
+ clearInterval(timer);
149
+ signal?.removeEventListener("abort", onAbort);
150
+ };
151
+ signal?.addEventListener("abort", onAbort, { once: true });
152
+ });
153
+ }
150
154
  terminate() {
151
155
  this.close();
152
156
  }
@@ -156,6 +160,14 @@ var Peer = class {
156
160
  unsubscribe(topic) {
157
161
  this._topics.delete(topic);
158
162
  }
163
+ publish(topic, data, options) {
164
+ this._publish(topic, data, options);
165
+ this._internal.sync?.publish({
166
+ namespace: this.namespace,
167
+ topic,
168
+ data: serializeMessage(data)
169
+ });
170
+ }
159
171
  toString() {
160
172
  return this.id;
161
173
  }
@@ -183,4 +195,4 @@ function createWsProxy(ws, request) {
183
195
  return value;
184
196
  } });
185
197
  }
186
- export { toString as i, Message as n, toBufferLike as r, Peer as t };
198
+ export { Message, Peer };
@@ -1,4 +1,4 @@
1
- import { n as AdapterInstance, r as AdapterOptions, t as Adapter } from "./adapter.mjs";
1
+ import { Adapter, AdapterInstance, AdapterOptions } from "./adapter.mjs";
2
2
  interface SSEAdapter extends AdapterInstance {
3
3
  fetch(req: Request): Promise<Response>;
4
4
  }
@@ -6,4 +6,4 @@ interface SSEOptions extends AdapterOptions {
6
6
  bidir?: boolean;
7
7
  }
8
8
  declare const sseAdapter: Adapter<SSEAdapter, SSEOptions>;
9
- export { SSEOptions as n, sseAdapter as r, SSEAdapter as t };
9
+ export { SSEAdapter, SSEOptions, sseAdapter };
@@ -293,4 +293,4 @@ interface WebSocket extends EventTarget {
293
293
  removeEventListener<K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
294
294
  removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
295
295
  }
296
- export { WebSocket as a, MessageEvent as i, Event as n, EventTarget as r, CloseEvent as t };
296
+ export { CloseEvent, Event, EventTarget, MessageEvent, WebSocket };
@@ -1,2 +1,2 @@
1
- import { n as BunOptions, r as bunAdapter, t as BunAdapter } from "../_chunks/bun.mjs";
1
+ import { BunAdapter, BunOptions, bunAdapter } from "../_chunks/bun.mjs";
2
2
  export { BunAdapter, BunOptions, bunAdapter as default };
@@ -1,11 +1,12 @@
1
- import { i as AdapterHookable, r as getPeers, t as adapterUtils } from "../_chunks/adapter.mjs";
2
- import { n as Message, r as toBufferLike, t as Peer } from "../_chunks/peer.mjs";
1
+ import { AdapterHookable, adapterUtils, getPeers, toBufferLike } from "../_chunks/adapter.mjs";
2
+ import { Message, Peer } from "../_chunks/peer.mjs";
3
3
  const bunAdapter = (options = {}) => {
4
4
  if (typeof Bun === "undefined") throw new Error("[crossws] Using Bun adapter in an incompatible environment.");
5
5
  const hooks = new AdapterHookable(options);
6
6
  const globalPeers = /* @__PURE__ */ new Map();
7
+ const baseUtils = adapterUtils(globalPeers, options, { nativePubSub: true });
7
8
  return {
8
- ...adapterUtils(globalPeers),
9
+ ...baseUtils,
9
10
  async handleUpgrade(request, server) {
10
11
  const { upgradeHeaders, endResponse, context, namespace } = await hooks.upgrade(request);
11
12
  if (endResponse) return endResponse;
@@ -21,34 +22,39 @@ const bunAdapter = (options = {}) => {
21
22
  },
22
23
  websocket: {
23
24
  message: (ws, message) => {
24
- const peer = getPeer(ws, getPeers(globalPeers, ws.data.namespace));
25
+ const peer = getPeer(ws, getPeers(globalPeers, ws.data.namespace), baseUtils.sync);
25
26
  hooks.callHook("message", peer, new Message(message, peer));
26
27
  },
27
28
  open: (ws) => {
28
29
  const peers = getPeers(globalPeers, ws.data.namespace);
29
- const peer = getPeer(ws, peers);
30
+ const peer = getPeer(ws, peers, baseUtils.sync);
30
31
  peers.add(peer);
31
32
  hooks.callHook("open", peer);
32
33
  },
33
34
  close: (ws, code, reason) => {
34
35
  const peers = getPeers(globalPeers, ws.data.namespace);
35
- const peer = getPeer(ws, peers);
36
+ const peer = getPeer(ws, peers, baseUtils.sync);
36
37
  peers.delete(peer);
37
38
  hooks.callHook("close", peer, {
38
39
  code,
39
40
  reason
40
41
  });
42
+ },
43
+ drain: (ws) => {
44
+ const peer = getPeer(ws, getPeers(globalPeers, ws.data.namespace));
45
+ hooks.callHook("drain", peer);
41
46
  }
42
47
  }
43
48
  };
44
49
  };
45
- function getPeer(ws, peers) {
50
+ function getPeer(ws, peers, sync) {
46
51
  if (ws.data.peer) return ws.data.peer;
47
52
  const peer = new BunPeer({
48
53
  ws,
49
54
  request: ws.data.request,
50
55
  peers,
51
- namespace: ws.data.namespace
56
+ namespace: ws.data.namespace,
57
+ sync
52
58
  });
53
59
  ws.data.peer = peer;
54
60
  return peer;
@@ -60,10 +66,13 @@ var BunPeer = class extends Peer {
60
66
  get context() {
61
67
  return this._internal.ws.data.context;
62
68
  }
69
+ get bufferedAmount() {
70
+ return this._internal.ws.getBufferedAmount();
71
+ }
63
72
  send(data, options) {
64
73
  return this._internal.ws.send(toBufferLike(data), options?.compress);
65
74
  }
66
- publish(topic, data, options) {
75
+ _publish(topic, data, options) {
67
76
  return this._internal.ws.publish(topic, toBufferLike(data), options?.compress);
68
77
  }
69
78
  subscribe(topic) {
@@ -1,2 +1,2 @@
1
- import { n as BunnyOptions, r as bunnyAdapter, t as BunnyAdapter } from "../_chunks/bunny.mjs";
1
+ import { BunnyAdapter, BunnyOptions, bunnyAdapter } from "../_chunks/bunny.mjs";
2
2
  export { BunnyAdapter, BunnyOptions, bunnyAdapter as default };
@@ -1,11 +1,12 @@
1
- import { i as AdapterHookable, r as getPeers, t as adapterUtils } from "../_chunks/adapter.mjs";
2
- import { n as Message, r as toBufferLike, t as Peer } from "../_chunks/peer.mjs";
3
- import { t as WSError } from "../_chunks/error.mjs";
1
+ import { AdapterHookable, adapterUtils, getPeers, toBufferLike } from "../_chunks/adapter.mjs";
2
+ import { Message, Peer } from "../_chunks/peer.mjs";
3
+ import { WSError } from "../_chunks/error.mjs";
4
4
  const bunnyAdapter = (options = {}) => {
5
5
  const hooks = new AdapterHookable(options);
6
6
  const globalPeers = /* @__PURE__ */ new Map();
7
+ const baseUtils = adapterUtils(globalPeers, options);
7
8
  return {
8
- ...adapterUtils(globalPeers),
9
+ ...baseUtils,
9
10
  handleUpgrade: async (request) => {
10
11
  if (!request.upgradeWebSocket || typeof request.upgradeWebSocket !== "function") throw new Error("[crossws] Bunny adapter requires the request to have an upgradeWebSocket method.");
11
12
  const { endResponse, context, namespace, upgradeHeaders } = await hooks.upgrade(request);
@@ -23,7 +24,8 @@ const bunnyAdapter = (options = {}) => {
23
24
  namespace,
24
25
  remoteAddress,
25
26
  peers,
26
- context
27
+ context,
28
+ sync: baseUtils.sync
27
29
  });
28
30
  peers.add(peer);
29
31
  socket.addEventListener("open", () => {
@@ -54,7 +56,7 @@ var BunnyPeer = class extends Peer {
54
56
  send(data) {
55
57
  return this._internal.ws.send(toBufferLike(data));
56
58
  }
57
- publish(topic, data) {
59
+ _publish(topic, data) {
58
60
  const dataBuff = toBufferLike(data);
59
61
  for (const peer of this._internal.peers) if (peer !== this && peer._topics.has(topic)) peer._internal.ws.send(dataBuff);
60
62
  }
@@ -1,2 +1,2 @@
1
- import { n as CloudflareOptions, r as cloudflareAdapter, t as CloudflareDurableAdapter } from "../_chunks/cloudflare.mjs";
1
+ import { CloudflareDurableAdapter, CloudflareOptions, cloudflareAdapter } from "../_chunks/cloudflare.mjs";
2
2
  export { CloudflareDurableAdapter, CloudflareOptions, cloudflareAdapter as default };
@@ -1,7 +1,7 @@
1
- import { i as AdapterHookable, r as getPeers, t as adapterUtils } from "../_chunks/adapter.mjs";
2
- import { n as Message, r as toBufferLike, t as Peer } from "../_chunks/peer.mjs";
3
- import { t as WSError } from "../_chunks/error.mjs";
4
- import { t as StubRequest } from "../_chunks/_request.mjs";
1
+ import { AdapterHookable, adapterUtils, getPeers, toBufferLike } from "../_chunks/adapter.mjs";
2
+ import { Message, Peer } from "../_chunks/peer.mjs";
3
+ import { WSError } from "../_chunks/error.mjs";
4
+ import { StubRequest } from "../_chunks/_request.mjs";
5
5
  import { env } from "cloudflare:workers";
6
6
  const cloudflareAdapter = (opts = {}) => {
7
7
  const hooks = new AdapterHookable(opts);
@@ -14,7 +14,8 @@ const cloudflareAdapter = (opts = {}) => {
14
14
  return binding.get(instanceId);
15
15
  }
16
16
  });
17
- const { publish: durablePublish, ...utils } = adapterUtils(globalPeers);
17
+ const baseUtils = adapterUtils(globalPeers, opts);
18
+ const { publish: durablePublish, ...utils } = baseUtils;
18
19
  return {
19
20
  ...utils,
20
21
  handleUpgrade: async (request, cfEnv, cfCtx) => {
@@ -65,7 +66,7 @@ const cloudflareAdapter = (opts = {}) => {
65
66
  const pair = new WebSocketPair();
66
67
  const client = pair[0];
67
68
  const server = pair[1];
68
- const peer = CloudflareDurablePeer._restore(obj, server, request, namespace);
69
+ const peer = CloudflareDurablePeer._restore(obj, server, request, namespace, baseUtils.sync);
69
70
  peers.add(peer);
70
71
  obj.ctx.acceptWebSocket(server);
71
72
  await hooks.callHook("open", peer);
@@ -76,11 +77,11 @@ const cloudflareAdapter = (opts = {}) => {
76
77
  });
77
78
  },
78
79
  handleDurableMessage: async (obj, ws, message) => {
79
- const peer = CloudflareDurablePeer._restore(obj, ws);
80
+ const peer = CloudflareDurablePeer._restore(obj, ws, void 0, void 0, baseUtils.sync);
80
81
  await hooks.callHook("message", peer, new Message(message, peer));
81
82
  },
82
83
  handleDurableClose: async (obj, ws, code, reason, wasClean) => {
83
- const peer = CloudflareDurablePeer._restore(obj, ws);
84
+ const peer = CloudflareDurablePeer._restore(obj, ws, void 0, void 0, baseUtils.sync);
84
85
  getPeers(globalPeers, peer.namespace).delete(peer);
85
86
  const details = {
86
87
  code,
@@ -106,7 +107,7 @@ const cloudflareAdapter = (opts = {}) => {
106
107
  };
107
108
  var CloudflareDurablePeer = class CloudflareDurablePeer extends Peer {
108
109
  get peers() {
109
- return new Set(this.#getwebsockets().map((ws) => CloudflareDurablePeer._restore(this._internal.durable, ws)));
110
+ return new Set(this.#getwebsockets().map((ws) => CloudflareDurablePeer._restore(this._internal.durable, ws, void 0, void 0, this._internal.sync)));
110
111
  }
111
112
  #getwebsockets() {
112
113
  return this._internal.durable.ctx.getWebSockets();
@@ -121,7 +122,7 @@ var CloudflareDurablePeer = class CloudflareDurablePeer extends Peer {
121
122
  state.t.add(topic);
122
123
  setAttachedState(this._internal.ws, state);
123
124
  }
124
- publish(topic, data) {
125
+ _publish(topic, data) {
125
126
  const websockets = this.#getwebsockets();
126
127
  if (websockets.length < 2) return;
127
128
  const dataBuff = toBufferLike(data);
@@ -133,7 +134,7 @@ var CloudflareDurablePeer = class CloudflareDurablePeer extends Peer {
133
134
  close(code, reason) {
134
135
  this._internal.ws.close(code, reason);
135
136
  }
136
- static _restore(durable, ws, request, namespace) {
137
+ static _restore(durable, ws, request, namespace, sync) {
137
138
  let peer = ws._crosswsPeer;
138
139
  if (peer) return peer;
139
140
  const state = ws.deserializeAttachment() || {};
@@ -141,11 +142,13 @@ var CloudflareDurablePeer = class CloudflareDurablePeer extends Peer {
141
142
  ws,
142
143
  request: request || new StubRequest(state.u || ""),
143
144
  namespace: namespace || state.n || "",
144
- durable
145
+ durable,
146
+ sync
145
147
  });
146
148
  if (state.i) peer._id = state.i;
147
149
  if (request?.url) state.u = request.url;
148
150
  state.i = peer.id;
151
+ state.n = peer.namespace;
149
152
  setAttachedState(ws, state);
150
153
  return peer;
151
154
  }
@@ -155,7 +158,7 @@ var CloudflareFallbackPeer = class extends Peer {
155
158
  this._internal.wsServer.send(toBufferLike(data));
156
159
  return 0;
157
160
  }
158
- publish(_topic, _message) {
161
+ _publish(_topic, _message) {
159
162
  console.warn("[crossws] [cloudflare] pub/sub support requires Durable Objects.");
160
163
  }
161
164
  close(code, reason) {
@@ -1,2 +1,2 @@
1
- import { n as DenoOptions, r as denoAdapter, t as DenoAdapter } from "../_chunks/deno.mjs";
1
+ import { DenoAdapter, DenoOptions, denoAdapter } from "../_chunks/deno.mjs";
2
2
  export { DenoAdapter, DenoOptions, denoAdapter as default };