@colyseus/bun-websockets 0.16.0-preview.8 → 0.16.0

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.
@@ -138,10 +138,8 @@ class BunWebSockets extends import_core.Transport {
138
138
  break;
139
139
  }
140
140
  case "GET": {
141
- const matchedParams = req.path.match(import_core.matchMaker.controller.allowedRoomNameChars);
142
- const roomName = matchedParams && matchedParams.length > 1 ? matchedParams[matchedParams.length - 1] : "";
143
141
  writeHeaders(req, res);
144
- res.json(await import_core.matchMaker.controller.getAvailableRooms(roomName || ""));
142
+ res.status(404).end();
145
143
  break;
146
144
  }
147
145
  case "POST": {
@@ -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';\n\nimport type http from 'http';\nimport bunExpress from 'bun-serve-express';\nimport type { Application, Request, Response } from \"express\";\n\nimport { HttpServerMock, matchMaker, Transport, debugAndPrintError, spliceOne, ServerError, getBearerToken } from '@colyseus/core';\nimport { WebSocketClient, WebSocketWrapper } from './WebSocketClient.js';\n\nexport type TransportOptions = Partial<Omit<WebSocketHandler, \"message\" | \"open\" | \"drain\" | \"close\" | \"ping\" | \"pong\">>;\n\ninterface WebSocketData {\n url: URL;\n headers: any;\n}\n\nexport class BunWebSockets extends Transport {\n public expressApp: Application;\n\n protected clients: ServerWebSocket<WebSocketData>[] = [];\n protected clientWrappers = new WeakMap<ServerWebSocket<WebSocketData>, WebSocketWrapper>();\n\n private _listening: any;\n private _originalRawSend: typeof WebSocketClient.prototype.raw | null = null;\n\n constructor(private options: TransportOptions = {}) {\n super();\n\n const self = this;\n\n this.expressApp = bunExpress({\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 // Adding a mock object for Transport.server\n if (!this.server) {\n // @ts-ignore\n this.server = new HttpServerMock();\n }\n }\n\n public listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void) {\n this._listening = this.expressApp.listen(port, listeningListener);\n\n this.expressApp.use(`/${matchMaker.controller.matchmakeRoute}`, async (req, res) => {\n try {\n await this.handleMatchMakeRequest(req, res);\n } catch (e) {\n res.status(500).json({\n code: e.code,\n error: e.message\n });\n }\n });\n\n // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\n // @ts-ignore\n this.server.emit(\"listening\");\n\n return this;\n }\n\n public shutdown() {\n if (this._listening) {\n this._listening.close();\n\n // @ts-ignore\n this.server.emit(\"close\"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\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 = Array.from(buf);\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 parsedURL = new URL(rawClient.data.url);\n\n const sessionId = parsedURL.searchParams.get(\"sessionId\");\n const processAndRoomId = parsedURL.pathname.match(/\\/[a-zA-Z0-9_\\-]+\\/([a-zA-Z0-9_\\-]+)$/);\n const roomId = processAndRoomId && processAndRoomId[1];\n\n const room = matchMaker.getLocalRoomById(roomId);\n const client = new WebSocketClient(sessionId, wrapper);\n\n //\n // TODO: DRY code below with all transports\n //\n\n try {\n if (!room || !room.hasReservedSeat(sessionId, parsedURL.searchParams.get(\"reconnectionToken\") as string)) {\n throw new Error('seat reservation expired.');\n }\n\n await room._onJoin(client, {\n token: getBearerToken(rawClient.data.headers['authorization']),\n headers: rawClient.data.headers,\n ip: rawClient.data.headers['x-real-ip'] ?? rawClient.remoteAddress,\n });\n\n } catch (e) {\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 protected async handleMatchMakeRequest(req: Request, res: Response) {\n const writeHeaders = (req: Request, res: Response) => {\n if (res.destroyed) return;\n\n res.set(Object.assign(\n {},\n matchMaker.controller.DEFAULT_CORS_HEADERS,\n matchMaker.controller.getCorsHeaders.call(undefined, req)\n ));\n\n return true;\n };\n\n try {\n switch (req.method) {\n case 'OPTIONS': {\n writeHeaders(req, res);\n res.status(200).end();\n break;\n }\n\n case 'GET': {\n const matchedParams = req.path.match(matchMaker.controller.allowedRoomNameChars);\n const roomName = (matchedParams && matchedParams.length > 1)\n ? matchedParams[matchedParams.length - 1]\n : \"\";\n\n writeHeaders(req, res);\n res.json(await matchMaker.controller.getAvailableRooms(roomName || ''));\n break;\n }\n\n case 'POST': {\n // do not accept matchmaking requests if already shutting down\n if (matchMaker.state === matchMaker.MatchMakerState.SHUTTING_DOWN) {\n throw new ServerError(503, \"server is shutting down\");\n }\n\n const matchedParams = req.path.match(matchMaker.controller.allowedRoomNameChars);\n const matchmakeIndex = matchedParams.indexOf(matchMaker.controller.matchmakeRoute);\n let clientOptions = req.body; // Bun.readableStreamToJSON(req.body);\n\n if (clientOptions == null) {\n throw new ServerError(500, \"invalid JSON input\");\n }\n\n if (typeof clientOptions === 'string' && clientOptions.length > 2) {\n clientOptions = JSON.parse(clientOptions);\n } else if (typeof clientOptions !== 'object') {\n clientOptions = {};\n }\n\n const method = matchedParams[matchmakeIndex + 1];\n const roomName = matchedParams[matchmakeIndex + 2] || '';\n\n writeHeaders(req, res);\n res.json(await matchMaker.controller.invokeMethod(\n method,\n roomName,\n clientOptions,\n {\n token: getBearerToken(req.headers['authorization']),\n headers: req.headers,\n ip: req.headers['x-real-ip'] ?? req.ips,\n },\n ));\n break;\n }\n\n default: throw new ServerError(500, \"invalid request method\");\n }\n\n } catch (e) {\n writeHeaders(req, res);\n res.status(500)\n .json({ code: e.code, error: e.message });\n }\n }\n\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// <reference types=\"bun-types\" />\n\n// \"bun-types\" is currently conflicting with \"ws\" types.\n// @ts-ignore\nimport { ServerWebSocket, WebSocketHandler } from 'bun';\n\nimport type http from 'http';\nimport bunExpress from 'bun-serve-express';\nimport type { Application, Request, Response } from \"express\";\n\nimport { HttpServerMock, matchMaker, Transport, debugAndPrintError, spliceOne, ServerError, getBearerToken } from '@colyseus/core';\nimport { WebSocketClient, WebSocketWrapper } from './WebSocketClient.js';\n\nexport type TransportOptions = Partial<Omit<WebSocketHandler, \"message\" | \"open\" | \"drain\" | \"close\" | \"ping\" | \"pong\">>;\n\ninterface WebSocketData {\n url: URL;\n headers: any;\n}\n\nexport class BunWebSockets extends Transport {\n public expressApp: Application;\n\n protected clients: ServerWebSocket<WebSocketData>[] = [];\n protected clientWrappers = new WeakMap<ServerWebSocket<WebSocketData>, WebSocketWrapper>();\n\n private _listening: any;\n private _originalRawSend: typeof WebSocketClient.prototype.raw | null = null;\n\n constructor(private options: TransportOptions = {}) {\n super();\n\n const self = this;\n\n this.expressApp = bunExpress({\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 // Adding a mock object for Transport.server\n if (!this.server) {\n // @ts-ignore\n this.server = new HttpServerMock();\n }\n }\n\n public listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void) {\n this._listening = this.expressApp.listen(port, listeningListener);\n\n this.expressApp.use(`/${matchMaker.controller.matchmakeRoute}`, async (req, res) => {\n try {\n await this.handleMatchMakeRequest(req, res);\n } catch (e) {\n res.status(500).json({\n code: e.code,\n error: e.message\n });\n }\n });\n\n // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\n // @ts-ignore\n this.server.emit(\"listening\");\n\n return this;\n }\n\n public shutdown() {\n if (this._listening) {\n this._listening.close();\n\n // @ts-ignore\n this.server.emit(\"close\"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\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 = Array.from(buf);\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 parsedURL = new URL(rawClient.data.url);\n\n const sessionId = parsedURL.searchParams.get(\"sessionId\");\n const processAndRoomId = parsedURL.pathname.match(/\\/[a-zA-Z0-9_\\-]+\\/([a-zA-Z0-9_\\-]+)$/);\n const roomId = processAndRoomId && processAndRoomId[1];\n\n const room = matchMaker.getLocalRoomById(roomId);\n const client = new WebSocketClient(sessionId, wrapper);\n\n //\n // TODO: DRY code below with all transports\n //\n\n try {\n if (!room || !room.hasReservedSeat(sessionId, parsedURL.searchParams.get(\"reconnectionToken\") as string)) {\n throw new Error('seat reservation expired.');\n }\n\n await room._onJoin(client, {\n token: getBearerToken(rawClient.data.headers['authorization']),\n headers: rawClient.data.headers,\n ip: rawClient.data.headers['x-real-ip'] ?? rawClient.remoteAddress,\n });\n\n } catch (e) {\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 protected async handleMatchMakeRequest(req: Request, res: Response) {\n const writeHeaders = (req: Request, res: Response) => {\n if (res.destroyed) return;\n\n res.set(Object.assign(\n {},\n matchMaker.controller.DEFAULT_CORS_HEADERS,\n matchMaker.controller.getCorsHeaders.call(undefined, req)\n ));\n\n return true;\n };\n\n try {\n switch (req.method) {\n case 'OPTIONS': {\n writeHeaders(req, res);\n res.status(200).end();\n break;\n }\n\n case 'GET': {\n writeHeaders(req, res);\n res.status(404).end();\n break;\n }\n\n case 'POST': {\n // do not accept matchmaking requests if already shutting down\n if (matchMaker.state === matchMaker.MatchMakerState.SHUTTING_DOWN) {\n throw new ServerError(503, \"server is shutting down\");\n }\n\n const matchedParams = req.path.match(matchMaker.controller.allowedRoomNameChars);\n const matchmakeIndex = matchedParams.indexOf(matchMaker.controller.matchmakeRoute);\n let clientOptions = req.body; // Bun.readableStreamToJSON(req.body);\n\n if (clientOptions == null) {\n throw new ServerError(500, \"invalid JSON input\");\n }\n\n if (typeof clientOptions === 'string' && clientOptions.length > 2) {\n clientOptions = JSON.parse(clientOptions);\n } else if (typeof clientOptions !== 'object') {\n clientOptions = {};\n }\n\n const method = matchedParams[matchmakeIndex + 1];\n const roomName = matchedParams[matchmakeIndex + 2] || '';\n\n writeHeaders(req, res);\n res.json(await matchMaker.controller.invokeMethod(\n method,\n roomName,\n clientOptions,\n {\n token: getBearerToken(req.headers['authorization']),\n headers: req.headers,\n ip: req.headers['x-real-ip'] ?? req.ips,\n },\n ));\n break;\n }\n\n default: throw new ServerError(500, \"invalid request method\");\n }\n\n } catch (e) {\n writeHeaders(req, res);\n res.status(500)\n .json({ code: e.code, error: e.message });\n }\n }\n\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["bunExpress", "req", "res"]
7
7
  }
