@coderbuzz/ken 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.
@@ -0,0 +1,816 @@
1
+ import {
2
+ BaseContext
3
+ } from "./chunk-2MK26YDD.js";
4
+ import {
5
+ EMPTY_PARAMS,
6
+ PubSubHub,
7
+ Router,
8
+ WS_DEFAULTS,
9
+ WsReadyState,
10
+ createExecutor,
11
+ createNotFoundExecutor,
12
+ toResponse
13
+ } from "./chunk-DPU3PBLP.js";
14
+
15
+ // src/runtime/node.ts
16
+ import { createServer } from "http";
17
+
18
+ // src/context/node.ts
19
+ var NodeContext = class extends BaseContext {
20
+ req;
21
+ _url;
22
+ _bodyBuffer = null;
23
+ constructor(req, url, params = EMPTY_PARAMS, getRemoteInfo, schema) {
24
+ super(params, getRemoteInfo, schema);
25
+ this.req = req;
26
+ this._url = url;
27
+ }
28
+ get url() {
29
+ return this._url;
30
+ }
31
+ get method() {
32
+ return this.req.method || "GET";
33
+ }
34
+ get body() {
35
+ return this.req;
36
+ }
37
+ _getRawHeaders() {
38
+ return this.req.headers;
39
+ }
40
+ _getRawUrl() {
41
+ return this._url;
42
+ }
43
+ /**
44
+ * Lazily parses and validates headers.
45
+ */
46
+ get headers() {
47
+ let cached = this._headers;
48
+ if (cached !== null) return cached;
49
+ const schema = this._schema?.headers;
50
+ const nodeHeaders = this.req.headers;
51
+ if (schema !== void 0) {
52
+ const result = {};
53
+ for (const key in schema) {
54
+ const validator = schema[key];
55
+ const value = nodeHeaders[key.toLowerCase()];
56
+ result[key] = validator(Array.isArray(value) ? value[0] : value || "");
57
+ }
58
+ this._headers = result;
59
+ return result;
60
+ }
61
+ const h = {};
62
+ for (const key in nodeHeaders) {
63
+ const value = nodeHeaders[key];
64
+ h[key] = Array.isArray(value) ? value[0] : value || "";
65
+ }
66
+ this._headers = h;
67
+ return h;
68
+ }
69
+ /**
70
+ * Read body buffer from IncomingMessage stream
71
+ */
72
+ async _readBody() {
73
+ if (this._bodyBuffer !== null) return this._bodyBuffer;
74
+ return new Promise((resolve, reject) => {
75
+ const chunks = [];
76
+ this.req.on("data", (chunk) => chunks.push(chunk));
77
+ this.req.on("end", () => {
78
+ this._bodyBuffer = Buffer.concat(chunks);
79
+ resolve(this._bodyBuffer);
80
+ });
81
+ this.req.on("error", reject);
82
+ });
83
+ }
84
+ /**
85
+ * Lazily parses and validates JSON body.
86
+ */
87
+ get json() {
88
+ if (this._json) return this._json;
89
+ const validator = this._schema?.json;
90
+ if (validator) {
91
+ this._json = (async () => {
92
+ const buffer = await this._readBody();
93
+ const data = JSON.parse(buffer.toString("utf8"));
94
+ try {
95
+ return validator(data);
96
+ } catch (err) {
97
+ throw new Error(`JSON Body validation failed: ${err.message}`);
98
+ }
99
+ })();
100
+ } else {
101
+ this._json = (async () => {
102
+ try {
103
+ const buffer = await this._readBody();
104
+ return JSON.parse(buffer.toString("utf8"));
105
+ } catch {
106
+ return null;
107
+ }
108
+ })();
109
+ }
110
+ return this._json;
111
+ }
112
+ /**
113
+ * Lazily parses and validates text body.
114
+ */
115
+ get text() {
116
+ if (this._text) return this._text;
117
+ const validator = this._schema?.text;
118
+ if (validator) {
119
+ this._text = (async () => {
120
+ const buffer = await this._readBody();
121
+ const data = buffer.toString("utf8");
122
+ try {
123
+ return validator(data);
124
+ } catch (err) {
125
+ throw new Error(`Text Body validation failed: ${err.message}`);
126
+ }
127
+ })();
128
+ } else {
129
+ this._text = (async () => {
130
+ const buffer = await this._readBody();
131
+ return buffer.toString("utf8");
132
+ })();
133
+ }
134
+ return this._text;
135
+ }
136
+ /**
137
+ * Lazily parses and validates form body.
138
+ * Supports both application/x-www-form-urlencoded and multipart/form-data
139
+ */
140
+ get form() {
141
+ if (this._form) return this._form;
142
+ const schema = this._schema?.form;
143
+ this._form = (async () => {
144
+ const buffer = await this._readBody();
145
+ const contentType = this.req.headers["content-type"] || "";
146
+ if (contentType.startsWith("multipart/form-data")) {
147
+ const protocol = this.req.socket.encrypted ? "https:" : "http:";
148
+ const host = this.req.headers.host || "localhost";
149
+ const fullUrl = `${protocol}//${host}${this._url}`;
150
+ const headers = new Headers();
151
+ for (const key in this.req.headers) {
152
+ const value = this.req.headers[key];
153
+ if (value) {
154
+ headers.set(key, Array.isArray(value) ? value[0] : value);
155
+ }
156
+ }
157
+ const request = new Request(fullUrl, {
158
+ method: this.method,
159
+ headers,
160
+ body: new Uint8Array(buffer),
161
+ // @ts-ignore - duplex is needed for streaming
162
+ duplex: "half"
163
+ });
164
+ const formData2 = await request.formData();
165
+ if (schema) {
166
+ const result = {};
167
+ for (const key in schema) {
168
+ try {
169
+ result[key] = schema[key](formData2.get(key));
170
+ } catch (err) {
171
+ throw new Error(`Form Body validation failed for "${key}": ${err.message}`);
172
+ }
173
+ }
174
+ return result;
175
+ }
176
+ return formData2;
177
+ }
178
+ const text = buffer.toString("utf8");
179
+ const formData = /* @__PURE__ */ new Map();
180
+ const pairs = text.split("&");
181
+ for (const pair of pairs) {
182
+ const [key, value] = pair.split("=");
183
+ if (key) {
184
+ formData.set(
185
+ decodeURIComponent(key),
186
+ value ? decodeURIComponent(value.replace(/\+/g, " ")) : ""
187
+ );
188
+ }
189
+ }
190
+ if (schema) {
191
+ const result = {};
192
+ for (const key in schema) {
193
+ try {
194
+ result[key] = schema[key](formData.get(key) ?? null);
195
+ } catch (err) {
196
+ throw new Error(`Form Body validation failed for "${key}": ${err.message}`);
197
+ }
198
+ }
199
+ return result;
200
+ }
201
+ return formData;
202
+ })();
203
+ return this._form;
204
+ }
205
+ };
206
+
207
+ // src/ws/node.ts
208
+ import { createHash } from "crypto";
209
+ var WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
210
+ var OPCODE_CONTINUATION = 0;
211
+ var OPCODE_TEXT = 1;
212
+ var OPCODE_BINARY = 2;
213
+ var OPCODE_CLOSE = 8;
214
+ var OPCODE_PING = 9;
215
+ var OPCODE_PONG = 10;
216
+ var FIN_BIT = 128;
217
+ var MASK_BIT = 128;
218
+ var OPCODE_MASK = 15;
219
+ var LENGTH_MASK = 127;
220
+ function unmaskPayload(payload, mask) {
221
+ const len = payload.length;
222
+ for (let i = 0; i < len; i++) {
223
+ payload[i] ^= mask[i & 3];
224
+ }
225
+ }
226
+ function createFrame(opcode, payload, fin = true) {
227
+ const payloadLen = payload.length;
228
+ let headerLen;
229
+ let extendedOffset;
230
+ if (payloadLen < 126) {
231
+ headerLen = 2;
232
+ extendedOffset = -1;
233
+ } else if (payloadLen < 65536) {
234
+ headerLen = 4;
235
+ extendedOffset = 2;
236
+ } else {
237
+ headerLen = 10;
238
+ extendedOffset = 2;
239
+ }
240
+ const frame = Buffer.allocUnsafe(headerLen + payloadLen);
241
+ frame[0] = (fin ? FIN_BIT : 0) | opcode;
242
+ if (payloadLen < 126) {
243
+ frame[1] = payloadLen;
244
+ } else if (payloadLen < 65536) {
245
+ frame[1] = 126;
246
+ frame.writeUInt16BE(payloadLen, extendedOffset);
247
+ } else {
248
+ frame[1] = 127;
249
+ frame.writeUInt32BE(0, extendedOffset);
250
+ frame.writeUInt32BE(payloadLen, extendedOffset + 4);
251
+ }
252
+ if (payloadLen > 0) {
253
+ if (Buffer.isBuffer(payload)) {
254
+ payload.copy(frame, headerLen);
255
+ } else {
256
+ frame.set(payload, headerLen);
257
+ }
258
+ }
259
+ return frame;
260
+ }
261
+ function toBuffer(data) {
262
+ if (Buffer.isBuffer(data)) return data;
263
+ if (typeof data === "string") return Buffer.from(data);
264
+ if (data instanceof ArrayBuffer) return Buffer.from(data);
265
+ if (data instanceof Uint8Array) return Buffer.from(data.buffer, data.byteOffset, data.byteLength);
266
+ return Buffer.from(String(data));
267
+ }
268
+ var NodeWsPeer = class {
269
+ data;
270
+ readyState = WsReadyState.OPEN;
271
+ remoteAddress;
272
+ socket;
273
+ hub;
274
+ handler;
275
+ opts;
276
+ // Frame parsing state
277
+ frameBuffer = null;
278
+ fragments = [];
279
+ fragmentOpcode = 0;
280
+ // Ping/pong tracking
281
+ lastPongReceived = Date.now();
282
+ constructor(socket, data, hub, handler, opts) {
283
+ this.socket = socket;
284
+ this.data = data;
285
+ this.hub = hub;
286
+ this.handler = handler;
287
+ this.opts = opts;
288
+ this.remoteAddress = socket.remoteAddress || "0.0.0.0";
289
+ this.setupSocket();
290
+ }
291
+ setupSocket() {
292
+ const socket = this.socket;
293
+ socket.on("data", (chunk) => {
294
+ this.onData(chunk);
295
+ });
296
+ socket.on("close", () => {
297
+ if (this.readyState !== WsReadyState.CLOSED) {
298
+ this.readyState = WsReadyState.CLOSED;
299
+ this.hub.removeAll(this);
300
+ try {
301
+ this.handler.close?.(this, 1006, "Connection closed abnormally");
302
+ } catch {
303
+ }
304
+ }
305
+ });
306
+ socket.on("error", (err) => {
307
+ try {
308
+ this.handler.error?.(this, err);
309
+ } catch {
310
+ }
311
+ });
312
+ try {
313
+ this.handler.open?.(this);
314
+ } catch {
315
+ }
316
+ }
317
+ onData(chunk) {
318
+ let buffer;
319
+ if (this.frameBuffer) {
320
+ buffer = Buffer.concat([this.frameBuffer, chunk]);
321
+ this.frameBuffer = null;
322
+ } else {
323
+ buffer = chunk;
324
+ }
325
+ let offset = 0;
326
+ while (offset < buffer.length) {
327
+ if (buffer.length - offset < 2) {
328
+ this.frameBuffer = buffer.subarray(offset);
329
+ return;
330
+ }
331
+ const firstByte = buffer[offset];
332
+ const secondByte = buffer[offset + 1];
333
+ const isFin = (firstByte & FIN_BIT) !== 0;
334
+ const opcode = firstByte & OPCODE_MASK;
335
+ const isMasked = (secondByte & MASK_BIT) !== 0;
336
+ let payloadLen = secondByte & LENGTH_MASK;
337
+ let headerSize = 2;
338
+ if (payloadLen === 126) headerSize += 2;
339
+ else if (payloadLen === 127) headerSize += 8;
340
+ if (isMasked) headerSize += 4;
341
+ if (buffer.length - offset < headerSize) {
342
+ this.frameBuffer = buffer.subarray(offset);
343
+ return;
344
+ }
345
+ let extOffset = offset + 2;
346
+ if (payloadLen === 126) {
347
+ payloadLen = buffer.readUInt16BE(extOffset);
348
+ extOffset += 2;
349
+ } else if (payloadLen === 127) {
350
+ const high = buffer.readUInt32BE(extOffset);
351
+ if (high > 0) {
352
+ this.closeInternal(1009, "Message too large");
353
+ return;
354
+ }
355
+ payloadLen = buffer.readUInt32BE(extOffset + 4);
356
+ extOffset += 8;
357
+ }
358
+ if (payloadLen > this.opts.maxPayloadLength) {
359
+ this.closeInternal(1009, "Message too large");
360
+ return;
361
+ }
362
+ let mask = null;
363
+ if (isMasked) {
364
+ mask = buffer.subarray(extOffset, extOffset + 4);
365
+ extOffset += 4;
366
+ }
367
+ const totalFrameSize = extOffset - offset + payloadLen;
368
+ if (buffer.length - offset < totalFrameSize) {
369
+ this.frameBuffer = buffer.subarray(offset);
370
+ return;
371
+ }
372
+ const payload = Buffer.from(buffer.subarray(extOffset, extOffset + payloadLen));
373
+ if (mask) {
374
+ unmaskPayload(payload, mask);
375
+ }
376
+ offset += totalFrameSize;
377
+ this.processFrame(opcode, payload, isFin);
378
+ }
379
+ }
380
+ processFrame(opcode, payload, isFin) {
381
+ if (opcode >= 8) {
382
+ switch (opcode) {
383
+ case OPCODE_CLOSE:
384
+ this.handleClose(payload);
385
+ break;
386
+ case OPCODE_PING:
387
+ this.handlePing(payload);
388
+ break;
389
+ case OPCODE_PONG:
390
+ this.handlePong(payload);
391
+ break;
392
+ }
393
+ return;
394
+ }
395
+ if (opcode === OPCODE_CONTINUATION) {
396
+ this.fragments.push(payload);
397
+ if (isFin) {
398
+ const fullPayload = Buffer.concat(this.fragments);
399
+ this.fragments = [];
400
+ this.dispatchMessage(this.fragmentOpcode, fullPayload);
401
+ }
402
+ } else {
403
+ if (isFin) {
404
+ this.dispatchMessage(opcode, payload);
405
+ } else {
406
+ this.fragmentOpcode = opcode;
407
+ this.fragments = [payload];
408
+ }
409
+ }
410
+ }
411
+ dispatchMessage(opcode, payload) {
412
+ try {
413
+ const message = opcode === OPCODE_TEXT ? payload.toString("utf8") : payload;
414
+ this.handler.message(this, message);
415
+ } catch {
416
+ }
417
+ }
418
+ handleClose(payload) {
419
+ let code = 1e3;
420
+ let reason = "";
421
+ if (payload.length >= 2) {
422
+ code = payload.readUInt16BE(0);
423
+ if (payload.length > 2) {
424
+ reason = payload.subarray(2).toString("utf8");
425
+ }
426
+ }
427
+ if (this.readyState === WsReadyState.OPEN) {
428
+ this.readyState = WsReadyState.CLOSING;
429
+ const closePayload = Buffer.allocUnsafe(2);
430
+ closePayload.writeUInt16BE(code, 0);
431
+ this.sendFrame(OPCODE_CLOSE, closePayload);
432
+ }
433
+ this.readyState = WsReadyState.CLOSED;
434
+ this.hub.removeAll(this);
435
+ try {
436
+ this.handler.close?.(this, code, reason);
437
+ } catch {
438
+ }
439
+ this.socket.end();
440
+ }
441
+ handlePing(payload) {
442
+ this.sendFrame(OPCODE_PONG, payload);
443
+ try {
444
+ this.handler.ping?.(this, payload);
445
+ } catch {
446
+ }
447
+ }
448
+ handlePong(payload) {
449
+ this.lastPongReceived = Date.now();
450
+ this.hub.markAlive(this);
451
+ try {
452
+ this.handler.pong?.(this, payload);
453
+ } catch {
454
+ }
455
+ }
456
+ // ==================== Public API ====================
457
+ send(data, _compress) {
458
+ if (this.readyState !== WsReadyState.OPEN) return -1;
459
+ const buf = toBuffer(data);
460
+ const opcode = typeof data === "string" ? OPCODE_TEXT : OPCODE_BINARY;
461
+ return this.sendFrame(opcode, buf);
462
+ }
463
+ close(code = 1e3, reason) {
464
+ if (this.readyState !== WsReadyState.OPEN) return;
465
+ this.readyState = WsReadyState.CLOSING;
466
+ const reasonBuf = reason ? Buffer.from(reason) : Buffer.alloc(0);
467
+ const payload = Buffer.allocUnsafe(2 + reasonBuf.length);
468
+ payload.writeUInt16BE(code, 0);
469
+ if (reasonBuf.length > 0) reasonBuf.copy(payload, 2);
470
+ this.sendFrame(OPCODE_CLOSE, payload);
471
+ this.hub.removeAll(this);
472
+ try {
473
+ this.handler.close?.(this, code, reason || "");
474
+ } catch {
475
+ }
476
+ setTimeout(() => {
477
+ if (this.readyState !== WsReadyState.CLOSED) {
478
+ this.readyState = WsReadyState.CLOSED;
479
+ this.socket.end();
480
+ }
481
+ }, 1e3);
482
+ }
483
+ subscribe(topic) {
484
+ this.hub.subscribe(this, topic);
485
+ }
486
+ unsubscribe(topic) {
487
+ this.hub.unsubscribe(this, topic);
488
+ }
489
+ publish(topic, data, compress) {
490
+ this.hub.publish(this, topic, data, compress);
491
+ }
492
+ isSubscribed(topic) {
493
+ return this.hub.isSubscribed(this, topic);
494
+ }
495
+ ping(data) {
496
+ if (this.readyState !== WsReadyState.OPEN) return;
497
+ const buf = data ? toBuffer(data) : Buffer.alloc(0);
498
+ this.sendFrame(OPCODE_PING, buf);
499
+ }
500
+ pong(data) {
501
+ if (this.readyState !== WsReadyState.OPEN) return;
502
+ const buf = data ? toBuffer(data) : Buffer.alloc(0);
503
+ this.sendFrame(OPCODE_PONG, buf);
504
+ }
505
+ /** Check if pong was received within timeout */
506
+ isAlive(pongTimeout) {
507
+ return Date.now() - this.lastPongReceived < pongTimeout * 1e3;
508
+ }
509
+ // ==================== Internal ====================
510
+ sendFrame(opcode, payload) {
511
+ if (this.socket.destroyed) return -1;
512
+ const frame = createFrame(opcode, payload);
513
+ this.socket.write(frame);
514
+ return frame.length;
515
+ }
516
+ closeInternal(code, reason) {
517
+ if (this.readyState === WsReadyState.OPEN) {
518
+ this.close(code, reason);
519
+ }
520
+ }
521
+ /** Force-destroy the underlying socket */
522
+ destroy() {
523
+ this.readyState = WsReadyState.CLOSED;
524
+ this.hub.removeAll(this);
525
+ this.socket.destroy();
526
+ }
527
+ };
528
+ function computeAcceptKey(key) {
529
+ return createHash("sha1").update(key + WS_GUID).digest("base64");
530
+ }
531
+ function createNodeWsHandler(handler, options = {}) {
532
+ const opts = { ...WS_DEFAULTS, ...options };
533
+ const hub = new PubSubHub();
534
+ const peers = /* @__PURE__ */ new Set();
535
+ let heartbeatInterval = null;
536
+ async function handleUpgrade(req, socket, head) {
537
+ const key = req.headers["sec-websocket-key"];
538
+ const version = req.headers["sec-websocket-version"];
539
+ if (!key || version !== "13") {
540
+ socket.write("HTTP/1.1 400 Bad Request\r\n\r\n");
541
+ socket.destroy();
542
+ return;
543
+ }
544
+ let data = void 0;
545
+ if (handler.upgrade) {
546
+ try {
547
+ const url = `http://${req.headers.host || "localhost"}${req.url || "/"}`;
548
+ const headers = new Headers();
549
+ for (const [k, v] of Object.entries(req.headers)) {
550
+ if (v) headers.set(k, Array.isArray(v) ? v.join(", ") : v);
551
+ }
552
+ const request = new Request(url, {
553
+ method: req.method || "GET",
554
+ headers
555
+ });
556
+ const result = await handler.upgrade(request);
557
+ if (result instanceof Response) {
558
+ const status = result.status;
559
+ const body = await result.text();
560
+ socket.write(`HTTP/1.1 ${status} ${result.statusText || "Error"}\r
561
+ `);
562
+ result.headers.forEach((v, k) => {
563
+ socket.write(`${k}: ${v}\r
564
+ `);
565
+ });
566
+ socket.write(`Content-Length: ${Buffer.byteLength(body)}\r
567
+ `);
568
+ socket.write("\r\n");
569
+ socket.write(body);
570
+ socket.destroy();
571
+ return;
572
+ }
573
+ data = result;
574
+ } catch (err) {
575
+ socket.write("HTTP/1.1 500 Internal Server Error\r\n\r\n");
576
+ socket.destroy();
577
+ return;
578
+ }
579
+ }
580
+ const acceptKey = computeAcceptKey(key);
581
+ const upgradeResponse = `HTTP/1.1 101 Switching Protocols\r
582
+ Upgrade: websocket\r
583
+ Connection: Upgrade\r
584
+ Sec-WebSocket-Accept: ${acceptKey}\r
585
+ \r
586
+ `;
587
+ socket.write(upgradeResponse);
588
+ const peer = new NodeWsPeer(socket, data, hub, handler, opts);
589
+ peers.add(peer);
590
+ socket.on("close", () => {
591
+ peers.delete(peer);
592
+ });
593
+ if (head && head.length > 0) {
594
+ peer.onData(head);
595
+ }
596
+ }
597
+ function startHeartbeat() {
598
+ if (opts.pingInterval <= 0) return;
599
+ if (heartbeatInterval) return;
600
+ heartbeatInterval = setInterval(() => {
601
+ for (const peer of peers) {
602
+ if (peer.readyState !== WsReadyState.OPEN) continue;
603
+ if (opts.pongTimeout > 0 && !peer.isAlive(opts.pingInterval + opts.pongTimeout)) {
604
+ peer.destroy();
605
+ peers.delete(peer);
606
+ continue;
607
+ }
608
+ peer.ping();
609
+ }
610
+ }, opts.pingInterval * 1e3);
611
+ if (heartbeatInterval && typeof heartbeatInterval === "object" && "unref" in heartbeatInterval) {
612
+ heartbeatInterval.unref();
613
+ }
614
+ }
615
+ function stopHeartbeat() {
616
+ if (heartbeatInterval) {
617
+ clearInterval(heartbeatInterval);
618
+ heartbeatInterval = null;
619
+ }
620
+ }
621
+ return { hub, peers, handleUpgrade, startHeartbeat, stopHeartbeat };
622
+ }
623
+
624
+ // src/runtime/node.ts
625
+ async function sendResponse(nodeRes, response) {
626
+ nodeRes.statusCode = response.status;
627
+ const setCookies = response.headers.getSetCookie();
628
+ if (setCookies.length > 0) {
629
+ nodeRes.setHeader("Set-Cookie", setCookies);
630
+ }
631
+ response.headers.forEach((value, key) => {
632
+ if (key.toLowerCase() !== "set-cookie") {
633
+ nodeRes.setHeader(key, value);
634
+ }
635
+ });
636
+ const body = response.body;
637
+ if (!body) {
638
+ nodeRes.end();
639
+ return;
640
+ }
641
+ const reader = body.getReader();
642
+ try {
643
+ while (true) {
644
+ const { done, value } = await reader.read();
645
+ if (done) break;
646
+ if (!nodeRes.write(value)) {
647
+ await new Promise((resolve) => nodeRes.once("drain", resolve));
648
+ }
649
+ }
650
+ } finally {
651
+ reader.releaseLock();
652
+ nodeRes.end();
653
+ }
654
+ }
655
+ function getPathnameFromUrl(url) {
656
+ if (!url) return "/";
657
+ const idx = url.indexOf("?");
658
+ return idx === -1 ? url : url.slice(0, idx);
659
+ }
660
+ var nodeContextFactory = (req, params, getRemoteInfo, schema, url) => new NodeContext(req, url || req.url || "/", params, getRemoteInfo, schema);
661
+ function server({ port, hostname, router }) {
662
+ let httpServer = void 0;
663
+ const compiledRouter = new Router();
664
+ for (const route of router.routes) {
665
+ let mergedSchema = route.schema;
666
+ if (typeof router.matchMiddleware === "function" && typeof router.mergeSchemas === "function") {
667
+ const matchedMiddleware = router.matchMiddleware(route.path);
668
+ mergedSchema = router.mergeSchemas(matchedMiddleware, route.schema);
669
+ }
670
+ if (route.handler) {
671
+ const executor = createExecutor(nodeContextFactory, route.handler, mergedSchema);
672
+ const nodeHandler = async (req, res, params, getRemoteInfo) => {
673
+ const url = req.url || "/";
674
+ try {
675
+ const result = await executor(req, params, getRemoteInfo, url);
676
+ const response = toResponse(result);
677
+ await sendResponse(res, response);
678
+ } catch (error) {
679
+ const errResponse = error instanceof Response ? error : new Response(error instanceof Error ? error.message : String(error), { status: 500 });
680
+ await sendResponse(res, errResponse);
681
+ }
682
+ };
683
+ compiledRouter.registerCompiled(route.method, route.path, nodeHandler, mergedSchema);
684
+ } else if (route.staticValue !== void 0) {
685
+ const cachedResponse = toResponse(route.staticValue);
686
+ const statusCode = cachedResponse.status;
687
+ cachedResponse.text().then((body) => {
688
+ const bodyBuffer = Buffer.from(body);
689
+ const headersObj = {};
690
+ cachedResponse.headers.forEach((v, k) => {
691
+ headersObj[k] = v;
692
+ });
693
+ headersObj["content-length"] = String(bodyBuffer.length);
694
+ const staticExecutor = (_req, res) => {
695
+ res.writeHead(statusCode, headersObj);
696
+ res.end(bodyBuffer);
697
+ };
698
+ compiledRouter.registerCompiled(route.method, route.path, staticExecutor, mergedSchema, cachedResponse);
699
+ });
700
+ }
701
+ }
702
+ const match = compiledRouter.matcher();
703
+ const notFoundExecutor = createNotFoundExecutor(router, nodeContextFactory);
704
+ const NOT_FOUND_BODY = "Not Found";
705
+ const wsRoutes = router.wsRoutes || [];
706
+ const wsHandlers = /* @__PURE__ */ new Map();
707
+ for (const wsRoute of wsRoutes) {
708
+ wsHandlers.set(wsRoute.path, createNodeWsHandler(wsRoute.handler, wsRoute.options));
709
+ }
710
+ return {
711
+ async run() {
712
+ httpServer?.close();
713
+ return new Promise((resolve, reject) => {
714
+ httpServer = createServer((req, res) => {
715
+ const pathname = getPathnameFromUrl(req.url);
716
+ const method = req.method || "GET";
717
+ const matchResult = match(method, pathname);
718
+ if (matchResult !== void 0) {
719
+ if (matchResult.response !== void 0) {
720
+ matchResult.handler(req, res);
721
+ return;
722
+ }
723
+ const getRemoteInfo = () => ({
724
+ address: req.socket.remoteAddress || "0.0.0.0",
725
+ port: req.socket.remotePort || 0
726
+ });
727
+ const result = matchResult.handler(req, res, matchResult.params, getRemoteInfo);
728
+ if (result && typeof result.then === "function") {
729
+ result.catch((error) => {
730
+ if (!res.headersSent) {
731
+ res.statusCode = 500;
732
+ res.end(error instanceof Error ? error.message : String(error));
733
+ }
734
+ });
735
+ }
736
+ return;
737
+ }
738
+ if (notFoundExecutor) {
739
+ const getRemoteInfo = () => ({
740
+ address: req.socket.remoteAddress || "0.0.0.0",
741
+ port: req.socket.remotePort || 0
742
+ });
743
+ const url = req.url || "/";
744
+ const result = notFoundExecutor(req, getRemoteInfo, pathname, url);
745
+ if (result && typeof result.then === "function") {
746
+ result.then(async (resp) => {
747
+ const response = toResponse(resp);
748
+ await sendResponse(res, response);
749
+ }).catch((error) => {
750
+ if (!res.headersSent) {
751
+ res.statusCode = 500;
752
+ res.end(error instanceof Error ? error.message : String(error));
753
+ }
754
+ });
755
+ } else {
756
+ const response = toResponse(result);
757
+ sendResponse(res, response).catch((error) => {
758
+ if (!res.headersSent) {
759
+ res.statusCode = 500;
760
+ res.end(error instanceof Error ? error.message : String(error));
761
+ }
762
+ });
763
+ }
764
+ return;
765
+ }
766
+ res.statusCode = 404;
767
+ res.end(NOT_FOUND_BODY);
768
+ });
769
+ httpServer.on("error", reject);
770
+ if (wsHandlers.size > 0) {
771
+ httpServer.on("upgrade", (req, socket, head) => {
772
+ const pathname = getPathnameFromUrl(req.url);
773
+ const wsHandler = wsHandlers.get(pathname);
774
+ if (wsHandler) {
775
+ wsHandler.handleUpgrade(req, socket, head);
776
+ } else {
777
+ socket.write("HTTP/1.1 404 Not Found\r\n\r\n");
778
+ socket.destroy();
779
+ }
780
+ });
781
+ for (const wsHandler of wsHandlers.values()) {
782
+ wsHandler.startHeartbeat();
783
+ }
784
+ }
785
+ httpServer.listen(port || 3e3, hostname || "0.0.0.0", () => {
786
+ const addr = httpServer?.address();
787
+ if (addr && typeof addr !== "string") {
788
+ resolve({ hostname: addr.address, port: addr.port });
789
+ } else {
790
+ resolve({ hostname: hostname || "0.0.0.0", port: port || 3e3 });
791
+ }
792
+ });
793
+ });
794
+ },
795
+ stop() {
796
+ for (const wsHandler of wsHandlers.values()) {
797
+ wsHandler.stopHeartbeat();
798
+ }
799
+ return new Promise((resolve, reject) => {
800
+ if (httpServer) {
801
+ httpServer.close((err) => {
802
+ if (err) reject(err);
803
+ else resolve();
804
+ });
805
+ httpServer = void 0;
806
+ } else {
807
+ resolve();
808
+ }
809
+ });
810
+ }
811
+ };
812
+ }
813
+ export {
814
+ server
815
+ };
816
+ //# sourceMappingURL=node-JLUTIPEN.js.map