@wxn0brp/gloves-link-server 0.0.3 → 0.0.5
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/README.md +63 -0
- package/dist/index.d.ts +5 -7
- package/dist/index.js +42 -26
- package/dist/namespace.d.ts +17 -0
- package/dist/namespace.js +30 -0
- package/dist/room.d.ts +1 -1
- package/dist/socket.d.ts +2 -0
- package/dist/socket.js +6 -2
- package/dist/types.d.ts +5 -2
- package/package.json +43 -46
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# GlovesLink
|
|
2
|
+
|
|
3
|
+
GlovesLink is a WebSocket communication library designed for seamless interaction between clients and servers.
|
|
4
|
+
|
|
5
|
+
[Main repo](https://github.com/wxn0brP/GlovesLink) |
|
|
6
|
+
[Client repo](https://github.com/wxn0brP/GlovesLink-client) |
|
|
7
|
+
[Server repo](https://github.com/wxn0brP/GlovesLink-server)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
### General
|
|
12
|
+
- **WebSocket Communication**: Establish real-time communication between clients and servers.
|
|
13
|
+
- **Automatic Reconnection**: Automatically reconnects after disconnection.
|
|
14
|
+
- **Authentication Support**: Token-based authentication for secure connections.
|
|
15
|
+
- **Logging**: Optional logging for debugging and monitoring.
|
|
16
|
+
- **Rooms**: Organize communication within specific rooms for better organization and control.
|
|
17
|
+
|
|
18
|
+
### Communication
|
|
19
|
+
- **Event Emission**: Send events with arbitrary data.
|
|
20
|
+
- **Callbacks**: Handle server/client responses with callback functions.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm i @wxn0brp/gloves-link-server @wxn0brp/falcon-frame
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { GlovesLinkServer } from '@wxn0brp/gloves-link-server';
|
|
32
|
+
import { FalconFrame } from '@wxn0brp/falcon-frame';
|
|
33
|
+
|
|
34
|
+
const app = new FalconFrame();
|
|
35
|
+
const httpServer = app.listen(3000);
|
|
36
|
+
|
|
37
|
+
const glovesLink = new GlovesLinkServer({
|
|
38
|
+
server: httpServer,
|
|
39
|
+
logs: true,
|
|
40
|
+
authFn: async ({ headers, url, token }) => {
|
|
41
|
+
// Implement your authentication logic here
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
glovesLink.falconFrame(app);
|
|
46
|
+
|
|
47
|
+
glovesLink.onConnect((socket) => {
|
|
48
|
+
console.log('New connection:', socket.id);
|
|
49
|
+
|
|
50
|
+
socket.on('exampleEvent', (data) => {
|
|
51
|
+
console.log('Received data:', data);
|
|
52
|
+
socket.emit('response', 'Hello from server');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
MIT License
|
|
60
|
+
|
|
61
|
+
## Contributing
|
|
62
|
+
|
|
63
|
+
Contributions are welcome!
|
package/dist/index.d.ts
CHANGED
|
@@ -3,23 +3,21 @@ import { Server_Opts } from "./types.js";
|
|
|
3
3
|
import { GLSocket } from "./socket.js";
|
|
4
4
|
import FalconFrame from "@wxn0brp/falcon-frame";
|
|
5
5
|
import { Room, Rooms } from "./room.js";
|
|
6
|
+
import { Namespace } from "./namespace.js";
|
|
6
7
|
export declare class GlovesLinkServer {
|
|
7
8
|
wss: WebSocketServer;
|
|
8
|
-
private onConnectEvent;
|
|
9
9
|
logs: boolean;
|
|
10
10
|
opts: Server_Opts;
|
|
11
11
|
initStatusTemp: {
|
|
12
12
|
[key: string]: number;
|
|
13
13
|
};
|
|
14
14
|
rooms: Rooms;
|
|
15
|
-
|
|
15
|
+
private namespaces;
|
|
16
16
|
constructor(opts: Partial<Server_Opts>);
|
|
17
17
|
private saveSocketStatus;
|
|
18
|
-
|
|
19
|
-
broadcast(event: string, ...args: any[]): void;
|
|
18
|
+
of(path: string): Namespace;
|
|
20
19
|
broadcastRoom(roomName: string, event: string, ...args: any[]): void;
|
|
21
|
-
broadcastWithoutSelf(socket: GLSocket, event: string, ...args: any[]): void;
|
|
22
20
|
room(name: string): Room;
|
|
23
|
-
falconFrame(app: FalconFrame, clientDir?: string): void;
|
|
21
|
+
falconFrame(app: FalconFrame, clientDir?: string | false): void;
|
|
24
22
|
}
|
|
25
|
-
export { GLSocket };
|
|
23
|
+
export { GLSocket, Namespace, Server_Opts };
|
package/dist/index.js
CHANGED
|
@@ -2,19 +2,18 @@ import { WebSocketServer } from "ws";
|
|
|
2
2
|
import { GLSocket } from "./socket.js";
|
|
3
3
|
import { Router } from "@wxn0brp/falcon-frame";
|
|
4
4
|
import { Room } from "./room.js";
|
|
5
|
+
import { Namespace } from "./namespace.js";
|
|
5
6
|
export class GlovesLinkServer {
|
|
6
7
|
wss;
|
|
7
|
-
onConnectEvent;
|
|
8
8
|
logs = false;
|
|
9
9
|
opts;
|
|
10
10
|
initStatusTemp = {};
|
|
11
11
|
rooms = new Map();
|
|
12
|
-
|
|
12
|
+
namespaces = new Map();
|
|
13
13
|
constructor(opts) {
|
|
14
14
|
this.opts = {
|
|
15
15
|
server: null,
|
|
16
16
|
logs: false,
|
|
17
|
-
authFn: () => true,
|
|
18
17
|
...opts
|
|
19
18
|
};
|
|
20
19
|
if (!this.opts?.server) {
|
|
@@ -29,9 +28,17 @@ export class GlovesLinkServer {
|
|
|
29
28
|
const url = new URL(request.url, `http://${request.headers.host}`);
|
|
30
29
|
const token = url.searchParams.get("token");
|
|
31
30
|
socketSelfId = url.searchParams.get("id");
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
const { pathname } = url;
|
|
32
|
+
const namespace = this.namespaces.get(pathname);
|
|
33
|
+
if (!namespace) {
|
|
34
|
+
this.saveSocketStatus(socketSelfId, pathname, 404);
|
|
35
|
+
socket.write("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
36
|
+
socket.destroy();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const authResult = await namespace.authFn({ headers, url, token, request, socket, head });
|
|
40
|
+
if (!authResult) {
|
|
41
|
+
this.saveSocketStatus(socketSelfId, pathname, 401);
|
|
35
42
|
socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
|
|
36
43
|
socket.destroy();
|
|
37
44
|
return;
|
|
@@ -39,12 +46,14 @@ export class GlovesLinkServer {
|
|
|
39
46
|
this.wss.handleUpgrade(request, socket, head, (ws) => {
|
|
40
47
|
const glSocket = new GLSocket(ws, this);
|
|
41
48
|
glSocket.logs = this.logs;
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
if (typeof authResult === "object" && authResult !== null)
|
|
50
|
+
glSocket.user = authResult;
|
|
51
|
+
glSocket.namespace = pathname;
|
|
52
|
+
namespace.room.join(glSocket);
|
|
53
|
+
namespace.onConnectHandler(glSocket);
|
|
44
54
|
ws.on("close", () => {
|
|
45
55
|
glSocket.handlers?.disconnect?.();
|
|
46
|
-
|
|
47
|
-
this.globalRoom.leave(glSocket);
|
|
56
|
+
namespace.room.leave(glSocket);
|
|
48
57
|
});
|
|
49
58
|
});
|
|
50
59
|
}
|
|
@@ -53,25 +62,28 @@ export class GlovesLinkServer {
|
|
|
53
62
|
console.error("[GlovesLinkServer]", err);
|
|
54
63
|
if (this.logs)
|
|
55
64
|
console.warn("[auth] Error during authentication:", err);
|
|
56
|
-
this.saveSocketStatus(socketSelfId, 500);
|
|
65
|
+
this.saveSocketStatus(socketSelfId, "/", 500);
|
|
57
66
|
socket.write("HTTP/1.1 500 Internal Server Error\r\n\r\n");
|
|
58
67
|
socket.destroy();
|
|
59
68
|
}
|
|
60
69
|
});
|
|
61
70
|
}
|
|
62
|
-
saveSocketStatus(socketSelfId, status) {
|
|
71
|
+
saveSocketStatus(socketSelfId, namespace, status) {
|
|
63
72
|
if (!socketSelfId)
|
|
64
73
|
return;
|
|
65
|
-
|
|
74
|
+
const id = namespace + "-" + socketSelfId;
|
|
75
|
+
this.initStatusTemp[id] = status;
|
|
66
76
|
setTimeout(() => {
|
|
67
|
-
delete this.initStatusTemp[
|
|
77
|
+
delete this.initStatusTemp[id];
|
|
68
78
|
}, 10_000);
|
|
69
79
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
of(path) {
|
|
81
|
+
let namespace = this.namespaces.get(path);
|
|
82
|
+
if (!namespace) {
|
|
83
|
+
namespace = new Namespace(path, this);
|
|
84
|
+
this.namespaces.set(path, namespace);
|
|
85
|
+
}
|
|
86
|
+
return namespace;
|
|
75
87
|
}
|
|
76
88
|
broadcastRoom(roomName, event, ...args) {
|
|
77
89
|
const room = this.room(roomName);
|
|
@@ -79,24 +91,24 @@ export class GlovesLinkServer {
|
|
|
79
91
|
return;
|
|
80
92
|
room.emit(event, ...args);
|
|
81
93
|
}
|
|
82
|
-
broadcastWithoutSelf(socket, event, ...args) {
|
|
83
|
-
this.globalRoom.emitWithoutSelf(socket, event, ...args);
|
|
84
|
-
}
|
|
85
94
|
room(name) {
|
|
86
95
|
return this.rooms.get(name) || this.rooms.set(name, new Room()).get(name);
|
|
87
96
|
}
|
|
88
97
|
falconFrame(app, clientDir) {
|
|
89
|
-
clientDir = clientDir || "node_modules/@wxn0brp/gloves-link-client/dist/";
|
|
90
98
|
const router = new Router();
|
|
91
99
|
app.use("/gloves-link", router);
|
|
92
|
-
router.static("/", clientDir);
|
|
93
100
|
router.get("/status", (req, res) => {
|
|
94
101
|
const id = req.query.id;
|
|
95
102
|
if (!id) {
|
|
96
103
|
res.status(400).json({ err: true, msg: "No id provided" });
|
|
97
104
|
return;
|
|
98
105
|
}
|
|
99
|
-
const
|
|
106
|
+
const path = req.query.path;
|
|
107
|
+
if (!path) {
|
|
108
|
+
res.status(400).json({ err: true, msg: "No path provided" });
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const status = this.initStatusTemp[path + "-" + id];
|
|
100
112
|
if (status === undefined) {
|
|
101
113
|
res.status(404).json({ err: true, msg: "Socket not found" });
|
|
102
114
|
return;
|
|
@@ -104,10 +116,14 @@ export class GlovesLinkServer {
|
|
|
104
116
|
res.json({ status });
|
|
105
117
|
delete this.initStatusTemp[id];
|
|
106
118
|
});
|
|
119
|
+
if (clientDir === false)
|
|
120
|
+
return;
|
|
121
|
+
clientDir = clientDir || "node_modules/@wxn0brp/gloves-link-client/dist/";
|
|
122
|
+
router.static("/", clientDir);
|
|
107
123
|
router.get("/*", (req, res) => {
|
|
108
124
|
res.redirect("/gloves-link/GlovesLinkClient.js");
|
|
109
125
|
res.end();
|
|
110
126
|
});
|
|
111
127
|
}
|
|
112
128
|
}
|
|
113
|
-
export { GLSocket };
|
|
129
|
+
export { GLSocket, Namespace };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { AuthFn } from "./types.js";
|
|
2
|
+
import { GLSocket } from "./socket.js";
|
|
3
|
+
import { Room } from "./room.js";
|
|
4
|
+
import { GlovesLinkServer } from "./index.js";
|
|
5
|
+
export declare class Namespace {
|
|
6
|
+
name: string;
|
|
7
|
+
private server;
|
|
8
|
+
private onConnectEvent;
|
|
9
|
+
authFn: AuthFn;
|
|
10
|
+
room: Room;
|
|
11
|
+
constructor(name: string, server: GlovesLinkServer);
|
|
12
|
+
onConnect(handler: (ws: GLSocket) => void): this;
|
|
13
|
+
auth(authFn: AuthFn): this;
|
|
14
|
+
get onConnectHandler(): (ws: GLSocket) => void;
|
|
15
|
+
emit(event: string, ...args: any[]): void;
|
|
16
|
+
emitWithoutSelf(socket: GLSocket, event: string, ...args: any[]): void;
|
|
17
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export class Namespace {
|
|
2
|
+
name;
|
|
3
|
+
server;
|
|
4
|
+
onConnectEvent = () => { };
|
|
5
|
+
authFn = () => false;
|
|
6
|
+
room;
|
|
7
|
+
constructor(name, server) {
|
|
8
|
+
this.name = name;
|
|
9
|
+
this.server = server;
|
|
10
|
+
const roomName = `gls-namespace-${name}`;
|
|
11
|
+
this.room = this.server.room(roomName);
|
|
12
|
+
}
|
|
13
|
+
onConnect(handler) {
|
|
14
|
+
this.onConnectEvent = handler;
|
|
15
|
+
return this;
|
|
16
|
+
}
|
|
17
|
+
auth(authFn) {
|
|
18
|
+
this.authFn = authFn;
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
get onConnectHandler() {
|
|
22
|
+
return this.onConnectEvent;
|
|
23
|
+
}
|
|
24
|
+
emit(event, ...args) {
|
|
25
|
+
this.room.emit(event, ...args);
|
|
26
|
+
}
|
|
27
|
+
emitWithoutSelf(socket, event, ...args) {
|
|
28
|
+
this.room.emitWithoutSelf(socket, event, ...args);
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/room.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { GLSocket } from "./socket.js";
|
|
|
3
3
|
export type Rooms = Map<string, Room>;
|
|
4
4
|
export declare class Room {
|
|
5
5
|
clients: Set<GLSocket>;
|
|
6
|
-
eventEmitter: EventEmitter<
|
|
6
|
+
eventEmitter: EventEmitter<any>;
|
|
7
7
|
join(socket: GLSocket): void;
|
|
8
8
|
leave(socket: GLSocket): void;
|
|
9
9
|
leaveAll(): void;
|
package/dist/socket.d.ts
CHANGED
package/dist/socket.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { joinSocketToRoom,
|
|
1
|
+
import { joinSocketToRoom, leaveSocketFromRoom } from "./room.js";
|
|
2
2
|
export class GLSocket {
|
|
3
3
|
ws;
|
|
4
4
|
server;
|
|
5
5
|
id;
|
|
6
|
+
user = {};
|
|
7
|
+
namespace;
|
|
6
8
|
ackIdCounter = 1;
|
|
7
9
|
ackCallbacks = new Map();
|
|
8
10
|
logs = false;
|
|
@@ -85,7 +87,9 @@ export class GLSocket {
|
|
|
85
87
|
this.rooms.delete(roomName);
|
|
86
88
|
}
|
|
87
89
|
leaveAllRooms() {
|
|
88
|
-
|
|
90
|
+
for (const roomName of this.rooms) {
|
|
91
|
+
leaveSocketFromRoom(this, roomName);
|
|
92
|
+
}
|
|
89
93
|
this.rooms.clear();
|
|
90
94
|
}
|
|
91
95
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import http from "http";
|
|
2
|
+
import Stream from "stream";
|
|
2
3
|
export interface Server_Opts {
|
|
3
4
|
server: http.Server;
|
|
4
5
|
logs: boolean;
|
|
5
|
-
authFn: AuthFn;
|
|
6
6
|
}
|
|
7
7
|
export interface Server_DataEvent {
|
|
8
8
|
evt: string;
|
|
@@ -17,5 +17,8 @@ export interface Server_Auth_Opts {
|
|
|
17
17
|
headers: http.IncomingHttpHeaders;
|
|
18
18
|
url: URL;
|
|
19
19
|
token?: string;
|
|
20
|
+
request: http.IncomingMessage;
|
|
21
|
+
socket: Stream.Duplex;
|
|
22
|
+
head: Buffer<ArrayBufferLike>;
|
|
20
23
|
}
|
|
21
|
-
export type AuthFn = (data: Server_Auth_Opts) => boolean |
|
|
24
|
+
export type AuthFn = (data: Server_Auth_Opts) => (Promise<object | boolean> | object | boolean);
|
package/package.json
CHANGED
|
@@ -1,50 +1,47 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
2
|
+
"name": "@wxn0brp/gloves-link-server",
|
|
3
|
+
"version": "0.0.5",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"author": "wxn0brP",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/wxn0brP/GlovesLink-server.git"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/wxn0brP/GlovesLink",
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@wxn0brp/falcon-frame": "^0.5.3",
|
|
16
|
+
"@types/bun": "*",
|
|
17
|
+
"@types/ws": "^8.18.1",
|
|
18
|
+
"tsc-alias": "*",
|
|
19
|
+
"typescript": "*"
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"@wxn0brp/falcon-frame": ">=0.5.3"
|
|
23
|
+
},
|
|
24
|
+
"peerDependenciesMeta": {
|
|
25
|
+
"@wxn0brp/falcon-frame": {
|
|
26
|
+
"optional": true
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"ws": "^8.18.3"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"import": "./dist/index.js",
|
|
39
|
+
"default": "./dist/index.js"
|
|
34
40
|
},
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
".": {
|
|
40
|
-
"types": "./dist/index.d.ts",
|
|
41
|
-
"import": "./dist/index.js",
|
|
42
|
-
"default": "./dist/index.js"
|
|
43
|
-
},
|
|
44
|
-
"./*": {
|
|
45
|
-
"types": "./dist/*.d.ts",
|
|
46
|
-
"import": "./dist/*.js",
|
|
47
|
-
"default": "./dist/*.js"
|
|
48
|
-
}
|
|
41
|
+
"./*": {
|
|
42
|
+
"types": "./dist/*.d.ts",
|
|
43
|
+
"import": "./dist/*.js",
|
|
44
|
+
"default": "./dist/*.js"
|
|
49
45
|
}
|
|
46
|
+
}
|
|
50
47
|
}
|