@zap-socket/server 0.0.20 → 0.0.21

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.
@@ -0,0 +1,18 @@
1
+ import { IncomingMessage } from "http";
2
+ export interface WebSocketAdapter {
3
+ send(data: string): void;
4
+ close(): void;
5
+ on(event: 'message', listener: (data: Buffer | string) => void): void;
6
+ on(event: 'close', listener: () => void): void;
7
+ on(event: 'error', listener: (err: Error) => void): void;
8
+ }
9
+ export interface WebSocketServerAdapter {
10
+ on(event: 'listening', listener: () => void): void;
11
+ on(event: 'connection', listener: (ws: WebSocketAdapter, req: IncomingMessage) => void): void;
12
+ close(): void;
13
+ }
14
+ export interface WebSocketBackend {
15
+ createServer(options: {
16
+ port: number;
17
+ }): WebSocketServerAdapter;
18
+ }
@@ -0,0 +1 @@
1
+ export {};
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,6 @@
1
+ import { WebSocketServerAdapter, WebSocketBackend } from "./types.js";
2
+ export declare class WSBackend implements WebSocketBackend {
3
+ createServer(options: {
4
+ port: number;
5
+ }): WebSocketServerAdapter;
6
+ }
@@ -0,0 +1,48 @@
1
+ import { WebSocketServer } from "ws";
2
+ class WSAdapter {
3
+ ws;
4
+ constructor(ws) {
5
+ this.ws = ws;
6
+ }
7
+ send(data) {
8
+ this.ws.send(data);
9
+ }
10
+ close() {
11
+ this.ws.close();
12
+ }
13
+ on(event, listener) {
14
+ if (event === 'message') {
15
+ this.ws.on('message', (data) => {
16
+ listener(data.toString());
17
+ });
18
+ }
19
+ else {
20
+ this.ws.on(event, listener);
21
+ }
22
+ }
23
+ }
24
+ class WSServerAdapter {
25
+ wss;
26
+ constructor(wss) {
27
+ this.wss = wss;
28
+ }
29
+ on(event, listener) {
30
+ if (event === 'connection') {
31
+ this.wss.on('connection', (ws, req) => {
32
+ listener(new WSAdapter(ws), req);
33
+ });
34
+ }
35
+ else {
36
+ this.wss.on(event, listener);
37
+ }
38
+ }
39
+ close() {
40
+ this.wss.close();
41
+ }
42
+ }
43
+ export class WSBackend {
44
+ createServer(options) {
45
+ const wss = new WebSocketServer(options);
46
+ return new WSServerAdapter(wss);
47
+ }
48
+ }
package/dist/server.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { WebSocketServer, WebSocket } from "ws";
2
1
  import type { EventMap, ZapEvent, ZapServerEvent } from "@zap-socket/types";
3
2
  import { ZodType, z } from "zod";
