crossws 0.4.5 → 0.4.6

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/adapters/vercel.d.ts +2 -0
  2. package/dist/THIRD-PARTY-LICENSES.md +1 -1
  3. package/dist/_chunks/_request.mjs +1 -1
  4. package/dist/_chunks/_types.d.mts +8 -8
  5. package/dist/_chunks/adapter.d.mts +2 -2
  6. package/dist/_chunks/adapter.mjs +1 -1
  7. package/dist/_chunks/bun.d.mts +2 -2
  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 +31 -4
  13. package/dist/_chunks/node.d.mts +2 -2
  14. package/dist/_chunks/node.mjs +6 -6
  15. package/dist/_chunks/peer.mjs +2 -2
  16. package/dist/_chunks/rolldown-runtime.mjs +2 -2
  17. package/dist/_chunks/sse.d.mts +2 -2
  18. package/dist/_chunks/web.d.mts +1 -1
  19. package/dist/adapters/bun.d.mts +1 -1
  20. package/dist/adapters/bun.mjs +2 -2
  21. package/dist/adapters/bunny.d.mts +1 -1
  22. package/dist/adapters/bunny.mjs +3 -3
  23. package/dist/adapters/cloudflare.d.mts +1 -1
  24. package/dist/adapters/cloudflare.mjs +5 -4
  25. package/dist/adapters/deno.d.mts +1 -1
  26. package/dist/adapters/deno.mjs +3 -3
  27. package/dist/adapters/node.d.mts +2 -2
  28. package/dist/adapters/node.mjs +1 -1
  29. package/dist/adapters/sse.d.mts +1 -1
  30. package/dist/adapters/sse.mjs +2 -2
  31. package/dist/adapters/uws.d.mts +2 -2
  32. package/dist/adapters/uws.mjs +4 -3
  33. package/dist/adapters/vercel.d.mts +25 -0
  34. package/dist/adapters/vercel.mjs +48 -0
  35. package/dist/index.d.mts +19 -4
  36. package/dist/index.mjs +19 -3
  37. package/dist/server/bun.d.mts +1 -1
  38. package/dist/server/bunny.d.mts +1 -1
  39. package/dist/server/cloudflare.d.mts +1 -1
  40. package/dist/server/default.d.mts +1 -1
  41. package/dist/server/deno.d.mts +1 -1
  42. package/dist/server/node.d.mts +1 -1
  43. package/dist/server/node.mjs +1 -1
  44. package/dist/websocket/node.mjs +1 -1
  45. package/dist/websocket/sse.d.mts +1 -1
  46. package/package.json +22 -21
@@ -0,0 +1,2 @@
1
+ export * from "../dist/adapters/vercel.mjs";
2
+ export { default } from "../dist/adapters/vercel.mjs";
@@ -8,7 +8,7 @@ MIT
8
8
  ## ws
9
9
 
10
10
  License: MIT
