@zap-socket/server 0.0.5 → 0.0.7

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import type { EventInput, Context, ZapEvent, ZapServerEvent, MiddlewareType } from "@zap-socket/types";
2
+ import type { EventInput, Context, ZapEvent, ZapStream, ZapServerEvent, MiddlewareType } from "@zap-socket/types";
3
3
  export declare const zapEvent: <T extends EventInput, R>(eventObj: T extends z.ZodTypeAny ? {
4
4
  input: T;
5
5
  middleware?: MiddlewareType[];
@@ -8,6 +8,14 @@ export declare const zapEvent: <T extends EventInput, R>(eventObj: T extends z.Z
8
8
  middleware?: MiddlewareType[];
9
9
  process: (ctx: Context) => R;
10
10
  }) => ZapEvent<T, R>;
11
+ export declare const zapStream: <T extends EventInput, R>(eventObj: T extends z.ZodTypeAny ? {
12
+ input: T;
13
+ middleware?: MiddlewareType[];
14
+ process: (input: z.infer<T>, ctx: Context) => AsyncGenerator<R, void, unknown>;
15
+ } : {
16
+ middleware?: MiddlewareType[];
17
+ process: (ctx: Context) => AsyncGenerator<R, void, unknown>;
18
+ }) => ZapStream<T, R>;
11
19
  export declare const zapServerEvent: <T extends z.ZodTypeAny>(eventObj: {
12
20
  data: T;
13
21
  }) => ZapServerEvent<T>;
package/dist/events.js CHANGED
@@ -8,6 +8,15 @@ export const zapEvent = (eventObj) => {
8
8
  process: eventObj.process
9
9
  };
10
10
  };
