@zap-socket/server 0.0.15 → 0.0.17

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
@@ -6,16 +6,16 @@ export type Context = {
6
6
  id: string;
7
7
  buffer: MiddlwareContext;
8
8
  };
9
- export declare const zapEvent: <T extends EventInput, R, E = never>(eventObj: T extends z.ZodTypeAny ? {
9
+ export declare const zapEvent: <T extends EventInput, R, E = undefined>(eventObj: T extends z.ZodTypeAny ? {
10
10
  input: T;
11
11
  middleware?: MiddlewareType[];
12
12
  process: (input: z.infer<T>, ctx: Context) => R;
13
- emitType?: E;
13
+ emitType?: E extends undefined ? undefined : E;
14
14
  } : {
15
15
  middleware?: MiddlewareType[];
16
16
  process: (ctx: Context) => R;
17
- emitType?: E;
18
- }) => ZapEvent<T, R, E extends never ? undefined : E>;
17
+ emitType?: E extends undefined ? undefined : E;
18
+ }) => ZapEvent<T, R, E>;
19
19
  export declare const zapStream: <T extends EventInput, R>(eventObj: T extends z.ZodTypeAny ? {
20
20
  input: T;
21
21
  middleware?: MiddlewareType[];
package/dist/events.js CHANGED
@@ -5,14 +5,14 @@ export const zapEvent = (eventObj) => {
5
5
  input: eventObj.input,
6
6
  middleware: eventObj.middleware,
7
7
  process: eventObj.process,
8
- emitType: eventObj.emitType
8
+ emitType: eventObj.emitType ? eventObj.emitType : undefined
9
9
  };
10
10
  }
11
11
  return {
12
12
  input: z.void(),
13
13
  middleware: eventObj.middleware,
14
14
  process: eventObj.process,
15
- emitType: eventObj.emitType
15
+ emitType: eventObj.emitType ? eventObj.emitType : undefined
16
16
  };
17
17
  };
