@hackthedev/socket-tools 1.0.1
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/.gitattributes +2 -0
- package/README.md +69 -0
- package/bun.lock +57 -0
- package/index.mjs +109 -0
- package/package.json +15 -0
package/.gitattributes
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Socket Toolbox
|
|
2
|
+
|
|
3
|
+
This small and awesome library was made to simplify a basic socket.io setup by using an existing express server. In this example the express server will be setup with the [express-starter](#) library. The library will scan a directory for files that include socket.io handlers to keep everything modular without pain. Files inside the scanned directory that do not have the required socket handler block will fail to load and cause an error.
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
import ExpressStarter from "@hackthedev/express-starter"
|
|
7
|
+
import SocketTools from "@hackthedev/socket-tools"
|
|
8
|
+
|
|
9
|
+
// express setup
|
|
10
|
+
let starter = new ExpressStarter()
|
|
11
|
+
starter.registerErrorHandlers();
|
|
12
|
+
starter.registerTemplateMiddleware();
|
|
13
|
+
starter.app.use(starter.express.static(starter.dirname + "/public"));
|
|
14
|
+
starter.startHttpServer(5000);
|
|
15
|
+
|
|
16
|
+
// Socket.io Setup
|
|
17
|
+
let socketTools = new SocketTools({expressHttpServer: starter.server});
|
|
18
|
+
socketTools.listen();
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
------
|
|
22
|
+
|
|
23
|
+
## Setting up socket events
|
|
24
|
+
|
|
25
|
+
On default the socket library will recursively scan for `.mjs` files inside the `./modules/sockets` folder relative from what `process.cwd()` returns. These files at a very minimum are required to have this structure:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
export default (io) => (socket) => {
|
|
29
|
+
socket.on('hello', function (data, response) {
|
|
30
|
+
response({ error: null, message: "Hi " + data.name })
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
You can define multiple socket events and even functions inside or outside of the required `export default (io) => (socket) => {}` block. You can do literally anything as long as the required block is present, because otherwise it will abort loading that file and logging it as error.
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
function getLeftMessage(){
|
|
39
|
+
return ", have a great time!";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default (io) => (socket) => {
|
|
43
|
+
socket.on('hello', function (data, response) {
|
|
44
|
+
response({ error: null, message: "Hi " + data.name })
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
socket.on('bye', function (data, response) {
|
|
48
|
+
response({ error: null, message: "Bye " + data.name + getLeftMessage() })
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
------
|
|
54
|
+
|
|
55
|
+
## Custom sockets directory
|
|
56
|
+
|
|
57
|
+
You can also specify a custom directory path to scan that for files instead. The default will always be `path.join(process.cwd(), "modules/sockets")`
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
let socketTools = new SocketTools({expressHttpServer: starter.server, modulesDir: path.join(process.cwd(), "sockets")});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
------
|
|
64
|
+
|
|
65
|
+
## Non-socket files in scan directory
|
|
66
|
+
|
|
67
|
+
If you have files inside the "scan directory" aka `modulesDir` then you can add the following line to the file to avoid the socket toolbox from reporting errors: `export default (io) => (socket) => {}`
|
|
68
|
+
|
|
69
|
+
By leaving it empty it wont have any real effect and avoids errors and possible crashes.
|
package/bun.lock
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@hackthedev/terminal-logger": "^1.0.0",
|
|
8
|
+
"socket.io": "^4.8.3",
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
"packages": {
|
|
13
|
+
"@hackthedev/terminal-logger": ["@hackthedev/terminal-logger@1.0.0", "", {}, "sha512-n2SiSpJXCCbFJwgyzR94aDkgelTsK0CG7Ruh6W0n05D5oAm8wZz8F1+0SCJISf2L0/lhTD39zeNxGRVYse8EKA=="],
|
|
14
|
+
|
|
15
|
+
"@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="],
|
|
16
|
+
|
|
17
|
+
"@types/cors": ["@types/cors@2.8.19", "", { "dependencies": { "@types/node": "*" } }, "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg=="],
|
|
18
|
+
|
|
19
|
+
"@types/node": ["@types/node@25.0.9", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-/rpCXHlCWeqClNBwUhDcusJxXYDjZTyE8v5oTO7WbL8eij2nKhUeU89/6xgjU7N4/Vh3He0BtyhJdQbDyhiXAw=="],
|
|
20
|
+
|
|
21
|
+
"accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
|
|
22
|
+
|
|
23
|
+
"base64id": ["base64id@2.0.0", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="],
|
|
24
|
+
|
|
25
|
+
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
|
26
|
+
|
|
27
|
+
"cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
|
|
28
|
+
|
|
29
|
+
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
|
30
|
+
|
|
31
|
+
"engine.io": ["engine.io@6.6.5", "", { "dependencies": { "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.7.2", "cors": "~2.8.5", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.18.3" } }, "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A=="],
|
|
32
|
+
|
|
33
|
+
"engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="],
|
|
34
|
+
|
|
35
|
+
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
|
36
|
+
|
|
37
|
+
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
|
38
|
+
|
|
39
|
+
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
|
40
|
+
|
|
41
|
+
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
|
42
|
+
|
|
43
|
+
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
|
44
|
+
|
|
45
|
+
"socket.io": ["socket.io@4.8.3", "", { "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", "debug": "~4.4.1", "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" } }, "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A=="],
|
|
46
|
+
|
|
47
|
+
"socket.io-adapter": ["socket.io-adapter@2.5.6", "", { "dependencies": { "debug": "~4.4.1", "ws": "~8.18.3" } }, "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ=="],
|
|
48
|
+
|
|
49
|
+
"socket.io-parser": ["socket.io-parser@4.2.5", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1" } }, "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ=="],
|
|
50
|
+
|
|
51
|
+
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
|
52
|
+
|
|
53
|
+
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
|
54
|
+
|
|
55
|
+
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
|
56
|
+
}
|
|
57
|
+
}
|
package/index.mjs
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {Server} from "socket.io";
|
|
2
|
+
import {pathToFileURL} from "url";
|
|
3
|
+
import Logger from "@hackthedev/terminal-logger";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
|
|
7
|
+
export default class SocketTools {
|
|
8
|
+
constructor({
|
|
9
|
+
expressHttpServer,
|
|
10
|
+
maxHttpBufferSize,
|
|
11
|
+
pingInterval,
|
|
12
|
+
pingTimeout,
|
|
13
|
+
modulesDir,
|
|
14
|
+
cors = {}
|
|
15
|
+
} = {}) {
|
|
16
|
+
|
|
17
|
+
if (!expressHttpServer) throw new Error("SocketTools: expressHttpServer is required");
|
|
18
|
+
if (!maxHttpBufferSize) maxHttpBufferSize = 1e8;
|
|
19
|
+
if (!pingInterval) pingInterval = 25000;
|
|
20
|
+
if (!pingTimeout) pingTimeout = 60000;
|
|
21
|
+
if (!modulesDir) modulesDir = path.join(process.cwd(), "modules/sockets");
|
|
22
|
+
|
|
23
|
+
if (!cors?.origin) cors.origin = "*";
|
|
24
|
+
if (!cors?.methods) cors.methods = ["GET", "POST"];
|
|
25
|
+
if (!cors?.credentials) cors.credentials = false;
|
|
26
|
+
|
|
27
|
+
this.socketHandlers = [];
|
|
28
|
+
this.activeSockets = new Map();
|
|
29
|
+
|
|
30
|
+
this.io = new Server(expressHttpServer, {
|
|
31
|
+
maxHttpBufferSize,
|
|
32
|
+
secure: true,
|
|
33
|
+
pingInterval,
|
|
34
|
+
pingTimeout,
|
|
35
|
+
cors
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
this.loadSocketHandlers(this.modulesDir);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
listen({
|
|
42
|
+
onDisconnect = null,
|
|
43
|
+
onConnection = null
|
|
44
|
+
} = {}) {
|
|
45
|
+
|
|
46
|
+
this.io.on("connection", async (socket) => {
|
|
47
|
+
|
|
48
|
+
this.registerSocketEvents(socket);
|
|
49
|
+
socket.on("disconnect", () => {
|
|
50
|
+
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async loadSocketHandlers(mainHandlersDir) {
|
|
56
|
+
const fileList = [];
|
|
57
|
+
|
|
58
|
+
const scanDir = (dir) => {
|
|
59
|
+
const files = fs.readdirSync(dir, {withFileTypes: true});
|
|
60
|
+
for (const file of files) {
|
|
61
|
+
const filePath = path.join(dir, file.name);
|
|
62
|
+
if (file.isDirectory()) {
|
|
63
|
+
scanDir(filePath);
|
|
64
|
+
}
|
|
65
|
+
else if (file.name.endsWith(".mjs")) {
|
|
66
|
+
fileList.push(filePath);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
scanDir(mainHandlersDir);
|
|
72
|
+
|
|
73
|
+
for (const filePath of fileList) {
|
|
74
|
+
const fileUrl = pathToFileURL(filePath).href;
|
|
75
|
+
try {
|
|
76
|
+
const {default: handlerFactory} = await import(fileUrl);
|
|
77
|
+
const handler = handlerFactory(this.io);
|
|
78
|
+
|
|
79
|
+
if (typeof handler === "function") {
|
|
80
|
+
this.socketHandlers.push(handler);
|
|
81
|
+
Logger.debug(`Preloaded socket handler: ${filePath}`);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
Logger.warn(`Ignored invalid socket handler in ${filePath}`);
|
|
85
|
+
}
|
|
86
|
+
} catch (err) {
|
|
87
|
+
Logger.error(`Error importing socket handler: ${fileUrl}`);
|
|
88
|
+
Logger.error(err);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
registerSocketEvents(socket) {
|
|
94
|
+
try {
|
|
95
|
+
const attachedHandlers = [];
|
|
96
|
+
|
|
97
|
+
for (const handler of this.socketHandlers) {
|
|
98
|
+
const cleanup = handler(socket);
|
|
99
|
+
if (typeof cleanup === "function") {
|
|
100
|
+
attachedHandlers.push(cleanup);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.activeSockets.set(socket.id, attachedHandlers);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
console.error("Error registering socket events:", err);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hackthedev/socket-tools",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "This small and awesome library was made to simplify a basic socket.io setup by using an existing express server. In this example the express server will be setup with the [express-starter](#) library. The library will scan a directory for files that include socket.io handlers to keep everything modular without pain. Files inside the scanned directory that do not have the required socket handler block will fail to load and cause an error.",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"author": "",
|
|
7
|
+
"main": "index.mjs",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@hackthedev/terminal-logger": "^1.0.0",
|
|
13
|
+
"socket.io": "^4.8.3"
|
|
14
|
+
}
|
|
15
|
+
}
|