@zap-socket/server 0.0.17 → 0.0.18
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/server.d.ts +1 -0
- package/dist/server.js +92 -118
- package/package.json +2 -1
package/dist/server.d.ts
CHANGED
@@ -30,6 +30,7 @@ export declare class ZapServer<T extends EventMap> {
|
|
30
30
|
constructor({ port, events, options }: ZapServerConstructorT, callback?: () => void);
|
31
31
|
private removeClient;
|
32
32
|
private heartbeat;
|
33
|
+
private handleMessage;
|
33
34
|
sendMessageRaw(clientId: string, data: any): void;
|
34
35
|
sendMessage(event: keyof T, clientId: string, data: any): void;
|
35
36
|
broadcastRaw(data: any): void;
|
package/dist/server.js
CHANGED
@@ -13,35 +13,6 @@ export class ZapServer {
|
|
13
13
|
_events = {};
|
14
14
|
heartbeatMiss = new Map();
|
15
15
|
constructor({ port, events = {}, options }, callback) {
|
16
|
-
// this.server = http.createServer((req, res) => {
|
17
|
-
//
|
18
|
-
// if (cors) {
|
19
|
-
// const origin = req.headers.origin;
|
20
|
-
//
|
21
|
-
// if (origin && cors.origin && (cors.origin.includes(origin) || cors.origin.includes("*"))) {
|
22
|
-
// res.setHeader('Access-Control-Allow-Origin', origin);
|
23
|
-
// }
|
24
|
-
// if (cors.methods) {
|
25
|
-
// res.setHeader("Access-Control-Allow-Methods", cors.methods.join(", "));
|
26
|
-
// }
|
27
|
-
// if (cors.headers) {
|
28
|
-
// res.setHeader("Access-Control-Allow-Headers", cors.headers.join(", "));
|
29
|
-
// }
|
30
|
-
// if (cors.credentials !== undefined) {
|
31
|
-
// res.setHeader("Access-Control-Allow-Credentials", cors.credentials ? "true" : "false");
|
32
|
-
// }
|
33
|
-
// }
|
34
|
-
//
|
35
|
-
// // pre-flight response
|
36
|
-
// if (req.method === "OPTIONS") {
|
37
|
-
// res.writeHead(204);
|
38
|
-
// res.end();
|
39
|
-
// return;
|
40
|
-
// }
|
41
|
-
//
|
42
|
-
// res.writeHead(404);
|
43
|
-
// res.end();
|
44
|
-
// });
|
45
16
|
this.wss = new WebSocketServer({ port });
|
46
17
|
this.wsToId = new Map();
|
47
18
|
this.idToWs = new Map();
|
@@ -58,95 +29,8 @@ export class ZapServer {
|
|
58
29
|
callback();
|
59
30
|
});
|
60
31
|
this.wss.on("connection", (ws, req) => {
|
61
|
-
ws.on("message",
|
62
|
-
|
63
|
-
// setting up socket id
|
64
|
-
if (!id && message.toString() === "OPEN") {
|
65
|
-
const id = generateId();
|
66
|
-
this.wsToId.set(ws, id);
|
67
|
-
this.idToWs.set(id, ws);
|
68
|
-
ws.send("ID " + id);
|
69
|
-
this.onconnectHandler({
|
70
|
-
id,
|
71
|
-
ws
|
72
|
-
});
|
73
|
-
return;
|
74
|
-
}
|
75
|
-
else if (id && message.toString() === "heartbeat") {
|
76
|
-
this.heartbeatMiss.set(id, 0);
|
77
|
-
}
|
78
|
-
const clientId = this.wsToId.get(ws);
|
79
|
-
const parsedMessage = deserialize(message.toString());
|
80
|
-
if (!parsedMessage)
|
81
|
-
return;
|
82
|
-
const { event, stream, data, requestId, streamId, batch } = parsedMessage;
|
83
|
-
const key = event || stream;
|
84
|
-
const eventObj = this._events[key];
|
85
|
-
if (!eventObj || !isClientEvent(eventObj))
|
86
|
-
return;
|
87
|
-
// Type validation.
|
88
|
-
const inputType = eventObj.input;
|
89
|
-
const { success, error } = inputType.safeParse(data);
|
90
|
-
if (!success && error) {
|
91
|
-
// check if the message is of req-res
|
92
|
-
if (requestId) {
|
93
|
-
}
|
94
|
-
return;
|
95
|
-
}
|
96
|
-
const { process, middleware } = eventObj;
|
97
|
-
// Setup middleware context
|
98
|
-
const ctx = {};
|
99
|
-
if (middleware) {
|
100
|
-
for (const m of middleware) {
|
101
|
-
const metadata = {
|
102
|
-
id: clientId,
|
103
|
-
ip: req.socket.remoteAddress,
|
104
|
-
timestamp: Date.now(),
|
105
|
-
size: message.toString().length,
|
106
|
-
};
|
107
|
-
const msg = {
|
108
|
-
event: key,
|
109
|
-
data: parsedMessage,
|
110
|
-
metadata,
|
111
|
-
};
|
112
|
-
let shouldPass = m(ctx, msg);
|
113
|
-
shouldPass = shouldPass instanceof Promise ? await shouldPass : shouldPass;
|
114
|
-
if (!shouldPass)
|
115
|
-
return;
|
116
|
-
}
|
117
|
-
}
|
118
|
-
// All middleware passed
|
119
|
-
const context = { server: this, id: this.wsToId.get(ws), buffer: ctx };
|
120
|
-
if (requestId) { // req-res premitive
|
121
|
-
let result;
|
122
|
-
if (batch) {
|
123
|
-
result = data.map((part) => process(part, context));
|
124
|
-
}
|
125
|
-
else {
|
126
|
-
result = process(data, context);
|
127
|
-
if (result instanceof Promise) {
|
128
|
-
result = await result;
|
129
|
-
}
|
130
|
-
}
|
131
|
-
if (result === undefined) { // just ACK the request process returns nothing
|
132
|
-
ws.send("ACK " + requestId);
|
133
|
-
return;
|
134
|
-
}
|
135
|
-
const serialized = serialize({ requestId, event: key, data: result });
|
136
|
-
if (!serialized)
|
137
|
-
return;
|
138
|
-
ws.send(serialized);
|
139
|
-
}
|
140
|
-
else if (streamId) { // stream premitive
|
141
|
-
const consumeStream = async () => {
|
142
|
-
const result = process(data, context);
|
143
|
-
for await (const fragment of result) {
|
144
|
-
this.sendMessageRaw(clientId, { streamId, fragment });
|
145
|
-
}
|
146
|
-
this.sendMessageRaw(clientId, { streamId, done: true });
|
147
|
-
};
|
148
|
-
consumeStream();
|
149
|
-
}
|
32
|
+
ws.on("message", (message) => {
|
33
|
+
this.handleMessage(ws, req, message);
|
150
34
|
});
|
151
35
|
ws.on("close", () => {
|
152
36
|
this.removeClient(ws);
|
@@ -178,6 +62,96 @@ export class ZapServer {
|
|
178
62
|
});
|
179
63
|
this.broadcastRaw("heartbeat");
|
180
64
|
}
|
65
|
+
async handleMessage(ws, req, message) {
|
66
|
+
const id = this.wsToId.get(ws);
|
67
|
+
// setting up socket id
|
68
|
+
if (!id && message.toString() === "OPEN") {
|
69
|
+
const id = generateId();
|
70
|
+
this.wsToId.set(ws, id);
|
71
|
+
this.idToWs.set(id, ws);
|
72
|
+
ws.send("ID " + id);
|
73
|
+
this.onconnectHandler({
|
74
|
+
id,
|
75
|
+
ws
|
76
|
+
});
|
77
|
+
return;
|
78
|
+
}
|
79
|
+
else if (id && message.toString() === "heartbeat") {
|
80
|
+
this.heartbeatMiss.set(id, 0);
|
81
|
+
}
|
82
|
+
const clientId = this.wsToId.get(ws);
|
83
|
+
const parsedMessage = deserialize(message.toString());
|
84
|
+
if (!parsedMessage)
|
85
|
+
return;
|
86
|
+
const { event, stream, data, requestId, streamId, batch } = parsedMessage;
|
87
|
+
const key = event || stream;
|
88
|
+
const eventObj = this._events[key];
|
89
|
+
if (!eventObj || !isClientEvent(eventObj))
|
90
|
+
return;
|
91
|
+
// Type validation.
|
92
|
+
const inputType = eventObj.input;
|
93
|
+
const { success, error } = inputType.safeParse(data);
|
94
|
+
if (!success && error) {
|
95
|
+
// check if the message is of req-res
|
96
|
+
if (requestId) {
|
97
|
+
}
|
98
|
+
return;
|
99
|
+
}
|
100
|
+
const { process, middleware } = eventObj;
|
101
|
+
// Setup middleware context
|
102
|
+
const ctx = {};
|
103
|
+
if (middleware) {
|
104
|
+
for (const m of middleware) {
|
105
|
+
const metadata = {
|
106
|
+
id: clientId,
|
107
|
+
ip: req.socket.remoteAddress,
|
108
|
+
timestamp: Date.now(),
|
109
|
+
size: message.toString().length,
|
110
|
+
};
|
111
|
+
const msg = {
|
112
|
+
event: key,
|
113
|
+
data: parsedMessage,
|
114
|
+
metadata,
|
115
|
+
};
|
116
|
+
let shouldPass = m(ctx, msg);
|
117
|
+
shouldPass = shouldPass instanceof Promise ? await shouldPass : shouldPass;
|
118
|
+
if (!shouldPass)
|
119
|
+
return;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
// All middleware passed
|
123
|
+
const context = { server: this, id: this.wsToId.get(ws), buffer: ctx };
|
124
|
+
if (requestId) { // req-res premitive
|
125
|
+
let result;
|
126
|
+
if (batch) {
|
127
|
+
result = data.map((part) => process(part, context));
|
128
|
+
}
|
129
|
+
else {
|
130
|
+
result = process(data, context);
|
131
|
+
if (result instanceof Promise) {
|
132
|
+
result = await result;
|
133
|
+
}
|
134
|
+
}
|
135
|
+
if (result === undefined) { // just ACK the request process returns nothing
|
136
|
+
ws.send("ACK " + requestId);
|
137
|
+
return;
|
138
|
+
}
|
139
|
+
const serialized = serialize({ requestId, event: key, data: result });
|
140
|
+
if (!serialized)
|
141
|
+
return;
|
142
|
+
ws.send(serialized);
|
143
|
+
}
|
144
|
+
else if (streamId) { // stream premitive
|
145
|
+
const consumeStream = async () => {
|
146
|
+
const result = process(data, context);
|
147
|
+
for await (const fragment of result) {
|
148
|
+
this.sendMessageRaw(clientId, { streamId, fragment });
|
149
|
+
}
|
150
|
+
this.sendMessageRaw(clientId, { streamId, done: true });
|
151
|
+
};
|
152
|
+
consumeStream();
|
153
|
+
}
|
154
|
+
}
|
181
155
|
sendMessageRaw(clientId, data) {
|
182
156
|
const ws = this.idToWs.get(clientId);
|
183
157
|
// TODO: throw a nice error
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@zap-socket/server",
|
3
|
-
"version": "0.0.
|
3
|
+
"version": "0.0.18",
|
4
4
|
"description": "A fully typesafe tRPC-inspired WebSocket library with Zod validation, req-res model, and native subscriptions.",
|
5
5
|
"main": "dist/index.js",
|
6
6
|
"types": "dist/index.d.ts",
|
@@ -24,6 +24,7 @@
|
|
24
24
|
},
|
25
25
|
"dependencies": {
|
26
26
|
"@zap-socket/types": "^0.0.9",
|
27
|
+
"uWebSockets.js": "uNetworking/uWebSockets.js#v20.51.0",
|
27
28
|
"zod": "^3.24.2"
|
28
29
|
}
|
29
30
|
}
|