crossws 0.4.7 → 0.4.8

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.
package/dist/index.d.mts CHANGED
@@ -4,6 +4,11 @@ interface WebSocketProxyOptions {
4
4
  /**
5
5
  * Target WebSocket URL to proxy to (`ws://` or `wss://`).
6
6
  *
7
+ * A `ws+unix://<socketPath>:<pathname>` target is also supported out of the
8
+ * box for proxying to a Unix-socket upstream on Node, Bun, and Deno (no custom
9
+ * {@link WebSocket} constructor required) — crossws dials it through the
10
+ * matching per-runtime `crossws/websocket` client. See the guide for details.
11
+ *
7
12
  * Can be a static string/URL or a function that resolves the target dynamically
8
13
  * based on the incoming {@link Peer}. The resolver may be **async** (return a
9
14
  * promise) — useful when the upstream address isn't known yet at connect time
@@ -89,6 +94,39 @@ interface WebSocketProxyOptions {
89
94
  * ```
90
95
  */
91
96
  headers?: HeadersInit | ((peer: Peer) => HeadersInit | undefined | void);
97
+ /**
98
+ * Extra options merged into the upstream `WebSocket` constructor's third
99
+ * argument, as a static object or a per-peer resolver.
100
+ *
101
+ * This is the escape hatch for runtime- or client-specific dialing options
102
+ * that the WHATWG `WebSocket` signature doesn't cover — e.g. Deno's unstable
103
+ * `client` (to dial a Unix socket or use a custom `Deno.HttpClient`), or the
104
+ * `ws`/`undici` `createConnection`/`dispatcher`/`agent` options.
105
+ *
106
+ * Merged with {@link headers}: keys returned here are spread first, then the
107
+ * resolved `headers` option is applied on top (so a dedicated `headers`
108
+ * option wins over a `headers` key returned here).
109
+ *
110
+ * > [!NOTE]
111
+ * > Only honored by `WebSocket` constructors that accept a third options
112
+ * > argument. The WHATWG global browser constructor ignores it; Deno and Bun
113
+ * > extend the signature with their own options.
114
+ *
115
+ * @example Proxy to a Unix socket on Deno
116
+ * ```ts
117
+ * createWebSocketProxy({
118
+ * // Deno's WebSocket rejects the `ws+unix:` scheme, so keep a plain
119
+ * // `ws://` target and redirect the transport via the `client` option.
120
+ * target: (peer) => `ws://localhost${new URL(peer.request.url).pathname}`,
121
+ * webSocketOptions: () => ({
122
+ * client: Deno.createHttpClient({
123
+ * proxy: { transport: "unix", path: "/run/worker.sock" },
124
+ * }),
125
+ * }),
126
+ * });
127
+ * ```
128
+ */
129
+ webSocketOptions?: Record<string, unknown> | ((peer: Peer) => Record<string, unknown> | undefined | void);
92
130
  }
93
131
  /**
94
132
  * Create a set of crossws hooks that proxy incoming WebSocket connections
package/dist/index.mjs CHANGED
@@ -1,10 +1,11 @@
1
1
  import { defineHooks, defineWebSocketAdapter } from "./_chunks/adapter.mjs";
2
+ import runtimeWebSocket from "crossws/websocket";
2
3
  const DEFAULT_MAX_BUFFER_SIZE = 1024 * 1024;
3
4
  const DEFAULT_CONNECT_TIMEOUT = 1e4;
4
5
  const TOKEN_RE = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
5
6
  function createWebSocketProxy(target) {
6
7
  const options = typeof target === "string" || target instanceof URL || typeof target === "function" ? { target } : target;
7
- const WebSocketCtor = options.WebSocket ?? globalThis.WebSocket;
8
+ const WebSocketCtor = options.WebSocket ?? runtimeWebSocket;
8
9
  if (typeof WebSocketCtor !== "function") throw new TypeError("createWebSocketProxy requires a `WebSocket` constructor. Pass one via the `WebSocket` option, or use a runtime that provides a global `WebSocket` (Node.js >= 22, Bun, Deno, Cloudflare Workers, browsers).");
9
10
  const upstreams = /* @__PURE__ */ new Map();
