Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const require_constants = require('./constants-BXAKTxRC.cjs'); let node_http = require("node:http"); let node_http2 = require("node:http2"); let node_stream = require("node:stream"); let hono_ws = require("hono/ws"); //#region src/error.ts var RequestError = class extends Error { constructor(message, options) { super(message, options); this.name = "RequestError"; } }; //#endregion //#region src/url.ts const reValidRequestUrl = /^\/[!#$&-;=?-\[\]_a-z~]*$/; const reDotSegment = /\/\.\.?(?:[/?#]|$)/; const reValidHost = /^[a-z0-9._-]+(?::(?:[1-5]\d{3,4}|[6-9]\d{3}))?$/; const buildUrl = (scheme, host, incomingUrl) => { const url = `${scheme}://${host}${incomingUrl}`; if (!reValidHost.test(host)) { const urlObj = new URL(url); if (urlObj.hostname.length !== host.length && urlObj.hostname !== (host.includes(":") ? host.replace(/:\d+$/, "") : host).toLowerCase()) throw new RequestError("Invalid host header"); return urlObj.href; } else if (incomingUrl.length === 0) return url + "/"; else { if (incomingUrl.charCodeAt(0) !== 47) throw new RequestError("Invalid URL"); if (!reValidRequestUrl.test(incomingUrl) || reDotSegment.test(incomingUrl)) return new URL(url).href; return url; } }; //#endregion //#region src/request.ts const toRequestError = (e) => { if (e instanceof RequestError) return e; return new RequestError(e.message, { cause: e }); }; const GlobalRequest = global.Request; var Request$1 = class extends GlobalRequest { constructor(input, options) { if (typeof input === "object" && getRequestCache in input) { const hasReplacementBody = options !== void 0 && "body" in options && options.body != null; if (input[bodyConsumedDirectlyKey] && !hasReplacementBody) throw new TypeError("Cannot construct a Request with a Request object that has already been used."); input = input[getRequestCache](); } if (typeof (options?.body)?.getReader !== "undefined") options.duplex ??= "half"; super(input, options); } }; const newHeadersFromIncoming = (incoming) => { const headerRecord = []; const rawHeaders = incoming.rawHeaders; for (let i = 0, len = rawHeaders.length; i < len; i += 2) { const key = rawHeaders[i]; if (key.charCodeAt(0) !== 58) headerRecord.push([key, rawHeaders[i + 1]]); } return new Headers(headerRecord); }; const wrapBodyStream = Symbol("wrapBodyStream"); const newRequestFromIncoming = (method, url, headers, incoming, abortController) => { const init = { method, headers, signal: abortController.signal }; if (method === "TRACE") { init.method = "GET"; const req = new Request$1(url, init); Object.defineProperty(req, "method", { get() { return "TRACE"; } }); return req; } if (!(method === "GET" || method === "HEAD")) if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) init.body = new ReadableStream({ start(controller) { controller.enqueue(incoming.rawBody); controller.close(); } }); else if (incoming[wrapBodyStream]) { let reader; init.body = new ReadableStream({ async pull(controller) { try { reader ||= node_stream.Readable.toWeb(incoming).getReader(); const { done, value } = await reader.read(); if (done) controller.close(); else controller.enqueue(value); } catch (error) { controller.error(error); } } }); } else init.body = node_stream.Readable.toWeb(incoming); return new Request$1(url, init); }; const getRequestCache = Symbol("getRequestCache"); const requestCache = Symbol("requestCache"); const incomingKey = Symbol("incomingKey"); const urlKey = Symbol("urlKey"); const methodKey = Symbol("methodKey"); const headersKey = Symbol("headersKey"); const abortControllerKey = Symbol("abortControllerKey"); const getAbortController = Symbol("getAbortController"); const abortRequest = Symbol("abortRequest"); const bodyBufferKey = Symbol("bodyBuffer"); const bodyReadPromiseKey = Symbol("bodyReadPromise"); const bodyConsumedDirectlyKey = Symbol("bodyConsumedDirectly"); const bodyLockReaderKey = Symbol("bodyLockReader"); const abortReasonKey = Symbol("abortReason"); const newBodyUnusableError = () => { return /* @__PURE__ */ new TypeError("Body is unusable"); }; const rejectBodyUnusable = () => { return Promise.reject(newBodyUnusableError()); }; const textDecoder = new TextDecoder(); const consumeBodyDirectOnce = (request) => { if (request[bodyConsumedDirectlyKey]) return rejectBodyUnusable(); request[bodyConsumedDirectlyKey] = true; }; const toArrayBuffer = (buf) => { return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); }; const contentType = (request) => { return (request[headersKey] ||= newHeadersFromIncoming(request[incomingKey])).get("content-type") || ""; }; const methodTokenRegExp = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/; const normalizeIncomingMethod = (method) => { if (typeof method !== "string" || method.length === 0) return "GET"; switch (method) { case "DELETE": case "GET": case "HEAD": case "OPTIONS": case "POST": case "PUT": return method; } const upper = method.toUpperCase(); switch (upper) { case "DELETE": case "GET": case "HEAD": case "OPTIONS": case "POST": case "PUT": return upper; default: return method; } }; const validateDirectReadMethod = (method) => { if (!methodTokenRegExp.test(method)) return /* @__PURE__ */ new TypeError(`'${method}' is not a valid HTTP method.`); const normalized = method.toUpperCase(); if (normalized === "CONNECT" || normalized === "TRACK" || normalized === "TRACE" && method !== "TRACE") return /* @__PURE__ */ new TypeError(`'${method}' HTTP method is unsupported.`); }; const readBodyWithFastPath = (request, method, fromBuffer) => { if (request[bodyConsumedDirectlyKey]) return rejectBodyUnusable(); const methodName = request.method; if (methodName === "GET" || methodName === "HEAD") return request[getRequestCache]()[method](); const methodValidationError = validateDirectReadMethod(methodName); if (methodValidationError) return Promise.reject(methodValidationError); if (request[requestCache]) { if (methodName !== "TRACE") return request[requestCache][method](); } const alreadyUsedError = consumeBodyDirectOnce(request); if (alreadyUsedError) return alreadyUsedError; const raw = readRawBodyIfAvailable(request); if (raw) { const result = Promise.resolve(fromBuffer(raw, request)); request[bodyBufferKey] = void 0; return result; } return readBodyDirect(request).then((buf) => { const result = fromBuffer(buf, request); request[bodyBufferKey] = void 0; return result; }); }; const readRawBodyIfAvailable = (request) => { const incoming = request[incomingKey]; if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) return incoming.rawBody; }; const readBodyDirect = (request) => { if (request[bodyBufferKey]) return Promise.resolve(request[bodyBufferKey]); if (request[bodyReadPromiseKey]) return request[bodyReadPromiseKey]; const incoming = request[incomingKey]; if (node_stream.Readable.isDisturbed(incoming)) return rejectBodyUnusable(); const promise = new Promise((resolve, reject) => { const chunks = []; let settled = false; const finish = (callback) => { if (settled) return; settled = true; cleanup(); callback(); }; const onData = (chunk) => { chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); }; const onEnd = () => { finish(() => { const buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks); request[bodyBufferKey] = buffer; resolve(buffer); }); }; const onError = (error) => { finish(() => { reject(error); }); }; const onClose = () => { if (incoming.readableEnded) { onEnd(); return; } finish(() => { if (incoming.errored) { reject(incoming.errored); return; } const reason = request[abortReasonKey]; if (reason !== void 0) { reject(reason instanceof Error ? reason : new Error(String(reason))); return; } reject(/* @__PURE__ */ new Error("Client connection prematurely closed.")); }); }; const cleanup = () => { incoming.off("data", onData); incoming.off("end", onEnd); incoming.off("error", onError); incoming.off("close", onClose); request[bodyReadPromiseKey] = void 0; }; incoming.on("data", onData); incoming.on("end", onEnd); incoming.on("error", onError); incoming.on("close", onClose); queueMicrotask(() => { if (settled) return; if (incoming.readableEnded) onEnd(); else if (incoming.errored) onError(incoming.errored); else if (incoming.destroyed) onClose(); }); }); request[bodyReadPromiseKey] = promise; return promise; }; const requestPrototype = { get method() { return this[methodKey]; }, get url() { return this[urlKey]; }, get headers() { return this[headersKey] ||= newHeadersFromIncoming(this[incomingKey]); }, [abortRequest](reason) { if (this[abortReasonKey] === void 0) this[abortReasonKey] = reason; const abortController = this[abortControllerKey]; if (abortController && !abortController.signal.aborted) abortController.abort(reason); }, [getAbortController]() { this[abortControllerKey] ||= new AbortController(); if (this[abortReasonKey] !== void 0 && !this[abortControllerKey].signal.aborted) this[abortControllerKey].abort(this[abortReasonKey]); return this[abortControllerKey]; }, [getRequestCache]() { const abortController = this[getAbortController](); if (this[requestCache]) return this[requestCache]; const method = this.method; if (this[bodyConsumedDirectlyKey] && !(method === "GET" || method === "HEAD")) { this[bodyBufferKey] = void 0; const init = { method: method === "TRACE" ? "GET" : method, headers: this.headers, signal: abortController.signal }; if (method !== "TRACE") { init.body = new ReadableStream({ start(c) { c.close(); } }); init.duplex = "half"; } const req = new Request$1(this[urlKey], init); if (method === "TRACE") Object.defineProperty(req, "method", { get() { return "TRACE"; } }); return this[requestCache] = req; } return this[requestCache] = newRequestFromIncoming(this.method, this[urlKey], this.headers, this[incomingKey], abortController); }, get body() { if (!this[bodyConsumedDirectlyKey]) return this[getRequestCache]().body; const request = this[getRequestCache](); if (!this[bodyLockReaderKey] && request.body) this[bodyLockReaderKey] = request.body.getReader(); return request.body; }, get bodyUsed() { if (this[bodyConsumedDirectlyKey]) return true; if (this[requestCache]) return this[requestCache].bodyUsed; return false; } }; Object.defineProperty(requestPrototype, "signal", { get() { return this[getAbortController]().signal; } }); [ "cache", "credentials", "destination", "integrity", "mode", "redirect", "referrer", "referrerPolicy", "keepalive" ].forEach((k) => { Object.defineProperty(requestPrototype, k, { get() { return this[getRequestCache]()[k]; } }); }); ["clone", "formData"].forEach((k) => { Object.defineProperty(requestPrototype, k, { value: function() { if (this[bodyConsumedDirectlyKey]) { if (k === "clone") throw newBodyUnusableError(); return rejectBodyUnusable(); } return this[getRequestCache]()[k](); } }); }); Object.defineProperty(requestPrototype, "text", { value: function() { return readBodyWithFastPath(this, "text", (buf) => textDecoder.decode(buf)); } }); Object.defineProperty(requestPrototype, "arrayBuffer", { value: function() { return readBodyWithFastPath(this, "arrayBuffer", (buf) => toArrayBuffer(buf)); } }); Object.defineProperty(requestPrototype, "blob", { value: function() { return readBodyWithFastPath(this, "blob", (buf, request) => { const type = contentType(request); const init = type ? { headers: { "content-type": type } } : void 0; return new Response(buf, init).blob(); }); } }); Object.defineProperty(requestPrototype, "json", { value: function() { if (this[bodyConsumedDirectlyKey]) return rejectBodyUnusable(); return this.text().then(JSON.parse); } }); Object.defineProperty(requestPrototype, Symbol.for("nodejs.util.inspect.custom"), { value: function(depth, options, inspectFn) { return `Request (lightweight) ${inspectFn({ method: this.method, url: this.url, headers: this.headers, nativeRequest: this[requestCache] }, { ...options, depth: depth == null ? null : depth - 1 })}`; } }); Object.setPrototypeOf(requestPrototype, Request$1.prototype); const newRequest = (incoming, defaultHostname) => { const req = Object.create(requestPrototype); req[incomingKey] = incoming; req[methodKey] = normalizeIncomingMethod(incoming.method); const incomingUrl = incoming.url || ""; if (incomingUrl[0] !== "/" && (incomingUrl.startsWith("http://") || incomingUrl.startsWith("https://"))) { if (incoming instanceof node_http2.Http2ServerRequest) throw new RequestError("Absolute URL for :path is not allowed in HTTP/2"); try { req[urlKey] = new URL(incomingUrl).href; } catch (e) { throw new RequestError("Invalid absolute URL", { cause: e }); } return req; } const host = (incoming instanceof node_http2.Http2ServerRequest ? incoming.authority : incoming.headers.host) || defaultHostname; if (!host) throw new RequestError("Missing host header"); let scheme; if (incoming instanceof node_http2.Http2ServerRequest) { scheme = incoming.scheme; if (!(scheme === "http" || scheme === "https")) throw new RequestError("Unsupported scheme"); } else scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http"; try { req[urlKey] = buildUrl(scheme, host, incomingUrl); } catch (e) { if (e instanceof RequestError) throw e; else throw new RequestError("Invalid URL", { cause: e }); } return req; }; //#endregion //#region src/response.ts const defaultContentType = "text/plain; charset=UTF-8"; const responseCache = Symbol("responseCache"); const getResponseCache = Symbol("getResponseCache"); const cacheKey = Symbol("cache"); const GlobalResponse = global.Response; var Response$1 = class Response$1 { #body; #init; [getResponseCache]() { delete this[cacheKey]; return this[responseCache] ||= new GlobalResponse(this.#body, this.#init); } constructor(body, init) { let headers; this.#body = body; if (init instanceof Response$1) { const cachedGlobalResponse = init[responseCache]; if (cachedGlobalResponse) { this.#init = cachedGlobalResponse; this[getResponseCache](); return; } else { this.#init = init.#init; headers = new Headers(init.#init.headers); } } else this.#init = init; if (body == null || typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey] = [ init?.status || 200, body ?? null, headers || init?.headers ]; } get headers() { const cache = this[cacheKey]; if (cache) { if (!(cache[2] instanceof Headers)) cache[2] = new Headers(cache[2] || (cache[1] === null ? void 0 : { "content-type": defaultContentType })); return cache[2]; } return this[getResponseCache]().headers; } get status() { return this[cacheKey]?.[0] ?? this[getResponseCache]().status; } get ok() { const status = this.status; return status >= 200 && status < 300; } }; [ "body", "bodyUsed", "redirected", "statusText", "trailers", "type", "url" ].forEach((k) => { Object.defineProperty(Response$1.prototype, k, { get() { return this[getResponseCache]()[k]; } }); }); [ "arrayBuffer", "blob", "clone", "formData", "json", "text" ].forEach((k) => { Object.defineProperty(Response$1.prototype, k, { value: function() { return this[getResponseCache]()[k](); } }); }); Object.defineProperty(Response$1.prototype, Symbol.for("nodejs.util.inspect.custom"), { value: function(depth, options, inspectFn) { return `Response (lightweight) ${inspectFn({ status: this.status, headers: this.headers, ok: this.ok, nativeResponse: this[responseCache] }, { ...options, depth: depth == null ? null : depth - 1 })}`; } }); Object.setPrototypeOf(Response$1, GlobalResponse); Object.setPrototypeOf(Response$1.prototype, GlobalResponse.prototype); const validRedirectUrl = /^https?:\/\/[!#-;=?-[\]_a-z~A-Z]+$/; const parseRedirectUrl = (url) => { if (url instanceof URL) return url.href; if (validRedirectUrl.test(url)) return url; return new URL(url).href; }; const validRedirectStatuses = new Set([ 301, 302, 303, 307, 308 ]); Object.defineProperty(Response$1, "redirect", { value: function redirect(url, status = 302) { if (!validRedirectStatuses.has(status)) throw new RangeError("Invalid status code"); return new Response$1(null, { status, headers: { location: parseRedirectUrl(url) } }); }, writable: true, configurable: true }); Object.defineProperty(Response$1, "json", { value: function json(data, init) { const body = JSON.stringify(data); if (body === void 0) throw new TypeError("The data is not JSON serializable"); const initHeaders = init?.headers; let headers; if (initHeaders) { headers = new Headers(initHeaders); if (!headers.has("content-type")) headers.set("content-type", "application/json"); } else headers = { "content-type": "application/json" }; return new Response$1(body, { status: init?.status ?? 200, statusText: init?.statusText, headers }); }, writable: true, configurable: true }); //#endregion //#region src/utils.ts async function readWithoutBlocking(readPromise) { return Promise.race([readPromise, Promise.resolve().then(() => Promise.resolve(void 0))]); } function writeFromReadableStreamDefaultReader(reader, writable, currentReadPromise) { const cancel = (error) => { reader.cancel(error).catch(() => {}); }; writable.on("close", cancel); writable.on("error", cancel); (currentReadPromise ?? reader.read()).then(flow, handleStreamError); return reader.closed.finally(() => { writable.off("close", cancel); writable.off("error", cancel); }); function handleStreamError(error) { if (error) writable.destroy(error); } function onDrain() { reader.read().then(flow, handleStreamError); } function flow({ done, value }) { try { if (done) writable.end(); else if (!writable.write(value)) writable.once("drain", onDrain); else return reader.read().then(flow, handleStreamError); } catch (e) { handleStreamError(e); } } } function writeFromReadableStream(stream, writable) { if (stream.locked) throw new TypeError("ReadableStream is locked."); else if (writable.destroyed) return; return writeFromReadableStreamDefaultReader(stream.getReader(), writable); } const buildOutgoingHttpHeaders = (headers, defaultContentType) => { const res = {}; if (!(headers instanceof Headers)) headers = new Headers(headers ?? void 0); if (headers.has("set-cookie")) { const cookies = []; for (const [k, v] of headers) if (k === "set-cookie") cookies.push(v); else res[k] = v; if (cookies.length > 0) res["set-cookie"] = cookies; } else for (const [k, v] of headers) res[k] = v; if (defaultContentType) res["content-type"] ??= defaultContentType; return res; }; //#endregion //#region src/listener.ts const outgoingEnded = Symbol("outgoingEnded"); const incomingDraining = Symbol("incomingDraining"); const DRAIN_TIMEOUT_MS = 500; const MAX_DRAIN_BYTES = 64 * 1024 * 1024; const drainIncoming = (incoming) => { const incomingWithDrainState = incoming; if (incoming.destroyed || incomingWithDrainState[incomingDraining]) return; incomingWithDrainState[incomingDraining] = true; if (incoming instanceof node_http2.Http2ServerRequest) { try { incoming.stream?.close?.(node_http2.constants.NGHTTP2_NO_ERROR); } catch {} return; } let bytesRead = 0; const cleanup = () => { clearTimeout(timer); incoming.off("data", onData); incoming.off("end", cleanup); incoming.off("error", cleanup); }; const forceClose = () => { cleanup(); const socket = incoming.socket; if (socket && !socket.destroyed) socket.destroySoon(); }; const timer = setTimeout(forceClose, DRAIN_TIMEOUT_MS); timer.unref?.(); const onData = (chunk) => { bytesRead += chunk.length; if (bytesRead > MAX_DRAIN_BYTES) forceClose(); }; incoming.on("data", onData); incoming.on("end", cleanup); incoming.on("error", cleanup); incoming.resume(); }; const makeCloseHandler = (req, incoming, outgoing, needsBodyCleanup) => () => { if (incoming.errored) req[abortRequest](incoming.errored.toString()); else if (!outgoing.writableFinished) req[abortRequest]("Client connection prematurely closed."); if (needsBodyCleanup && !incoming.readableEnded) setTimeout(() => { if (!incoming.readableEnded) setTimeout(() => { drainIncoming(incoming); }); }); }; const isImmediateCacheableResponse = (res) => { if (!(cacheKey in res)) return false; const body = res[cacheKey][1]; return body === null || typeof body === "string" || body instanceof Uint8Array; }; const handleRequestError = () => new Response(null, { status: 400 }); const handleFetchError = (e) => new Response(null, { status: e instanceof Error && (e.name === "TimeoutError" || e.constructor.name === "TimeoutError") ? 504 : 500 }); const handleResponseError = (e, outgoing) => { const err = e instanceof Error ? e : new Error("unknown error", { cause: e }); if (err.code === "ERR_STREAM_PREMATURE_CLOSE") console.info("The user aborted a request."); else { console.error(e); if (!outgoing.headersSent) outgoing.writeHead(500, { "Content-Type": "text/plain" }); outgoing.end(`Error: ${err.message}`); outgoing.destroy(err); } }; const flushHeaders = (outgoing) => { if ("flushHeaders" in outgoing && outgoing.writable) outgoing.flushHeaders(); }; const responseViaCache = async (res, outgoing) => { let [status, body, header] = res[cacheKey]; if (!header) { if (body === null) { outgoing.writeHead(status); outgoing.end(); } else if (typeof body === "string") { outgoing.writeHead(status, { "Content-Type": defaultContentType, "Content-Length": Buffer.byteLength(body) }); outgoing.end(body); } else if (body instanceof Uint8Array) { outgoing.writeHead(status, { "Content-Type": defaultContentType, "Content-Length": body.byteLength }); outgoing.end(body); } else if (body instanceof Blob) { outgoing.writeHead(status, { "Content-Type": defaultContentType, "Content-Length": body.size }); outgoing.end(new Uint8Array(await body.arrayBuffer())); } else { outgoing.writeHead(status, { "Content-Type": defaultContentType }); flushHeaders(outgoing); await writeFromReadableStream(body, outgoing)?.catch((e) => handleResponseError(e, outgoing)); } outgoing[outgoingEnded]?.(); return; } let hasContentLength = false; if (header instanceof Headers) { hasContentLength = header.has("content-length"); header = buildOutgoingHttpHeaders(header, body === null ? void 0 : defaultContentType); } else if (Array.isArray(header)) { const headerObj = new Headers(header); hasContentLength = headerObj.has("content-length"); header = buildOutgoingHttpHeaders(headerObj, body === null ? void 0 : defaultContentType); } else for (const key in header) if (key.length === 14 && key.toLowerCase() === "content-length") { hasContentLength = true; break; } if (!hasContentLength) { if (typeof body === "string") header["Content-Length"] = Buffer.byteLength(body); else if (body instanceof Uint8Array) header["Content-Length"] = body.byteLength; else if (body instanceof Blob) header["Content-Length"] = body.size; } outgoing.writeHead(status, header); if (body == null) outgoing.end(); else if (typeof body === "string" || body instanceof Uint8Array) outgoing.end(body); else if (body instanceof Blob) outgoing.end(new Uint8Array(await body.arrayBuffer())); else { flushHeaders(outgoing); await writeFromReadableStream(body, outgoing)?.catch((e) => handleResponseError(e, outgoing)); } outgoing[outgoingEnded]?.(); }; const isPromise = (res) => typeof res.then === "function"; const responseViaResponseObject = async (res, outgoing, options = {}) => { if (isPromise(res)) if (options.errorHandler) try { res = await res; } catch (err) { const errRes = await options.errorHandler(err); if (!errRes) return; res = errRes; } else res = await res.catch(handleFetchError); if (cacheKey in res) return responseViaCache(res, outgoing); const resHeaderRecord = buildOutgoingHttpHeaders(res.headers, res.body === null ? void 0 : defaultContentType); if (res.body) { const reader = res.body.getReader(); const values = []; let done = false; let currentReadPromise = void 0; if (resHeaderRecord["transfer-encoding"] !== "chunked") { let maxReadCount = 2; for (let i = 0; i < maxReadCount; i++) { currentReadPromise ||= reader.read(); const chunk = await readWithoutBlocking(currentReadPromise).catch((e) => { console.error(e); done = true; }); if (!chunk) { if (i === 1) { await new Promise((resolve) => setTimeout(resolve)); maxReadCount = 3; continue; } break; } currentReadPromise = void 0; if (chunk.value) values.push(chunk.value); if (chunk.done) { done = true; break; } } if (done && !("content-length" in resHeaderRecord)) resHeaderRecord["content-length"] = values.reduce((acc, value) => acc + value.length, 0); } outgoing.writeHead(res.status, resHeaderRecord); values.forEach((value) => { outgoing.write(value); }); if (done) outgoing.end(); else { if (values.length === 0) flushHeaders(outgoing); await writeFromReadableStreamDefaultReader(reader, outgoing, currentReadPromise); } } else if (resHeaderRecord[require_constants.X_ALREADY_SENT]) {} else { outgoing.writeHead(res.status, resHeaderRecord); outgoing.end(); } outgoing[outgoingEnded]?.(); }; const getRequestListener = (fetchCallback, options = {}) => { const autoCleanupIncoming = options.autoCleanupIncoming ?? true; if (options.overrideGlobalObjects !== false && global.Request !== Request$1) { Object.defineProperty(global, "Request", { value: Request$1 }); Object.defineProperty(global, "Response", { value: Response$1 }); } return async (incoming, outgoing) => { let res, req; let needsBodyCleanup = false; let closeHandlerAttached = false; const ensureCloseHandler = () => { if (!req || closeHandlerAttached) return; closeHandlerAttached = true; outgoing.on("close", makeCloseHandler(req, incoming, outgoing, needsBodyCleanup)); }; try { req = newRequest(incoming, options.hostname); needsBodyCleanup = autoCleanupIncoming && !(incoming.method === "GET" || incoming.method === "HEAD"); if (needsBodyCleanup) { incoming[wrapBodyStream] = true; if (incoming instanceof node_http2.Http2ServerRequest) outgoing[outgoingEnded] = () => { if (!incoming.readableEnded) setTimeout(() => { if (!incoming.readableEnded) setTimeout(() => { incoming.destroy(); outgoing.destroy(); }); }); }; } res = fetchCallback(req, { incoming, outgoing }); if (!isPromise(res) && isImmediateCacheableResponse(res)) { if (needsBodyCleanup && !incoming.readableEnded) outgoing.once("finish", () => { if (!incoming.readableEnded) drainIncoming(incoming); }); return responseViaCache(res, outgoing); } ensureCloseHandler(); } catch (e) { if (!res) if (options.errorHandler) { ensureCloseHandler(); res = await options.errorHandler(req ? e : toRequestError(e)); if (!res) return; } else if (!req) res = handleRequestError(); else res = handleFetchError(e); else return handleResponseError(e, outgoing); } try { return await responseViaResponseObject(res, outgoing, options); } catch (e) { return handleResponseError(e, outgoing); } }; }; //#endregion //#region src/websocket.ts /** * @link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent */ const CloseEvent = globalThis.CloseEvent ?? class extends Event { #eventInitDict; constructor(type, eventInitDict = {}) { super(type, eventInitDict); this.#eventInitDict = eventInitDict; } get wasClean() { return this.#eventInitDict.wasClean ?? false; } get code() { return this.#eventInitDict.code ?? 0; } get reason() { return this.#eventInitDict.reason ?? ""; } }; const generateConnectionSymbol = () => Symbol("connection"); const CONNECTION_SYMBOL_KEY = Symbol("CONNECTION_SYMBOL_KEY"); const WAIT_FOR_WEBSOCKET_SYMBOL = Symbol("WAIT_FOR_WEBSOCKET_SYMBOL"); const rejectUpgradeRequest = (socket, status) => { socket.end(`HTTP/1.1 ${status.toString()} ${node_http.STATUS_CODES[status] ?? ""}\r\nConnection: close\r Content-Length: 0\r \r `); }; const createUpgradeRequest = (request) => { const protocol = request.socket.encrypted ? "https" : "http"; const url = new URL(request.url ?? "/", `${protocol}://${request.headers.host ?? "localhost"}`); const headers = new Headers(); for (const key in request.headers) { const value = request.headers[key]; if (!value) continue; headers.append(key, Array.isArray(value) ? value[0] : value); } return new Request(url, { headers }); }; const setupWebSocket = (options) => { const { server, fetchCallback, wss } = options; const waiterMap = /* @__PURE__ */ new Map(); wss.on("connection", (ws, request) => { const waiter = waiterMap.get(request); if (waiter) { waiter.resolve(ws); waiterMap.delete(request); } }); const waitForWebSocket = (request, connectionSymbol) => { return new Promise((resolve) => { waiterMap.set(request, { resolve, connectionSymbol }); }); }; server.on("upgrade", async (request, socket, head) => { if (request.headers.upgrade?.toLowerCase() !== "websocket") return; const env = { incoming: request, outgoing: void 0, wss, [WAIT_FOR_WEBSOCKET_SYMBOL]: waitForWebSocket }; let status = 400; try { const response = await fetchCallback(createUpgradeRequest(request), env); if (response instanceof Response) status = response.status; } catch { if (server.listenerCount("upgrade") === 1) rejectUpgradeRequest(socket, 500); return; } const waiter = waiterMap.get(request); if (!waiter || waiter.connectionSymbol !== env[CONNECTION_SYMBOL_KEY]) { waiterMap.delete(request); if (server.listenerCount("upgrade") === 1) rejectUpgradeRequest(socket, status); return; } wss.handleUpgrade(request, socket, head, (ws) => { wss.emit("connection", ws, request); }); }); server.on("close", () => { wss.close(); }); }; const upgradeWebSocket = (0, hono_ws.defineWebSocketHelper)(async (c, events, options) => { if (c.req.header("upgrade")?.toLowerCase() !== "websocket") return; const env = c.env; const waitForWebSocket = env[WAIT_FOR_WEBSOCKET_SYMBOL]; if (!waitForWebSocket || !env.incoming) return new Response(null, { status: 500 }); const connectionSymbol = generateConnectionSymbol(); env[CONNECTION_SYMBOL_KEY] = connectionSymbol; (async () => { const ws = await waitForWebSocket(env.incoming, connectionSymbol); const messagesReceivedInStarting = []; const bufferMessage = (data, isBinary) => { messagesReceivedInStarting.push([data, isBinary]); }; ws.on("message", bufferMessage); const ctx = { binaryType: "arraybuffer", close(code, reason) { ws.close(code, reason); }, protocol: ws.protocol, raw: ws, get readyState() { return ws.readyState; }, send(source, opts) { ws.send(source, { compress: opts?.compress }); }, url: new URL(c.req.url) }; try { events?.onOpen?.(new Event("open"), ctx); } catch (e) { (options?.onError ?? console.error)(e); } const handleMessage = (data, isBinary) => { const datas = Array.isArray(data) ? data : [data]; for (const data of datas) try { 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); } catch (e) { (options?.onError ?? console.error)(e); } }; ws.off("message", bufferMessage); for (const message of messagesReceivedInStarting) handleMessage(...message); ws.on("message", (data, isBinary) => { handleMessage(data, isBinary); }); ws.on("close", (code, reason) => { try { events?.onClose?.(new CloseEvent("close", { code, reason: reason.toString() }), ctx); } catch (e) { (options?.onError ?? console.error)(e); } }); ws.on("error", (error) => { try { events?.onError?.(new ErrorEvent("error", { error }), ctx); } catch (e) { (options?.onError ?? console.error)(e); } }); })(); return new Response(); }); //#endregion //#region src/server.ts const createAdaptorServer = (options) => { const fetchCallback = options.fetch; const requestListener = getRequestListener(fetchCallback, { hostname: options.hostname, overrideGlobalObjects: options.overrideGlobalObjects, autoCleanupIncoming: options.autoCleanupIncoming }); const server = (options.createServer || node_http.createServer)(options.serverOptions || {}, requestListener); if (options.websocket && options.websocket.server) { if (options.websocket.server.options.noServer !== true) throw new Error("WebSocket server must be created with { noServer: true } option"); setupWebSocket({ server, fetchCallback, wss: options.websocket.server }); } return server; }; const serve = (options, listeningListener) => { const server = createAdaptorServer(options); server.listen(options?.port ?? 3e3, options.hostname, () => { const serverInfo = server.address(); listeningListener && listeningListener(serverInfo); }); return server; }; //#endregion exports.RequestError = RequestError; exports.createAdaptorServer = createAdaptorServer; exports.getRequestListener = getRequestListener; exports.serve = serve; exports.upgradeWebSocket = upgradeWebSocket;