@wxn0brp/gloves-link-server 0.0.13 → 0.1.0-beta.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.
- package/dist/http.d.ts +18 -0
- package/dist/http.js +60 -0
- package/dist/index.d.ts +9 -23
- package/dist/index.js +46 -74
- package/dist/namespace.d.ts +20 -10
- package/dist/namespace.js +25 -14
- package/dist/room.d.ts +6 -16
- package/dist/room.js +28 -40
- package/dist/socket.d.ts +15 -30
- package/dist/socket.js +27 -54
- package/dist/transport.d.ts +3 -0
- package/dist/transport.js +47 -0
- package/dist/types.d.ts +7 -0
- package/package.json +1 -1
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Router } from "@wxn0brp/falcon-frame";
|
|
2
|
+
import { SocketStatus } from "./types.js";
|
|
3
|
+
import { GlovesLinkServer } from "./index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Saves the status of a socket connection for temporary tracking
|
|
6
|
+
*/
|
|
7
|
+
export declare function saveSocketStatus(wss: GlovesLinkServer, socketStatus: SocketStatus): void;
|
|
8
|
+
/**
|
|
9
|
+
* Creates a router for handling status requests
|
|
10
|
+
* @returns A router instance for status endpoints
|
|
11
|
+
*/
|
|
12
|
+
export declare function statusRouter(): Router;
|
|
13
|
+
/**
|
|
14
|
+
* Creates a router for serving client files
|
|
15
|
+
* @param clientDir - Optional directory path for client files, defaults to node_modules/@wxn0brp/gloves-link-client/dist/
|
|
16
|
+
* @returns A router instance for client file serving
|
|
17
|
+
*/
|
|
18
|
+
export declare function clientRouter(clientDir?: string): Router;
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Router } from "@wxn0brp/falcon-frame";
|
|
2
|
+
/**
|
|
3
|
+
* Saves the status of a socket connection for temporary tracking
|
|
4
|
+
*/
|
|
5
|
+
export function saveSocketStatus(wss, socketStatus) {
|
|
6
|
+
const { namespace, socketSelfId, status, msg } = socketStatus;
|
|
7
|
+
if (!socketSelfId)
|
|
8
|
+
return;
|
|
9
|
+
const id = namespace + "-" + socketSelfId;
|
|
10
|
+
wss.initStatusTemp[id] = {
|
|
11
|
+
status,
|
|
12
|
+
msg
|
|
13
|
+
};
|
|
14
|
+
setTimeout(() => {
|
|
15
|
+
delete wss.initStatusTemp[id];
|
|
16
|
+
}, wss.opts.statusTimeout);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates a router for handling status requests
|
|
20
|
+
* @returns A router instance for status endpoints
|
|
21
|
+
*/
|
|
22
|
+
export function statusRouter() {
|
|
23
|
+
const router = new Router();
|
|
24
|
+
router.get("/status", (req, res) => {
|
|
25
|
+
const id = req.query.id;
|
|
26
|
+
if (!id) {
|
|
27
|
+
res.status(400).json({ err: true, msg: "No id provided" });
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const path = req.query.path;
|
|
31
|
+
if (!path) {
|
|
32
|
+
res.status(400).json({ err: true, msg: "No path provided" });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const statusKey = path + "-" + id;
|
|
36
|
+
const status = this.initStatusTemp[statusKey];
|
|
37
|
+
if (status === undefined) {
|
|
38
|
+
res.status(404).json({ err: true, msg: "Socket not found" });
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
res.json({ err: false, status });
|
|
42
|
+
delete this.initStatusTemp[statusKey];
|
|
43
|
+
});
|
|
44
|
+
return router;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Creates a router for serving client files
|
|
48
|
+
* @param clientDir - Optional directory path for client files, defaults to node_modules/@wxn0brp/gloves-link-client/dist/
|
|
49
|
+
* @returns A router instance for client file serving
|
|
50
|
+
*/
|
|
51
|
+
export function clientRouter(clientDir) {
|
|
52
|
+
const router = new Router();
|
|
53
|
+
clientDir = clientDir || "node_modules/@wxn0brp/gloves-link-client/dist/";
|
|
54
|
+
router.static("/", clientDir);
|
|
55
|
+
router.get("/*", (req, res) => {
|
|
56
|
+
res.redirect("/gloves-link/GlovesLinkClient.js");
|
|
57
|
+
res.end();
|
|
58
|
+
});
|
|
59
|
+
return router;
|
|
60
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import FalconFrame
|
|
1
|
+
import FalconFrame from "@wxn0brp/falcon-frame";
|
|
2
2
|
import http from "http";
|
|
3
3
|
import { WebSocketServer } from "ws";
|
|
4
4
|
import { Namespace } from "./namespace.js";
|
|
5
|
-
import { Room
|
|
5
|
+
import { Room } from "./room.js";
|
|
6
6
|
import { GLSocket } from "./socket.js";
|
|
7
7
|
import { Server_Opts } from "./types.js";
|
|
8
8
|
/**
|
|
@@ -15,23 +15,13 @@ export declare class GlovesLinkServer {
|
|
|
15
15
|
status: number;
|
|
16
16
|
msg?: string;
|
|
17
17
|
}>;
|
|
18
|
-
rooms: Rooms;
|
|
19
18
|
namespaces: Map<string, Namespace>;
|
|
20
19
|
/**
|
|
21
20
|
* Creates a new GlovesLinkServer instance
|
|
22
21
|
* @param opts - Server options including the HTTP server instance
|
|
23
22
|
*/
|
|
24
23
|
constructor(opts: Partial<Server_Opts>);
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Saves the status of a socket connection for temporary tracking
|
|
28
|
-
* @param socketSelfId - The ID of the socket
|
|
29
|
-
* @param namespace - The namespace of the socket
|
|
30
|
-
* @param status - The status code to save
|
|
31
|
-
* @param msg - Optional message to save with the status
|
|
32
|
-
* @private
|
|
33
|
-
*/
|
|
34
|
-
private saveSocketStatus;
|
|
24
|
+
attachToHttpServer(server: http.Server): void;
|
|
35
25
|
/**
|
|
36
26
|
* Gets or creates a namespace by path
|
|
37
27
|
* @param path - The path for the namespace
|
|
@@ -46,22 +36,18 @@ export declare class GlovesLinkServer {
|
|
|
46
36
|
*/
|
|
47
37
|
broadcastRoom(roomName: string, event: string, ...args: any[]): void;
|
|
48
38
|
/**
|
|
49
|
-
* Gets or creates a room by name
|
|
39
|
+
* Gets or creates a room by name (from the root namespace)
|
|
50
40
|
* @param name - The name of the room
|
|
51
41
|
* @returns The room instance
|
|
52
42
|
*/
|
|
53
43
|
room(name: string): Room;
|
|
54
44
|
/**
|
|
55
|
-
*
|
|
56
|
-
* @
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Creates a router for serving client files
|
|
61
|
-
* @param clientDir - Optional directory path for client files, defaults to node_modules/@wxn0brp/gloves-link-client/dist/
|
|
62
|
-
* @returns A router instance for client file serving
|
|
45
|
+
* Emits an event to the socket associated with the specified user ID
|
|
46
|
+
* @param userId - The user ID to target
|
|
47
|
+
* @param event - The event name to emit
|
|
48
|
+
* @param args - The arguments to pass with the event
|
|
63
49
|
*/
|
|
64
|
-
|
|
50
|
+
emitToUserId(userId: string, event: string, ...args: any[]): void;
|
|
65
51
|
/**
|
|
66
52
|
* Integrates the GlovesLink server with a FalconFrame application
|
|
67
53
|
* @param app - The FalconFrame application instance
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Router } from "@wxn0brp/falcon-frame";
|
|
2
2
|
import { WebSocketServer } from "ws";
|
|
3
|
+
import { clientRouter, saveSocketStatus, statusRouter } from "./http.js";
|
|
3
4
|
import { Namespace } from "./namespace.js";
|
|
4
|
-
import {
|
|
5
|
+
import { getRoom } from "./room.js";
|
|
5
6
|
import { GLSocket } from "./socket.js";
|
|
6
7
|
/**
|
|
7
8
|
* GlovesLinkServer class provides a WebSocket server with namespace and room functionality
|
|
@@ -10,7 +11,6 @@ export class GlovesLinkServer {
|
|
|
10
11
|
wss;
|
|
11
12
|
opts;
|
|
12
13
|
initStatusTemp = {};
|
|
13
|
-
rooms = new Map();
|
|
14
14
|
namespaces = new Map();
|
|
15
15
|
/**
|
|
16
16
|
* Creates a new GlovesLinkServer instance
|
|
@@ -19,11 +19,12 @@ export class GlovesLinkServer {
|
|
|
19
19
|
constructor(opts) {
|
|
20
20
|
this.opts = {
|
|
21
21
|
logs: false,
|
|
22
|
+
statusTimeout: 10_000,
|
|
22
23
|
...opts
|
|
23
24
|
};
|
|
24
25
|
this.wss = new WebSocketServer({ noServer: true });
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
+
attachToHttpServer(server) {
|
|
27
28
|
server.on("upgrade", async (request, socket, head) => {
|
|
28
29
|
const headers = request.headers;
|
|
29
30
|
let socketSelfId;
|
|
@@ -31,10 +32,15 @@ export class GlovesLinkServer {
|
|
|
31
32
|
const url = new URL(request.url, `http://${request.headers.host}`);
|
|
32
33
|
const token = url.searchParams.get("token");
|
|
33
34
|
socketSelfId = url.searchParams.get("id");
|
|
35
|
+
const type = url.searchParams.get("type");
|
|
34
36
|
const { pathname } = url;
|
|
35
37
|
const namespace = this.namespaces.get(pathname);
|
|
36
38
|
if (!namespace) {
|
|
37
|
-
|
|
39
|
+
saveSocketStatus(this, {
|
|
40
|
+
socketSelfId,
|
|
41
|
+
namespace: pathname,
|
|
42
|
+
status: 404
|
|
43
|
+
});
|
|
38
44
|
socket.write("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
39
45
|
socket.destroy();
|
|
40
46
|
return;
|
|
@@ -47,7 +53,12 @@ export class GlovesLinkServer {
|
|
|
47
53
|
};
|
|
48
54
|
const authResult = await namespace.authFn(authData);
|
|
49
55
|
if (!authResult || authResult.status !== 200) {
|
|
50
|
-
|
|
56
|
+
saveSocketStatus(this, {
|
|
57
|
+
socketSelfId,
|
|
58
|
+
namespace: pathname,
|
|
59
|
+
status: authResult?.status || 401,
|
|
60
|
+
msg: authResult?.msg || "Unauthorized"
|
|
61
|
+
});
|
|
51
62
|
socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
|
|
52
63
|
socket.destroy();
|
|
53
64
|
return;
|
|
@@ -57,14 +68,24 @@ export class GlovesLinkServer {
|
|
|
57
68
|
glSocket.logs = this.opts.logs;
|
|
58
69
|
glSocket.authData = authData;
|
|
59
70
|
glSocket.authResult = authResult;
|
|
71
|
+
glSocket.dataFormatType = type && ["json", "bin"].includes(type) ? type : "json";
|
|
60
72
|
if (typeof authResult.user === "object" && authResult.user !== null)
|
|
61
73
|
glSocket.user = authResult.user;
|
|
62
|
-
glSocket.
|
|
63
|
-
namespace
|
|
64
|
-
namespace.
|
|
74
|
+
glSocket.namespacePath = pathname;
|
|
75
|
+
glSocket.namespace = namespace;
|
|
76
|
+
namespace._room.join(glSocket);
|
|
77
|
+
const userId = authResult?.user?._id;
|
|
78
|
+
if (userId)
|
|
79
|
+
getRoom(namespace.users, userId).join(glSocket);
|
|
80
|
+
namespace._onConnectHandler(glSocket, authData, authResult);
|
|
65
81
|
ws.on("close", () => {
|
|
66
|
-
glSocket.handlers
|
|
67
|
-
namespace.
|
|
82
|
+
glSocket.handlers.emit("disconnect");
|
|
83
|
+
namespace._room.leave(glSocket);
|
|
84
|
+
glSocket.leaveAllRooms();
|
|
85
|
+
if (userId) {
|
|
86
|
+
const room = getRoom(namespace.users, userId);
|
|
87
|
+
room.leave(glSocket);
|
|
88
|
+
}
|
|
68
89
|
});
|
|
69
90
|
});
|
|
70
91
|
}
|
|
@@ -73,32 +94,16 @@ export class GlovesLinkServer {
|
|
|
73
94
|
console.error("[GlovesLinkServer]", err);
|
|
74
95
|
if (this.opts.logs)
|
|
75
96
|
console.warn("[ws auth] Error during authentication:", err);
|
|
76
|
-
|
|
97
|
+
saveSocketStatus(this, {
|
|
98
|
+
socketSelfId,
|
|
99
|
+
namespace: "/",
|
|
100
|
+
status: 500
|
|
101
|
+
});
|
|
77
102
|
socket.write("HTTP/1.1 500 Internal Server Error\r\n\r\n");
|
|
78
103
|
socket.destroy();
|
|
79
104
|
}
|
|
80
105
|
});
|
|
81
106
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Saves the status of a socket connection for temporary tracking
|
|
84
|
-
* @param socketSelfId - The ID of the socket
|
|
85
|
-
* @param namespace - The namespace of the socket
|
|
86
|
-
* @param status - The status code to save
|
|
87
|
-
* @param msg - Optional message to save with the status
|
|
88
|
-
* @private
|
|
89
|
-
*/
|
|
90
|
-
saveSocketStatus(socketSelfId, namespace, status, msg) {
|
|
91
|
-
if (!socketSelfId)
|
|
92
|
-
return;
|
|
93
|
-
const id = namespace + "-" + socketSelfId;
|
|
94
|
-
this.initStatusTemp[id] = {
|
|
95
|
-
status,
|
|
96
|
-
msg
|
|
97
|
-
};
|
|
98
|
-
setTimeout(() => {
|
|
99
|
-
delete this.initStatusTemp[id];
|
|
100
|
-
}, 10_000);
|
|
101
|
-
}
|
|
102
107
|
/**
|
|
103
108
|
* Gets or creates a namespace by path
|
|
104
109
|
* @param path - The path for the namespace
|
|
@@ -125,54 +130,21 @@ export class GlovesLinkServer {
|
|
|
125
130
|
room.emit(event, ...args);
|
|
126
131
|
}
|
|
127
132
|
/**
|
|
128
|
-
* Gets or creates a room by name
|
|
133
|
+
* Gets or creates a room by name (from the root namespace)
|
|
129
134
|
* @param name - The name of the room
|
|
130
135
|
* @returns The room instance
|
|
131
136
|
*/
|
|
132
137
|
room(name) {
|
|
133
|
-
return this.
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Creates a router for handling status requests
|
|
137
|
-
* @returns A router instance for status endpoints
|
|
138
|
-
*/
|
|
139
|
-
statusRouter() {
|
|
140
|
-
const router = new Router();
|
|
141
|
-
router.get("/status", (req, res) => {
|
|
142
|
-
const id = req.query.id;
|
|
143
|
-
if (!id) {
|
|
144
|
-
res.status(400).json({ err: true, msg: "No id provided" });
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
const path = req.query.path;
|
|
148
|
-
if (!path) {
|
|
149
|
-
res.status(400).json({ err: true, msg: "No path provided" });
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
const status = this.initStatusTemp[path + "-" + id];
|
|
153
|
-
if (status === undefined) {
|
|
154
|
-
res.status(404).json({ err: true, msg: "Socket not found" });
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
res.json({ status });
|
|
158
|
-
delete this.initStatusTemp[id];
|
|
159
|
-
});
|
|
160
|
-
return router;
|
|
138
|
+
return this.of("/").room(name);
|
|
161
139
|
}
|
|
162
140
|
/**
|
|
163
|
-
*
|
|
164
|
-
* @param
|
|
165
|
-
* @
|
|
141
|
+
* Emits an event to the socket associated with the specified user ID
|
|
142
|
+
* @param userId - The user ID to target
|
|
143
|
+
* @param event - The event name to emit
|
|
144
|
+
* @param args - The arguments to pass with the event
|
|
166
145
|
*/
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
clientDir = clientDir || "node_modules/@wxn0brp/gloves-link-client/dist/";
|
|
170
|
-
router.static("/", clientDir);
|
|
171
|
-
router.get("/*", (req, res) => {
|
|
172
|
-
res.redirect("/gloves-link/GlovesLinkClient.js");
|
|
173
|
-
res.end();
|
|
174
|
-
});
|
|
175
|
-
return router;
|
|
146
|
+
emitToUserId(userId, event, ...args) {
|
|
147
|
+
this.namespaces.forEach((ns) => ns.emitToUserId(userId, event, ...args));
|
|
176
148
|
}
|
|
177
149
|
/**
|
|
178
150
|
* Integrates the GlovesLink server with a FalconFrame application
|
|
@@ -182,9 +154,9 @@ export class GlovesLinkServer {
|
|
|
182
154
|
falconFrame(app, clientDir) {
|
|
183
155
|
const router = new Router();
|
|
184
156
|
app.use("/gloves-link", router);
|
|
185
|
-
router.use(
|
|
157
|
+
router.use(statusRouter());
|
|
186
158
|
if (clientDir !== false)
|
|
187
|
-
router.use(
|
|
159
|
+
router.use(clientRouter(clientDir));
|
|
188
160
|
}
|
|
189
161
|
}
|
|
190
162
|
export { GLSocket, Namespace };
|
package/dist/namespace.d.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import { AuthFn, OnConnect } from "./types.js";
|
|
2
|
-
import { GLSocket } from "./socket.js";
|
|
3
|
-
import { Room } from "./room.js";
|
|
4
1
|
import { GlovesLinkServer } from "./index.js";
|
|
2
|
+
import { Room, Rooms } from "./room.js";
|
|
3
|
+
import { GLSocket } from "./socket.js";
|
|
4
|
+
import { AuthFn, OnConnect } from "./types.js";
|
|
5
5
|
/**
|
|
6
6
|
* Namespace class represents a logical grouping of sockets that can communicate with each other
|
|
7
7
|
*/
|
|
8
8
|
export declare class Namespace {
|
|
9
9
|
name: string;
|
|
10
10
|
private server;
|
|
11
|
-
|
|
11
|
+
_onConnectHandler: OnConnect;
|
|
12
12
|
authFn: AuthFn;
|
|
13
|
-
|
|
13
|
+
_room: Room;
|
|
14
|
+
rooms: Rooms;
|
|
15
|
+
users: Rooms;
|
|
14
16
|
/**
|
|
15
17
|
* Creates a new Namespace instance
|
|
16
18
|
* @param name - The name of the namespace
|
|
@@ -29,17 +31,18 @@ export declare class Namespace {
|
|
|
29
31
|
* @returns The current Namespace instance for chaining
|
|
30
32
|
*/
|
|
31
33
|
auth(authFn: AuthFn): this;
|
|
32
|
-
/**
|
|
33
|
-
* Gets the connection event handler for this namespace
|
|
34
|
-
* @returns The connection event handler function
|
|
35
|
-
*/
|
|
36
|
-
get onConnectHandler(): OnConnect;
|
|
37
34
|
/**
|
|
38
35
|
* Emits an event to all sockets in the namespace's room
|
|
39
36
|
* @param event - The event name to emit
|
|
40
37
|
* @param args - The arguments to pass with the event
|
|
41
38
|
*/
|
|
42
39
|
emit(event: string, ...args: any[]): void;
|
|
40
|
+
/**
|
|
41
|
+
* Gets or creates a room by name
|
|
42
|
+
* @param name - The name of the room to get or create
|
|
43
|
+
* @returns The Room instance
|
|
44
|
+
*/
|
|
45
|
+
room(name: string): Room;
|
|
43
46
|
/**
|
|
44
47
|
* Emits an event to all sockets in the namespace's room except the specified socket
|
|
45
48
|
* @param socket - The socket to exclude from the emission
|
|
@@ -47,4 +50,11 @@ export declare class Namespace {
|
|
|
47
50
|
* @param args - The arguments to pass with the event
|
|
48
51
|
*/
|
|
49
52
|
emitWithoutSelf(socket: GLSocket, event: string, ...args: any[]): void;
|
|
53
|
+
/**
|
|
54
|
+
* Emits an event to the socket associated with the specified user ID
|
|
55
|
+
* @param userId - The user ID to target
|
|
56
|
+
* @param event - The event name to emit
|
|
57
|
+
* @param args - The arguments to pass with the event
|
|
58
|
+
*/
|
|
59
|
+
emitToUserId(userId: string, event: string, ...args: any[]): void;
|
|
50
60
|
}
|
package/dist/namespace.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import { getRoom, Room } from "./room.js";
|
|
1
2
|
/**
|
|
2
3
|
* Namespace class represents a logical grouping of sockets that can communicate with each other
|
|
3
4
|
*/
|
|
4
5
|
export class Namespace {
|
|
5
6
|
name;
|
|
6
7
|
server;
|
|
7
|
-
|
|
8
|
+
_onConnectHandler = () => { };
|
|
8
9
|
authFn = async () => ({ status: 200 });
|
|
9
|
-
|
|
10
|
+
_room = new Room();
|
|
11
|
+
rooms = new Map();
|
|
12
|
+
users = new Map();
|
|
10
13
|
/**
|
|
11
14
|
* Creates a new Namespace instance
|
|
12
15
|
* @param name - The name of the namespace
|
|
@@ -15,8 +18,6 @@ export class Namespace {
|
|
|
15
18
|
constructor(name, server) {
|
|
16
19
|
this.name = name;
|
|
17
20
|
this.server = server;
|
|
18
|
-
const roomName = `gls-namespace-${name}`;
|
|
19
|
-
this.room = this.server.room(roomName);
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
22
23
|
* Sets the connection event handler for this namespace
|
|
@@ -24,7 +25,7 @@ export class Namespace {
|
|
|
24
25
|
* @returns The current Namespace instance for chaining
|
|
25
26
|
*/
|
|
26
27
|
onConnect(handler) {
|
|
27
|
-
this.
|
|
28
|
+
this._onConnectHandler = handler;
|
|
28
29
|
return this;
|
|
29
30
|
}
|
|
30
31
|
/**
|
|
@@ -36,20 +37,21 @@ export class Namespace {
|
|
|
36
37
|
this.authFn = authFn;
|
|
37
38
|
return this;
|
|
38
39
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Gets the connection event handler for this namespace
|
|
41
|
-
* @returns The connection event handler function
|
|
42
|
-
*/
|
|
43
|
-
get onConnectHandler() {
|
|
44
|
-
return this.onConnectEvent;
|
|
45
|
-
}
|
|
46
40
|
/**
|
|
47
41
|
* Emits an event to all sockets in the namespace's room
|
|
48
42
|
* @param event - The event name to emit
|
|
49
43
|
* @param args - The arguments to pass with the event
|
|
50
44
|
*/
|
|
51
45
|
emit(event, ...args) {
|
|
52
|
-
this.
|
|
46
|
+
this._room.emit(event, ...args);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Gets or creates a room by name
|
|
50
|
+
* @param name - The name of the room to get or create
|
|
51
|
+
* @returns The Room instance
|
|
52
|
+
*/
|
|
53
|
+
room(name) {
|
|
54
|
+
return getRoom(this.rooms, name);
|
|
53
55
|
}
|
|
54
56
|
/**
|
|
55
57
|
* Emits an event to all sockets in the namespace's room except the specified socket
|
|
@@ -58,6 +60,15 @@ export class Namespace {
|
|
|
58
60
|
* @param args - The arguments to pass with the event
|
|
59
61
|
*/
|
|
60
62
|
emitWithoutSelf(socket, event, ...args) {
|
|
61
|
-
this.
|
|
63
|
+
this._room.emitWithoutSelf(socket, event, ...args);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Emits an event to the socket associated with the specified user ID
|
|
67
|
+
* @param userId - The user ID to target
|
|
68
|
+
* @param event - The event name to emit
|
|
69
|
+
* @param args - The arguments to pass with the event
|
|
70
|
+
*/
|
|
71
|
+
emitToUserId(userId, event, ...args) {
|
|
72
|
+
this.users.get(userId)?.emit(event, ...args);
|
|
62
73
|
}
|
|
63
74
|
}
|
package/dist/room.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type Rooms = Map<string, Room>;
|
|
|
5
5
|
* Room class represents a collection of sockets that can communicate with each other
|
|
6
6
|
*/
|
|
7
7
|
export declare class Room {
|
|
8
|
-
|
|
8
|
+
_clients: Set<GLSocket>;
|
|
9
9
|
eventEmitter: EventEmitter<any>;
|
|
10
10
|
/**
|
|
11
11
|
* Adds a socket to the room
|
|
@@ -66,19 +66,9 @@ export declare class Room {
|
|
|
66
66
|
has(socket: GLSocket): boolean;
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
|
-
*
|
|
70
|
-
* @param
|
|
71
|
-
* @param name - The name of the room to
|
|
69
|
+
* Gets or creates a room by name
|
|
70
|
+
* @param rooms - The map of rooms to search in
|
|
71
|
+
* @param name - The name of the room to get or create
|
|
72
|
+
* @returns The Room instance
|
|
72
73
|
*/
|
|
73
|
-
export declare function
|
|
74
|
-
/**
|
|
75
|
-
* Removes a socket from a room by name
|
|
76
|
-
* @param socket - The socket to remove from the room
|
|
77
|
-
* @param roomName - The name of the room to leave
|
|
78
|
-
*/
|
|
79
|
-
export declare function leaveSocketFromRoom(socket: GLSocket, roomName: string): void;
|
|
80
|
-
/**
|
|
81
|
-
* Removes a socket from all rooms it has joined
|
|
82
|
-
* @param socket - The socket to remove from all rooms
|
|
83
|
-
*/
|
|
84
|
-
export declare function leaveAllSocketFromRoom(socket: GLSocket): void;
|
|
74
|
+
export declare function getRoom(rooms: Rooms, name: string): Room;
|
package/dist/room.js
CHANGED
|
@@ -3,14 +3,15 @@ import EventEmitter from "events";
|
|
|
3
3
|
* Room class represents a collection of sockets that can communicate with each other
|
|
4
4
|
*/
|
|
5
5
|
export class Room {
|
|
6
|
-
|
|
6
|
+
_clients = new Set();
|
|
7
7
|
eventEmitter = new EventEmitter();
|
|
8
8
|
/**
|
|
9
9
|
* Adds a socket to the room
|
|
10
10
|
* @param socket - The socket to add to the room
|
|
11
11
|
*/
|
|
12
12
|
join(socket) {
|
|
13
|
-
this.
|
|
13
|
+
this._clients.add(socket);
|
|
14
|
+
socket.rooms.add(this);
|
|
14
15
|
this.eventEmitter.emit("join", socket, this);
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
@@ -18,15 +19,21 @@ export class Room {
|
|
|
18
19
|
* @param socket - The socket to remove from the room
|
|
19
20
|
*/
|
|
20
21
|
leave(socket) {
|
|
21
|
-
this.
|
|
22
|
+
this._clients.delete(socket);
|
|
23
|
+
socket.rooms.delete(this);
|
|
22
24
|
this.eventEmitter.emit("leave", socket, this);
|
|
25
|
+
if (this._clients.size === 0)
|
|
26
|
+
this.eventEmitter.emit("empty", this);
|
|
23
27
|
}
|
|
24
28
|
/**
|
|
25
29
|
* Removes all sockets from the room
|
|
26
30
|
*/
|
|
27
31
|
leaveAll() {
|
|
28
|
-
this.
|
|
32
|
+
for (const socket of this._clients)
|
|
33
|
+
socket.rooms.delete(this);
|
|
34
|
+
this._clients.clear();
|
|
29
35
|
this.eventEmitter.emit("leaveAll", this);
|
|
36
|
+
this.eventEmitter.emit("empty", this);
|
|
30
37
|
}
|
|
31
38
|
/**
|
|
32
39
|
* Registers a handler for when a socket joins the room
|
|
@@ -51,14 +58,14 @@ export class Room {
|
|
|
51
58
|
* @returns The number of clients in the room
|
|
52
59
|
*/
|
|
53
60
|
get size() {
|
|
54
|
-
return this.
|
|
61
|
+
return this._clients.size;
|
|
55
62
|
}
|
|
56
63
|
/**
|
|
57
64
|
* Gets an array of all clients in the room
|
|
58
65
|
* @returns An array containing all the sockets in the room
|
|
59
66
|
*/
|
|
60
67
|
get sockets() {
|
|
61
|
-
return Array.from(this.
|
|
68
|
+
return Array.from(this._clients);
|
|
62
69
|
}
|
|
63
70
|
/**
|
|
64
71
|
* Emits an event to all clients in the room
|
|
@@ -66,7 +73,7 @@ export class Room {
|
|
|
66
73
|
* @param data - The data to send with the event
|
|
67
74
|
*/
|
|
68
75
|
emit(evtName, ...data) {
|
|
69
|
-
for (const socket of this.
|
|
76
|
+
for (const socket of this._clients) {
|
|
70
77
|
socket.emit(evtName, ...data);
|
|
71
78
|
}
|
|
72
79
|
}
|
|
@@ -77,7 +84,7 @@ export class Room {
|
|
|
77
84
|
* @param data - The data to send with the event
|
|
78
85
|
*/
|
|
79
86
|
emitWithoutSelf(socket, evtName, ...data) {
|
|
80
|
-
for (const client of this.
|
|
87
|
+
for (const client of this._clients) {
|
|
81
88
|
if (client === socket)
|
|
82
89
|
continue;
|
|
83
90
|
client.emit(evtName, ...data);
|
|
@@ -89,40 +96,21 @@ export class Room {
|
|
|
89
96
|
* @returns True if the socket is in the room, false otherwise
|
|
90
97
|
*/
|
|
91
98
|
has(socket) {
|
|
92
|
-
return this.
|
|
99
|
+
return this._clients.has(socket);
|
|
93
100
|
}
|
|
94
101
|
}
|
|
95
102
|
/**
|
|
96
|
-
*
|
|
97
|
-
* @param
|
|
98
|
-
* @param name - The name of the room to
|
|
103
|
+
* Gets or creates a room by name
|
|
104
|
+
* @param rooms - The map of rooms to search in
|
|
105
|
+
* @param name - The name of the room to get or create
|
|
106
|
+
* @returns The Room instance
|
|
99
107
|
*/
|
|
100
|
-
export function
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
* @param roomName - The name of the room to leave
|
|
109
|
-
*/
|
|
110
|
-
export function leaveSocketFromRoom(socket, roomName) {
|
|
111
|
-
const rooms = socket.server.rooms;
|
|
112
|
-
const room = rooms.get(roomName);
|
|
113
|
-
if (!room)
|
|
114
|
-
return;
|
|
115
|
-
room.leave(socket);
|
|
116
|
-
if (room.size > 0)
|
|
117
|
-
return;
|
|
118
|
-
rooms.delete(roomName);
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Removes a socket from all rooms it has joined
|
|
122
|
-
* @param socket - The socket to remove from all rooms
|
|
123
|
-
*/
|
|
124
|
-
export function leaveAllSocketFromRoom(socket) {
|
|
125
|
-
const rooms = socket.server.rooms;
|
|
126
|
-
for (const room of rooms.values())
|
|
127
|
-
room.leave(socket);
|
|
108
|
+
export function getRoom(rooms, name) {
|
|
109
|
+
const existedRoom = rooms.get(name);
|
|
110
|
+
if (existedRoom)
|
|
111
|
+
return existedRoom;
|
|
112
|
+
const createdRoom = new Room();
|
|
113
|
+
rooms.set(name, createdRoom);
|
|
114
|
+
createdRoom.eventEmitter.on("empty", () => rooms.delete(name));
|
|
115
|
+
return createdRoom;
|
|
128
116
|
}
|
package/dist/socket.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { WebSocket } from "ws";
|
|
2
|
-
import { GlovesLinkServer } from "./index.js";
|
|
3
|
-
import { Room
|
|
2
|
+
import { GlovesLinkServer, Namespace } from "./index.js";
|
|
3
|
+
import { Room } from "./room.js";
|
|
4
4
|
import { AuthFnResult, Server_Auth_Opts } from "./types.js";
|
|
5
|
+
import EventEmitter from "events";
|
|
5
6
|
/**
|
|
6
7
|
* GLSocket class represents a WebSocket connection with additional functionality
|
|
7
8
|
* @template T - The type of user data associated with the socket
|
|
@@ -13,16 +14,16 @@ export declare class GLSocket<T = {
|
|
|
13
14
|
server: GlovesLinkServer;
|
|
14
15
|
id: string;
|
|
15
16
|
user: T;
|
|
16
|
-
|
|
17
|
+
namespacePath: string;
|
|
18
|
+
namespace: Namespace;
|
|
19
|
+
rooms: Set<Room>;
|
|
17
20
|
ackIdCounter: number;
|
|
18
21
|
ackCallbacks: Map<number, Function>;
|
|
19
22
|
logs: boolean;
|
|
20
|
-
handlers:
|
|
21
|
-
[key: string]: Function;
|
|
22
|
-
};
|
|
23
|
-
rooms: Set<string>;
|
|
23
|
+
handlers: EventEmitter<any>;
|
|
24
24
|
authData: Server_Auth_Opts;
|
|
25
25
|
authResult: AuthFnResult;
|
|
26
|
+
dataFormatType: "json" | "bin";
|
|
26
27
|
/**
|
|
27
28
|
* Creates a new GLSocket instance
|
|
28
29
|
* @param ws - The underlying WebSocket connection
|
|
@@ -57,41 +58,25 @@ export declare class GLSocket<T = {
|
|
|
57
58
|
/**
|
|
58
59
|
* Closes the WebSocket connection
|
|
59
60
|
*/
|
|
60
|
-
|
|
61
|
+
disconnect(): void;
|
|
61
62
|
/**
|
|
62
63
|
* Joins the socket to a room
|
|
63
64
|
* @param roomName - The name of the room to join
|
|
64
65
|
*/
|
|
65
|
-
joinRoom(
|
|
66
|
+
joinRoom(roomOrName: Room | string): void;
|
|
66
67
|
/**
|
|
67
68
|
* Removes the socket from a room
|
|
68
69
|
* @param roomName - The name of the room to leave
|
|
69
70
|
*/
|
|
70
|
-
leaveRoom(
|
|
71
|
+
leaveRoom(roomOrName: Room | string): void;
|
|
71
72
|
/**
|
|
72
73
|
* Removes the socket from all rooms it has joined
|
|
73
74
|
*/
|
|
74
75
|
leaveAllRooms(): void;
|
|
75
76
|
/**
|
|
76
|
-
* Gets
|
|
77
|
-
* @
|
|
78
|
-
|
|
79
|
-
getNamespace(): import("./namespace.js").Namespace;
|
|
80
|
-
/**
|
|
81
|
-
* Gets the room associated with this socket's namespace
|
|
82
|
-
* @returns The room object or undefined if namespace is not found
|
|
83
|
-
*/
|
|
84
|
-
namespaceRoom(): Room;
|
|
85
|
-
/**
|
|
86
|
-
* Gets a room from the server by name
|
|
87
|
-
* @param roomName - The name of the room to retrieve
|
|
88
|
-
* @returns The room object
|
|
89
|
-
*/
|
|
90
|
-
serverRoom(roomName: string): Room;
|
|
91
|
-
/**
|
|
92
|
-
* Gets all rooms from the server
|
|
93
|
-
* @returns A map of all rooms on the server
|
|
77
|
+
* Gets a room by name
|
|
78
|
+
* @param name - The name of the room to get
|
|
79
|
+
* @returns The room object or undefined if not found
|
|
94
80
|
*/
|
|
95
|
-
|
|
96
|
-
disconnect(): void;
|
|
81
|
+
room(name: string): Room;
|
|
97
82
|
}
|
package/dist/socket.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parse, stringify } from "./transport.js";
|
|
2
|
+
import EventEmitter from "events";
|
|
2
3
|
/**
|
|
3
4
|
* GLSocket class represents a WebSocket connection with additional functionality
|
|
4
5
|
* @template T - The type of user data associated with the socket
|
|
@@ -8,14 +9,16 @@ export class GLSocket {
|
|
|
8
9
|
server;
|
|
9
10
|
id;
|
|
10
11
|
user;
|
|
12
|
+
namespacePath;
|
|
11
13
|
namespace;
|
|
14
|
+
rooms = new Set();
|
|
12
15
|
ackIdCounter = 1;
|
|
13
16
|
ackCallbacks = new Map();
|
|
14
17
|
logs = false;
|
|
15
|
-
handlers;
|
|
16
|
-
rooms = new Set();
|
|
18
|
+
handlers = new EventEmitter();
|
|
17
19
|
authData;
|
|
18
20
|
authResult;
|
|
21
|
+
dataFormatType = "json";
|
|
19
22
|
/**
|
|
20
23
|
* Creates a new GLSocket instance
|
|
21
24
|
* @param ws - The underlying WebSocket connection
|
|
@@ -27,21 +30,17 @@ export class GLSocket {
|
|
|
27
30
|
this.server = server;
|
|
28
31
|
this.id = id || Date.now().toString(36) + Math.random().toString(36).substring(2, 10);
|
|
29
32
|
this.user = { _id: this.id };
|
|
30
|
-
this.
|
|
31
|
-
this.ws.on("message", (raw) => this._handle(raw));
|
|
33
|
+
this.ws.on("message", (raw) => this._handle(raw.toString()));
|
|
32
34
|
}
|
|
33
35
|
/**
|
|
34
36
|
* Internal method to handle incoming messages from the WebSocket
|
|
35
37
|
* @param raw - The raw message string received from the WebSocket
|
|
36
38
|
*/
|
|
37
39
|
_handle(raw) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
msg = JSON.parse(raw);
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
40
|
+
const msg = parse(this.dataFormatType, raw);
|
|
41
|
+
if (!msg) {
|
|
43
42
|
if (this.logs)
|
|
44
|
-
console.warn(
|
|
43
|
+
console.warn(`[ws] Invalid format (${this.dataFormatType}):`, raw);
|
|
45
44
|
return;
|
|
46
45
|
}
|
|
47
46
|
if ("ack" in msg) {
|
|
@@ -67,7 +66,7 @@ export class GLSocket {
|
|
|
67
66
|
break;
|
|
68
67
|
const ackId = data[ackIndex];
|
|
69
68
|
data[ackIndex] = (...res) => {
|
|
70
|
-
this.ws.send(
|
|
69
|
+
this.ws.send(stringify(this.dataFormatType, { ack: ackId, data: res }));
|
|
71
70
|
};
|
|
72
71
|
}
|
|
73
72
|
}
|
|
@@ -79,7 +78,7 @@ export class GLSocket {
|
|
|
79
78
|
* @param handler - The function to be called when the event is received
|
|
80
79
|
*/
|
|
81
80
|
on(evt, handler) {
|
|
82
|
-
this.handlers
|
|
81
|
+
this.handlers.on(evt, handler);
|
|
83
82
|
}
|
|
84
83
|
/**
|
|
85
84
|
* Sends an event to the connected WebSocket client
|
|
@@ -97,7 +96,7 @@ export class GLSocket {
|
|
|
97
96
|
this.ackCallbacks.set(ackId, args[ackIndex]);
|
|
98
97
|
args[ackIndex] = ackId;
|
|
99
98
|
}
|
|
100
|
-
this.ws.send(
|
|
99
|
+
this.ws.send(stringify(this.dataFormatType, {
|
|
101
100
|
evt,
|
|
102
101
|
data: args || undefined,
|
|
103
102
|
ackI: ackI.length ? ackI : undefined
|
|
@@ -115,64 +114,38 @@ export class GLSocket {
|
|
|
115
114
|
/**
|
|
116
115
|
* Closes the WebSocket connection
|
|
117
116
|
*/
|
|
118
|
-
|
|
117
|
+
disconnect() {
|
|
119
118
|
this.ws.close();
|
|
120
119
|
}
|
|
121
120
|
/**
|
|
122
121
|
* Joins the socket to a room
|
|
123
122
|
* @param roomName - The name of the room to join
|
|
124
123
|
*/
|
|
125
|
-
joinRoom(
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
joinRoom(roomOrName) {
|
|
125
|
+
const room = typeof roomOrName === "string" ? this.room(roomOrName) : roomOrName;
|
|
126
|
+
room.join(this);
|
|
128
127
|
}
|
|
129
128
|
/**
|
|
130
129
|
* Removes the socket from a room
|
|
131
130
|
* @param roomName - The name of the room to leave
|
|
132
131
|
*/
|
|
133
|
-
leaveRoom(
|
|
134
|
-
|
|
135
|
-
|
|
132
|
+
leaveRoom(roomOrName) {
|
|
133
|
+
const room = typeof roomOrName === "string" ? this.room(roomOrName) : roomOrName;
|
|
134
|
+
room.leave(this);
|
|
136
135
|
}
|
|
137
136
|
/**
|
|
138
137
|
* Removes the socket from all rooms it has joined
|
|
139
138
|
*/
|
|
140
139
|
leaveAllRooms() {
|
|
141
|
-
for (const
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
this.rooms.clear();
|
|
140
|
+
for (const room of this.rooms.values())
|
|
141
|
+
room.leave(this);
|
|
145
142
|
}
|
|
146
143
|
/**
|
|
147
|
-
* Gets
|
|
148
|
-
* @
|
|
144
|
+
* Gets a room by name
|
|
145
|
+
* @param name - The name of the room to get
|
|
146
|
+
* @returns The room object or undefined if not found
|
|
149
147
|
*/
|
|
150
|
-
|
|
151
|
-
return this.
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Gets the room associated with this socket's namespace
|
|
155
|
-
* @returns The room object or undefined if namespace is not found
|
|
156
|
-
*/
|
|
157
|
-
namespaceRoom() {
|
|
158
|
-
return this.getNamespace()?.room;
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Gets a room from the server by name
|
|
162
|
-
* @param roomName - The name of the room to retrieve
|
|
163
|
-
* @returns The room object
|
|
164
|
-
*/
|
|
165
|
-
serverRoom(roomName) {
|
|
166
|
-
return this.server.room(roomName);
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Gets all rooms from the server
|
|
170
|
-
* @returns A map of all rooms on the server
|
|
171
|
-
*/
|
|
172
|
-
serverRooms() {
|
|
173
|
-
return this.server.rooms;
|
|
174
|
-
}
|
|
175
|
-
disconnect() {
|
|
176
|
-
this.ws.close();
|
|
148
|
+
room(name) {
|
|
149
|
+
return this.namespace.room(name);
|
|
177
150
|
}
|
|
178
151
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const DELIMITER = process.env.GLOVES_LINK_DELIMITER || "\b";
|
|
2
|
+
export function parse(type, raw) {
|
|
3
|
+
if (type === "json") {
|
|
4
|
+
try {
|
|
5
|
+
return JSON.parse(raw);
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
const [evt, ack, ackIdsString, ...contentString] = raw.split(DELIMITER);
|
|
13
|
+
const contents = contentString.map(value => {
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(value);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
if (ack) {
|
|
22
|
+
return {
|
|
23
|
+
ack: Number(ack),
|
|
24
|
+
data: contents,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const ackIds = ackIdsString.split(",").filter(Boolean).map(Number);
|
|
28
|
+
return {
|
|
29
|
+
evt,
|
|
30
|
+
ackI: ackIds,
|
|
31
|
+
data: contents,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function stringify(type, data) {
|
|
39
|
+
if (type === "json")
|
|
40
|
+
return JSON.stringify(data);
|
|
41
|
+
return [
|
|
42
|
+
data.evt ?? "",
|
|
43
|
+
(data.ack ?? ""),
|
|
44
|
+
(data.ackI || []).join(","),
|
|
45
|
+
...data.data.map(JSON.stringify)
|
|
46
|
+
].join(DELIMITER);
|
|
47
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import Stream from "stream";
|
|
|
3
3
|
import { GLSocket } from "./socket.js";
|
|
4
4
|
export interface Server_Opts {
|
|
5
5
|
logs: boolean;
|
|
6
|
+
statusTimeout: number;
|
|
6
7
|
}
|
|
7
8
|
export interface Server_DataEvent {
|
|
8
9
|
evt: string;
|
|
@@ -30,3 +31,9 @@ export interface AuthFnResult {
|
|
|
30
31
|
}
|
|
31
32
|
export type AuthFn = (data: Server_Auth_Opts) => Promise<AuthFnResult>;
|
|
32
33
|
export type OnConnect = (socket: GLSocket, auth: Server_Auth_Opts, result: AuthFnResult) => void;
|
|
34
|
+
export interface SocketStatus {
|
|
35
|
+
socketSelfId: string;
|
|
36
|
+
namespace: string;
|
|
37
|
+
status: number;
|
|
38
|
+
msg?: string;
|
|
39
|
+
}
|