@zap-socket/server 0.0.1 → 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/dist/events.d.ts +9 -12
- package/dist/events.js +6 -1
- package/dist/server.d.ts +10 -8
- package/dist/server.js +84 -18
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +21 -6
- package/package.json +2 -1
package/dist/events.d.ts
CHANGED
@@ -1,16 +1,13 @@
|
|
1
1
|
import { z } from "zod";
|
2
|
-
|
3
|
-
export
|
2
|
+
import type { EventInput, Context, ZapEvent, ZapServerEvent, MiddlewareType } from "@zap-socket/types";
|
3
|
+
export declare const zapEvent: <T extends EventInput, R>(eventObj: T extends z.ZodTypeAny ? {
|
4
4
|
input: T;
|
5
|
-
|
5
|
+
middleware?: MiddlewareType[];
|
6
|
+
process: (input: z.infer<T>, ctx: Context) => R;
|
6
7
|
} : {
|
7
|
-
|
8
|
-
process: () => R;
|
9
|
-
};
|
10
|
-
export type EventMap = Record<string, ZapEvent<any, any>>;
|
11
|
-
export declare const createZapEvent: <T extends EventInput, R>(eventObj: T extends z.ZodTypeAny ? {
|
12
|
-
input: T;
|
13
|
-
process: (input: z.infer<T>) => R;
|
14
|
-
} : {
|
15
|
-
process: () => R;
|
8
|
+
middleware?: MiddlewareType[];
|
9
|
+
process: (ctx: Context) => R;
|
16
10
|
}) => ZapEvent<T, R>;
|
11
|
+
export declare const zapServerEvent: <T extends z.ZodTypeAny>(eventObj: {
|
12
|
+
data: T;
|
13
|
+
}) => ZapServerEvent<T>;
|
package/dist/events.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { z } from "zod";
|
2
|
-
export const
|
2
|
+
export const zapEvent = (eventObj) => {
|
3
3
|
if ("input" in eventObj) {
|
4
4
|
return eventObj;
|
5
5
|
}
|
@@ -8,3 +8,8 @@ export const createZapEvent = (eventObj) => {
|
|
8
8
|
process: eventObj.process
|
9
9
|
};
|
10
10
|
};
|
11
|
+
export const zapServerEvent = (eventObj) => {
|
12
|
+
return {
|
13
|
+
data: eventObj.data
|
14
|
+
};
|
15
|
+
};
|
package/dist/server.d.ts
CHANGED
@@ -1,17 +1,19 @@
|
|
1
|
-
import type { EventMap } from "
|
1
|
+
import type { EventMap, ZapServerEvent } from "@zap-socket/types";
|
2
2
|
interface ZapServerConstructorT {
|
3
3
|
port: number;
|
4
|
+
events?: EventMap;
|
4
5
|
}
|
5
|
-
export declare class ZapServer {
|
6
|
+
export declare class ZapServer<T extends EventMap> {
|
6
7
|
private wss;
|
7
8
|
private wsToId;
|
8
9
|
private idToWs;
|
9
|
-
private
|
10
|
-
constructor({ port }: ZapServerConstructorT);
|
10
|
+
private _events;
|
11
|
+
constructor({ port, events }: ZapServerConstructorT, callback?: () => void);
|
11
12
|
private removeClient;
|
12
|
-
|
13
|
+
sendMessageRaw(clientId: string, data: any): void;
|
14
|
+
get event(): { [K in keyof T as T[K] extends ZapServerEvent<any> ? K : never]: {
|
15
|
+
send: (clientId: string, data?: (T[K] extends ZapServerEvent<any> ? T[K]["data"] : never)) => void;
|
16
|
+
}; };
|
13
17
|
}
|
14
|
-
export declare const createZapServer: ({ port }:
|
15
|
-
port: number;
|
16
|
-
}) => ZapServer;
|
18
|
+
export declare const createZapServer: <T extends EventMap>({ port, events }: ZapServerConstructorT, callback?: () => void) => ZapServer<T>;
|
17
19
|
export {};
|
package/dist/server.js
CHANGED
@@ -1,31 +1,70 @@
|
|
1
1
|
import { WebSocketServer } from "ws";
|
2
|
-
import { generateId } from "./utils";
|
2
|
+
import { serialize, deserialize, generateId } from "./utils";
|
3
|
+
const isZapEvent = (event) => {
|
4
|
+
return "process" in event;
|
5
|
+
};
|
3
6
|
export class ZapServer {
|
4
7
|
wss;
|
5
8
|
wsToId;
|
6
9
|
idToWs;
|
7
|
-
|
8
|
-
constructor({ port }) {
|
10
|
+
_events = {};
|
11
|
+
constructor({ port, events = {} }, callback) {
|
9
12
|
this.wss = new WebSocketServer({ port });
|
10
13
|
this.wsToId = new Map();
|
11
14
|
this.idToWs = new Map();
|
12
|
-
this.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
this._events = events;
|
16
|
+
this.wss.on("listening", () => {
|
17
|
+
if (callback)
|
18
|
+
callback();
|
19
|
+
});
|
20
|
+
this.wss.on("connection", (ws, req) => {
|
17
21
|
ws.on("message", (message) => {
|
18
22
|
if (!this.wsToId.get(ws)) {
|
19
23
|
const id = generateId();
|
20
24
|
this.wsToId.set(ws, id);
|
21
25
|
this.idToWs.set(id, ws);
|
22
26
|
ws.send("ID " + id);
|
27
|
+
console.log(`client ${id} connected.`);
|
28
|
+
return;
|
23
29
|
}
|
24
|
-
|
25
|
-
|
26
|
-
if (
|
27
|
-
|
28
|
-
|
30
|
+
const clientId = this.wsToId.get(ws);
|
31
|
+
for (const [event, eventObj] of Object.entries(this._events)) {
|
32
|
+
if (!isZapEvent(eventObj))
|
33
|
+
continue; // skip server events
|
34
|
+
const { process, middleware } = eventObj;
|
35
|
+
const parsedMessage = deserialize(message.toString());
|
36
|
+
if (parsedMessage &&
|
37
|
+
parsedMessage["event"] === event) {
|
38
|
+
const { data, requestId } = parsedMessage;
|
39
|
+
// Do middleware checks
|
40
|
+
const ctx = {};
|
41
|
+
if (middleware) {
|
42
|
+
middleware.forEach((m) => {
|
43
|
+
const metadata = {
|
44
|
+
id: clientId,
|
45
|
+
ip: req.socket.remoteAddress,
|
46
|
+
timestamp: Date.now(),
|
47
|
+
size: message.toString().length
|
48
|
+
};
|
49
|
+
const msg = {
|
50
|
+
event,
|
51
|
+
data: parsedMessage,
|
52
|
+
metadata
|
53
|
+
};
|
54
|
+
if (!m(ctx, msg)) {
|
55
|
+
return;
|
56
|
+
}
|
57
|
+
});
|
58
|
+
}
|
59
|
+
// By this point all the middlewares allow to pass through
|
60
|
+
const result = process(data, { server: this, id: this.wsToId.get(ws), buffer: ctx });
|
61
|
+
const serialized = serialize({ requestId, event, data: result });
|
62
|
+
// TODO: throw some nice error: only return stuff that is serializable
|
63
|
+
// i.e. primary data types and objects
|
64
|
+
if (!serialized)
|
65
|
+
return;
|
66
|
+
ws.send(serialized);
|
67
|
+
return; // finally return to avoid looping through rest of events unneccessarily
|
29
68
|
}
|
30
69
|
}
|
31
70
|
});
|
@@ -33,7 +72,7 @@ export class ZapServer {
|
|
33
72
|
this.removeClient(ws);
|
34
73
|
});
|
35
74
|
ws.on("error", (err) => {
|
36
|
-
console.error(`WebSocket error for ${
|
75
|
+
console.error(`WebSocket error for ${this.wsToId.get(ws)}:`, err);
|
37
76
|
});
|
38
77
|
});
|
39
78
|
}
|
@@ -44,10 +83,37 @@ export class ZapServer {
|
|
44
83
|
this.idToWs.delete(clientId);
|
45
84
|
}
|
46
85
|
}
|
47
|
-
|
48
|
-
|
86
|
+
sendMessageRaw(clientId, data) {
|
87
|
+
const ws = this.idToWs.get(clientId);
|
88
|
+
// TODO: throw a nice error
|
89
|
+
if (!ws)
|
90
|
+
return;
|
91
|
+
const serializedData = serialize(data);
|
92
|
+
// TODO: throw a nice error
|
93
|
+
if (!serializedData)
|
94
|
+
return;
|
95
|
+
ws.send(serializedData);
|
96
|
+
}
|
97
|
+
get event() {
|
98
|
+
return Object.fromEntries(Object.keys(this._events).map((eventName) => {
|
99
|
+
// HACK: use a better method to determine the type of event.
|
100
|
+
if ("data" in this._events[eventName]) {
|
101
|
+
// event is server event
|
102
|
+
return [eventName, {
|
103
|
+
send: (clientId, data) => {
|
104
|
+
const packet = {
|
105
|
+
event: eventName,
|
106
|
+
data
|
107
|
+
};
|
108
|
+
this.sendMessageRaw(clientId, packet);
|
109
|
+
}
|
110
|
+
}];
|
111
|
+
}
|
112
|
+
return null;
|
113
|
+
}).filter(entry => entry !== null));
|
49
114
|
}
|
50
115
|
}
|
51
|
-
export const createZapServer = ({ port }) => {
|
52
|
-
|
116
|
+
export const createZapServer = ({ port, events }, callback) => {
|
117
|
+
const server = new ZapServer({ port, events }, callback);
|
118
|
+
return server;
|
53
119
|
};
|
package/dist/utils.d.ts
CHANGED
@@ -1 +1,4 @@
|
|
1
1
|
export declare const generateId: (length?: number) => string;
|
2
|
+
export declare const safeJsonParse: (jsonString: string) => Record<string, any> | null;
|
3
|
+
export declare const serialize: <T>(data: T) => string | null;
|
4
|
+
export declare const deserialize: <T>(data: string) => T | null;
|
package/dist/utils.js
CHANGED
@@ -1,10 +1,25 @@
|
|
1
|
-
export const generateId = (length =
|
1
|
+
export const generateId = (length = 8) => {
|
2
2
|
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
3
|
-
let id = "";
|
4
3
|
const randomValues = new Uint8Array(length);
|
5
4
|
crypto.getRandomValues(randomValues);
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
return Array.from(randomValues, (num) => chars[num % chars.length]).join('');
|
6
|
+
};
|
7
|
+
export const safeJsonParse = (jsonString) => {
|
8
|
+
try {
|
9
|
+
return JSON.parse(jsonString);
|
10
|
+
}
|
11
|
+
catch {
|
12
|
+
return null;
|
13
|
+
}
|
14
|
+
};
|
15
|
+
export const serialize = (data) => {
|
16
|
+
try {
|
17
|
+
return JSON.stringify(data);
|
18
|
+
}
|
19
|
+
catch {
|
20
|
+
return null;
|
21
|
+
}
|
22
|
+
};
|
23
|
+
export const deserialize = (data) => {
|
24
|
+
return safeJsonParse(data);
|
10
25
|
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@zap-socket/server",
|
3
|
-
"version": "0.0.
|
3
|
+
"version": "0.0.5",
|
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",
|
@@ -23,6 +23,7 @@
|
|
23
23
|
"vitest": "^3.0.9"
|
24
24
|
},
|
25
25
|
"dependencies": {
|
26
|
+
"@zap-socket/types": "^0.0.2",
|
26
27
|
"zod": "^3.24.2"
|
27
28
|
}
|
28
29
|
}
|