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.
Files changed (87) hide show
  1. package/dist/build.d.ts +4 -12
  2. package/dist/build.d.ts.map +1 -0
  3. package/dist/chokidar.d.ts +7 -9
  4. package/dist/chokidar.d.ts.map +1 -0
  5. package/dist/cli.d.ts +6 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +17 -4
  8. package/dist/config.d.ts +29 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/dom.d.ts +7 -14
  11. package/dist/dom.d.ts.map +1 -0
  12. package/dist/el.d.ts +19 -191
  13. package/dist/el.d.ts.map +1 -0
  14. package/dist/fs.d.ts +35 -35
  15. package/dist/fs.d.ts.map +1 -0
  16. package/dist/hmr.d.ts +3 -3
  17. package/dist/hmr.d.ts.map +1 -0
  18. package/dist/http.d.ts +20 -22
  19. package/dist/http.d.ts.map +1 -0
  20. package/dist/https.d.ts +12 -15
  21. package/dist/https.d.ts.map +1 -0
  22. package/dist/index.d.ts +10 -629
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/mime-types.d.ts +9 -9
  25. package/dist/mime-types.d.ts.map +1 -0
  26. package/dist/path.d.ts +22 -19
  27. package/dist/path.d.ts.map +1 -0
  28. package/dist/router.d.ts +10 -17
  29. package/dist/router.d.ts.map +1 -0
  30. package/dist/runtime.d.ts +5 -6
  31. package/dist/runtime.d.ts.map +1 -0
  32. package/dist/server.d.ts +105 -7
  33. package/dist/server.d.ts.map +1 -0
  34. package/dist/server.js +14 -2
  35. package/dist/server.mjs +14 -2
  36. package/dist/state.d.ts +21 -27
  37. package/dist/state.d.ts.map +1 -0
  38. package/dist/style.d.ts +14 -55
  39. package/dist/style.d.ts.map +1 -0
  40. package/dist/types.d.ts +26 -240
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/ws.d.ts +14 -17
  43. package/dist/ws.d.ts.map +1 -0
  44. package/dist/wss.d.ts +16 -16
  45. package/dist/wss.d.ts.map +1 -0
  46. package/package.json +3 -2
  47. package/src/build.ts +337 -0
  48. package/src/chokidar.ts +401 -0
  49. package/src/cli.ts +638 -0
  50. package/src/config.ts +205 -0
  51. package/src/dom.ts +817 -0
  52. package/src/el.ts +164 -0
  53. package/src/fs.ts +727 -0
  54. package/src/hmr.ts +137 -0
  55. package/src/http.ts +775 -0
  56. package/src/https.ts +411 -0
  57. package/src/index.ts +14 -0
  58. package/src/mime-types.ts +222 -0
  59. package/src/path.ts +493 -0
  60. package/src/router.ts +237 -0
  61. package/src/runtime.ts +97 -0
  62. package/src/server.ts +1290 -0
  63. package/src/state.ts +468 -0
  64. package/src/style.ts +524 -0
  65. package/{dist/types-Du6kfwTm.d.ts → src/types.ts} +58 -141
  66. package/src/ws.ts +506 -0
  67. package/src/wss.ts +241 -0
  68. package/dist/build.d.mts +0 -20
  69. package/dist/chokidar.d.mts +0 -134
  70. package/dist/dom.d.mts +0 -87
  71. package/dist/el.d.mts +0 -207
  72. package/dist/fs.d.mts +0 -255
  73. package/dist/hmr.d.mts +0 -38
  74. package/dist/http.d.mts +0 -163
  75. package/dist/https.d.mts +0 -108
  76. package/dist/index.d.mts +0 -629
  77. package/dist/mime-types.d.mts +0 -48
  78. package/dist/path.d.mts +0 -163
  79. package/dist/router.d.mts +0 -47
  80. package/dist/runtime.d.mts +0 -97
  81. package/dist/server.d.mts +0 -7
  82. package/dist/state.d.mts +0 -111
  83. package/dist/style.d.mts +0 -159
  84. package/dist/types-C0nGi6MX.d.mts +0 -346
  85. package/dist/types.d.mts +0 -452
  86. package/dist/ws.d.mts +0 -195
  87. 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
+ };