@colyseus/bun-websockets 0.17.4 → 0.17.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.
- package/build/BunWebSockets.cjs +99 -25
- package/build/BunWebSockets.cjs.map +2 -2
- package/build/BunWebSockets.d.ts +11 -5
- package/build/BunWebSockets.mjs +95 -23
- package/build/BunWebSockets.mjs.map +2 -2
- package/build/WebSocketClient.cjs +6 -4
- package/build/WebSocketClient.cjs.map +1 -1
- package/build/index.cjs +4 -2
- package/build/index.cjs.map +1 -1
- package/package.json +3 -3
- package/src/BunWebSockets.ts +133 -71
package/build/BunWebSockets.cjs
CHANGED
|
@@ -16,6 +16,8 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// packages/transport/bun-websockets/src/BunWebSockets.ts
|
|
19
21
|
var BunWebSockets_exports = {};
|
|
20
22
|
__export(BunWebSockets_exports, {
|
|
21
23
|
BunWebSockets: () => BunWebSockets
|
|
@@ -23,38 +25,109 @@ __export(BunWebSockets_exports, {
|
|
|
23
25
|
module.exports = __toCommonJS(BunWebSockets_exports);
|
|
24
26
|
var import_bun = require("bun");
|
|
25
27
|
var import_core = require("@colyseus/core");
|
|
26
|
-
var import_WebSocketClient = require("./WebSocketClient.
|
|
27
|
-
|
|
28
|
+
var import_WebSocketClient = require("./WebSocketClient.cjs");
|
|
29
|
+
var BunWebSockets = class extends import_core.Transport {
|
|
28
30
|
constructor(options = {}) {
|
|
29
31
|
super();
|
|
30
32
|
this.clients = [];
|
|
31
33
|
this.clientWrappers = /* @__PURE__ */ new WeakMap();
|
|
32
34
|
this._originalRawSend = null;
|
|
33
35
|
this.options = {};
|
|
34
|
-
const self = this;
|
|
35
36
|
this.options = options;
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
}
|
|
38
|
+
getExpressApp() {
|
|
39
|
+
if (!this._expressApp) {
|
|
40
|
+
console.warn("");
|
|
41
|
+
console.warn("\u274C Error: Express integration not yet implemented for BunWebSockets");
|
|
42
|
+
console.warn(" Please use bindRouter() instead or consider using uWebSocketsTransport");
|
|
43
|
+
console.warn("");
|
|
38
44
|
}
|
|
45
|
+
return this._expressApp;
|
|
46
|
+
}
|
|
47
|
+
bindRouter(router) {
|
|
48
|
+
this._router = router;
|
|
39
49
|
}
|
|
40
50
|
listen(port, hostname, backlog, listeningListener) {
|
|
41
|
-
|
|
42
|
-
this.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
const self = this;
|
|
52
|
+
this._server = Bun.serve({
|
|
53
|
+
port,
|
|
54
|
+
hostname,
|
|
55
|
+
async fetch(req, server) {
|
|
56
|
+
const url = new URL(req.url);
|
|
57
|
+
if (server.upgrade(req, {
|
|
58
|
+
data: {
|
|
59
|
+
url: url.pathname + url.search,
|
|
60
|
+
searchParams: url.searchParams,
|
|
61
|
+
headers: req.headers,
|
|
62
|
+
remoteAddress: server.requestIP(req)?.address || "unknown"
|
|
63
|
+
}
|
|
64
|
+
})) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (self._router) {
|
|
68
|
+
try {
|
|
69
|
+
const corsHeaders = {
|
|
70
|
+
...import_core.matchMaker.controller.DEFAULT_CORS_HEADERS,
|
|
71
|
+
...import_core.matchMaker.controller.getCorsHeaders(req.headers)
|
|
72
|
+
};
|
|
73
|
+
if (req.method === "OPTIONS") {
|
|
74
|
+
return new Response(null, {
|
|
75
|
+
status: 204,
|
|
76
|
+
headers: corsHeaders
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const response = await self._router.handler(req);
|
|
80
|
+
const headers = new Headers(response.headers);
|
|
81
|
+
Object.entries(corsHeaders).forEach(([key, value]) => {
|
|
82
|
+
if (!headers.has(key)) {
|
|
83
|
+
headers.set(key, value.toString());
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return new Response(response.body, {
|
|
87
|
+
status: response.status,
|
|
88
|
+
statusText: response.statusText,
|
|
89
|
+
headers
|
|
90
|
+
});
|
|
91
|
+
} catch (e) {
|
|
92
|
+
(0, import_core.debugAndPrintError)(e);
|
|
93
|
+
return new Response(JSON.stringify({
|
|
94
|
+
code: e.code,
|
|
95
|
+
error: e.message
|
|
96
|
+
}), {
|
|
97
|
+
status: 500,
|
|
98
|
+
headers: { "Content-Type": "application/json" }
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (self._expressApp) {
|
|
103
|
+
console.warn("Express integration not yet implemented for BunWebSockets");
|
|
104
|
+
}
|
|
105
|
+
return new Response("Not Found", { status: 404 });
|
|
106
|
+
},
|
|
107
|
+
websocket: {
|
|
108
|
+
...this.options,
|
|
109
|
+
async open(ws) {
|
|
110
|
+
await self.onConnection(ws);
|
|
111
|
+
},
|
|
112
|
+
message(ws, message) {
|
|
113
|
+
self.clientWrappers.get(ws)?.emit("message", message);
|
|
114
|
+
},
|
|
115
|
+
close(ws, code, reason) {
|
|
116
|
+
(0, import_core.spliceOne)(self.clients, self.clients.indexOf(ws));
|
|
117
|
+
const clientWrapper = self.clientWrappers.get(ws);
|
|
118
|
+
if (clientWrapper) {
|
|
119
|
+
self.clientWrappers.delete(ws);
|
|
120
|
+
clientWrapper.emit("close", code);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
49
123
|
}
|
|
50
124
|
});
|
|
51
|
-
|
|
125
|
+
listeningListener?.();
|
|
52
126
|
return this;
|
|
53
127
|
}
|
|
54
128
|
shutdown() {
|
|
55
|
-
if (this.
|
|
56
|
-
this.
|
|
57
|
-
this.server.emit("close");
|
|
129
|
+
if (this._server) {
|
|
130
|
+
this._server.stop();
|
|
58
131
|
}
|
|
59
132
|
}
|
|
60
133
|
simulateLatency(milliseconds) {
|
|
@@ -72,9 +145,10 @@ class BunWebSockets extends import_core.Transport {
|
|
|
72
145
|
const wrapper = new import_WebSocketClient.WebSocketWrapper(rawClient);
|
|
73
146
|
this.clients.push(rawClient);
|
|
74
147
|
this.clientWrappers.set(rawClient, wrapper);
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
const
|
|
148
|
+
const url = rawClient.data.url;
|
|
149
|
+
const searchParams = rawClient.data.searchParams;
|
|
150
|
+
const sessionId = searchParams.get("sessionId");
|
|
151
|
+
const processAndRoomId = url.match(/\/[a-zA-Z0-9_\-]+\/([a-zA-Z0-9_\-]+)$/);
|
|
78
152
|
const roomId = processAndRoomId && processAndRoomId[1];
|
|
79
153
|
if (!sessionId && !roomId) {
|
|
80
154
|
const timeout = setTimeout(() => rawClient.close(import_core.CloseCode.NORMAL_CLOSURE), 1e3);
|
|
@@ -84,13 +158,13 @@ class BunWebSockets extends import_core.Transport {
|
|
|
84
158
|
}
|
|
85
159
|
const room = import_core.matchMaker.getLocalRoomById(roomId);
|
|
86
160
|
const client = new import_WebSocketClient.WebSocketClient(sessionId, wrapper);
|
|
87
|
-
const reconnectionToken =
|
|
88
|
-
const skipHandshake =
|
|
161
|
+
const reconnectionToken = searchParams.get("reconnectionToken");
|
|
162
|
+
const skipHandshake = searchParams.has("skipHandshake");
|
|
89
163
|
try {
|
|
90
164
|
await (0, import_core.connectClientToRoom)(room, client, {
|
|
91
|
-
token:
|
|
165
|
+
token: searchParams.get("_authToken") ?? (0, import_core.getBearerToken)(rawClient.data.headers["authorization"]),
|
|
92
166
|
headers: rawClient.data.headers,
|
|
93
|
-
ip: rawClient.data.headers["x-real-ip"] ?? rawClient.data.headers["x-forwarded-for"] ?? rawClient.remoteAddress
|
|
167
|
+
ip: rawClient.data.headers["x-real-ip"] ?? rawClient.data.headers["x-forwarded-for"] ?? rawClient.data.remoteAddress
|
|
94
168
|
}, {
|
|
95
169
|
reconnectionToken,
|
|
96
170
|
skipHandshake
|
|
@@ -100,7 +174,7 @@ class BunWebSockets extends import_core.Transport {
|
|
|
100
174
|
client.error(e.code, e.message, () => rawClient.close());
|
|
101
175
|
}
|
|
102
176
|
}
|
|
103
|
-
}
|
|
177
|
+
};
|
|
104
178
|
// Annotate the CommonJS export names for ESM import in node:
|
|
105
179
|
0 && (module.exports = {
|
|
106
180
|
BunWebSockets
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/BunWebSockets.ts"],
|
|
4
|
-
"sourcesContent": ["// <reference types=\"bun-types\" />\n\n// \"bun-types\" is currently conflicting with \"ws\" types.\n// @ts-ignore\nimport { ServerWebSocket, WebSocketHandler } from 'bun';\
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["// <reference types=\"bun-types\" />\n\n// \"bun-types\" is currently conflicting with \"ws\" types.\n// @ts-ignore\nimport { Server, ServerWebSocket, WebSocketHandler } from 'bun';\nimport type { Application } from \"express\";\nimport type { Router } from '@colyseus/core';\n\nimport { matchMaker, Protocol, Transport, debugAndPrintError, getBearerToken, CloseCode, connectClientToRoom, spliceOne } from '@colyseus/core';\nimport { WebSocketClient, WebSocketWrapper } from './WebSocketClient.ts';\n\n// Bun global is available at runtime\ndeclare const Bun: any;\n\nexport type TransportOptions = Partial<Omit<WebSocketHandler<WebSocketData>, \"message\" | \"open\" | \"drain\" | \"close\" | \"ping\" | \"pong\">>;\n\ninterface WebSocketData {\n url: string;\n searchParams: URLSearchParams;\n headers: Headers;\n remoteAddress: string;\n}\n\nexport class BunWebSockets extends Transport {\n protected clients: ServerWebSocket<WebSocketData>[] = [];\n protected clientWrappers = new WeakMap<ServerWebSocket<WebSocketData>, WebSocketWrapper>();\n\n private _server: Server<WebSocketData> | undefined;\n private _expressApp: Application | undefined;\n private _router: Router | undefined;\n private _originalRawSend: typeof WebSocketClient.prototype.raw | null = null;\n private options: TransportOptions = {};\n\n constructor(options: TransportOptions = {}) {\n super();\n this.options = options;\n }\n\n public getExpressApp(): Application {\n if (!this._expressApp) {\n console.warn(\"\");\n console.warn(\"\u274C Error: Express integration not yet implemented for BunWebSockets\");\n console.warn(\" Please use bindRouter() instead or consider using uWebSocketsTransport\");\n console.warn(\"\");\n }\n return this._expressApp;\n }\n\n public bindRouter(router: Router) {\n this._router = router;\n }\n\n public listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void) {\n const self = this;\n\n this._server = Bun.serve({\n port,\n hostname,\n\n async fetch(req, server) {\n const url = new URL(req.url);\n\n // Try to upgrade to WebSocket\n if (server.upgrade(req, {\n data: {\n url: url.pathname + url.search,\n searchParams: url.searchParams,\n headers: req.headers as Headers,\n remoteAddress: server.requestIP(req)?.address || 'unknown',\n }\n })) {\n return; // WebSocket upgrade successful\n }\n\n // Handle HTTP requests through router\n if (self._router) {\n try {\n // Write CORS headers\n const corsHeaders = {\n ...matchMaker.controller.DEFAULT_CORS_HEADERS,\n ...matchMaker.controller.getCorsHeaders(req.headers)\n };\n\n // Handle OPTIONS requests\n if (req.method === \"OPTIONS\") {\n return new Response(null, {\n status: 204,\n headers: corsHeaders\n });\n }\n\n const response = await self._router.handler(req);\n\n // Add CORS headers to response\n const headers = new Headers(response.headers);\n Object.entries(corsHeaders).forEach(([key, value]) => {\n if (!headers.has(key)) {\n headers.set(key, value.toString());\n }\n });\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers\n });\n\n } catch (e: any) {\n debugAndPrintError(e);\n return new Response(JSON.stringify({\n code: e.code,\n error: e.message\n }), {\n status: 500,\n headers: { 'Content-Type': 'application/json' }\n });\n }\n }\n\n // Fallback to express app if available\n if (self._expressApp) {\n // TODO: Implement express integration for Bun\n console.warn(\"Express integration not yet implemented for BunWebSockets\");\n }\n\n return new Response(\"Not Found\", { status: 404 });\n },\n\n websocket: {\n ...this.options,\n\n async open(ws) {\n await self.onConnection(ws);\n },\n\n message(ws, message) {\n self.clientWrappers.get(ws)?.emit('message', message);\n },\n\n close(ws, code, reason) {\n // remove from client list\n spliceOne(self.clients, self.clients.indexOf(ws));\n\n const clientWrapper = self.clientWrappers.get(ws);\n if (clientWrapper) {\n self.clientWrappers.delete(ws);\n\n // emit 'close' on wrapper\n clientWrapper.emit('close', code);\n }\n },\n }\n });\n\n listeningListener?.();\n\n return this;\n }\n\n public shutdown() {\n if (this._server) {\n this._server.stop();\n }\n }\n\n public simulateLatency(milliseconds: number) {\n if (this._originalRawSend == null) {\n this._originalRawSend = WebSocketClient.prototype.raw;\n }\n\n const originalRawSend = this._originalRawSend;\n WebSocketClient.prototype.raw = milliseconds <= Number.EPSILON ? originalRawSend : function (...args: any[]) {\n let [buf, ...rest] = args;\n buf = Buffer.from(buf);\n // @ts-ignore\n setTimeout(() => originalRawSend.apply(this, [buf, ...rest]), milliseconds);\n };\n }\n\n protected async onConnection(rawClient: ServerWebSocket<WebSocketData>) {\n const wrapper = new WebSocketWrapper(rawClient);\n // keep reference to client and its wrapper\n this.clients.push(rawClient);\n this.clientWrappers.set(rawClient, wrapper);\n\n const url = rawClient.data.url;\n const searchParams = rawClient.data.searchParams;\n\n const sessionId = searchParams.get(\"sessionId\");\n const processAndRoomId = url.match(/\\/[a-zA-Z0-9_\\-]+\\/([a-zA-Z0-9_\\-]+)$/);\n const roomId = processAndRoomId && processAndRoomId[1];\n\n // If sessionId is not provided, allow ping-pong utility.\n if (!sessionId && !roomId) {\n // Disconnect automatically after 1 second if no message is received.\n const timeout = setTimeout(() => rawClient.close(CloseCode.NORMAL_CLOSURE), 1000);\n wrapper.on('message', (_) => rawClient.send(new Uint8Array([Protocol.PING])));\n wrapper.on('close', () => clearTimeout(timeout));\n return;\n }\n\n const room = matchMaker.getLocalRoomById(roomId);\n const client = new WebSocketClient(sessionId, wrapper);\n const reconnectionToken = searchParams.get(\"reconnectionToken\");\n const skipHandshake = searchParams.has(\"skipHandshake\");\n\n try {\n await connectClientToRoom(room, client, {\n token: searchParams.get(\"_authToken\") ?? getBearerToken(rawClient.data.headers['authorization']),\n headers: rawClient.data.headers,\n ip: rawClient.data.headers['x-real-ip'] ?? rawClient.data.headers['x-forwarded-for'] ?? rawClient.data.remoteAddress,\n }, {\n reconnectionToken,\n skipHandshake\n });\n\n } catch (e: any) {\n debugAndPrintError(e);\n\n // send error code to client then terminate\n client.error(e.code, e.message, () => rawClient.close());\n }\n }\n\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/build/BunWebSockets.d.ts
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import { ServerWebSocket, WebSocketHandler } from 'bun';
|
|
2
2
|
import type { Application } from "express";
|
|
3
|
+
import type { Router } from '@colyseus/core';
|
|
3
4
|
import { Transport } from '@colyseus/core';
|
|
4
5
|
import { WebSocketWrapper } from './WebSocketClient.ts';
|
|
5
|
-
export type TransportOptions = Partial<Omit<WebSocketHandler
|
|
6
|
+
export type TransportOptions = Partial<Omit<WebSocketHandler<WebSocketData>, "message" | "open" | "drain" | "close" | "ping" | "pong">>;
|
|
6
7
|
interface WebSocketData {
|
|
7
|
-
url:
|
|
8
|
-
|
|
8
|
+
url: string;
|
|
9
|
+
searchParams: URLSearchParams;
|
|
10
|
+
headers: Headers;
|
|
11
|
+
remoteAddress: string;
|
|
9
12
|
}
|
|
10
13
|
export declare class BunWebSockets extends Transport {
|
|
11
|
-
expressApp: Application;
|
|
12
14
|
protected clients: ServerWebSocket<WebSocketData>[];
|
|
13
15
|
protected clientWrappers: WeakMap<ServerWebSocket<WebSocketData>, WebSocketWrapper>;
|
|
14
|
-
private
|
|
16
|
+
private _server;
|
|
17
|
+
private _expressApp;
|
|
18
|
+
private _router;
|
|
15
19
|
private _originalRawSend;
|
|
16
20
|
private options;
|
|
17
21
|
constructor(options?: TransportOptions);
|
|
22
|
+
getExpressApp(): Application;
|
|
23
|
+
bindRouter(router: Router): void;
|
|
18
24
|
listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void): this;
|
|
19
25
|
shutdown(): void;
|
|
20
26
|
simulateLatency(milliseconds: number): void;
|
package/build/BunWebSockets.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// packages/transport/bun-websockets/src/BunWebSockets.ts
|
|
2
2
|
import "bun";
|
|
3
|
-
import {
|
|
3
|
+
import { matchMaker, Protocol, Transport, debugAndPrintError, getBearerToken, CloseCode, connectClientToRoom, spliceOne } from "@colyseus/core";
|
|
4
4
|
import { WebSocketClient, WebSocketWrapper } from "./WebSocketClient.mjs";
|
|
5
5
|
var BunWebSockets = class extends Transport {
|
|
6
6
|
constructor(options = {}) {
|
|
@@ -9,30 +9,101 @@ var BunWebSockets = class extends Transport {
|
|
|
9
9
|
this.clientWrappers = /* @__PURE__ */ new WeakMap();
|
|
10
10
|
this._originalRawSend = null;
|
|
11
11
|
this.options = {};
|
|
12
|
-
const self = this;
|
|
13
12
|
this.options = options;
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
}
|
|
14
|
+
getExpressApp() {
|
|
15
|
+
if (!this._expressApp) {
|
|
16
|
+
console.warn("");
|
|
17
|
+
console.warn("\u274C Error: Express integration not yet implemented for BunWebSockets");
|
|
18
|
+
console.warn(" Please use bindRouter() instead or consider using uWebSocketsTransport");
|
|
19
|
+
console.warn("");
|
|
16
20
|
}
|
|
21
|
+
return this._expressApp;
|
|
22
|
+
}
|
|
23
|
+
bindRouter(router) {
|
|
24
|
+
this._router = router;
|
|
17
25
|
}
|
|
18
26
|
listen(port, hostname, backlog, listeningListener) {
|
|
19
|
-
|
|
20
|
-
this.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
const self = this;
|
|
28
|
+
this._server = Bun.serve({
|
|
29
|
+
port,
|
|
30
|
+
hostname,
|
|
31
|
+
async fetch(req, server) {
|
|
32
|
+
const url = new URL(req.url);
|
|
33
|
+
if (server.upgrade(req, {
|
|
34
|
+
data: {
|
|
35
|
+
url: url.pathname + url.search,
|
|
36
|
+
searchParams: url.searchParams,
|
|
37
|
+
headers: req.headers,
|
|
38
|
+
remoteAddress: server.requestIP(req)?.address || "unknown"
|
|
39
|
+
}
|
|
40
|
+
})) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (self._router) {
|
|
44
|
+
try {
|
|
45
|
+
const corsHeaders = {
|
|
46
|
+
...matchMaker.controller.DEFAULT_CORS_HEADERS,
|
|
47
|
+
...matchMaker.controller.getCorsHeaders(req.headers)
|
|
48
|
+
};
|
|
49
|
+
if (req.method === "OPTIONS") {
|
|
50
|
+
return new Response(null, {
|
|
51
|
+
status: 204,
|
|
52
|
+
headers: corsHeaders
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const response = await self._router.handler(req);
|
|
56
|
+
const headers = new Headers(response.headers);
|
|
57
|
+
Object.entries(corsHeaders).forEach(([key, value]) => {
|
|
58
|
+
if (!headers.has(key)) {
|
|
59
|
+
headers.set(key, value.toString());
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return new Response(response.body, {
|
|
63
|
+
status: response.status,
|
|
64
|
+
statusText: response.statusText,
|
|
65
|
+
headers
|
|
66
|
+
});
|
|
67
|
+
} catch (e) {
|
|
68
|
+
debugAndPrintError(e);
|
|
69
|
+
return new Response(JSON.stringify({
|
|
70
|
+
code: e.code,
|
|
71
|
+
error: e.message
|
|
72
|
+
}), {
|
|
73
|
+
status: 500,
|
|
74
|
+
headers: { "Content-Type": "application/json" }
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (self._expressApp) {
|
|
79
|
+
console.warn("Express integration not yet implemented for BunWebSockets");
|
|
80
|
+
}
|
|
81
|
+
return new Response("Not Found", { status: 404 });
|
|
82
|
+
},
|
|
83
|
+
websocket: {
|
|
84
|
+
...this.options,
|
|
85
|
+
async open(ws) {
|
|
86
|
+
await self.onConnection(ws);
|
|
87
|
+
},
|
|
88
|
+
message(ws, message) {
|
|
89
|
+
self.clientWrappers.get(ws)?.emit("message", message);
|
|
90
|
+
},
|
|
91
|
+
close(ws, code, reason) {
|
|
92
|
+
spliceOne(self.clients, self.clients.indexOf(ws));
|
|
93
|
+
const clientWrapper = self.clientWrappers.get(ws);
|
|
94
|
+
if (clientWrapper) {
|
|
95
|
+
self.clientWrappers.delete(ws);
|
|
96
|
+
clientWrapper.emit("close", code);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
27
99
|
}
|
|
28
100
|
});
|
|
29
|
-
|
|
101
|
+
listeningListener?.();
|
|
30
102
|
return this;
|
|
31
103
|
}
|
|
32
104
|
shutdown() {
|
|
33
|
-
if (this.
|
|
34
|
-
this.
|
|
35
|
-
this.server.emit("close");
|
|
105
|
+
if (this._server) {
|
|
106
|
+
this._server.stop();
|
|
36
107
|
}
|
|
37
108
|
}
|
|
38
109
|
simulateLatency(milliseconds) {
|
|
@@ -50,9 +121,10 @@ var BunWebSockets = class extends Transport {
|
|
|
50
121
|
const wrapper = new WebSocketWrapper(rawClient);
|
|
51
122
|
this.clients.push(rawClient);
|
|
52
123
|
this.clientWrappers.set(rawClient, wrapper);
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const
|
|
124
|
+
const url = rawClient.data.url;
|
|
125
|
+
const searchParams = rawClient.data.searchParams;
|
|
126
|
+
const sessionId = searchParams.get("sessionId");
|
|
127
|
+
const processAndRoomId = url.match(/\/[a-zA-Z0-9_\-]+\/([a-zA-Z0-9_\-]+)$/);
|
|
56
128
|
const roomId = processAndRoomId && processAndRoomId[1];
|
|
57
129
|
if (!sessionId && !roomId) {
|
|
58
130
|
const timeout = setTimeout(() => rawClient.close(CloseCode.NORMAL_CLOSURE), 1e3);
|
|
@@ -62,13 +134,13 @@ var BunWebSockets = class extends Transport {
|
|
|
62
134
|
}
|
|
63
135
|
const room = matchMaker.getLocalRoomById(roomId);
|
|
64
136
|
const client = new WebSocketClient(sessionId, wrapper);
|
|
65
|
-
const reconnectionToken =
|
|
66
|
-
const skipHandshake =
|
|
137
|
+
const reconnectionToken = searchParams.get("reconnectionToken");
|
|
138
|
+
const skipHandshake = searchParams.has("skipHandshake");
|
|
67
139
|
try {
|
|
68
140
|
await connectClientToRoom(room, client, {
|
|
69
|
-
token:
|
|
141
|
+
token: searchParams.get("_authToken") ?? getBearerToken(rawClient.data.headers["authorization"]),
|
|
70
142
|
headers: rawClient.data.headers,
|
|
71
|
-
ip: rawClient.data.headers["x-real-ip"] ?? rawClient.data.headers["x-forwarded-for"] ?? rawClient.remoteAddress
|
|
143
|
+
ip: rawClient.data.headers["x-real-ip"] ?? rawClient.data.headers["x-forwarded-for"] ?? rawClient.data.remoteAddress
|
|
72
144
|
}, {
|
|
73
145
|
reconnectionToken,
|
|
74
146
|
skipHandshake
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/BunWebSockets.ts"],
|
|
4
|
-
"sourcesContent": ["// <reference types=\"bun-types\" />\n\n// \"bun-types\" is currently conflicting with \"ws\" types.\n// @ts-ignore\nimport { ServerWebSocket, WebSocketHandler } from 'bun';\
|
|
5
|
-
"mappings": ";AAIA,
|
|
4
|
+
"sourcesContent": ["// <reference types=\"bun-types\" />\n\n// \"bun-types\" is currently conflicting with \"ws\" types.\n// @ts-ignore\nimport { Server, ServerWebSocket, WebSocketHandler } from 'bun';\nimport type { Application } from \"express\";\nimport type { Router } from '@colyseus/core';\n\nimport { matchMaker, Protocol, Transport, debugAndPrintError, getBearerToken, CloseCode, connectClientToRoom, spliceOne } from '@colyseus/core';\nimport { WebSocketClient, WebSocketWrapper } from './WebSocketClient.ts';\n\n// Bun global is available at runtime\ndeclare const Bun: any;\n\nexport type TransportOptions = Partial<Omit<WebSocketHandler<WebSocketData>, \"message\" | \"open\" | \"drain\" | \"close\" | \"ping\" | \"pong\">>;\n\ninterface WebSocketData {\n url: string;\n searchParams: URLSearchParams;\n headers: Headers;\n remoteAddress: string;\n}\n\nexport class BunWebSockets extends Transport {\n protected clients: ServerWebSocket<WebSocketData>[] = [];\n protected clientWrappers = new WeakMap<ServerWebSocket<WebSocketData>, WebSocketWrapper>();\n\n private _server: Server<WebSocketData> | undefined;\n private _expressApp: Application | undefined;\n private _router: Router | undefined;\n private _originalRawSend: typeof WebSocketClient.prototype.raw | null = null;\n private options: TransportOptions = {};\n\n constructor(options: TransportOptions = {}) {\n super();\n this.options = options;\n }\n\n public getExpressApp(): Application {\n if (!this._expressApp) {\n console.warn(\"\");\n console.warn(\"\u274C Error: Express integration not yet implemented for BunWebSockets\");\n console.warn(\" Please use bindRouter() instead or consider using uWebSocketsTransport\");\n console.warn(\"\");\n }\n return this._expressApp;\n }\n\n public bindRouter(router: Router) {\n this._router = router;\n }\n\n public listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void) {\n const self = this;\n\n this._server = Bun.serve({\n port,\n hostname,\n\n async fetch(req, server) {\n const url = new URL(req.url);\n\n // Try to upgrade to WebSocket\n if (server.upgrade(req, {\n data: {\n url: url.pathname + url.search,\n searchParams: url.searchParams,\n headers: req.headers as Headers,\n remoteAddress: server.requestIP(req)?.address || 'unknown',\n }\n })) {\n return; // WebSocket upgrade successful\n }\n\n // Handle HTTP requests through router\n if (self._router) {\n try {\n // Write CORS headers\n const corsHeaders = {\n ...matchMaker.controller.DEFAULT_CORS_HEADERS,\n ...matchMaker.controller.getCorsHeaders(req.headers)\n };\n\n // Handle OPTIONS requests\n if (req.method === \"OPTIONS\") {\n return new Response(null, {\n status: 204,\n headers: corsHeaders\n });\n }\n\n const response = await self._router.handler(req);\n\n // Add CORS headers to response\n const headers = new Headers(response.headers);\n Object.entries(corsHeaders).forEach(([key, value]) => {\n if (!headers.has(key)) {\n headers.set(key, value.toString());\n }\n });\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers\n });\n\n } catch (e: any) {\n debugAndPrintError(e);\n return new Response(JSON.stringify({\n code: e.code,\n error: e.message\n }), {\n status: 500,\n headers: { 'Content-Type': 'application/json' }\n });\n }\n }\n\n // Fallback to express app if available\n if (self._expressApp) {\n // TODO: Implement express integration for Bun\n console.warn(\"Express integration not yet implemented for BunWebSockets\");\n }\n\n return new Response(\"Not Found\", { status: 404 });\n },\n\n websocket: {\n ...this.options,\n\n async open(ws) {\n await self.onConnection(ws);\n },\n\n message(ws, message) {\n self.clientWrappers.get(ws)?.emit('message', message);\n },\n\n close(ws, code, reason) {\n // remove from client list\n spliceOne(self.clients, self.clients.indexOf(ws));\n\n const clientWrapper = self.clientWrappers.get(ws);\n if (clientWrapper) {\n self.clientWrappers.delete(ws);\n\n // emit 'close' on wrapper\n clientWrapper.emit('close', code);\n }\n },\n }\n });\n\n listeningListener?.();\n\n return this;\n }\n\n public shutdown() {\n if (this._server) {\n this._server.stop();\n }\n }\n\n public simulateLatency(milliseconds: number) {\n if (this._originalRawSend == null) {\n this._originalRawSend = WebSocketClient.prototype.raw;\n }\n\n const originalRawSend = this._originalRawSend;\n WebSocketClient.prototype.raw = milliseconds <= Number.EPSILON ? originalRawSend : function (...args: any[]) {\n let [buf, ...rest] = args;\n buf = Buffer.from(buf);\n // @ts-ignore\n setTimeout(() => originalRawSend.apply(this, [buf, ...rest]), milliseconds);\n };\n }\n\n protected async onConnection(rawClient: ServerWebSocket<WebSocketData>) {\n const wrapper = new WebSocketWrapper(rawClient);\n // keep reference to client and its wrapper\n this.clients.push(rawClient);\n this.clientWrappers.set(rawClient, wrapper);\n\n const url = rawClient.data.url;\n const searchParams = rawClient.data.searchParams;\n\n const sessionId = searchParams.get(\"sessionId\");\n const processAndRoomId = url.match(/\\/[a-zA-Z0-9_\\-]+\\/([a-zA-Z0-9_\\-]+)$/);\n const roomId = processAndRoomId && processAndRoomId[1];\n\n // If sessionId is not provided, allow ping-pong utility.\n if (!sessionId && !roomId) {\n // Disconnect automatically after 1 second if no message is received.\n const timeout = setTimeout(() => rawClient.close(CloseCode.NORMAL_CLOSURE), 1000);\n wrapper.on('message', (_) => rawClient.send(new Uint8Array([Protocol.PING])));\n wrapper.on('close', () => clearTimeout(timeout));\n return;\n }\n\n const room = matchMaker.getLocalRoomById(roomId);\n const client = new WebSocketClient(sessionId, wrapper);\n const reconnectionToken = searchParams.get(\"reconnectionToken\");\n const skipHandshake = searchParams.has(\"skipHandshake\");\n\n try {\n await connectClientToRoom(room, client, {\n token: searchParams.get(\"_authToken\") ?? getBearerToken(rawClient.data.headers['authorization']),\n headers: rawClient.data.headers,\n ip: rawClient.data.headers['x-real-ip'] ?? rawClient.data.headers['x-forwarded-for'] ?? rawClient.data.remoteAddress,\n }, {\n reconnectionToken,\n skipHandshake\n });\n\n } catch (e: any) {\n debugAndPrintError(e);\n\n // send error code to client then terminate\n client.error(e.code, e.message, () => rawClient.close());\n }\n }\n\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -26,6 +26,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
mod
|
|
27
27
|
));
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// packages/transport/bun-websockets/src/WebSocketClient.ts
|
|
29
31
|
var WebSocketClient_exports = {};
|
|
30
32
|
__export(WebSocketClient_exports, {
|
|
31
33
|
WebSocketClient: () => WebSocketClient,
|
|
@@ -34,13 +36,13 @@ __export(WebSocketClient_exports, {
|
|
|
34
36
|
module.exports = __toCommonJS(WebSocketClient_exports);
|
|
35
37
|
var import_events = __toESM(require("events"), 1);
|
|
36
38
|
var import_core = require("@colyseus/core");
|
|
37
|
-
|
|
39
|
+
var WebSocketWrapper = class extends import_events.default {
|
|
38
40
|
constructor(ws) {
|
|
39
41
|
super();
|
|
40
42
|
this.ws = ws;
|
|
41
43
|
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
+
};
|
|
45
|
+
var WebSocketClient = class {
|
|
44
46
|
constructor(id, ref) {
|
|
45
47
|
this.state = import_core.ClientState.JOINING;
|
|
46
48
|
this._enqueuedMessages = [];
|
|
@@ -102,7 +104,7 @@ class WebSocketClient {
|
|
|
102
104
|
toJSON() {
|
|
103
105
|
return { sessionId: this.sessionId, readyState: this.readyState };
|
|
104
106
|
}
|
|
105
|
-
}
|
|
107
|
+
};
|
|
106
108
|
// Annotate the CommonJS export names for ESM import in node:
|
|
107
109
|
0 && (module.exports = {
|
|
108
110
|
WebSocketClient,
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/WebSocketClient.ts"],
|
|
4
4
|
"sourcesContent": ["// <reference types=\"bun-types\" />\n\n// \"bun-types\" is currently conflicting with \"ws\" types.\n// @ts-ignore\nimport type { ServerWebSocket } from 'bun';\nimport EventEmitter from 'events';\n\nimport { Protocol, type Client, type ClientPrivate, ClientState, type ISendOptions, getMessageBytes, logger, debugMessage } from '@colyseus/core';\n\nexport class WebSocketWrapper extends EventEmitter {\n public ws: ServerWebSocket<any>;\n\n constructor(ws: ServerWebSocket<any>) {\n super();\n this.ws = ws;\n }\n}\n\nexport class WebSocketClient implements Client, ClientPrivate {\n '~messages': any;\n\n public id: string;\n public ref: WebSocketWrapper;\n\n public sessionId: string;\n public state: ClientState = ClientState.JOINING;\n public reconnectionToken: string;\n\n public _enqueuedMessages: any[] = [];\n public _afterNextPatchQueue;\n public _reconnectionToken: string;\n public _joinedAt: number;\n\n constructor(id: string, ref: WebSocketWrapper,) {\n this.id = this.sessionId = id;\n this.ref = ref;\n }\n\n public sendBytes(type: string | number, bytes: Buffer | Uint8Array, options?: ISendOptions) {\n debugMessage(\"send bytes(to %s): '%s' -> %j\", this.sessionId, type, bytes);\n\n this.enqueueRaw(\n getMessageBytes.raw(Protocol.ROOM_DATA_BYTES, type, undefined, bytes),\n options,\n );\n }\n\n public send(messageOrType: any, messageOrOptions?: any | ISendOptions, options?: ISendOptions) {\n debugMessage(\"send(to %s): '%s' -> %j\", this.sessionId, messageOrType, messageOrOptions);\n\n this.enqueueRaw(\n getMessageBytes.raw(Protocol.ROOM_DATA, messageOrType, messageOrOptions),\n options,\n );\n }\n\n public enqueueRaw(data: Uint8Array | Buffer, options?: ISendOptions) {\n // use room's afterNextPatch queue\n if (options?.afterNextPatch) {\n this._afterNextPatchQueue.push([this, [data]]);\n return;\n }\n\n if (this.state === ClientState.JOINING) {\n // sending messages during `onJoin`.\n // - the client-side cannot register \"onMessage\" callbacks at this point.\n // - enqueue the messages to be send after JOIN_ROOM message has been sent\n // - create a new buffer for enqueued messages, as the underlying buffer might be modified\n this._enqueuedMessages.push(data);\n return;\n }\n\n this.raw(data, options);\n }\n\n public raw(data: Uint8Array | Buffer, options?: ISendOptions, cb?: (err?: Error) => void) {\n // skip if client not open\n\n // WebSocket is globally available on Bun runtime\n // @ts-ignore\n if (this.ref.ws.readyState !== WebSocket.OPEN) {\n return;\n }\n\n // FIXME: can we avoid creating a new buffer here?\n this.ref.ws.sendBinary(data);\n }\n\n public error(code: number, message: string = '', cb?: (err?: Error) => void) {\n this.raw(getMessageBytes[Protocol.ERROR](code, message));\n\n if (cb) {\n // (same API as \"ws\" transport)\n setTimeout(cb, 1);\n }\n }\n\n get readyState() {\n return this.ref.ws.readyState;\n }\n\n public leave(code?: number, data?: string) {\n this.ref.ws.close(code, data);\n }\n\n public close(code?: number, data?: string) {\n logger.warn('DEPRECATION WARNING: use client.leave() instead of client.close()');\n try {\n throw new Error();\n } catch (e: any) {\n logger.info(e.stack);\n }\n this.leave(code, data);\n }\n\n public toJSON() {\n return { sessionId: this.sessionId, readyState: this.readyState };\n }\n}\n"],
|
|
5
|
-
"mappings": "
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,oBAAyB;AAEzB,kBAAiI;AAE1H,IAAM,mBAAN,cAA+B,cAAAA,QAAa;AAAA,EAGjD,YAAY,IAA0B;AACpC,UAAM;AACN,SAAK,KAAK;AAAA,EACZ;AACF;AAEO,IAAM,kBAAN,MAAuD;AAAA,EAe5D,YAAY,IAAY,KAAwB;AARhD,SAAO,QAAqB,wBAAY;AAGxC,SAAO,oBAA2B,CAAC;AAMjC,SAAK,KAAK,KAAK,YAAY;AAC3B,SAAK,MAAM;AAAA,EACb;AAAA,EAEO,UAAU,MAAuB,OAA4B,SAAwB;AAC1F,kCAAa,iCAAiC,KAAK,WAAW,MAAM,KAAK;AAEzE,SAAK;AAAA,MACH,4BAAgB,IAAI,qBAAS,iBAAiB,MAAM,QAAW,KAAK;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AAAA,EAEO,KAAK,eAAoB,kBAAuC,SAAwB;AAC7F,kCAAa,2BAA2B,KAAK,WAAW,eAAe,gBAAgB;AAEvF,SAAK;AAAA,MACH,4BAAgB,IAAI,qBAAS,WAAW,eAAe,gBAAgB;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AAAA,EAEO,WAAW,MAA2B,SAAwB;AAEnE,QAAI,SAAS,gBAAgB;AAC3B,WAAK,qBAAqB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC7C;AAAA,IACF;AAEA,QAAI,KAAK,UAAU,wBAAY,SAAS;AAKtC,WAAK,kBAAkB,KAAK,IAAI;AAChC;AAAA,IACF;AAEA,SAAK,IAAI,MAAM,OAAO;AAAA,EACxB;AAAA,EAEO,IAAI,MAA2B,SAAwB,IAA4B;AAKxF,QAAI,KAAK,IAAI,GAAG,eAAe,UAAU,MAAM;AAC7C;AAAA,IACF;AAGA,SAAK,IAAI,GAAG,WAAW,IAAI;AAAA,EAC7B;AAAA,EAEO,MAAM,MAAc,UAAkB,IAAI,IAA4B;AAC3E,SAAK,IAAI,4BAAgB,qBAAS,KAAK,EAAE,MAAM,OAAO,CAAC;AAEvD,QAAI,IAAI;AAEN,iBAAW,IAAI,CAAC;AAAA,IAClB;AAAA,EACF;AAAA,EAEA,IAAI,aAAa;AACf,WAAO,KAAK,IAAI,GAAG;AAAA,EACrB;AAAA,EAEO,MAAM,MAAe,MAAe;AACzC,SAAK,IAAI,GAAG,MAAM,MAAM,IAAI;AAAA,EAC9B;AAAA,EAEO,MAAM,MAAe,MAAe;AACzC,uBAAO,KAAK,mEAAmE;AAC/E,QAAI;AACF,YAAM,IAAI,MAAM;AAAA,IAClB,SAAS,GAAQ;AACf,yBAAO,KAAK,EAAE,KAAK;AAAA,IACrB;AACA,SAAK,MAAM,MAAM,IAAI;AAAA,EACvB;AAAA,EAEO,SAAS;AACd,WAAO,EAAE,WAAW,KAAK,WAAW,YAAY,KAAK,WAAW;AAAA,EAClE;AACF;",
|
|
6
6
|
"names": ["EventEmitter"]
|
|
7
7
|
}
|
package/build/index.cjs
CHANGED
|
@@ -16,14 +16,16 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// packages/transport/bun-websockets/src/index.ts
|
|
19
21
|
var index_exports = {};
|
|
20
22
|
__export(index_exports, {
|
|
21
23
|
BunWebSockets: () => import_BunWebSockets.BunWebSockets,
|
|
22
24
|
WebSocketClient: () => import_WebSocketClient.WebSocketClient
|
|
23
25
|
});
|
|
24
26
|
module.exports = __toCommonJS(index_exports);
|
|
25
|
-
var import_WebSocketClient = require("./WebSocketClient.
|
|
26
|
-
var import_BunWebSockets = require("./BunWebSockets.
|
|
27
|
+
var import_WebSocketClient = require("./WebSocketClient.cjs");
|
|
28
|
+
var import_BunWebSockets = require("./BunWebSockets.cjs");
|
|
27
29
|
// Annotate the CommonJS export names for ESM import in node:
|
|
28
30
|
0 && (module.exports = {
|
|
29
31
|
BunWebSockets,
|
package/build/index.cjs.map
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
4
|
"sourcesContent": ["export { WebSocketClient } from './WebSocketClient.ts';\nexport { BunWebSockets, type TransportOptions } from './BunWebSockets.ts';"],
|
|
5
|
-
"mappings": "
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAAgC;AAChC,2BAAqD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colyseus/bun-websockets",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"input": "./src/index.ts",
|
|
6
6
|
"main": "./build/index.cjs",
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@colyseus/core": "^0.17.
|
|
24
|
+
"@colyseus/core": "^0.17.22"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"bun-types": "^1.2.0",
|
|
28
|
-
"@colyseus/core": "^0.17.
|
|
28
|
+
"@colyseus/core": "^0.17.22"
|
|
29
29
|
},
|
|
30
30
|
"author": "Endel Dreyer",
|
|
31
31
|
"license": "MIT",
|
package/src/BunWebSockets.ts
CHANGED
|
@@ -2,100 +2,161 @@
|
|
|
2
2
|
|
|
3
3
|
// "bun-types" is currently conflicting with "ws" types.
|
|
4
4
|
// @ts-ignore
|
|
5
|
-
import { ServerWebSocket, WebSocketHandler } from 'bun';
|
|
5
|
+
import { Server, ServerWebSocket, WebSocketHandler } from 'bun';
|
|
6
|
+
import express, { type Application } from "express";
|
|
7
|
+
import type { Router } from '@colyseus/core';
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
import type { Application, Request, Response } from "express";
|
|
9
|
-
|
|
10
|
-
import { HttpServerMock, matchMaker, Protocol, Transport, debugAndPrintError, getBearerToken, CloseCode, connectClientToRoom } from '@colyseus/core';
|
|
9
|
+
import { matchMaker, Protocol, Transport, debugAndPrintError, getBearerToken, CloseCode, connectClientToRoom, spliceOne } from '@colyseus/core';
|
|
11
10
|
import { WebSocketClient, WebSocketWrapper } from './WebSocketClient.ts';
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
// Bun global is available at runtime
|
|
13
|
+
declare const Bun: any;
|
|
14
|
+
|
|
15
|
+
export type TransportOptions = Partial<Omit<WebSocketHandler<WebSocketData>, "message" | "open" | "drain" | "close" | "ping" | "pong">>;
|
|
14
16
|
|
|
15
17
|
interface WebSocketData {
|
|
16
|
-
url:
|
|
17
|
-
|
|
18
|
+
url: string;
|
|
19
|
+
searchParams: URLSearchParams;
|
|
20
|
+
headers: Headers;
|
|
21
|
+
remoteAddress: string;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
export class BunWebSockets extends Transport {
|
|
21
|
-
public expressApp: Application;
|
|
22
|
-
|
|
23
25
|
protected clients: ServerWebSocket<WebSocketData>[] = [];
|
|
24
26
|
protected clientWrappers = new WeakMap<ServerWebSocket<WebSocketData>, WebSocketWrapper>();
|
|
25
27
|
|
|
26
|
-
private
|
|
28
|
+
private _server: Server<WebSocketData> | undefined;
|
|
29
|
+
private _expressApp: Application | undefined;
|
|
30
|
+
private _router: Router | undefined;
|
|
27
31
|
private _originalRawSend: typeof WebSocketClient.prototype.raw | null = null;
|
|
28
32
|
private options: TransportOptions = {};
|
|
29
33
|
|
|
30
34
|
constructor(options: TransportOptions = {}) {
|
|
31
35
|
super();
|
|
32
|
-
|
|
33
|
-
const self = this;
|
|
34
|
-
|
|
35
36
|
this.options = options;
|
|
37
|
+
}
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// async open(ws) {
|
|
42
|
-
// await self.onConnection(ws);
|
|
43
|
-
// },
|
|
44
|
-
|
|
45
|
-
// message(ws, message) {
|
|
46
|
-
// self.clientWrappers.get(ws)?.emit('message', message);
|
|
47
|
-
// },
|
|
48
|
-
|
|
49
|
-
// close(ws, code, reason) {
|
|
50
|
-
// // remove from client list
|
|
51
|
-
// spliceOne(self.clients, self.clients.indexOf(ws));
|
|
52
|
-
|
|
53
|
-
// const clientWrapper = self.clientWrappers.get(ws);
|
|
54
|
-
// if (clientWrapper) {
|
|
55
|
-
// self.clientWrappers.delete(ws);
|
|
56
|
-
|
|
57
|
-
// // emit 'close' on wrapper
|
|
58
|
-
// clientWrapper.emit('close', code);
|
|
59
|
-
// }
|
|
60
|
-
// },
|
|
61
|
-
// }
|
|
62
|
-
// });
|
|
63
|
-
|
|
64
|
-
// Adding a mock object for Transport.server
|
|
65
|
-
if (!this.server) {
|
|
66
|
-
// @ts-ignore
|
|
67
|
-
this.server = new HttpServerMock();
|
|
39
|
+
public getExpressApp(): Application {
|
|
40
|
+
if (!this._expressApp) {
|
|
41
|
+
this._expressApp = express();
|
|
68
42
|
}
|
|
43
|
+
return this._expressApp;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public bindRouter(router: Router) {
|
|
47
|
+
this._router = router;
|
|
69
48
|
}
|
|
70
49
|
|
|
71
50
|
public listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
this.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
51
|
+
const self = this;
|
|
52
|
+
|
|
53
|
+
this._server = Bun.serve({
|
|
54
|
+
port,
|
|
55
|
+
hostname,
|
|
56
|
+
|
|
57
|
+
async fetch(req, server) {
|
|
58
|
+
const url = new URL(req.url);
|
|
59
|
+
|
|
60
|
+
// Try to upgrade to WebSocket
|
|
61
|
+
if (server.upgrade(req, {
|
|
62
|
+
data: {
|
|
63
|
+
url: url.pathname + url.search,
|
|
64
|
+
searchParams: url.searchParams,
|
|
65
|
+
headers: req.headers as Headers,
|
|
66
|
+
remoteAddress: server.requestIP(req)?.address || 'unknown',
|
|
67
|
+
}
|
|
68
|
+
})) {
|
|
69
|
+
return; // WebSocket upgrade successful
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Handle HTTP requests through router
|
|
73
|
+
if (self._router) {
|
|
74
|
+
try {
|
|
75
|
+
// Write CORS headers
|
|
76
|
+
const corsHeaders = {
|
|
77
|
+
...matchMaker.controller.DEFAULT_CORS_HEADERS,
|
|
78
|
+
...matchMaker.controller.getCorsHeaders(req.headers)
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Handle OPTIONS requests
|
|
82
|
+
if (req.method === "OPTIONS") {
|
|
83
|
+
return new Response(null, {
|
|
84
|
+
status: 204,
|
|
85
|
+
headers: corsHeaders
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const response = await self._router.handler(req);
|
|
90
|
+
|
|
91
|
+
// Add CORS headers to response
|
|
92
|
+
const headers = new Headers(response.headers);
|
|
93
|
+
Object.entries(corsHeaders).forEach(([key, value]) => {
|
|
94
|
+
if (!headers.has(key)) {
|
|
95
|
+
headers.set(key, value.toString());
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return new Response(response.body, {
|
|
100
|
+
status: response.status,
|
|
101
|
+
statusText: response.statusText,
|
|
102
|
+
headers
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
} catch (e: any) {
|
|
106
|
+
debugAndPrintError(e);
|
|
107
|
+
return new Response(JSON.stringify({
|
|
108
|
+
code: e.code,
|
|
109
|
+
error: e.message
|
|
110
|
+
}), {
|
|
111
|
+
status: 500,
|
|
112
|
+
headers: { 'Content-Type': 'application/json' }
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Fallback to express app if available
|
|
118
|
+
if (self._expressApp) {
|
|
119
|
+
// TODO: Implement express integration for Bun
|
|
120
|
+
console.warn("Express integration not yet implemented for BunWebSockets");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return new Response("Not Found", { status: 404 });
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
websocket: {
|
|
127
|
+
...this.options,
|
|
128
|
+
|
|
129
|
+
async open(ws) {
|
|
130
|
+
await self.onConnection(ws);
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
message(ws, message) {
|
|
134
|
+
self.clientWrappers.get(ws)?.emit('message', message);
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
close(ws, code, reason) {
|
|
138
|
+
// remove from client list
|
|
139
|
+
spliceOne(self.clients, self.clients.indexOf(ws));
|
|
140
|
+
|
|
141
|
+
const clientWrapper = self.clientWrappers.get(ws);
|
|
142
|
+
if (clientWrapper) {
|
|
143
|
+
self.clientWrappers.delete(ws);
|
|
144
|
+
|
|
145
|
+
// emit 'close' on wrapper
|
|
146
|
+
clientWrapper.emit('close', code);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
83
149
|
}
|
|
84
150
|
});
|
|
85
151
|
|
|
86
|
-
|
|
87
|
-
// @ts-ignore
|
|
88
|
-
this.server.emit("listening");
|
|
152
|
+
listeningListener?.();
|
|
89
153
|
|
|
90
154
|
return this;
|
|
91
155
|
}
|
|
92
156
|
|
|
93
157
|
public shutdown() {
|
|
94
|
-
if (this.
|
|
95
|
-
this.
|
|
96
|
-
|
|
97
|
-
// @ts-ignore
|
|
98
|
-
this.server.emit("close"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458
|
|
158
|
+
if (this._server) {
|
|
159
|
+
this._server.stop();
|
|
99
160
|
}
|
|
100
161
|
}
|
|
101
162
|
|
|
@@ -119,10 +180,11 @@ export class BunWebSockets extends Transport {
|
|
|
119
180
|
this.clients.push(rawClient);
|
|
120
181
|
this.clientWrappers.set(rawClient, wrapper);
|
|
121
182
|
|
|
122
|
-
const
|
|
183
|
+
const url = rawClient.data.url;
|
|
184
|
+
const searchParams = rawClient.data.searchParams;
|
|
123
185
|
|
|
124
|
-
const sessionId =
|
|
125
|
-
const processAndRoomId =
|
|
186
|
+
const sessionId = searchParams.get("sessionId");
|
|
187
|
+
const processAndRoomId = url.match(/\/[a-zA-Z0-9_\-]+\/([a-zA-Z0-9_\-]+)$/);
|
|
126
188
|
const roomId = processAndRoomId && processAndRoomId[1];
|
|
127
189
|
|
|
128
190
|
// If sessionId is not provided, allow ping-pong utility.
|
|
@@ -136,14 +198,14 @@ export class BunWebSockets extends Transport {
|
|
|
136
198
|
|
|
137
199
|
const room = matchMaker.getLocalRoomById(roomId);
|
|
138
200
|
const client = new WebSocketClient(sessionId, wrapper);
|
|
139
|
-
const reconnectionToken =
|
|
140
|
-
const skipHandshake =
|
|
201
|
+
const reconnectionToken = searchParams.get("reconnectionToken");
|
|
202
|
+
const skipHandshake = searchParams.has("skipHandshake");
|
|
141
203
|
|
|
142
204
|
try {
|
|
143
205
|
await connectClientToRoom(room, client, {
|
|
144
|
-
token:
|
|
206
|
+
token: searchParams.get("_authToken") ?? getBearerToken(rawClient.data.headers['authorization']),
|
|
145
207
|
headers: rawClient.data.headers,
|
|
146
|
-
ip: rawClient.data.headers['x-real-ip'] ?? rawClient.data.headers['x-forwarded-for'] ?? rawClient.remoteAddress,
|
|
208
|
+
ip: rawClient.data.headers['x-real-ip'] ?? rawClient.data.headers['x-forwarded-for'] ?? rawClient.data.remoteAddress,
|
|
147
209
|
}, {
|
|
148
210
|
reconnectionToken,
|
|
149
211
|
skipHandshake
|