zhang-wei-jian's picture
:rocket:
7d73cf2
import { withoutTrailingSlash, withoutBase, getQuery as getQuery$1, decodePath, withLeadingSlash, parseURL } from 'ufo';
import { parse as parse$1, serialize } from 'cookie-es';
import { createRouter as createRouter$1, toRouteMatcher } from 'radix3';
import destr from 'destr';
import { defu } from 'defu';
import crypto from 'uncrypto';
import { seal, defaults, unseal } from 'iron-webcrypto';
import { IncomingMessage } from 'unenv/runtime/node/http/_request';
import { ServerResponse } from 'unenv/runtime/node/http/_response';
function useBase(base, handler) {
base = withoutTrailingSlash(base);
if (!base || base === "/") {
return handler;
}
return eventHandler(async (event) => {
event.node.req.originalUrl = event.node.req.originalUrl || event.node.req.url || "/";
const _path = event._path || event.node.req.url || "/";
event._path = withoutBase(event.path || "/", base);
event.node.req.url = event._path;
try {
return await handler(event);
} finally {
event._path = event.node.req.url = _path;
}
});
}
class H3Error extends Error {
constructor(message, opts = {}) {
super(message, opts);
this.statusCode = 500;
this.fatal = false;
this.unhandled = false;
if (opts.cause && !this.cause) {
this.cause = opts.cause;
}
}
toJSON() {
const obj = {
message: this.message,
statusCode: sanitizeStatusCode(this.statusCode, 500)
};
if (this.statusMessage) {
obj.statusMessage = sanitizeStatusMessage(this.statusMessage);
}
if (this.data !== void 0) {
obj.data = this.data;
}
return obj;
}
}
H3Error.__h3_error__ = true;
function createError(input) {
if (typeof input === "string") {
return new H3Error(input);
}
if (isError(input)) {
return input;
}
const err = new H3Error(input.message ?? input.statusMessage ?? "", {
cause: input.cause || input
});
if ("stack" in input) {
try {
Object.defineProperty(err, "stack", {
get() {
return input.stack;
}
});
} catch {
try {
err.stack = input.stack;
} catch {
}
}
}
if (input.data) {
err.data = input.data;
}
if (input.statusCode) {
err.statusCode = sanitizeStatusCode(input.statusCode, err.statusCode);
} else if (input.status) {
err.statusCode = sanitizeStatusCode(input.status, err.statusCode);
}
if (input.statusMessage) {
err.statusMessage = input.statusMessage;
} else if (input.statusText) {
err.statusMessage = input.statusText;
}
if (err.statusMessage) {
const originalMessage = err.statusMessage;
const sanitizedMessage = sanitizeStatusMessage(err.statusMessage);
if (sanitizedMessage !== originalMessage) {
console.warn(
"[h3] Please prefer using `message` for longer error messages instead of `statusMessage`. In the future, `statusMessage` will be sanitized by default."
);
}
}
if (input.fatal !== void 0) {
err.fatal = input.fatal;
}
if (input.unhandled !== void 0) {
err.unhandled = input.unhandled;
}
return err;
}
function sendError(event, error, debug) {
if (event.handled) {
return;
}
const h3Error = isError(error) ? error : createError(error);
const responseBody = {
statusCode: h3Error.statusCode,
statusMessage: h3Error.statusMessage,
stack: [],
data: h3Error.data
};
if (debug) {
responseBody.stack = (h3Error.stack || "").split("\n").map((l) => l.trim());
}
if (event.handled) {
return;
}
const _code = Number.parseInt(h3Error.statusCode);
setResponseStatus(event, _code, h3Error.statusMessage);
event.node.res.setHeader("content-type", MIMES.json);
event.node.res.end(JSON.stringify(responseBody, void 0, 2));
}
function isError(input) {
return input?.constructor?.__h3_error__ === true;
}
function parse(multipartBodyBuffer, boundary) {
let lastline = "";
let state = 0 /* INIT */;
let buffer = [];
const allParts = [];
let currentPartHeaders = [];
for (let i = 0; i < multipartBodyBuffer.length; i++) {
const prevByte = i > 0 ? multipartBodyBuffer[i - 1] : null;
const currByte = multipartBodyBuffer[i];
const newLineChar = currByte === 10 || currByte === 13;
if (!newLineChar) {
lastline += String.fromCodePoint(currByte);
}
const newLineDetected = currByte === 10 && prevByte === 13;
if (0 /* INIT */ === state && newLineDetected) {
if ("--" + boundary === lastline) {
state = 1 /* READING_HEADERS */;
}
lastline = "";
} else if (1 /* READING_HEADERS */ === state && newLineDetected) {
if (lastline.length > 0) {
const i2 = lastline.indexOf(":");
if (i2 > 0) {
const name = lastline.slice(0, i2).toLowerCase();
const value = lastline.slice(i2 + 1).trim();
currentPartHeaders.push([name, value]);
}
} else {
state = 2 /* READING_DATA */;
buffer = [];
}
lastline = "";
} else if (2 /* READING_DATA */ === state) {
if (lastline.length > boundary.length + 4) {
lastline = "";
}
if ("--" + boundary === lastline) {
const j = buffer.length - lastline.length;
const part = buffer.slice(0, j - 1);
allParts.push(process(part, currentPartHeaders));
buffer = [];
currentPartHeaders = [];
lastline = "";
state = 3 /* READING_PART_SEPARATOR */;
} else {
buffer.push(currByte);
}
if (newLineDetected) {
lastline = "";
}
} else if (3 /* READING_PART_SEPARATOR */ === state && newLineDetected) {
state = 1 /* READING_HEADERS */;
}
}
return allParts;
}
function process(data, headers) {
const dataObj = {};
const contentDispositionHeader = headers.find((h) => h[0] === "content-disposition")?.[1] || "";
for (const i of contentDispositionHeader.split(";")) {
const s = i.split("=");
if (s.length !== 2) {
continue;
}
const key = (s[0] || "").trim();
if (key === "name" || key === "filename") {
const _value = (s[1] || "").trim().replace(/"/g, "");
dataObj[key] = Buffer.from(_value, "latin1").toString("utf8");
}
}
const contentType = headers.find((h) => h[0] === "content-type")?.[1] || "";
if (contentType) {
dataObj.type = contentType;
}
dataObj.data = Buffer.from(data);
return dataObj;
}
async function validateData(data, fn) {
try {
const res = await fn(data);
if (res === false) {
throw createValidationError();
}
if (res === true) {
return data;
}
return res ?? data;
} catch (error) {
throw createValidationError(error);
}
}
function createValidationError(validateError) {
throw createError({
status: 400,
message: validateError.message || "Validation Failed",
...validateError
});
}
function getQuery(event) {
return getQuery$1(event.path || "");
}
function getValidatedQuery(event, validate) {
const query = getQuery(event);
return validateData(query, validate);
}
function getRouterParams(event) {
return event.context.params || {};
}
function getRouterParam(event, name) {
const params = getRouterParams(event);
return params[name];
}
function getMethod(event, defaultMethod = "GET") {
return (event.node.req.method || defaultMethod).toUpperCase();
}
function isMethod(event, expected, allowHead) {
if (allowHead && event.method === "HEAD") {
return true;
}
if (typeof expected === "string") {
if (event.method === expected) {
return true;
}
} else if (expected.includes(event.method)) {
return true;
}
return false;
}
function assertMethod(event, expected, allowHead) {
if (!isMethod(event, expected, allowHead)) {
throw createError({
statusCode: 405,
statusMessage: "HTTP method is not allowed."
});
}
}
function getRequestHeaders(event) {
const _headers = {};
for (const key in event.node.req.headers) {
const val = event.node.req.headers[key];
_headers[key] = Array.isArray(val) ? val.filter(Boolean).join(", ") : val;
}
return _headers;
}
const getHeaders = getRequestHeaders;
function getRequestHeader(event, name) {
const headers = getRequestHeaders(event);
const value = headers[name.toLowerCase()];
return value;
}
const getHeader = getRequestHeader;
function getRequestHost(event, opts = {}) {
if (opts.xForwardedHost) {
const xForwardedHost = event.node.req.headers["x-forwarded-host"];
if (xForwardedHost) {
return xForwardedHost;
}
}
return event.node.req.headers.host || "localhost";
}
function getRequestProtocol(event, opts = {}) {
if (opts.xForwardedProto !== false && event.node.req.headers["x-forwarded-proto"] === "https") {
return "https";
}
return event.node.req.connection.encrypted ? "https" : "http";
}
const DOUBLE_SLASH_RE = /[/\\]{2,}/g;
function getRequestPath(event) {
const path = (event.node.req.url || "/").replace(DOUBLE_SLASH_RE, "/");
return path;
}
function getRequestURL(event, opts = {}) {
const host = getRequestHost(event, opts);
const protocol = getRequestProtocol(event);
const path = (event.node.req.originalUrl || event.path).replace(
/^[/\\]+/g,
"/"
);
return new URL(path, `${protocol}://${host}`);
}
function toWebRequest(event) {
return event.web?.request || new Request(getRequestURL(event), {
// @ts-ignore Undici option
duplex: "half",
method: event.method,
headers: event.headers,
body: getRequestWebStream(event)
});
}
function getRequestIP(event, opts = {}) {
if (event.context.clientAddress) {
return event.context.clientAddress;
}
if (opts.xForwardedFor) {
const xForwardedFor = getRequestHeader(event, "x-forwarded-for")?.split(",")?.pop();
if (xForwardedFor) {
return xForwardedFor;
}
}
if (event.node.req.socket.remoteAddress) {
return event.node.req.socket.remoteAddress;
}
}
const RawBodySymbol = Symbol.for("h3RawBody");
const ParsedBodySymbol = Symbol.for("h3ParsedBody");
const PayloadMethods$1 = ["PATCH", "POST", "PUT", "DELETE"];
function readRawBody(event, encoding = "utf8") {
assertMethod(event, PayloadMethods$1);
const _rawBody = event._requestBody || event.web?.request?.body || event.node.req[RawBodySymbol] || event.node.req.body;
if (_rawBody) {
const promise2 = Promise.resolve(_rawBody).then((_resolved) => {
if (Buffer.isBuffer(_resolved)) {
return _resolved;
}
if (typeof _resolved.pipeTo === "function") {
return new Promise((resolve, reject) => {
const chunks = [];
_resolved.pipeTo(
new WritableStream({
write(chunk) {
chunks.push(chunk);
},
close() {
resolve(Buffer.concat(chunks));
},
abort(reason) {
reject(reason);
}
})
).catch(reject);
});
} else if (typeof _resolved.pipe === "function") {
return new Promise((resolve, reject) => {
const chunks = [];
_resolved.on("data", (chunk) => {
chunks.push(chunk);
}).on("end", () => {
resolve(Buffer.concat(chunks));
}).on("error", reject);
});
}
if (_resolved.constructor === Object) {
return Buffer.from(JSON.stringify(_resolved));
}
return Buffer.from(_resolved);
});
return encoding ? promise2.then((buff) => buff.toString(encoding)) : promise2;
}
if (!Number.parseInt(event.node.req.headers["content-length"] || "")) {
return Promise.resolve(void 0);
}
const promise = event.node.req[RawBodySymbol] = new Promise(
(resolve, reject) => {
const bodyData = [];
event.node.req.on("error", (err) => {
reject(err);
}).on("data", (chunk) => {
bodyData.push(chunk);
}).on("end", () => {
resolve(Buffer.concat(bodyData));
});
}
);
const result = encoding ? promise.then((buff) => buff.toString(encoding)) : promise;
return result;
}
async function readBody(event, options = {}) {
const request = event.node.req;
if (ParsedBodySymbol in request) {
return request[ParsedBodySymbol];
}
const contentType = request.headers["content-type"] || "";
const body = await readRawBody(event);
let parsed;
if (contentType === "application/json") {
parsed = _parseJSON(body, options.strict ?? true);
} else if (contentType.startsWith("application/x-www-form-urlencoded")) {
parsed = _parseURLEncodedBody(body);
} else if (contentType.startsWith("text/")) {
parsed = body;
} else {
parsed = _parseJSON(body, options.strict ?? false);
}
request[ParsedBodySymbol] = parsed;
return parsed;
}
async function readValidatedBody(event, validate) {
const _body = await readBody(event, { strict: true });
return validateData(_body, validate);
}
async function readMultipartFormData(event) {
const contentType = getRequestHeader(event, "content-type");
if (!contentType || !contentType.startsWith("multipart/form-data")) {
return;
}
const boundary = contentType.match(/boundary=([^;]*)(;|$)/i)?.[1];
if (!boundary) {
return;
}
const body = await readRawBody(event, false);
if (!body) {
return;
}
return parse(body, boundary);
}
async function readFormData(event) {
return await toWebRequest(event).formData();
}
function getRequestWebStream(event) {
if (!PayloadMethods$1.includes(event.method)) {
return;
}
return event.web?.request?.body || event._requestBody || new ReadableStream({
start: (controller) => {
event.node.req.on("data", (chunk) => {
controller.enqueue(chunk);
});
event.node.req.on("end", () => {
controller.close();
});
event.node.req.on("error", (err) => {
controller.error(err);
});
}
});
}
function _parseJSON(body = "", strict) {
if (!body) {
return void 0;
}
try {
return destr(body, { strict });
} catch {
throw createError({
statusCode: 400,
statusMessage: "Bad Request",
message: "Invalid JSON body"
});
}
}
function _parseURLEncodedBody(body) {
const form = new URLSearchParams(body);
const parsedForm = /* @__PURE__ */ Object.create(null);
for (const [key, value] of form.entries()) {
if (key in parsedForm) {
if (!Array.isArray(parsedForm[key])) {
parsedForm[key] = [parsedForm[key]];
}
parsedForm[key].push(value);
} else {
parsedForm[key] = value;
}
}
return parsedForm;
}
function handleCacheHeaders(event, opts) {
const cacheControls = ["public", ...opts.cacheControls || []];
let cacheMatched = false;
if (opts.maxAge !== void 0) {
cacheControls.push(`max-age=${+opts.maxAge}`, `s-maxage=${+opts.maxAge}`);
}
if (opts.modifiedTime) {
const modifiedTime = new Date(opts.modifiedTime);
const ifModifiedSince = event.node.req.headers["if-modified-since"];
event.node.res.setHeader("last-modified", modifiedTime.toUTCString());
if (ifModifiedSince && new Date(ifModifiedSince) >= opts.modifiedTime) {
cacheMatched = true;
}
}
if (opts.etag) {
event.node.res.setHeader("etag", opts.etag);
const ifNonMatch = event.node.req.headers["if-none-match"];
if (ifNonMatch === opts.etag) {
cacheMatched = true;
}
}
event.node.res.setHeader("cache-control", cacheControls.join(", "));
if (cacheMatched) {
event.node.res.statusCode = 304;
if (!event.handled) {
event.node.res.end();
}
return true;
}
return false;
}
const MIMES = {
html: "text/html",
json: "application/json"
};
const 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 = Number.parseInt(statusCode, 10);
}
if (statusCode < 100 || statusCode > 999) {
return defaultStatusCode;
}
return statusCode;
}
function parseCookies(event) {
return parse$1(event.node.req.headers.cookie || "");
}
function getCookie(event, name) {
return parseCookies(event)[name];
}
function setCookie(event, name, value, serializeOptions) {
const cookieStr = serialize(name, value, {
path: "/",
...serializeOptions
});
let setCookies = event.node.res.getHeader("set-cookie");
if (!Array.isArray(setCookies)) {
setCookies = [setCookies];
}
setCookies = setCookies.filter((cookieValue) => {
return cookieValue && !cookieValue.startsWith(name + "=");
});
event.node.res.setHeader("set-cookie", [...setCookies, cookieStr]);
}
function deleteCookie(event, name, serializeOptions) {
setCookie(event, name, "", {
...serializeOptions,
maxAge: 0
});
}
function splitCookiesString(cookiesString) {
if (Array.isArray(cookiesString)) {
return cookiesString.flatMap((c) => splitCookiesString(c));
}
if (typeof cookiesString !== "string") {
return [];
}
const cookiesStrings = [];
let pos = 0;
let start;
let ch;
let lastComma;
let nextStart;
let cookiesSeparatorFound;
const skipWhitespace = () => {
while (pos < cookiesString.length && /\s/.test(cookiesString.charAt(pos))) {
pos += 1;
}
return pos < cookiesString.length;
};
const notSpecialChar = () => {
ch = cookiesString.charAt(pos);
return ch !== "=" && ch !== ";" && ch !== ",";
};
while (pos < cookiesString.length) {
start = pos;
cookiesSeparatorFound = false;
while (skipWhitespace()) {
ch = cookiesString.charAt(pos);
if (ch === ",") {
lastComma = pos;
pos += 1;
skipWhitespace();
nextStart = pos;
while (pos < cookiesString.length && notSpecialChar()) {
pos += 1;
}
if (pos < cookiesString.length && cookiesString.charAt(pos) === "=") {
cookiesSeparatorFound = true;
pos = nextStart;
cookiesStrings.push(cookiesString.slice(start, lastComma));
start = pos;
} else {
pos = lastComma + 1;
}
} else {
pos += 1;
}
}
if (!cookiesSeparatorFound || pos >= cookiesString.length) {
cookiesStrings.push(cookiesString.slice(start, cookiesString.length));
}
}
return cookiesStrings;
}
const defer = typeof setImmediate === "undefined" ? (fn) => fn() : setImmediate;
function send(event, data, type) {
if (type) {
defaultContentType(event, type);
}
return new Promise((resolve) => {
defer(() => {
if (!event.handled) {
event.node.res.end(data);
}
resolve();
});
});
}
function sendNoContent(event, code) {
if (event.handled) {
return;
}
const _code = sanitizeStatusCode(code, 204);
if (_code === 204) {
event.node.res.removeHeader("content-length");
}
event.node.res.writeHead(_code);
event.node.res.end();
}
function setResponseStatus(event, code, text) {
if (code) {
event.node.res.statusCode = sanitizeStatusCode(
code,
event.node.res.statusCode
);
}
if (text) {
event.node.res.statusMessage = sanitizeStatusMessage(text);
}
}
function getResponseStatus(event) {
return event.node.res.statusCode;
}
function getResponseStatusText(event) {
return event.node.res.statusMessage;
}
function defaultContentType(event, type) {
if (type && !event.node.res.getHeader("content-type")) {
event.node.res.setHeader("content-type", type);
}
}
function sendRedirect(event, location, code = 302) {
event.node.res.statusCode = sanitizeStatusCode(
code,
event.node.res.statusCode
);
event.node.res.setHeader("location", location);
const encodedLoc = location.replace(/"/g, "%22");
const html = `<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=${encodedLoc}"></head></html>`;
return send(event, html, MIMES.html);
}
function getResponseHeaders(event) {
return event.node.res.getHeaders();
}
function getResponseHeader(event, name) {
return event.node.res.getHeader(name);
}
function setResponseHeaders(event, headers) {
for (const [name, value] of Object.entries(headers)) {
event.node.res.setHeader(name, value);
}
}
const setHeaders = setResponseHeaders;
function setResponseHeader(event, name, value) {
event.node.res.setHeader(name, value);
}
const setHeader = setResponseHeader;
function appendResponseHeaders(event, headers) {
for (const [name, value] of Object.entries(headers)) {
appendResponseHeader(event, name, value);
}
}
const appendHeaders = appendResponseHeaders;
function appendResponseHeader(event, name, value) {
let current = event.node.res.getHeader(name);
if (!current) {
event.node.res.setHeader(name, value);
return;
}
if (!Array.isArray(current)) {
current = [current.toString()];
}
event.node.res.setHeader(name, [...current, value]);
}
const appendHeader = appendResponseHeader;
function clearResponseHeaders(event, headerNames) {
if (headerNames && headerNames.length > 0) {
for (const name of headerNames) {
removeResponseHeader(event, name);
}
} else {
for (const [name] of Object.entries(getResponseHeaders(event))) {
removeResponseHeader(event, name);
}
}
}
function removeResponseHeader(event, name) {
return event.node.res.removeHeader(name);
}
function isStream(data) {
if (!data || typeof data !== "object") {
return false;
}
if (typeof data.pipe === "function") {
if (typeof data._read === "function") {
return true;
}
if (typeof data.abort === "function") {
return true;
}
}
if (typeof data.pipeTo === "function") {
return true;
}
return false;
}
function isWebResponse(data) {
return typeof Response !== "undefined" && data instanceof Response;
}
function sendStream(event, stream) {
if (!stream || typeof stream !== "object") {
throw new Error("[h3] Invalid stream provided.");
}
event.node.res._data = stream;
if (!event.node.res.socket) {
event._handled = true;
return Promise.resolve();
}
if ("pipeTo" in stream) {
return stream.pipeTo(
new WritableStream({
write(chunk) {
event.node.res.write(chunk);
}
})
).then(() => {
event.node.res.end();
});
}
if (typeof stream.pipe === "function") {
return new Promise((resolve, reject) => {
stream.pipe(event.node.res);
if (stream.on) {
stream.on("end", () => {
event.node.res.end();
resolve();
});
stream.on("error", (error) => {
reject(error);
});
}
event.node.res.on("close", () => {
if (stream.abort) {
stream.abort();
}
});
});
}
throw new Error("[h3] Invalid or incompatible stream provided.");
}
const noop = () => {
};
function writeEarlyHints(event, hints, cb = noop) {
if (!event.node.res.socket) {
cb();
return;
}
if (typeof hints === "string" || Array.isArray(hints)) {
hints = { link: hints };
}
if (hints.link) {
hints.link = Array.isArray(hints.link) ? hints.link : hints.link.split(",");
}
const headers = Object.entries(hints).map(
(e) => [e[0].toLowerCase(), e[1]]
);
if (headers.length === 0) {
cb();
return;
}
let hint = "HTTP/1.1 103 Early Hints";
if (hints.link) {
hint += `\r
Link: ${hints.link.join(", ")}`;
}
for (const [header, value] of headers) {
if (header === "link") {
continue;
}
hint += `\r
${header}: ${value}`;
}
if (event.node.res.socket) {
event.node.res.socket.write(
`${hint}\r
\r
`,
"utf8",
cb
);
} else {
cb();
}
}
function sendWebResponse(event, response) {
for (const [key, value] of response.headers) {
if (key === "set-cookie") {
event.node.res.appendHeader(key, splitCookiesString(value));
} else {
event.node.res.setHeader(key, value);
}
}
if (response.status) {
event.node.res.statusCode = sanitizeStatusCode(
response.status,
event.node.res.statusCode
);
}
if (response.statusText) {
event.node.res.statusMessage = sanitizeStatusMessage(response.statusText);
}
if (response.redirected) {
event.node.res.setHeader("location", response.url);
}
if (!response.body) {
event.node.res.end();
return;
}
return sendStream(event, response.body);
}
function resolveCorsOptions(options = {}) {
const defaultOptions = {
origin: "*",
methods: "*",
allowHeaders: "*",
exposeHeaders: "*",
credentials: false,
maxAge: false,
preflight: {
statusCode: 204
}
};
return defu(options, defaultOptions);
}
function isPreflightRequest(event) {
const origin = getRequestHeader(event, "origin");
const accessControlRequestMethod = getRequestHeader(
event,
"access-control-request-method"
);
return event.method === "OPTIONS" && !!origin && !!accessControlRequestMethod;
}
function isCorsOriginAllowed(origin, options) {
const { origin: originOption } = options;
if (!origin || !originOption || originOption === "*" || originOption === "null") {
return true;
}
if (Array.isArray(originOption)) {
return originOption.some((_origin) => {
if (_origin instanceof RegExp) {
return _origin.test(origin);
}
return origin === _origin;
});
}
return originOption(origin);
}
function createOriginHeaders(event, options) {
const { origin: originOption } = options;
const origin = getRequestHeader(event, "origin");
if (!origin || !originOption || originOption === "*") {
return { "access-control-allow-origin": "*" };
}
if (typeof originOption === "string") {
return { "access-control-allow-origin": originOption, vary: "origin" };
}
return isCorsOriginAllowed(origin, options) ? { "access-control-allow-origin": origin, vary: "origin" } : {};
}
function createMethodsHeaders(options) {
const { methods } = options;
if (!methods) {
return {};
}
if (methods === "*") {
return { "access-control-allow-methods": "*" };
}
return methods.length > 0 ? { "access-control-allow-methods": methods.join(",") } : {};
}
function createCredentialsHeaders(options) {
const { credentials } = options;
if (credentials) {
return { "access-control-allow-credentials": "true" };
}
return {};
}
function createAllowHeaderHeaders(event, options) {
const { allowHeaders } = options;
if (!allowHeaders || allowHeaders === "*" || allowHeaders.length === 0) {
const header = getRequestHeader(event, "access-control-request-headers");
return header ? {
"access-control-allow-headers": header,
vary: "access-control-request-headers"
} : {};
}
return {
"access-control-allow-headers": allowHeaders.join(","),
vary: "access-control-request-headers"
};
}
function createExposeHeaders(options) {
const { exposeHeaders } = options;
if (!exposeHeaders) {
return {};
}
if (exposeHeaders === "*") {
return { "access-control-expose-headers": exposeHeaders };
}
return { "access-control-expose-headers": exposeHeaders.join(",") };
}
function appendCorsPreflightHeaders(event, options) {
appendHeaders(event, createOriginHeaders(event, options));
appendHeaders(event, createCredentialsHeaders(options));
appendHeaders(event, createExposeHeaders(options));
appendHeaders(event, createMethodsHeaders(options));
appendHeaders(event, createAllowHeaderHeaders(event, options));
}
function appendCorsHeaders(event, options) {
appendHeaders(event, createOriginHeaders(event, options));
appendHeaders(event, createCredentialsHeaders(options));
appendHeaders(event, createExposeHeaders(options));
}
function handleCors(event, options) {
const _options = resolveCorsOptions(options);
if (isPreflightRequest(event)) {
appendCorsPreflightHeaders(event, options);
sendNoContent(event, _options.preflight.statusCode);
return true;
}
appendCorsHeaders(event, options);
return false;
}
const PayloadMethods = /* @__PURE__ */ new Set(["PATCH", "POST", "PUT", "DELETE"]);
const ignoredHeaders = /* @__PURE__ */ new Set([
"transfer-encoding",
"connection",
"keep-alive",
"upgrade",
"expect",
"host"
]);
async function proxyRequest(event, target, opts = {}) {
let body;
let duplex;
if (PayloadMethods.has(event.method)) {
if (opts.streamRequest) {
body = getRequestWebStream(event);
duplex = "half";
} else {
body = await readRawBody(event, false).catch(() => void 0);
}
}
const method = opts.fetchOptions?.method || event.method;
const fetchHeaders = mergeHeaders(
getProxyRequestHeaders(event),
opts.fetchOptions?.headers,
opts.headers
);
return sendProxy(event, target, {
...opts,
fetchOptions: {
method,
body,
duplex,
...opts.fetchOptions,
headers: fetchHeaders
}
});
}
async function sendProxy(event, target, opts = {}) {
const response = await _getFetch(opts.fetch)(target, {
headers: opts.headers,
ignoreResponseError: true,
// make $ofetch.raw transparent
...opts.fetchOptions
});
event.node.res.statusCode = sanitizeStatusCode(
response.status,
event.node.res.statusCode
);
event.node.res.statusMessage = sanitizeStatusMessage(response.statusText);
const cookies = [];
for (const [key, value] of response.headers.entries()) {
if (key === "content-encoding") {
continue;
}
if (key === "content-length") {
continue;
}
if (key === "set-cookie") {
cookies.push(...splitCookiesString(value));
continue;
}
event.node.res.setHeader(key, value);
}
if (cookies.length > 0) {
event.node.res.setHeader(
"set-cookie",
cookies.map((cookie) => {
if (opts.cookieDomainRewrite) {
cookie = rewriteCookieProperty(
cookie,
opts.cookieDomainRewrite,
"domain"
);
}
if (opts.cookiePathRewrite) {
cookie = rewriteCookieProperty(
cookie,
opts.cookiePathRewrite,
"path"
);
}
return cookie;
})
);
}
if (opts.onResponse) {
await opts.onResponse(event, response);
}
if (response._data !== void 0) {
return response._data;
}
if (event.handled) {
return;
}
if (opts.sendStream === false) {
const data = new Uint8Array(await response.arrayBuffer());
return event.node.res.end(data);
}
if (response.body) {
for await (const chunk of response.body) {
event.node.res.write(chunk);
}
}
return event.node.res.end();
}
function getProxyRequestHeaders(event) {
const headers = /* @__PURE__ */ Object.create(null);
const reqHeaders = getRequestHeaders(event);
for (const name in reqHeaders) {
if (!ignoredHeaders.has(name)) {
headers[name] = reqHeaders[name];
}
}
return headers;
}
function fetchWithEvent(event, req, init, options) {
return _getFetch(options?.fetch)(req, {
...init,
context: init?.context || event.context,
headers: {
...getProxyRequestHeaders(event),
...init?.headers
}
});
}
function _getFetch(_fetch) {
if (_fetch) {
return _fetch;
}
if (globalThis.fetch) {
return globalThis.fetch;
}
throw new Error(
"fetch is not available. Try importing `node-fetch-native/polyfill` for Node.js."
);
}
function rewriteCookieProperty(header, map, property) {
const _map = typeof map === "string" ? { "*": map } : map;
return header.replace(
new RegExp(`(;\\s*${property}=)([^;]+)`, "gi"),
(match, prefix, previousValue) => {
let newValue;
if (previousValue in _map) {
newValue = _map[previousValue];
} else if ("*" in _map) {
newValue = _map["*"];
} else {
return match;
}
return newValue ? prefix + newValue : "";
}
);
}
function mergeHeaders(defaults, ...inputs) {
const _inputs = inputs.filter(Boolean);
if (_inputs.length === 0) {
return defaults;
}
const merged = new Headers(defaults);
for (const input of _inputs) {
for (const [key, value] of Object.entries(input)) {
if (value !== void 0) {
merged.set(key, value);
}
}
}
return merged;
}
const DEFAULT_NAME = "h3";
const DEFAULT_COOKIE = {
path: "/",
secure: true,
httpOnly: true
};
async function useSession(event, config) {
const sessionName = config.name || DEFAULT_NAME;
await getSession(event, config);
const sessionManager = {
get id() {
return event.context.sessions?.[sessionName]?.id;
},
get data() {
return event.context.sessions?.[sessionName]?.data || {};
},
update: async (update) => {
await updateSession(event, config, update);
return sessionManager;
},
clear: async () => {
await clearSession(event, config);
return sessionManager;
}
};
return sessionManager;
}
async function getSession(event, config) {
const sessionName = config.name || DEFAULT_NAME;
if (!event.context.sessions) {
event.context.sessions = /* @__PURE__ */ Object.create(null);
}
if (event.context.sessions[sessionName]) {
return event.context.sessions[sessionName];
}
const session = {
id: "",
createdAt: 0,
data: /* @__PURE__ */ Object.create(null)
};
event.context.sessions[sessionName] = session;
let sealedSession;
if (config.sessionHeader !== false) {
const headerName = typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`;
const headerValue = event.node.req.headers[headerName];
if (typeof headerValue === "string") {
sealedSession = headerValue;
}
}
if (!sealedSession) {
sealedSession = getCookie(event, sessionName);
}
if (sealedSession) {
const unsealed = await unsealSession(event, config, sealedSession).catch(
() => {
}
);
Object.assign(session, unsealed);
}
if (!session.id) {
session.id = config.generateId?.() ?? (config.crypto || crypto).randomUUID();
session.createdAt = Date.now();
await updateSession(event, config);
}
return session;
}
async function updateSession(event, config, update) {
const sessionName = config.name || DEFAULT_NAME;
const session = event.context.sessions?.[sessionName] || await getSession(event, config);
if (typeof update === "function") {
update = update(session.data);
}
if (update) {
Object.assign(session.data, update);
}
if (config.cookie !== false) {
const sealed = await sealSession(event, config);
setCookie(event, sessionName, sealed, {
...DEFAULT_COOKIE,
expires: config.maxAge ? new Date(session.createdAt + config.maxAge * 1e3) : void 0,
...config.cookie
});
}
return session;
}
async function sealSession(event, config) {
const sessionName = config.name || DEFAULT_NAME;
const session = event.context.sessions?.[sessionName] || await getSession(event, config);
const sealed = await seal(config.crypto || crypto, session, config.password, {
...defaults,
ttl: config.maxAge ? config.maxAge * 1e3 : 0,
...config.seal
});
return sealed;
}
async function unsealSession(_event, config, sealed) {
const unsealed = await unseal(
config.crypto || crypto,
sealed,
config.password,
{
...defaults,
ttl: config.maxAge ? config.maxAge * 1e3 : 0,
...config.seal
}
);
if (config.maxAge) {
const age = Date.now() - (unsealed.createdAt || Number.NEGATIVE_INFINITY);
if (age > config.maxAge * 1e3) {
throw new Error("Session expired!");
}
}
return unsealed;
}
async function clearSession(event, config) {
const sessionName = config.name || DEFAULT_NAME;
if (event.context.sessions?.[sessionName]) {
delete event.context.sessions[sessionName];
}
await setCookie(event, sessionName, "", {
...DEFAULT_COOKIE,
...config.cookie
});
}
async function serveStatic(event, options) {
if (event.method !== "GET" && event.method !== "HEAD") {
if (!options.fallthrough) {
throw createError({
statusMessage: "Method Not Allowed",
statusCode: 405
});
}
return false;
}
const originalId = decodePath(
withLeadingSlash(withoutTrailingSlash(parseURL(event.path).pathname))
);
const acceptEncodings = parseAcceptEncoding(
getRequestHeader(event, "accept-encoding"),
options.encodings
);
if (acceptEncodings.length > 1) {
setResponseHeader(event, "vary", "accept-encoding");
}
let id = originalId;
let meta;
const _ids = idSearchPaths(
originalId,
acceptEncodings,
options.indexNames || ["/index.html"]
);
for (const _id of _ids) {
const _meta = await options.getMeta(_id);
if (_meta) {
meta = _meta;
id = _id;
break;
}
}
if (!meta) {
if (!options.fallthrough) {
throw createError({
statusMessage: "Cannot find static asset " + id,
statusCode: 404
});
}
return false;
}
const ifNotMatch = meta.etag && getRequestHeader(event, "if-none-match") === meta.etag;
if (ifNotMatch) {
setResponseStatus(event, 304, "Not Modified");
return send(event, "");
}
if (meta.mtime) {
const mtimeDate = new Date(meta.mtime);
const ifModifiedSinceH = getRequestHeader(event, "if-modified-since");
if (ifModifiedSinceH && new Date(ifModifiedSinceH) >= mtimeDate) {
setResponseStatus(event, 304, "Not Modified");
return send(event, null);
}
if (!getResponseHeader(event, "last-modified")) {
setResponseHeader(event, "last-modified", mtimeDate.toUTCString());
}
}
if (meta.type && !getResponseHeader(event, "content-type")) {
setResponseHeader(event, "content-type", meta.type);
}
if (meta.etag && !getResponseHeader(event, "etag")) {
setResponseHeader(event, "etag", meta.etag);
}
if (meta.encoding && !getResponseHeader(event, "content-encoding")) {
setResponseHeader(event, "content-encoding", meta.encoding);
}
if (meta.size !== void 0 && meta.size > 0 && !getResponseHeader(event, "content-length")) {
setResponseHeader(event, "content-length", meta.size);
}
if (event.method === "HEAD") {
return send(event, null);
}
const contents = await options.getContents(id);
return isStream(contents) ? sendStream(event, contents) : send(event, contents);
}
function parseAcceptEncoding(header, encodingMap) {
if (!encodingMap || !header) {
return [];
}
return String(header || "").split(",").map((e) => encodingMap[e.trim()]).filter(Boolean);
}
function idSearchPaths(id, encodings, indexNames) {
const ids = [];
for (const suffix of ["", ...indexNames]) {
for (const encoding of [...encodings, ""]) {
ids.push(`${id}${suffix}${encoding}`);
}
}
return ids;
}
class H3Event {
constructor(req, res) {
this["__is_event__"] = true;
// Web
this.context = {};
// Response
this._handled = false;
this.node = { req, res };
}
// --- Request ---
get method() {
if (!this._method) {
this._method = (this.node.req.method || "GET").toUpperCase();
}
return this._method;
}
get path() {
return this._path || this.node.req.url || "/";
}
get headers() {
if (!this._headers) {
this._headers = _normalizeNodeHeaders(this.node.req.headers);
}
return this._headers;
}
// --- Respoonse ---
get handled() {
return this._handled || this.node.res.writableEnded || this.node.res.headersSent;
}
respondWith(response) {
return Promise.resolve(response).then(
(_response) => sendWebResponse(this, _response)
);
}
// --- Utils ---
toString() {
return `[${this.method}] ${this.path}`;
}
toJSON() {
return this.toString();
}
// --- Deprecated ---
/** @deprecated Please use `event.node.req` instead. **/
get req() {
return this.node.req;
}
/** @deprecated Please use `event.node.res` instead. **/
get res() {
return this.node.res;
}
}
function isEvent(input) {
return "__is_event__" in input;
}
function createEvent(req, res) {
return new H3Event(req, res);
}
function _normalizeNodeHeaders(nodeHeaders) {
const headers = new Headers();
for (const [name, value] of Object.entries(nodeHeaders)) {
if (Array.isArray(value)) {
for (const item of value) {
headers.append(name, item);
}
} else if (value) {
headers.set(name, value);
}
}
return headers;
}
function defineEventHandler(handler) {
if (typeof handler === "function") {
return Object.assign(handler, { __is_handler__: true });
}
const _hooks = {
onRequest: _normalizeArray(handler.onRequest),
onBeforeResponse: _normalizeArray(handler.onBeforeResponse)
};
const _handler = (event) => {
return _callHandler(event, handler.handler, _hooks);
};
return Object.assign(_handler, { __is_handler__: true });
}
function _normalizeArray(input) {
return input ? Array.isArray(input) ? input : [input] : void 0;
}
async function _callHandler(event, handler, hooks) {
if (hooks.onRequest) {
for (const hook of hooks.onRequest) {
await hook(event);
if (event.handled) {
return;
}
}
}
const body = await handler(event);
const response = { body };
if (hooks.onBeforeResponse) {
for (const hook of hooks.onBeforeResponse) {
await hook(event, response);
}
}
return response.body;
}
const eventHandler = defineEventHandler;
function defineRequestMiddleware(fn) {
return fn;
}
function defineResponseMiddleware(fn) {
return fn;
}
function isEventHandler(input) {
return "__is_handler__" in input;
}
function toEventHandler(input, _, _route) {
if (!isEventHandler(input)) {
console.warn(
"[h3] Implicit event handler conversion is deprecated. Use `eventHandler()` or `fromNodeMiddleware()` to define event handlers.",
_route && _route !== "/" ? `
Route: ${_route}` : "",
`
Handler: ${input}`
);
}
return input;
}
function dynamicEventHandler(initial) {
let current = initial;
const wrapper = eventHandler((event) => {
if (current) {
return current(event);
}
});
wrapper.set = (handler) => {
current = handler;
};
return wrapper;
}
function defineLazyEventHandler(factory) {
let _promise;
let _resolved;
const resolveHandler = () => {
if (_resolved) {
return Promise.resolve(_resolved);
}
if (!_promise) {
_promise = Promise.resolve(factory()).then((r) => {
const handler = r.default || r;
if (typeof handler !== "function") {
throw new TypeError(
"Invalid lazy handler result. It should be a function:",
handler
);
}
_resolved = toEventHandler(r.default || r);
return _resolved;
});
}
return _promise;
};
return eventHandler((event) => {
if (_resolved) {
return _resolved(event);
}
return resolveHandler().then((handler) => handler(event));
});
}
const lazyEventHandler = defineLazyEventHandler;
class H3Headers {
constructor(init) {
if (!init) {
this._headers = {};
} else if (Array.isArray(init)) {
this._headers = Object.fromEntries(
init.map(([key, value]) => [key.toLowerCase(), value])
);
} else if (init && "append" in init) {
this._headers = Object.fromEntries(init.entries());
} else {
this._headers = Object.fromEntries(
Object.entries(init).map(([key, value]) => [key.toLowerCase(), value])
);
}
}
[Symbol.iterator]() {
return this.entries();
}
entries() {
throw Object.entries(this._headers)[Symbol.iterator]();
}
keys() {
return Object.keys(this._headers)[Symbol.iterator]();
}
values() {
throw Object.values(this._headers)[Symbol.iterator]();
}
append(name, value) {
const _name = name.toLowerCase();
this.set(_name, [this.get(_name), value].filter(Boolean).join(", "));
}
delete(name) {
delete this._headers[name.toLowerCase()];
}
get(name) {
return this._headers[name.toLowerCase()];
}
has(name) {
return name.toLowerCase() in this._headers;
}
set(name, value) {
this._headers[name.toLowerCase()] = String(value);
}
forEach(callbackfn) {
for (const [key, value] of Object.entries(this._headers)) {
callbackfn(value, key, this);
}
}
}
class H3Response {
constructor(body = null, init = {}) {
// TODO: yet to implement
this.body = null;
this.type = "default";
this.bodyUsed = false;
this.headers = new H3Headers(init.headers);
this.status = init.status ?? 200;
this.statusText = init.statusText || "";
this.redirected = !!init.status && [301, 302, 307, 308].includes(init.status);
this._body = body;
this.url = "";
this.ok = this.status < 300 && this.status > 199;
}
clone() {
return new H3Response(this.body, {
headers: this.headers,
status: this.status,
statusText: this.statusText
});
}
arrayBuffer() {
return Promise.resolve(this._body);
}
blob() {
return Promise.resolve(this._body);
}
formData() {
return Promise.resolve(this._body);
}
json() {
return Promise.resolve(this._body);
}
text() {
return Promise.resolve(this._body);
}
}
function createApp(options = {}) {
const stack = [];
const handler = createAppEventHandler(stack, options);
const app = {
// @ts-ignore
use: (arg1, arg2, arg3) => use(app, arg1, arg2, arg3),
handler,
stack,
options
};
return app;
}
function use(app, arg1, arg2, arg3) {
if (Array.isArray(arg1)) {
for (const i of arg1) {
use(app, i, arg2, arg3);
}
} else if (Array.isArray(arg2)) {
for (const i of arg2) {
use(app, arg1, i, arg3);
}
} else if (typeof arg1 === "string") {
app.stack.push(
normalizeLayer({ ...arg3, route: arg1, handler: arg2 })
);
} else if (typeof arg1 === "function") {
app.stack.push(
normalizeLayer({ ...arg2, route: "/", handler: arg1 })
);
} else {
app.stack.push(normalizeLayer({ ...arg1 }));
}
return app;
}
function createAppEventHandler(stack, options) {
const spacing = options.debug ? 2 : void 0;
return eventHandler(async (event) => {
event.node.req.originalUrl = event.node.req.originalUrl || event.node.req.url || "/";
const _reqPath = event._path || event.node.req.url || "/";
let _layerPath;
if (options.onRequest) {
await options.onRequest(event);
}
for (const layer of stack) {
if (layer.route.length > 1) {
if (!_reqPath.startsWith(layer.route)) {
continue;
}
_layerPath = _reqPath.slice(layer.route.length) || "/";
} else {
_layerPath = _reqPath;
}
if (layer.match && !layer.match(_layerPath, event)) {
continue;
}
event._path = _layerPath;
event.node.req.url = _layerPath;
const val = await layer.handler(event);
const _body = val === void 0 ? void 0 : await val;
if (_body !== void 0) {
const _response = { body: _body };
if (options.onBeforeResponse) {
await options.onBeforeResponse(event, _response);
}
await handleHandlerResponse(event, _response.body, spacing);
if (options.onAfterResponse) {
await options.onAfterResponse(event, _response);
}
return;
}
if (event.handled) {
if (options.onAfterResponse) {
await options.onAfterResponse(event, void 0);
}
return;
}
}
if (!event.handled) {
throw createError({
statusCode: 404,
statusMessage: `Cannot find any path matching ${event.path || "/"}.`
});
}
if (options.onAfterResponse) {
await options.onAfterResponse(event, void 0);
}
});
}
function normalizeLayer(input) {
let handler = input.handler;
if (handler.handler) {
handler = handler.handler;
}
if (input.lazy) {
handler = lazyEventHandler(handler);
} else if (!isEventHandler(handler)) {
handler = toEventHandler(handler, void 0, input.route);
}
return {
route: withoutTrailingSlash(input.route),
match: input.match,
handler
};
}
function handleHandlerResponse(event, val, jsonSpace) {
if (val === null) {
return sendNoContent(event);
}
if (val) {
if (isWebResponse(val)) {
return sendWebResponse(event, val);
}
if (isStream(val)) {
return sendStream(event, val);
}
if (val.buffer) {
return send(event, val);
}
if (val.arrayBuffer && typeof val.arrayBuffer === "function") {
return val.arrayBuffer().then((arrayBuffer) => {
return send(event, Buffer.from(arrayBuffer), val.type);
});
}
if (val instanceof Error) {
throw createError(val);
}
if (typeof val.end === "function") {
return true;
}
}
const valType = typeof val;
if (valType === "string") {
return send(event, val, MIMES.html);
}
if (valType === "object" || valType === "boolean" || valType === "number") {
return send(event, JSON.stringify(val, void 0, jsonSpace), MIMES.json);
}
if (valType === "bigint") {
return send(event, val.toString(), MIMES.json);
}
throw createError({
statusCode: 500,
statusMessage: `[h3] Cannot send ${valType} as response.`
});
}
const RouterMethods = [
"connect",
"delete",
"get",
"head",
"options",
"post",
"put",
"trace",
"patch"
];
function createRouter(opts = {}) {
const _router = createRouter$1({});
const routes = {};
let _matcher;
const router = {};
const addRoute = (path, handler, method) => {
let route = routes[path];
if (!route) {
routes[path] = route = { path, handlers: {} };
_router.insert(path, route);
}
if (Array.isArray(method)) {
for (const m of method) {
addRoute(path, handler, m);
}
} else {
route.handlers[method] = toEventHandler(handler, void 0, path);
}
return router;
};
router.use = router.add = (path, handler, method) => addRoute(path, handler, method || "all");
for (const method of RouterMethods) {
router[method] = (path, handle) => router.add(path, handle, method);
}
router.handler = eventHandler((event) => {
let path = event.path || "/";
const qIndex = path.indexOf("?");
if (qIndex !== -1) {
path = path.slice(0, Math.max(0, qIndex));
}
const matched = _router.lookup(path);
if (!matched || !matched.handlers) {
if (opts.preemptive || opts.preemtive) {
throw createError({
statusCode: 404,
name: "Not Found",
statusMessage: `Cannot find any route matching ${event.path || "/"}.`
});
} else {
return;
}
}
const method = (event.node.req.method || "get").toLowerCase();
let handler = matched.handlers[method] || matched.handlers.all;
if (!handler) {
if (!_matcher) {
_matcher = toRouteMatcher(_router);
}
const _matches = _matcher.matchAll(path).reverse();
for (const _match of _matches) {
if (_match.handlers[method]) {
handler = _match.handlers[method];
matched.handlers[method] = matched.handlers[method] || handler;
break;
}
if (_match.handlers.all) {
handler = _match.handlers.all;
matched.handlers.all = matched.handlers.all || handler;
break;
}
}
}
if (!handler) {
if (opts.preemptive || opts.preemtive) {
throw createError({
statusCode: 405,
name: "Method Not Allowed",
statusMessage: `Method ${method} is not allowed on this route.`
});
} else {
return;
}
}
event.context.matchedRoute = matched;
const params = matched.params || {};
event.context.params = params;
return Promise.resolve(handler(event)).then((res) => {
if (res === void 0 && (opts.preemptive || opts.preemtive)) {
return null;
}
return res;
});
});
return router;
}
const defineNodeListener = (handler) => handler;
const defineNodeMiddleware = (middleware) => middleware;
function fromNodeMiddleware(handler) {
if (isEventHandler(handler)) {
return handler;
}
if (typeof handler !== "function") {
throw new TypeError(
"Invalid handler. It should be a function:",
handler
);
}
return eventHandler((event) => {
return callNodeListener(
handler,
event.node.req,
event.node.res
);
});
}
function toNodeListener(app) {
const toNodeHandle = async function(req, res) {
const event = createEvent(req, res);
try {
await app.handler(event);
} catch (_error) {
const error = createError(_error);
if (!isError(_error)) {
error.unhandled = true;
}
if (app.options.onError) {
await app.options.onError(error, event);
}
if (event.handled) {
return;
}
if (error.unhandled || error.fatal) {
console.error("[h3]", error.fatal ? "[fatal]" : "[unhandled]", error);
}
await sendError(event, error, !!app.options.debug);
}
};
return toNodeHandle;
}
function promisifyNodeListener(handler) {
return function(req, res) {
return callNodeListener(handler, req, res);
};
}
function callNodeListener(handler, req, res) {
const isMiddleware = handler.length > 2;
return new Promise((resolve, reject) => {
const next = (err) => {
if (isMiddleware) {
res.off("close", next);
res.off("error", next);
}
return err ? reject(createError(err)) : resolve(void 0);
};
try {
const returned = handler(req, res, next);
if (isMiddleware && returned === void 0) {
res.once("close", next);
res.once("error", next);
} else {
resolve(returned);
}
} catch (error) {
next(error);
}
});
}
function toPlainHandler(app) {
const handler = (request) => {
return _handlePlainRequest(app, request);
};
return handler;
}
function fromPlainHandler(handler) {
return eventHandler(async (event) => {
const res = await handler({
method: event.method,
path: event.path,
headers: Object.fromEntries(event.headers.entries()),
body: getRequestWebStream(event),
context: event.context
});
setResponseStatus(event, res.status, res.statusText);
for (const [key, value] of res.headers) {
setResponseHeader(event, key, value);
}
return res.body;
});
}
async function _handlePlainRequest(app, request) {
const path = request.path;
const method = (request.method || "GET").toUpperCase();
const headers = new Headers(request.headers);
const nodeReq = new IncomingMessage();
const nodeRes = new ServerResponse(nodeReq);
nodeReq.method = method;
nodeReq.url = path;
nodeReq.headers = Object.fromEntries(headers.entries());
const event = createEvent(nodeReq, nodeRes);
event._method = method;
event._path = path;
event._headers = headers;
if (request.body) {
event._requestBody = request.body;
}
if (request._eventOverrides) {
Object.assign(event, request._eventOverrides);
}
if (request.context) {
Object.assign(event.context, request.context);
}
try {
await app.handler(event);
} catch (_error) {
const error = createError(_error);
if (!isError(_error)) {
error.unhandled = true;
}
if (app.options.onError) {
await app.options.onError(error, event);
}
if (!event.handled) {
if (error.unhandled || error.fatal) {
console.error("[h3]", error.fatal ? "[fatal]" : "[unhandled]", error);
}
await sendError(event, error, !!app.options.debug);
}
}
return {
status: nodeRes.statusCode,
statusText: nodeRes.statusMessage,
headers: _normalizeUnenvHeaders(nodeRes._headers),
body: nodeRes._data
};
}
function _normalizeUnenvHeaders(input) {
const headers = [];
const cookies = [];
for (const _key in input) {
const key = _key.toLowerCase();
if (key === "set-cookie") {
cookies.push(
...splitCookiesString(input["set-cookie"])
);
continue;
}
const value = input[key];
if (Array.isArray(value)) {
for (const _value of value) {
headers.push([key, _value]);
}
} else if (value !== void 0) {
headers.push([key, String(value)]);
}
}
if (cookies.length > 0) {
for (const cookie of cookies) {
headers.push(["set-cookie", cookie]);
}
}
return headers;
}
function toWebHandler(app) {
const webHandler = (request, context) => {
return _handleWebRequest(app, request, context);
};
return webHandler;
}
function fromWebHandler(handler) {
return eventHandler((event) => handler(toWebRequest(event), event.context));
}
const nullBodyResponses = /* @__PURE__ */ new Set([101, 204, 205, 304]);
async function _handleWebRequest(app, request, context) {
const url = new URL(request.url);
const res = await _handlePlainRequest(app, {
_eventOverrides: {
web: { request, url }
},
context,
method: request.method,
path: url.pathname + url.search,
headers: request.headers,
body: request.body
});
const body = nullBodyResponses.has(res.status) || request.method === "HEAD" ? null : res.body;
return new Response(body, {
status: res.status,
statusText: res.statusText,
headers: res.headers
});
}
export { H3Error, H3Event, H3Headers, H3Response, MIMES, appendCorsHeaders, appendCorsPreflightHeaders, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, clearResponseHeaders, clearSession, createApp, createAppEventHandler, createError, createEvent, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, defineRequestMiddleware, defineResponseMiddleware, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, fromNodeMiddleware, fromPlainHandler, fromWebHandler, getCookie, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestHeader, getRequestHeaders, getRequestHost, getRequestIP, getRequestPath, getRequestProtocol, getRequestURL, getRequestWebStream, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, getValidatedQuery, handleCacheHeaders, handleCors, isCorsOriginAllowed, isError, isEvent, isEventHandler, isMethod, isPreflightRequest, isStream, isWebResponse, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readFormData, readMultipartFormData, readRawBody, readValidatedBody, removeResponseHeader, sanitizeStatusCode, sanitizeStatusMessage, sealSession, send, sendError, sendNoContent, sendProxy, sendRedirect, sendStream, sendWebResponse, serveStatic, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, splitCookiesString, toEventHandler, toNodeListener, toPlainHandler, toWebHandler, toWebRequest, unsealSession, updateSession, use, useBase, useSession, writeEarlyHints };