@@ -107,10 +107,8 @@ var BunWebSockets = class extends Transport {
107
107
  break;
108
108
  }
109
109
  case "GET": {
110
- const matchedParams = req.path.match(matchMaker.controller.allowedRoomNameChars);
111
- const roomName = matchedParams && matchedParams.length > 1 ? matchedParams[matchedParams.length - 1] : "";
112
110
  writeHeaders(req, res);
113
- res.json(await matchMaker.controller.getAvailableRooms(roomName || ""));
111
+ res.status(404).end();
114
112
  break;
115
113
  }
116
114
  case "POST": {
@@ -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';\n\nimport type http from 'http';\nimport bunExpress from 'bun-serve-express';\nimport type { Application, Request, Response } from \"express\";\n\nimport { HttpServerMock, matchMaker, Transport, debugAndPrintError, spliceOne, ServerError, getBearerToken } from '@colyseus/core';\nimport { WebSocketClient, WebSocketWrapper } from './WebSocketClient.js';\n\nexport type TransportOptions = Partial<Omit<WebSocketHandler, \"message\" | \"open\" | \"drain\" | \"close\" | \"ping\" | \"pong\">>;\n\ninterface WebSocketData {\n url: URL;\n headers: any;\n}\n\nexport class BunWebSockets extends Transport {\n public expressApp: Application;\n\n protected clients: ServerWebSocket<WebSocketData>[] = [];\n protected clientWrappers = new WeakMap<ServerWebSocket<WebSocketData>, WebSocketWrapper>();\n\n private _listening: any;\n private _originalRawSend: typeof WebSocketClient.prototype.raw | null = null;\n\n constructor(private options: TransportOptions = {}) {\n super();\n\n const self = this;\n\n this.expressApp = bunExpress({\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 // Adding a mock object for Transport.server\n if (!this.server) {\n // @ts-ignore\n this.server = new HttpServerMock();\n }\n }\n\n public listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void) {\n this._listening = this.expressApp.listen(port, listeningListener);\n\n this.expressApp.use(`/${matchMaker.controller.matchmakeRoute}`, async (req, res) => {\n try {\n await this.handleMatchMakeRequest(req, res);\n } catch (e) {\n res.status(500).json({\n code: e.code,\n error: e.message\n });\n }\n });\n\n // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\n // @ts-ignore\n this.server.emit(\"listening\");\n\n return this;\n }\n\n public shutdown() {\n if (this._listening) {\n this._listening.close();\n\n // @ts-ignore\n this.server.emit(\"close\"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\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 = Array.from(buf);\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 parsedURL = new URL(rawClient.data.url);\n\n const sessionId = parsedURL.searchParams.get(\"sessionId\");\n const processAndRoomId = parsedURL.pathname.match(/\\/[a-zA-Z0-9_\\-]+\\/([a-zA-Z0-9_\\-]+)$/);\n const roomId = processAndRoomId && processAndRoomId[1];\n\n const room = matchMaker.getLocalRoomById(roomId);\n const client = new WebSocketClient(sessionId, wrapper);\n\n //\n // TODO: DRY code below with all transports\n //\n\n try {\n if (!room || !room.hasReservedSeat(sessionId, parsedURL.searchParams.get(\"reconnectionToken\") as string)) {\n throw new Error('seat reservation expired.');\n }\n\n await room._onJoin(client, {\n token: getBearerToken(rawClient.data.headers['authorization']),\n headers: rawClient.data.headers,\n ip: rawClient.data.headers['x-real-ip'] ?? rawClient.remoteAddress,\n });\n\n } catch (e) {\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 protected async handleMatchMakeRequest(req: Request, res: Response) {\n const writeHeaders = (req: Request, res: Response) => {\n if (res.destroyed) return;\n\n res.set(Object.assign(\n {},\n matchMaker.controller.DEFAULT_CORS_HEADERS,\n matchMaker.controller.getCorsHeaders.call(undefined, req)\n ));\n\n return true;\n };\n\n try {\n switch (req.method) {\n case 'OPTIONS': {\n writeHeaders(req, res);\n res.status(200).end();\n break;\n }\n\n case 'GET': {\n const matchedParams = req.path.match(matchMaker.controller.allowedRoomNameChars);\n const roomName = (matchedParams && matchedParams.length > 1)\n ? matchedParams[matchedParams.length - 1]\n : \"\";\n\n writeHeaders(req, res);\n res.json(await matchMaker.controller.getAvailableRooms(roomName || ''));\n break;\n }\n\n case 'POST': {\n // do not accept matchmaking requests if already shutting down\n if (matchMaker.state === matchMaker.MatchMakerState.SHUTTING_DOWN) {\n throw new ServerError(503, \"server is shutting down\");\n }\n\n const matchedParams = req.path.match(matchMaker.controller.allowedRoomNameChars);\n const matchmakeIndex = matchedParams.indexOf(matchMaker.controller.matchmakeRoute);\n let clientOptions = req.body; // Bun.readableStreamToJSON(req.body);\n\n if (clientOptions == null) {\n throw new ServerError(500, \"invalid JSON input\");\n }\n\n if (typeof clientOptions === 'string' && clientOptions.length > 2) {\n clientOptions = JSON.parse(clientOptions);\n } else if (typeof clientOptions !== 'object') {\n clientOptions = {};\n }\n\n const method = matchedParams[matchmakeIndex + 1];\n const roomName = matchedParams[matchmakeIndex + 2] || '';\n\n writeHeaders(req, res);\n res.json(await matchMaker.controller.invokeMethod(\n method,\n roomName,\n clientOptions,\n {\n token: getBearerToken(req.headers['authorization']),\n headers: req.headers,\n ip: req.headers['x-real-ip'] ?? req.ips,\n },\n ));\n break;\n }\n\n default: throw new ServerError(500, \"invalid request method\");\n }\n\n } catch (e) {\n writeHeaders(req, res);\n res.status(500)\n .json({ code: e.code, error: e.message });\n }\n }\n\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// <reference types=\"bun-types\" />\n\n// \"bun-types\" is currently conflicting with \"ws\" types.\n// @ts-ignore\nimport { ServerWebSocket, WebSocketHandler } from 'bun';\n\nimport type http from 'http';\nimport bunExpress from 'bun-serve-express';\nimport type { Application, Request, Response } from \"express\";\n\nimport { HttpServerMock, matchMaker, Transport, debugAndPrintError, spliceOne, ServerError, getBearerToken } from '@colyseus/core';\nimport { WebSocketClient, WebSocketWrapper } from './WebSocketClient.js';\n\nexport type TransportOptions = Partial<Omit<WebSocketHandler, \"message\" | \"open\" | \"drain\" | \"close\" | \"ping\" | \"pong\">>;\n\ninterface WebSocketData {\n url: URL;\n headers: any;\n}\n\nexport class BunWebSockets extends Transport {\n public expressApp: Application;\n\n protected clients: ServerWebSocket<WebSocketData>[] = [];\n protected clientWrappers = new WeakMap<ServerWebSocket<WebSocketData>, WebSocketWrapper>();\n\n private _listening: any;\n private _originalRawSend: typeof WebSocketClient.prototype.raw | null = null;\n\n constructor(private options: TransportOptions = {}) {\n super();\n\n const self = this;\n\n this.expressApp = bunExpress({\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 // Adding a mock object for Transport.server\n if (!this.server) {\n // @ts-ignore\n this.server = new HttpServerMock();\n }\n }\n\n public listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void) {\n this._listening = this.expressApp.listen(port, listeningListener);\n\n this.expressApp.use(`/${matchMaker.controller.matchmakeRoute}`, async (req, res) => {\n try {\n await this.handleMatchMakeRequest(req, res);\n } catch (e) {\n res.status(500).json({\n code: e.code,\n error: e.message\n });\n }\n });\n\n // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\n // @ts-ignore\n this.server.emit(\"listening\");\n\n return this;\n }\n\n public shutdown() {\n if (this._listening) {\n this._listening.close();\n\n // @ts-ignore\n this.server.emit(\"close\"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\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 = Array.from(buf);\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 parsedURL = new URL(rawClient.data.url);\n\n const sessionId = parsedURL.searchParams.get(\"sessionId\");\n const processAndRoomId = parsedURL.pathname.match(/\\/[a-zA-Z0-9_\\-]+\\/([a-zA-Z0-9_\\-]+)$/);\n const roomId = processAndRoomId && processAndRoomId[1];\n\n const room = matchMaker.getLocalRoomById(roomId);\n const client = new WebSocketClient(sessionId, wrapper);\n\n //\n // TODO: DRY code below with all transports\n //\n\n try {\n if (!room || !room.hasReservedSeat(sessionId, parsedURL.searchParams.get(\"reconnectionToken\") as string)) {\n throw new Error('seat reservation expired.');\n }\n\n await room._onJoin(client, {\n token: getBearerToken(rawClient.data.headers['authorization']),\n headers: rawClient.data.headers,\n ip: rawClient.data.headers['x-real-ip'] ?? rawClient.remoteAddress,\n });\n\n } catch (e) {\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 protected async handleMatchMakeRequest(req: Request, res: Response) {\n const writeHeaders = (req: Request, res: Response) => {\n if (res.destroyed) return;\n\n res.set(Object.assign(\n {},\n matchMaker.controller.DEFAULT_CORS_HEADERS,\n matchMaker.controller.getCorsHeaders.call(undefined, req)\n ));\n\n return true;\n };\n\n try {\n switch (req.method) {\n case 'OPTIONS': {\n writeHeaders(req, res);\n res.status(200).end();\n break;\n }\n\n case 'GET': {\n writeHeaders(req, res);\n res.status(404).end();\n break;\n }\n\n case 'POST': {\n // do not accept matchmaking requests if already shutting down\n if (matchMaker.state === matchMaker.MatchMakerState.SHUTTING_DOWN) {\n throw new ServerError(503, \"server is shutting down\");\n }\n\n const matchedParams = req.path.match(matchMaker.controller.allowedRoomNameChars);\n const matchmakeIndex = matchedParams.indexOf(matchMaker.controller.matchmakeRoute);\n let clientOptions = req.body; // Bun.readableStreamToJSON(req.body);\n\n if (clientOptions == null) {\n throw new ServerError(500, \"invalid JSON input\");\n }\n\n if (typeof clientOptions === 'string' && clientOptions.length > 2) {\n clientOptions = JSON.parse(clientOptions);\n } else if (typeof clientOptions !== 'object') {\n clientOptions = {};\n }\n\n const method = matchedParams[matchmakeIndex + 1];\n const roomName = matchedParams[matchmakeIndex + 2] || '';\n\n writeHeaders(req, res);\n res.json(await matchMaker.controller.invokeMethod(\n method,\n roomName,\n clientOptions,\n {\n token: getBearerToken(req.headers['authorization']),\n headers: req.headers,\n ip: req.headers['x-real-ip'] ?? req.ips,\n },\n ));\n break;\n }\n\n default: throw new ServerError(500, \"invalid request method\");\n }\n\n } catch (e) {\n writeHeaders(req, res);\n res.status(500)\n .json({ code: e.code, error: e.message });\n }\n }\n\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["req", "res"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colyseus/bun-websockets",
3
- "version": "0.16.0-preview.8",
3
+ "version": "0.16.0",
4
4
  "input": "./src/index.ts",
5
5
  "main": "./build/index.js",
6
6
  "module": "./build/index.mjs",
@@ -14,10 +14,10 @@
14
14
  "dependencies": {
15
15
  "bun-serve-express": "^1.0.5",
16
16
  "express": "^4.17.1",
17
- "@colyseus/core": "^0.16.0-preview.36"
17
+ "@colyseus/core": "^0.16.0"
18
18
  },
19
19
  "devDependencies": {
20
- "@colyseus/core": "^0.16.0-preview.36"
20
+ "@colyseus/core": "^0.16.0"
21
21
  },
22
22
  "author": "Endel Dreyer",
23
23
  "license": "MIT",