@zap-socket/server 0.0.17 → 0.0.19
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/events.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/server.d.ts +2 -0
- package/dist/server.js +103 -119
- package/package.json +2 -1
package/dist/events.d.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { z } from "zod";
|
2
2
|
import type { EventInput, ZapEvent, ZapStream, ZapServerEvent, MiddlewareType, MiddlwareContext } from "@zap-socket/types";
|
3
|
-
import { ZapServer } from "./server";
|
3
|
+
import { ZapServer } from "./server.js";
|
4
4
|
export type Context = {
|
5
5
|
server: ZapServer<any>;
|
6
6
|
id: string;
|
package/dist/index.d.ts
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
export * from "./events";
|
2
|
-
export * from "./server";
|
1
|
+
export * from "./events.js";
|
2
|
+
export * from "./server.js";
|
package/dist/index.js
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
export * from "./events";
|
2
|
-
export * from "./server";
|
1
|
+
export * from "./events.js";
|
2
|
+
export * from "./server.js";
|
package/dist/server.d.ts
CHANGED
@@ -25,11 +25,13 @@ export declare class ZapServer<T extends EventMap> {
|
|
25
25
|
private onconnectHandler;
|
26
26
|
private wsToId;
|
27
27
|
private idToWs;
|
28
|
+
private persistantContext;
|
28
29
|
private _events;
|
29
30
|
private heartbeatMiss;
|
30
31
|
constructor({ port, events, options }: ZapServerConstructorT, callback?: () => void);
|
31
32
|
private removeClient;
|
32
33
|
private heartbeat;
|
34
|
+
private handleMessage;
|
33
35
|
sendMessageRaw(clientId: string, data: any): void;
|
34
36
|
sendMessage(event: keyof T, clientId: string, data: any): void;
|
35
37
|
broadcastRaw(data: any): void;
|
package/dist/server.js
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
import { WebSocketServer } from "ws";
|
2
|
-
import { serialize, deserialize, generateId } from "./utils";
|
2
|
+
import { serialize, deserialize, generateId } from "./utils.js";
|
3
3
|
const isClientEvent = (event) => {
|
4
4
|
return "process" in event; // both zapEvent and zapStream have process in them.
|
5
5
|
};
|
6
|
+
// make the server class abstracted away
|
7
|
+
// or atleast the ws interactions
|
8
|
+
// so that we can change the backends
|
9
|
+
// with ease (ws & uWebSocket).
|
6
10
|
export class ZapServer {
|
7
11
|
// public server: http.Server;
|
8
12
|
wss;
|
@@ -10,41 +14,14 @@ export class ZapServer {
|
|
10
14
|
onconnectHandler;
|
11
15
|
wsToId;
|
12
16
|
idToWs;
|
17
|
+
persistantContext;
|
13
18
|
_events = {};
|
14
19
|
heartbeatMiss = new Map();
|
15
20
|
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
21
|
this.wss = new WebSocketServer({ port });
|
46
22
|
this.wsToId = new Map();
|
47
23
|
this.idToWs = new Map();
|
24
|
+
this.persistantContext = new Map();
|
48
25
|
this._events = events;
|
49
26
|
this.onconnectHandler = () => { };
|
50
27
|
this.onconnect = (handler) => {
|
@@ -58,95 +35,8 @@ export class ZapServer {
|
|
58
35
|
callback();
|
59
36
|
});
|
60
37
|
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
|
-
}
|
38
|
+
ws.on("message", (message) => {
|
39
|
+
this.handleMessage(ws, req, message);
|
150
40
|
});
|
151
41
|
ws.on("close", () => {
|
152
42
|
this.removeClient(ws);
|
@@ -178,6 +68,100 @@ export class ZapServer {
|
|
178
68
|
});
|
179
69
|
this.broadcastRaw("heartbeat");
|
180
70
|
}
|
71
|
+
async handleMessage(ws, req, message) {
|
72
|
+
const id = this.wsToId.get(ws);
|
73
|
+
// setting up socket id
|
74
|
+
if (!id && message.toString() === "OPEN") {
|
75
|
+
const id = generateId();
|
76
|
+
this.wsToId.set(ws, id);
|
77
|
+
this.idToWs.set(id, ws);
|
78
|
+
ws.send("ID " + id);
|
79
|
+
this.onconnectHandler({
|
80
|
+
id,
|
81
|
+
ws
|
82
|
+
});
|
83
|
+
return;
|
84
|
+
}
|
85
|
+
else if (id && message.toString() === "heartbeat") {
|
86
|
+
this.heartbeatMiss.set(id, 0);
|
87
|
+
}
|
88
|
+
const clientId = this.wsToId.get(ws);
|
89
|
+
const parsedMessage = deserialize(message.toString());
|
90
|
+
if (!parsedMessage)
|
91
|
+
return;
|
92
|
+
const { event, stream, data, requestId, streamId, batch } = parsedMessage;
|
93
|
+
const key = event || stream;
|
94
|
+
const eventObj = this._events[key];
|
95
|
+
if (!eventObj || !isClientEvent(eventObj))
|
96
|
+
return;
|
97
|
+
// Type validation.
|
98
|
+
const inputType = eventObj.input;
|
99
|
+
const { success, error } = inputType.safeParse(data);
|
100
|
+
if (!success && error) {
|
101
|
+
// check if the message is of req-res
|
102
|
+
if (requestId) {
|
103
|
+
}
|
104
|
+
return;
|
105
|
+
}
|
106
|
+
const { process, middleware } = eventObj;
|
107
|
+
if (!id) {
|
108
|
+
ws.close();
|
109
|
+
return;
|
110
|
+
}
|
111
|
+
const buffer = this.persistantContext.get(id);
|
112
|
+
const ctx = { buffer };
|
113
|
+
if (middleware) {
|
114
|
+
for (const m of middleware) {
|
115
|
+
const metadata = {
|
116
|
+
id: clientId,
|
117
|
+
ip: req.socket.remoteAddress,
|
118
|
+
timestamp: Date.now(),
|
119
|
+
size: message.toString().length,
|
120
|
+
};
|
121
|
+
const msg = {
|
122
|
+
event: key,
|
123
|
+
data: parsedMessage,
|
124
|
+
metadata,
|
125
|
+
};
|
126
|
+
let shouldPass = m(ctx, msg);
|
127
|
+
shouldPass = shouldPass instanceof Promise ? await shouldPass : shouldPass;
|
128
|
+
if (!shouldPass)
|
129
|
+
return;
|
130
|
+
}
|
131
|
+
}
|
132
|
+
// All middleware passed
|
133
|
+
const context = { server: this, id: this.wsToId.get(ws), buffer: ctx };
|
134
|
+
if (requestId) { // req-res premitive
|
135
|
+
let result;
|
136
|
+
if (batch) {
|
137
|
+
result = data.map((part) => process(part, context));
|
138
|
+
}
|
139
|
+
else {
|
140
|
+
result = process(data, context);
|
141
|
+
if (result instanceof Promise) {
|
142
|
+
result = await result;
|
143
|
+
}
|
144
|
+
}
|
145
|
+
if (result === undefined) { // just ACK the request process returns nothing
|
146
|
+
ws.send("ACK " + requestId);
|
147
|
+
return;
|
148
|
+
}
|
149
|
+
const serialized = serialize({ requestId, event: key, data: result });
|
150
|
+
if (!serialized)
|
151
|
+
return;
|
152
|
+
ws.send(serialized);
|
153
|
+
}
|
154
|
+
else if (streamId) { // stream premitive
|
155
|
+
const consumeStream = async () => {
|
156
|
+
const result = process(data, context);
|
157
|
+
for await (const fragment of result) {
|
158
|
+
this.sendMessageRaw(clientId, { streamId, fragment });
|
159
|
+
}
|
160
|
+
this.sendMessageRaw(clientId, { streamId, done: true });
|
161
|
+
};
|
162
|
+
consumeStream();
|
163
|
+
}
|
164
|
+
}
|
181
165
|
sendMessageRaw(clientId, data) {
|
182
166
|
const ws = this.idToWs.get(clientId);
|
183
167
|
// 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.19",
|
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
|
}
|