@colyseus/monitor 0.16.0-preview.2 → 0.16.0-preview.4

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.
@@ -1,140 +1,151 @@
1
- .json-view {
2
- display: block;
3
- color: #4d4d4d;
4
- --json-property: #009033;
5
- --json-index: #676dff;
6
- --json-number: #676dff;
7
- --json-string: #b2762e;
8
- --json-boolean: #dc155e;
9
- --json-null: #dc155e;
10
- }
11
- .json-view .json-view--property {
12
- color: var(--json-property);
13
- }
14
- .json-view .json-view--index {
15
- color: var(--json-index);
16
- }
17
- .json-view .json-view--number {
18
- color: var(--json-number);
19
- }
20
- .json-view .json-view--string {
21
- color: var(--json-string);
22
- }
23
- .json-view .json-view--boolean {
24
- color: var(--json-boolean);
25
- }
26
- .json-view .json-view--null {
27
- color: var(--json-null);
28
- }
29
-
30
- .json-view .jv-indent {
31
- padding-left: 1em;
32
- }
33
- .json-view .jv-chevron {
34
- display: inline-block;
35
- vertical-align: -20%;
36
- cursor: pointer;
37
- opacity: 0.4;
38
- width: 1em;
39
- height: 1em;
40
- }
41
- :is(.json-view .jv-chevron:hover, .json-view .jv-size:hover + .jv-chevron) {
42
- opacity: 0.8;
43
- }
44
- .json-view .jv-size {
45
- cursor: pointer;
46
- opacity: 0.4;
47
- font-size: 0.875em;
48
- font-style: italic;
49
- margin-left: 0.5em;
50
- vertical-align: -5%;
51
- line-height: 1;
52
- }
53
-
54
- .json-view :is(.json-view--copy, .json-view--edit) {
55
- display: none;
56
- width: 1em;
57
- height: 1em;
58
- margin-left: 0.25em;
59
- cursor: pointer;
60
- }
61
-
62
- .json-view .json-view--input {
63
- width: 120px;
64
- margin-left: 0.25em;
65
- border-radius: 4px;
66
- border: 1px solid currentColor;
67
- padding: 0px 4px;
68
- font-size: 87.5%;
69
- line-height: 1.25;
70
- background: transparent;
71
- }
72
- .json-view .json-view--deleting {
73
- outline: 1px solid #da0000;
74
- background-color: #da000011;
75
- text-decoration-line: line-through;
76
- }
77
-
78
- :is(.json-view:hover, .json-view--pair:hover) > :is(.json-view--copy, .json-view--edit) {
79
- display: inline-block;
80
- }
81
-
82
- .json-view .jv-button {
83
- background: transparent;
84
- outline: none;
85
- border: none;
86
- cursor: pointer;
87
- }
88
- .json-view .cursor-pointer {
89
- cursor: pointer;
90
- }
91
-
92
- /* Themes */
93
- .json-view_a11y {
94
- color: #545454;
95
- --json-property: #aa5d00;
96
- --json-index: #007299;
97
- --json-number: #007299;
98
- --json-string: #008000;
99
- --json-boolean: #d91e18;
100
- --json-null: #d91e18;
101
- }
102
- .json-view_github {
103
- color: #005cc5;
104
- --json-property: #005cc5;
105
- --json-index: #005cc5;
106
- --json-number: #005cc5;
107
- --json-string: #032f62;
108
- --json-boolean: #005cc5;
109
- --json-null: #005cc5;
110
- }
111
- .json-view_vscode {
112
- color: #005cc5;
113
- --json-property: #0451a5;
114
- --json-index: #0000ff;
115
- --json-number: #0000ff;
116
- --json-string: #a31515;
117
- --json-boolean: #0000ff;
118
- --json-null: #0000ff;
119
- }
120
- .json-view_atom {
121
- color: #383a42;
122
- --json-property: #e45649;
123
- --json-index: #986801;
124
- --json-number: #986801;
125
- --json-string: #50a14f;
126
- --json-boolean: #0184bc;
127
- --json-null: #0184bc;
128
- }
129
- .json-view_winter-is-coming {
130
- color: #0431fa;
131
- --json-property: #3a9685;
132
- --json-index: #ae408b;
133
- --json-number: #ae408b;
134
- --json-string: #8123a9;
135
- --json-boolean: #0184bc;
136
- --json-null: #0184bc;
137
- }
1
+ .json-view {
2
+ display: block;
3
+ color: #4d4d4d;
4
+ text-align: left;
5
+ --json-property: #009033;
6
+ --json-index: #676dff;
7
+ --json-number: #676dff;
8
+ --json-string: #b2762e;
9
+ --json-boolean: #dc155e;
10
+ --json-null: #dc155e;
11
+ }
12
+ .json-view .json-view--property {
13
+ color: var(--json-property);
14
+ }
15
+ .json-view .json-view--index {
16
+ color: var(--json-index);
17
+ }
18
+ .json-view .json-view--number {
19
+ color: var(--json-number);
20
+ }
21
+ .json-view .json-view--string {
22
+ color: var(--json-string);
23
+ }
24
+ .json-view .json-view--boolean {
25
+ color: var(--json-boolean);
26
+ }
27
+ .json-view .json-view--null {
28
+ color: var(--json-null);
29
+ }
30
+
31
+ .json-view .jv-indent {
32
+ padding-left: 1em;
33
+ }
34
+ .json-view .jv-chevron {
35
+ display: inline-block;
36
+ vertical-align: -20%;
37
+ cursor: pointer;
38
+ opacity: 0.4;
39
+ width: 1em;
40
+ height: 1em;
41
+ }
42
+ :is(.json-view .jv-chevron:hover, .json-view .jv-size:hover + .jv-chevron) {
43
+ opacity: 0.8;
44
+ }
45
+ .json-view .jv-size {
46
+ cursor: pointer;
47
+ opacity: 0.4;
48
+ font-size: 0.875em;
49
+ font-style: italic;
50
+ margin-left: 0.5em;
51
+ vertical-align: -5%;
52
+ line-height: 1;
53
+ }
54
+
55
+ .json-view :is(.json-view--copy, .json-view--edit),
56
+ .json-view .json-view--link svg {
57
+ display: none;
58
+ width: 1em;
59
+ height: 1em;
60
+ margin-left: 0.25em;
61
+ cursor: pointer;
62
+ }
63
+
64
+ .json-view .json-view--input {
65
+ width: 120px;
66
+ margin-left: 0.25em;
67
+ border-radius: 4px;
68
+ border: 1px solid currentColor;
69
+ padding: 0px 4px;
70
+ font-size: 87.5%;
71
+ line-height: 1.25;
72
+ background: transparent;
73
+ }
74
+ .json-view .json-view--deleting {
75
+ outline: 1px solid #da0000;
76
+ background-color: #da000011;
77
+ text-decoration-line: line-through;
78
+ }
79
+
80
+ :is(.json-view:hover, .json-view--pair:hover) > :is(.json-view--copy, .json-view--edit),
81
+ :is(.json-view:hover, .json-view--pair:hover) > .json-view--link svg {
82
+ display: inline-block;
83
+ }
84
+
85
+ .json-view .jv-button {
86
+ background: transparent;
87
+ outline: none;
88
+ border: none;
89
+ cursor: pointer;
90
+ color: inherit;
91
+ }
92
+ .json-view .cursor-pointer {
93
+ cursor: pointer;
94
+ }
95
+
96
+ .json-view svg {
97
+ vertical-align: -10%;
98
+ }
99
+ .jv-size-chevron ~ svg {
100
+ vertical-align: -16%;
101
+ }
102
+
103
+ /* Themes */
104
+ .json-view_a11y {
105
+ color: #545454;
106
+ --json-property: #aa5d00;
107
+ --json-index: #007299;
108
+ --json-number: #007299;
109
+ --json-string: #008000;
110
+ --json-boolean: #d91e18;
111
+ --json-null: #d91e18;
112
+ }
113
+ .json-view_github {
114
+ color: #005cc5;
115
+ --json-property: #005cc5;
116
+ --json-index: #005cc5;
117
+ --json-number: #005cc5;
118
+ --json-string: #032f62;
119
+ --json-boolean: #005cc5;
120
+ --json-null: #005cc5;
121
+ }
122
+ .json-view_vscode {
123
+ color: #005cc5;
124
+ --json-property: #0451a5;
125
+ --json-index: #0000ff;
126
+ --json-number: #0000ff;
127
+ --json-string: #a31515;
128
+ --json-boolean: #0000ff;
129
+ --json-null: #0000ff;
130
+ }
131
+ .json-view_atom {
132
+ color: #383a42;
133
+ --json-property: #e45649;
134
+ --json-index: #986801;
135
+ --json-number: #986801;
136
+ --json-string: #50a14f;
137
+ --json-boolean: #0184bc;
138
+ --json-null: #0184bc;
139
+ }
140
+ .json-view_winter-is-coming {
141
+ color: #0431fa;
142
+ --json-property: #3a9685;
143
+ --json-index: #ae408b;
144
+ --json-number: #ae408b;
145
+ --json-string: #8123a9;
146
+ --json-boolean: #0184bc;
147
+ --json-null: #0184bc;
148
+ }
138
149
  html, body {
139
150
  margin: 0;
140
151
  padding: 0;
@@ -1 +1 @@
1
- {"version":3,"file":"styles.css","sources":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;A","sourceRoot":""}
1
+ {"version":3,"file":"styles.css","sources":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;A","sourceRoot":""}
package/build.mjs ADDED
@@ -0,0 +1,90 @@
1
+ import path from 'path';
2
+ import glob from 'fast-glob';
3
+ import { fileURLToPath } from 'url';
4
+ import ts from "typescript";
5
+ import esbuild from "esbuild";
6
+
7
+ // we need to change up how __dirname is used for ES6 purposes
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+
10
+ async function main() {
11
+ // Absolute path to package directory
12
+ const basePath = __dirname;
13
+ const target = "es2017";
14
+
15
+ // Get all .ts as input files
16
+ const entryPoints = glob.sync(path.resolve(basePath, "src-backend", "**", "**.ts")
17
+ .replace(/\\/g, '/')); // windows support
18
+
19
+ const outdir = path.join(basePath, 'build');
20
+
21
+ // Emit only .d.ts files
22
+ const emitTSDeclaration = () => {
23
+ console.log("Generating .d.ts...");
24
+ const program = ts.createProgram(entryPoints, {
25
+ declaration: true,
26
+ emitDeclarationOnly: true,
27
+ skipLibCheck: true,
28
+ module: "commonjs",
29
+ target,
30
+ outDir: outdir,
31
+ esModuleInterop: true,
32
+ experimentalDecorators: true,
33
+ });
34
+ const emitResult = program.emit();
35
+
36
+ const allDiagnostics = ts
37
+ .getPreEmitDiagnostics(program)
38
+ .concat(emitResult.diagnostics);
39
+
40
+ allDiagnostics.forEach(diagnostic => {
41
+ if (diagnostic.file) {
42
+ const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
43
+ const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
44
+ console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
45
+ } else {
46
+ console.log(ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"));
47
+ }
48
+ });
49
+ }
50
+
51
+ // CommonJS output
52
+ console.log("Generating CJS build...");
53
+ esbuild.build({
54
+ entryPoints,
55
+ outdir,
56
+ target,
57
+ format: "cjs",
58
+ target: "es2017",
59
+ sourcemap: "external",
60
+ platform: "node",
61
+ });
62
+
63
+ // ESM output
64
+ console.log("Generating ESM build...");
65
+ esbuild.build({
66
+ entryPoints,
67
+ outdir,
68
+ target,
69
+ format: "esm",
70
+ bundle: true,
71
+ sourcemap: "external",
72
+ platform: "node",
73
+ outExtension: { '.js': '.mjs', },
74
+ plugins: [{
75
+ name: 'add-mjs',
76
+ setup(build) {
77
+ build.onResolve({ filter: /.*/ }, (args) => {
78
+ if (args.importer) return { path: args.path.replace(/^\.(.*)\.js$/, '.$1.mjs'), external: true }
79
+ })
80
+ },
81
+ }]
82
+ });
83
+
84
+ // emit .d.ts files
85
+ emitTSDeclaration();
86
+ console.log("Done!");
87
+ }
88
+
89
+ export default await main();
90
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colyseus/monitor",
3
- "version": "0.16.0-preview.2",
3
+ "version": "0.16.0-preview.4",
4
4
  "description": "Web Monitoring Panel for Colyseus",
5
5
  "input": "./src/index.ts",
6
6
  "main": "./build/index.js",
@@ -8,8 +8,8 @@
8
8
  "typings": "./build/index.d.ts",
9
9
  "scripts": {
10
10
  "start": "ts-node example/Server.ts",
11
- "build": "tsc && NODE_OPTIONS=--openssl-legacy-provider webpack --env.production",
12
- "prepublishOnly": "tsc && npm run build"
11
+ "build": "node build.mjs && NODE_OPTIONS=--openssl-legacy-provider webpack --env.production",
12
+ "prepublishOnly": "node build.mjs && npm run build"
13
13
  },
14
14
  "repository": {
15
15
  "type": "git",
@@ -45,7 +45,9 @@
45
45
  "@types/superagent": "^3.5.8",
46
46
  "colyseus": "^0.16.0-preview.0",
47
47
  "css-loader": "^0.28.11",
48
+ "esbuild": "^0.23.0",
48
49
  "extract-text-webpack-plugin": "^4.0.0-beta.0",
50
+ "fast-glob": "^3.3.2",
49
51
  "file-loader": "^1.1.11",
50
52
  "history": "^5.3.0",
51
53
  "html-webpack-plugin": "^3.2.0",
@@ -0,0 +1,84 @@
1
+ import { matchMaker } from '@colyseus/core';
2
+
3
+ import express from 'express';
4
+ import osUtils from 'node-os-utils';
5
+
6
+ import type { MonitorOptions } from './index.js';
7
+
8
+ const UNAVAILABLE_ROOM_ERROR = "@colyseus/monitor: room $roomId is not available anymore.";
9
+
10
+ export function getAPI (opts: Partial<MonitorOptions>) {
11
+ const api = express.Router();
12
+
13
+ api.get("/", async (req: express.Request, res: express.Response) => {
14
+ try {
15
+ const rooms: any[] = await matchMaker.query({});
16
+ const columns = opts.columns || ['roomId', 'name', 'clients', 'maxClients', 'locked', 'elapsedTime'];
17
+
18
+ // extend columns to expose "publicAddress", if present
19
+ if (!opts.columns && rooms[0] && rooms[0].publicAddress !== undefined) {
20
+ columns.push("publicAddress");
21
+ }
22
+
23
+ let connections: number = 0;
24
+
25
+ res.json({
26
+ columns,
27
+ rooms: rooms.map(room => {
28
+ const data = JSON.parse(JSON.stringify(room));
29
+
30
+ connections += room.clients;
31
+
32
+ // additional data
33
+ data.locked = room.locked || false;
34
+ data.private = room.private;
35
+
36
+ data.maxClients = `${room.maxClients}`;
37
+
38
+ data.elapsedTime = Date.now() - new Date(room.createdAt).getTime();
39
+ return data;
40
+ }),
41
+
42
+ connections,
43
+ cpu: await osUtils.cpu.usage(),
44
+ memory: await osUtils.mem.used()
45
+ });
46
+ } catch (e) {
47
+ const message = e.message;
48
+ console.error(message);
49
+ res.status(500);
50
+ res.json({ message });
51
+ }
52
+ });
53
+
54
+ api.get("/room", async (req: express.Request, res: express.Response) => {
55
+ const roomId = req.query.roomId as string;
56
+ try {
57
+ const inspectData = await matchMaker.remoteRoomCall(roomId, "getInspectData");
58
+ res.json(inspectData);
59
+ } catch (e) {
60
+ const message = UNAVAILABLE_ROOM_ERROR.replace("$roomId", roomId);
61
+ console.error(message);
62
+ res.status(500);
63
+ res.json({ message });
64
+ }
65
+ });
66
+
67
+ api.get("/room/call", async (req: express.Request, res: express.Response) => {
68
+ const roomId = req.query.roomId as string;
69
+ const method = req.query.method as string;
70
+ const args = JSON.parse(req.query.args as string);
71
+
72
+ try {
73
+ const data = await matchMaker.remoteRoomCall(roomId, method, args);
74
+ res.json(data);
75
+ } catch (e) {
76
+ const message = UNAVAILABLE_ROOM_ERROR.replace("$roomId", roomId);
77
+ console.error(message);
78
+ res.status(500);
79
+ res.json({ message });
80
+ }
81
+ });
82
+
83
+ return api;
84
+ }
@@ -0,0 +1,67 @@
1
+ //
2
+ // Monkey-patch Colyseus' default behaviour
3
+ //
4
+ import { Room, Client, ClientPrivate } from "@colyseus/core";
5
+
6
+ function getStateSize(room) {
7
+ // TODO: `Serializer<T>` should provide a method for this (e.g. `serializer.hasState()`)
8
+ const hasState = (
9
+ room._serializer.encoder || // schema v3
10
+ room._serializer.state || // schema v2
11
+ room._serializer.previousState // legacy-fossil-delta
12
+ );
13
+ const fullState = hasState && room._serializer.getFullState();
14
+ return fullState && (fullState.byteLength || fullState.length) || 0;
15
+ }
16
+
17
+ (<any>Room.prototype).getAvailableData = function () {
18
+ return {
19
+ clients: this.clients.length,
20
+ maxClients: this.maxClients,
21
+ metadata: this.metadata,
22
+ roomId: this.roomId,
23
+ };
24
+ };
25
+
26
+ (<any>Room.prototype).getRoomListData = async function () {
27
+ const stateSize = getStateSize(this);
28
+ const elapsedTime = this.clock.elapsedTime;
29
+ const locked = this.locked;
30
+ const data = this.getAvailableData();
31
+
32
+ return { ...data, locked, elapsedTime, stateSize };
33
+ };
34
+
35
+ (<any>Room.prototype).getInspectData = async function () {
36
+ const state = this.state;
37
+ const stateSize = getStateSize(this);
38
+ const roomElapsedTime = this.clock.elapsedTime;
39
+
40
+ const data = this.getAvailableData();
41
+ const clients = this.clients.map((client: Client & ClientPrivate) => ({
42
+ sessionId: client.sessionId,
43
+ elapsedTime: roomElapsedTime - client._joinedAt
44
+ }));
45
+ const locked = this.locked;
46
+
47
+ return { ...data, locked, clients, state, stateSize };
48
+ };
49
+
50
+ // Actions
51
+ (<any>Room.prototype)._forceClientDisconnect = async function (sessionId) {
52
+ for (let i = 0; i < this.clients.length; i++) {
53
+ if (this.clients[i].sessionId === sessionId) {
54
+ this.clients[i].leave();
55
+ break;
56
+ }
57
+ }
58
+ };
59
+
60
+ (<any>Room.prototype)._sendMessageToClient = async function (sessionId, type, data) {
61
+ for (let i = 0; i < this.clients.length; i++) {
62
+ if (this.clients[i].sessionId === sessionId) {
63
+ this.clients[i].send(type, data);
64
+ break;
65
+ }
66
+ }
67
+ };
@@ -0,0 +1,31 @@
1
+ import express from 'express';
2
+ import path from 'path';
3
+
4
+ import { getAPI } from './api.js';
5
+ import './ext/Room.js';
6
+
7
+ const frontendDirectory = path.resolve(__dirname, "..", "build", "static");
8
+
9
+ export interface MonitorOptions {
10
+ columns: Array<
11
+ 'roomId' |
12
+ 'name' |
13
+ 'clients' |
14
+ 'maxClients' |
15
+ 'locked' |
16
+ 'elapsedTime' |
17
+ { metadata: string } |
18
+ 'processId' |
19
+ "publicAddress"
20
+ >
21
+ }
22
+
23
+ /**
24
+ * TODO: expose the `router` instead on next major version.
25
+ */
26
+ export function monitor (opts: Partial<MonitorOptions> = {}): express.Router {
27
+ const router = express.Router();
28
+ router.use(express.static(frontendDirectory));
29
+ router.use('/api', getAPI(opts));
30
+ return router;
31
+ }
package/tsconfig.json CHANGED
@@ -13,9 +13,9 @@
13
13
  "jsx": "react"
14
14
  },
15
15
  "include": [
16
- "src/**/*.ts"
16
+ "src-backend/**/*.ts"
17
17
  ],
18
18
  "exclude": [
19
- "src/frontend/**"
19
+ "src/**"
20
20
  ]
21
21
  }
package/webpack.config.js CHANGED
@@ -16,7 +16,7 @@ module.exports = function(options) {
16
16
  return {
17
17
  mode: MODE,
18
18
 
19
- entry: path.resolve("src", "frontend", "index.tsx"),
19
+ entry: path.resolve("src", "index.tsx"),
20
20
 
21
21
  output: {
22
22
  path: __dirname + "/build/static",
@@ -40,7 +40,7 @@ module.exports = function(options) {
40
40
  }),
41
41
  new ExtractTextPlugin("styles.css"),
42
42
  new HtmlWebpackPlugin({
43
- template: path.resolve("src", "frontend", "index.html")
43
+ template: path.resolve("src", "index.html")
44
44
  }),
45
45
  ],
46
46