elit 3.0.0 → 3.0.2
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/build.d.ts +4 -12
- package/dist/build.d.ts.map +1 -0
- package/dist/chokidar.d.ts +7 -9
- package/dist/chokidar.d.ts.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +17 -4
- package/dist/config.d.ts +29 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/dom.d.ts +7 -14
- package/dist/dom.d.ts.map +1 -0
- package/dist/el.d.ts +19 -191
- package/dist/el.d.ts.map +1 -0
- package/dist/fs.d.ts +35 -35
- package/dist/fs.d.ts.map +1 -0
- package/dist/hmr.d.ts +3 -3
- package/dist/hmr.d.ts.map +1 -0
- package/dist/http.d.ts +20 -22
- package/dist/http.d.ts.map +1 -0
- package/dist/https.d.ts +12 -15
- package/dist/https.d.ts.map +1 -0
- package/dist/index.d.ts +10 -629
- package/dist/index.d.ts.map +1 -0
- package/dist/mime-types.d.ts +9 -9
- package/dist/mime-types.d.ts.map +1 -0
- package/dist/path.d.ts +22 -19
- package/dist/path.d.ts.map +1 -0
- package/dist/router.d.ts +10 -17
- package/dist/router.d.ts.map +1 -0
- package/dist/runtime.d.ts +5 -6
- package/dist/runtime.d.ts.map +1 -0
- package/dist/server.d.ts +105 -7
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +14 -2
- package/dist/server.mjs +14 -2
- package/dist/state.d.ts +21 -27
- package/dist/state.d.ts.map +1 -0
- package/dist/style.d.ts +14 -55
- package/dist/style.d.ts.map +1 -0
- package/dist/types.d.ts +26 -240
- package/dist/types.d.ts.map +1 -0
- package/dist/ws.d.ts +14 -17
- package/dist/ws.d.ts.map +1 -0
- package/dist/wss.d.ts +16 -16
- package/dist/wss.d.ts.map +1 -0
- package/package.json +3 -2
- package/src/build.ts +337 -0
- package/src/chokidar.ts +401 -0
- package/src/cli.ts +638 -0
- package/src/config.ts +205 -0
- package/src/dom.ts +817 -0
- package/src/el.ts +164 -0
- package/src/fs.ts +727 -0
- package/src/hmr.ts +137 -0
- package/src/http.ts +775 -0
- package/src/https.ts +411 -0
- package/src/index.ts +14 -0
- package/src/mime-types.ts +222 -0
- package/src/path.ts +493 -0
- package/src/router.ts +237 -0
- package/src/runtime.ts +97 -0
- package/src/server.ts +1290 -0
- package/src/state.ts +468 -0
- package/src/style.ts +524 -0
- package/{dist/types-Du6kfwTm.d.ts → src/types.ts} +58 -141
- package/src/ws.ts +506 -0
- package/src/wss.ts +241 -0
- package/dist/build.d.mts +0 -20
- package/dist/chokidar.d.mts +0 -134
- package/dist/dom.d.mts +0 -87
- package/dist/el.d.mts +0 -207
- package/dist/fs.d.mts +0 -255
- package/dist/hmr.d.mts +0 -38
- package/dist/http.d.mts +0 -163
- package/dist/https.d.mts +0 -108
- package/dist/index.d.mts +0 -629
- package/dist/mime-types.d.mts +0 -48
- package/dist/path.d.mts +0 -163
- package/dist/router.d.mts +0 -47
- package/dist/runtime.d.mts +0 -97
- package/dist/server.d.mts +0 -7
- package/dist/state.d.mts +0 -111
- package/dist/style.d.mts +0 -159
- package/dist/types-C0nGi6MX.d.mts +0 -346
- package/dist/types.d.mts +0 -452
- package/dist/ws.d.mts +0 -195
- package/dist/wss.d.mts +0 -108
package/src/ws.ts
ADDED
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket module with unified API across runtimes
|
|
3
|
+
* Pure implementation without external dependencies
|
|
4
|
+
* - Node.js: uses native 'ws' module (built-in WebSocket implementation)
|
|
5
|
+
* - Bun: uses native WebSocket
|
|
6
|
+
* - Deno: uses native WebSocket
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { EventEmitter } from 'events';
|
|
10
|
+
import type { IncomingMessage } from './http';
|
|
11
|
+
import { runtime } from './runtime';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* WebSocket ready state
|
|
15
|
+
*/
|
|
16
|
+
export enum ReadyState {
|
|
17
|
+
CONNECTING = 0,
|
|
18
|
+
OPEN = 1,
|
|
19
|
+
CLOSING = 2,
|
|
20
|
+
CLOSED = 3,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* WebSocket close codes
|
|
25
|
+
*/
|
|
26
|
+
export const CLOSE_CODES = {
|
|
27
|
+
NORMAL: 1000,
|
|
28
|
+
GOING_AWAY: 1001,
|
|
29
|
+
PROTOCOL_ERROR: 1002,
|
|
30
|
+
UNSUPPORTED_DATA: 1003,
|
|
31
|
+
NO_STATUS: 1005,
|
|
32
|
+
ABNORMAL: 1006,
|
|
33
|
+
INVALID_DATA: 1007,
|
|
34
|
+
POLICY_VIOLATION: 1008,
|
|
35
|
+
MESSAGE_TOO_BIG: 1009,
|
|
36
|
+
EXTENSION_REQUIRED: 1010,
|
|
37
|
+
INTERNAL_ERROR: 1011,
|
|
38
|
+
SERVICE_RESTART: 1012,
|
|
39
|
+
TRY_AGAIN_LATER: 1013,
|
|
40
|
+
BAD_GATEWAY: 1014,
|
|
41
|
+
TLS_HANDSHAKE_FAIL: 1015,
|
|
42
|
+
} as const;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* WebSocket data types
|
|
46
|
+
*/
|
|
47
|
+
export type Data = string | Buffer | ArrayBuffer | Buffer[];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* WebSocket send options
|
|
51
|
+
*/
|
|
52
|
+
export interface SendOptions {
|
|
53
|
+
binary?: boolean;
|
|
54
|
+
compress?: boolean;
|
|
55
|
+
fin?: boolean;
|
|
56
|
+
mask?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* WebSocket server options
|
|
61
|
+
*/
|
|
62
|
+
export interface ServerOptions {
|
|
63
|
+
host?: string;
|
|
64
|
+
port?: number;
|
|
65
|
+
backlog?: number;
|
|
66
|
+
server?: any;
|
|
67
|
+
verifyClient?: VerifyClientCallback;
|
|
68
|
+
handleProtocols?: (protocols: Set<string>, request: IncomingMessage) => string | false;
|
|
69
|
+
path?: string;
|
|
70
|
+
noServer?: boolean;
|
|
71
|
+
clientTracking?: boolean;
|
|
72
|
+
perMessageDeflate?: boolean | object;
|
|
73
|
+
maxPayload?: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Verify client callback
|
|
78
|
+
*/
|
|
79
|
+
export type VerifyClientCallback = (
|
|
80
|
+
info: {
|
|
81
|
+
origin: string;
|
|
82
|
+
secure: boolean;
|
|
83
|
+
req: IncomingMessage;
|
|
84
|
+
},
|
|
85
|
+
callback?: (result: boolean, code?: number, message?: string) => void
|
|
86
|
+
) => boolean | void;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Helper: Queue callback with optional error (eliminates duplication in callback handling)
|
|
90
|
+
*/
|
|
91
|
+
function queueCallback(callback?: (err?: Error) => void, error?: Error): void {
|
|
92
|
+
if (callback) {
|
|
93
|
+
queueMicrotask(() => callback(error));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Helper: Create native WebSocket instance (eliminates duplication in constructor)
|
|
99
|
+
*/
|
|
100
|
+
function createNativeWebSocket(url: string, protocols?: string[]): any {
|
|
101
|
+
// @ts-ignore - WebSocket is available in Node.js 18+ and all modern runtimes
|
|
102
|
+
if (runtime === 'node' && typeof globalThis.WebSocket === 'undefined') {
|
|
103
|
+
throw new Error('WebSocket is not available. Please use Node.js 18+ or install ws package.');
|
|
104
|
+
}
|
|
105
|
+
// @ts-ignore
|
|
106
|
+
return new globalThis.WebSocket(url, protocols);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* WebSocket class - Pure implementation
|
|
111
|
+
*/
|
|
112
|
+
export class WebSocket extends EventEmitter {
|
|
113
|
+
public readyState: ReadyState = ReadyState.CONNECTING;
|
|
114
|
+
public url: string;
|
|
115
|
+
public protocol: string = '';
|
|
116
|
+
public extensions: string = '';
|
|
117
|
+
public binaryType: 'nodebuffer' | 'arraybuffer' | 'fragments' = 'nodebuffer';
|
|
118
|
+
|
|
119
|
+
/** @internal */
|
|
120
|
+
public _socket: any;
|
|
121
|
+
|
|
122
|
+
constructor(address: string | URL, protocols?: string | string[], _options?: any) {
|
|
123
|
+
super();
|
|
124
|
+
this.url = typeof address === 'string' ? address : address.toString();
|
|
125
|
+
const protocolsArray = Array.isArray(protocols) ? protocols : protocols ? [protocols] : undefined;
|
|
126
|
+
this._socket = createNativeWebSocket(this.url, protocolsArray);
|
|
127
|
+
this._setupNativeSocket();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private _setupNativeSocket(): void {
|
|
131
|
+
this._socket.onopen = () => {
|
|
132
|
+
this.readyState = ReadyState.OPEN;
|
|
133
|
+
this.emit('open');
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
this._socket.onmessage = (event: MessageEvent) => {
|
|
137
|
+
const isBinary = event.data instanceof ArrayBuffer || event.data instanceof Blob;
|
|
138
|
+
this.emit('message', event.data, isBinary);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
this._socket.onclose = (event: CloseEvent) => {
|
|
142
|
+
this.readyState = ReadyState.CLOSED;
|
|
143
|
+
this.emit('close', event.code, event.reason);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
this._socket.onerror = () => {
|
|
147
|
+
this.emit('error', new Error('WebSocket error'));
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Send data through WebSocket
|
|
153
|
+
*/
|
|
154
|
+
send(data: Data, options?: SendOptions | ((err?: Error) => void), callback?: (err?: Error) => void): void {
|
|
155
|
+
const cb = typeof options === 'function' ? options : callback;
|
|
156
|
+
|
|
157
|
+
if (this.readyState !== ReadyState.OPEN) {
|
|
158
|
+
return queueCallback(cb, new Error('WebSocket is not open'));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
this._socket.send(data);
|
|
163
|
+
queueCallback(cb);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
queueCallback(cb, error as Error);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Close the WebSocket connection
|
|
171
|
+
*/
|
|
172
|
+
close(code?: number, reason?: string | Buffer): void {
|
|
173
|
+
if (this.readyState === ReadyState.CLOSED || this.readyState === ReadyState.CLOSING) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.readyState = ReadyState.CLOSING;
|
|
178
|
+
this._socket.close(code, typeof reason === 'string' ? reason : reason?.toString());
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Pause the socket (no-op for native WebSocket)
|
|
183
|
+
*/
|
|
184
|
+
pause(): void {
|
|
185
|
+
// Native WebSocket doesn't support pause
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Resume the socket (no-op for native WebSocket)
|
|
190
|
+
*/
|
|
191
|
+
resume(): void {
|
|
192
|
+
// Native WebSocket doesn't support resume
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Send a ping frame (no-op for native WebSocket)
|
|
197
|
+
*/
|
|
198
|
+
ping(_data?: Data, _mask?: boolean, callback?: (err?: Error) => void): void {
|
|
199
|
+
queueCallback(callback); // Native WebSocket doesn't expose ping
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Send a pong frame (no-op for native WebSocket)
|
|
204
|
+
*/
|
|
205
|
+
pong(_data?: Data, _mask?: boolean, callback?: (err?: Error) => void): void {
|
|
206
|
+
queueCallback(callback); // Native WebSocket doesn't expose pong
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Terminate the connection
|
|
211
|
+
*/
|
|
212
|
+
terminate(): void {
|
|
213
|
+
this._socket.close();
|
|
214
|
+
this.readyState = ReadyState.CLOSED;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Get buffered amount
|
|
219
|
+
*/
|
|
220
|
+
get bufferedAmount(): number {
|
|
221
|
+
return this._socket.bufferedAmount || 0;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* WebSocket Server - Server-side WebSocket implementation
|
|
227
|
+
*/
|
|
228
|
+
export class WebSocketServer extends EventEmitter {
|
|
229
|
+
public clients: Set<WebSocket> = new Set();
|
|
230
|
+
public options: ServerOptions;
|
|
231
|
+
public path: string;
|
|
232
|
+
|
|
233
|
+
private _httpServer: any;
|
|
234
|
+
|
|
235
|
+
constructor(options?: ServerOptions, callback?: () => void) {
|
|
236
|
+
super();
|
|
237
|
+
this.options = options || {};
|
|
238
|
+
this.path = options?.path || '/';
|
|
239
|
+
|
|
240
|
+
if (runtime === 'node') {
|
|
241
|
+
// Node.js - create HTTP server with WebSocket upgrade
|
|
242
|
+
if (options?.server) {
|
|
243
|
+
this._httpServer = options.server;
|
|
244
|
+
this._setupUpgradeHandler();
|
|
245
|
+
} else if (options?.noServer) {
|
|
246
|
+
// No server mode - user will call handleUpgrade manually
|
|
247
|
+
} else {
|
|
248
|
+
// Create new HTTP server
|
|
249
|
+
const http = require('http');
|
|
250
|
+
this._httpServer = http.createServer();
|
|
251
|
+
this._setupUpgradeHandler();
|
|
252
|
+
|
|
253
|
+
if (options?.port) {
|
|
254
|
+
this._httpServer.listen(options.port, options.host, callback);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
// Bun/Deno - WebSocket server setup
|
|
259
|
+
queueCallback(callback as any);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private _setupUpgradeHandler(): void {
|
|
264
|
+
this._httpServer.on('upgrade', (request: any, socket: any, head: Buffer) => {
|
|
265
|
+
console.log('[WebSocket] Upgrade request:', request.url, 'Expected:', this.path);
|
|
266
|
+
if (this.path && this.path !== '/' && request.url !== this.path) {
|
|
267
|
+
console.log('[WebSocket] Path mismatch, ignoring');
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
this.handleUpgrade(request, socket, head, (client) => {
|
|
272
|
+
console.log('[WebSocket] Client connected');
|
|
273
|
+
this.emit('connection', client, request);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Handle HTTP upgrade for WebSocket
|
|
280
|
+
*/
|
|
281
|
+
handleUpgrade(request: IncomingMessage, socket: any, _head: Buffer, callback: (client: WebSocket) => void): void {
|
|
282
|
+
// Simple WebSocket handshake
|
|
283
|
+
const key = request.headers['sec-websocket-key'];
|
|
284
|
+
if (!key) {
|
|
285
|
+
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Generate accept key
|
|
290
|
+
const crypto = require('crypto');
|
|
291
|
+
const acceptKey = crypto
|
|
292
|
+
.createHash('sha1')
|
|
293
|
+
.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
|
|
294
|
+
.digest('base64');
|
|
295
|
+
|
|
296
|
+
// Send handshake response
|
|
297
|
+
const headers = [
|
|
298
|
+
'HTTP/1.1 101 Switching Protocols',
|
|
299
|
+
'Upgrade: websocket',
|
|
300
|
+
'Connection: Upgrade',
|
|
301
|
+
`Sec-WebSocket-Accept: ${acceptKey}`,
|
|
302
|
+
'',
|
|
303
|
+
''
|
|
304
|
+
];
|
|
305
|
+
|
|
306
|
+
socket.write(headers.join('\r\n'));
|
|
307
|
+
|
|
308
|
+
// Create WebSocket client from raw socket
|
|
309
|
+
const client = this._createClientFromSocket(socket);
|
|
310
|
+
|
|
311
|
+
if (this.options.clientTracking !== false) {
|
|
312
|
+
this.clients.add(client);
|
|
313
|
+
client.on('close', () => {
|
|
314
|
+
this.clients.delete(client);
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
callback(client);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private _createClientFromSocket(socket: any): WebSocket {
|
|
322
|
+
const client = Object.create(WebSocket.prototype);
|
|
323
|
+
EventEmitter.call(client);
|
|
324
|
+
|
|
325
|
+
client.readyState = ReadyState.OPEN;
|
|
326
|
+
client.url = 'ws://localhost';
|
|
327
|
+
client.protocol = '';
|
|
328
|
+
client.extensions = '';
|
|
329
|
+
client.binaryType = 'nodebuffer';
|
|
330
|
+
client._socket = socket;
|
|
331
|
+
|
|
332
|
+
// Handle incoming frames
|
|
333
|
+
socket.on('data', (data: Buffer) => {
|
|
334
|
+
// Simple frame parsing (minimal implementation)
|
|
335
|
+
try {
|
|
336
|
+
const message = this._parseFrame(data);
|
|
337
|
+
if (message) {
|
|
338
|
+
client.emit('message', message, false);
|
|
339
|
+
}
|
|
340
|
+
} catch (error) {
|
|
341
|
+
client.emit('error', error);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
socket.on('end', () => {
|
|
346
|
+
client.readyState = ReadyState.CLOSED;
|
|
347
|
+
client.emit('close', CLOSE_CODES.NORMAL, '');
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
socket.on('error', (error: Error) => {
|
|
351
|
+
client.emit('error', error);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// Override send method
|
|
355
|
+
client.send = (data: Data, _options?: any, callback?: (err?: Error) => void) => {
|
|
356
|
+
try {
|
|
357
|
+
const frame = this._createFrame(data);
|
|
358
|
+
socket.write(frame);
|
|
359
|
+
queueCallback(callback);
|
|
360
|
+
} catch (error) {
|
|
361
|
+
queueCallback(callback, error as Error);
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// Override close method
|
|
366
|
+
client.close = (_code?: number, _reason?: string) => {
|
|
367
|
+
socket.end();
|
|
368
|
+
client.readyState = ReadyState.CLOSED;
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
return client;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
private _parseFrame(data: Buffer): string | null {
|
|
375
|
+
// Minimal WebSocket frame parsing
|
|
376
|
+
if (data.length < 2) return null;
|
|
377
|
+
|
|
378
|
+
const firstByte = data[0];
|
|
379
|
+
const secondByte = data[1];
|
|
380
|
+
|
|
381
|
+
const opcode = firstByte & 0x0f;
|
|
382
|
+
const isMasked = (secondByte & 0x80) === 0x80;
|
|
383
|
+
let payloadLength = secondByte & 0x7f;
|
|
384
|
+
let offset = 2;
|
|
385
|
+
|
|
386
|
+
if (payloadLength === 126) {
|
|
387
|
+
payloadLength = data.readUInt16BE(2);
|
|
388
|
+
offset = 4;
|
|
389
|
+
} else if (payloadLength === 127) {
|
|
390
|
+
payloadLength = Number(data.readBigUInt64BE(2));
|
|
391
|
+
offset = 10;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
let payload = data.subarray(offset);
|
|
395
|
+
|
|
396
|
+
if (isMasked) {
|
|
397
|
+
const maskKey = data.subarray(offset, offset + 4);
|
|
398
|
+
payload = data.subarray(offset + 4, offset + 4 + payloadLength);
|
|
399
|
+
|
|
400
|
+
// Unmask payload
|
|
401
|
+
for (let i = 0; i < payload.length; i++) {
|
|
402
|
+
payload[i] ^= maskKey[i % 4];
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Text frame (opcode 1)
|
|
407
|
+
if (opcode === 1) {
|
|
408
|
+
return payload.toString('utf8');
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
private _createFrame(data: Data): Buffer {
|
|
415
|
+
// Create simple text frame (opcode 1, no masking)
|
|
416
|
+
const payload = typeof data === 'string' ? Buffer.from(data) : data;
|
|
417
|
+
const payloadLength = Buffer.isBuffer(payload) ? payload.length : 0;
|
|
418
|
+
|
|
419
|
+
let frame: Buffer;
|
|
420
|
+
let offset = 2;
|
|
421
|
+
|
|
422
|
+
if (payloadLength < 126) {
|
|
423
|
+
frame = Buffer.allocUnsafe(2 + payloadLength);
|
|
424
|
+
frame[1] = payloadLength;
|
|
425
|
+
} else if (payloadLength < 65536) {
|
|
426
|
+
frame = Buffer.allocUnsafe(4 + payloadLength);
|
|
427
|
+
frame[1] = 126;
|
|
428
|
+
frame.writeUInt16BE(payloadLength, 2);
|
|
429
|
+
offset = 4;
|
|
430
|
+
} else {
|
|
431
|
+
frame = Buffer.allocUnsafe(10 + payloadLength);
|
|
432
|
+
frame[1] = 127;
|
|
433
|
+
frame.writeBigUInt64BE(BigInt(payloadLength), 2);
|
|
434
|
+
offset = 10;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
frame[0] = 0x81; // FIN + text frame
|
|
438
|
+
|
|
439
|
+
if (Buffer.isBuffer(payload)) {
|
|
440
|
+
payload.copy(frame, offset);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return frame;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Close the server
|
|
448
|
+
*/
|
|
449
|
+
close(callback?: (err?: Error) => void): void {
|
|
450
|
+
this.clients.forEach(client => client.close());
|
|
451
|
+
this.clients.clear();
|
|
452
|
+
|
|
453
|
+
if (this._httpServer) {
|
|
454
|
+
this._httpServer.close(callback);
|
|
455
|
+
} else {
|
|
456
|
+
this.emit('close');
|
|
457
|
+
queueCallback(callback);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Check if server should handle request
|
|
463
|
+
*/
|
|
464
|
+
shouldHandle(request: IncomingMessage): boolean {
|
|
465
|
+
if (this.path && request.url !== this.path) {
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
return true;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Get server address
|
|
473
|
+
*/
|
|
474
|
+
address(): { port: number; family: string; address: string } | null {
|
|
475
|
+
if (this._httpServer && this._httpServer.address) {
|
|
476
|
+
return this._httpServer.address();
|
|
477
|
+
}
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Create WebSocket server
|
|
484
|
+
*/
|
|
485
|
+
export function createWebSocketServer(options?: ServerOptions, callback?: () => void): WebSocketServer {
|
|
486
|
+
return new WebSocketServer(options, callback);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Get current runtime
|
|
491
|
+
*/
|
|
492
|
+
export function getRuntime(): 'node' | 'bun' | 'deno' {
|
|
493
|
+
return runtime;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Default export
|
|
498
|
+
*/
|
|
499
|
+
export default {
|
|
500
|
+
WebSocket,
|
|
501
|
+
WebSocketServer,
|
|
502
|
+
createWebSocketServer,
|
|
503
|
+
ReadyState,
|
|
504
|
+
CLOSE_CODES,
|
|
505
|
+
getRuntime,
|
|
506
|
+
};
|