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/_chunks/node.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AdapterHookable, adapterUtils, getPeers } from "./adapter.mjs";
|
|
2
1
|
import { import_websocket_server } from "./libs/ws.mjs";
|
|
3
|
-
import {
|
|
2
|
+
import { AdapterHookable, adapterUtils, getPeers, toBufferLike } from "./adapter.mjs";
|
|
3
|
+
import { Message, Peer } from "./peer.mjs";
|
|
4
4
|
import { WSError } from "./error.mjs";
|
|
5
5
|
import { StubRequest } from "./_request.mjs";
|
|
6
6
|
function fromNodeUpgradeHandler(handler) {
|
|
@@ -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
|
-
...
|
|
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
|
|
98
|
+
return this._internal.ws.bufferedAmount;
|
|
93
99
|
}
|
|
94
|
-
|
|
100
|
+
_publish(topic, data, options) {
|
|
95
101
|
const dataBuff = toBufferLike(data);
|
|
96
|
-
const isBinary = typeof
|
|
102
|
+
const isBinary = typeof dataBuff !== "string";
|
|
97
103
|
const sendOptions = {
|
|
98
104
|
compress: options?.compress,
|
|
99
105
|
binary: isBinary,
|
package/dist/_chunks/peer.mjs
CHANGED
|
@@ -1,28 +1,4 @@
|
|
|
1
|
-
|
|
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 { Message, Peer
|
|
198
|
+
export { Message, Peer };
|
package/dist/adapters/bun.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { AdapterHookable, adapterUtils, getPeers } from "../_chunks/adapter.mjs";
|
|
2
|
-
import { Message, Peer
|
|
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
|
-
...
|
|
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
|
-
|
|
75
|
+
_publish(topic, data, options) {
|
|
67
76
|
return this._internal.ws.publish(topic, toBufferLike(data), options?.compress);
|
|
68
77
|
}
|
|
69
78
|
subscribe(topic) {
|
package/dist/adapters/bunny.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { AdapterHookable, adapterUtils, getPeers } from "../_chunks/adapter.mjs";
|
|
2
|
-
import { Message, Peer
|
|
1
|
+
import { AdapterHookable, adapterUtils, getPeers, toBufferLike } from "../_chunks/adapter.mjs";
|
|
2
|
+
import { Message, Peer } from "../_chunks/peer.mjs";
|
|
3
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
|
-
...
|
|
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
|
-
|
|
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,5 +1,5 @@
|
|
|
1
|
-
import { AdapterHookable, adapterUtils, getPeers } from "../_chunks/adapter.mjs";
|
|
2
|
-
import { Message, Peer
|
|
1
|
+
import { AdapterHookable, adapterUtils, getPeers, toBufferLike } from "../_chunks/adapter.mjs";
|
|
2
|
+
import { Message, Peer } from "../_chunks/peer.mjs";
|
|
3
3
|
import { WSError } from "../_chunks/error.mjs";
|
|
4
4
|
import { StubRequest } from "../_chunks/_request.mjs";
|
|
5
5
|
import { env } from "cloudflare:workers";
|
|
@@ -14,7 +14,8 @@ const cloudflareAdapter = (opts = {}) => {
|
|
|
14
14
|
return binding.get(instanceId);
|
|
15
15
|
}
|
|
16
16
|
});
|
|
17
|
-
const
|
|
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
|
-
|
|
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,7 +142,8 @@ 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;
|
|
@@ -156,7 +158,7 @@ var CloudflareFallbackPeer = class extends Peer {
|
|
|
156
158
|
this._internal.wsServer.send(toBufferLike(data));
|
|
157
159
|
return 0;
|
|
158
160
|
}
|
|
159
|
-
|
|
161
|
+
_publish(_topic, _message) {
|
|
160
162
|
console.warn("[crossws] [cloudflare] pub/sub support requires Durable Objects.");
|
|
161
163
|
}
|
|
162
164
|
close(code, reason) {
|
package/dist/adapters/deno.mjs
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import { AdapterHookable, adapterUtils, getPeers } from "../_chunks/adapter.mjs";
|
|
2
|
-
import { Message, Peer
|
|
1
|
+
import { AdapterHookable, adapterUtils, getPeers, toBufferLike } from "../_chunks/adapter.mjs";
|
|
2
|
+
import { Message, Peer } from "../_chunks/peer.mjs";
|
|
3
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);
|
|
7
7
|
const globalPeers = /* @__PURE__ */ new Map();
|
|
8
|
+
const baseUtils = adapterUtils(globalPeers, options);
|
|
8
9
|
return {
|
|
9
|
-
...
|
|
10
|
+
...baseUtils,
|
|
10
11
|
handleUpgrade: async (request, info) => {
|
|
12
|
+
const remoteAddress = info.remoteAddr?.hostname;
|
|
13
|
+
const requestSnapshot = snapshotRequest(request);
|
|
11
14
|
const { upgradeHeaders, endResponse, context, namespace } = await hooks.upgrade(request);
|
|
12
15
|
if (endResponse) return endResponse;
|
|
13
16
|
const headers = upgradeHeaders instanceof Headers ? upgradeHeaders : new Headers(upgradeHeaders);
|
|
@@ -18,11 +21,12 @@ const denoAdapter = (options = {}) => {
|
|
|
18
21
|
const peers = getPeers(globalPeers, namespace);
|
|
19
22
|
const peer = new DenoPeer({
|
|
20
23
|
ws: upgrade.socket,
|
|
21
|
-
request,
|
|
24
|
+
request: requestSnapshot,
|
|
22
25
|
peers,
|
|
23
|
-
|
|
26
|
+
remoteAddress,
|
|
24
27
|
context,
|
|
25
|
-
namespace
|
|
28
|
+
namespace,
|
|
29
|
+
sync: baseUtils.sync
|
|
26
30
|
});
|
|
27
31
|
peers.add(peer);
|
|
28
32
|
upgrade.socket.addEventListener("open", () => {
|
|
@@ -43,14 +47,23 @@ const denoAdapter = (options = {}) => {
|
|
|
43
47
|
}
|
|
44
48
|
};
|
|
45
49
|
};
|
|
50
|
+
function snapshotRequest(request) {
|
|
51
|
+
const url = request.url;
|
|
52
|
+
const headers = new Headers(request.headers);
|
|
53
|
+
return new Proxy(request, { get(target, prop, receiver) {
|
|
54
|
+
if (prop === "url") return url;
|
|
55
|
+
if (prop === "headers") return headers;
|
|
56
|
+
return Reflect.get(target, prop, receiver);
|
|
57
|
+
} });
|
|
58
|
+
}
|
|
46
59
|
var DenoPeer = class extends Peer {
|
|
47
60
|
get remoteAddress() {
|
|
48
|
-
return this._internal.
|
|
61
|
+
return this._internal.remoteAddress;
|
|
49
62
|
}
|
|
50
63
|
send(data) {
|
|
51
64
|
return this._internal.ws.send(toBufferLike(data));
|
|
52
65
|
}
|
|
53
|
-
|
|
66
|
+
_publish(topic, data) {
|
|
54
67
|
const dataBuff = toBufferLike(data);
|
|
55
68
|
for (const peer of this._internal.peers) if (peer !== this && peer._topics.has(topic)) peer._internal.ws.send(dataBuff);
|
|
56
69
|
}
|
package/dist/adapters/sse.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { AdapterHookable, adapterUtils, getPeers } from "../_chunks/adapter.mjs";
|
|
2
|
-
import { Message, Peer
|
|
1
|
+
import { AdapterHookable, adapterUtils, getPeers, toString } from "../_chunks/adapter.mjs";
|
|
2
|
+
import { Message, Peer } from "../_chunks/peer.mjs";
|
|
3
3
|
const sseAdapter = (opts = {}) => {
|
|
4
4
|
const hooks = new AdapterHookable(opts);
|
|
5
5
|
const globalPeers = /* @__PURE__ */ new Map();
|
|
6
6
|
const peersMap = opts.bidir ? /* @__PURE__ */ new Map() : void 0;
|
|
7
|
+
const baseUtils = adapterUtils(globalPeers, opts);
|
|
7
8
|
return {
|
|
8
|
-
...
|
|
9
|
+
...baseUtils,
|
|
9
10
|
fetch: async (request) => {
|
|
10
11
|
const { upgradeHeaders, endResponse, context, namespace } = await hooks.upgrade(request);
|
|
11
12
|
if (endResponse) return endResponse;
|
|
@@ -31,7 +32,8 @@ const sseAdapter = (opts = {}) => {
|
|
|
31
32
|
hooks,
|
|
32
33
|
ws,
|
|
33
34
|
context,
|
|
34
|
-
namespace
|
|
35
|
+
namespace,
|
|
36
|
+
sync: baseUtils.sync
|
|
35
37
|
});
|
|
36
38
|
peers.add(peer);
|
|
37
39
|
if (opts.bidir) {
|
|
@@ -83,7 +85,7 @@ var SSEPeer = class extends Peer {
|
|
|
83
85
|
this._sendEvent("message", toString(data));
|
|
84
86
|
return 0;
|
|
85
87
|
}
|
|
86
|
-
|
|
88
|
+
_publish(topic, data) {
|
|
87
89
|
const dataBuff = toString(data);
|
|
88
90
|
for (const peer of this._internal.peers) if (peer !== this && peer._topics.has(topic)) peer._sendEvent("message", dataBuff);
|
|
89
91
|
}
|
package/dist/adapters/uws.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Adapter, AdapterInstance, AdapterOptions, Peer, PeerContext } from "../_chunks/adapter.mjs";
|
|
1
|
+
import { Adapter, AdapterInstance, AdapterOptions, Peer, PeerContext, SyncDriver } from "../_chunks/adapter.mjs";
|
|
2
2
|
import { WebSocket } from "../_chunks/web.mjs";
|
|
3
3
|
import uws from "uWebSockets.js";
|
|
4
4
|
declare const StubRequest: {
|
|
@@ -29,6 +29,7 @@ declare class UWSPeer extends Peer<{
|
|
|
29
29
|
uws: uws.WebSocket<UserData>;
|
|
30
30
|
ws: UwsWebSocketProxy;
|
|
31
31
|
uwsData: UserData;
|
|
32
|
+
sync?: SyncDriver;
|
|
32
33
|
}> {
|
|
33
34
|
get remoteAddress(): string | undefined;
|
|
34
35
|
get context(): PeerContext;
|
|
@@ -37,7 +38,7 @@ declare class UWSPeer extends Peer<{
|
|
|
37
38
|
}): number;
|
|
38
39
|
subscribe(topic: string): void;
|
|
39
40
|
unsubscribe(topic: string): void;
|
|
40
|
-
|
|
41
|
+
_publish(topic: string, message: string, options?: {
|
|
41
42
|
compress?: boolean;
|
|
42
43
|
}): number;
|
|
43
44
|
close(code?: number, reason?: uws.RecognizedString): void;
|
|
@@ -47,8 +48,8 @@ declare class UWSReqProxy extends StubRequest {
|
|
|
47
48
|
constructor(req: uws.HttpRequest);
|
|
48
49
|
}
|
|
49
50
|
declare class UwsWebSocketProxy implements Partial<WebSocket> {
|
|
50
|
-
private _uws;
|
|
51
51
|
readyState?: number;
|
|
52
|
+
private _uws;
|
|
52
53
|
constructor(_uws: uws.WebSocket<UserData>);
|
|
53
54
|
get bufferedAmount(): number;
|
|
54
55
|
get protocol(): string;
|
package/dist/adapters/uws.mjs
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { AdapterHookable, adapterUtils, getPeers } from "../_chunks/adapter.mjs";
|
|
2
|
-
import { Message, Peer
|
|
1
|
+
import { AdapterHookable, adapterUtils, getPeers, toBufferLike } from "../_chunks/adapter.mjs";
|
|
2
|
+
import { Message, Peer } from "../_chunks/peer.mjs";
|
|
3
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();
|
|
7
|
+
const baseUtils = adapterUtils(globalPeers, options, { nativePubSub: true });
|
|
7
8
|
return {
|
|
8
|
-
...
|
|
9
|
+
...baseUtils,
|
|
9
10
|
websocket: {
|
|
10
11
|
...options.uws,
|
|
11
12
|
close(ws, code, message) {
|
|
12
13
|
const peers = getPeers(globalPeers, ws.getUserData().namespace);
|
|
13
|
-
const peer = getPeer(ws, peers);
|
|
14
|
+
const peer = getPeer(ws, peers, baseUtils.sync);
|
|
14
15
|
peer._internal.ws.readyState = 2;
|
|
15
16
|
peers.delete(peer);
|
|
16
17
|
hooks.callHook("close", peer, {
|
|
@@ -20,12 +21,16 @@ const uwsAdapter = (options = {}) => {
|
|
|
20
21
|
peer._internal.ws.readyState = 3;
|
|
21
22
|
},
|
|
22
23
|
message(ws, message, _isBinary) {
|
|
23
|
-
const peer = getPeer(ws, getPeers(globalPeers, ws.getUserData().namespace));
|
|
24
|
+
const peer = getPeer(ws, getPeers(globalPeers, ws.getUserData().namespace), baseUtils.sync);
|
|
24
25
|
hooks.callHook("message", peer, new Message(message, peer));
|
|
25
26
|
},
|
|
27
|
+
drain(ws) {
|
|
28
|
+
const peer = getPeer(ws, getPeers(globalPeers, ws.getUserData().namespace));
|
|
29
|
+
hooks.callHook("drain", peer);
|
|
30
|
+
},
|
|
26
31
|
open(ws) {
|
|
27
32
|
const peers = getPeers(globalPeers, ws.getUserData().namespace);
|
|
28
|
-
const peer = getPeer(ws, peers);
|
|
33
|
+
const peer = getPeer(ws, peers, baseUtils.sync);
|
|
29
34
|
peers.add(peer);
|
|
30
35
|
hooks.callHook("open", peer);
|
|
31
36
|
},
|
|
@@ -70,7 +75,7 @@ const uwsAdapter = (options = {}) => {
|
|
|
70
75
|
}
|
|
71
76
|
};
|
|
72
77
|
};
|
|
73
|
-
function getPeer(uws, peers) {
|
|
78
|
+
function getPeer(uws, peers, sync) {
|
|
74
79
|
const uwsData = uws.getUserData();
|
|
75
80
|
if (uwsData.peer) return uwsData.peer;
|
|
76
81
|
const peer = new UWSPeer({
|
|
@@ -79,7 +84,8 @@ function getPeer(uws, peers) {
|
|
|
79
84
|
ws: new UwsWebSocketProxy(uws),
|
|
80
85
|
request: uwsData.webReq,
|
|
81
86
|
namespace: uwsData.namespace,
|
|
82
|
-
uwsData
|
|
87
|
+
uwsData,
|
|
88
|
+
sync
|
|
83
89
|
});
|
|
84
90
|
uwsData.peer = peer;
|
|
85
91
|
return peer;
|
|
@@ -106,7 +112,7 @@ var UWSPeer = class extends Peer {
|
|
|
106
112
|
this._topics.delete(topic);
|
|
107
113
|
this._internal.uws.unsubscribe(topic);
|
|
108
114
|
}
|
|
109
|
-
|
|
115
|
+
_publish(topic, message, options) {
|
|
110
116
|
const data = toBufferLike(message);
|
|
111
117
|
const isBinary = typeof data !== "string";
|
|
112
118
|
this._internal.uws.publish(topic, data, isBinary, options?.compress);
|
|
@@ -136,8 +142,8 @@ var UWSReqProxy = class extends StubRequest {
|
|
|
136
142
|
}
|
|
137
143
|
};
|
|
138
144
|
var UwsWebSocketProxy = class {
|
|
139
|
-
_uws;
|
|
140
145
|
readyState = 1;
|
|
146
|
+
_uws;
|
|
141
147
|
constructor(_uws) {
|
|
142
148
|
this._uws = _uws;
|
|
143
149
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
|
-
import { Adapter, AdapterInstance, AdapterInternal, AdapterOptions, Hooks, Message, Peer, PeerContext, ResolveHooks, WSError, defineHooks, defineWebSocketAdapter } from "./_chunks/adapter.mjs";
|
|
1
|
+
import { Adapter, AdapterInstance, AdapterInternal, AdapterOptions, Hooks, Message, Peer, PeerContext, ResolveHooks, SyncAdapter, SyncDriver, SyncErrorContext, SyncMessage, WSError, WaitForDrainOptions, defineHooks, defineWebSocketAdapter } from "./_chunks/adapter.mjs";
|
|
2
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://`).
|
|
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
|
-
* based on the incoming {@link Peer}.
|
|
13
|
+
* based on the incoming {@link Peer}. The resolver may be **async** (return a
|
|
14
|
+
* promise) — useful when the upstream address isn't known yet at connect time
|
|
15
|
+
* (e.g. a worker that's still booting or being hot-reloaded). Client frames
|
|
16
|
+
* sent in the meantime are buffered (bounded by {@link maxBufferSize}) and a
|
|
17
|
+
* non-zero {@link connectTimeout} also covers the resolution, so a resolver
|
|
18
|
+
* that never settles closes the peer with `1011` rather than hanging. With
|
|
19
|
+
* `connectTimeout: 0` (timeout disabled) a never-settling resolver leaves the
|
|
20
|
+
* peer open until {@link maxBufferSize} is hit (`1009`), so pair an unbounded
|
|
21
|
+
* timeout with your own resolver deadline.
|
|
9
22
|
*/
|
|
10
|
-
target: string | URL | ((peer: Peer) => string | URL);
|
|
23
|
+
target: string | URL | ((peer: Peer) => string | URL | Promise<string | URL>);
|
|
11
24
|
/**
|
|
12
25
|
* Subprotocol(s) to offer the upstream during the handshake.
|
|
13
26
|
*
|
|
@@ -81,6 +94,39 @@ interface WebSocketProxyOptions {
|
|
|
81
94
|
* ```
|
|
82
95
|
*/
|
|
83
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);
|
|
84
130
|
}
|
|
85
131
|
/**
|
|
86
132
|
* Create a set of crossws hooks that proxy incoming WebSocket connections
|
|
@@ -94,4 +140,4 @@ interface WebSocketProxyOptions {
|
|
|
94
140
|
* ```
|
|
95
141
|
*/
|
|
96
142
|
declare function createWebSocketProxy(target: WebSocketProxyOptions["target"] | WebSocketProxyOptions): Partial<Hooks>;
|
|
97
|
-
export { type Adapter, type AdapterInstance, type AdapterInternal, type AdapterOptions, type Hooks, type Message, type Peer, type PeerContext, type ResolveHooks, type ServerWithWSOptions, type WSError, type WSOptions, type WebSocketProxyOptions, createWebSocketProxy, defineHooks, defineWebSocketAdapter };
|
|
143
|
+
export { type Adapter, type AdapterInstance, type AdapterInternal, type AdapterOptions, type Hooks, type Message, type Peer, type PeerContext, type ResolveHooks, type ServerWithWSOptions, type SyncAdapter, type SyncDriver, type SyncErrorContext, type SyncMessage, type WSError, type WSOptions, type WaitForDrainOptions, type WebSocketProxyOptions, createWebSocketProxy, defineHooks, defineWebSocketAdapter };
|