crossws 0.4.6 → 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/_chunks/_request.mjs +1 -1
- package/dist/_chunks/adapter.d.mts +457 -3
- package/dist/_chunks/adapter.mjs +90 -13
- package/dist/_chunks/bun.d.mts +4 -2
- package/dist/_chunks/libs/ws.mjs +23 -1
- package/dist/_chunks/node.mjs +13 -7
- package/dist/_chunks/peer.mjs +38 -26
- package/dist/adapters/bun.mjs +18 -9
- package/dist/adapters/bunny.mjs +7 -5
- package/dist/adapters/cloudflare.mjs +13 -11
- package/dist/adapters/deno.mjs +21 -8
- package/dist/adapters/sse.mjs +7 -5
- package/dist/adapters/uws.d.mts +4 -3
- package/dist/adapters/uws.mjs +16 -10
- package/dist/index.d.mts +50 -4
- package/dist/index.mjs +69 -38
- package/dist/sync.d.mts +2 -0
- package/dist/sync.mjs +200 -0
- 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 +34 -46
- package/sync.d.ts +2 -0
- package/dist/_chunks/rolldown-runtime.mjs +0 -24
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 {
|
|
@@ -16,19 +17,8 @@ function createWebSocketProxy(target) {
|
|
|
16
17
|
return { headers: { "sec-websocket-protocol": accepted } };
|
|
17
18
|
},
|
|
18
19
|
open(peer) {
|
|
19
|
-
let ws;
|
|
20
|
-
try {
|
|
21
|
-
const url = _resolveTarget(options.target, peer);
|
|
22
|
-
const protocols = _resolveProtocols(peer, options.forwardProtocol);
|
|
23
|
-
const wsOptions = _resolveWsOptions(options.headers, peer);
|
|
24
|
-
ws = wsOptions ? new WebSocketCtor(url, protocols, wsOptions) : new WebSocketCtor(url, protocols);
|
|
25
|
-
ws.binaryType = "arraybuffer";
|
|
26
|
-
} catch {
|
|
27
|
-
_safeClose(peer, 1011, "Upstream setup failed");
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
20
|
const state = {
|
|
31
|
-
ws,
|
|
21
|
+
ws: void 0,
|
|
32
22
|
buffer: [],
|
|
33
23
|
bufferSize: 0,
|
|
34
24
|
open: false,
|
|
@@ -41,26 +31,20 @@ function createWebSocketProxy(target) {
|
|
|
41
31
|
_cleanupState(upstreams, peer.id, state);
|
|
42
32
|
_safeClose(peer, 1011, "Upstream connect timeout");
|
|
43
33
|
}, timeoutMs);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
state.buffer.length = 0;
|
|
49
|
-
state.bufferSize = 0;
|
|
50
|
-
});
|
|
51
|
-
ws.addEventListener("message", (event) => {
|
|
52
|
-
_safeSend(peer, event.data);
|
|
53
|
-
});
|
|
54
|
-
ws.addEventListener("close", (event) => {
|
|
55
|
-
if (upstreams.get(peer.id) !== state) return;
|
|
34
|
+
let resolved;
|
|
35
|
+
try {
|
|
36
|
+
resolved = _resolveTarget(options.target, peer);
|
|
37
|
+
} catch {
|
|
56
38
|
_cleanupState(upstreams, peer.id, state);
|
|
57
|
-
_safeClose(peer,
|
|
58
|
-
|
|
59
|
-
|
|
39
|
+
_safeClose(peer, 1011, "Upstream setup failed");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (resolved instanceof Promise) resolved.then((url) => _dialUpstream(upstreams, peer, state, url, options, WebSocketCtor), () => {
|
|
60
43
|
if (upstreams.get(peer.id) !== state) return;
|
|
61
44
|
_cleanupState(upstreams, peer.id, state);
|
|
62
|
-
_safeClose(peer, 1011, "Upstream
|
|
45
|
+
_safeClose(peer, 1011, "Upstream setup failed");
|
|
63
46
|
});
|
|
47
|
+
else _dialUpstream(upstreams, peer, state, resolved, options, WebSocketCtor);
|
|
64
48
|
},
|
|
65
49
|
message(peer, message) {
|
|
66
50
|
const state = upstreams.get(peer.id);
|
|
@@ -68,7 +52,7 @@ function createWebSocketProxy(target) {
|
|
|
68
52
|
const raw = typeof message.rawData === "string" ? message.rawData : message.uint8Array();
|
|
69
53
|
if (state.open) {
|
|
70
54
|
try {
|
|
71
|
-
state.ws
|
|
55
|
+
state.ws?.send(raw);
|
|
72
56
|
} catch {}
|
|
73
57
|
return;
|
|
74
58
|
}
|
|
@@ -88,7 +72,7 @@ function createWebSocketProxy(target) {
|
|
|
88
72
|
_clearTimeout(state);
|
|
89
73
|
upstreams.delete(peer.id);
|
|
90
74
|
try {
|
|
91
|
-
state.ws
|
|
75
|
+
state.ws?.close(_normalizeOutgoingCode(details.code), _truncateReason(details.reason));
|
|
92
76
|
} catch {}
|
|
93
77
|
},
|
|
94
78
|
error(peer) {
|
|
@@ -97,16 +81,56 @@ function createWebSocketProxy(target) {
|
|
|
97
81
|
_clearTimeout(state);
|
|
98
82
|
upstreams.delete(peer.id);
|
|
99
83
|
try {
|
|
100
|
-
state.ws
|
|
84
|
+
state.ws?.close(1011, "Peer error");
|
|
101
85
|
} catch {}
|
|
102
86
|
}
|
|
103
87
|
};
|
|
104
88
|
}
|
|
89
|
+
function _dialUpstream(upstreams, peer, state, url, options, WebSocketCtor) {
|
|
90
|
+
if (upstreams.get(peer.id) !== state) return;
|
|
91
|
+
let ws;
|
|
92
|
+
try {
|
|
93
|
+
const protocols = _resolveProtocols(peer, options.forwardProtocol);
|
|
94
|
+
const wsOptions = _resolveWsOptions(options, peer);
|
|
95
|
+
ws = wsOptions ? new WebSocketCtor(url, protocols, wsOptions) : new WebSocketCtor(url, protocols);
|
|
96
|
+
ws.binaryType = "arraybuffer";
|
|
97
|
+
} catch {
|
|
98
|
+
_cleanupState(upstreams, peer.id, state);
|
|
99
|
+
_safeClose(peer, 1011, "Upstream setup failed");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
state.ws = ws;
|
|
103
|
+
ws.addEventListener("open", () => {
|
|
104
|
+
if (upstreams.get(peer.id) !== state) return;
|
|
105
|
+
_clearTimeout(state);
|
|
106
|
+
state.open = true;
|
|
107
|
+
try {
|
|
108
|
+
for (const data of state.buffer) ws.send(data);
|
|
109
|
+
} catch {} finally {
|
|
110
|
+
state.buffer.length = 0;
|
|
111
|
+
state.bufferSize = 0;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
ws.addEventListener("message", (event) => {
|
|
115
|
+
if (upstreams.get(peer.id) !== state) return;
|
|
116
|
+
_safeSend(peer, event.data);
|
|
117
|
+
});
|
|
118
|
+
ws.addEventListener("close", (event) => {
|
|
119
|
+
if (upstreams.get(peer.id) !== state) return;
|
|
120
|
+
_cleanupState(upstreams, peer.id, state);
|
|
121
|
+
_safeClose(peer, _remapIncomingCode(event.code), event.reason);
|
|
122
|
+
});
|
|
123
|
+
ws.addEventListener("error", () => {
|
|
124
|
+
if (upstreams.get(peer.id) !== state) return;
|
|
125
|
+
_cleanupState(upstreams, peer.id, state);
|
|
126
|
+
_safeClose(peer, 1011, "Upstream error");
|
|
127
|
+
});
|
|
128
|
+
}
|
|
105
129
|
function _cleanupState(upstreams, id, state) {
|
|
106
130
|
_clearTimeout(state);
|
|
107
131
|
upstreams.delete(id);
|
|
108
132
|
try {
|
|
109
|
-
state.ws
|
|
133
|
+
state.ws?.close();
|
|
110
134
|
} catch {}
|
|
111
135
|
}
|
|
112
136
|
function _clearTimeout(state) {
|
|
@@ -117,13 +141,20 @@ function _clearTimeout(state) {
|
|
|
117
141
|
}
|
|
118
142
|
function _resolveTarget(target, peer) {
|
|
119
143
|
const raw = typeof target === "function" ? target(peer) : target;
|
|
144
|
+
if (_isThenable(raw)) return Promise.resolve(raw).then((value) => value instanceof URL ? value : new URL(value));
|
|
120
145
|
return raw instanceof URL ? raw : new URL(raw);
|
|
121
146
|
}
|
|
122
|
-
function
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
147
|
+
function _isThenable(value) {
|
|
148
|
+
return value != null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
|
149
|
+
}
|
|
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;
|
|
127
158
|
}
|
|
128
159
|
function _resolveProtocols(peer, forwardProtocol) {
|
|
129
160
|
if (forwardProtocol === false) return;
|
package/dist/sync.d.mts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { PostgresClientLike, RedisClientLike, SyncAdapter, SyncDriver, SyncDriverName, SyncDriverOptions, SyncMessage, broadcastChannel, cluster, decodeEnvelope, encodeEnvelope, pgsql, redis, setupPrimaryCluster, syncDrivers } from "./_chunks/adapter.mjs";
|
|
2
|
+
export { type PostgresClientLike, type RedisClientLike, type SyncAdapter, type SyncDriver, SyncDriverName, SyncDriverOptions, type SyncMessage, broadcastChannel, cluster, decodeEnvelope, encodeEnvelope, pgsql, redis, setupPrimaryCluster, syncDrivers };
|
package/dist/sync.mjs
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
function broadcastChannel(opts) {
|
|
2
|
+
return ({ id }) => {
|
|
3
|
+
const channel = new BroadcastChannel(opts.channel);
|
|
4
|
+
return {
|
|
5
|
+
subscribe(deliver) {
|
|
6
|
+
channel.addEventListener("message", (event) => {
|
|
7
|
+
const envelope = event.data;
|
|
8
|
+
const msg = envelope?.msg;
|
|
9
|
+
if (!envelope || envelope.id === id || typeof msg?.topic !== "string" || typeof msg.namespace !== "string" || !(typeof msg.data === "string" || msg.data instanceof Uint8Array)) return;
|
|
10
|
+
deliver(msg);
|
|
11
|
+
});
|
|
12
|
+
},
|
|
13
|
+
publish(msg) {
|
|
14
|
+
channel.postMessage({
|
|
15
|
+
id,
|
|
16
|
+
msg
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
close() {
|
|
20
|
+
channel.close();
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function encodeEnvelope(id, msg) {
|
|
26
|
+
const binary = msg.data instanceof Uint8Array;
|
|
27
|
+
return JSON.stringify({
|
|
28
|
+
id,
|
|
29
|
+
msg: {
|
|
30
|
+
namespace: msg.namespace,
|
|
31
|
+
topic: msg.topic,
|
|
32
|
+
binary,
|
|
33
|
+
data: binary ? toBase64(msg.data) : msg.data
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function decodeEnvelope(raw) {
|
|
38
|
+
try {
|
|
39
|
+
const parsed = JSON.parse(raw);
|
|
40
|
+
if (!parsed || typeof parsed.id !== "string" || !parsed.msg || typeof parsed.msg.topic !== "string" || typeof parsed.msg.namespace !== "string" || typeof parsed.msg.data !== "string") return;
|
|
41
|
+
return {
|
|
42
|
+
id: parsed.id,
|
|
43
|
+
msg: {
|
|
44
|
+
namespace: parsed.msg.namespace,
|
|
45
|
+
topic: parsed.msg.topic,
|
|
46
|
+
data: parsed.msg.binary ? fromBase64(parsed.msg.data) : parsed.msg.data
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
} catch {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function toBase64(data) {
|
|
54
|
+
let binary = "";
|
|
55
|
+
for (const byte of data) binary += String.fromCharCode(byte);
|
|
56
|
+
return btoa(binary);
|
|
57
|
+
}
|
|
58
|
+
function fromBase64(data) {
|
|
59
|
+
const binary = atob(data);
|
|
60
|
+
const out = new Uint8Array(binary.length);
|
|
61
|
+
for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
function redis(opts) {
|
|
65
|
+
const channel = opts.channel;
|
|
66
|
+
const isNodeRedis = opts.connector === void 0 ? typeof opts.client.pSubscribe === "function" : opts.connector === "node-redis";
|
|
67
|
+
return ({ id }) => {
|
|
68
|
+
const subscriber = opts.client.duplicate();
|
|
69
|
+
let started = false;
|
|
70
|
+
let onMessage;
|
|
71
|
+
return {
|
|
72
|
+
async subscribe(deliver) {
|
|
73
|
+
started = true;
|
|
74
|
+
const handle = (raw) => {
|
|
75
|
+
const envelope = decodeEnvelope(raw);
|
|
76
|
+
if (!envelope || envelope.id === id) return;
|
|
77
|
+
deliver(envelope.msg);
|
|
78
|
+
};
|
|
79
|
+
if (isNodeRedis) {
|
|
80
|
+
await subscriber.connect?.();
|
|
81
|
+
await subscriber.subscribe(channel, (raw) => handle(raw));
|
|
82
|
+
} else {
|
|
83
|
+
onMessage = (ch, raw) => {
|
|
84
|
+
if (ch === channel) handle(raw);
|
|
85
|
+
};
|
|
86
|
+
subscriber.on?.("message", onMessage);
|
|
87
|
+
await subscriber.subscribe(channel);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
async publish(msg) {
|
|
91
|
+
await opts.client.publish(channel, encodeEnvelope(id, msg));
|
|
92
|
+
},
|
|
93
|
+
async close() {
|
|
94
|
+
if (!started) return;
|
|
95
|
+
if (onMessage) {
|
|
96
|
+
subscriber.off?.("message", onMessage);
|
|
97
|
+
onMessage = void 0;
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
await subscriber.quit?.();
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error("[crossws] sync redis close failed:", error);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function pgsql(opts) {
|
|
109
|
+
const channel = opts.channel;
|
|
110
|
+
if (new TextEncoder().encode(channel).length > 63) throw new Error(`[crossws] pgsql sync channel name must be at most 63 bytes (got ${channel.length} chars): ${channel}`);
|
|
111
|
+
const isPostgresJs = opts.connector === void 0 ? typeof opts.client.listen === "function" : opts.connector === "postgres.js";
|
|
112
|
+
if (!isPostgresJs && "idleCount" in opts.client && "totalCount" in opts.client && typeof opts.client.query === "function") throw new Error("[crossws] pgsql sync requires a dedicated `Client`, not a `Pool` (pool connections rotate, so LISTEN/NOTIFY can't deliver). Pass `new Client()` (node-postgres) or a `postgres()` instance (postgres.js).");
|
|
113
|
+
const quotedChannel = `"${channel.replace(/"/g, "\"\"")}"`;
|
|
114
|
+
return ({ id }) => {
|
|
115
|
+
let unlisten;
|
|
116
|
+
let onNotification;
|
|
117
|
+
return {
|
|
118
|
+
async subscribe(deliver) {
|
|
119
|
+
const handle = (raw) => {
|
|
120
|
+
const envelope = decodeEnvelope(raw);
|
|
121
|
+
if (!envelope || envelope.id === id) return;
|
|
122
|
+
deliver(envelope.msg);
|
|
123
|
+
};
|
|
124
|
+
if (isPostgresJs) unlisten = (await opts.client.listen(channel, (payload) => handle(payload)))?.unlisten;
|
|
125
|
+
else {
|
|
126
|
+
onNotification = (msg) => {
|
|
127
|
+
if (msg.channel === channel && msg.payload !== void 0) handle(msg.payload);
|
|
128
|
+
};
|
|
129
|
+
opts.client.on("notification", onNotification);
|
|
130
|
+
await opts.client.query(`LISTEN ${quotedChannel}`);
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
async publish(msg) {
|
|
134
|
+
const payload = encodeEnvelope(id, msg);
|
|
135
|
+
if (isPostgresJs) await opts.client.notify(channel, payload);
|
|
136
|
+
else await opts.client.query("SELECT pg_notify($1, $2)", [channel, payload]);
|
|
137
|
+
},
|
|
138
|
+
async close() {
|
|
139
|
+
if (isPostgresJs) await unlisten?.();
|
|
140
|
+
else if (onNotification) {
|
|
141
|
+
opts.client.removeListener?.("notification", onNotification);
|
|
142
|
+
onNotification = void 0;
|
|
143
|
+
await opts.client.query?.(`UNLISTEN ${quotedChannel}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const CLUSTER_MESSAGE = "crossws:sync";
|
|
150
|
+
function isClusterEnvelope(message) {
|
|
151
|
+
return typeof message === "object" && message !== null && typeof message[CLUSTER_MESSAGE] === "string" && typeof message.env === "string";
|
|
152
|
+
}
|
|
153
|
+
let relayInstalled = false;
|
|
154
|
+
async function setupPrimaryCluster() {
|
|
155
|
+
const cluster = (await import("node:cluster")).default;
|
|
156
|
+
if (!cluster.isPrimary || relayInstalled) return;
|
|
157
|
+
relayInstalled = true;
|
|
158
|
+
cluster.on("message", (_worker, message) => {
|
|
159
|
+
if (!isClusterEnvelope(message)) return;
|
|
160
|
+
for (const id in cluster.workers) cluster.workers[id]?.send(message);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function cluster(opts) {
|
|
164
|
+
const channel = opts.channel;
|
|
165
|
+
return ({ id }) => {
|
|
166
|
+
const proc = globalThis.process;
|
|
167
|
+
let onMessage;
|
|
168
|
+
return {
|
|
169
|
+
subscribe(deliver) {
|
|
170
|
+
if (typeof proc?.send !== "function") throw new Error("[crossws] cluster sync must run in a worker forked by node:cluster (process.send is unavailable). Call setupPrimaryCluster() in the primary process and start your server in the workers.");
|
|
171
|
+
onMessage = (message) => {
|
|
172
|
+
if (!isClusterEnvelope(message) || message[CLUSTER_MESSAGE] !== channel) return;
|
|
173
|
+
const envelope = decodeEnvelope(message.env);
|
|
174
|
+
if (!envelope || envelope.id === id) return;
|
|
175
|
+
deliver(envelope.msg);
|
|
176
|
+
};
|
|
177
|
+
proc.on("message", onMessage);
|
|
178
|
+
},
|
|
179
|
+
publish(msg) {
|
|
180
|
+
proc?.send?.({
|
|
181
|
+
[CLUSTER_MESSAGE]: channel,
|
|
182
|
+
env: encodeEnvelope(id, msg)
|
|
183
|
+
});
|
|
184
|
+
},
|
|
185
|
+
close() {
|
|
186
|
+
if (onMessage) {
|
|
187
|
+
proc?.off?.("message", onMessage);
|
|
188
|
+
onMessage = void 0;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
const syncDrivers = [
|
|
195
|
+
"broadcastChannel",
|
|
196
|
+
"redis",
|
|
197
|
+
"pgsql",
|
|
198
|
+
"cluster"
|
|
199
|
+
];
|
|
200
|
+
export { broadcastChannel, cluster, decodeEnvelope, encodeEnvelope, pgsql, redis, setupPrimaryCluster, syncDrivers };
|
|
@@ -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,24 +1,15 @@
|
|
|
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",
|
|
12
|
+
"./sync": "./dist/sync.mjs",
|
|
22
13
|
"./adapters/bun": "./dist/adapters/bun.mjs",
|
|
23
14
|
"./adapters/bunny": "./dist/adapters/bunny.mjs",
|
|
24
15
|
"./adapters/deno": "./dist/adapters/deno.mjs",
|
|
@@ -42,19 +33,30 @@
|
|
|
42
33
|
"./websocket": {
|
|
43
34
|
"browser": "./dist/websocket/native.mjs",
|
|
44
35
|
"worker": "./dist/websocket/native.mjs",
|
|
45
|
-
"bun": "./dist/websocket/
|
|
46
|
-
"deno": "./dist/websocket/
|
|
36
|
+
"bun": "./dist/websocket/bun.mjs",
|
|
37
|
+
"deno": "./dist/websocket/deno.mjs",
|
|
47
38
|
"edge-light": "./dist/websocket/native.mjs",
|
|
48
39
|
"workerd": "./dist/websocket/native.mjs",
|
|
49
40
|
"node": "./dist/websocket/node.mjs",
|
|
50
41
|
"default": "./dist/websocket/native.mjs"
|
|
51
42
|
}
|
|
52
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
|
+
],
|
|
53
54
|
"scripts": {
|
|
55
|
+
"prepare": "obuild --stub",
|
|
54
56
|
"build": "obuild",
|
|
55
57
|
"dev": "vitest",
|
|
56
|
-
"lint": "oxlint . && oxfmt --check src test",
|
|
57
58
|
"fmt": "oxlint . --fix && oxfmt src test",
|
|
59
|
+
"lint": "oxlint . && oxfmt --check src test",
|
|
58
60
|
"prepack": "pnpm run build",
|
|
59
61
|
"play:bun": "bun --watch test/fixture/bun.ts",
|
|
60
62
|
"play:cf": "wrangler dev --port 3001 -c test/fixture/wrangler.toml",
|
|
@@ -67,15 +69,18 @@
|
|
|
67
69
|
"test": "pnpm lint && pnpm typecheck && vitest run --coverage",
|
|
68
70
|
"typecheck": "tsgo --noEmit --skipLibCheck"
|
|
69
71
|
},
|
|
72
|
+
"resolutions": {
|
|
73
|
+
"crossws": "workspace:*"
|
|
74
|
+
},
|
|
70
75
|
"devDependencies": {
|
|
71
|
-
"@cloudflare/workers-types": "^4.
|
|
76
|
+
"@cloudflare/workers-types": "^4.20260701.1",
|
|
72
77
|
"@types/bun": "^1.3.14",
|
|
73
78
|
"@types/deno": "^2.7.0",
|
|
74
|
-
"@types/node": "^
|
|
75
|
-
"@types/web": "^0.0.
|
|
79
|
+
"@types/node": "^26.1.0",
|
|
80
|
+
"@types/web": "^0.0.352",
|
|
76
81
|
"@types/ws": "^8.18.1",
|
|
77
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
78
|
-
"@vitest/coverage-v8": "^4.1.
|
|
82
|
+
"@typescript/native-preview": "^7.0.0-dev.20260701.1",
|
|
83
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
79
84
|
"automd": "^0.4.3",
|
|
80
85
|
"changelogen": "^0.6.2",
|
|
81
86
|
"consola": "^3.4.2",
|
|
@@ -86,16 +91,16 @@
|
|
|
86
91
|
"h3": "2.0.1-rc.22",
|
|
87
92
|
"jiti": "^2.7.0",
|
|
88
93
|
"listhen": "^1.10.0",
|
|
89
|
-
"obuild": "^0.4.
|
|
90
|
-
"oxfmt": "^0.
|
|
91
|
-
"oxlint": "^1.
|
|
92
|
-
"srvx": "^0.11.
|
|
94
|
+
"obuild": "^0.4.37",
|
|
95
|
+
"oxfmt": "^0.57.0",
|
|
96
|
+
"oxlint": "^1.72.0",
|
|
97
|
+
"srvx": "^0.11.18",
|
|
93
98
|
"typescript": "^6.0.3",
|
|
94
99
|
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.57.0",
|
|
95
100
|
"unbuild": "^3.6.1",
|
|
96
|
-
"undici": "^8.
|
|
97
|
-
"vitest": "^4.1.
|
|
98
|
-
"wrangler": "^4.
|
|
101
|
+
"undici": "^8.5.0",
|
|
102
|
+
"vitest": "^4.1.9",
|
|
103
|
+
"wrangler": "^4.106.0",
|
|
99
104
|
"ws": "^8.21.0"
|
|
100
105
|
},
|
|
101
106
|
"peerDependencies": {
|
|
@@ -106,22 +111,5 @@
|
|
|
106
111
|
"optional": true
|
|
107
112
|
}
|
|
108
113
|
},
|
|
109
|
-
"
|
|
110
|
-
"crossws": "workspace:*"
|
|
111
|
-
},
|
|
112
|
-
"packageManager": "pnpm@11.5.2",
|
|
113
|
-
"pnpm": {
|
|
114
|
-
"ignoredBuiltDependencies": [
|
|
115
|
-
"@parcel/watcher",
|
|
116
|
-
"esbuild",
|
|
117
|
-
"sharp",
|
|
118
|
-
"workerd"
|
|
119
|
-
],
|
|
120
|
-
"onlyBuiltDependencies": [
|
|
121
|
-
"@parcel/watcher",
|
|
122
|
-
"esbuild",
|
|
123
|
-
"sharp",
|
|
124
|
-
"workerd"
|
|
125
|
-
]
|
|
126
|
-
}
|
|
114
|
+
"packageManager": "pnpm@11.7.0"
|
|
127
115
|
}
|
package/sync.d.ts
ADDED
|
@@ -1,24 +0,0 @@
|
|
|
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);
|
|
24
|
-
export { __commonJSMin, __require, __toESM };
|