11
+ export const zapStream = (eventObj) => {
12
+ if ("input" in eventObj) {
13
+ return eventObj;
14
+ }
15
+ return {
16
+ input: z.void(),
17
+ process: eventObj.process
18
+ };
19
+ };
11
20
  export const zapServerEvent = (eventObj) => {
12
21
  return {
13
22
  data: eventObj.data
package/dist/server.d.ts CHANGED
@@ -1,19 +1,32 @@
1
+ import { WebSocketServer, WebSocket } from "ws";
1
2
  import type { EventMap, ZapServerEvent } from "@zap-socket/types";
2
3
  interface ZapServerConstructorT {
3
4
  port: number;
4
5
  events?: EventMap;
5
6
  }
6
7
  export declare class ZapServer<T extends EventMap> {
7
- private wss;
8
+ wss: WebSocketServer;
9
+ onconnect: (handler: (ctx: {
10
+ id: string;
11
+ ws: WebSocket;
12
+ }) => void) => void;
13
+ private onconnectHandler;
8
14
  private wsToId;
9
15
  private idToWs;
10
16
  private _events;
11
17
  constructor({ port, events }: ZapServerConstructorT, callback?: () => void);
12
18
  private removeClient;
13
19
  sendMessageRaw(clientId: string, data: any): void;
20
+ sendMessage(event: keyof T, clientId: string, data: any): void;
21
+ broadcastRaw(data: any): void;
22
+ broadcast(event: keyof T, data: any): void;
23
+ selectiveBroascast(event: string, data: any, connections: string[]): void;
14
24
  get event(): { [K in keyof T as T[K] extends ZapServerEvent<any> ? K : never]: {
15
25
  send: (clientId: string, data?: (T[K] extends ZapServerEvent<any> ? T[K]["data"] : never)) => void;
26
+ broadcast: (data?: (T[K] extends ZapServerEvent<any> ? T[K]["data"] : never)) => void;
16
27
  }; };
28
+ get clients(): Set<string>;
29
+ get socketMap(): Map<string, WebSocket>;
17
30
  }
18
31
  export declare const createZapServer: <T extends EventMap>({ port, events }: ZapServerConstructorT, callback?: () => void) => ZapServer<T>;
19
32
  export {};
package/dist/server.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import { WebSocketServer } from "ws";
2
2
  import { serialize, deserialize, generateId } from "./utils";
3
- const isZapEvent = (event) => {
4
- return "process" in event;
3
+ const isClientEvent = (event) => {
4
+ return "process" in event; // both zapEvent and zapStream have process in them.
5
5
  };
6
6
  export class ZapServer {
7
7
  wss;
8
+ onconnect;
9
+ onconnectHandler;
8
10
  wsToId;
9
11
  idToWs;
10
12
  _events = {};
@@ -13,60 +15,79 @@ export class ZapServer {
13
15
  this.wsToId = new Map();
14
16
  this.idToWs = new Map();
15
17
  this._events = events;
18
+ this.onconnectHandler = () => { };
19
+ this.onconnect = (handler) => {
20
+ this.onconnectHandler = handler;
21
+ };
16
22
  this.wss.on("listening", () => {
17
23
  if (callback)
18
24
  callback();
19
25
  });
20
26
  this.wss.on("connection", (ws, req) => {
21
27
  ws.on("message", (message) => {
22
- if (!this.wsToId.get(ws)) {
28
+ if (!this.wsToId.get(ws) && message.toString() === "OPEN") {
23
29
  const id = generateId();
24
30
  this.wsToId.set(ws, id);
25
31
  this.idToWs.set(id, ws);
26
32
  ws.send("ID " + id);
27
- console.log(`client ${id} connected.`);
33
+ this.onconnectHandler({
34
+ id,
35
+ ws
36
+ });
28
37
  return;
29
38
  }
30
39
  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)
40
+ const parsedMessage = deserialize(message.toString());
41
+ if (!parsedMessage)
42
+ return;
43
+ const { event, stream, data, requestId, streamId } = parsedMessage;
44
+ const key = event || stream;
45
+ const eventObj = this._events[key];
46
+ if (!eventObj || !isClientEvent(eventObj))
47
+ return;
48
+ const { process, middleware } = eventObj;
49
+ // Setup middleware context
50
+ const ctx = {};
51
+ if (middleware) {
52
+ for (const m of middleware) {
53
+ const metadata = {
54
+ id: clientId,
55
+ ip: req.socket.remoteAddress,
56
+ timestamp: Date.now(),
57
+ size: message.toString().length,
58
+ };
59
+ const msg = {
60
+ event: key,
61
+ data: parsedMessage,
62
+ metadata,
63
+ };
64
+ if (!m(ctx, msg))
65
65
  return;
66
- ws.send(serialized);
67
- return; // finally return to avoid looping through rest of events unneccessarily
68
66
  }
69
67
  }
68
+ // All middleware passed
69
+ const context = { server: this, id: this.wsToId.get(ws), buffer: ctx };
70
+ if (requestId) { // req-res premitive
71
+ const result = process(data, context);
72
+ if (result === undefined) { // just ACK the request process returns nothing
73
+ ws.send("ACK " + requestId);
74
+ return;
75
+ }
76
+ const serialized = serialize({ requestId, event: key, data: result });
77
+ if (!serialized)
78
+ return;
79
+ ws.send(serialized);
80
+ }
81
+ else if (streamId) { // stream premitive
82
+ const consumeStream = async () => {
83
+ const result = process(data, context);
84
+ for await (const fragment of result) {
85
+ this.sendMessageRaw(clientId, { streamId, fragment });
86
+ }
87
+ this.sendMessageRaw(clientId, { streamId, done: true });
88
+ };
89
+ consumeStream();
90
+ }
70
91
  });
71
92
  ws.on("close", () => {
72
93
  this.removeClient(ws);
@@ -94,6 +115,60 @@ export class ZapServer {
94
115
  return;
95
116
  ws.send(serializedData);
96
117
  }
118
+ sendMessage(event, clientId, data) {
119
+ const ws = this.idToWs.get(clientId);
120
+ // TODO: throw a nice error
121
+ if (!ws)
122
+ return;
123
+ const packet = {
124
+ event,
125
+ data
126
+ };
127
+ const serializedPacket = serialize(packet);
128
+ // TODO: throw a nice error
129
+ if (!serializedPacket)
130
+ return;
131
+ ws.send(serializedPacket);
132
+ }
133
+ broadcastRaw(data) {
134
+ const serializedData = serialize(data);
135
+ if (!serializedData) {
136
+ // TODO: throw a nice error
137
+ return;
138
+ }
139
+ this.idToWs.forEach((ws) => {
140
+ ws.send(serializedData);
141
+ });
142
+ }
143
+ broadcast(event, data) {
144
+ const packet = {
145
+ event,
146
+ data
147
+ };
148
+ const serializedPacket = serialize(packet);
149
+ if (!serializedPacket)
150
+ return;
151
+ this.idToWs.forEach((ws) => {
152
+ ws.send(serializedPacket);
153
+ });
154
+ }
155
+ selectiveBroascast(event, data, connections) {
156
+ const serialized = serialize(data);
157
+ if (!serialized) {
158
+ // TODO: throw a nice error
159
+ return;
160
+ }
161
+ const packet = {
162
+ event,
163
+ data
164
+ };
165
+ const serializedPacket = serialize(packet); // if data is serializable then packet is too, so no need to check
166
+ connections
167
+ .flatMap(x => this.idToWs.get(x) ?? [])
168
+ .forEach((ws) => {
169
+ ws.send(serializedPacket);
170
+ });
171
+ }
97
172
  get event() {
98
173
  return Object.fromEntries(Object.keys(this._events).map((eventName) => {
99
174
  // HACK: use a better method to determine the type of event.
@@ -106,12 +181,25 @@ export class ZapServer {
106
181
  data
107
182
  };
108
183
  this.sendMessageRaw(clientId, packet);
184
+ },
185
+ broadcast: (data) => {
186
+ const packet = {
187
+ event: eventName,
188
+ data
189
+ };
190
+ this.broadcastRaw(packet);
109
191
  }
110
192
  }];
111
193
  }
112
194
  return null;
113
195
  }).filter(entry => entry !== null));
114
196
  }
197
+ get clients() {
198
+ return new Set(this.idToWs.keys());
199
+ }
200
+ get socketMap() {
201
+ return this.idToWs;
202
+ }
115
203
  }
116
204
  export const createZapServer = ({ port, events }, callback) => {
117
205
  const server = new ZapServer({ port, events }, callback);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zap-socket/server",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
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,7 +23,7 @@
23
23
  "vitest": "^3.0.9"
24
24
  },
25
25
  "dependencies": {
26
- "@zap-socket/types": "^0.0.2",
26
+ "@zap-socket/types": "^0.0.3",
27
27
  "zod": "^3.24.2"
28
28
  }
29
29
  }