redweb 0.7.2 → 0.7.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.
- package/LICENSE +21 -21
- package/README.md +90 -252
- package/index.d.ts +173 -173
- package/index.js +19 -19
- package/package.json +27 -27
- package/src/htmx/HtmxRenderer.js +65 -65
- package/src/htmx/RedWebHtmxComponent.js +11 -11
- package/src/http/BaseHttpServer.js +105 -105
- package/src/http/HttpServer.js +14 -14
- package/src/http/HttpsServer.js +17 -17
- package/src/http/index.js +1 -1
- package/src/sslConfig.js +20 -20
- package/src/ws/BaseHandler.js +39 -39
- package/src/ws/BaseSocketServer.js +76 -62
- package/src/ws/DefaultHandler.js +13 -13
- package/src/ws/DefaultRoute.js +13 -13
- package/src/ws/SecureSocketServer.js +20 -20
- package/src/ws/SocketRegistry.js +37 -37
- package/src/ws/SocketRoute.js +149 -149
- package/src/ws/SocketServer.js +18 -18
- package/src/ws/SocketService.js +32 -32
- package/src/ws/index.js +8 -8
- package/src/ws/util.js +9 -9
package/src/ws/SocketRegistry.js
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
// handlers/SocketRegistry.js
|
|
2
|
-
const { EventEmitter } = require("events");
|
|
3
|
-
|
|
4
|
-
class SocketRegistry extends EventEmitter {
|
|
5
|
-
constructor() {
|
|
6
|
-
super();
|
|
7
|
-
this.items = [];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
add(item) {
|
|
11
|
-
this.items.push(item);
|
|
12
|
-
this.emit("added", item);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
remove(itemOrId, by = "id") {
|
|
16
|
-
const idx = typeof itemOrId === "object"
|
|
17
|
-
? this.items.findIndex(i => i === itemOrId)
|
|
18
|
-
: this.items.findIndex(i => i[by] === itemOrId);
|
|
19
|
-
|
|
20
|
-
if (idx !== -1) {
|
|
21
|
-
const [removed] = this.items.splice(idx, 1);
|
|
22
|
-
this.emit("removed", removed);
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
all() {
|
|
29
|
-
return [...this.items];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
count() {
|
|
33
|
-
return this.all().length;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
module.exports = SocketRegistry;
|
|
1
|
+
// handlers/SocketRegistry.js
|
|
2
|
+
const { EventEmitter } = require("events");
|
|
3
|
+
|
|
4
|
+
class SocketRegistry extends EventEmitter {
|
|
5
|
+
constructor() {
|
|
6
|
+
super();
|
|
7
|
+
this.items = [];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
add(item) {
|
|
11
|
+
this.items.push(item);
|
|
12
|
+
this.emit("added", item);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
remove(itemOrId, by = "id") {
|
|
16
|
+
const idx = typeof itemOrId === "object"
|
|
17
|
+
? this.items.findIndex(i => i === itemOrId)
|
|
18
|
+
: this.items.findIndex(i => i[by] === itemOrId);
|
|
19
|
+
|
|
20
|
+
if (idx !== -1) {
|
|
21
|
+
const [removed] = this.items.splice(idx, 1);
|
|
22
|
+
this.emit("removed", removed);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
all() {
|
|
29
|
+
return [...this.items];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
count() {
|
|
33
|
+
return this.all().length;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = SocketRegistry;
|
package/src/ws/SocketRoute.js
CHANGED
|
@@ -1,150 +1,150 @@
|
|
|
1
|
-
const { WebSocketServer } = require("ws");
|
|
2
|
-
const { sendJson, broadcast } = require("./util");
|
|
3
|
-
const { randomUUID } = require("crypto");
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Represents a WebSocket route configuration.
|
|
7
|
-
* This class is used to define a specific WebSocket endpoint (`path`) and its associated handlers.
|
|
8
|
-
*/
|
|
9
|
-
class SocketRoute {
|
|
10
|
-
/**
|
|
11
|
-
* Creates a new instance of `SocketRoute`.
|
|
12
|
-
* @param {Object} options - Configuration options for the WebSocket route.
|
|
13
|
-
* @param {string} options.path - The path of the WebSocket route (e.g., `/chat`, `/lobby`).
|
|
14
|
-
* @param {boolean} options.allowDuplicateConnections - Whether to allow multiple connections from the same client IP address.
|
|
15
|
-
* @param {import('./BaseHandler').BaseHandler[]} options.handlers - An array of handler instances that manage connections and messages for this route.
|
|
16
|
-
* @param {Array<new () => SocketService>} [options.services]
|
|
17
|
-
*/
|
|
18
|
-
constructor({ path, handlers, services = [], allowDuplicateConnections } = {}) {
|
|
19
|
-
if (!path) {
|
|
20
|
-
throw new Error('A `path` must be specified for the SocketRoute.');
|
|
21
|
-
}
|
|
22
|
-
if (!handlers || !Array.isArray(handlers) || handlers.length === 0) {
|
|
23
|
-
throw new Error('At least one handler must be specified for the SocketRoute.');
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* The path of the WebSocket route.
|
|
27
|
-
* This determines the endpoint that clients must connect to (e.g., `ws://localhost:3000/chat`).
|
|
28
|
-
* @type {string}
|
|
29
|
-
*/
|
|
30
|
-
this.path = path;
|
|
31
|
-
/**
|
|
32
|
-
* The array of handler instances associated with this route.
|
|
33
|
-
* Each handler is responsible for managing WebSocket connections and message handling logic.
|
|
34
|
-
* @type {import('./BaseHandler').BaseHandler[]}
|
|
35
|
-
*/
|
|
36
|
-
this.handlers = handlers.map(HandlerClass => new HandlerClass());
|
|
37
|
-
this.clients = new Map();
|
|
38
|
-
this.server = new WebSocketServer({ noServer: true, path });
|
|
39
|
-
this.server.on('connection', this.handleConnection.bind(this));
|
|
40
|
-
this.allowDuplicateConnections = allowDuplicateConnections;
|
|
41
|
-
|
|
42
|
-
/* ─── ROUTE‑SCOPED SERVICES ─────────────────────────── */
|
|
43
|
-
this.services = services.map(SvcClass => {
|
|
44
|
-
const svc = new SvcClass();
|
|
45
|
-
if (typeof svc.onInit === 'function') svc.onInit(this);
|
|
46
|
-
return svc;
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Adds a new handler to the WebSocket server.
|
|
51
|
-
* @param {new () => BaseHandler} HandlerClass - The handler class to add.
|
|
52
|
-
*/
|
|
53
|
-
addHandler(HandlerClass) {
|
|
54
|
-
const newHandler = new HandlerClass();
|
|
55
|
-
if (this.handlers.find(handler => handler.name === newHandler.name)) {
|
|
56
|
-
console.warn(`Handler with name '${newHandler.name}' already exists.`);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
this.handlers.push(newHandler);
|
|
60
|
-
console.log(`Handler '${newHandler.name}' added successfully.`);
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Handles a new WebSocket connection.
|
|
64
|
-
* @param {WebSocket} socket - The WebSocket connection instance.
|
|
65
|
-
* @param {import('http').IncomingMessage} req - The HTTP request object associated with the connection.
|
|
66
|
-
*/
|
|
67
|
-
handleConnection(socket, req) {
|
|
68
|
-
const ip = req.socket.remoteAddress;
|
|
69
|
-
console.log(`New client connected: ${ip}`);
|
|
70
|
-
if (this.allowDuplicateConnections) {
|
|
71
|
-
this.clients.set(randomUUID(), socket);
|
|
72
|
-
} else {
|
|
73
|
-
if (this.clients.get(ip) !== undefined) {
|
|
74
|
-
const oldClient = this.clients.get(ip);
|
|
75
|
-
console.warn(`Client ${ip} already connected, disconnecting existing connection.`);
|
|
76
|
-
oldClient.send(
|
|
77
|
-
JSON.stringify({ msg: 'You are being disconnected because a new client is connected with your IP address.' })
|
|
78
|
-
);
|
|
79
|
-
oldClient.close();
|
|
80
|
-
}
|
|
81
|
-
this.clients.set(ip, socket);
|
|
82
|
-
}
|
|
83
|
-
socket.isAssigned = false; // Tracks whether the socket has been assigned a handler.
|
|
84
|
-
socket.sendJson = (data) => sendJson(socket, data);
|
|
85
|
-
socket.broadcast = (data) => broadcast([...this.clients.values()].filter(sock => sock !== socket), data);
|
|
86
|
-
|
|
87
|
-
this.connectionOpenCallback(socket);
|
|
88
|
-
socket.on('close', this.handleClose.bind(this));
|
|
89
|
-
socket.on('error', this.handleError.bind(this));
|
|
90
|
-
socket.on('message', (message) => {
|
|
91
|
-
try {
|
|
92
|
-
const parsed = JSON.parse(message);
|
|
93
|
-
this.handleMessage(socket, parsed);
|
|
94
|
-
} catch (error) {
|
|
95
|
-
console.error(`Error parsing message from ${ip}:`, error);
|
|
96
|
-
socket.sendJson({ error: 'Invalid JSON format' });
|
|
97
|
-
socket.close();
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
connectionOpenCallback(socket) {
|
|
104
|
-
console.log(`Opening new connection: ${socket.remoteAddress}`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
handleMessage(sock, data) {
|
|
108
|
-
const handler = this.handlers.find((handler) => handler.name == data.type);
|
|
109
|
-
if (!handler) {
|
|
110
|
-
sendJson(sock, { error: `No such handler ${data.type}` });
|
|
111
|
-
sock.close();
|
|
112
|
-
} else {
|
|
113
|
-
try {
|
|
114
|
-
handler.handleMessage(sock, data);
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error(`Error handling message in handler ${handler.name}:`, error);
|
|
117
|
-
sendJson(sock, { error: `${error.message}` });
|
|
118
|
-
sock.close();
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Handles socket disconnection.
|
|
125
|
-
* @param {WebSocket} socket - The WebSocket connection instance.
|
|
126
|
-
* @param {string} ip - The client's IP address.
|
|
127
|
-
*/
|
|
128
|
-
handleClose(socket, ip) {
|
|
129
|
-
console.log(`Client disconnected: ${ip}`);
|
|
130
|
-
this.clients.delete(ip);
|
|
131
|
-
if (this.connectionCloseCallback) this.connectionCloseCallback(socket);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
shutdown() {
|
|
135
|
-
this.services.forEach(svc => svc.onShutdown && svc.onShutdown());
|
|
136
|
-
this.server.close();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Handles socket errors.
|
|
141
|
-
* @param {WebSocket} socket - The WebSocket connection instance.
|
|
142
|
-
* @param {Error} error - The error object.
|
|
143
|
-
* @param {string} ip - The client's IP address.
|
|
144
|
-
*/
|
|
145
|
-
handleError(socket, error, ip) {
|
|
146
|
-
console.error(`Socket error from ${ip}:`, error);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
1
|
+
const { WebSocketServer } = require("ws");
|
|
2
|
+
const { sendJson, broadcast } = require("./util");
|
|
3
|
+
const { randomUUID } = require("crypto");
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Represents a WebSocket route configuration.
|
|
7
|
+
* This class is used to define a specific WebSocket endpoint (`path`) and its associated handlers.
|
|
8
|
+
*/
|
|
9
|
+
class SocketRoute {
|
|
10
|
+
/**
|
|
11
|
+
* Creates a new instance of `SocketRoute`.
|
|
12
|
+
* @param {Object} options - Configuration options for the WebSocket route.
|
|
13
|
+
* @param {string} options.path - The path of the WebSocket route (e.g., `/chat`, `/lobby`).
|
|
14
|
+
* @param {boolean} options.allowDuplicateConnections - Whether to allow multiple connections from the same client IP address.
|
|
15
|
+
* @param {import('./BaseHandler').BaseHandler[]} options.handlers - An array of handler instances that manage connections and messages for this route.
|
|
16
|
+
* @param {Array<new () => SocketService>} [options.services]
|
|
17
|
+
*/
|
|
18
|
+
constructor({ path, handlers, services = [], allowDuplicateConnections } = {}) {
|
|
19
|
+
if (!path) {
|
|
20
|
+
throw new Error('A `path` must be specified for the SocketRoute.');
|
|
21
|
+
}
|
|
22
|
+
if (!handlers || !Array.isArray(handlers) || handlers.length === 0) {
|
|
23
|
+
throw new Error('At least one handler must be specified for the SocketRoute.');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The path of the WebSocket route.
|
|
27
|
+
* This determines the endpoint that clients must connect to (e.g., `ws://localhost:3000/chat`).
|
|
28
|
+
* @type {string}
|
|
29
|
+
*/
|
|
30
|
+
this.path = path;
|
|
31
|
+
/**
|
|
32
|
+
* The array of handler instances associated with this route.
|
|
33
|
+
* Each handler is responsible for managing WebSocket connections and message handling logic.
|
|
34
|
+
* @type {import('./BaseHandler').BaseHandler[]}
|
|
35
|
+
*/
|
|
36
|
+
this.handlers = handlers.map(HandlerClass => new HandlerClass());
|
|
37
|
+
this.clients = new Map();
|
|
38
|
+
this.server = new WebSocketServer({ noServer: true, path });
|
|
39
|
+
this.server.on('connection', this.handleConnection.bind(this));
|
|
40
|
+
this.allowDuplicateConnections = allowDuplicateConnections;
|
|
41
|
+
|
|
42
|
+
/* ─── ROUTE‑SCOPED SERVICES ─────────────────────────── */
|
|
43
|
+
this.services = services.map(SvcClass => {
|
|
44
|
+
const svc = new SvcClass();
|
|
45
|
+
if (typeof svc.onInit === 'function') svc.onInit(this);
|
|
46
|
+
return svc;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Adds a new handler to the WebSocket server.
|
|
51
|
+
* @param {new () => BaseHandler} HandlerClass - The handler class to add.
|
|
52
|
+
*/
|
|
53
|
+
addHandler(HandlerClass) {
|
|
54
|
+
const newHandler = new HandlerClass();
|
|
55
|
+
if (this.handlers.find(handler => handler.name === newHandler.name)) {
|
|
56
|
+
console.warn(`Handler with name '${newHandler.name}' already exists.`);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.handlers.push(newHandler);
|
|
60
|
+
console.log(`Handler '${newHandler.name}' added successfully.`);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Handles a new WebSocket connection.
|
|
64
|
+
* @param {WebSocket} socket - The WebSocket connection instance.
|
|
65
|
+
* @param {import('http').IncomingMessage} req - The HTTP request object associated with the connection.
|
|
66
|
+
*/
|
|
67
|
+
handleConnection(socket, req) {
|
|
68
|
+
const ip = req.socket.remoteAddress;
|
|
69
|
+
console.log(`New client connected: ${ip}`);
|
|
70
|
+
if (this.allowDuplicateConnections) {
|
|
71
|
+
this.clients.set(randomUUID(), socket);
|
|
72
|
+
} else {
|
|
73
|
+
if (this.clients.get(ip) !== undefined) {
|
|
74
|
+
const oldClient = this.clients.get(ip);
|
|
75
|
+
console.warn(`Client ${ip} already connected, disconnecting existing connection.`);
|
|
76
|
+
oldClient.send(
|
|
77
|
+
JSON.stringify({ msg: 'You are being disconnected because a new client is connected with your IP address.' })
|
|
78
|
+
);
|
|
79
|
+
oldClient.close();
|
|
80
|
+
}
|
|
81
|
+
this.clients.set(ip, socket);
|
|
82
|
+
}
|
|
83
|
+
socket.isAssigned = false; // Tracks whether the socket has been assigned a handler.
|
|
84
|
+
socket.sendJson = (data) => sendJson(socket, data);
|
|
85
|
+
socket.broadcast = (data) => broadcast([...this.clients.values()].filter(sock => sock !== socket), data);
|
|
86
|
+
|
|
87
|
+
this.connectionOpenCallback(socket);
|
|
88
|
+
socket.on('close', this.handleClose.bind(this));
|
|
89
|
+
socket.on('error', this.handleError.bind(this));
|
|
90
|
+
socket.on('message', (message) => {
|
|
91
|
+
try {
|
|
92
|
+
const parsed = JSON.parse(message);
|
|
93
|
+
this.handleMessage(socket, parsed);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error(`Error parsing message from ${ip}:`, error);
|
|
96
|
+
socket.sendJson({ error: 'Invalid JSON format' });
|
|
97
|
+
socket.close();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
connectionOpenCallback(socket) {
|
|
104
|
+
console.log(`Opening new connection: ${socket.remoteAddress}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
handleMessage(sock, data) {
|
|
108
|
+
const handler = this.handlers.find((handler) => handler.name == data.type);
|
|
109
|
+
if (!handler) {
|
|
110
|
+
sendJson(sock, { error: `No such handler ${data.type}` });
|
|
111
|
+
sock.close();
|
|
112
|
+
} else {
|
|
113
|
+
try {
|
|
114
|
+
handler.handleMessage(sock, data);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error(`Error handling message in handler ${handler.name}:`, error);
|
|
117
|
+
sendJson(sock, { error: `${error.message}` });
|
|
118
|
+
sock.close();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Handles socket disconnection.
|
|
125
|
+
* @param {WebSocket} socket - The WebSocket connection instance.
|
|
126
|
+
* @param {string} ip - The client's IP address.
|
|
127
|
+
*/
|
|
128
|
+
handleClose(socket, ip) {
|
|
129
|
+
console.log(`Client disconnected: ${ip}`);
|
|
130
|
+
this.clients.delete(ip);
|
|
131
|
+
if (this.connectionCloseCallback) this.connectionCloseCallback(socket);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
shutdown() {
|
|
135
|
+
this.services.forEach(svc => svc.onShutdown && svc.onShutdown());
|
|
136
|
+
this.server.close();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Handles socket errors.
|
|
141
|
+
* @param {WebSocket} socket - The WebSocket connection instance.
|
|
142
|
+
* @param {Error} error - The error object.
|
|
143
|
+
* @param {string} ip - The client's IP address.
|
|
144
|
+
*/
|
|
145
|
+
handleError(socket, error, ip) {
|
|
146
|
+
console.error(`Socket error from ${ip}:`, error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
150
|
module.exports = SocketRoute;
|
package/src/ws/SocketServer.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
const http = require('http');
|
|
2
|
-
const { BaseSocketServer } = require('./BaseSocketServer');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* WebSocket Server
|
|
6
|
-
* @param {SocketServerOptions} options - Configuration options for SocketServer.
|
|
7
|
-
* @return {Object} WebSocket server instance.
|
|
8
|
-
*/
|
|
9
|
-
class SocketServer extends BaseSocketServer {
|
|
10
|
-
constructor(options = {}) {
|
|
11
|
-
const server = options?.server || http.createServer();
|
|
12
|
-
super(server, options);
|
|
13
|
-
server.listen(this.port, () => console.log(`RedWeb SocketServer listening on port ${this.port}`));
|
|
14
|
-
return this;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
module.exports = SocketServer;
|
|
1
|
+
const http = require('http');
|
|
2
|
+
const { BaseSocketServer } = require('./BaseSocketServer');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* WebSocket Server
|
|
6
|
+
* @param {SocketServerOptions} options - Configuration options for SocketServer.
|
|
7
|
+
* @return {Object} WebSocket server instance.
|
|
8
|
+
*/
|
|
9
|
+
class SocketServer extends BaseSocketServer {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
const server = options?.server || http.createServer();
|
|
12
|
+
super(server, options);
|
|
13
|
+
server.listen(this.port, () => console.log(`RedWeb SocketServer listening on port ${this.port}`));
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = SocketServer;
|
package/src/ws/SocketService.js
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
// handlers/SocketService.js
|
|
2
|
-
class SocketService {
|
|
3
|
-
/**
|
|
4
|
-
* @param {string} name – service identifier
|
|
5
|
-
* @param {?number} tickRateMs – optional tick interval (ms)
|
|
6
|
-
*/
|
|
7
|
-
constructor(name, tickRateMs = null) {
|
|
8
|
-
this.name = name;
|
|
9
|
-
this.tickRateMs = tickRateMs;
|
|
10
|
-
this._tickHandle = null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Called once by the owning SocketRoute.
|
|
15
|
-
* @param {import('../SocketRoute')} route – the route this service belongs to
|
|
16
|
-
*/
|
|
17
|
-
onInit(route) {
|
|
18
|
-
this.route = route; // full access to route, clients, broadcast…
|
|
19
|
-
if (this.tickRateMs && this.onTick) {
|
|
20
|
-
this._tickHandle = setInterval(() => this.onTick(), this.tickRateMs);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/* Optional */
|
|
25
|
-
// onTick() {}
|
|
26
|
-
|
|
27
|
-
onShutdown() {
|
|
28
|
-
if (this._tickHandle) clearInterval(this._tickHandle);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
module.exports = SocketService;
|
|
1
|
+
// handlers/SocketService.js
|
|
2
|
+
class SocketService {
|
|
3
|
+
/**
|
|
4
|
+
* @param {string} name – service identifier
|
|
5
|
+
* @param {?number} tickRateMs – optional tick interval (ms)
|
|
6
|
+
*/
|
|
7
|
+
constructor(name, tickRateMs = null) {
|
|
8
|
+
this.name = name;
|
|
9
|
+
this.tickRateMs = tickRateMs;
|
|
10
|
+
this._tickHandle = null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Called once by the owning SocketRoute.
|
|
15
|
+
* @param {import('../SocketRoute')} route – the route this service belongs to
|
|
16
|
+
*/
|
|
17
|
+
onInit(route) {
|
|
18
|
+
this.route = route; // full access to route, clients, broadcast…
|
|
19
|
+
if (this.tickRateMs && this.onTick) {
|
|
20
|
+
this._tickHandle = setInterval(() => this.onTick(), this.tickRateMs);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Optional */
|
|
25
|
+
// onTick() {}
|
|
26
|
+
|
|
27
|
+
onShutdown() {
|
|
28
|
+
if (this._tickHandle) clearInterval(this._tickHandle);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = SocketService;
|
|
33
33
|
|
package/src/ws/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
const { SOCKET_OPTIONS } = require('./BaseSocketServer');
|
|
2
|
-
module.exports = {
|
|
3
|
-
SecureSocketServer: require('./SecureSocketServer'),
|
|
4
|
-
SocketServer: require('./SocketServer'),
|
|
5
|
-
SocketRoute: require('./SocketRoute'),
|
|
6
|
-
SocketService: require('./SocketService'),
|
|
7
|
-
SocketRegistry: require('./SocketRegistry'),
|
|
8
|
-
SOCKET_OPTIONS
|
|
1
|
+
const { SOCKET_OPTIONS } = require('./BaseSocketServer');
|
|
2
|
+
module.exports = {
|
|
3
|
+
SecureSocketServer: require('./SecureSocketServer'),
|
|
4
|
+
SocketServer: require('./SocketServer'),
|
|
5
|
+
SocketRoute: require('./SocketRoute'),
|
|
6
|
+
SocketService: require('./SocketService'),
|
|
7
|
+
SocketRegistry: require('./SocketRegistry'),
|
|
8
|
+
SOCKET_OPTIONS
|
|
9
9
|
}
|
package/src/ws/util.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
function sendJson(socket, data) {
|
|
2
|
-
socket.send(JSON.stringify(data));
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
function broadcast(sockets, data) {
|
|
6
|
-
sockets.forEach(socket => sendJson(socket, data));
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
module.exports = { sendJson, broadcast }
|
|
1
|
+
function sendJson(socket, data) {
|
|
2
|
+
socket.send(JSON.stringify(data));
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function broadcast(sockets, data) {
|
|
6
|
+
sockets.forEach(socket => sendJson(socket, data));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
module.exports = { sendJson, broadcast }
|