@forklaunch/ws 0.1.0

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/lib/index.d.ts ADDED
@@ -0,0 +1,809 @@
1
+ import { EventSchema, ExtractSchemaFromRecord, ExtractSchemaFromEntry, ServerEventSchema } from '@forklaunch/core/ws';
2
+ import { AnySchemaValidator, IdiomaticSchema, Schema } from '@forklaunch/validator';
3
+ import { IncomingMessage, ClientRequest } from 'http';
4
+ import { WebSocket, WebSocketServer } from 'ws';
5
+ export { WebSocket, WebSocketServer } from 'ws';
6
+ export { EventEmitter } from 'events';
7
+
8
+ /**
9
+ * Event map for outgoing events (emit)
10
+ * Maps event names to their argument signatures for type-safe event emission
11
+ *
12
+ * @internal
13
+ */
14
+ type OutgoingEventMap<SV extends AnySchemaValidator, ES extends EventSchema<SV>> = {
15
+ message: [
16
+ data: Schema<ExtractSchemaFromRecord<SV, ES['clientMessages']>, SV>,
17
+ isBinary: boolean
18
+ ];
19
+ close: [
20
+ code: number,
21
+ reason: Schema<ExtractSchemaFromRecord<SV, ES['closeReason']>, SV>
22
+ ];
23
+ error: [error: Schema<ExtractSchemaFromRecord<SV, ES['errors']>, SV>];
24
+ open: [];
25
+ ping: [data: Schema<ExtractSchemaFromEntry<SV, ES['ping']>, SV>];
26
+ pong: [data: Schema<ExtractSchemaFromEntry<SV, ES['pong']>, SV>];
27
+ };
28
+ /**
29
+ * Extended WebSocket with built-in schema validation and automatic data transformation.
30
+ *
31
+ * ForklaunchWebSocket automatically:
32
+ * - Validates incoming and outgoing messages against provided schemas
33
+ * - Decodes Buffer data to JavaScript objects for incoming messages
34
+ * - Encodes JavaScript objects to Buffer data for outgoing messages
35
+ * - Provides type-safe event handlers with full TypeScript support
36
+ *
37
+ * @template SV - The schema validator type (e.g., ZodSchemaValidator)
38
+ * @template ES - The event schema defining message types for each event
39
+ *
40
+ * @example Basic Usage
41
+ * ```typescript
42
+ * import { ZodSchemaValidator } from '@forklaunch/validator';
43
+ * import { z } from 'zod';
44
+ * import { ForklaunchWebSocket } from '@forklaunch/ws';
45
+ *
46
+ * const validator = new ZodSchemaValidator();
47
+ * const schemas = {
48
+ * ping: z.object({ ts: z.number() }),
49
+ * pong: z.object({ ts: z.number() }),
50
+ * clientMessages: z.object({ type: z.string(), data: z.any() }),
51
+ * serverMessages: z.object({ type: z.string(), data: z.any() }),
52
+ * errors: z.object({ code: z.number(), message: z.string() }),
53
+ * closeReason: z.object({ reason: z.string() })
54
+ * };
55
+ *
56
+ * const ws = new ForklaunchWebSocket(
57
+ * validator,
58
+ * schemas,
59
+ * 'ws://localhost:8080'
60
+ * );
61
+ *
62
+ * // Incoming messages are automatically validated and decoded
63
+ * ws.on('message', (data, isBinary) => {
64
+ * console.log('Received:', data); // data is typed and validated
65
+ * });
66
+ *
67
+ * // Outgoing messages are automatically validated and encoded
68
+ * ws.send({ type: 'hello', data: 'world' });
69
+ * ```
70
+ *
71
+ * @example With Custom Schemas
72
+ * ```typescript
73
+ * const chatSchemas = {
74
+ * ping: z.object({ timestamp: z.number() }),
75
+ * pong: z.object({ timestamp: z.number() }),
76
+ * clientMessages: z.discriminatedUnion('type', [
77
+ * z.object({ type: z.literal('chat'), message: z.string(), userId: z.string() }),
78
+ * z.object({ type: z.literal('join'), roomId: z.string() })
79
+ * ]),
80
+ * serverMessages: z.discriminatedUnion('type', [
81
+ * z.object({ type: z.literal('chat'), message: z.string(), userId: z.string() }),
82
+ * z.object({ type: z.literal('user-joined'), userId: z.string() })
83
+ * ])
84
+ * };
85
+ *
86
+ * const ws = new ForklaunchWebSocket(validator, chatSchemas, 'ws://chat.example.com');
87
+ * ```
88
+ *
89
+ * @see {@link EventSchema} for schema structure
90
+ * @see {@link ForklaunchWebSocketServer} for server-side usage
91
+ */
92
+ declare class ForklaunchWebSocket<SV extends AnySchemaValidator, const ES extends EventSchema<SV>> extends WebSocket {
93
+ private eventSchemas;
94
+ private schemas;
95
+ private schemaValidator;
96
+ /**
97
+ * Creates a new ForklaunchWebSocket instance with schema validation.
98
+ *
99
+ * @param schemaValidator - The schema validator instance (e.g., ZodSchemaValidator)
100
+ * @param eventSchemas - Schema definitions for all WebSocket events
101
+ * @param websocketParams - Standard WebSocket constructor parameters (address, protocols, options)
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * const ws = new ForklaunchWebSocket(
106
+ * validator,
107
+ * schemas,
108
+ * 'ws://localhost:8080',
109
+ * ['chat-protocol'],
110
+ * { handshakeTimeout: 5000 }
111
+ * );
112
+ * ```
113
+ */
114
+ constructor(schemaValidator: SV, eventSchemas: ES, ...websocketParams: ConstructorParameters<typeof WebSocket>);
115
+ /**
116
+ * Decodes and validates incoming data from the WebSocket.
117
+ *
118
+ * This method handles multiple data formats:
119
+ * - Buffer → decoded to UTF-8 string → parsed as JSON → validated
120
+ * - ArrayBuffer → converted to Buffer → decoded → parsed → validated
121
+ * - TypedArray → converted to Buffer → decoded → parsed → validated
122
+ * - String → parsed as JSON → validated
123
+ * - Object → validated directly (already parsed)
124
+ *
125
+ * @param data - The raw data received from the WebSocket
126
+ * @param schema - Optional schema to validate against
127
+ * @returns The validated and decoded data
128
+ * @throws {Error} If validation fails with pretty-printed error messages
129
+ *
130
+ * @internal
131
+ */
132
+ protected decodeAndValidate(data: unknown, schema: IdiomaticSchema<SV> | undefined): unknown;
133
+ /**
134
+ * Validates and encodes outgoing data for transmission over the WebSocket.
135
+ *
136
+ * This method:
137
+ * 1. Validates data against the provided schema (if present)
138
+ * 2. Encodes data to Buffer format:
139
+ * - Buffer → returned as-is
140
+ * - Object/Array → JSON.stringify → Buffer
141
+ * - String → Buffer
142
+ * - Number/Boolean → JSON.stringify → Buffer
143
+ * - null/undefined → returned as-is
144
+ *
145
+ * @param data - The data to validate and encode
146
+ * @param schema - Optional schema to validate against
147
+ * @returns The validated and encoded data as Buffer, or null/undefined
148
+ * @throws {Error} If validation fails with pretty-printed error messages
149
+ *
150
+ * @internal
151
+ */
152
+ protected validateAndEncode(data: unknown, schema: IdiomaticSchema<SV> | undefined, allowUndefined?: boolean, context?: string): Buffer<ArrayBufferLike> | undefined;
153
+ /**
154
+ * Transforms incoming event arguments by decoding and validating them.
155
+ *
156
+ * Applies transformation for:
157
+ * - `message` events: validates message data
158
+ * - `close` events: validates close reason
159
+ * - `ping` events: validates ping data
160
+ * - `pong` events: validates pong data
161
+ *
162
+ * @param event - The event name
163
+ * @param args - The raw event arguments
164
+ * @returns Transformed and validated arguments
165
+ *
166
+ * @internal
167
+ */
168
+ protected transformIncomingArgs(event: string | symbol, args: unknown[]): unknown[];
169
+ /**
170
+ * Wraps an event listener with data transformation logic.
171
+ *
172
+ * This helper intercepts listener registration to inject automatic
173
+ * decoding and validation of incoming event data before passing it
174
+ * to the user's listener function.
175
+ *
176
+ * @param superMethod - The parent class method to call (super.on, super.once, etc.)
177
+ * @param event - The event name
178
+ * @param listener - The user's listener function
179
+ * @returns `this` for method chaining
180
+ *
181
+ * @internal
182
+ */
183
+ protected wrapListenerWithTransformation<Event extends string | symbol>(superMethod: (event: Event, listener: (ws: WebSocket, ...args: never[]) => void) => this, event: Event, listener: (ws: WebSocket, ...args: unknown[]) => void): this;
184
+ /**
185
+ * Registers an event listener with automatic data validation and transformation.
186
+ *
187
+ * All incoming data is automatically:
188
+ * - Decoded from Buffer to JavaScript objects
189
+ * - Parsed from JSON
190
+ * - Validated against the provided schemas
191
+ *
192
+ * @param event - The event name to listen for
193
+ * @param listener - The callback function to invoke when the event occurs
194
+ * @returns `this` for method chaining
195
+ *
196
+ * @example Listen for validated messages
197
+ * ```typescript
198
+ * ws.on('message', (data, isBinary) => {
199
+ * // data is automatically validated and typed according to serverMessages schema
200
+ * console.log('Received:', data);
201
+ * });
202
+ * ```
203
+ *
204
+ * @example Listen for connection events
205
+ * ```typescript
206
+ * ws.on('open', () => {
207
+ * console.log('Connected!');
208
+ * });
209
+ *
210
+ * ws.on('close', (code, reason) => {
211
+ * console.log('Disconnected:', code, reason);
212
+ * });
213
+ *
214
+ * ws.on('error', (error) => {
215
+ * console.error('WebSocket error:', error);
216
+ * });
217
+ * ```
218
+ */
219
+ on(event: 'close', listener: (this: WebSocket, code: number, reason: Schema<ExtractSchemaFromRecord<SV, ES['closeReason']>, SV>) => void): this;
220
+ on(event: 'error', listener: (this: WebSocket, error: Error) => void): this;
221
+ on(event: 'upgrade', listener: (this: WebSocket, request: IncomingMessage) => void): this;
222
+ on(event: 'message', listener: (this: WebSocket, data: Schema<ExtractSchemaFromRecord<SV, ES['serverMessages']>, SV>, isBinary: boolean) => void): this;
223
+ on(event: 'open', listener: (this: WebSocket) => void): this;
224
+ on(event: 'ping', listener: (this: WebSocket, data: Schema<ExtractSchemaFromEntry<SV, ES['ping']>, SV>) => void): this;
225
+ on(event: 'pong', listener: (this: WebSocket, data: Schema<ExtractSchemaFromEntry<SV, ES['pong']>, SV>) => void): this;
226
+ on(event: 'redirect', listener: (this: WebSocket, url: string, request: ClientRequest) => void): this;
227
+ on(event: 'unexpected-response', listener: (this: WebSocket, request: ClientRequest, response: IncomingMessage) => void): this;
228
+ on(event: string | symbol, listener: (this: WebSocket, ...args: never[]) => void): this;
229
+ /**
230
+ * Registers a one-time event listener with automatic data validation and transformation.
231
+ *
232
+ * The listener will be invoked at most once after being registered, and then removed.
233
+ * All incoming data is automatically decoded, parsed, and validated.
234
+ *
235
+ * @param event - The event name to listen for
236
+ * @param listener - The callback function to invoke when the event occurs
237
+ * @returns `this` for method chaining
238
+ *
239
+ * @example
240
+ * ```typescript
241
+ * // Listen for the first message only
242
+ * ws.once('message', (data, isBinary) => {
243
+ * console.log('First message:', data);
244
+ * });
245
+ *
246
+ * // Wait for connection
247
+ * ws.once('open', () => {
248
+ * console.log('Connected! This will only fire once.');
249
+ * });
250
+ * ```
251
+ */
252
+ once(event: 'close', listener: (this: WebSocket, code: number, reason: Schema<ExtractSchemaFromRecord<SV, ES['closeReason']>, SV>) => void): this;
253
+ once(event: 'error', listener: (this: WebSocket, error: Error) => void): this;
254
+ once(event: 'upgrade', listener: (this: WebSocket, request: IncomingMessage) => void): this;
255
+ once(event: 'message', listener: (this: WebSocket, data: Schema<ExtractSchemaFromRecord<SV, ES['serverMessages']>, SV>, isBinary: boolean) => void): this;
256
+ once(event: 'open', listener: (this: WebSocket) => void): this;
257
+ once(event: 'ping', listener: (this: WebSocket, data: Schema<ExtractSchemaFromEntry<SV, ES['ping']>, SV>) => void): this;
258
+ once(event: 'pong', listener: (this: WebSocket, data: Schema<ExtractSchemaFromEntry<SV, ES['pong']>, SV>) => void): this;
259
+ once(event: 'redirect', listener: (this: WebSocket, url: string, request: ClientRequest) => void): this;
260
+ once(event: 'unexpected-response', listener: (this: WebSocket, request: ClientRequest, response: IncomingMessage) => void): this;
261
+ once(event: string | symbol, listener: (this: WebSocket, ...args: never[]) => void): this;
262
+ /**
263
+ * Removes a previously registered event listener.
264
+ *
265
+ * To successfully remove a listener, you must pass the exact same function
266
+ * reference that was used when registering it.
267
+ *
268
+ * @param event - The event name
269
+ * @param listener - The exact function reference to remove
270
+ * @returns `this` for method chaining
271
+ *
272
+ * @example
273
+ * ```typescript
274
+ * const messageHandler = (data, isBinary) => {
275
+ * console.log('Message:', data);
276
+ * };
277
+ *
278
+ * // Register
279
+ * ws.on('message', messageHandler);
280
+ *
281
+ * // Later, remove
282
+ * ws.off('message', messageHandler);
283
+ * ```
284
+ */
285
+ off(event: 'close', listener: (this: WebSocket, code: number, reason: Schema<ExtractSchemaFromRecord<SV, ES['closeReason']>, SV>) => void): this;
286
+ off(event: 'error', listener: (this: WebSocket, error: Error) => void): this;
287
+ off(event: 'upgrade', listener: (this: WebSocket, request: IncomingMessage) => void): this;
288
+ off(event: 'message', listener: (this: WebSocket, data: Schema<ExtractSchemaFromRecord<SV, ES['serverMessages']>, SV>, isBinary: boolean) => void): this;
289
+ off(event: 'open', listener: (this: WebSocket) => void): this;
290
+ off(event: 'ping', listener: (this: WebSocket, data: Schema<ExtractSchemaFromEntry<SV, ES['ping']>, SV>) => void): this;
291
+ off(event: 'pong', listener: (this: WebSocket, data: Schema<ExtractSchemaFromEntry<SV, ES['pong']>, SV>) => void): this;
292
+ off(event: 'redirect', listener: (this: WebSocket, url: string, request: ClientRequest) => void): this;
293
+ off(event: 'unexpected-response', listener: (this: WebSocket, request: ClientRequest, response: IncomingMessage) => void): this;
294
+ off(event: string | symbol, listener: (this: WebSocket, ...args: never[]) => void): this;
295
+ /**
296
+ * Registers an event listener (alias for `on()`).
297
+ *
298
+ * This method is functionally identical to `on()` and is provided for
299
+ * compatibility with the EventEmitter API.
300
+ *
301
+ * @param event - The event name to listen for
302
+ * @param listener - The callback function to invoke when the event occurs
303
+ * @returns `this` for method chaining
304
+ *
305
+ * @see {@link on} for detailed documentation and examples
306
+ */
307
+ addListener(event: 'close', listener: (this: WebSocket, code: number, reason: Schema<ExtractSchemaFromRecord<SV, ES['closeReason']>, SV>) => void): this;
308
+ addListener(event: 'error', listener: (this: WebSocket, error: Error) => void): this;
309
+ addListener(event: 'upgrade', listener: (this: WebSocket, request: IncomingMessage) => void): this;
310
+ addListener(event: 'message', listener: (this: WebSocket, data: Schema<ExtractSchemaFromRecord<SV, ES['serverMessages']>, SV>, isBinary: boolean) => void): this;
311
+ addListener(event: 'open', listener: (this: WebSocket) => void): this;
312
+ addListener(event: 'ping', listener: (this: WebSocket, data: Schema<ExtractSchemaFromEntry<SV, ES['ping']>, SV>) => void): this;
313
+ addListener(event: 'pong', listener: (this: WebSocket, data: Schema<ExtractSchemaFromEntry<SV, ES['pong']>, SV>) => void): this;
314
+ addListener(event: 'redirect', listener: (this: WebSocket, url: string, request: ClientRequest) => void): this;
315
+ addListener(event: 'unexpected-response', listener: (this: WebSocket, request: ClientRequest, response: IncomingMessage) => void): this;
316
+ addListener(event: string | symbol, listener: (this: WebSocket, ...args: never[]) => void): this;
317
+ /**
318
+ * Removes a previously registered event listener (alias for `off()`).
319
+ *
320
+ * This method is functionally identical to `off()` and is provided for
321
+ * compatibility with the EventEmitter API.
322
+ *
323
+ * @param event - The event name
324
+ * @param listener - The exact function reference to remove
325
+ * @returns `this` for method chaining
326
+ *
327
+ * @see {@link off} for detailed documentation and examples
328
+ */
329
+ removeListener(event: 'close', listener: (this: WebSocket, code: number, reason: Schema<ExtractSchemaFromRecord<SV, ES['closeReason']>, SV>) => void): this;
330
+ removeListener(event: 'error', listener: (this: WebSocket, error: Error) => void): this;
331
+ removeListener(event: 'upgrade', listener: (this: WebSocket, request: IncomingMessage) => void): this;
332
+ removeListener(event: 'message', listener: (this: WebSocket, data: Schema<ExtractSchemaFromRecord<SV, ES['serverMessages']>, SV>, isBinary: boolean) => void): this;
333
+ removeListener(event: 'open', listener: (this: WebSocket) => void): this;
334
+ removeListener(event: 'ping', listener: (this: WebSocket, data: Schema<ExtractSchemaFromEntry<SV, ES['ping']>, SV>) => void): this;
335
+ removeListener(event: 'pong', listener: (this: WebSocket, data: Schema<ExtractSchemaFromEntry<SV, ES['pong']>, SV>) => void): this;
336
+ removeListener(event: 'redirect', listener: (this: WebSocket, url: string, request: ClientRequest) => void): this;
337
+ removeListener(event: 'unexpected-response', listener: (this: WebSocket, request: ClientRequest, response: IncomingMessage) => void): this;
338
+ removeListener(event: string | symbol, listener: (this: WebSocket, ...args: never[]) => void): this;
339
+ /**
340
+ * Emits an event with automatic data validation and encoding.
341
+ *
342
+ * All outgoing data is automatically:
343
+ * - Validated against the provided schemas
344
+ * - Encoded from JavaScript objects to Buffer format
345
+ * - Transmitted over the WebSocket connection
346
+ *
347
+ * @param event - The event name to emit
348
+ * @param args - The event arguments (type-checked based on event name)
349
+ * @returns `true` if the event had listeners, `false` otherwise
350
+ *
351
+ * @example Emit a message
352
+ * ```typescript
353
+ * // Data is validated against clientMessages schema and auto-encoded
354
+ * ws.emit('message', { type: 'chat', text: 'Hello!' }, true);
355
+ * ```
356
+ *
357
+ * @example Emit other events
358
+ * ```typescript
359
+ * ws.emit('ping', { timestamp: Date.now() });
360
+ * ws.emit('close', 1000, { reason: 'Normal closure' });
361
+ * ws.emit('error', { code: 500, message: 'Server error' });
362
+ * ```
363
+ */
364
+ emit<K extends keyof OutgoingEventMap<SV, ES>>(event: K, ...args: OutgoingEventMap<SV, ES>[K]): boolean;
365
+ /**
366
+ * Sends data over the WebSocket with automatic validation and encoding.
367
+ *
368
+ * The data is:
369
+ * 1. Validated against the `clientMessages` schema
370
+ * 2. Automatically encoded to Buffer format (JSON.stringify)
371
+ * 3. Transmitted to the server
372
+ *
373
+ * @param data - The message data to send (must match clientMessages schema)
374
+ * @param options - Optional send options (mask, binary, compress, fin)
375
+ * @param cb - Optional callback invoked when send completes or errors
376
+ * @throws {Error} If validation fails or data cannot be encoded
377
+ *
378
+ * @example Send a message
379
+ * ```typescript
380
+ * // Data is type-checked and validated at runtime
381
+ * ws.send({ type: 'chat', message: 'Hello, world!', userId: '123' });
382
+ * ```
383
+ *
384
+ * @example Send with options and callback
385
+ * ```typescript
386
+ * ws.send(
387
+ * { type: 'ping', timestamp: Date.now() },
388
+ * { compress: true, binary: true },
389
+ * (error) => {
390
+ * if (error) console.error('Send failed:', error);
391
+ * else console.log('Send succeeded');
392
+ * }
393
+ * );
394
+ * ```
395
+ *
396
+ * @remarks
397
+ * This method intentionally restricts the parameter type from `BufferLike` to
398
+ * schema-validated types to provide compile-time type safety. This is a deliberate
399
+ * design choice to catch type errors at compile time rather than runtime.
400
+ */
401
+ send(data: Schema<ExtractSchemaFromRecord<SV, ES['clientMessages']>, SV>, cb?: (err?: Error) => void): void;
402
+ send(data: Schema<ExtractSchemaFromRecord<SV, ES['clientMessages']>, SV>, options: {
403
+ mask?: boolean;
404
+ binary?: boolean;
405
+ compress?: boolean;
406
+ fin?: boolean;
407
+ }, cb?: (err?: Error) => void): void;
408
+ /**
409
+ * Closes the WebSocket connection with optional validated close reason.
410
+ *
411
+ * @param code - Optional close code (default: 1000 for normal closure)
412
+ * @param reason - Optional close reason (validated against closeReason schema)
413
+ *
414
+ * @example Close with default code
415
+ * ```typescript
416
+ * ws.close();
417
+ * ```
418
+ *
419
+ * @example Close with code and reason
420
+ * ```typescript
421
+ * ws.close(1000, { reason: 'User logged out' });
422
+ * ```
423
+ *
424
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code|WebSocket Close Codes}
425
+ */
426
+ close(code?: number, reason?: Schema<ExtractSchemaFromRecord<SV, ES['closeReason']>, SV>): void;
427
+ /**
428
+ * Sends a ping frame with optional validated data payload.
429
+ *
430
+ * Ping frames are used to check if the connection is alive. The server
431
+ * should respond with a pong frame.
432
+ *
433
+ * @param data - Optional ping data (validated against ping schema)
434
+ * @param mask - Whether to mask the frame (default: true for clients)
435
+ * @param cb - Optional callback invoked when ping is sent or errors
436
+ *
437
+ * @example Send a ping
438
+ * ```typescript
439
+ * ws.ping({ timestamp: Date.now() });
440
+ * ```
441
+ *
442
+ * @example Send ping with callback
443
+ * ```typescript
444
+ * ws.ping({ ts: Date.now() }, true, (error) => {
445
+ * if (error) console.error('Ping failed:', error);
446
+ * });
447
+ * ```
448
+ */
449
+ ping(data?: Schema<ExtractSchemaFromEntry<SV, ES['ping']>, SV>, mask?: boolean, cb?: (err: Error) => void): void;
450
+ /**
451
+ * Sends a pong frame with optional validated data payload.
452
+ *
453
+ * Pong frames are typically sent in response to ping frames, but can
454
+ * also be sent unsolicited as a unidirectional heartbeat.
455
+ *
456
+ * @param data - Optional pong data (validated against pong schema)
457
+ * @param mask - Whether to mask the frame (default: true for clients)
458
+ * @param cb - Optional callback invoked when pong is sent or errors
459
+ *
460
+ * @example Send a pong
461
+ * ```typescript
462
+ * ws.pong({ timestamp: Date.now() });
463
+ * ```
464
+ *
465
+ * @example Respond to ping
466
+ * ```typescript
467
+ * ws.on('ping', (data) => {
468
+ * console.log('Received ping:', data);
469
+ * ws.pong(data); // Echo the ping data back
470
+ * });
471
+ * ```
472
+ */
473
+ pong(data?: Schema<ExtractSchemaFromEntry<SV, ES['pong']>, SV>, mask?: boolean, cb?: (err: Error) => void): void;
474
+ }
475
+
476
+ /**
477
+ * Extended WebSocketServer that automatically injects schema validation into all connected clients.
478
+ *
479
+ * When clients connect, they receive a {@link ForklaunchWebSocket} instance with automatic:
480
+ * - Message validation against provided schemas
481
+ * - Buffer ↔ Object transformation
482
+ * - Type-safe event handlers
483
+ *
484
+ * Note: The server-side WebSocket swaps `clientMessages` and `serverMessages` schemas,
485
+ * so what the client sends as `clientMessages` arrives at the server as `serverMessages`.
486
+ *
487
+ * @template SV - The schema validator type (e.g., ZodSchemaValidator)
488
+ * @template ES - The event schema defining message types for each event
489
+ *
490
+ * @example Basic Chat Server
491
+ * ```typescript
492
+ * import { ZodSchemaValidator } from '@forklaunch/validator';
493
+ * import { z } from 'zod';
494
+ * import { ForklaunchWebSocketServer } from '@forklaunch/ws';
495
+ *
496
+ * const validator = new ZodSchemaValidator();
497
+ * const schemas = {
498
+ * ping: z.object({ ts: z.number() }),
499
+ * pong: z.object({ ts: z.number() }),
500
+ * clientMessages: z.discriminatedUnion('type', [
501
+ * z.object({ type: z.literal('chat'), message: z.string(), userId: z.string() }),
502
+ * z.object({ type: z.literal('join'), roomId: z.string() })
503
+ * ]),
504
+ * serverMessages: z.discriminatedUnion('type', [
505
+ * z.object({ type: z.literal('chat'), message: z.string(), userId: z.string() }),
506
+ * z.object({ type: z.literal('user-joined'), userId: z.string() })
507
+ * ])
508
+ * };
509
+ *
510
+ * const wss = new ForklaunchWebSocketServer(validator, schemas, { port: 8080 });
511
+ *
512
+ * wss.on('connection', (ws, request) => {
513
+ * console.log('Client connected:', request.socket.remoteAddress);
514
+ *
515
+ * // Send welcome message (validated and encoded automatically)
516
+ * ws.send({ type: 'chat', message: 'Welcome!', userId: 'server' });
517
+ *
518
+ * // Listen for incoming messages (validated and decoded automatically)
519
+ * ws.on('message', (data, isBinary) => {
520
+ * console.log('Received:', data); // data is typed and validated
521
+ *
522
+ * // Broadcast to other clients
523
+ * wss.clients.forEach((client) => {
524
+ * if (client !== ws && client.readyState === WebSocket.OPEN) {
525
+ * client.send(data);
526
+ * }
527
+ * });
528
+ * });
529
+ *
530
+ * ws.on('close', (code, reason) => {
531
+ * console.log('Client disconnected:', code, reason);
532
+ * });
533
+ * });
534
+ * ```
535
+ *
536
+ * @example With Authentication
537
+ * ```typescript
538
+ * wss.on('connection', (ws, request) => {
539
+ * const token = new URL(request.url!, 'ws://localhost').searchParams.get('token');
540
+ *
541
+ * if (!token || !isValidToken(token)) {
542
+ * ws.close(1008, { reason: 'Unauthorized' });
543
+ * return;
544
+ * }
545
+ *
546
+ * // Proceed with authenticated connection
547
+ * ws.on('message', (data) => {
548
+ * // Handle authenticated messages
549
+ * });
550
+ * });
551
+ * ```
552
+ *
553
+ * @see {@link ForklaunchWebSocket} for client-side usage
554
+ * @see {@link EventSchema} for schema structure
555
+ */
556
+ declare class ForklaunchWebSocketServer<SV extends AnySchemaValidator, const ES extends EventSchema<SV>> extends WebSocketServer {
557
+ /**
558
+ * Creates a new ForklaunchWebSocketServer with schema validation.
559
+ *
560
+ * @param _schemaValidator - The schema validator instance (e.g., ZodSchemaValidator)
561
+ * @param _eventSchemas - Schema definitions for all WebSocket events
562
+ * @param options - Standard WebSocketServer options (port, host, server, etc.)
563
+ * @param callback - Optional callback invoked when the server starts listening
564
+ *
565
+ * @example Create a server on port 8080
566
+ * ```typescript
567
+ * const wss = new ForklaunchWebSocketServer(
568
+ * validator,
569
+ * schemas,
570
+ * { port: 8080 },
571
+ * () => console.log('Server started on port 8080')
572
+ * );
573
+ * ```
574
+ *
575
+ * @example Create a server with existing HTTP server
576
+ * ```typescript
577
+ * import { createServer } from 'http';
578
+ *
579
+ * const httpServer = createServer();
580
+ * const wss = new ForklaunchWebSocketServer(
581
+ * validator,
582
+ * schemas,
583
+ * { server: httpServer }
584
+ * );
585
+ *
586
+ * httpServer.listen(8080);
587
+ * ```
588
+ *
589
+ * @example No server mode (for upgrade handling)
590
+ * ```typescript
591
+ * const wss = new ForklaunchWebSocketServer(
592
+ * validator,
593
+ * schemas,
594
+ * { noServer: true }
595
+ * );
596
+ *
597
+ * httpServer.on('upgrade', (request, socket, head) => {
598
+ * wss.handleUpgrade(request, socket, head, (ws) => {
599
+ * wss.emit('connection', ws, request);
600
+ * });
601
+ * });
602
+ * ```
603
+ */
604
+ constructor(_schemaValidator: SV, _eventSchemas: ES, options?: ConstructorParameters<typeof WebSocketServer>[0], callback?: () => void);
605
+ /**
606
+ * Registers an event listener for server events.
607
+ *
608
+ * The `connection` event provides a {@link ForklaunchWebSocket} instance (not a plain WebSocket),
609
+ * which includes automatic validation and transformation for all messages.
610
+ *
611
+ * @param event - The server event name to listen for
612
+ * @param listener - The callback function to invoke when the event occurs
613
+ * @returns `this` for method chaining
614
+ *
615
+ * @example Handle new connections
616
+ * ```typescript
617
+ * wss.on('connection', (ws, request) => {
618
+ * console.log('Client connected from:', request.socket.remoteAddress);
619
+ *
620
+ * ws.on('message', (data, isBinary) => {
621
+ * console.log('Received:', data); // Auto-validated and typed
622
+ * });
623
+ * });
624
+ * ```
625
+ *
626
+ * @example Handle server errors
627
+ * ```typescript
628
+ * wss.on('error', (error) => {
629
+ * console.error('Server error:', error);
630
+ * });
631
+ * ```
632
+ *
633
+ * @example Listen for server lifecycle events
634
+ * ```typescript
635
+ * wss.on('listening', () => {
636
+ * console.log('Server is listening');
637
+ * });
638
+ *
639
+ * wss.on('close', () => {
640
+ * console.log('Server closed');
641
+ * });
642
+ * ```
643
+ *
644
+ * @remarks
645
+ * Note: TypeScript shows `ws` as `WebSocket` in the type signature due to override
646
+ * limitations, but at runtime it's actually a `ForklaunchWebSocket` with full validation.
647
+ */
648
+ on(event: 'connection', listener: (ws: ForklaunchWebSocket<SV, ServerEventSchema<SV, ES>>, request: IncomingMessage) => void): this;
649
+ on(event: 'error', listener: (error: Error) => void): this;
650
+ on(event: 'headers', listener: (headers: string[], request: IncomingMessage) => void): this;
651
+ on(event: 'close', listener: () => void): this;
652
+ on(event: 'listening', listener: () => void): this;
653
+ on(event: string | symbol, listener: (...args: never[]) => void): this;
654
+ /**
655
+ * Registers a one-time event listener for server events.
656
+ *
657
+ * The listener will be invoked at most once after being registered, and then removed.
658
+ *
659
+ * @param event - The server event name to listen for
660
+ * @param listener - The callback function to invoke when the event occurs
661
+ * @returns `this` for method chaining
662
+ *
663
+ * @example Wait for first connection
664
+ * ```typescript
665
+ * wss.once('connection', (ws, request) => {
666
+ * console.log('First client connected!');
667
+ * // This will only fire for the first connection
668
+ * });
669
+ * ```
670
+ *
671
+ * @example Wait for server to start
672
+ * ```typescript
673
+ * wss.once('listening', () => {
674
+ * console.log('Server is now ready to accept connections');
675
+ * });
676
+ * ```
677
+ */
678
+ once(event: 'connection', listener: (ws: ForklaunchWebSocket<SV, ServerEventSchema<SV, ES>>, request: IncomingMessage) => void): this;
679
+ once(event: 'error', listener: (error: Error) => void): this;
680
+ once(event: 'headers', listener: (headers: string[], request: IncomingMessage) => void): this;
681
+ once(event: 'close', listener: () => void): this;
682
+ once(event: 'listening', listener: () => void): this;
683
+ once(event: string | symbol, listener: (...args: never[]) => void): this;
684
+ /**
685
+ * Removes a previously registered event listener.
686
+ *
687
+ * To successfully remove a listener, you must pass the exact same function
688
+ * reference that was used when registering it.
689
+ *
690
+ * @param event - The server event name
691
+ * @param listener - The exact function reference to remove
692
+ * @returns `this` for method chaining
693
+ *
694
+ * @example
695
+ * ```typescript
696
+ * const connectionHandler = (ws, request) => {
697
+ * console.log('Client connected');
698
+ * };
699
+ *
700
+ * // Register
701
+ * wss.on('connection', connectionHandler);
702
+ *
703
+ * // Later, remove
704
+ * wss.off('connection', connectionHandler);
705
+ * ```
706
+ */
707
+ off(event: 'connection', listener: (ws: ForklaunchWebSocket<SV, ServerEventSchema<SV, ES>>, request: IncomingMessage) => void): this;
708
+ off(event: 'error', listener: (error: Error) => void): this;
709
+ off(event: 'headers', listener: (headers: string[], request: IncomingMessage) => void): this;
710
+ off(event: 'close', listener: () => void): this;
711
+ off(event: 'listening', listener: () => void): this;
712
+ off(event: string | symbol, listener: (...args: never[]) => void): this;
713
+ /**
714
+ * Registers an event listener (alias for `on()`).
715
+ *
716
+ * This method is functionally identical to `on()` and is provided for
717
+ * compatibility with the EventEmitter API.
718
+ *
719
+ * @param event - The server event name to listen for
720
+ * @param listener - The callback function to invoke when the event occurs
721
+ * @returns `this` for method chaining
722
+ *
723
+ * @see {@link on} for detailed documentation and examples
724
+ */
725
+ addListener(event: 'connection', listener: (ws: ForklaunchWebSocket<SV, ServerEventSchema<SV, ES>>, request: IncomingMessage) => void): this;
726
+ addListener(event: 'error', listener: (error: Error) => void): this;
727
+ addListener(event: 'headers', listener: (headers: string[], request: IncomingMessage) => void): this;
728
+ addListener(event: 'close', listener: () => void): this;
729
+ addListener(event: 'listening', listener: () => void): this;
730
+ addListener(event: string | symbol, listener: (...args: never[]) => void): this;
731
+ /**
732
+ * Removes a previously registered event listener (alias for `off()`).
733
+ *
734
+ * This method is functionally identical to `off()` and is provided for
735
+ * compatibility with the EventEmitter API.
736
+ *
737
+ * @param event - The server event name
738
+ * @param listener - The exact function reference to remove
739
+ * @returns `this` for method chaining
740
+ *
741
+ * @see {@link off} for detailed documentation and examples
742
+ */
743
+ removeListener(event: 'connection', listener: (ws: ForklaunchWebSocket<SV, ServerEventSchema<SV, ES>>, request: IncomingMessage) => void): this;
744
+ removeListener(event: 'error', listener: (error: Error) => void): this;
745
+ removeListener(event: 'headers', listener: (headers: string[], request: IncomingMessage) => void): this;
746
+ removeListener(event: 'close', listener: () => void): this;
747
+ removeListener(event: 'listening', listener: () => void): this;
748
+ removeListener(event: string | symbol, listener: (...args: never[]) => void): this;
749
+ }
750
+
751
+ /**
752
+ * @packageDocumentation
753
+ *
754
+ * # ForkLaunch WebSocket Library
755
+ *
756
+ * Type-safe WebSocket client and server with automatic schema validation and data transformation.
757
+ *
758
+ * ## Features
759
+ *
760
+ * - **Automatic Validation**: All messages validated against provided schemas
761
+ * - **Auto Transformation**: Buffer ↔ Object conversion handled automatically
762
+ * - **Type Safety**: Full TypeScript support with proper event typing
763
+ * - **Schema Agnostic**: Works with Zod, TypeBox, or any validator
764
+ * - **Drop-in Replacement**: Extends standard `ws` library
765
+ *
766
+ * ## Quick Start
767
+ *
768
+ * ```typescript
769
+ * import { ZodSchemaValidator } from '@forklaunch/validator';
770
+ * import { z } from 'zod';
771
+ * import { ForklaunchWebSocket, ForklaunchWebSocketServer } from '@forklaunch/ws';
772
+ *
773
+ * // Define schemas
774
+ * const validator = new ZodSchemaValidator();
775
+ * const schemas = {
776
+ * clientMessages: {
777
+ * chat: z.object({ type: z.literal('chat'), message: z.string() })
778
+ * },
779
+ * serverMessages: {
780
+ * response: z.object({ type: z.literal('response'), data: z.string() })
781
+ * }
782
+ * };
783
+ *
784
+ * // Server
785
+ * const wss = new ForklaunchWebSocketServer(validator, schemas, { port: 8080 });
786
+ * wss.on('connection', (ws) => {
787
+ * ws.on('message', (data) => {
788
+ * console.log('Validated message:', data);
789
+ * });
790
+ * });
791
+ *
792
+ * // Client
793
+ * const ws = new ForklaunchWebSocket(validator, schemas, 'ws://localhost:8080');
794
+ * ws.on('open', () => {
795
+ * ws.send({ type: 'chat', message: 'Hello!' });
796
+ * });
797
+ * ```
798
+ *
799
+ * @see {@link ForklaunchWebSocket} for client documentation
800
+ * @see {@link ForklaunchWebSocketServer} for server documentation
801
+ * @see {@link EventSchema} for schema structure
802
+ */
803
+
804
+ declare const CLOSED: 3;
805
+ declare const CLOSING: 2;
806
+ declare const CONNECTING: 0;
807
+ declare const OPEN: 1;
808
+
809
+ export { CLOSED, CLOSING, CONNECTING, ForklaunchWebSocket, ForklaunchWebSocketServer, OPEN, ForklaunchWebSocket as default };