@colyseus/monitor 0.17.8 → 0.18.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.
@@ -22,8 +22,7 @@ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
22
22
  var import_core = require("@colyseus/core");
23
23
  function getStateSize(room) {
24
24
  const hasState = room._serializer.encoder || // schema v3
25
- room._serializer.state || // schema v2
26
- room._serializer.previousState;
25
+ room._serializer.state;
27
26
  const fullState = hasState && room._serializer.getFullState();
28
27
  return fullState && (fullState.byteLength || fullState.length) || 0;
29
28
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src-backend/ext/Room.ts"],
4
- "sourcesContent": ["//\n// Monkey-patch Colyseus' default behaviour\n//\nimport { Room, type Client, type ClientPrivate } from \"@colyseus/core\";\n\nfunction getStateSize(room) {\n // TODO: `Serializer<T>` should provide a method for this (e.g. `serializer.hasState()`)\n const hasState = (\n room._serializer.encoder || // schema v3\n room._serializer.state || // schema v2\n room._serializer.previousState // legacy-fossil-delta\n );\n const fullState = hasState && room._serializer.getFullState();\n return fullState && (fullState.byteLength || fullState.length) || 0;\n}\n\n(Room.prototype as any).getAvailableData = function () {\n return {\n clients: this.clients.length,\n maxClients: this.maxClients,\n metadata: this.metadata,\n roomId: this.roomId,\n };\n};\n\n(Room.prototype as any).getRoomListData = async function () {\n const stateSize = getStateSize(this);\n const elapsedTime = this.clock.elapsedTime;\n const locked = this.locked;\n const data = this.getAvailableData();\n\n return { ...data, locked, elapsedTime, stateSize };\n};\n\n(Room.prototype as any).getInspectData = async function () {\n const state = this.state;\n const stateSize = getStateSize(this);\n const roomElapsedTime = this.clock.elapsedTime;\n\n const data = this.getAvailableData();\n const clients = this.clients.map((client: Client & ClientPrivate) => ({\n sessionId: client.sessionId,\n elapsedTime: roomElapsedTime - client._joinedAt\n }));\n const locked = this.locked;\n\n return { ...data, locked, clients, state, stateSize };\n};\n\n// Actions\n(Room.prototype as any)._forceClientDisconnect = async function (sessionId) {\n for (let i = 0; i < this.clients.length; i++) {\n if (this.clients[i].sessionId === sessionId) {\n this.clients[i].leave();\n break;\n }\n }\n};\n\n(Room.prototype as any)._sendMessageToClient = async function (sessionId, type, data) {\n for (let i = 0; i < this.clients.length; i++) {\n if (this.clients[i].sessionId === sessionId) {\n this.clients[i].send(type, data);\n break;\n }\n }\n};\n\nfunction getChild(parent: any, key: string | number) {\n if (typeof parent.at === 'function' && typeof key === 'number') {\n return parent.at(key);\n }\n if (typeof parent.get === 'function') {\n return parent.get(String(key));\n }\n return parent[key];\n}\n\n(Room.prototype as any)._editStateProperty = async function (path: (string | number)[], value: any) {\n let current = this.state;\n for (let i = 0; i < path.length - 1; i++) {\n if (current === null || current === undefined) return;\n current = getChild(current, path[i]);\n }\n if (current === null || current === undefined) return;\n const property = path[path.length - 1];\n if (typeof current.set === 'function') {\n current.set(String(property), value);\n } else {\n current[property] = value;\n }\n};\n\n(Room.prototype as any)._deleteStateProperty = async function (path: (string | number)[]) {\n let current = this.state;\n for (let i = 0; i < path.length - 1; i++) {\n if (current === null || current === undefined) return;\n current = getChild(current, path[i]);\n }\n if (current === null || current === undefined) return;\n const property = path[path.length - 1];\n if (typeof current.delete === 'function') {\n current.delete(String(property));\n } else {\n current[property] = undefined;\n }\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["//\n// Monkey-patch Colyseus' default behaviour\n//\nimport { Room, type Client, type ClientPrivate } from \"@colyseus/core\";\n\nfunction getStateSize(room) {\n // TODO: `Serializer<T>` should provide a method for this (e.g. `serializer.hasState()`)\n const hasState = (\n room._serializer.encoder || // schema v3\n room._serializer.state // schema v2\n );\n const fullState = hasState && room._serializer.getFullState();\n return fullState && (fullState.byteLength || fullState.length) || 0;\n}\n\n(Room.prototype as any).getAvailableData = function () {\n return {\n clients: this.clients.length,\n maxClients: this.maxClients,\n metadata: this.metadata,\n roomId: this.roomId,\n };\n};\n\n(Room.prototype as any).getRoomListData = async function () {\n const stateSize = getStateSize(this);\n const elapsedTime = this.clock.elapsedTime;\n const locked = this.locked;\n const data = this.getAvailableData();\n\n return { ...data, locked, elapsedTime, stateSize };\n};\n\n(Room.prototype as any).getInspectData = async function () {\n const state = this.state;\n const stateSize = getStateSize(this);\n const roomElapsedTime = this.clock.elapsedTime;\n\n const data = this.getAvailableData();\n const clients = this.clients.map((client: Client & ClientPrivate) => ({\n sessionId: client.sessionId,\n elapsedTime: roomElapsedTime - client._joinedAt\n }));\n const locked = this.locked;\n\n return { ...data, locked, clients, state, stateSize };\n};\n\n// Actions\n(Room.prototype as any)._forceClientDisconnect = async function (sessionId) {\n for (let i = 0; i < this.clients.length; i++) {\n if (this.clients[i].sessionId === sessionId) {\n this.clients[i].leave();\n break;\n }\n }\n};\n\n(Room.prototype as any)._sendMessageToClient = async function (sessionId, type, data) {\n for (let i = 0; i < this.clients.length; i++) {\n if (this.clients[i].sessionId === sessionId) {\n this.clients[i].send(type, data);\n break;\n }\n }\n};\n\nfunction getChild(parent: any, key: string | number) {\n if (typeof parent.at === 'function' && typeof key === 'number') {\n return parent.at(key);\n }\n if (typeof parent.get === 'function') {\n return parent.get(String(key));\n }\n return parent[key];\n}\n\n(Room.prototype as any)._editStateProperty = async function (path: (string | number)[], value: any) {\n let current = this.state;\n for (let i = 0; i < path.length - 1; i++) {\n if (current === null || current === undefined) return;\n current = getChild(current, path[i]);\n }\n if (current === null || current === undefined) return;\n const property = path[path.length - 1];\n if (typeof current.set === 'function') {\n current.set(String(property), value);\n } else {\n current[property] = value;\n }\n};\n\n(Room.prototype as any)._deleteStateProperty = async function (path: (string | number)[]) {\n let current = this.state;\n for (let i = 0; i < path.length - 1; i++) {\n if (current === null || current === undefined) return;\n current = getChild(current, path[i]);\n }\n if (current === null || current === undefined) return;\n const property = path[path.length - 1];\n if (typeof current.delete === 'function') {\n current.delete(String(property));\n } else {\n current[property] = undefined;\n }\n};\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,8 +2,7 @@
2
2
  import { Room } from "@colyseus/core";
3
3
  function getStateSize(room) {
4
4
  const hasState = room._serializer.encoder || // schema v3
5
- room._serializer.state || // schema v2
6
- room._serializer.previousState;
5
+ room._serializer.state;
7
6
  const fullState = hasState && room._serializer.getFullState();
8
7
  return fullState && (fullState.byteLength || fullState.length) || 0;
9
8
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src-backend/ext/Room.ts"],
4
- "sourcesContent": ["//\n// Monkey-patch Colyseus' default behaviour\n//\nimport { Room, type Client, type ClientPrivate } from \"@colyseus/core\";\n\nfunction getStateSize(room) {\n // TODO: `Serializer<T>` should provide a method for this (e.g. `serializer.hasState()`)\n const hasState = (\n room._serializer.encoder || // schema v3\n room._serializer.state || // schema v2\n room._serializer.previousState // legacy-fossil-delta\n );\n const fullState = hasState && room._serializer.getFullState();\n return fullState && (fullState.byteLength || fullState.length) || 0;\n}\n\n(Room.prototype as any).getAvailableData = function () {\n return {\n clients: this.clients.length,\n maxClients: this.maxClients,\n metadata: this.metadata,\n roomId: this.roomId,\n };\n};\n\n(Room.prototype as any).getRoomListData = async function () {\n const stateSize = getStateSize(this);\n const elapsedTime = this.clock.elapsedTime;\n const locked = this.locked;\n const data = this.getAvailableData();\n\n return { ...data, locked, elapsedTime, stateSize };\n};\n\n(Room.prototype as any).getInspectData = async function () {\n const state = this.state;\n const stateSize = getStateSize(this);\n const roomElapsedTime = this.clock.elapsedTime;\n\n const data = this.getAvailableData();\n const clients = this.clients.map((client: Client & ClientPrivate) => ({\n sessionId: client.sessionId,\n elapsedTime: roomElapsedTime - client._joinedAt\n }));\n const locked = this.locked;\n\n return { ...data, locked, clients, state, stateSize };\n};\n\n// Actions\n(Room.prototype as any)._forceClientDisconnect = async function (sessionId) {\n for (let i = 0; i < this.clients.length; i++) {\n if (this.clients[i].sessionId === sessionId) {\n this.clients[i].leave();\n break;\n }\n }\n};\n\n(Room.prototype as any)._sendMessageToClient = async function (sessionId, type, data) {\n for (let i = 0; i < this.clients.length; i++) {\n if (this.clients[i].sessionId === sessionId) {\n this.clients[i].send(type, data);\n break;\n }\n }\n};\n\nfunction getChild(parent: any, key: string | number) {\n if (typeof parent.at === 'function' && typeof key === 'number') {\n return parent.at(key);\n }\n if (typeof parent.get === 'function') {\n return parent.get(String(key));\n }\n return parent[key];\n}\n\n(Room.prototype as any)._editStateProperty = async function (path: (string | number)[], value: any) {\n let current = this.state;\n for (let i = 0; i < path.length - 1; i++) {\n if (current === null || current === undefined) return;\n current = getChild(current, path[i]);\n }\n if (current === null || current === undefined) return;\n const property = path[path.length - 1];\n if (typeof current.set === 'function') {\n current.set(String(property), value);\n } else {\n current[property] = value;\n }\n};\n\n(Room.prototype as any)._deleteStateProperty = async function (path: (string | number)[]) {\n let current = this.state;\n for (let i = 0; i < path.length - 1; i++) {\n if (current === null || current === undefined) return;\n current = getChild(current, path[i]);\n }\n if (current === null || current === undefined) return;\n const property = path[path.length - 1];\n if (typeof current.delete === 'function') {\n current.delete(String(property));\n } else {\n current[property] = undefined;\n }\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["//\n// Monkey-patch Colyseus' default behaviour\n//\nimport { Room, type Client, type ClientPrivate } from \"@colyseus/core\";\n\nfunction getStateSize(room) {\n // TODO: `Serializer<T>` should provide a method for this (e.g. `serializer.hasState()`)\n const hasState = (\n room._serializer.encoder || // schema v3\n room._serializer.state // schema v2\n );\n const fullState = hasState && room._serializer.getFullState();\n return fullState && (fullState.byteLength || fullState.length) || 0;\n}\n\n(Room.prototype as any).getAvailableData = function () {\n return {\n clients: this.clients.length,\n maxClients: this.maxClients,\n metadata: this.metadata,\n roomId: this.roomId,\n };\n};\n\n(Room.prototype as any).getRoomListData = async function () {\n const stateSize = getStateSize(this);\n const elapsedTime = this.clock.elapsedTime;\n const locked = this.locked;\n const data = this.getAvailableData();\n\n return { ...data, locked, elapsedTime, stateSize };\n};\n\n(Room.prototype as any).getInspectData = async function () {\n const state = this.state;\n const stateSize = getStateSize(this);\n const roomElapsedTime = this.clock.elapsedTime;\n\n const data = this.getAvailableData();\n const clients = this.clients.map((client: Client & ClientPrivate) => ({\n sessionId: client.sessionId,\n elapsedTime: roomElapsedTime - client._joinedAt\n }));\n const locked = this.locked;\n\n return { ...data, locked, clients, state, stateSize };\n};\n\n// Actions\n(Room.prototype as any)._forceClientDisconnect = async function (sessionId) {\n for (let i = 0; i < this.clients.length; i++) {\n if (this.clients[i].sessionId === sessionId) {\n this.clients[i].leave();\n break;\n }\n }\n};\n\n(Room.prototype as any)._sendMessageToClient = async function (sessionId, type, data) {\n for (let i = 0; i < this.clients.length; i++) {\n if (this.clients[i].sessionId === sessionId) {\n this.clients[i].send(type, data);\n break;\n }\n }\n};\n\nfunction getChild(parent: any, key: string | number) {\n if (typeof parent.at === 'function' && typeof key === 'number') {\n return parent.at(key);\n }\n if (typeof parent.get === 'function') {\n return parent.get(String(key));\n }\n return parent[key];\n}\n\n(Room.prototype as any)._editStateProperty = async function (path: (string | number)[], value: any) {\n let current = this.state;\n for (let i = 0; i < path.length - 1; i++) {\n if (current === null || current === undefined) return;\n current = getChild(current, path[i]);\n }\n if (current === null || current === undefined) return;\n const property = path[path.length - 1];\n if (typeof current.set === 'function') {\n current.set(String(property), value);\n } else {\n current[property] = value;\n }\n};\n\n(Room.prototype as any)._deleteStateProperty = async function (path: (string | number)[]) {\n let current = this.state;\n for (let i = 0; i < path.length - 1; i++) {\n if (current === null || current === undefined) return;\n current = getChild(current, path[i]);\n }\n if (current === null || current === undefined) return;\n const property = path[path.length - 1];\n if (typeof current.delete === 'function') {\n current.delete(String(property));\n } else {\n current[property] = undefined;\n }\n};\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/build/index.cjs CHANGED
@@ -32,16 +32,138 @@ __export(index_exports, {
32
32
  monitor: () => monitor
33
33
  });
34
34
  module.exports = __toCommonJS(index_exports);
35
- var import_express = __toESM(require("express"));
35
+ var import_promises = __toESM(require("fs/promises"));
36
36
  var import_path = __toESM(require("path"));
37
- var import_api = require("./api.cjs");
37
+ var import_url = require("url");
38
+ var import_zod = require("zod");
39
+ var import_core = require("@colyseus/core");
40
+ var import_node_os_utils = require("node-os-utils");
41
+ var import_serve_static = require("./serve-static.cjs");
38
42
  var import_Room = require("./ext/Room.cjs");
39
- var frontendDirectory = import_path.default.resolve(__dirname, "..", "build", "static");
43
+ var import_meta = {};
44
+ var osutils = new import_node_os_utils.OSUtils();
45
+ var UNAVAILABLE_ROOM_ERROR = "@colyseus/monitor: room $roomId is not available anymore.";
46
+ var SPA_DIST = import_path.default.resolve(import_path.default.dirname((0, import_url.fileURLToPath)(import_meta.url)), "..", "build", "static");
40
47
  function monitor(opts = {}) {
41
- const router = import_express.default.Router();
42
- router.use(import_express.default.static(frontendDirectory));
43
- router.use("/api", (0, import_api.getAPI)(opts));
44
- return router;
48
+ var _a, _b;
49
+ const prefix = (_a = opts.prefix) != null ? _a : "/monitor";
50
+ const use = (_b = opts.use) != null ? _b : [];
51
+ const columnsOpt = opts.columns;
52
+ const endpoints = {
53
+ "monitor-api-rooms": (0, import_core.createEndpoint)(`${prefix}/api`, { method: "GET", use }, async () => {
54
+ var _a2, _b2;
55
+ try {
56
+ const rooms = await import_core.matchMaker.query({});
57
+ const columns = columnsOpt != null ? columnsOpt : ["roomId", "name", "clients", "maxClients", "locked", "elapsedTime"];
58
+ if (!columnsOpt && rooms[0] && rooms[0].publicAddress !== void 0) {
59
+ columns.push("publicAddress");
60
+ }
61
+ let connections = 0;
62
+ const cpuUsage = await osutils.cpu.usage();
63
+ const cpu = cpuUsage.success ? cpuUsage.data : NaN;
64
+ const memoryInfo = await osutils.memory.info();
65
+ const totalMemMb = memoryInfo.success ? (_a2 = memoryInfo.data.total) == null ? void 0 : _a2.toMB() : NaN;
66
+ const usedMemMb = memoryInfo.success ? (_b2 = memoryInfo.data.used) == null ? void 0 : _b2.toMB() : NaN;
67
+ return {
68
+ columns,
69
+ rooms: rooms.map((room) => {
70
+ const data = JSON.parse(JSON.stringify(room));
71
+ connections += room.clients;
72
+ data.locked = room.locked || false;
73
+ data.private = room.private;
74
+ data.maxClients = `${room.maxClients}`;
75
+ data.elapsedTime = Date.now() - new Date(room.createdAt).getTime();
76
+ return data;
77
+ }),
78
+ connections,
79
+ cpu,
80
+ memory: { totalMemMb, usedMemMb }
81
+ };
82
+ } catch (e) {
83
+ console.error(e.message);
84
+ return new Response(JSON.stringify({ message: e.message }), {
85
+ status: 500,
86
+ headers: { "content-type": "application/json" }
87
+ });
88
+ }
89
+ }),
90
+ "monitor-api-room": (0, import_core.createEndpoint)(`${prefix}/api/room`, {
91
+ method: "GET",
92
+ query: import_zod.z.object({ roomId: import_zod.z.string() }),
93
+ use
94
+ }, async (ctx) => {
95
+ const roomId = ctx.query.roomId;
96
+ try {
97
+ return await import_core.matchMaker.remoteRoomCall(roomId, "getInspectData");
98
+ } catch (e) {
99
+ return new Response(JSON.stringify({ message: UNAVAILABLE_ROOM_ERROR.replace("$roomId", roomId) }), {
100
+ status: 500,
101
+ headers: { "content-type": "application/json" }
102
+ });
103
+ }
104
+ }),
105
+ "monitor-api-room-call": (0, import_core.createEndpoint)(`${prefix}/api/room/call`, {
106
+ method: "GET",
107
+ query: import_zod.z.object({ roomId: import_zod.z.string(), method: import_zod.z.string(), args: import_zod.z.string() }),
108
+ use
109
+ }, async (ctx) => {
110
+ const { roomId, method } = ctx.query;
111
+ try {
112
+ const args = JSON.parse(ctx.query.args);
113
+ const data = await import_core.matchMaker.remoteRoomCall(roomId, method, args);
114
+ return data != null ? data : {};
115
+ } catch (e) {
116
+ return new Response(JSON.stringify({ message: UNAVAILABLE_ROOM_ERROR.replace("$roomId", roomId) }), {
117
+ status: 500,
118
+ headers: { "content-type": "application/json" }
119
+ });
120
+ }
121
+ }),
122
+ "monitor-index": (0, import_core.createEndpoint)(`${prefix}/`, { method: "GET", use }, async () => {
123
+ return (0, import_serve_static.serveStatic)(SPA_DIST, "");
124
+ }),
125
+ "monitor-static": (0, import_core.createEndpoint)(`${prefix}/**:splat`, { method: "GET", use }, async (ctx) => {
126
+ return (0, import_serve_static.serveStatic)(SPA_DIST, ctx.params.splat);
127
+ })
128
+ };
129
+ const SPA_DIST_RESOLVED = import_path.default.resolve(SPA_DIST);
130
+ return (0, import_core.dualModeEndpoints)(endpoints, {
131
+ catchAllKey: "monitor-static",
132
+ buildMiddleware: ({ specificRouter, specificHandler, fullHandler }) => (req, res, next) => {
133
+ var _a2, _b2, _c;
134
+ if (req.method !== "GET") {
135
+ return next();
136
+ }
137
+ const dispatchUrl = ((_b2 = (_a2 = req.originalUrl) != null ? _a2 : req.url) != null ? _b2 : "").split("?")[0];
138
+ if (prefix && dispatchUrl === prefix) {
139
+ res.writeHead(301, { location: `${prefix}/` });
140
+ res.end();
141
+ return;
142
+ }
143
+ const route = specificRouter.findRoute("GET", dispatchUrl);
144
+ if (route && ((_c = route.data) == null ? void 0 : _c.path) === dispatchUrl) {
145
+ return specificHandler(req, res).catch(next);
146
+ }
147
+ if (!dispatchUrl.startsWith(prefix)) {
148
+ return next();
149
+ }
150
+ const rel = dispatchUrl.slice(prefix.length).replace(/^\/+/, "");
151
+ if (!rel || rel.includes("..")) {
152
+ return next();
153
+ }
154
+ const filePath = import_path.default.resolve(SPA_DIST_RESOLVED, rel);
155
+ if (!filePath.startsWith(SPA_DIST_RESOLVED + import_path.default.sep)) {
156
+ return next();
157
+ }
158
+ import_promises.default.stat(filePath).then((stat) => {
159
+ if (stat.isFile()) {
160
+ fullHandler(req, res).catch(next);
161
+ } else {
162
+ next();
163
+ }
164
+ }).catch(() => next());
165
+ }
166
+ });
45
167
  }
46
168
  // Annotate the CommonJS export names for ESM import in node:
47
169
  0 && (module.exports = {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src-backend/index.ts"],
4
- "sourcesContent": ["import express from 'express';\nimport path from 'path';\n\n// required for ESM support. (esbuild uses it)\nimport { fileURLToPath } from 'url';\n\nimport { getAPI } from './api.js';\nimport './ext/Room.js';\n\nconst frontendDirectory = path.resolve(__dirname, \"..\", \"build\", \"static\");\n\nexport interface MonitorOptions {\n columns: Array<\n 'roomId' |\n 'name' |\n 'clients' |\n 'maxClients' |\n 'locked' |\n 'elapsedTime' |\n { metadata: string } |\n 'processId' |\n \"publicAddress\"\n >\n}\n\nexport function monitor (opts: Partial<MonitorOptions> = {}): express.Router {\n const router = express.Router();\n router.use(express.static(frontendDirectory));\n router.use('/api', getAPI(opts));\n return router;\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAoB;AACpB,kBAAiB;AAKjB,iBAAuB;AACvB,kBAAO;AAEP,IAAM,oBAAoB,YAAAA,QAAK,QAAQ,WAAW,MAAM,SAAS,QAAQ;AAgBlE,SAAS,QAAS,OAAgC,CAAC,GAAmB;AACzE,QAAM,SAAS,eAAAC,QAAQ,OAAO;AAC9B,SAAO,IAAI,eAAAA,QAAQ,OAAO,iBAAiB,CAAC;AAC5C,SAAO,IAAI,YAAQ,mBAAO,IAAI,CAAC;AAC/B,SAAO;AACX;",
6
- "names": ["path", "express"]
4
+ "sourcesContent": ["import fs from 'fs/promises';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport { z } from 'zod';\nimport { createEndpoint, dualModeEndpoints, matchMaker, type Endpoint } from '@colyseus/core';\nimport { OSUtils } from 'node-os-utils';\n\nimport { serveStatic } from './serve-static.js';\nimport './ext/Room.js';\n\nconst osutils = new OSUtils();\nconst UNAVAILABLE_ROOM_ERROR = \"@colyseus/monitor: room $roomId is not available anymore.\";\nconst SPA_DIST = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'build', 'static');\n\nexport interface MonitorOptions {\n /** Mount prefix used when spread into `createRouter`. Ignored in express-middleware mode. Defaults to `''`. */\n prefix?: string;\n /** Better-call middleware applied to every monitor endpoint \u2014 use for auth gating. */\n use?: any[];\n /** Columns shown in the rooms grid. */\n columns?: Array<\n 'roomId' |\n 'name' |\n 'clients' |\n 'maxClients' |\n 'locked' |\n 'elapsedTime' |\n { metadata: string } |\n 'processId' |\n 'publicAddress'\n >;\n}\n\nexport function monitor(opts: MonitorOptions = {}) {\n const prefix = opts.prefix ?? '/monitor';\n const use = opts.use ?? [];\n const columnsOpt = opts.columns;\n\n const endpoints: Record<string, Endpoint> = {\n 'monitor-api-rooms': createEndpoint(`${prefix}/api`, { method: 'GET', use }, async () => {\n try {\n const rooms: any[] = await matchMaker.query({});\n const columns = columnsOpt ?? ['roomId', 'name', 'clients', 'maxClients', 'locked', 'elapsedTime'];\n\n if (!columnsOpt && rooms[0] && rooms[0].publicAddress !== undefined) {\n columns.push('publicAddress');\n }\n\n let connections = 0;\n const cpuUsage = await osutils.cpu.usage();\n const cpu = cpuUsage.success ? cpuUsage.data : NaN;\n const memoryInfo = await osutils.memory.info();\n const totalMemMb = memoryInfo.success ? memoryInfo.data.total?.toMB() : NaN;\n const usedMemMb = memoryInfo.success ? memoryInfo.data.used?.toMB() : NaN;\n\n return {\n columns,\n rooms: rooms.map((room) => {\n const data = JSON.parse(JSON.stringify(room));\n connections += room.clients;\n data.locked = room.locked || false;\n data.private = room.private;\n data.maxClients = `${room.maxClients}`;\n data.elapsedTime = Date.now() - new Date(room.createdAt).getTime();\n return data;\n }),\n connections,\n cpu,\n memory: { totalMemMb, usedMemMb },\n };\n } catch (e: any) {\n console.error(e.message);\n return new Response(JSON.stringify({ message: e.message }), {\n status: 500,\n headers: { 'content-type': 'application/json' },\n });\n }\n }),\n\n 'monitor-api-room': createEndpoint(`${prefix}/api/room`, {\n method: 'GET',\n query: z.object({ roomId: z.string() }),\n use,\n }, async (ctx) => {\n const roomId = ctx.query.roomId;\n try {\n return await matchMaker.remoteRoomCall(roomId, 'getInspectData');\n } catch {\n return new Response(JSON.stringify({ message: UNAVAILABLE_ROOM_ERROR.replace('$roomId', roomId) }), {\n status: 500,\n headers: { 'content-type': 'application/json' },\n });\n }\n }),\n\n 'monitor-api-room-call': createEndpoint(`${prefix}/api/room/call`, {\n method: 'GET',\n query: z.object({ roomId: z.string(), method: z.string(), args: z.string() }),\n use,\n }, async (ctx) => {\n const { roomId, method } = ctx.query;\n try {\n const args = JSON.parse(ctx.query.args);\n const data = await matchMaker.remoteRoomCall(roomId, method, args);\n return data ?? {};\n } catch {\n return new Response(JSON.stringify({ message: UNAVAILABLE_ROOM_ERROR.replace('$roomId', roomId) }), {\n status: 500,\n headers: { 'content-type': 'application/json' },\n });\n }\n }),\n\n 'monitor-index': createEndpoint(`${prefix}/`, { method: 'GET', use }, async () => {\n return serveStatic(SPA_DIST, '');\n }),\n\n 'monitor-static': createEndpoint(`${prefix}/**:splat`, { method: 'GET', use }, async (ctx) => {\n return serveStatic(SPA_DIST, (ctx.params as any).splat);\n }),\n };\n\n const SPA_DIST_RESOLVED = path.resolve(SPA_DIST);\n\n // Express compat \u2014 works with either `app.use(\"/monitor\", monitor())` or\n // `app.use(\"/\", monitor())`. Routing decisions use originalUrl (the same\n // string better-call's getRequest uses to build the dispatched Request URL),\n // so the middleware's match check and the actual dispatch always agree.\n return dualModeEndpoints(endpoints, {\n catchAllKey: 'monitor-static',\n buildMiddleware: ({ specificRouter, specificHandler, fullHandler }) => (req, res, next) => {\n if (req.method !== 'GET') { return next(); }\n\n const dispatchUrl = ((req as any).originalUrl ?? req.url ?? '').split('?')[0]!;\n\n // Bare prefix (`/monitor`) \u2192 301 to canonical `/monitor/`.\n if (prefix && dispatchUrl === prefix) {\n res.writeHead(301, { location: `${prefix}/` });\n res.end();\n return;\n }\n\n const route = specificRouter.findRoute('GET', dispatchUrl);\n if (route && route.data?.path === dispatchUrl) {\n return specificHandler(req as any, res as any).catch(next);\n }\n\n // Asset request \u2014 only delegate if the file exists on disk.\n if (!dispatchUrl.startsWith(prefix)) { return next(); }\n const rel = dispatchUrl.slice(prefix.length).replace(/^\\/+/, '');\n if (!rel || rel.includes('..')) { return next(); }\n const filePath = path.resolve(SPA_DIST_RESOLVED, rel);\n if (!filePath.startsWith(SPA_DIST_RESOLVED + path.sep)) { return next(); }\n\n fs.stat(filePath).then((stat) => {\n if (stat.isFile()) {\n fullHandler(req as any, res as any).catch(next);\n } else {\n next();\n }\n }).catch(() => next());\n },\n });\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["path", "_a", "_b", "fs"]
7
7
  }
package/build/index.d.ts CHANGED
@@ -1,8 +1,13 @@
1
- import express from 'express';
1
+ import { type Endpoint } from '@colyseus/core';
2
2
  import './ext/Room.js';
3
3
  export interface MonitorOptions {
4
- columns: Array<'roomId' | 'name' | 'clients' | 'maxClients' | 'locked' | 'elapsedTime' | {
4
+ /** Mount prefix used when spread into `createRouter`. Ignored in express-middleware mode. Defaults to `''`. */
5
+ prefix?: string;
6
+ /** Better-call middleware applied to every monitor endpoint — use for auth gating. */
7
+ use?: any[];
8
+ /** Columns shown in the rooms grid. */
9
+ columns?: Array<'roomId' | 'name' | 'clients' | 'maxClients' | 'locked' | 'elapsedTime' | {
5
10
  metadata: string;
6
- } | 'processId' | "publicAddress">;
11
+ } | 'processId' | 'publicAddress'>;
7
12
  }
8
- export declare function monitor(opts?: Partial<MonitorOptions>): express.Router;
13
+ export declare function monitor(opts?: MonitorOptions): import("@colyseus/core").ExpressMiddleware & Record<string, Endpoint>;
package/build/index.mjs CHANGED
@@ -1,15 +1,132 @@
1
1
  // src-backend/index.ts
2
- import express from "express";
2
+ import fs from "fs/promises";
3
3
  import path from "path";
4
4
  import { fileURLToPath } from "url";
5
- import { getAPI } from "./api.mjs";
5
+ import { z } from "zod";
6
+ import { createEndpoint, dualModeEndpoints, matchMaker } from "@colyseus/core";
7
+ import { OSUtils } from "node-os-utils";
8
+ import { serveStatic } from "./serve-static.mjs";
6
9
  import "./ext/Room.mjs";
7
- var frontendDirectory = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "build", "static");
10
+ var osutils = new OSUtils();
11
+ var UNAVAILABLE_ROOM_ERROR = "@colyseus/monitor: room $roomId is not available anymore.";
12
+ var SPA_DIST = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "build", "static");
8
13
  function monitor(opts = {}) {
9
- const router = express.Router();
10
- router.use(express.static(frontendDirectory));
11
- router.use("/api", getAPI(opts));
12
- return router;
14
+ const prefix = opts.prefix ?? "/monitor";
15
+ const use = opts.use ?? [];
16
+ const columnsOpt = opts.columns;
17
+ const endpoints = {
18
+ "monitor-api-rooms": createEndpoint(`${prefix}/api`, { method: "GET", use }, async () => {
19
+ try {
20
+ const rooms = await matchMaker.query({});
21
+ const columns = columnsOpt ?? ["roomId", "name", "clients", "maxClients", "locked", "elapsedTime"];
22
+ if (!columnsOpt && rooms[0] && rooms[0].publicAddress !== void 0) {
23
+ columns.push("publicAddress");
24
+ }
25
+ let connections = 0;
26
+ const cpuUsage = await osutils.cpu.usage();
27
+ const cpu = cpuUsage.success ? cpuUsage.data : NaN;
28
+ const memoryInfo = await osutils.memory.info();
29
+ const totalMemMb = memoryInfo.success ? memoryInfo.data.total?.toMB() : NaN;
30
+ const usedMemMb = memoryInfo.success ? memoryInfo.data.used?.toMB() : NaN;
31
+ return {
32
+ columns,
33
+ rooms: rooms.map((room) => {
34
+ const data = JSON.parse(JSON.stringify(room));
35
+ connections += room.clients;
36
+ data.locked = room.locked || false;
37
+ data.private = room.private;
38
+ data.maxClients = `${room.maxClients}`;
39
+ data.elapsedTime = Date.now() - new Date(room.createdAt).getTime();
40
+ return data;
41
+ }),
42
+ connections,
43
+ cpu,
44
+ memory: { totalMemMb, usedMemMb }
45
+ };
46
+ } catch (e) {
47
+ console.error(e.message);
48
+ return new Response(JSON.stringify({ message: e.message }), {
49
+ status: 500,
50
+ headers: { "content-type": "application/json" }
51
+ });
52
+ }
53
+ }),
54
+ "monitor-api-room": createEndpoint(`${prefix}/api/room`, {
55
+ method: "GET",
56
+ query: z.object({ roomId: z.string() }),
57
+ use
58
+ }, async (ctx) => {
59
+ const roomId = ctx.query.roomId;
60
+ try {
61
+ return await matchMaker.remoteRoomCall(roomId, "getInspectData");
62
+ } catch {
63
+ return new Response(JSON.stringify({ message: UNAVAILABLE_ROOM_ERROR.replace("$roomId", roomId) }), {
64
+ status: 500,
65
+ headers: { "content-type": "application/json" }
66
+ });
67
+ }
68
+ }),
69
+ "monitor-api-room-call": createEndpoint(`${prefix}/api/room/call`, {
70
+ method: "GET",
71
+ query: z.object({ roomId: z.string(), method: z.string(), args: z.string() }),
72
+ use
73
+ }, async (ctx) => {
74
+ const { roomId, method } = ctx.query;
75
+ try {
76
+ const args = JSON.parse(ctx.query.args);
77
+ const data = await matchMaker.remoteRoomCall(roomId, method, args);
78
+ return data ?? {};
79
+ } catch {
80
+ return new Response(JSON.stringify({ message: UNAVAILABLE_ROOM_ERROR.replace("$roomId", roomId) }), {
81
+ status: 500,
82
+ headers: { "content-type": "application/json" }
83
+ });
84
+ }
85
+ }),
86
+ "monitor-index": createEndpoint(`${prefix}/`, { method: "GET", use }, async () => {
87
+ return serveStatic(SPA_DIST, "");
88
+ }),
89
+ "monitor-static": createEndpoint(`${prefix}/**:splat`, { method: "GET", use }, async (ctx) => {
90
+ return serveStatic(SPA_DIST, ctx.params.splat);
91
+ })
92
+ };
93
+ const SPA_DIST_RESOLVED = path.resolve(SPA_DIST);
94
+ return dualModeEndpoints(endpoints, {
95
+ catchAllKey: "monitor-static",
96
+ buildMiddleware: ({ specificRouter, specificHandler, fullHandler }) => (req, res, next) => {
97
+ if (req.method !== "GET") {
98
+ return next();
99
+ }
100
+ const dispatchUrl = (req.originalUrl ?? req.url ?? "").split("?")[0];
101
+ if (prefix && dispatchUrl === prefix) {
102
+ res.writeHead(301, { location: `${prefix}/` });
103
+ res.end();
104
+ return;
105
+ }
106
+ const route = specificRouter.findRoute("GET", dispatchUrl);
107
+ if (route && route.data?.path === dispatchUrl) {
108
+ return specificHandler(req, res).catch(next);
109
+ }
110
+ if (!dispatchUrl.startsWith(prefix)) {
111
+ return next();
112
+ }
113
+ const rel = dispatchUrl.slice(prefix.length).replace(/^\/+/, "");
114
+ if (!rel || rel.includes("..")) {
115
+ return next();
116
+ }
117
+ const filePath = path.resolve(SPA_DIST_RESOLVED, rel);
118
+ if (!filePath.startsWith(SPA_DIST_RESOLVED + path.sep)) {
119
+ return next();
120
+ }
121
+ fs.stat(filePath).then((stat) => {
122
+ if (stat.isFile()) {
123
+ fullHandler(req, res).catch(next);
124
+ } else {
125
+ next();
126
+ }
127
+ }).catch(() => next());
128
+ }
129
+ });
13
130
  }
14
131
  export {
15
132
  monitor
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src-backend/index.ts"],
4
- "sourcesContent": ["import express from 'express';\nimport path from 'path';\n\n// required for ESM support. (esbuild uses it)\nimport { fileURLToPath } from 'url';\n\nimport { getAPI } from './api.js';\nimport './ext/Room.js';\n\nconst frontendDirectory = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \"..\", \"build\", \"static\");\n\nexport interface MonitorOptions {\n columns: Array<\n 'roomId' |\n 'name' |\n 'clients' |\n 'maxClients' |\n 'locked' |\n 'elapsedTime' |\n { metadata: string } |\n 'processId' |\n \"publicAddress\"\n >\n}\n\nexport function monitor (opts: Partial<MonitorOptions> = {}): express.Router {\n const router = express.Router();\n router.use(express.static(frontendDirectory));\n router.use('/api', getAPI(opts));\n return router;\n}\n"],
5
- "mappings": ";AAAA,OAAO,aAAa;AACpB,OAAO,UAAU;AAGjB,SAAS,qBAAqB;AAE9B,SAAS,cAAc;AACvB,OAAO;AAEP,IAAM,oBAAoB,KAAK,QAAQ,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC,GAAG,MAAM,SAAS,QAAQ;AAgBrG,SAAS,QAAS,OAAgC,CAAC,GAAmB;AACzE,QAAM,SAAS,QAAQ,OAAO;AAC9B,SAAO,IAAI,QAAQ,OAAO,iBAAiB,CAAC;AAC5C,SAAO,IAAI,QAAQ,OAAO,IAAI,CAAC;AAC/B,SAAO;AACX;",
4
+ "sourcesContent": ["import fs from 'fs/promises';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport { z } from 'zod';\nimport { createEndpoint, dualModeEndpoints, matchMaker, type Endpoint } from '@colyseus/core';\nimport { OSUtils } from 'node-os-utils';\n\nimport { serveStatic } from './serve-static.js';\nimport './ext/Room.js';\n\nconst osutils = new OSUtils();\nconst UNAVAILABLE_ROOM_ERROR = \"@colyseus/monitor: room $roomId is not available anymore.\";\nconst SPA_DIST = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'build', 'static');\n\nexport interface MonitorOptions {\n /** Mount prefix used when spread into `createRouter`. Ignored in express-middleware mode. Defaults to `''`. */\n prefix?: string;\n /** Better-call middleware applied to every monitor endpoint \u2014 use for auth gating. */\n use?: any[];\n /** Columns shown in the rooms grid. */\n columns?: Array<\n 'roomId' |\n 'name' |\n 'clients' |\n 'maxClients' |\n 'locked' |\n 'elapsedTime' |\n { metadata: string } |\n 'processId' |\n 'publicAddress'\n >;\n}\n\nexport function monitor(opts: MonitorOptions = {}) {\n const prefix = opts.prefix ?? '/monitor';\n const use = opts.use ?? [];\n const columnsOpt = opts.columns;\n\n const endpoints: Record<string, Endpoint> = {\n 'monitor-api-rooms': createEndpoint(`${prefix}/api`, { method: 'GET', use }, async () => {\n try {\n const rooms: any[] = await matchMaker.query({});\n const columns = columnsOpt ?? ['roomId', 'name', 'clients', 'maxClients', 'locked', 'elapsedTime'];\n\n if (!columnsOpt && rooms[0] && rooms[0].publicAddress !== undefined) {\n columns.push('publicAddress');\n }\n\n let connections = 0;\n const cpuUsage = await osutils.cpu.usage();\n const cpu = cpuUsage.success ? cpuUsage.data : NaN;\n const memoryInfo = await osutils.memory.info();\n const totalMemMb = memoryInfo.success ? memoryInfo.data.total?.toMB() : NaN;\n const usedMemMb = memoryInfo.success ? memoryInfo.data.used?.toMB() : NaN;\n\n return {\n columns,\n rooms: rooms.map((room) => {\n const data = JSON.parse(JSON.stringify(room));\n connections += room.clients;\n data.locked = room.locked || false;\n data.private = room.private;\n data.maxClients = `${room.maxClients}`;\n data.elapsedTime = Date.now() - new Date(room.createdAt).getTime();\n return data;\n }),\n connections,\n cpu,\n memory: { totalMemMb, usedMemMb },\n };\n } catch (e: any) {\n console.error(e.message);\n return new Response(JSON.stringify({ message: e.message }), {\n status: 500,\n headers: { 'content-type': 'application/json' },\n });\n }\n }),\n\n 'monitor-api-room': createEndpoint(`${prefix}/api/room`, {\n method: 'GET',\n query: z.object({ roomId: z.string() }),\n use,\n }, async (ctx) => {\n const roomId = ctx.query.roomId;\n try {\n return await matchMaker.remoteRoomCall(roomId, 'getInspectData');\n } catch {\n return new Response(JSON.stringify({ message: UNAVAILABLE_ROOM_ERROR.replace('$roomId', roomId) }), {\n status: 500,\n headers: { 'content-type': 'application/json' },\n });\n }\n }),\n\n 'monitor-api-room-call': createEndpoint(`${prefix}/api/room/call`, {\n method: 'GET',\n query: z.object({ roomId: z.string(), method: z.string(), args: z.string() }),\n use,\n }, async (ctx) => {\n const { roomId, method } = ctx.query;\n try {\n const args = JSON.parse(ctx.query.args);\n const data = await matchMaker.remoteRoomCall(roomId, method, args);\n return data ?? {};\n } catch {\n return new Response(JSON.stringify({ message: UNAVAILABLE_ROOM_ERROR.replace('$roomId', roomId) }), {\n status: 500,\n headers: { 'content-type': 'application/json' },\n });\n }\n }),\n\n 'monitor-index': createEndpoint(`${prefix}/`, { method: 'GET', use }, async () => {\n return serveStatic(SPA_DIST, '');\n }),\n\n 'monitor-static': createEndpoint(`${prefix}/**:splat`, { method: 'GET', use }, async (ctx) => {\n return serveStatic(SPA_DIST, (ctx.params as any).splat);\n }),\n };\n\n const SPA_DIST_RESOLVED = path.resolve(SPA_DIST);\n\n // Express compat \u2014 works with either `app.use(\"/monitor\", monitor())` or\n // `app.use(\"/\", monitor())`. Routing decisions use originalUrl (the same\n // string better-call's getRequest uses to build the dispatched Request URL),\n // so the middleware's match check and the actual dispatch always agree.\n return dualModeEndpoints(endpoints, {\n catchAllKey: 'monitor-static',\n buildMiddleware: ({ specificRouter, specificHandler, fullHandler }) => (req, res, next) => {\n if (req.method !== 'GET') { return next(); }\n\n const dispatchUrl = ((req as any).originalUrl ?? req.url ?? '').split('?')[0]!;\n\n // Bare prefix (`/monitor`) \u2192 301 to canonical `/monitor/`.\n if (prefix && dispatchUrl === prefix) {\n res.writeHead(301, { location: `${prefix}/` });\n res.end();\n return;\n }\n\n const route = specificRouter.findRoute('GET', dispatchUrl);\n if (route && route.data?.path === dispatchUrl) {\n return specificHandler(req as any, res as any).catch(next);\n }\n\n // Asset request \u2014 only delegate if the file exists on disk.\n if (!dispatchUrl.startsWith(prefix)) { return next(); }\n const rel = dispatchUrl.slice(prefix.length).replace(/^\\/+/, '');\n if (!rel || rel.includes('..')) { return next(); }\n const filePath = path.resolve(SPA_DIST_RESOLVED, rel);\n if (!filePath.startsWith(SPA_DIST_RESOLVED + path.sep)) { return next(); }\n\n fs.stat(filePath).then((stat) => {\n if (stat.isFile()) {\n fullHandler(req as any, res as any).catch(next);\n } else {\n next();\n }\n }).catch(() => next());\n },\n });\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,107 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src-backend/serve-static.ts
30
+ var serve_static_exports = {};
31
+ __export(serve_static_exports, {
32
+ serveStatic: () => serveStatic
33
+ });
34
+ module.exports = __toCommonJS(serve_static_exports);
35
+ var import_promises = __toESM(require("fs/promises"));
36
+ var import_path = __toESM(require("path"));
37
+ var MIME = {
38
+ ".html": "text/html; charset=utf-8",
39
+ ".js": "application/javascript; charset=utf-8",
40
+ ".mjs": "application/javascript; charset=utf-8",
41
+ ".css": "text/css; charset=utf-8",
42
+ ".json": "application/json; charset=utf-8",
43
+ ".svg": "image/svg+xml",
44
+ ".png": "image/png",
45
+ ".jpg": "image/jpeg",
46
+ ".jpeg": "image/jpeg",
47
+ ".gif": "image/gif",
48
+ ".ico": "image/x-icon",
49
+ ".woff": "font/woff",
50
+ ".woff2": "font/woff2",
51
+ ".ttf": "font/ttf",
52
+ ".map": "application/json; charset=utf-8",
53
+ ".txt": "text/plain; charset=utf-8"
54
+ };
55
+ async function serveStatic(root, relPath) {
56
+ const safe = sanitize(relPath != null ? relPath : "");
57
+ const filePath = safe ? import_path.default.join(root, safe) : import_path.default.join(root, "index.html");
58
+ const resolved = import_path.default.resolve(filePath);
59
+ const resolvedRoot = import_path.default.resolve(root);
60
+ if (!resolved.startsWith(resolvedRoot + import_path.default.sep) && resolved !== resolvedRoot) {
61
+ return new Response("forbidden", { status: 403 });
62
+ }
63
+ const data = await tryRead(resolved);
64
+ if (data) {
65
+ return fileResponse(data, mimeOf(resolved));
66
+ }
67
+ if (!import_path.default.extname(safe)) {
68
+ const index = await tryRead(import_path.default.join(resolvedRoot, "index.html"));
69
+ if (index) {
70
+ return fileResponse(index, "text/html; charset=utf-8");
71
+ }
72
+ }
73
+ return new Response("not found", { status: 404 });
74
+ }
75
+ function sanitize(p) {
76
+ return p.replace(/^\/+/, "").split("/").filter((seg) => seg && seg !== ".." && seg !== ".").join("/");
77
+ }
78
+ function mimeOf(filePath) {
79
+ var _a;
80
+ return (_a = MIME[import_path.default.extname(filePath).toLowerCase()]) != null ? _a : "application/octet-stream";
81
+ }
82
+ async function tryRead(filePath) {
83
+ try {
84
+ const stat = await import_promises.default.stat(filePath);
85
+ if (!stat.isFile()) {
86
+ return null;
87
+ }
88
+ return await import_promises.default.readFile(filePath);
89
+ } catch (e) {
90
+ return null;
91
+ }
92
+ }
93
+ function fileResponse(buf, contentType) {
94
+ return new Response(new Uint8Array(buf), {
95
+ status: 200,
96
+ headers: {
97
+ "content-type": contentType,
98
+ "content-length": String(buf.length),
99
+ "cache-control": "no-cache",
100
+ "connection": "close"
101
+ }
102
+ });
103
+ }
104
+ // Annotate the CommonJS export names for ESM import in node:
105
+ 0 && (module.exports = {
106
+ serveStatic
107
+ });
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src-backend/serve-static.ts"],
4
+ "sourcesContent": ["// Static-file helper. Duplicated from @colyseus/admin / @colyseus/playground.\nimport fs from 'fs/promises';\nimport path from 'path';\n\nconst MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'application/javascript; charset=utf-8',\n '.mjs': 'application/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.ico': 'image/x-icon',\n '.woff': 'font/woff',\n '.woff2': 'font/woff2',\n '.ttf': 'font/ttf',\n '.map': 'application/json; charset=utf-8',\n '.txt': 'text/plain; charset=utf-8',\n};\n\nexport async function serveStatic(root: string, relPath: string | undefined): Promise<Response> {\n const safe = sanitize(relPath ?? '');\n const filePath = safe ? path.join(root, safe) : path.join(root, 'index.html');\n\n const resolved = path.resolve(filePath);\n const resolvedRoot = path.resolve(root);\n if (!resolved.startsWith(resolvedRoot + path.sep) && resolved !== resolvedRoot) {\n return new Response('forbidden', { status: 403 });\n }\n\n const data = await tryRead(resolved);\n if (data) { return fileResponse(data, mimeOf(resolved)); }\n\n if (!path.extname(safe)) {\n const index = await tryRead(path.join(resolvedRoot, 'index.html'));\n if (index) { return fileResponse(index, 'text/html; charset=utf-8'); }\n }\n\n return new Response('not found', { status: 404 });\n}\n\nfunction sanitize(p: string): string {\n return p.replace(/^\\/+/, '').split('/').filter((seg) => seg && seg !== '..' && seg !== '.').join('/');\n}\n\nfunction mimeOf(filePath: string): string {\n return MIME[path.extname(filePath).toLowerCase()] ?? 'application/octet-stream';\n}\n\nasync function tryRead(filePath: string): Promise<Buffer | null> {\n try {\n const stat = await fs.stat(filePath);\n if (!stat.isFile()) { return null; }\n return await fs.readFile(filePath);\n } catch {\n return null;\n }\n}\n\nfunction fileResponse(buf: Buffer, contentType: string): Response {\n return new Response(new Uint8Array(buf) as any, {\n status: 200,\n headers: {\n 'content-type': contentType,\n 'content-length': String(buf.length),\n 'cache-control': 'no-cache',\n 'connection': 'close',\n },\n });\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,sBAAe;AACf,kBAAiB;AAEjB,IAAM,OAA+B;AAAA,EACnC,SAAS;AAAA,EACT,OAAS;AAAA,EACT,QAAS;AAAA,EACT,QAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAS;AAAA,EACT,QAAS;AAAA,EACT,QAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAS;AAAA,EACT,QAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAS;AAAA,EACT,QAAS;AAAA,EACT,QAAS;AACX;AAEA,eAAsB,YAAY,MAAc,SAAgD;AAC9F,QAAM,OAAO,SAAS,4BAAW,EAAE;AACnC,QAAM,WAAW,OAAO,YAAAA,QAAK,KAAK,MAAM,IAAI,IAAI,YAAAA,QAAK,KAAK,MAAM,YAAY;AAE5E,QAAM,WAAW,YAAAA,QAAK,QAAQ,QAAQ;AACtC,QAAM,eAAe,YAAAA,QAAK,QAAQ,IAAI;AACtC,MAAI,CAAC,SAAS,WAAW,eAAe,YAAAA,QAAK,GAAG,KAAK,aAAa,cAAc;AAC9E,WAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClD;AAEA,QAAM,OAAO,MAAM,QAAQ,QAAQ;AACnC,MAAI,MAAM;AAAE,WAAO,aAAa,MAAM,OAAO,QAAQ,CAAC;AAAA,EAAG;AAEzD,MAAI,CAAC,YAAAA,QAAK,QAAQ,IAAI,GAAG;AACvB,UAAM,QAAQ,MAAM,QAAQ,YAAAA,QAAK,KAAK,cAAc,YAAY,CAAC;AACjE,QAAI,OAAO;AAAE,aAAO,aAAa,OAAO,0BAA0B;AAAA,IAAG;AAAA,EACvE;AAEA,SAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,IAAI,CAAC;AAClD;AAEA,SAAS,SAAS,GAAmB;AACnC,SAAO,EAAE,QAAQ,QAAQ,EAAE,EAAE,MAAM,GAAG,EAAE,OAAO,CAAC,QAAQ,OAAO,QAAQ,QAAQ,QAAQ,GAAG,EAAE,KAAK,GAAG;AACtG;AAEA,SAAS,OAAO,UAA0B;AAhD1C;AAiDE,UAAO,UAAK,YAAAA,QAAK,QAAQ,QAAQ,EAAE,YAAY,CAAC,MAAzC,YAA8C;AACvD;AAEA,eAAe,QAAQ,UAA0C;AAC/D,MAAI;AACF,UAAM,OAAO,MAAM,gBAAAC,QAAG,KAAK,QAAQ;AACnC,QAAI,CAAC,KAAK,OAAO,GAAG;AAAE,aAAO;AAAA,IAAM;AACnC,WAAO,MAAM,gBAAAA,QAAG,SAAS,QAAQ;AAAA,EACnC,SAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAa,KAAa,aAA+B;AAChE,SAAO,IAAI,SAAS,IAAI,WAAW,GAAG,GAAU;AAAA,IAC9C,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,kBAAkB,OAAO,IAAI,MAAM;AAAA,MACnC,iBAAiB;AAAA,MACjB,cAAc;AAAA,IAChB;AAAA,EACF,CAAC;AACH;",
6
+ "names": ["path", "fs"]
7
+ }
@@ -0,0 +1 @@
1
+ export declare function serveStatic(root: string, relPath: string | undefined): Promise<Response>;