10
11
  return {
@@ -90,7 +91,7 @@ function _dialUpstream(upstreams, peer, state, url, options, WebSocketCtor) {
90
91
  let ws;
91
92
  try {
92
93
  const protocols = _resolveProtocols(peer, options.forwardProtocol);
93
- const wsOptions = _resolveWsOptions(options.headers, peer);
94
+ const wsOptions = _resolveWsOptions(options, peer);
94
95
  ws = wsOptions ? new WebSocketCtor(url, protocols, wsOptions) : new WebSocketCtor(url, protocols);
95
96
  ws.binaryType = "arraybuffer";
96
97
  } catch {
@@ -146,11 +147,14 @@ function _resolveTarget(target, peer) {
146
147
  function _isThenable(value) {
147
148
  return value != null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
148
149
  }
149
- function _resolveWsOptions(headers, peer) {
150
- if (!headers) return;
151
- const resolved = typeof headers === "function" ? headers(peer) : headers;
152
- if (!resolved) return;
153
- return { headers: resolved };
150
+ function _resolveWsOptions(options, peer) {
151
+ const { headers, webSocketOptions } = options;
152
+ const extra = typeof webSocketOptions === "function" ? webSocketOptions(peer) : webSocketOptions;
153
+ const resolvedHeaders = typeof headers === "function" ? headers(peer) : headers;
154
+ if (!extra && !resolvedHeaders) return;
155
+ const merged = { ...extra };
156
+ if (resolvedHeaders) merged.headers = resolvedHeaders;
157
+ return merged;
154
158
  }
155
159
  function _resolveProtocols(peer, forwardProtocol) {
156
160
  if (forwardProtocol === false) return;
@@ -0,0 +1,2 @@
1
+ declare const _default: typeof globalThis.WebSocket;
2
+ export { _default as default };
@@ -0,0 +1,13 @@
1
+ const _WebSocket = globalThis.WebSocket;
2
+ var BunWebSocket = class extends _WebSocket {
3
+ constructor(url, protocols, options) {
4
+ if (!options) {
5
+ super(url, protocols);
6
+ return;
7
+ }
8
+ const opts = { ...options };
9
+ if (protocols !== void 0) opts.protocols = protocols;
10
+ super(url, opts);
11
+ }
12
+ };
13
+ export { BunWebSocket as default };
@@ -0,0 +1,2 @@
1
+ declare const _default: typeof globalThis.WebSocket;
2
+ export { _default as default };
@@ -0,0 +1,37 @@
1
+ const _WebSocket = globalThis.WebSocket;
2
+ var DenoWebSocket = class extends _WebSocket {
3
+ constructor(url, protocols, options) {
4
+ const href = typeof url === "string" ? url : url.href;
5
+ const isUnix = href.startsWith("ws+unix:");
6
+ if (!isUnix && !options) {
7
+ super(url, protocols);
8
+ return;
9
+ }
10
+ const opts = { ...options };
11
+ if (protocols !== void 0) opts.protocols = protocols;
12
+ if (isUnix) {
13
+ const { socketPath, path } = _parseUnixTarget(href);
14
+ opts.client ??= Deno.createHttpClient({ proxy: {
15
+ transport: "unix",
16
+ path: socketPath
17
+ } });
18
+ super(`ws://localhost${path}`, opts);
19
+ return;
20
+ }
21
+ super(url, opts);
22
+ }
23
+ };
24
+ function _parseUnixTarget(href) {
25
+ const { pathname, search } = new URL(href);
26
+ const raw = pathname + search;
27
+ const colon = raw.indexOf(":");
28
+ if (colon === -1) return {
29
+ socketPath: raw,
30
+ path: "/"
31
+ };
32
+ return {
33
+ socketPath: raw.slice(0, colon),
34
+ path: raw.slice(colon + 1) || "/"
35
+ };
36
+ }
37
+ export { DenoWebSocket as default };
@@ -1,2 +1,2 @@
1
- declare const Websocket: typeof globalThis.WebSocket;
2
- export { Websocket as default };
1
+ declare const NodeWebSocket: typeof globalThis.WebSocket;
2
+ export { NodeWebSocket as default };
@@ -1,3 +1,8 @@
1
1
  import { import_websocket } from "../_chunks/libs/ws.mjs";
2
- const Websocket = globalThis.WebSocket || import_websocket.default;
3
- export { Websocket as default };
2
+ const _global = globalThis.WebSocket;
3
+ const NodeWebSocket = _global ? new Proxy(_global, { construct(target, args) {
4
+ const url = args[0];
5
+ const Ctor = (typeof url === "string" ? url : url?.href ?? "").startsWith("ws+unix:") || args[2] != null ? import_websocket.default : target;
6
+ return Reflect.construct(Ctor, args);
7
+ } }) : import_websocket.default;
8
+ export { NodeWebSocket as default };
package/package.json CHANGED
@@ -1,22 +1,12 @@
1
1
  {
2
2
  "name": "crossws",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
4
4
  "description": "Cross-platform WebSocket Servers for Node.js, Deno, Bun and Cloudflare Workers",
5
5
  "homepage": "https://crossws.h3.dev",
6
- "license": "MIT",
7
6
  "repository": "h3js/crossws",
8
- "files": [
9
- "dist",
10
- "adapters",
11
- "websocket",
12
- "server",
13
- "*.d.ts"
14
- ],
15
- "type": "module",
7
+ "license": "MIT",
16
8
  "sideEffects": false,
17
- "main": "./dist/index.mjs",
18
- "module": "./dist/index.mjs",
19
- "types": "./dist/index.d.mts",
9
+ "type": "module",
20
10
  "exports": {
21
11
  ".": "./dist/index.mjs",
22
12
  "./sync": "./dist/sync.mjs",
@@ -43,19 +33,30 @@
43
33
  "./websocket": {
44
34
  "browser": "./dist/websocket/native.mjs",
45
35
  "worker": "./dist/websocket/native.mjs",
46
- "bun": "./dist/websocket/native.mjs",
47
- "deno": "./dist/websocket/native.mjs",
36
+ "bun": "./dist/websocket/bun.mjs",
37
+ "deno": "./dist/websocket/deno.mjs",
48
38
  "edge-light": "./dist/websocket/native.mjs",
49
39
  "workerd": "./dist/websocket/native.mjs",
50
40
  "node": "./dist/websocket/node.mjs",
51
41
  "default": "./dist/websocket/native.mjs"
52
42
  }
53
43
  },
44
+ "main": "./dist/index.mjs",
45
+ "module": "./dist/index.mjs",
46
+ "types": "./dist/index.d.mts",
47
+ "files": [
48
+ "dist",
49
+ "adapters",
50
+ "websocket",
51
+ "server",
52
+ "*.d.ts"
53
+ ],
54
54
  "scripts": {
55
+ "prepare": "obuild --stub",
55
56
  "build": "obuild",
56
57
  "dev": "vitest",
57
- "lint": "oxlint . && oxfmt --check src test",
58
58
  "fmt": "oxlint . --fix && oxfmt src test",
59
+ "lint": "oxlint . && oxfmt --check src test",
59
60
  "prepack": "pnpm run build",
60
61
  "play:bun": "bun --watch test/fixture/bun.ts",
61
62
  "play:cf": "wrangler dev --port 3001 -c test/fixture/wrangler.toml",
@@ -68,14 +69,17 @@
68
69
  "test": "pnpm lint && pnpm typecheck && vitest run --coverage",
69
70
  "typecheck": "tsgo --noEmit --skipLibCheck"
70
71
  },
72
+ "resolutions": {
73
+ "crossws": "workspace:*"
74
+ },
71
75
  "devDependencies": {
72
- "@cloudflare/workers-types": "^4.20260629.1",
76
+ "@cloudflare/workers-types": "^4.20260701.1",
73
77
  "@types/bun": "^1.3.14",
74
78
  "@types/deno": "^2.7.0",
75
- "@types/node": "^26.0.1",
76
- "@types/web": "^0.0.351",
79
+ "@types/node": "^26.1.0",
80
+ "@types/web": "^0.0.352",
77
81
  "@types/ws": "^8.18.1",
78
- "@typescript/native-preview": "7.0.0-dev.20260629.1",
82
+ "@typescript/native-preview": "^7.0.0-dev.20260701.1",
79
83
  "@vitest/coverage-v8": "^4.1.9",
80
84
  "automd": "^0.4.3",
81
85
  "changelogen": "^0.6.2",
@@ -88,15 +92,15 @@
88
92
  "jiti": "^2.7.0",
89
93
  "listhen": "^1.10.0",
90
94
  "obuild": "^0.4.37",
91
- "oxfmt": "^0.56.0",
92
- "oxlint": "^1.71.0",
93
- "srvx": "^0.11.17",
95
+ "oxfmt": "^0.57.0",
96
+ "oxlint": "^1.72.0",
97
+ "srvx": "^0.11.18",
94
98
  "typescript": "^6.0.3",
95
99
  "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.57.0",
96
100
  "unbuild": "^3.6.1",
97
101
  "undici": "^8.5.0",
98
102
  "vitest": "^4.1.9",
99
- "wrangler": "^4.105.0",
103
+ "wrangler": "^4.106.0",
100
104
  "ws": "^8.21.0"
101
105
  },
102
106
  "peerDependencies": {
@@ -107,8 +111,5 @@
107
111
  "optional": true
108
112
  }
109
113
  },
110
- "resolutions": {
111
- "crossws": "workspace:*"
112
- },
113
114
  "packageManager": "pnpm@11.7.0"
114
115
  }