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 +38 -0
- package/dist/index.mjs +11 -7
- package/dist/websocket/bun.d.mts +2 -0
- package/dist/websocket/bun.mjs +13 -0
- package/dist/websocket/deno.d.mts +2 -0
- package/dist/websocket/deno.mjs +37 -0
- package/dist/websocket/node.d.mts +2 -2
- package/dist/websocket/node.mjs +7 -2
- package/package.json +28 -27
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 ??
|
|
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
|
|
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(
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
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,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,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
|
|
2
|
-
export {
|
|
1
|
+
declare const NodeWebSocket: typeof globalThis.WebSocket;
|
|
2
|
+
export { NodeWebSocket as default };
|
package/dist/websocket/node.mjs
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import { import_websocket } from "../_chunks/libs/ws.mjs";
|
|
2
|
-
const
|
|
3
|
-
|
|
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.
|
|
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
|
-
"
|
|
9
|
-
"dist",
|
|
10
|
-
"adapters",
|
|
11
|
-
"websocket",
|
|
12
|
-
"server",
|
|
13
|
-
"*.d.ts"
|
|
14
|
-
],
|
|
15
|
-
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
16
8
|
"sideEffects": false,
|
|
17
|
-
"
|
|
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/
|
|
47
|
-
"deno": "./dist/websocket/
|
|
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.
|
|
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
|
|
76
|
-
"@types/web": "^0.0.
|
|
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.
|
|
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.
|
|
92
|
-
"oxlint": "^1.
|
|
93
|
-
"srvx": "^0.11.
|
|
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.
|
|
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
|
}
|