@hono/node-server 2.0.0-rc.1 → 2.0.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.
@@ -1,6 +1,9 @@
1
- const require_constants = require('./constants-B7DBcQew.js');
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_constants = require('./constants-BXAKTxRC.cjs');
3
+ let node_http = require("node:http");
2
4
  let node_http2 = require("node:http2");
3
5
  let node_stream = require("node:stream");
6
+ let hono_ws = require("hono/ws");
4
7
 
5
8
  //#region src/error.ts
6
9
  var RequestError = class extends Error {
@@ -12,69 +15,19 @@ var RequestError = class extends Error {
12
15
 
13
16
  //#endregion
14
17
  //#region src/url.ts
15
- const isPathDelimiter = (charCode) => charCode === 47 || charCode === 63 || charCode === 35;
16
- const hasDotSegment = (url, dotIndex) => {
17
- if ((dotIndex === 0 ? 47 : url.charCodeAt(dotIndex - 1)) !== 47) return false;
18
- const nextIndex = dotIndex + 1;
19
- if (nextIndex === url.length) return true;
20
- const next = url.charCodeAt(nextIndex);
21
- if (isPathDelimiter(next)) return true;
22
- if (next !== 46) return false;
23
- const nextNextIndex = dotIndex + 2;
24
- if (nextNextIndex === url.length) return true;
25
- return isPathDelimiter(url.charCodeAt(nextNextIndex));
26
- };
27
- const allowedRequestUrlChar = new Uint8Array(128);
28
- for (let c = 48; c <= 57; c++) allowedRequestUrlChar[c] = 1;
29
- for (let c = 65; c <= 90; c++) allowedRequestUrlChar[c] = 1;
30
- for (let c = 97; c <= 122; c++) allowedRequestUrlChar[c] = 1;
31
- (() => {
32
- const chars = "-./:?#[]@!$&'()*+,;=~_";
33
- for (let i = 0; i < 22; i++) allowedRequestUrlChar[chars.charCodeAt(i)] = 1;
34
- })();
35
- const safeHostChar = new Uint8Array(128);
36
- for (let c = 48; c <= 57; c++) safeHostChar[c] = 1;
37
- for (let c = 97; c <= 122; c++) safeHostChar[c] = 1;
38
- (() => {
39
- const chars = ".-_:";
40
- for (let i = 0; i < 4; i++) safeHostChar[chars.charCodeAt(i)] = 1;
41
- })();
18
+ const reValidRequestUrl = /^\/[!#$&-;=?-\[\]_a-z~]*$/;
19
+ const reDotSegment = /\/\.\.?(?:[/?#]|$)/;
20
+ const reValidHost = /^[a-z0-9._-]+(?::(?:[1-5]\d{3,4}|[6-9]\d{3}))?$/;
42
21
  const buildUrl = (scheme, host, incomingUrl) => {
43
22
  const url = `${scheme}://${host}${incomingUrl}`;
44
- let needsHostValidationByURL = false;
45
- for (let i = 0, len = host.length; i < len; i++) {
46
- const c = host.charCodeAt(i);
47
- if (c > 127 || safeHostChar[c] === 0) {
48
- needsHostValidationByURL = true;
49
- break;
50
- }
51
- if (c === 58) {
52
- i++;
53
- const firstDigit = host.charCodeAt(i);
54
- if (firstDigit < 49 || firstDigit > 57 || i + 4 > len || i + (firstDigit < 54 ? 5 : 4) < len) {
55
- needsHostValidationByURL = true;
56
- break;
57
- }
58
- for (; i < len; i++) {
59
- const c = host.charCodeAt(i);
60
- if (c < 48 || c > 57) {
61
- needsHostValidationByURL = true;
62
- break;
63
- }
64
- }
65
- }
66
- }
67
- if (needsHostValidationByURL) {
23
+ if (!reValidHost.test(host)) {
68
24
  const urlObj = new URL(url);
69
25
  if (urlObj.hostname.length !== host.length && urlObj.hostname !== (host.includes(":") ? host.replace(/:\d+$/, "") : host).toLowerCase()) throw new RequestError("Invalid host header");
70
26
  return urlObj.href;
71
27
  } else if (incomingUrl.length === 0) return url + "/";
72
28
  else {
73
29
  if (incomingUrl.charCodeAt(0) !== 47) throw new RequestError("Invalid URL");
74
- for (let i = 1, len = incomingUrl.length; i < len; i++) {
75
- const c = incomingUrl.charCodeAt(i);
76
- if (c > 127 || allowedRequestUrlChar[c] === 0 || c === 46 && hasDotSegment(incomingUrl, i)) return new URL(url).href;
77
- }
30
+ if (!reValidRequestUrl.test(incomingUrl) || reDotSegment.test(incomingUrl)) return new URL(url).href;
78
31
  return url;
79
32
  }
80
33
  };
@@ -86,7 +39,7 @@ const toRequestError = (e) => {
86
39
  return new RequestError(e.message, { cause: e });
87
40
  };
88
41
  const GlobalRequest = global.Request;
89
- var Request = class extends GlobalRequest {
42
+ var Request$1 = class extends GlobalRequest {
90
43
  constructor(input, options) {
91
44
  if (typeof input === "object" && getRequestCache in input) {
92
45
  const hasReplacementBody = options !== void 0 && "body" in options && options.body != null;
@@ -100,9 +53,9 @@ var Request = class extends GlobalRequest {
100
53
  const newHeadersFromIncoming = (incoming) => {
101
54
  const headerRecord = [];
102
55
  const rawHeaders = incoming.rawHeaders;
103
- for (let i = 0; i < rawHeaders.length; i += 2) {
104
- const { [i]: key, [i + 1]: value } = rawHeaders;
105
- if (key.charCodeAt(0) !== 58) headerRecord.push([key, value]);
56
+ for (let i = 0, len = rawHeaders.length; i < len; i += 2) {
57
+ const key = rawHeaders[i];
58
+ if (key.charCodeAt(0) !== 58) headerRecord.push([key, rawHeaders[i + 1]]);
106
59
  }
107
60
  return new Headers(headerRecord);
108
61
  };
@@ -115,7 +68,7 @@ const newRequestFromIncoming = (method, url, headers, incoming, abortController)
115
68
  };
116
69
  if (method === "TRACE") {
117
70
  init.method = "GET";
118
- const req = new Request(url, init);
71
+ const req = new Request$1(url, init);
119
72
  Object.defineProperty(req, "method", { get() {
120
73
  return "TRACE";
121
74
  } });
@@ -138,7 +91,7 @@ const newRequestFromIncoming = (method, url, headers, incoming, abortController)
138
91
  }
139
92
  } });
140
93
  } else init.body = node_stream.Readable.toWeb(incoming);
141
- return new Request(url, init);
94
+ return new Request$1(url, init);
142
95
  };
143
96
  const getRequestCache = Symbol("getRequestCache");
144
97
  const requestCache = Symbol("requestCache");
@@ -330,7 +283,7 @@ const requestPrototype = {
330
283
  } });
331
284
  init.duplex = "half";
332
285
  }
333
- const req = new Request(this[urlKey], init);
286
+ const req = new Request$1(this[urlKey], init);
334
287
  if (method === "TRACE") Object.defineProperty(req, "method", { get() {
335
288
  return "TRACE";
336
289
  } });
@@ -350,6 +303,9 @@ const requestPrototype = {
350
303
  return false;
351
304
  }
352
305
  };
306
+ Object.defineProperty(requestPrototype, "signal", { get() {
307
+ return this[getAbortController]().signal;
308
+ } });
353
309
  [
354
310
  "cache",
355
311
  "credentials",
@@ -359,7 +315,6 @@ const requestPrototype = {
359
315
  "redirect",
360
316
  "referrer",
361
317
  "referrerPolicy",
362
- "signal",
363
318
  "keepalive"
364
319
  ].forEach((k) => {
365
320
  Object.defineProperty(requestPrototype, k, { get() {
@@ -392,7 +347,18 @@ Object.defineProperty(requestPrototype, "json", { value: function() {
392
347
  if (this[bodyConsumedDirectlyKey]) return rejectBodyUnusable();
393
348
  return this.text().then(JSON.parse);
394
349
  } });
395
- Object.setPrototypeOf(requestPrototype, Request.prototype);
350
+ Object.defineProperty(requestPrototype, Symbol.for("nodejs.util.inspect.custom"), { value: function(depth, options, inspectFn) {
351
+ return `Request (lightweight) ${inspectFn({
352
+ method: this.method,
353
+ url: this.url,
354
+ headers: this.headers,
355
+ nativeRequest: this[requestCache]
356
+ }, {
357
+ ...options,
358
+ depth: depth == null ? null : depth - 1
359
+ })}`;
360
+ } });
361
+ Object.setPrototypeOf(requestPrototype, Request$1.prototype);
396
362
  const newRequest = (incoming, defaultHostname) => {
397
363
  const req = Object.create(requestPrototype);
398
364
  req[incomingKey] = incoming;
@@ -425,6 +391,7 @@ const newRequest = (incoming, defaultHostname) => {
425
391
 
426
392
  //#endregion
427
393
  //#region src/response.ts
394
+ const defaultContentType = "text/plain; charset=UTF-8";
428
395
  const responseCache = Symbol("responseCache");
429
396
  const getResponseCache = Symbol("getResponseCache");
430
397
  const cacheKey = Symbol("cache");
@@ -450,16 +417,16 @@ var Response$1 = class Response$1 {
450
417
  headers = new Headers(init.#init.headers);
451
418
  }
452
419
  } else this.#init = init;
453
- if (typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey] = [
420
+ if (body == null || typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey] = [
454
421
  init?.status || 200,
455
- body,
422
+ body ?? null,
456
423
  headers || init?.headers
457
424
  ];
458
425
  }
459
426
  get headers() {
460
427
  const cache = this[cacheKey];
461
428
  if (cache) {
462
- if (!(cache[2] instanceof Headers)) cache[2] = new Headers(cache[2] || { "content-type": "text/plain; charset=UTF-8" });
429
+ if (!(cache[2] instanceof Headers)) cache[2] = new Headers(cache[2] || (cache[1] === null ? void 0 : { "content-type": defaultContentType }));
463
430
  return cache[2];
464
431
  }
465
432
  return this[getResponseCache]().headers;
@@ -497,8 +464,62 @@ var Response$1 = class Response$1 {
497
464
  return this[getResponseCache]()[k]();
498
465
  } });
499
466
  });
467
+ Object.defineProperty(Response$1.prototype, Symbol.for("nodejs.util.inspect.custom"), { value: function(depth, options, inspectFn) {
468
+ return `Response (lightweight) ${inspectFn({
469
+ status: this.status,
470
+ headers: this.headers,
471
+ ok: this.ok,
472
+ nativeResponse: this[responseCache]
473
+ }, {
474
+ ...options,
475
+ depth: depth == null ? null : depth - 1
476
+ })}`;
477
+ } });
500
478
  Object.setPrototypeOf(Response$1, GlobalResponse);
501
479
  Object.setPrototypeOf(Response$1.prototype, GlobalResponse.prototype);
480
+ const validRedirectUrl = /^https?:\/\/[!#-;=?-[\]_a-z~A-Z]+$/;
481
+ const parseRedirectUrl = (url) => {
482
+ if (url instanceof URL) return url.href;
483
+ if (validRedirectUrl.test(url)) return url;
484
+ return new URL(url).href;
485
+ };
486
+ const validRedirectStatuses = new Set([
487
+ 301,
488
+ 302,
489
+ 303,
490
+ 307,
491
+ 308
492
+ ]);
493
+ Object.defineProperty(Response$1, "redirect", {
494
+ value: function redirect(url, status = 302) {
495
+ if (!validRedirectStatuses.has(status)) throw new RangeError("Invalid status code");
496
+ return new Response$1(null, {
497
+ status,
498
+ headers: { location: parseRedirectUrl(url) }
499
+ });
500
+ },
501
+ writable: true,
502
+ configurable: true
503
+ });
504
+ Object.defineProperty(Response$1, "json", {
505
+ value: function json(data, init) {
506
+ const body = JSON.stringify(data);
507
+ if (body === void 0) throw new TypeError("The data is not JSON serializable");
508
+ const initHeaders = init?.headers;
509
+ let headers;
510
+ if (initHeaders) {
511
+ headers = new Headers(initHeaders);
512
+ if (!headers.has("content-type")) headers.set("content-type", "application/json");
513
+ } else headers = { "content-type": "application/json" };
514
+ return new Response$1(body, {
515
+ status: init?.status ?? 200,
516
+ statusText: init?.statusText,
517
+ headers
518
+ });
519
+ },
520
+ writable: true,
521
+ configurable: true
522
+ });
502
523
 
503
524
  //#endregion
504
525
  //#region src/utils.ts
@@ -537,7 +558,7 @@ function writeFromReadableStream(stream, writable) {
537
558
  else if (writable.destroyed) return;
538
559
  return writeFromReadableStreamDefaultReader(stream.getReader(), writable);
539
560
  }
540
- const buildOutgoingHttpHeaders = (headers) => {
561
+ const buildOutgoingHttpHeaders = (headers, defaultContentType) => {
541
562
  const res = {};
542
563
  if (!(headers instanceof Headers)) headers = new Headers(headers ?? void 0);
543
564
  if (headers.has("set-cookie")) {
@@ -546,13 +567,63 @@ const buildOutgoingHttpHeaders = (headers) => {
546
567
  else res[k] = v;
547
568
  if (cookies.length > 0) res["set-cookie"] = cookies;
548
569
  } else for (const [k, v] of headers) res[k] = v;
549
- res["content-type"] ??= "text/plain; charset=UTF-8";
570
+ if (defaultContentType) res["content-type"] ??= defaultContentType;
550
571
  return res;
551
572
  };
552
573
 
553
574
  //#endregion
554
575
  //#region src/listener.ts
555
576
  const outgoingEnded = Symbol("outgoingEnded");
577
+ const incomingDraining = Symbol("incomingDraining");
578
+ const DRAIN_TIMEOUT_MS = 500;
579
+ const MAX_DRAIN_BYTES = 64 * 1024 * 1024;
580
+ const drainIncoming = (incoming) => {
581
+ const incomingWithDrainState = incoming;
582
+ if (incoming.destroyed || incomingWithDrainState[incomingDraining]) return;
583
+ incomingWithDrainState[incomingDraining] = true;
584
+ if (incoming instanceof node_http2.Http2ServerRequest) {
585
+ try {
586
+ incoming.stream?.close?.(node_http2.constants.NGHTTP2_NO_ERROR);
587
+ } catch {}
588
+ return;
589
+ }
590
+ let bytesRead = 0;
591
+ const cleanup = () => {
592
+ clearTimeout(timer);
593
+ incoming.off("data", onData);
594
+ incoming.off("end", cleanup);
595
+ incoming.off("error", cleanup);
596
+ };
597
+ const forceClose = () => {
598
+ cleanup();
599
+ const socket = incoming.socket;
600
+ if (socket && !socket.destroyed) socket.destroySoon();
601
+ };
602
+ const timer = setTimeout(forceClose, DRAIN_TIMEOUT_MS);
603
+ timer.unref?.();
604
+ const onData = (chunk) => {
605
+ bytesRead += chunk.length;
606
+ if (bytesRead > MAX_DRAIN_BYTES) forceClose();
607
+ };
608
+ incoming.on("data", onData);
609
+ incoming.on("end", cleanup);
610
+ incoming.on("error", cleanup);
611
+ incoming.resume();
612
+ };
613
+ const makeCloseHandler = (req, incoming, outgoing, needsBodyCleanup) => () => {
614
+ if (incoming.errored) req[abortRequest](incoming.errored.toString());
615
+ else if (!outgoing.writableFinished) req[abortRequest]("Client connection prematurely closed.");
616
+ if (needsBodyCleanup && !incoming.readableEnded) setTimeout(() => {
617
+ if (!incoming.readableEnded) setTimeout(() => {
618
+ drainIncoming(incoming);
619
+ });
620
+ });
621
+ };
622
+ const isImmediateCacheableResponse = (res) => {
623
+ if (!(cacheKey in res)) return false;
624
+ const body = res[cacheKey][1];
625
+ return body === null || typeof body === "string" || body instanceof Uint8Array;
626
+ };
556
627
  const handleRequestError = () => new Response(null, { status: 400 });
557
628
  const handleFetchError = (e) => new Response(null, { status: e instanceof Error && (e.name === "TimeoutError" || e.constructor.name === "TimeoutError") ? 504 : 500 });
558
629
  const handleResponseError = (e, outgoing) => {
@@ -570,15 +641,44 @@ const flushHeaders = (outgoing) => {
570
641
  };
571
642
  const responseViaCache = async (res, outgoing) => {
572
643
  let [status, body, header] = res[cacheKey];
644
+ if (!header) {
645
+ if (body === null) {
646
+ outgoing.writeHead(status);
647
+ outgoing.end();
648
+ } else if (typeof body === "string") {
649
+ outgoing.writeHead(status, {
650
+ "Content-Type": defaultContentType,
651
+ "Content-Length": Buffer.byteLength(body)
652
+ });
653
+ outgoing.end(body);
654
+ } else if (body instanceof Uint8Array) {
655
+ outgoing.writeHead(status, {
656
+ "Content-Type": defaultContentType,
657
+ "Content-Length": body.byteLength
658
+ });
659
+ outgoing.end(body);
660
+ } else if (body instanceof Blob) {
661
+ outgoing.writeHead(status, {
662
+ "Content-Type": defaultContentType,
663
+ "Content-Length": body.size
664
+ });
665
+ outgoing.end(new Uint8Array(await body.arrayBuffer()));
666
+ } else {
667
+ outgoing.writeHead(status, { "Content-Type": defaultContentType });
668
+ flushHeaders(outgoing);
669
+ await writeFromReadableStream(body, outgoing)?.catch((e) => handleResponseError(e, outgoing));
670
+ }
671
+ outgoing[outgoingEnded]?.();
672
+ return;
673
+ }
573
674
  let hasContentLength = false;
574
- if (!header) header = { "content-type": "text/plain; charset=UTF-8" };
575
- else if (header instanceof Headers) {
675
+ if (header instanceof Headers) {
576
676
  hasContentLength = header.has("content-length");
577
- header = buildOutgoingHttpHeaders(header);
677
+ header = buildOutgoingHttpHeaders(header, body === null ? void 0 : defaultContentType);
578
678
  } else if (Array.isArray(header)) {
579
679
  const headerObj = new Headers(header);
580
680
  hasContentLength = headerObj.has("content-length");
581
- header = buildOutgoingHttpHeaders(headerObj);
681
+ header = buildOutgoingHttpHeaders(headerObj, body === null ? void 0 : defaultContentType);
582
682
  } else for (const key in header) if (key.length === 14 && key.toLowerCase() === "content-length") {
583
683
  hasContentLength = true;
584
684
  break;
@@ -589,7 +689,8 @@ const responseViaCache = async (res, outgoing) => {
589
689
  else if (body instanceof Blob) header["Content-Length"] = body.size;
590
690
  }
591
691
  outgoing.writeHead(status, header);
592
- if (typeof body === "string" || body instanceof Uint8Array) outgoing.end(body);
692
+ if (body == null) outgoing.end();
693
+ else if (typeof body === "string" || body instanceof Uint8Array) outgoing.end(body);
593
694
  else if (body instanceof Blob) outgoing.end(new Uint8Array(await body.arrayBuffer()));
594
695
  else {
595
696
  flushHeaders(outgoing);
@@ -608,7 +709,7 @@ const responseViaResponseObject = async (res, outgoing, options = {}) => {
608
709
  }
609
710
  else res = await res.catch(handleFetchError);
610
711
  if (cacheKey in res) return responseViaCache(res, outgoing);
611
- const resHeaderRecord = buildOutgoingHttpHeaders(res.headers);
712
+ const resHeaderRecord = buildOutgoingHttpHeaders(res.headers, res.body === null ? void 0 : defaultContentType);
612
713
  if (res.body) {
613
714
  const reader = res.body.getReader();
614
715
  const values = [];
@@ -656,47 +757,47 @@ const responseViaResponseObject = async (res, outgoing, options = {}) => {
656
757
  };
657
758
  const getRequestListener = (fetchCallback, options = {}) => {
658
759
  const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
659
- if (options.overrideGlobalObjects !== false && global.Request !== Request) {
660
- Object.defineProperty(global, "Request", { value: Request });
760
+ if (options.overrideGlobalObjects !== false && global.Request !== Request$1) {
761
+ Object.defineProperty(global, "Request", { value: Request$1 });
661
762
  Object.defineProperty(global, "Response", { value: Response$1 });
662
763
  }
663
764
  return async (incoming, outgoing) => {
664
765
  let res, req;
766
+ let needsBodyCleanup = false;
767
+ let closeHandlerAttached = false;
768
+ const ensureCloseHandler = () => {
769
+ if (!req || closeHandlerAttached) return;
770
+ closeHandlerAttached = true;
771
+ outgoing.on("close", makeCloseHandler(req, incoming, outgoing, needsBodyCleanup));
772
+ };
665
773
  try {
666
774
  req = newRequest(incoming, options.hostname);
667
- let incomingEnded = !autoCleanupIncoming || incoming.method === "GET" || incoming.method === "HEAD";
668
- if (!incomingEnded) {
775
+ needsBodyCleanup = autoCleanupIncoming && !(incoming.method === "GET" || incoming.method === "HEAD");
776
+ if (needsBodyCleanup) {
669
777
  incoming[wrapBodyStream] = true;
670
- incoming.on("end", () => {
671
- incomingEnded = true;
672
- });
673
778
  if (incoming instanceof node_http2.Http2ServerRequest) outgoing[outgoingEnded] = () => {
674
- if (!incomingEnded) setTimeout(() => {
675
- if (!incomingEnded) setTimeout(() => {
779
+ if (!incoming.readableEnded) setTimeout(() => {
780
+ if (!incoming.readableEnded) setTimeout(() => {
676
781
  incoming.destroy();
677
782
  outgoing.destroy();
678
783
  });
679
784
  });
680
785
  };
681
786
  }
682
- outgoing.on("close", () => {
683
- let abortReason;
684
- if (incoming.errored) abortReason = incoming.errored.toString();
685
- else if (!outgoing.writableFinished) abortReason = "Client connection prematurely closed.";
686
- if (abortReason !== void 0) req[abortRequest](abortReason);
687
- if (!incomingEnded) setTimeout(() => {
688
- if (!incomingEnded) setTimeout(() => {
689
- incoming.destroy();
690
- });
691
- });
692
- });
693
787
  res = fetchCallback(req, {
694
788
  incoming,
695
789
  outgoing
696
790
  });
697
- if (cacheKey in res) return responseViaCache(res, outgoing);
791
+ if (!isPromise(res) && isImmediateCacheableResponse(res)) {
792
+ if (needsBodyCleanup && !incoming.readableEnded) outgoing.once("finish", () => {
793
+ if (!incoming.readableEnded) drainIncoming(incoming);
794
+ });
795
+ return responseViaCache(res, outgoing);
796
+ }
797
+ ensureCloseHandler();
698
798
  } catch (e) {
699
799
  if (!res) if (options.errorHandler) {
800
+ ensureCloseHandler();
700
801
  res = await options.errorHandler(req ? e : toRequestError(e));
701
802
  if (!res) return;
702
803
  } else if (!req) res = handleRequestError();
@@ -712,15 +813,194 @@ const getRequestListener = (fetchCallback, options = {}) => {
712
813
  };
713
814
 
714
815
  //#endregion
715
- Object.defineProperty(exports, 'RequestError', {
716
- enumerable: true,
717
- get: function () {
718
- return RequestError;
719
- }
816
+ //#region src/websocket.ts
817
+ /**
818
+ * @link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
819
+ */
820
+ const CloseEvent = globalThis.CloseEvent ?? class extends Event {
821
+ #eventInitDict;
822
+ constructor(type, eventInitDict = {}) {
823
+ super(type, eventInitDict);
824
+ this.#eventInitDict = eventInitDict;
825
+ }
826
+ get wasClean() {
827
+ return this.#eventInitDict.wasClean ?? false;
828
+ }
829
+ get code() {
830
+ return this.#eventInitDict.code ?? 0;
831
+ }
832
+ get reason() {
833
+ return this.#eventInitDict.reason ?? "";
834
+ }
835
+ };
836
+ const generateConnectionSymbol = () => Symbol("connection");
837
+ const CONNECTION_SYMBOL_KEY = Symbol("CONNECTION_SYMBOL_KEY");
838
+ const WAIT_FOR_WEBSOCKET_SYMBOL = Symbol("WAIT_FOR_WEBSOCKET_SYMBOL");
839
+ const rejectUpgradeRequest = (socket, status) => {
840
+ socket.end(`HTTP/1.1 ${status.toString()} ${node_http.STATUS_CODES[status] ?? ""}\r\nConnection: close\r
841
+ Content-Length: 0\r
842
+ \r
843
+ `);
844
+ };
845
+ const createUpgradeRequest = (request) => {
846
+ const protocol = request.socket.encrypted ? "https" : "http";
847
+ const url = new URL(request.url ?? "/", `${protocol}://${request.headers.host ?? "localhost"}`);
848
+ const headers = new Headers();
849
+ for (const key in request.headers) {
850
+ const value = request.headers[key];
851
+ if (!value) continue;
852
+ headers.append(key, Array.isArray(value) ? value[0] : value);
853
+ }
854
+ return new Request(url, { headers });
855
+ };
856
+ const setupWebSocket = (options) => {
857
+ const { server, fetchCallback, wss } = options;
858
+ const waiterMap = /* @__PURE__ */ new Map();
859
+ wss.on("connection", (ws, request) => {
860
+ const waiter = waiterMap.get(request);
861
+ if (waiter) {
862
+ waiter.resolve(ws);
863
+ waiterMap.delete(request);
864
+ }
865
+ });
866
+ const waitForWebSocket = (request, connectionSymbol) => {
867
+ return new Promise((resolve) => {
868
+ waiterMap.set(request, {
869
+ resolve,
870
+ connectionSymbol
871
+ });
872
+ });
873
+ };
874
+ server.on("upgrade", async (request, socket, head) => {
875
+ if (request.headers.upgrade?.toLowerCase() !== "websocket") return;
876
+ const env = {
877
+ incoming: request,
878
+ outgoing: void 0,
879
+ wss,
880
+ [WAIT_FOR_WEBSOCKET_SYMBOL]: waitForWebSocket
881
+ };
882
+ let status = 400;
883
+ try {
884
+ const response = await fetchCallback(createUpgradeRequest(request), env);
885
+ if (response instanceof Response) status = response.status;
886
+ } catch {
887
+ if (server.listenerCount("upgrade") === 1) rejectUpgradeRequest(socket, 500);
888
+ return;
889
+ }
890
+ const waiter = waiterMap.get(request);
891
+ if (!waiter || waiter.connectionSymbol !== env[CONNECTION_SYMBOL_KEY]) {
892
+ waiterMap.delete(request);
893
+ if (server.listenerCount("upgrade") === 1) rejectUpgradeRequest(socket, status);
894
+ return;
895
+ }
896
+ wss.handleUpgrade(request, socket, head, (ws) => {
897
+ wss.emit("connection", ws, request);
898
+ });
899
+ });
900
+ server.on("close", () => {
901
+ wss.close();
902
+ });
903
+ };
904
+ const upgradeWebSocket = (0, hono_ws.defineWebSocketHelper)(async (c, events, options) => {
905
+ if (c.req.header("upgrade")?.toLowerCase() !== "websocket") return;
906
+ const env = c.env;
907
+ const waitForWebSocket = env[WAIT_FOR_WEBSOCKET_SYMBOL];
908
+ if (!waitForWebSocket || !env.incoming) return new Response(null, { status: 500 });
909
+ const connectionSymbol = generateConnectionSymbol();
910
+ env[CONNECTION_SYMBOL_KEY] = connectionSymbol;
911
+ (async () => {
912
+ const ws = await waitForWebSocket(env.incoming, connectionSymbol);
913
+ const messagesReceivedInStarting = [];
914
+ const bufferMessage = (data, isBinary) => {
915
+ messagesReceivedInStarting.push([data, isBinary]);
916
+ };
917
+ ws.on("message", bufferMessage);
918
+ const ctx = {
919
+ binaryType: "arraybuffer",
920
+ close(code, reason) {
921
+ ws.close(code, reason);
922
+ },
923
+ protocol: ws.protocol,
924
+ raw: ws,
925
+ get readyState() {
926
+ return ws.readyState;
927
+ },
928
+ send(source, opts) {
929
+ ws.send(source, { compress: opts?.compress });
930
+ },
931
+ url: new URL(c.req.url)
932
+ };
933
+ try {
934
+ events?.onOpen?.(new Event("open"), ctx);
935
+ } catch (e) {
936
+ (options?.onError ?? console.error)(e);
937
+ }
938
+ const handleMessage = (data, isBinary) => {
939
+ const datas = Array.isArray(data) ? data : [data];
940
+ for (const data of datas) try {
941
+ events?.onMessage?.(new MessageEvent("message", { data: isBinary ? data instanceof ArrayBuffer ? data : data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) : data.toString("utf-8") }), ctx);
942
+ } catch (e) {
943
+ (options?.onError ?? console.error)(e);
944
+ }
945
+ };
946
+ ws.off("message", bufferMessage);
947
+ for (const message of messagesReceivedInStarting) handleMessage(...message);
948
+ ws.on("message", (data, isBinary) => {
949
+ handleMessage(data, isBinary);
950
+ });
951
+ ws.on("close", (code, reason) => {
952
+ try {
953
+ events?.onClose?.(new CloseEvent("close", {
954
+ code,
955
+ reason: reason.toString()
956
+ }), ctx);
957
+ } catch (e) {
958
+ (options?.onError ?? console.error)(e);
959
+ }
960
+ });
961
+ ws.on("error", (error) => {
962
+ try {
963
+ events?.onError?.(new ErrorEvent("error", { error }), ctx);
964
+ } catch (e) {
965
+ (options?.onError ?? console.error)(e);
966
+ }
967
+ });
968
+ })();
969
+ return new Response();
720
970
  });
721
- Object.defineProperty(exports, 'getRequestListener', {
722
- enumerable: true,
723
- get: function () {
724
- return getRequestListener;
725
- }
726
- });
971
+
972
+ //#endregion
973
+ //#region src/server.ts
974
+ const createAdaptorServer = (options) => {
975
+ const fetchCallback = options.fetch;
976
+ const requestListener = getRequestListener(fetchCallback, {
977
+ hostname: options.hostname,
978
+ overrideGlobalObjects: options.overrideGlobalObjects,
979
+ autoCleanupIncoming: options.autoCleanupIncoming
980
+ });
981
+ const server = (options.createServer || node_http.createServer)(options.serverOptions || {}, requestListener);
982
+ if (options.websocket && options.websocket.server) {
983
+ if (options.websocket.server.options.noServer !== true) throw new Error("WebSocket server must be created with { noServer: true } option");
984
+ setupWebSocket({
985
+ server,
986
+ fetchCallback,
987
+ wss: options.websocket.server
988
+ });
989
+ }
990
+ return server;
991
+ };
992
+ const serve = (options, listeningListener) => {
993
+ const server = createAdaptorServer(options);
994
+ server.listen(options?.port ?? 3e3, options.hostname, () => {
995
+ const serverInfo = server.address();
996
+ listeningListener && listeningListener(serverInfo);
997
+ });
998
+ return server;
999
+ };
1000
+
1001
+ //#endregion
1002
+ exports.RequestError = RequestError;
1003
+ exports.createAdaptorServer = createAdaptorServer;
1004
+ exports.getRequestListener = getRequestListener;
1005
+ exports.serve = serve;
1006
+ exports.upgradeWebSocket = upgradeWebSocket;