1211 lines
40 KiB
JavaScript
1211 lines
40 KiB
JavaScript
import nodeHTTP from "node:http";
|
|
import { PassThrough, Readable } from "node:stream";
|
|
import { pipeline } from "node:stream/promises";
|
|
import nodeHTTPS from "node:https";
|
|
import nodeHTTP2 from "node:http2";
|
|
//#region node_modules/srvx/dist/_chunks/_url.mjs
|
|
function lazyInherit(target, source, sourceKey) {
|
|
for (const key of [...Object.getOwnPropertyNames(source), ...Object.getOwnPropertySymbols(source)]) {
|
|
if (key === "constructor") continue;
|
|
const targetDesc = Object.getOwnPropertyDescriptor(target, key);
|
|
const desc = Object.getOwnPropertyDescriptor(source, key);
|
|
let modified = false;
|
|
if (desc.get) {
|
|
modified = true;
|
|
desc.get = targetDesc?.get || function() {
|
|
return this[sourceKey][key];
|
|
};
|
|
}
|
|
if (desc.set) {
|
|
modified = true;
|
|
desc.set = targetDesc?.set || function(value) {
|
|
this[sourceKey][key] = value;
|
|
};
|
|
}
|
|
if (!targetDesc?.value && typeof desc.value === "function") {
|
|
modified = true;
|
|
desc.value = function(...args) {
|
|
return this[sourceKey][key](...args);
|
|
};
|
|
}
|
|
if (modified) Object.defineProperty(target, key, desc);
|
|
}
|
|
}
|
|
var _needsNormRE = /(?:(?:^|\/)(?:\.|\.\.|%2e|%2e\.|\.%2e|%2e%2e)(?:\/|$))|[\\^\x80-\uffff]/i;
|
|
/**
|
|
* URL wrapper with fast paths to access to the following props:
|
|
*
|
|
* - `url.pathname`
|
|
* - `url.search`
|
|
* - `url.searchParams`
|
|
* - `url.protocol`
|
|
*
|
|
* **NOTES:**
|
|
*
|
|
* - It is assumed that the input URL is **already encoded** and formatted from an HTTP request and contains no hash.
|
|
* - Triggering the setters or getters on other props will deoptimize to full URL parsing.
|
|
* - Changes to `searchParams` will be discarded as we don't track them.
|
|
*/
|
|
var FastURL = /* @__PURE__ */ (() => {
|
|
const NativeURL = globalThis.URL;
|
|
const FastURL = class URL {
|
|
#url;
|
|
#href;
|
|
#protocol;
|
|
#host;
|
|
#pathname;
|
|
#search;
|
|
#searchParams;
|
|
#pos;
|
|
constructor(url) {
|
|
if (typeof url === "string") if (url[0] === "/") this.#href = url;
|
|
else this.#url = new NativeURL(url);
|
|
else if (_needsNormRE.test(url.pathname)) this.#url = new NativeURL(`${url.protocol || "http:"}//${url.host || "localhost"}${url.pathname}${url.search || ""}`);
|
|
else {
|
|
this.#protocol = url.protocol;
|
|
this.#host = url.host;
|
|
this.#pathname = url.pathname;
|
|
this.#search = url.search;
|
|
}
|
|
}
|
|
static [Symbol.hasInstance](val) {
|
|
return val instanceof NativeURL;
|
|
}
|
|
get _url() {
|
|
if (this.#url) return this.#url;
|
|
this.#url = new NativeURL(this.href);
|
|
this.#href = void 0;
|
|
this.#protocol = void 0;
|
|
this.#host = void 0;
|
|
this.#pathname = void 0;
|
|
this.#search = void 0;
|
|
this.#searchParams = void 0;
|
|
this.#pos = void 0;
|
|
return this.#url;
|
|
}
|
|
get href() {
|
|
if (this.#url) return this.#url.href;
|
|
if (!this.#href) this.#href = `${this.#protocol || "http:"}//${this.#host || "localhost"}${this.#pathname || "/"}${this.#search || ""}`;
|
|
return this.#href;
|
|
}
|
|
#getPos() {
|
|
if (!this.#pos) {
|
|
const url = this.href;
|
|
const protoIndex = url.indexOf("://");
|
|
const pathnameIndex = protoIndex === -1 ? -1 : url.indexOf("/", protoIndex + 4);
|
|
this.#pos = [
|
|
protoIndex,
|
|
pathnameIndex,
|
|
pathnameIndex === -1 ? -1 : url.indexOf("?", pathnameIndex)
|
|
];
|
|
}
|
|
return this.#pos;
|
|
}
|
|
get pathname() {
|
|
if (this.#url) return this.#url.pathname;
|
|
if (this.#pathname === void 0) {
|
|
const [, pathnameIndex, queryIndex] = this.#getPos();
|
|
if (pathnameIndex === -1) return this._url.pathname;
|
|
this.#pathname = this.href.slice(pathnameIndex, queryIndex === -1 ? void 0 : queryIndex);
|
|
}
|
|
return this.#pathname;
|
|
}
|
|
get search() {
|
|
if (this.#url) return this.#url.search;
|
|
if (this.#search === void 0) {
|
|
const [, pathnameIndex, queryIndex] = this.#getPos();
|
|
if (pathnameIndex === -1) return this._url.search;
|
|
const url = this.href;
|
|
this.#search = queryIndex === -1 || queryIndex === url.length - 1 ? "" : url.slice(queryIndex);
|
|
}
|
|
return this.#search;
|
|
}
|
|
get searchParams() {
|
|
if (this.#url) return this.#url.searchParams;
|
|
if (!this.#searchParams) this.#searchParams = new URLSearchParams(this.search);
|
|
return this.#searchParams;
|
|
}
|
|
get protocol() {
|
|
if (this.#url) return this.#url.protocol;
|
|
if (this.#protocol === void 0) {
|
|
const [protocolIndex] = this.#getPos();
|
|
if (protocolIndex === -1) return this._url.protocol;
|
|
this.#protocol = this.href.slice(0, protocolIndex + 1);
|
|
}
|
|
return this.#protocol;
|
|
}
|
|
toString() {
|
|
return this.href;
|
|
}
|
|
toJSON() {
|
|
return this.href;
|
|
}
|
|
};
|
|
lazyInherit(FastURL.prototype, NativeURL.prototype, "_url");
|
|
Object.setPrototypeOf(FastURL.prototype, NativeURL.prototype);
|
|
Object.setPrototypeOf(FastURL, NativeURL);
|
|
return FastURL;
|
|
})();
|
|
//#endregion
|
|
//#region node_modules/srvx/dist/_chunks/_utils2.mjs
|
|
function resolvePortAndHost(opts) {
|
|
const _port = opts.port ?? globalThis.process?.env.PORT ?? 3e3;
|
|
const port = typeof _port === "number" ? _port : Number.parseInt(_port, 10);
|
|
if (port < 0 || port > 65535) throw new RangeError(`Port must be between 0 and 65535 (got "${port}").`);
|
|
return {
|
|
port,
|
|
hostname: opts.hostname ?? globalThis.process?.env.HOST
|
|
};
|
|
}
|
|
function fmtURL(host, port, secure) {
|
|
if (!host || !port) return;
|
|
if (host.includes(":")) host = `[${host}]`;
|
|
return `http${secure ? "s" : ""}://${host}:${port}/`;
|
|
}
|
|
function printListening(opts, url) {
|
|
if (!url || (opts.silent ?? globalThis.process?.env?.TEST)) return;
|
|
let additionalInfo = "";
|
|
try {
|
|
const _url = new URL(url);
|
|
if (_url.hostname === "[::]" || _url.hostname === "0.0.0.0") {
|
|
_url.hostname = "localhost";
|
|
url = _url.href;
|
|
additionalInfo = " (all interfaces)";
|
|
}
|
|
} catch {}
|
|
let listeningOn = `➜ Listening on:`;
|
|
if (globalThis.process.stdout?.isTTY) {
|
|
listeningOn = `\u001B[32m${listeningOn}\u001B[0m`;
|
|
url = `\u001B[36m${url}\u001B[0m`;
|
|
additionalInfo = `\u001B[2m${additionalInfo}\u001B[0m`;
|
|
}
|
|
console.log(`${listeningOn} ${url}${additionalInfo}`);
|
|
}
|
|
function resolveTLSOptions(opts) {
|
|
if (!opts.tls || opts.protocol === "http") return;
|
|
const cert = resolveCertOrKey(opts.tls.cert);
|
|
const key = resolveCertOrKey(opts.tls.key);
|
|
if (!cert && !key) {
|
|
if (opts.protocol === "https") throw new TypeError("TLS `cert` and `key` must be provided for `https` protocol.");
|
|
return;
|
|
}
|
|
if (!cert || !key) throw new TypeError("TLS `cert` and `key` must be provided together.");
|
|
return {
|
|
cert,
|
|
key,
|
|
passphrase: opts.tls.passphrase
|
|
};
|
|
}
|
|
function resolveCertOrKey(value) {
|
|
if (!value) return;
|
|
if (typeof value !== "string") throw new TypeError("TLS certificate and key must be strings in PEM format or file paths.");
|
|
if (value.startsWith("-----BEGIN ")) return value;
|
|
const { readFileSync } = process.getBuiltinModule("node:fs");
|
|
return readFileSync(value, "utf8");
|
|
}
|
|
function createWaitUntil() {
|
|
const promises = /* @__PURE__ */ new Set();
|
|
return {
|
|
waitUntil: (promise) => {
|
|
if (typeof promise?.then !== "function") return;
|
|
promises.add(Promise.resolve(promise).catch(console.error).finally(() => {
|
|
promises.delete(promise);
|
|
}));
|
|
},
|
|
wait: () => {
|
|
return Promise.all(promises);
|
|
}
|
|
};
|
|
}
|
|
//#endregion
|
|
//#region node_modules/srvx/dist/_chunks/_utils.mjs
|
|
var noColor = /* @__PURE__ */ (() => {
|
|
const env = globalThis.process?.env ?? {};
|
|
return env.NO_COLOR === "1" || env.TERM === "dumb";
|
|
})();
|
|
var _c = (c, r = 39) => (t) => noColor ? t : `\u001B[${c}m${t}\u001B[${r}m`;
|
|
var bold = /* @__PURE__ */ _c(1, 22);
|
|
var red = /* @__PURE__ */ _c(31);
|
|
var green = /* @__PURE__ */ _c(32);
|
|
var gray = /* @__PURE__ */ _c(90);
|
|
//#endregion
|
|
//#region node_modules/srvx/dist/_chunks/_plugins.mjs
|
|
function wrapFetch(server) {
|
|
const fetchHandler = server.options.fetch;
|
|
const middleware = server.options.middleware || [];
|
|
return middleware.length === 0 ? fetchHandler : (request) => callMiddleware$1(request, fetchHandler, middleware, 0);
|
|
}
|
|
function callMiddleware$1(request, fetchHandler, middleware, index) {
|
|
if (index === middleware.length) return fetchHandler(request);
|
|
return middleware[index](request, () => callMiddleware$1(request, fetchHandler, middleware, index + 1));
|
|
}
|
|
var errorPlugin = (server) => {
|
|
const errorHandler = server.options.error;
|
|
if (!errorHandler) return;
|
|
server.options.middleware.unshift((_req, next) => {
|
|
try {
|
|
const res = next();
|
|
return res instanceof Promise ? res.catch((error) => errorHandler(error)) : res;
|
|
} catch (error) {
|
|
return errorHandler(error);
|
|
}
|
|
});
|
|
};
|
|
var gracefulShutdownPlugin = (server) => {
|
|
const config = server.options?.gracefulShutdown;
|
|
if (!globalThis.process?.on || config === false || config === void 0 && (process.env.CI || process.env.TEST)) return;
|
|
const gracefulTimeout = config === true || !config?.gracefulTimeout ? Number.parseInt(process.env.SERVER_SHUTDOWN_TIMEOUT || "") || 5 : config.gracefulTimeout;
|
|
let isClosing = false;
|
|
let isClosed = false;
|
|
const w = server.options.silent ? () => {} : process.stderr.write.bind(process.stderr);
|
|
const forceClose = async () => {
|
|
if (isClosed) return;
|
|
w(red("\x1B[2K\rForcibly closing connections...\n"));
|
|
isClosed = true;
|
|
await server.close(true);
|
|
};
|
|
const shutdown = async () => {
|
|
if (isClosing || isClosed) return;
|
|
setTimeout(() => {
|
|
globalThis.process.once("SIGINT", forceClose);
|
|
}, 100);
|
|
isClosing = true;
|
|
const closePromise = server.close();
|
|
for (let remaining = gracefulTimeout; remaining > 0; remaining--) {
|
|
w(gray(`\rStopping server gracefully (${remaining}s)... Press ${bold("Ctrl+C")} again to force close.`));
|
|
if (await Promise.race([closePromise.then(() => true), new Promise((r) => setTimeout(() => r(false), 1e3))])) {
|
|
w("\x1B[2K\r" + green("Server closed successfully.\n"));
|
|
isClosed = true;
|
|
return;
|
|
}
|
|
}
|
|
w("\x1B[2K\rGraceful shutdown timed out.\n");
|
|
await forceClose();
|
|
};
|
|
for (const sig of ["SIGINT", "SIGTERM"]) globalThis.process.on(sig, shutdown);
|
|
};
|
|
//#endregion
|
|
//#region node_modules/srvx/dist/adapters/node.mjs
|
|
async function sendNodeResponse(nodeRes, webRes) {
|
|
if (!webRes) {
|
|
nodeRes.statusCode = 500;
|
|
return endNodeResponse(nodeRes);
|
|
}
|
|
if (webRes._toNodeResponse) {
|
|
const res = webRes._toNodeResponse();
|
|
if (res.body) {
|
|
if (res.body instanceof ReadableStream) {
|
|
writeHead(nodeRes, res.status, res.statusText, res.headers);
|
|
return streamBody(res.body, nodeRes);
|
|
} else if (typeof res.body?.pipe === "function") return pipeBody(res.body, nodeRes, res.status, res.statusText, res.headers);
|
|
writeHead(nodeRes, res.status, res.statusText, res.headers);
|
|
nodeRes.write(res.body);
|
|
} else writeHead(nodeRes, res.status, res.statusText, res.headers);
|
|
return endNodeResponse(nodeRes);
|
|
}
|
|
const rawHeaders = [...webRes.headers];
|
|
writeHead(nodeRes, webRes.status, webRes.statusText, rawHeaders);
|
|
return webRes.body ? streamBody(webRes.body, nodeRes) : endNodeResponse(nodeRes);
|
|
}
|
|
function writeHead(nodeRes, status, statusText, rawHeaders) {
|
|
const writeHeaders = globalThis.Deno ? rawHeaders : rawHeaders.flat();
|
|
if (!nodeRes.headersSent) if (nodeRes.req?.httpVersion === "2.0") nodeRes.writeHead(status, writeHeaders);
|
|
else nodeRes.writeHead(status, statusText, writeHeaders);
|
|
}
|
|
function endNodeResponse(nodeRes) {
|
|
return new Promise((resolve) => nodeRes.end(resolve));
|
|
}
|
|
function pipeBody(stream, nodeRes, status, statusText, headers) {
|
|
if (nodeRes.destroyed) {
|
|
stream.destroy?.();
|
|
return;
|
|
}
|
|
if (typeof stream.on !== "function" || typeof stream.destroy !== "function") {
|
|
writeHead(nodeRes, status, statusText, headers);
|
|
stream.pipe(nodeRes);
|
|
return new Promise((resolve) => nodeRes.on("close", resolve));
|
|
}
|
|
if (stream.destroyed) {
|
|
writeHead(nodeRes, 500, "Internal Server Error", []);
|
|
return endNodeResponse(nodeRes);
|
|
}
|
|
return new Promise((resolve) => {
|
|
function onEarlyError() {
|
|
stream.off("readable", onReadable);
|
|
stream.destroy();
|
|
writeHead(nodeRes, 500, "Internal Server Error", []);
|
|
endNodeResponse(nodeRes).then(resolve);
|
|
}
|
|
function onReadable() {
|
|
stream.off("error", onEarlyError);
|
|
if (nodeRes.destroyed) {
|
|
stream.destroy();
|
|
return resolve();
|
|
}
|
|
writeHead(nodeRes, status, statusText, headers);
|
|
pipeline(stream, nodeRes).catch(() => {}).then(() => resolve());
|
|
}
|
|
stream.once("error", onEarlyError);
|
|
stream.once("readable", onReadable);
|
|
});
|
|
}
|
|
function streamBody(stream, nodeRes) {
|
|
if (nodeRes.destroyed) {
|
|
stream.cancel();
|
|
return;
|
|
}
|
|
const reader = stream.getReader();
|
|
function streamCancel(error) {
|
|
reader.cancel(error).catch(() => {});
|
|
if (error) nodeRes.destroy(error);
|
|
}
|
|
function streamHandle({ done, value }) {
|
|
try {
|
|
if (done) nodeRes.end();
|
|
else if (nodeRes.write(value)) reader.read().then(streamHandle, streamCancel);
|
|
else nodeRes.once("drain", () => reader.read().then(streamHandle, streamCancel));
|
|
} catch (error) {
|
|
streamCancel(error instanceof Error ? error : void 0);
|
|
}
|
|
}
|
|
nodeRes.on("close", streamCancel);
|
|
nodeRes.on("error", streamCancel);
|
|
reader.read().then(streamHandle, streamCancel);
|
|
return reader.closed.catch(streamCancel).finally(() => {
|
|
nodeRes.off("close", streamCancel);
|
|
nodeRes.off("error", streamCancel);
|
|
});
|
|
}
|
|
/**
|
|
* Validates an HTTP Host header value (domain, IPv4, or bracketed IPv6) with optional port.
|
|
* Intended for preliminary filtering invalid values like "localhost:3000/foobar?"
|
|
*/
|
|
var HOST_RE = /^(\[(?:[A-Fa-f0-9:.]+)\]|(?:[A-Za-z0-9_-]+\.)*[A-Za-z0-9_-]+|(?:\d{1,3}\.){3}\d{1,3})(:\d{1,5})?$/;
|
|
var NodeRequestURL = class extends FastURL {
|
|
#req;
|
|
constructor({ req }) {
|
|
const path = req.url || "/";
|
|
if (path[0] === "/") {
|
|
const qIndex = path.indexOf("?");
|
|
const pathname = qIndex === -1 ? path : path?.slice(0, qIndex) || "/";
|
|
const search = qIndex === -1 ? "" : path?.slice(qIndex) || "";
|
|
let host = req.headers.host || req.headers[":authority"];
|
|
if (host && !HOST_RE.test(host)) host = "_invalid_";
|
|
else if (!host) if (req.socket) host = `${req.socket.localFamily === "IPv6" ? "[" + req.socket.localAddress + "]" : req.socket.localAddress}:${req.socket?.localPort || "80"}`;
|
|
else host = "localhost";
|
|
const protocol = req.socket?.encrypted || req.headers["x-forwarded-proto"] === "https" || req.headers[":scheme"] === "https" ? "https:" : "http:";
|
|
super({
|
|
protocol,
|
|
host,
|
|
pathname,
|
|
search
|
|
});
|
|
} else super(path);
|
|
this.#req = req;
|
|
}
|
|
get pathname() {
|
|
return super.pathname;
|
|
}
|
|
set pathname(value) {
|
|
this._url.pathname = value;
|
|
this.#req.url = this._url.pathname + this._url.search;
|
|
}
|
|
};
|
|
var NodeRequestHeaders = /* @__PURE__ */ (() => {
|
|
const NativeHeaders = globalThis.Headers;
|
|
class Headers {
|
|
#req;
|
|
#headers;
|
|
constructor(req) {
|
|
this.#req = req;
|
|
}
|
|
static [Symbol.hasInstance](val) {
|
|
return val instanceof NativeHeaders;
|
|
}
|
|
get _headers() {
|
|
if (!this.#headers) {
|
|
const headers = new NativeHeaders();
|
|
const rawHeaders = this.#req.rawHeaders;
|
|
const len = rawHeaders.length;
|
|
for (let i = 0; i < len; i += 2) {
|
|
const key = rawHeaders[i];
|
|
if (key.charCodeAt(0) === 58) continue;
|
|
const value = rawHeaders[i + 1];
|
|
headers.append(key, value);
|
|
}
|
|
this.#headers = headers;
|
|
}
|
|
return this.#headers;
|
|
}
|
|
get(name) {
|
|
if (this.#headers) return this.#headers.get(name);
|
|
const value = this.#req.headers[name.toLowerCase()];
|
|
return Array.isArray(value) ? value.join(", ") : value || null;
|
|
}
|
|
has(name) {
|
|
if (this.#headers) return this.#headers.has(name);
|
|
return name.toLowerCase() in this.#req.headers;
|
|
}
|
|
getSetCookie() {
|
|
if (this.#headers) return this.#headers.getSetCookie();
|
|
const value = this.#req.headers["set-cookie"];
|
|
return Array.isArray(value) ? value : value ? [value] : [];
|
|
}
|
|
entries() {
|
|
return this._headers.entries();
|
|
}
|
|
[Symbol.iterator]() {
|
|
return this.entries();
|
|
}
|
|
}
|
|
lazyInherit(Headers.prototype, NativeHeaders.prototype, "_headers");
|
|
Object.setPrototypeOf(Headers, NativeHeaders);
|
|
Object.setPrototypeOf(Headers.prototype, NativeHeaders.prototype);
|
|
return Headers;
|
|
})();
|
|
var NodeRequest = /* @__PURE__ */ (() => {
|
|
const NativeRequest = globalThis.Request;
|
|
class Request {
|
|
runtime;
|
|
#req;
|
|
#url;
|
|
#bodyStream;
|
|
#request;
|
|
#headers;
|
|
#abortController;
|
|
constructor(ctx) {
|
|
this.#req = ctx.req;
|
|
this.runtime = {
|
|
name: "node",
|
|
node: ctx
|
|
};
|
|
}
|
|
static [Symbol.hasInstance](val) {
|
|
return val instanceof NativeRequest;
|
|
}
|
|
get ip() {
|
|
return this.#req.socket?.remoteAddress;
|
|
}
|
|
get method() {
|
|
if (this.#request) return this.#request.method;
|
|
return this.#req.method || "GET";
|
|
}
|
|
get _url() {
|
|
return this.#url ||= new NodeRequestURL({ req: this.#req });
|
|
}
|
|
set _url(url) {
|
|
this.#url = url;
|
|
}
|
|
get url() {
|
|
if (this.#request) return this.#request.url;
|
|
return this._url.href;
|
|
}
|
|
get headers() {
|
|
if (this.#request) return this.#request.headers;
|
|
return this.#headers ||= new NodeRequestHeaders(this.#req);
|
|
}
|
|
get _abortController() {
|
|
if (!this.#abortController) {
|
|
this.#abortController = new AbortController();
|
|
const { req, res } = this.runtime.node;
|
|
const abortController = this.#abortController;
|
|
const abort = (err) => abortController.abort?.(err);
|
|
if (res) res.once("close", () => {
|
|
const reqError = req.errored;
|
|
if (reqError) abort(reqError);
|
|
else if (!res.writableEnded) abort();
|
|
});
|
|
else req.once("close", () => {
|
|
if (!req.complete) abort();
|
|
});
|
|
}
|
|
return this.#abortController;
|
|
}
|
|
get signal() {
|
|
return this.#request ? this.#request.signal : this._abortController.signal;
|
|
}
|
|
get body() {
|
|
if (this.#request) return this.#request.body;
|
|
if (this.#bodyStream === void 0) {
|
|
const method = this.method;
|
|
this.#bodyStream = !(method === "GET" || method === "HEAD") ? Readable.toWeb(this.#req) : null;
|
|
}
|
|
return this.#bodyStream;
|
|
}
|
|
text() {
|
|
if (this.#request) return this.#request.text();
|
|
if (this.#bodyStream !== void 0) return this.#bodyStream ? new Response(this.#bodyStream).text() : Promise.resolve("");
|
|
return readBody(this.#req).then((buf) => buf.toString());
|
|
}
|
|
json() {
|
|
if (this.#request) return this.#request.json();
|
|
return this.text().then((text) => JSON.parse(text));
|
|
}
|
|
get _request() {
|
|
if (!this.#request) {
|
|
const body = this.body;
|
|
this.#request = new NativeRequest(this.url, {
|
|
method: this.method,
|
|
headers: this.headers,
|
|
signal: this._abortController.signal,
|
|
body,
|
|
duplex: body ? "half" : void 0
|
|
});
|
|
this.#headers = void 0;
|
|
this.#bodyStream = void 0;
|
|
}
|
|
return this.#request;
|
|
}
|
|
}
|
|
lazyInherit(Request.prototype, NativeRequest.prototype, "_request");
|
|
Object.setPrototypeOf(Request.prototype, NativeRequest.prototype);
|
|
return Request;
|
|
})();
|
|
function readBody(req) {
|
|
if ("rawBody" in req && Buffer.isBuffer(req.rawBody)) return Promise.resolve(req.rawBody);
|
|
return new Promise((resolve, reject) => {
|
|
const chunks = [];
|
|
const onData = (chunk) => {
|
|
chunks.push(chunk);
|
|
};
|
|
const onError = (err) => {
|
|
reject(err);
|
|
};
|
|
const onEnd = () => {
|
|
req.off("error", onError);
|
|
req.off("data", onData);
|
|
resolve(Buffer.concat(chunks));
|
|
};
|
|
req.on("data", onData).once("end", onEnd).once("error", onError);
|
|
});
|
|
}
|
|
/**
|
|
* Fast Response for Node.js runtime
|
|
*
|
|
* It is faster because in most cases it doesn't create a full Response instance.
|
|
*/
|
|
var NodeResponse = /* @__PURE__ */ (() => {
|
|
const NativeResponse = globalThis.Response;
|
|
const STATUS_CODES = globalThis.process?.getBuiltinModule?.("node:http")?.STATUS_CODES || {};
|
|
class NodeResponse {
|
|
#body;
|
|
#init;
|
|
#headers;
|
|
#response;
|
|
constructor(body, init) {
|
|
this.#body = body;
|
|
this.#init = init;
|
|
}
|
|
static [Symbol.hasInstance](val) {
|
|
return val instanceof NativeResponse;
|
|
}
|
|
get status() {
|
|
return this.#response?.status || this.#init?.status || 200;
|
|
}
|
|
get statusText() {
|
|
return this.#response?.statusText || this.#init?.statusText || STATUS_CODES[this.status] || "";
|
|
}
|
|
get headers() {
|
|
if (this.#response) return this.#response.headers;
|
|
if (this.#headers) return this.#headers;
|
|
const initHeaders = this.#init?.headers;
|
|
return this.#headers = initHeaders instanceof Headers ? initHeaders : new Headers(initHeaders);
|
|
}
|
|
get ok() {
|
|
if (this.#response) return this.#response.ok;
|
|
const status = this.status;
|
|
return status >= 200 && status < 300;
|
|
}
|
|
get _response() {
|
|
if (this.#response) return this.#response;
|
|
let body = this.#body;
|
|
if (body && typeof body.pipe === "function" && !(body instanceof Readable)) {
|
|
const stream = new PassThrough();
|
|
body.pipe(stream);
|
|
const abort = body.abort;
|
|
if (abort) stream.once("close", () => abort());
|
|
body = stream;
|
|
}
|
|
this.#response = new NativeResponse(body, this.#headers ? {
|
|
...this.#init,
|
|
headers: this.#headers
|
|
} : this.#init);
|
|
this.#init = void 0;
|
|
this.#headers = void 0;
|
|
this.#body = void 0;
|
|
return this.#response;
|
|
}
|
|
_toNodeResponse() {
|
|
const status = this.status;
|
|
const statusText = this.statusText;
|
|
let body;
|
|
let contentType;
|
|
let contentLength;
|
|
if (this.#response) body = this.#response.body;
|
|
else if (this.#body) if (this.#body instanceof ReadableStream) body = this.#body;
|
|
else if (typeof this.#body === "string") {
|
|
body = this.#body;
|
|
contentType = "text/plain; charset=UTF-8";
|
|
contentLength = Buffer.byteLength(this.#body);
|
|
} else if (this.#body instanceof ArrayBuffer) {
|
|
body = Buffer.from(this.#body);
|
|
contentLength = this.#body.byteLength;
|
|
} else if (this.#body instanceof Uint8Array) {
|
|
body = this.#body;
|
|
contentLength = this.#body.byteLength;
|
|
} else if (this.#body instanceof DataView) {
|
|
body = Buffer.from(this.#body.buffer);
|
|
contentLength = this.#body.byteLength;
|
|
} else if (this.#body instanceof Blob) {
|
|
body = this.#body.stream();
|
|
contentType = this.#body.type;
|
|
contentLength = this.#body.size;
|
|
} else if (typeof this.#body.pipe === "function") body = this.#body;
|
|
else body = this._response.body;
|
|
const headers = [];
|
|
const initHeaders = this.#init?.headers;
|
|
const headerEntries = this.#response?.headers || this.#headers || (initHeaders ? Array.isArray(initHeaders) ? initHeaders : initHeaders?.entries ? initHeaders.entries() : Object.entries(initHeaders).map(([k, v]) => [k.toLowerCase(), v]) : void 0);
|
|
let hasContentTypeHeader;
|
|
let hasContentLength;
|
|
if (headerEntries) for (const [key, value] of headerEntries) {
|
|
if (Array.isArray(value)) for (const v of value) headers.push([key, v]);
|
|
else headers.push([key, value]);
|
|
if (key === "content-type") hasContentTypeHeader = true;
|
|
else if (key === "content-length") hasContentLength = true;
|
|
}
|
|
if (contentType && !hasContentTypeHeader) headers.push(["content-type", contentType]);
|
|
if (contentLength && !hasContentLength) headers.push(["content-length", String(contentLength)]);
|
|
this.#init = void 0;
|
|
this.#headers = void 0;
|
|
this.#response = void 0;
|
|
this.#body = void 0;
|
|
return {
|
|
status,
|
|
statusText,
|
|
headers,
|
|
body
|
|
};
|
|
}
|
|
}
|
|
lazyInherit(NodeResponse.prototype, NativeResponse.prototype, "_response");
|
|
Object.setPrototypeOf(NodeResponse, NativeResponse);
|
|
Object.setPrototypeOf(NodeResponse.prototype, NativeResponse.prototype);
|
|
return NodeResponse;
|
|
})();
|
|
function serve(options) {
|
|
return new NodeServer(options);
|
|
}
|
|
var NodeServer = class {
|
|
runtime = "node";
|
|
options;
|
|
node;
|
|
serveOptions;
|
|
fetch;
|
|
waitUntil;
|
|
#isSecure;
|
|
#listeningPromise;
|
|
#listenError;
|
|
#wait;
|
|
constructor(options) {
|
|
this.options = {
|
|
...options,
|
|
middleware: [...options.middleware || []]
|
|
};
|
|
for (const plugin of options.plugins || []) plugin(this);
|
|
errorPlugin(this);
|
|
const fetchHandler = this.fetch = wrapFetch(this);
|
|
const handler = (nodeReq, nodeRes) => {
|
|
const request = new NodeRequest({
|
|
req: nodeReq,
|
|
res: nodeRes
|
|
});
|
|
request.waitUntil = this.#wait?.waitUntil;
|
|
const res = fetchHandler(request);
|
|
return res instanceof Promise ? res.then((resolvedRes) => sendNodeResponse(nodeRes, resolvedRes)) : sendNodeResponse(nodeRes, res);
|
|
};
|
|
this.node = {
|
|
handler,
|
|
server: void 0
|
|
};
|
|
const loader = globalThis.__srvxLoader__;
|
|
if (loader) {
|
|
loader({ server: this });
|
|
return;
|
|
}
|
|
gracefulShutdownPlugin(this);
|
|
this.#wait = createWaitUntil();
|
|
this.waitUntil = this.#wait.waitUntil;
|
|
const tls = resolveTLSOptions(this.options);
|
|
const { port, hostname: host } = resolvePortAndHost(this.options);
|
|
this.serveOptions = {
|
|
port,
|
|
host,
|
|
exclusive: !this.options.reusePort,
|
|
...tls ? {
|
|
cert: tls.cert,
|
|
key: tls.key,
|
|
passphrase: tls.passphrase
|
|
} : {},
|
|
...this.options.node
|
|
};
|
|
let server;
|
|
this.#isSecure = !!this.serveOptions.cert && this.options.protocol !== "http";
|
|
if (this.options.node?.http2 ?? this.#isSecure) if (this.#isSecure) server = nodeHTTP2.createSecureServer({
|
|
allowHTTP1: true,
|
|
...this.serveOptions
|
|
}, handler);
|
|
else throw new Error("node.http2 option requires tls certificate!");
|
|
else if (this.#isSecure) server = nodeHTTPS.createServer(this.serveOptions, handler);
|
|
else server = nodeHTTP.createServer(this.serveOptions, handler);
|
|
this.node.server = server;
|
|
if (!options.manual) this.serve().catch(() => {});
|
|
}
|
|
serve() {
|
|
if (this.#listeningPromise) return this.#listeningPromise.then(() => this);
|
|
const server = this.node?.server;
|
|
if (!server) return Promise.reject(/* @__PURE__ */ new Error("Server not initialized"));
|
|
this.#listenError = void 0;
|
|
this.#listeningPromise = new Promise((resolve, reject) => {
|
|
const onError = (error) => {
|
|
server.off("listening", onListening);
|
|
this.#listenError = error;
|
|
this.#listeningPromise = void 0;
|
|
reject(error);
|
|
};
|
|
const onListening = () => {
|
|
server.off("error", onError);
|
|
printListening(this.options, this.url);
|
|
resolve();
|
|
};
|
|
server.once("error", onError);
|
|
server.once("listening", onListening);
|
|
server.listen(this.serveOptions);
|
|
});
|
|
return this.#listeningPromise.then(() => this);
|
|
}
|
|
get url() {
|
|
const addr = this.node?.server?.address();
|
|
if (!addr) return;
|
|
return typeof addr === "string" ? addr : fmtURL(addr.address, addr.port, this.#isSecure);
|
|
}
|
|
ready() {
|
|
if (this.#listenError) return Promise.reject(this.#listenError);
|
|
return Promise.resolve(this.#listeningPromise).then(() => this);
|
|
}
|
|
async close(closeAll) {
|
|
await Promise.all([this.#wait?.wait(), new Promise((resolve, reject) => {
|
|
const server = this.node?.server;
|
|
if (server && closeAll && "closeAllConnections" in server) server.closeAllConnections();
|
|
if (!server || !server.listening) return resolve();
|
|
server.close((error) => error ? reject(error) : resolve());
|
|
})]);
|
|
}
|
|
};
|
|
//#endregion
|
|
//#region node_modules/rou3/dist/index.mjs
|
|
var NullProtoObj = /* @__PURE__ */ (() => {
|
|
const e = function() {};
|
|
return e.prototype = Object.create(null), Object.freeze(e.prototype), e;
|
|
})();
|
|
//#endregion
|
|
//#region node_modules/h3/dist/h3.mjs
|
|
function decodePathname(pathname) {
|
|
return decodeURI(pathname.includes("%25") ? pathname.replace(/%25/g, "%2525") : pathname);
|
|
}
|
|
var kEventNS = "h3.internal.event.";
|
|
var kEventRes = /* @__PURE__ */ Symbol.for(`${kEventNS}res`);
|
|
var kEventResHeaders = /* @__PURE__ */ Symbol.for(`${kEventNS}res.headers`);
|
|
var kEventResErrHeaders = /* @__PURE__ */ Symbol.for(`${kEventNS}res.err.headers`);
|
|
var H3Event = class {
|
|
app;
|
|
req;
|
|
url;
|
|
context;
|
|
static __is_event__ = true;
|
|
constructor(req, context, app) {
|
|
this.context = context || req.context || new NullProtoObj();
|
|
this.req = req;
|
|
this.app = app;
|
|
const _url = req._url;
|
|
const url = _url && _url instanceof URL ? _url : new FastURL(req.url);
|
|
if (url.pathname.includes("%")) url.pathname = decodePathname(url.pathname);
|
|
this.url = url;
|
|
}
|
|
get res() {
|
|
return this[kEventRes] ||= new H3EventResponse();
|
|
}
|
|
get runtime() {
|
|
return this.req.runtime;
|
|
}
|
|
waitUntil(promise) {
|
|
this.req.waitUntil?.(promise);
|
|
}
|
|
toString() {
|
|
return `[${this.req.method}] ${this.req.url}`;
|
|
}
|
|
toJSON() {
|
|
return this.toString();
|
|
}
|
|
get node() {
|
|
return this.req.runtime?.node;
|
|
}
|
|
get headers() {
|
|
return this.req.headers;
|
|
}
|
|
get path() {
|
|
return this.url.pathname + this.url.search;
|
|
}
|
|
get method() {
|
|
return this.req.method;
|
|
}
|
|
};
|
|
var H3EventResponse = class {
|
|
status;
|
|
statusText;
|
|
get headers() {
|
|
return this[kEventResHeaders] ||= new Headers();
|
|
}
|
|
get errHeaders() {
|
|
return this[kEventResErrHeaders] ||= new Headers();
|
|
}
|
|
};
|
|
var DISALLOWED_STATUS_CHARS = /[^\u0009\u0020-\u007E]/g;
|
|
function sanitizeStatusMessage(statusMessage = "") {
|
|
return statusMessage.replace(DISALLOWED_STATUS_CHARS, "");
|
|
}
|
|
function sanitizeStatusCode(statusCode, defaultStatusCode = 200) {
|
|
if (!statusCode) return defaultStatusCode;
|
|
if (typeof statusCode === "string") statusCode = +statusCode;
|
|
if (statusCode < 100 || statusCode > 599) return defaultStatusCode;
|
|
return statusCode;
|
|
}
|
|
var HTTPError = class HTTPError extends Error {
|
|
get name() {
|
|
return "HTTPError";
|
|
}
|
|
status;
|
|
statusText;
|
|
headers;
|
|
cause;
|
|
data;
|
|
body;
|
|
unhandled;
|
|
static isError(input) {
|
|
return input instanceof Error && input?.name === "HTTPError";
|
|
}
|
|
static status(status, statusText, details) {
|
|
return new HTTPError({
|
|
...details,
|
|
statusText,
|
|
status
|
|
});
|
|
}
|
|
constructor(arg1, arg2) {
|
|
let messageInput;
|
|
let details;
|
|
if (typeof arg1 === "string") {
|
|
messageInput = arg1;
|
|
details = arg2;
|
|
} else details = arg1;
|
|
const status = sanitizeStatusCode(details?.status || details?.statusCode || (details?.cause)?.status || (details?.cause)?.statusCode, 500);
|
|
const statusText = sanitizeStatusMessage(details?.statusText || details?.statusMessage || (details?.cause)?.statusText || (details?.cause)?.statusMessage);
|
|
const message = messageInput || details?.message || (details?.cause)?.message || details?.statusText || details?.statusMessage || [
|
|
"HTTPError",
|
|
status,
|
|
statusText
|
|
].filter(Boolean).join(" ");
|
|
super(message, { cause: details });
|
|
this.cause = details;
|
|
this.status = status;
|
|
this.statusText = statusText || void 0;
|
|
const rawHeaders = details?.headers || (details?.cause)?.headers;
|
|
this.headers = rawHeaders ? new Headers(rawHeaders) : void 0;
|
|
this.unhandled = details?.unhandled ?? (details?.cause)?.unhandled ?? void 0;
|
|
this.data = details?.data;
|
|
this.body = details?.body;
|
|
}
|
|
get statusCode() {
|
|
return this.status;
|
|
}
|
|
get statusMessage() {
|
|
return this.statusText;
|
|
}
|
|
toJSON() {
|
|
const unhandled = this.unhandled;
|
|
return {
|
|
status: this.status,
|
|
statusText: this.statusText,
|
|
unhandled,
|
|
message: unhandled ? "HTTPError" : this.message,
|
|
data: unhandled ? void 0 : this.data,
|
|
...unhandled ? void 0 : this.body
|
|
};
|
|
}
|
|
};
|
|
function isJSONSerializable(value, _type) {
|
|
if (value === null || value === void 0) return true;
|
|
if (_type !== "object") return _type === "boolean" || _type === "number" || _type === "string";
|
|
if (typeof value.toJSON === "function") return true;
|
|
if (Array.isArray(value)) return true;
|
|
if (typeof value.pipe === "function" || typeof value.pipeTo === "function") return false;
|
|
if (value instanceof NullProtoObj) return true;
|
|
const proto = Object.getPrototypeOf(value);
|
|
return proto === Object.prototype || proto === null;
|
|
}
|
|
var kNotFound = /* @__PURE__ */ Symbol.for("h3.notFound");
|
|
var kHandled = /* @__PURE__ */ Symbol.for("h3.handled");
|
|
function toResponse(val, event, config = {}) {
|
|
if (typeof val?.then === "function") return val.then((resolvedVal) => toResponse(resolvedVal, event, config), (r) => toResponse(typeof r === "number" ? new HTTPError({ status: r }) : r, event, config));
|
|
const response = prepareResponse(val, event, config);
|
|
if (typeof response?.then === "function") return toResponse(response, event, config);
|
|
const { onResponse } = config;
|
|
return onResponse ? Promise.resolve(onResponse(response, event)).then(() => response) : response;
|
|
}
|
|
var HTTPResponse = class {
|
|
#headers;
|
|
#init;
|
|
body;
|
|
constructor(body, init) {
|
|
this.body = body;
|
|
this.#init = init;
|
|
}
|
|
get status() {
|
|
return this.#init?.status || 200;
|
|
}
|
|
get statusText() {
|
|
return this.#init?.statusText || "OK";
|
|
}
|
|
get headers() {
|
|
return this.#headers ||= new Headers(this.#init?.headers);
|
|
}
|
|
};
|
|
function prepareResponse(val, event, config, nested) {
|
|
if (val === kHandled) return new NodeResponse(null);
|
|
if (val === kNotFound) val = new HTTPError({
|
|
status: 404,
|
|
message: `Cannot find any route matching [${event.req.method}] ${event.url}`
|
|
});
|
|
if (val && val instanceof Error) {
|
|
const isHTTPError = HTTPError.isError(val);
|
|
const error = isHTTPError ? val : new HTTPError(val);
|
|
if (!isHTTPError) {
|
|
error.unhandled = true;
|
|
if (val?.stack) error.stack = val.stack;
|
|
}
|
|
if (error.unhandled && !config.silent) console.error(error);
|
|
const { onError } = config;
|
|
const errHeaders = event[kEventRes]?.[kEventResErrHeaders];
|
|
return onError && !nested ? Promise.resolve(onError(error, event)).catch((error) => error).then((newVal) => prepareResponse(newVal ?? val, event, config, true)) : errorResponse(error, config.debug, errHeaders);
|
|
}
|
|
const preparedRes = event[kEventRes];
|
|
const preparedHeaders = preparedRes?.[kEventResHeaders];
|
|
event[kEventRes] = void 0;
|
|
if (!(val instanceof Response)) {
|
|
const res = prepareResponseBody(val, event, config);
|
|
const status = res.status || preparedRes?.status;
|
|
return new NodeResponse(nullBody(event.req.method, status) ? null : res.body, {
|
|
status,
|
|
statusText: res.statusText || preparedRes?.statusText,
|
|
headers: res.headers && preparedHeaders ? mergeHeaders$1(res.headers, preparedHeaders) : res.headers || preparedHeaders
|
|
});
|
|
}
|
|
if (!preparedHeaders || nested || !val.ok) return val;
|
|
try {
|
|
mergeHeaders$1(val.headers, preparedHeaders, val.headers);
|
|
return val;
|
|
} catch {
|
|
return new NodeResponse(nullBody(event.req.method, val.status) ? null : val.body, {
|
|
status: val.status,
|
|
statusText: val.statusText,
|
|
headers: mergeHeaders$1(val.headers, preparedHeaders)
|
|
});
|
|
}
|
|
}
|
|
function mergeHeaders$1(base, overrides, target = new Headers(base)) {
|
|
for (const [name, value] of overrides) if (name === "set-cookie") target.append(name, value);
|
|
else target.set(name, value);
|
|
return target;
|
|
}
|
|
var frozen = (name) => (...args) => {
|
|
throw new Error(`Headers are frozen (${name} ${args.join(", ")})`);
|
|
};
|
|
var FrozenHeaders = class extends Headers {
|
|
set = frozen("set");
|
|
append = frozen("append");
|
|
delete = frozen("delete");
|
|
};
|
|
var emptyHeaders = /* @__PURE__ */ new FrozenHeaders({ "content-length": "0" });
|
|
var jsonHeaders = /* @__PURE__ */ new FrozenHeaders({ "content-type": "application/json;charset=UTF-8" });
|
|
function prepareResponseBody(val, event, config) {
|
|
if (val === null || val === void 0) return {
|
|
body: "",
|
|
headers: emptyHeaders
|
|
};
|
|
const valType = typeof val;
|
|
if (valType === "string") return { body: val };
|
|
if (val instanceof Uint8Array) {
|
|
event.res.headers.set("content-length", val.byteLength.toString());
|
|
return { body: val };
|
|
}
|
|
if (val instanceof HTTPResponse || val?.constructor?.name === "HTTPResponse") return val;
|
|
if (isJSONSerializable(val, valType)) return {
|
|
body: JSON.stringify(val, void 0, config.debug ? 2 : void 0),
|
|
headers: jsonHeaders
|
|
};
|
|
if (valType === "bigint") return {
|
|
body: val.toString(),
|
|
headers: jsonHeaders
|
|
};
|
|
if (val instanceof Blob) {
|
|
const headers = new Headers({
|
|
"content-type": val.type,
|
|
"content-length": val.size.toString()
|
|
});
|
|
let filename = val.name;
|
|
if (filename) {
|
|
filename = encodeURIComponent(filename);
|
|
headers.set("content-disposition", `filename="${filename}"; filename*=UTF-8''${filename}`);
|
|
}
|
|
return {
|
|
body: val.stream(),
|
|
headers
|
|
};
|
|
}
|
|
if (valType === "symbol") return { body: val.toString() };
|
|
if (valType === "function") return { body: `${val.name}()` };
|
|
return { body: val };
|
|
}
|
|
function nullBody(method, status) {
|
|
return method === "HEAD" || status === 100 || status === 101 || status === 102 || status === 204 || status === 205 || status === 304;
|
|
}
|
|
function errorResponse(error, debug, errHeaders) {
|
|
let headers = error.headers ? mergeHeaders$1(jsonHeaders, error.headers) : new Headers(jsonHeaders);
|
|
if (errHeaders) headers = mergeHeaders$1(headers, errHeaders);
|
|
return new NodeResponse(JSON.stringify({
|
|
...error.toJSON(),
|
|
stack: debug && error.stack ? error.stack.split("\n").map((l) => l.trim()) : void 0
|
|
}, void 0, debug ? 2 : void 0), {
|
|
status: error.status,
|
|
statusText: error.statusText,
|
|
headers
|
|
});
|
|
}
|
|
function callMiddleware(event, middleware, handler, index = 0) {
|
|
if (index === middleware.length) return handler(event);
|
|
const fn = middleware[index];
|
|
let nextCalled;
|
|
let nextResult;
|
|
const next = () => {
|
|
if (nextCalled) return nextResult;
|
|
nextCalled = true;
|
|
nextResult = callMiddleware(event, middleware, handler, index + 1);
|
|
return nextResult;
|
|
};
|
|
const ret = fn(event, next);
|
|
return isUnhandledResponse(ret) ? next() : typeof ret?.then === "function" ? ret.then((resolved) => isUnhandledResponse(resolved) ? next() : resolved) : ret;
|
|
}
|
|
function isUnhandledResponse(val) {
|
|
return val === void 0 || val === kNotFound;
|
|
}
|
|
function toRequest(input, options) {
|
|
if (typeof input === "string") {
|
|
let url = input;
|
|
if (url[0] === "/") {
|
|
const headers = options?.headers ? new Headers(options.headers) : void 0;
|
|
const host = headers?.get("host") || "localhost";
|
|
url = `${headers?.get("x-forwarded-proto") === "https" ? "https" : "http"}://${host}${url}`;
|
|
}
|
|
return new Request(url, options);
|
|
} else if (options || input instanceof URL) return new Request(input, options);
|
|
return input;
|
|
}
|
|
function defineHandler(input) {
|
|
if (typeof input === "function") return handlerWithFetch(input);
|
|
const handler = input.handler || (input.fetch ? function _fetchHandler(event) {
|
|
return input.fetch(event.req);
|
|
} : NoHandler);
|
|
return Object.assign(handlerWithFetch(input.middleware?.length ? function _handlerMiddleware(event) {
|
|
return callMiddleware(event, input.middleware, handler);
|
|
} : handler), input);
|
|
}
|
|
function handlerWithFetch(handler) {
|
|
if ("fetch" in handler) return handler;
|
|
return Object.assign(handler, { fetch: (req) => {
|
|
if (typeof req === "string") req = new URL(req, "http://_");
|
|
if (req instanceof URL) req = new Request(req);
|
|
const event = new H3Event(req);
|
|
try {
|
|
return Promise.resolve(toResponse(handler(event), event));
|
|
} catch (error) {
|
|
return Promise.resolve(toResponse(error, event));
|
|
}
|
|
} });
|
|
}
|
|
function defineLazyEventHandler(loader) {
|
|
let handler;
|
|
let promise;
|
|
return defineHandler(function lazyHandler(event) {
|
|
return handler ? handler(event) : (promise ??= Promise.resolve(loader()).then(function resolveLazyHandler(r) {
|
|
handler = toEventHandler(r) || toEventHandler(r.default);
|
|
if (typeof handler !== "function") throw new TypeError("Invalid lazy handler", { cause: { resolved: r } });
|
|
return handler;
|
|
})).then((r) => r(event));
|
|
});
|
|
}
|
|
function toEventHandler(handler) {
|
|
if (typeof handler === "function") return handler;
|
|
if (typeof handler?.handler === "function" && handler.constructor?.["~h3"]) return handler.handler;
|
|
if (typeof handler?.fetch === "function") return function _fetchHandler(event) {
|
|
return handler.fetch(event.req);
|
|
};
|
|
}
|
|
var NoHandler = () => kNotFound;
|
|
var H3Core = class {
|
|
static "~h3" = true;
|
|
config;
|
|
"~middleware";
|
|
"~routes" = [];
|
|
constructor(config = {}) {
|
|
this["~middleware"] = [];
|
|
this.config = config;
|
|
this.fetch = this.fetch.bind(this);
|
|
this.handler = this.handler.bind(this);
|
|
}
|
|
fetch(request) {
|
|
return this["~request"](request);
|
|
}
|
|
handler(event) {
|
|
const route = this["~findRoute"](event);
|
|
if (route) {
|
|
event.context.params = route.params;
|
|
event.context.matchedRoute = route.data;
|
|
}
|
|
const routeHandler = route?.data.handler || NoHandler;
|
|
const middleware = this["~getMiddleware"](event, route);
|
|
return middleware.length > 0 ? callMiddleware(event, middleware, routeHandler) : routeHandler(event);
|
|
}
|
|
"~request"(request, context) {
|
|
const event = new H3Event(request, context, this);
|
|
let handlerRes;
|
|
try {
|
|
if (this.config.onRequest) {
|
|
const hookRes = this.config.onRequest(event);
|
|
handlerRes = typeof hookRes?.then === "function" ? hookRes.then(() => this.handler(event)) : this.handler(event);
|
|
} else handlerRes = this.handler(event);
|
|
} catch (error) {
|
|
handlerRes = Promise.reject(error);
|
|
}
|
|
return toResponse(handlerRes, event, this.config);
|
|
}
|
|
"~findRoute"(_event) {}
|
|
"~addRoute"(_route) {
|
|
this["~routes"].push(_route);
|
|
}
|
|
"~getMiddleware"(_event, route) {
|
|
const routeMiddleware = route?.data.middleware;
|
|
const globalMiddleware = this["~middleware"];
|
|
return routeMiddleware ? [...globalMiddleware, ...routeMiddleware] : globalMiddleware;
|
|
}
|
|
};
|
|
//#endregion
|
|
export { toEventHandler as a, NodeResponse as c, defineLazyEventHandler as i, serve as l, HTTPError as n, toRequest as o, defineHandler as r, NullProtoObj as s, H3Core as t, FastURL as u };
|