18
18
  export const zapStream = (eventObj) => {
package/dist/server.d.ts CHANGED
@@ -1,9 +1,21 @@
1
1
  import { WebSocketServer, WebSocket } from "ws";
2
2
  import type { EventMap, ZapEvent, ZapServerEvent } from "@zap-socket/types";
3
- interface ZapServerConstructorT {
3
+ import { ZodType, z } from "zod";
4
+ type CorsOptions = {
5
+ origin: string[];
6
+ methods: string[];
7
+ headers: string[];
8
+ credentials: boolean;
9
+ };
10
+ type ZapServerConstructorT = {
4
11
  port: number;
5
12
  events?: EventMap;
6
- }
13
+ cors?: CorsOptions;
14
+ options?: {
15
+ heartbeatPingFrequency: number;
16
+ };
17
+ };
18
+ type ExtractSendData<T, K extends keyof T> = T[K] extends ZapServerEvent<any> ? T[K]['data'] : T[K] extends ZapEvent<any, any> ? T[K]['emitType'] extends ZodType<any, any, any> ? z.infer<T[K]['emitType']> : ReturnType<T[K]['process']> extends undefined ? undefined : ReturnType<T[K]['process']> : never;
7
19
  export declare class ZapServer<T extends EventMap> {
8
20
  wss: WebSocketServer;
9
21
  onconnect: (handler: (ctx: {
@@ -14,19 +26,21 @@ export declare class ZapServer<T extends EventMap> {
14
26
  private wsToId;
15
27
  private idToWs;
16
28
  private _events;
17
- constructor({ port, events }: ZapServerConstructorT, callback?: () => void);
29
+ private heartbeatMiss;
30
+ constructor({ port, events, options }: ZapServerConstructorT, callback?: () => void);
18
31
  private removeClient;
32
+ private heartbeat;
19
33
  sendMessageRaw(clientId: string, data: any): void;
20
34
  sendMessage(event: keyof T, clientId: string, data: any): void;
21
35
  broadcastRaw(data: any): void;
22
36
  broadcast(event: keyof T, data: any): void;
23
37
  selectiveBroascast(event: string, data: any, connections: string[]): void;
24
38
  get events(): { [K in keyof T as T[K] extends ZapServerEvent<any> | ZapEvent<any, any> ? K : never]: {
25
- send: (clientId: string, data?: (T[K] extends ZapServerEvent<any> ? T[K]["data"] : T[K] extends ZapEvent<any, any> ? ReturnType<T[K]["process"]> : never)) => void;
26
- broadcast: (data?: (T[K] extends ZapServerEvent<any> ? T[K]["data"] : T[K] extends ZapEvent<any, any> ? ReturnType<T[K]["process"]> : never)) => void;
39
+ send: (clientId: string, data?: ExtractSendData<T, K>) => void;
40
+ broadcast: (data?: ExtractSendData<T, K>) => void;
27
41
  }; };
28
42
  get clients(): string[];
29
43
  get socketMap(): Map<string, WebSocket>;
30
44
  }
31
- export declare const createZapServer: <T extends EventMap>({ port, events }: ZapServerConstructorT, callback?: () => void) => ZapServer<T>;
45
+ export declare const createZapServer: <T extends EventMap>({ port, events, cors, options }: ZapServerConstructorT, callback?: () => void) => ZapServer<T>;
32
46
  export {};
package/dist/server.js CHANGED
@@ -4,13 +4,44 @@ const isClientEvent = (event) => {
4
4
  return "process" in event; // both zapEvent and zapStream have process in them.
5
5
  };
6
6
  export class ZapServer {
7
+ // public server: http.Server;
7
8
  wss;
8
9
  onconnect;
9
10
  onconnectHandler;
10
11
  wsToId;
11
12
  idToWs;
12
13
  _events = {};
13
- constructor({ port, events = {} }, callback) {
14
+ heartbeatMiss = new Map();
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
+ // });
14
45
  this.wss = new WebSocketServer({ port });
15
46
  this.wsToId = new Map();
16
47
  this.idToWs = new Map();
@@ -19,13 +50,18 @@ export class ZapServer {
19
50
  this.onconnect = (handler) => {
20
51
  this.onconnectHandler = handler;
21
52
  };
53
+ const seconds = options?.heartbeatPingFrequency ?? 5;
54
+ const frequency = (Number.isFinite(seconds) && seconds > 0 ? seconds : 5) * 1000;
55
+ setInterval(() => this.heartbeat(), frequency);
22
56
  this.wss.on("listening", () => {
23
57
  if (callback)
24
58
  callback();
25
59
  });
26
60
  this.wss.on("connection", (ws, req) => {
27
- ws.on("message", (message) => {
28
- if (!this.wsToId.get(ws) && message.toString() === "OPEN") {
61
+ ws.on("message", async (message) => {
62
+ const id = this.wsToId.get(ws);
63
+ // setting up socket id
64
+ if (!id && message.toString() === "OPEN") {
29
65
  const id = generateId();
30
66
  this.wsToId.set(ws, id);
31
67
  this.idToWs.set(id, ws);
@@ -36,6 +72,9 @@ export class ZapServer {
36
72
  });
37
73
  return;
38
74
  }
75
+ else if (id && message.toString() === "heartbeat") {
76
+ this.heartbeatMiss.set(id, 0);
77
+ }
39
78
  const clientId = this.wsToId.get(ws);
40
79
  const parsedMessage = deserialize(message.toString());
41
80
  if (!parsedMessage)
@@ -45,6 +84,15 @@ export class ZapServer {
45
84
  const eventObj = this._events[key];
46
85
  if (!eventObj || !isClientEvent(eventObj))
47
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
+ }
48
96
  const { process, middleware } = eventObj;
49
97
  // Setup middleware context
50
98
  const ctx = {};
@@ -61,7 +109,9 @@ export class ZapServer {
61
109
  data: parsedMessage,
62
110
  metadata,
63
111
  };
64
- if (!m(ctx, msg))
112
+ let shouldPass = m(ctx, msg);
113
+ shouldPass = shouldPass instanceof Promise ? await shouldPass : shouldPass;
114
+ if (!shouldPass)
65
115
  return;
66
116
  }
67
117
  }
@@ -70,14 +120,13 @@ export class ZapServer {
70
120
  if (requestId) { // req-res premitive
71
121
  let result;
72
122
  if (batch) {
73
- if (!data) {
74
- ws.send("ACK " + requestId);
75
- return;
76
- }
77
123
  result = data.map((part) => process(part, context));
78
124
  }
79
125
  else {
80
126
  result = process(data, context);
127
+ if (result instanceof Promise) {
128
+ result = await result;
129
+ }
81
130
  }
82
131
  if (result === undefined) { // just ACK the request process returns nothing
83
132
  ws.send("ACK " + requestId);
@@ -114,6 +163,21 @@ export class ZapServer {
114
163
  this.idToWs.delete(clientId);
115
164
  }
116
165
  }
166
+ heartbeat() {
167
+ this.idToWs.forEach((ws, id) => {
168
+ const misses = this.heartbeatMiss.get(id) ?? 0;
169
+ if (misses > 2) {
170
+ if (ws)
171
+ ws.close();
172
+ this.idToWs.delete(id);
173
+ this.heartbeatMiss.delete(id);
174
+ }
175
+ else {
176
+ this.heartbeatMiss.set(id, misses + 1);
177
+ }
178
+ });
179
+ this.broadcastRaw("heartbeat");
180
+ }
117
181
  sendMessageRaw(clientId, data) {
118
182
  const ws = this.idToWs.get(clientId);
119
183
  // TODO: throw a nice error
@@ -209,7 +273,7 @@ export class ZapServer {
209
273
  return this.idToWs;
210
274
  }
211
275
  }
212
- export const createZapServer = ({ port, events }, callback) => {
213
- const server = new ZapServer({ port, events }, callback);
276
+ export const createZapServer = ({ port, events, cors, options }, callback) => {
277
+ const server = new ZapServer({ port, events, cors, options }, callback);
214
278
  return server;
215
279
  };
package/dist/utils.js CHANGED
@@ -13,6 +13,8 @@ export const safeJsonParse = (jsonString) => {
13
13
  }
14
14
  };
15
15
  export const serialize = (data) => {
16
+ if (typeof data === "string")
17
+ return data;
16
18
  try {
17
19
  return JSON.stringify(data);
18
20
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zap-socket/server",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
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.7",
26
+ "@zap-socket/types": "^0.0.9",
27
27
  "zod": "^3.24.2"
28
28
  }
29
29
  }