3
+ import { WebSocketAdapter } from "./adapters/types.js";
4
4
  type CorsOptions = {
5
5
  origin: string[];
6
6
  methods: string[];
@@ -11,16 +11,18 @@ type ZapServerConstructorT = {
11
11
  port: number;
12
12
  events?: EventMap;
13
13
  cors?: CorsOptions;
14
+ backend?: 'ws' | 'uws';
14
15
  options?: {
15
16
  heartbeatPingFrequency: number;
16
17
  };
17
18
  };
18
19
  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;
19
20
  export declare class ZapServer<T extends EventMap> {
20
- wss: WebSocketServer;
21
+ private wss;
22
+ private backend;
21
23
  onconnect: (handler: (ctx: {
22
24
  id: string;
23
- ws: WebSocket;
25
+ ws: WebSocketAdapter;
24
26
  }) => void) => void;
25
27
  private onconnectHandler;
26
28
  private wsToId;
@@ -28,7 +30,7 @@ export declare class ZapServer<T extends EventMap> {
28
30
  private persistantContext;
29
31
  private _events;
30
32
  private heartbeatMiss;
31
- constructor({ port, events, options }: ZapServerConstructorT, callback?: () => void);
33
+ constructor({ port, events, backend, options }: ZapServerConstructorT, callback?: () => void);
32
34
  private removeClient;
33
35
  private heartbeat;
34
36
  private handleMessage;
@@ -42,7 +44,7 @@ export declare class ZapServer<T extends EventMap> {
42
44
  broadcast: (data?: ExtractSendData<T, K>) => void;
43
45
  }; };
44
46
  get clients(): string[];
45
- get socketMap(): Map<string, WebSocket>;
47
+ get socketMap(): Map<string, WebSocketAdapter>;
46
48
  }
47
- export declare const createZapServer: <T extends EventMap>({ port, events, cors, options }: ZapServerConstructorT, callback?: () => void) => ZapServer<T>;
49
+ export declare const createZapServer: <T extends EventMap>({ port, events, cors, backend, options }: ZapServerConstructorT, callback?: () => void) => ZapServer<T>;
48
50
  export {};
package/dist/server.js CHANGED
@@ -1,15 +1,11 @@
1
- import { WebSocketServer } from "ws";
2
1
  import { serialize, deserialize, generateId } from "./utils.js";
2
+ import { WSBackend } from "./adapters/wsAdapter.js";
3
3
  const isClientEvent = (event) => {
4
- return "process" in event; // both zapEvent and zapStream have process in them.
4
+ return "process" in event;
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).
10
6
  export class ZapServer {
11
- // public server: http.Server;
12
7
  wss;
8
+ backend;
13
9
  onconnect;
14
10
  onconnectHandler;
15
11
  wsToId;
@@ -17,8 +13,18 @@ export class ZapServer {
17
13
  persistantContext;
18
14
  _events = {};
19
15
  heartbeatMiss = new Map();
20
- constructor({ port, events = {}, options }, callback) {
21
- this.wss = new WebSocketServer({ port });
16
+ constructor({ port, events = {}, backend = 'ws', options }, callback) {
17
+ // Select backend
18
+ switch (backend) {
19
+ // case 'uws':
20
+ // this.backend = new UWSBackend();
21
+ // break;
22
+ case 'ws':
23
+ default:
24
+ this.backend = new WSBackend();
25
+ break;
26
+ }
27
+ this.wss = this.backend.createServer({ port });
22
28
  this.wsToId = new Map();
23
29
  this.idToWs = new Map();
24
30
  this.persistantContext = new Map();
@@ -69,9 +75,10 @@ export class ZapServer {
69
75
  this.broadcastRaw("heartbeat");
70
76
  }
71
77
  async handleMessage(ws, req, message) {
78
+ const messageStr = message.toString();
72
79
  const id = this.wsToId.get(ws);
73
80
  // setting up socket id
74
- if (!id && message.toString() === "OPEN") {
81
+ if (!id && messageStr === "OPEN") {
75
82
  const id = generateId();
76
83
  this.wsToId.set(ws, id);
77
84
  this.idToWs.set(id, ws);
@@ -82,11 +89,11 @@ export class ZapServer {
82
89
  });
83
90
  return;
84
91
  }
85
- else if (id && message.toString() === "heartbeat") {
92
+ else if (id && messageStr === "heartbeat") {
86
93
  this.heartbeatMiss.set(id, 0);
87
94
  }
88
95
  const clientId = this.wsToId.get(ws);
89
- const parsedMessage = deserialize(message.toString());
96
+ const parsedMessage = deserialize(messageStr);
90
97
  if (!parsedMessage)
91
98
  return;
92
99
  const { event, stream, data, requestId, streamId, batch } = parsedMessage;
@@ -98,8 +105,8 @@ export class ZapServer {
98
105
  const inputType = eventObj.input;
99
106
  const { success, error } = inputType.safeParse(data);
100
107
  if (!success && error) {
101
- // check if the message is of req-res
102
108
  if (requestId) {
109
+ // Handle validation error response
103
110
  }
104
111
  return;
105
112
  }
@@ -116,7 +123,7 @@ export class ZapServer {
116
123
  id: clientId,
117
124
  ip: req.socket.remoteAddress,
118
125
  timestamp: Date.now(),
119
- size: message.toString().length,
126
+ size: messageStr.length,
120
127
  };
121
128
  const msg = {
122
129
  event: key,
@@ -131,7 +138,7 @@ export class ZapServer {
131
138
  }
132
139
  // All middleware passed
133
140
  const context = { server: this, id: this.wsToId.get(ws), buffer: ctx };
134
- if (requestId) { // req-res premitive
141
+ if (requestId) { // req-res primitive
135
142
  let result;
136
143
  if (batch) {
137
144
  result = data.map((part) => process(part, context));
@@ -151,7 +158,7 @@ export class ZapServer {
151
158
  return;
152
159
  ws.send(serialized);
153
160
  }
154
- else if (streamId) { // stream premitive
161
+ else if (streamId) { // stream primitive
155
162
  const consumeStream = async () => {
156
163
  const result = process(data, context);
157
164
  for await (const fragment of result) {
@@ -164,18 +171,15 @@ export class ZapServer {
164
171
  }
165
172
  sendMessageRaw(clientId, data) {
166
173
  const ws = this.idToWs.get(clientId);
167
- // TODO: throw a nice error
168
174
  if (!ws)
169
175
  return;
170
176
  const serializedData = serialize(data);
171
- // TODO: throw a nice error
172
177
  if (!serializedData)
173
178
  return;
174
179
  ws.send(serializedData);
175
180
  }
176
181
  sendMessage(event, clientId, data) {
177
182
  const ws = this.idToWs.get(clientId);
178
- // TODO: throw a nice error
179
183
  if (!ws)
180
184
  return;
181
185
  const packet = {
@@ -183,7 +187,6 @@ export class ZapServer {
183
187
  data
184
188
  };
185
189
  const serializedPacket = serialize(packet);
186
- // TODO: throw a nice error
187
190
  if (!serializedPacket)
188
191
  return;
189
192
  ws.send(serializedPacket);
@@ -191,7 +194,6 @@ export class ZapServer {
191
194
  broadcastRaw(data) {
192
195
  const serializedData = serialize(data);
193
196
  if (!serializedData) {
194
- // TODO: throw a nice error
195
197
  return;
196
198
  }
197
199
  this.idToWs.forEach((ws) => {
@@ -213,14 +215,13 @@ export class ZapServer {
213
215
  selectiveBroascast(event, data, connections) {
214
216
  const serialized = serialize(data);
215
217
  if (!serialized) {
216
- // TODO: throw a nice error
217
218
  return;
218
219
  }
219
220
  const packet = {
220
221
  event,
221
222
  data
222
223
  };
223
- const serializedPacket = serialize(packet); // if data is serializable then packet is too, so no need to check
224
+ const serializedPacket = serialize(packet);
224
225
  connections
225
226
  .flatMap(x => this.idToWs.get(x) ?? [])
226
227
  .forEach((ws) => {
@@ -251,13 +252,13 @@ export class ZapServer {
251
252
  }));
252
253
  }
253
254
  get clients() {
254
- return this.idToWs.keys().toArray();
255
+ return Array.from(this.idToWs.keys());
255
256
  }
256
257
  get socketMap() {
257
258
  return this.idToWs;
258
259
  }
259
260
  }
260
- export const createZapServer = ({ port, events, cors, options }, callback) => {
261
- const server = new ZapServer({ port, events, cors, options }, callback);
261
+ export const createZapServer = ({ port, events, cors, backend = 'ws', options }, callback) => {
262
+ const server = new ZapServer({ port, events, cors, backend, options }, callback);
262
263
  return server;
263
264
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zap-socket/server",
3
- "version": "0.0.20",
3
+ "version": "0.0.21",
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",