@colyseus/monitor 0.18.1 → 0.18.3
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/ext/Room.cjs +1 -0
- package/build/ext/Room.cjs.map +1 -1
- package/build/index.cjs +5 -40
- package/build/index.cjs.map +3 -3
- package/build/index.d.ts +1 -1
- package/build/index.mjs +3 -37
- package/build/index.mjs.map +2 -2
- package/build/serve-static.cjs +3 -2
- package/build/serve-static.cjs.map +1 -1
- package/build/static/assets/{index-CDY0bZlU.js → index-D9MoG02G.js} +2 -2
- package/build/static/assets/{index-CDY0bZlU.js.map → index-D9MoG02G.js.map} +1 -1
- package/build/static/index.html +1 -1
- package/package.json +6 -2
- package/src-backend/index.ts +4 -42
package/build/ext/Room.cjs
CHANGED
package/build/ext/Room.cjs.map
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src-backend/ext/Room.ts"],
|
|
4
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": "
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/build/index.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const __cjsImportMetaUrl = require('node:url').pathToFileURL(__filename).href;
|
|
1
2
|
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -32,18 +33,15 @@ __export(index_exports, {
|
|
|
32
33
|
monitor: () => monitor
|
|
33
34
|
});
|
|
34
35
|
module.exports = __toCommonJS(index_exports);
|
|
35
|
-
var
|
|
36
|
-
var import_path = __toESM(require("path"));
|
|
37
|
-
var import_url = require("url");
|
|
36
|
+
var import_path = __toESM(require("path"), 1);
|
|
38
37
|
var import_zod = require("zod");
|
|
39
38
|
var import_core = require("@colyseus/core");
|
|
40
39
|
var import_node_os_utils = require("node-os-utils");
|
|
41
40
|
var import_serve_static = require("./serve-static.cjs");
|
|
42
41
|
var import_Room = require("./ext/Room.cjs");
|
|
43
|
-
var import_meta = {};
|
|
44
42
|
var osutils = new import_node_os_utils.OSUtils();
|
|
45
43
|
var UNAVAILABLE_ROOM_ERROR = "@colyseus/monitor: room $roomId is not available anymore.";
|
|
46
|
-
var SPA_DIST = import_path.default.resolve(
|
|
44
|
+
var SPA_DIST = import_path.default.resolve(__dirname, "..", "build", "static");
|
|
47
45
|
function monitor(opts = {}) {
|
|
48
46
|
var _a, _b;
|
|
49
47
|
const prefix = (_a = opts.prefix) != null ? _a : "/monitor";
|
|
@@ -126,43 +124,10 @@ function monitor(opts = {}) {
|
|
|
126
124
|
return (0, import_serve_static.serveStatic)(SPA_DIST, ctx.params.splat);
|
|
127
125
|
})
|
|
128
126
|
};
|
|
129
|
-
const SPA_DIST_RESOLVED = import_path.default.resolve(SPA_DIST);
|
|
130
127
|
return (0, import_core.dualModeEndpoints)(endpoints, {
|
|
131
128
|
catchAllKey: "monitor-static",
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
}
|
|
129
|
+
prefix,
|
|
130
|
+
staticDir: SPA_DIST
|
|
166
131
|
});
|
|
167
132
|
}
|
|
168
133
|
// Annotate the CommonJS export names for ESM import in node:
|
package/build/index.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src-backend/index.ts"],
|
|
4
|
-
"sourcesContent": ["import
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": ["path", "_a", "_b"
|
|
4
|
+
"sourcesContent": ["import path from 'path';\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(import.meta.dirname, '..', 'build', 'static');\n\nexport interface MonitorOptions {\n /** Endpoint path prefix \u2014 namespaces the routes when spread into `createRouter`, honored at root express mounts. Under a path mount (`app.use(\"/stats\", monitor())`) the mount path takes over. Defaults to `'/monitor'`. */\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 return dualModeEndpoints(endpoints, {\n catchAllKey: 'monitor-static',\n prefix,\n staticDir: SPA_DIST,\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["path", "_a", "_b"]
|
|
7
7
|
}
|
package/build/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Endpoint } from '@colyseus/core';
|
|
2
2
|
import './ext/Room.js';
|
|
3
3
|
export interface MonitorOptions {
|
|
4
|
-
/**
|
|
4
|
+
/** Endpoint path prefix — namespaces the routes when spread into `createRouter`, honored at root express mounts. Under a path mount (`app.use("/stats", monitor())`) the mount path takes over. Defaults to `'/monitor'`. */
|
|
5
5
|
prefix?: string;
|
|
6
6
|
/** Better-call middleware applied to every monitor endpoint — use for auth gating. */
|
|
7
7
|
use?: any[];
|
package/build/index.mjs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
// src-backend/index.ts
|
|
2
|
-
import fs from "fs/promises";
|
|
3
2
|
import path from "path";
|
|
4
|
-
import { fileURLToPath } from "url";
|
|
5
3
|
import { z } from "zod";
|
|
6
4
|
import { createEndpoint, dualModeEndpoints, matchMaker } from "@colyseus/core";
|
|
7
5
|
import { OSUtils } from "node-os-utils";
|
|
@@ -9,7 +7,7 @@ import { serveStatic } from "./serve-static.mjs";
|
|
|
9
7
|
import "./ext/Room.mjs";
|
|
10
8
|
var osutils = new OSUtils();
|
|
11
9
|
var UNAVAILABLE_ROOM_ERROR = "@colyseus/monitor: room $roomId is not available anymore.";
|
|
12
|
-
var SPA_DIST = path.resolve(
|
|
10
|
+
var SPA_DIST = path.resolve(import.meta.dirname, "..", "build", "static");
|
|
13
11
|
function monitor(opts = {}) {
|
|
14
12
|
const prefix = opts.prefix ?? "/monitor";
|
|
15
13
|
const use = opts.use ?? [];
|
|
@@ -90,42 +88,10 @@ function monitor(opts = {}) {
|
|
|
90
88
|
return serveStatic(SPA_DIST, ctx.params.splat);
|
|
91
89
|
})
|
|
92
90
|
};
|
|
93
|
-
const SPA_DIST_RESOLVED = path.resolve(SPA_DIST);
|
|
94
91
|
return dualModeEndpoints(endpoints, {
|
|
95
92
|
catchAllKey: "monitor-static",
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
}
|
|
93
|
+
prefix,
|
|
94
|
+
staticDir: SPA_DIST
|
|
129
95
|
});
|
|
130
96
|
}
|
|
131
97
|
export {
|
package/build/index.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src-backend/index.ts"],
|
|
4
|
-
"sourcesContent": ["import
|
|
5
|
-
"mappings": ";AAAA,OAAO,
|
|
4
|
+
"sourcesContent": ["import path from 'path';\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(import.meta.dirname, '..', 'build', 'static');\n\nexport interface MonitorOptions {\n /** Endpoint path prefix \u2014 namespaces the routes when spread into `createRouter`, honored at root express mounts. Under a path mount (`app.use(\"/stats\", monitor())`) the mount path takes over. Defaults to `'/monitor'`. */\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 return dualModeEndpoints(endpoints, {\n catchAllKey: 'monitor-static',\n prefix,\n staticDir: SPA_DIST,\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/build/serve-static.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const __cjsImportMetaUrl = require('node:url').pathToFileURL(__filename).href;
|
|
1
2
|
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -32,8 +33,8 @@ __export(serve_static_exports, {
|
|
|
32
33
|
serveStatic: () => serveStatic
|
|
33
34
|
});
|
|
34
35
|
module.exports = __toCommonJS(serve_static_exports);
|
|
35
|
-
var import_promises = __toESM(require("fs/promises"));
|
|
36
|
-
var import_path = __toESM(require("path"));
|
|
36
|
+
var import_promises = __toESM(require("fs/promises"), 1);
|
|
37
|
+
var import_path = __toESM(require("path"), 1);
|
|
37
38
|
var MIME = {
|
|
38
39
|
".html": "text/html; charset=utf-8",
|
|
39
40
|
".js": "application/javascript; charset=utf-8",
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src-backend/serve-static.ts"],
|
|
4
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": "
|
|
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
6
|
"names": ["path", "fs"]
|
|
7
7
|
}
|