11
- By: Einar Otto Stangvik
11
+ By: Einar Otto Stangvik <einaros@gmail.com> (http://2x.io)
12
12
  Repository: https://github.com/websockets/ws
13
13
 
14
14
  > Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
@@ -77,4 +77,4 @@ const StubRequest = /* @__PURE__ */ (() => {
77
77
  Object.setPrototypeOf(StubRequest.prototype, globalThis.Request.prototype);
78
78
  return StubRequest;
79
79
  })();
80
- export { StubRequest as t };
80
+ export { StubRequest };
@@ -1,10 +1,10 @@
1
- import { a as Hooks } from "./adapter.mjs";
2
- import { n as BunOptions } from "./bun.mjs";
3
- import { n as BunnyOptions } from "./bunny.mjs";
4
- import { n as CloudflareOptions } from "./cloudflare.mjs";
5
- import { n as DenoOptions } from "./deno.mjs";
6
- import { n as NodeOptions } from "./node.mjs";
7
- import { n as SSEOptions } from "./sse.mjs";
1
+ import { Hooks } from "./adapter.mjs";
2
+ import { BunOptions } from "./bun.mjs";
3
+ import { BunnyOptions } from "./bunny.mjs";
4
+ import { CloudflareOptions } from "./cloudflare.mjs";
5
+ import { DenoOptions } from "./deno.mjs";
6
+ import { NodeOptions } from "./node.mjs";
7
+ import { SSEOptions } from "./sse.mjs";
8
8
  import { Server, ServerOptions, ServerPlugin, ServerRequest } from "srvx";
9
9
  type WSOptions = Partial<Hooks> & {
10
10
  resolve?: (req: ServerRequest) => Partial<Hooks> | Promise<Partial<Hooks>>;
@@ -20,4 +20,4 @@ type WSOptions = Partial<Hooks> & {
20
20
  type ServerWithWSOptions = ServerOptions & {
21
21
  websocket?: WSOptions;
22
22
  };
23
- export { WSOptions as n, ServerWithWSOptions as t };
23
+ export { ServerWithWSOptions, WSOptions };
@@ -1,4 +1,4 @@
1
- import { a as WebSocket } from "./web.mjs";
1
+ import { WebSocket } from "./web.mjs";
2
2
  declare class WSError extends Error {
3
3
  constructor(...args: any[]);
4
4
  }
@@ -163,4 +163,4 @@ interface AdapterOptions {
163
163
  }
164
164
  type Adapter<AdapterT extends AdapterInstance = AdapterInstance, Options extends AdapterOptions = AdapterOptions> = (options?: Options) => AdapterT;
165
165
  declare function defineWebSocketAdapter<AdapterT extends AdapterInstance = AdapterInstance, Options extends AdapterOptions = AdapterOptions>(factory: Adapter<AdapterT, Options>): Adapter<AdapterT, Options>;
166
- export { Hooks as a, Message as c, PeerContext as d, WSError as f, defineWebSocketAdapter as i, AdapterInternal as l, AdapterInstance as n, ResolveHooks as o, AdapterOptions as r, defineHooks as s, Adapter as t, Peer as u };
166
+ export { Adapter, AdapterInstance, AdapterInternal, AdapterOptions, Hooks, Message, Peer, PeerContext, ResolveHooks, WSError, defineHooks, defineWebSocketAdapter };
@@ -89,4 +89,4 @@ function getPeers(globalPeers, namespace) {
89
89
  function defineWebSocketAdapter(factory) {
90
90
  return factory;
91
91
  }
92
- export { defineHooks as a, AdapterHookable as i, defineWebSocketAdapter as n, getPeers as r, adapterUtils as t };
92
+ export { AdapterHookable, adapterUtils, defineHooks, defineWebSocketAdapter, getPeers };
@@ -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 } from "./adapter.mjs";
2
2
  import { Server, ServerWebSocket, WebSocketHandler } from "bun";
3
3
  interface BunAdapter extends AdapterInstance {
4
4
  websocket: WebSocketHandler<ContextData>;
@@ -32,4 +32,4 @@ declare class BunPeer extends Peer<{
32
32
  close(code?: number, reason?: string): void;
33
33
  terminate(): void;
34
34
  }
35
- export { BunOptions as n, bunAdapter as r, BunAdapter as t };
35
+ 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,4 @@
1
- import { n as __require, r as __toESM, t as __commonJSMin } from "../rolldown-runtime.mjs";
1
+ import { __commonJSMin, __require, __toESM } from "../rolldown-runtime.mjs";
2
2
  var require_constants = /* @__PURE__ */ __commonJSMin(((exports, module) => {
3
3
  const BINARY_TYPES = [
4
4
  "nodebuffer",
@@ -509,6 +509,8 @@ var require_receiver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
509
509
  this._binaryType = options.binaryType || BINARY_TYPES[0];
510
510
  this._extensions = options.extensions || {};
511
511
  this._isServer = !!options.isServer;
512
+ this._maxBufferedChunks = options.maxBufferedChunks | 0;
513
+ this._maxFragments = options.maxFragments | 0;
512
514
  this._maxPayload = options.maxPayload | 0;
513
515
  this._skipUTF8Validation = !!options.skipUTF8Validation;
514
516
  this[kWebSocket] = void 0;
@@ -530,6 +532,10 @@ var require_receiver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
530
532
  }
531
533
  _write(chunk, encoding, cb) {
532
534
  if (this._opcode === 8 && this._state == GET_INFO) return cb();
535
+ if (this._maxBufferedChunks > 0 && this._buffers.length >= this._maxBufferedChunks) {
536
+ cb(this.createError(RangeError, "Too many buffered chunks", false, 1008, "WS_ERR_TOO_MANY_BUFFERED_PARTS"));
537
+ return;
538
+ }
533
539
  this._bufferedBytes += chunk.length;
534
540
  this._buffers.push(chunk);
535
541
  this.startLoop(cb);
@@ -709,6 +715,10 @@ var require_receiver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
709
715
  return;
710
716
  }
711
717
  if (data.length) {
718
+ if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
719
+ cb(this.createError(RangeError, "Too many message fragments", false, 1008, "WS_ERR_TOO_MANY_BUFFERED_PARTS"));
720
+ return;
721
+ }
712
722
  this._messageLength = this._totalPayloadLength;
713
723
  this._fragments.push(data);
714
724
  }
@@ -723,6 +733,10 @@ var require_receiver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
723
733
  cb(this.createError(RangeError, "Max payload size exceeded", false, 1009, "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"));
724
734
  return;
725
735
  }
736
+ if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
737
+ cb(this.createError(RangeError, "Too many message fragments", false, 1008, "WS_ERR_TOO_MANY_BUFFERED_PARTS"));
738
+ return;
739
+ }
726
740
  this._fragments.push(buf);
727
741
  }
728
742
  this.dataMessage(cb);
@@ -827,6 +841,7 @@ var require_receiver = /* @__PURE__ */ __commonJSMin(((exports, module) => {
827
841
  var require_sender = /* @__PURE__ */ __commonJSMin(((exports, module) => {
828
842
  const { Duplex: Duplex$3 } = __require("stream");
829
843
  const { randomFillSync } = __require("crypto");
844
+ const { types: { isUint8Array } } = __require("util");
830
845
  const PerMessageDeflate = require_permessage_deflate();
831
846
  const { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants();
832
847
  const { isBlob, isValidStatusCode } = require_validation();
@@ -931,7 +946,8 @@ var require_sender = /* @__PURE__ */ __commonJSMin(((exports, module) => {
931
946
  buf = Buffer.allocUnsafe(2 + length);
932
947
  buf.writeUInt16BE(code, 0);
933
948
  if (typeof data === "string") buf.write(data, 2);
934
- else buf.set(data, 2);
949
+ else if (isUint8Array(data)) buf.set(data, 2);
950
+ else throw new TypeError("Second argument must be a string or a Uint8Array");
935
951
  }
936
952
  const options = {
937
953
  [kByteLength]: buf.length,
@@ -1119,7 +1135,8 @@ var require_sender = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1119
1135
  this._state = DEFLATING;
1120
1136
  perMessageDeflate.compress(data, options.fin, (_, buf) => {
1121
1137
  if (this._socket.destroyed) {
1122
- callCallbacks(this, /* @__PURE__ */ new Error("The socket was closed while data was being compressed"), cb);
1138
+ const err = /* @__PURE__ */ new Error("The socket was closed while data was being compressed");
1139
+ callCallbacks(this, err, cb);
1123
1140
  return;
1124
1141
  }
1125
1142
  this._bufferedBytes -= options[kByteLength];
@@ -1507,6 +1524,8 @@ var require_websocket = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1507
1524
  binaryType: this.binaryType,
1508
1525
  extensions: this._extensions,
1509
1526
  isServer: this._isServer,
1527
+ maxBufferedChunks: options.maxBufferedChunks,
1528
+ maxFragments: options.maxFragments,
1510
1529
  maxPayload: options.maxPayload,
1511
1530
  skipUTF8Validation: options.skipUTF8Validation
1512
1531
  });
@@ -1714,6 +1733,8 @@ var require_websocket = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1714
1733
  autoPong: true,
1715
1734
  closeTimeout: CLOSE_TIMEOUT,
1716
1735
  protocolVersion: protocolVersions[1],
1736
+ maxBufferedChunks: 1024 * 1024,
1737
+ maxFragments: 128 * 1024,
1717
1738
  maxPayload: 100 * 1024 * 1024,
1718
1739
  skipUTF8Validation: false,
1719
1740
  perMessageDeflate: true,
@@ -1903,6 +1924,8 @@ var require_websocket = /* @__PURE__ */ __commonJSMin(((exports, module) => {
1903
1924
  websocket.setSocket(socket, head, {
1904
1925
  allowSynchronousEvents: opts.allowSynchronousEvents,
1905
1926
  generateMask: opts.generateMask,
1927
+ maxBufferedChunks: opts.maxBufferedChunks,
1928
+ maxFragments: opts.maxFragments,
1906
1929
  maxPayload: opts.maxPayload,
1907
1930
  skipUTF8Validation: opts.skipUTF8Validation
1908
1931
  });
@@ -2188,6 +2211,8 @@ var require_websocket_server = /* @__PURE__ */ __commonJSMin(((exports, module)
2188
2211
  options = {
2189
2212
  allowSynchronousEvents: true,
2190
2213
  autoPong: true,
2214
+ maxBufferedChunks: 1024 * 1024,
2215
+ maxFragments: 128 * 1024,
2191
2216
  maxPayload: 100 * 1024 * 1024,
2192
2217
  skipUTF8Validation: false,
2193
2218
  perMessageDeflate: false,
@@ -2372,6 +2397,8 @@ var require_websocket_server = /* @__PURE__ */ __commonJSMin(((exports, module)
2372
2397
  socket.removeListener("error", socketOnError);
2373
2398
  ws.setSocket(socket, head, {
2374
2399
  allowSynchronousEvents: this.options.allowSynchronousEvents,
2400
+ maxBufferedChunks: this.options.maxBufferedChunks,
2401
+ maxFragments: this.options.maxFragments,
2375
2402
  maxPayload: this.options.maxPayload,
2376
2403
  skipUTF8Validation: this.options.skipUTF8Validation
2377
2404
  });
@@ -2426,4 +2453,4 @@ require_sender();
2426
2453
  require_subprotocol();
2427
2454
  var import_websocket = /* @__PURE__ */ __toESM(require_websocket(), 1);
2428
2455
  var import_websocket_server = /* @__PURE__ */ __toESM(require_websocket_server(), 1);
2429
- export { import_websocket_server as n, import_websocket as t };
2456
+ 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 { AdapterHookable, adapterUtils, getPeers } from "./adapter.mjs";
2
+ import { import_websocket_server } from "./libs/ws.mjs";
3
+ import { Message, Peer, toBufferLike } 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;
@@ -126,4 +126,4 @@ async function sendResponse(socket, res) {
126
126
  });
127
127
  });
128
128
  }
129
- export { fromNodeUpgradeHandler as n, nodeAdapter as t };
129
+ export { fromNodeUpgradeHandler, nodeAdapter };
@@ -1,4 +1,4 @@
1
- const kNodeInspect = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
1
+ const kNodeInspect = /*#__PURE__*/ Symbol.for("nodejs.util.inspect.custom");
2
2
  function toBufferLike(val) {
3
3
  if (val === void 0 || val === null) return "";
4
4
  const type = typeof val;
@@ -183,4 +183,4 @@ function createWsProxy(ws, request) {
183
183
  return value;
184
184
  } });
185
185
  }
186
- export { toString as i, Message as n, toBufferLike as r, Peer as t };
186
+ export { Message, Peer, toBufferLike, toString };
@@ -5,7 +5,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
8
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
9
9
  var __copyProps = (to, from, except, desc) => {
10
10
  if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
11
  key = keys[i];
@@ -21,4 +21,4 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
24
- export { __require as n, __toESM as r, __commonJSMin as t };
24
+ export { __commonJSMin, __require, __toESM };
@@ -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,5 +1,5 @@
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 } from "../_chunks/adapter.mjs";
2
+ import { Message, Peer, toBufferLike } 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);
@@ -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,6 +1,6 @@
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 } from "../_chunks/adapter.mjs";
2
+ import { Message, Peer, toBufferLike } 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();
@@ -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 } from "../_chunks/adapter.mjs";
2
+ import { Message, Peer, toBufferLike } 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);
@@ -146,6 +146,7 @@ var CloudflareDurablePeer = class CloudflareDurablePeer extends Peer {
146
146
  if (state.i) peer._id = state.i;
147
147
  if (request?.url) state.u = request.url;
148
148
  state.i = peer.id;
149
+ state.n = peer.namespace;
149
150
  setAttachedState(ws, state);
150
151
  return peer;
151
152
  }
@@ -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 };
@@ -1,6 +1,6 @@
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 } from "../_chunks/adapter.mjs";
2
+ import { Message, Peer, toBufferLike } from "../_chunks/peer.mjs";
3
+ import { WSError } from "../_chunks/error.mjs";
4
4
  const denoAdapter = (options = {}) => {
5
5
  if (typeof Deno === "undefined") throw new Error("[crossws] Using Deno adapter in an incompatible environment.");
6
6
  const hooks = new AdapterHookable(options);
@@ -1,2 +1,2 @@
1
- import { a as fromNodeUpgradeHandler, i as NodeUpgradeHandler, n as NodeOptions, r as nodeAdapter, t as NodeAdapter } from "../_chunks/node.mjs";
2
- export { NodeAdapter, NodeOptions, NodeUpgradeHandler, nodeAdapter as default, fromNodeUpgradeHandler };
1
+ import { NodeAdapter, NodeOptions, NodeUpgradeHandler, fromNodeUpgradeHandler, nodeAdapter } from "../_chunks/node.mjs";
2
+ export { NodeAdapter, NodeOptions, type NodeUpgradeHandler, nodeAdapter as default, fromNodeUpgradeHandler };
@@ -1,2 +1,2 @@
1
- import { n as fromNodeUpgradeHandler, t as nodeAdapter } from "../_chunks/node.mjs";
1
+ import { fromNodeUpgradeHandler, nodeAdapter } from "../_chunks/node.mjs";
2
2
  export { nodeAdapter as default, fromNodeUpgradeHandler };
@@ -1,2 +1,2 @@
1
- import { n as SSEOptions, r as sseAdapter, t as SSEAdapter } from "../_chunks/sse.mjs";
1
+ import { SSEAdapter, SSEOptions, sseAdapter } from "../_chunks/sse.mjs";
2
2
  export { SSEAdapter, SSEOptions, sseAdapter as default };
@@ -1,5 +1,5 @@
1
- import { i as AdapterHookable, r as getPeers, t as adapterUtils } from "../_chunks/adapter.mjs";
2
- import { i as toString, n as Message, t as Peer } from "../_chunks/peer.mjs";
1
+ import { AdapterHookable, adapterUtils, getPeers } from "../_chunks/adapter.mjs";
2
+ import { Message, Peer, toString } from "../_chunks/peer.mjs";
3
3
  const sseAdapter = (opts = {}) => {
4
4
  const hooks = new AdapterHookable(opts);
5
5
  const globalPeers = /* @__PURE__ */ new Map();
@@ -1,5 +1,5 @@
1
- import { d as PeerContext, n as AdapterInstance, r as AdapterOptions, t as Adapter, u as Peer } from "../_chunks/adapter.mjs";
2
- import { a as WebSocket } from "../_chunks/web.mjs";
1
+ import { Adapter, AdapterInstance, AdapterOptions, Peer, PeerContext } from "../_chunks/adapter.mjs";
2
+ import { WebSocket } from "../_chunks/web.mjs";
3
3
  import uws from "uWebSockets.js";
4
4
  declare const StubRequest: {
5
5
  new (url: string, init?: RequestInit): Request;
@@ -1,6 +1,6 @@
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 StubRequest } from "../_chunks/_request.mjs";
1
+ import { AdapterHookable, adapterUtils, getPeers } from "../_chunks/adapter.mjs";
2
+ import { Message, Peer, toBufferLike } from "../_chunks/peer.mjs";
3
+ import { StubRequest } from "../_chunks/_request.mjs";
4
4
  const uwsAdapter = (options = {}) => {
5
5
  const hooks = new AdapterHookable(options);
6
6
  const globalPeers = /* @__PURE__ */ new Map();
@@ -136,6 +136,7 @@ var UWSReqProxy = class extends StubRequest {
136
136
  }
137
137
  };
138
138
  var UwsWebSocketProxy = class {
139
+ _uws;
139
140
  readyState = 1;
140
141
  constructor(_uws) {
141
142
  this._uws = _uws;
@@ -0,0 +1,25 @@
1
+ import { Adapter } from "../_chunks/adapter.mjs";
2
+ import { NodeAdapter, NodeOptions } from "../_chunks/node.mjs";
3
+ import { IncomingMessage, ServerResponse } from "node:http";
4
+ interface VercelAdapter extends Omit<NodeAdapter, "handleUpgrade"> {
5
+ /**
6
+ * Handle a WebSocket upgrade from a Web `Request` (fetch-style handlers).
7
+ *
8
+ * Returns a `204` {@link Response} when the upgrade was handled, or
9
+ * `undefined` when the request is not a WebSocket upgrade or Vercel's upgrade
10
+ * context is unavailable.
11
+ */
12
+ handleWebUpgrade(request: Request): Promise<Response | undefined>;
13
+ /**
14
+ * Handle a WebSocket upgrade from a Node.js `IncomingMessage` (Node-style
15
+ * handlers).
16
+ *
17
+ * Returns `true` when the upgrade was handled (and ends `res` with `204`), or
18
+ * `false` when the request is not a WebSocket upgrade or Vercel's upgrade
19
+ * context is unavailable.
20
+ */
21
+ handleNodeUpgrade(req: IncomingMessage, res: ServerResponse): Promise<boolean>;
22
+ }
23
+ interface VercelOptions extends NodeOptions {}
24
+ declare const vercelAdapter: Adapter<VercelAdapter, VercelOptions>;
25
+ export { VercelAdapter, VercelOptions, vercelAdapter as default };
@@ -0,0 +1,48 @@
1
+ import { nodeAdapter } from "../_chunks/node.mjs";
2
+ import { NodeRequest } from "srvx/node";
3
+ const VERCEL_REQUEST_CONTEXT_SYMBOL = Symbol.for("@vercel/request-context");
4
+ const vercelAdapter = (options = {}) => {
5
+ const wss = nodeAdapter(options);
6
+ async function handleWebUpgrade(request) {
7
+ if (!_isWsUpgrade(request.method, request.headers.get("upgrade") || void 0)) return;
8
+ const upgrade = _getVercelUpgrade();
9
+ if (!upgrade) return;
10
+ await wss.handleUpgrade(upgrade.req, upgrade.socket, upgrade.head, request);
11
+ return new Response(null, { status: 204 });
12
+ }
13
+ async function handleNodeUpgrade(req, res) {
14
+ if (!_isWsUpgrade(req.method, req.headers.upgrade)) return false;
15
+ const upgrade = _getVercelUpgrade();
16
+ if (!upgrade) return false;
17
+ await wss.handleUpgrade(upgrade.req, upgrade.socket, upgrade.head, new NodeRequest({
18
+ req,
19
+ res
20
+ }));
21
+ if (!res.headersSent && !res.writableEnded) {
22
+ res.statusCode = 204;
23
+ res.end();
24
+ }
25
+ return true;
26
+ }
27
+ const { handleUpgrade: _, ...rest } = wss;
28
+ return {
29
+ ...rest,
30
+ handleWebUpgrade,
31
+ handleNodeUpgrade
32
+ };
33
+ };
34
+ function _isWsUpgrade(method, upgradeHeader) {
35
+ return method === "GET" && upgradeHeader?.toLowerCase?.() === "websocket";
36
+ }
37
+ function _getVercelUpgrade() {
38
+ const upgrade = _getVercelRequestContext()?.upgradeWebSocket?.();
39
+ return upgrade?.req && upgrade?.socket && upgrade?.head ? upgrade : void 0;
40
+ }
41
+ function _getVercelRequestContext() {
42
+ const store = globalThis[VERCEL_REQUEST_CONTEXT_SYMBOL];
43
+ if (typeof store?.get !== "function") return;
44
+ const context = store.get();
45
+ if (!context || typeof context !== "object") return;
46
+ return context;
47
+ }
48
+ export { vercelAdapter as default };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as Hooks, c as Message, d as PeerContext, f as WSError, i as defineWebSocketAdapter, l as AdapterInternal, n as AdapterInstance, o as ResolveHooks, r as AdapterOptions, s as defineHooks, t as Adapter, u as Peer } from "./_chunks/adapter.mjs";
2
- import { n as WSOptions, t as ServerWithWSOptions } from "./_chunks/_types.mjs";
1
+ import { Adapter, AdapterInstance, AdapterInternal, AdapterOptions, Hooks, Message, Peer, PeerContext, ResolveHooks, WSError, defineHooks, defineWebSocketAdapter } from "./_chunks/adapter.mjs";
2
+ import { ServerWithWSOptions, WSOptions } from "./_chunks/_types.mjs";
3
3
  interface WebSocketProxyOptions {
4
4
  /**
5
5
  * Target WebSocket URL to proxy to (`ws://` or `wss://`).
@@ -9,11 +9,26 @@ interface WebSocketProxyOptions {
9
9
  */
10
10
  target: string | URL | ((peer: Peer) => string | URL);
11
11
  /**
12
- * Forward the client's `sec-websocket-protocol` header to the upstream.
12
+ * Subprotocol(s) to offer the upstream during the handshake.
13
+ *
14
+ * - `true` (default) — forward the client's `sec-websocket-protocol` verbatim.
15
+ * - `false` — offer no subprotocol upstream.
16
+ * - `string` / `string[]` — offer a fixed subprotocol (or list) upstream,
17
+ * regardless of what the client requested.
18
+ * - `Record<string, string>` — rewrite map applied to the client's offered
19
+ * tokens: a token that matches a key is swapped for its value; tokens not
20
+ * in the map are forwarded verbatim.
21
+ * - function — resolve the upstream subprotocol(s) per {@link Peer}. Return a
22
+ * string, an array of strings, or `undefined` to offer none. Useful when the
23
+ * rewrite depends on more than the token value alone.
24
+ *
25
+ * Note: this controls only what is offered to the *upstream*. The subprotocol
26
+ * echoed back to the *client* remains the first token the client offered (per
27
+ * RFC 6455, the selected protocol must be one the client proposed).
13
28
  *
14
29
  * @default true
15
30
  */
16
- forwardProtocol?: boolean;
31
+ forwardProtocol?: boolean | string | string[] | Record<string, string> | ((peer: Peer) => string | string[] | undefined | void);
17
32
  /**
18
33
  * Maximum number of bytes buffered per peer while the upstream connection
19
34
  * is still opening. If exceeded, the peer is closed with code `1009`
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as defineHooks, n as defineWebSocketAdapter } from "./_chunks/adapter.mjs";
1
+ import { defineHooks, defineWebSocketAdapter } from "./_chunks/adapter.mjs";
2
2
  const DEFAULT_MAX_BUFFER_SIZE = 1024 * 1024;
3
3
  const DEFAULT_CONNECT_TIMEOUT = 1e4;
4
4
  const TOKEN_RE = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
@@ -11,8 +11,8 @@ function createWebSocketProxy(target) {
11
11
  upgrade(request) {
12
12
  const reqProtocol = request.headers.get("sec-websocket-protocol");
13
13
  if (options.forwardProtocol === false || !reqProtocol) return;
14
- const accepted = reqProtocol.split(",")[0].trim();
15
- if (!TOKEN_RE.test(accepted)) return;
14
+ const accepted = _splitProtocolHeader(reqProtocol)[0];
15
+ if (!accepted || !TOKEN_RE.test(accepted)) return;
16
16
  return { headers: { "sec-websocket-protocol": accepted } };
17
17
  },
18
18
  open(peer) {
@@ -127,10 +127,26 @@ function _resolveWsOptions(headers, peer) {
127
127
  }
128
128
  function _resolveProtocols(peer, forwardProtocol) {
129
129
  if (forwardProtocol === false) return;
130
+ if (typeof forwardProtocol === "function") return _normalizeProtocols(forwardProtocol(peer));
131
+ if (typeof forwardProtocol === "string" || Array.isArray(forwardProtocol)) return _normalizeProtocols(forwardProtocol);
130
132
  const header = peer.request?.headers.get("sec-websocket-protocol");
131
133
  if (!header) return;
134
+ const offered = _splitProtocolHeader(header);
135
+ if (forwardProtocol && typeof forwardProtocol === "object") {
136
+ const map = forwardProtocol;
137
+ return _normalizeProtocols(offered.map((p) => Object.prototype.hasOwnProperty.call(map, p) ? map[p] : p));
138
+ }
139
+ return _normalizeProtocols(offered);
140
+ }
141
+ function _splitProtocolHeader(header) {
132
142
  return header.split(",").map((p) => p.trim()).filter(Boolean);
133
143
  }
144
+ function _normalizeProtocols(value) {
145
+ if (value == null) return;
146
+ const list = (Array.isArray(value) ? value : [value]).filter((p) => p != null).map((p) => String(p).trim()).filter(Boolean);
147
+ const deduped = [...new Set(list)];
148
+ return deduped.length > 0 ? deduped : void 0;
149
+ }
134
150
  function _safeClose(peer, code, reason) {
135
151
  try {
136
152
  peer.close(code, _truncateReason(reason));
@@ -1,4 +1,4 @@
1
- import { n as WSOptions, t as ServerWithWSOptions } from "../_chunks/_types.mjs";
1
+ import { ServerWithWSOptions, WSOptions } from "../_chunks/_types.mjs";
2
2
  import { Server, ServerPlugin } from "srvx";
3
3
  declare function plugin(wsOpts: WSOptions): ServerPlugin;
4
4
  declare function serve(options: ServerWithWSOptions): Server;
@@ -1,4 +1,4 @@
1
- import { n as WSOptions, t as ServerWithWSOptions } from "../_chunks/_types.mjs";
1
+ import { ServerWithWSOptions, WSOptions } from "../_chunks/_types.mjs";
2
2
  import { Server, ServerPlugin } from "srvx";
3
3
  declare function plugin(wsOpts: WSOptions): ServerPlugin;
4
4
  declare function serve(options: ServerWithWSOptions): Server;
@@ -1,4 +1,4 @@
1
- import { n as WSOptions, t as ServerWithWSOptions } from "../_chunks/_types.mjs";
1
+ import { ServerWithWSOptions, WSOptions } from "../_chunks/_types.mjs";
2
2
  import { Server, ServerPlugin } from "srvx";
3
3
  declare function plugin(wsOpts: WSOptions): ServerPlugin;
4
4
  declare function serve(options: ServerWithWSOptions): Server;
@@ -1,4 +1,4 @@
1
- import { n as WSOptions, t as ServerWithWSOptions } from "../_chunks/_types.mjs";
1
+ import { ServerWithWSOptions, WSOptions } from "../_chunks/_types.mjs";
2
2
  import { Server, ServerPlugin } from "srvx";
3
3
  declare function plugin(wsOpts: WSOptions): ServerPlugin;
4
4
  declare function serve(options: ServerWithWSOptions): Server;
@@ -1,4 +1,4 @@
1
- import { n as WSOptions, t as ServerWithWSOptions } from "../_chunks/_types.mjs";
1
+ import { ServerWithWSOptions, WSOptions } from "../_chunks/_types.mjs";
2
2
  import { Server, ServerPlugin } from "srvx";
3
3
  declare function plugin(wsOpts: WSOptions): ServerPlugin;
4
4
  declare function serve(options: ServerWithWSOptions): Server;
@@ -1,4 +1,4 @@
1
- import { n as WSOptions, t as ServerWithWSOptions } from "../_chunks/_types.mjs";
1
+ import { ServerWithWSOptions, WSOptions } from "../_chunks/_types.mjs";
2
2
  import { Server, ServerPlugin } from "srvx";
3
3
  declare function plugin(wsOpts: WSOptions): ServerPlugin;
4
4
  declare function serve(options: ServerWithWSOptions): Server;
@@ -1,4 +1,4 @@
1
- import { t as nodeAdapter } from "../_chunks/node.mjs";
1
+ import { nodeAdapter } from "../_chunks/node.mjs";
2
2
  import { NodeRequest, serve as serve$1 } from "srvx/node";
3
3
  function plugin(wsOpts) {
4
4
  return (server) => {
@@ -1,3 +1,3 @@
1
- import { t as import_websocket } from "../_chunks/libs/ws.mjs";
1
+ import { import_websocket } from "../_chunks/libs/ws.mjs";
2
2
  const Websocket = globalThis.WebSocket || import_websocket.default;
3
3
  export { Websocket as default };
@@ -1,4 +1,4 @@
1
- import { a as WebSocket, i as MessageEvent, n as Event, r as EventTarget, t as CloseEvent } from "../_chunks/web.mjs";
1
+ import { CloseEvent, Event, EventTarget, MessageEvent, WebSocket } from "../_chunks/web.mjs";
2
2
  type Ctor<T> = {
3
3
  prototype: T;
4
4
  new (): T;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crossws",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "description": "Cross-platform WebSocket Servers for Node.js, Deno, Bun and Cloudflare Workers",
5
5
  "homepage": "https://crossws.h3.dev",
6
6
  "license": "MIT",
@@ -25,6 +25,7 @@
25
25
  "./adapters/cloudflare": "./dist/adapters/cloudflare.mjs",
26
26
  "./adapters/sse": "./dist/adapters/sse.mjs",
27
27
  "./adapters/node": "./dist/adapters/node.mjs",
28
+ "./adapters/vercel": "./dist/adapters/vercel.mjs",
28
29
  "./adapters/uws": "./dist/adapters/uws.mjs",
29
30
  "./server/bun": "./dist/server/bun.mjs",
30
31
  "./server/bunny": "./dist/server/bunny.mjs",
@@ -67,14 +68,14 @@
67
68
  "typecheck": "tsgo --noEmit --skipLibCheck"
68
69
  },
69
70
  "devDependencies": {
70
- "@cloudflare/workers-types": "^4.20260410.1",
71
- "@types/bun": "^1.3.12",
72
- "@types/deno": "^2.5.0",
73
- "@types/node": "^25.6.0",
74
- "@types/web": "^0.0.345",
71
+ "@cloudflare/workers-types": "^4.20260608.1",
72
+ "@types/bun": "^1.3.14",
73
+ "@types/deno": "^2.7.0",
74
+ "@types/node": "^25.9.2",
75
+ "@types/web": "^0.0.350",
75
76
  "@types/ws": "^8.18.1",
76
- "@typescript/native-preview": "^7.0.0-dev.20260410.1",
77
- "@vitest/coverage-v8": "^4.1.4",
77
+ "@typescript/native-preview": "7.0.0-dev.20260608.1",
78
+ "@vitest/coverage-v8": "^4.1.8",
78
79
  "automd": "^0.4.3",
79
80
  "changelogen": "^0.6.2",
80
81
  "consola": "^3.4.2",
@@ -82,20 +83,20 @@
82
83
  "eventsource": "^4.1.0",
83
84
  "execa": "^9.6.1",
84
85
  "get-port-please": "^3.2.0",
85
- "h3": "^2.0.1-rc.20",
86
- "jiti": "^2.6.1",
87
- "listhen": "^1.9.1",
88
- "obuild": "^0.4.33",
89
- "oxfmt": "^0.44.0",
90
- "oxlint": "^1.59.0",
91
- "srvx": "^0.11.15",
92
- "typescript": "^6.0.2",
86
+ "h3": "2.0.1-rc.22",
87
+ "jiti": "^2.7.0",
88
+ "listhen": "^1.10.0",
89
+ "obuild": "^0.4.36",
90
+ "oxfmt": "^0.53.0",
91
+ "oxlint": "^1.68.0",
92
+ "srvx": "^0.11.16",
93
+ "typescript": "^6.0.3",
93
94
  "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.57.0",
94
95
  "unbuild": "^3.6.1",
95
- "undici": "^8.0.2",
96
- "vitest": "^4.1.4",
97
- "wrangler": "^4.81.1",
98
- "ws": "^8.20.0"
96
+ "undici": "^8.4.1",
97
+ "vitest": "^4.1.8",
98
+ "wrangler": "^4.98.0",
99
+ "ws": "^8.21.0"
99
100
  },
100
101
  "peerDependencies": {
101
102
  "srvx": ">=0.11.5"
@@ -108,7 +109,7 @@
108
109
  "resolutions": {
109
110
  "crossws": "workspace:*"
110
111
  },
111
- "packageManager": "pnpm@10.33.0",
112
+ "packageManager": "pnpm@11.5.2",
112
113
  "pnpm": {
113
114
  "ignoredBuiltDependencies": [
114
115
  "@parcel/watcher",