'use strict'; const ufo = require('ufo'); const cookieEs = require('cookie-es'); const ohash = require('ohash'); const radix3 = require('radix3'); const destr = require('destr'); const defu = require('defu'); const crypto = require('uncrypto'); const ironWebcrypto = require('iron-webcrypto'); const index = require('unenv/runtime/node/http/index'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; } const destr__default = /*#__PURE__*/_interopDefaultCompat(destr); const crypto__default = /*#__PURE__*/_interopDefaultCompat(crypto); function useBase(base, handler) { base = ufo.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 = ufo.withoutBase(event.path || "/", base); event.node.req.url = event._path; try { return await handler(event); } finally { event._path = event.node.req.url = _path; } }); } function hasProp(obj, prop) { try { return prop in obj; } catch { return false; } } var __defProp$2 = Object.defineProperty; var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField$2 = (obj, key, value) => { __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; class H3Error extends Error { constructor(message, opts = {}) { super(message, opts); __publicField$2(this, "statusCode", 500); __publicField$2(this, "fatal", false); __publicField$2(this, "unhandled", false); __publicField$2(this, "statusMessage"); __publicField$2(this, "data"); __publicField$2(this, "cause"); 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; } } __publicField$2(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 (hasProp(input, "stack")) { 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, statusMessage: "Validation Error", message: validateError?.message || "Validation Error", data: validateError }); } function getQuery(event) { return ufo.getQuery(event.path || ""); } function getValidatedQuery(event, validate) { const query = getQuery(event); return validateData(query, validate); } function getRouterParams(event, opts = {}) { let params = event.context.params || {}; if (opts.decode) { params = { ...params }; for (const key in params) { params[key] = ufo.decode(params[key]); } } return params; } function getValidatedRouterParams(event, validate, opts = {}) { const routerParams = getRouterParams(event, opts); return validateData(routerParams, validate); } function getRouterParam(event, name, opts = {}) { const params = getRouterParams(event, opts); 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, opts); 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(",").shift()?.trim(); 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.rawBody || 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)); } if (_resolved instanceof URLSearchParams) { return Buffer.from(_resolved.toString()); } return Buffer.from(_resolved); }); return encoding ? promise2.then((buff) => buff.toString(encoding)) : promise2; } if (!Number.parseInt(event.node.req.headers["content-length"] || "") && !String(event.node.req.headers["transfer-encoding"] ?? "").split(",").map((e) => e.trim()).filter(Boolean).includes("chunked")) { 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 (hasProp(request, ParsedBodySymbol)) { 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; } const bodyStream = event.web?.request?.body || event._requestBody; if (bodyStream) { return bodyStream; } const _hasRawBody = RawBodySymbol in event.node.req || "rawBody" in event.node.req || "body" in event.node.req || "__unenv__" in event.node.req; if (_hasRawBody) { return new ReadableStream({ async start(controller) { const _rawBody = await readRawBody(event, false); if (_rawBody) { controller.enqueue(_rawBody); } controller.close(); } }); } return 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__default(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 (hasProp(parsedForm, key)) { 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 cookieEs.parse(event.node.req.headers.cookie || ""); } function getCookie(event, name) { return parseCookies(event)[name]; } function setCookie(event, name, value, serializeOptions) { serializeOptions = { path: "/", ...serializeOptions }; const cookieStr = cookieEs.serialize(name, value, serializeOptions); let setCookies = event.node.res.getHeader("set-cookie"); if (!Array.isArray(setCookies)) { setCookies = [setCookies]; } const _optionsHash = ohash.objectHash(serializeOptions); setCookies = setCookies.filter((cookieValue) => { return cookieValue && _optionsHash !== ohash.objectHash(cookieEs.parse(cookieValue)); }); 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)); } } return cookiesStrings; } function serializeIterableValue(value) { switch (typeof value) { case "string": { return value; } case "boolean": case "number": case "bigint": case "symbol": { return value.toString(); } case "function": case "undefined": { return void 0; } case "object": { if (value instanceof Uint8Array) { return value; } return JSON.stringify(value); } } } function coerceIterable(iterable) { if (typeof iterable === "function") { iterable = iterable(); } if (Symbol.iterator in iterable) { return iterable[Symbol.iterator](); } if (Symbol.asyncIterator in iterable) { return iterable[Symbol.asyncIterator](); } return iterable; } 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; } if (!code && event.node.res.statusCode !== 200) { code = event.node.res.statusCode; } 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.statusCode !== 304 && !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 = `
`; 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 (hasProp(stream, "pipeTo") && typeof stream.pipeTo === "function") { return stream.pipeTo( new WritableStream({ write(chunk) { event.node.res.write(chunk); } }) ).then(() => { event.node.res.end(); }); } if (hasProp(stream, "pipe") && 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 sendIterable(event, iterable, options) { const serializer = options?.serializer ?? serializeIterableValue; const iterator = coerceIterable(iterable); return sendStream( event, new ReadableStream({ async pull(controller) { const { value, done } = await iterator.next(); if (value !== void 0) { const chunk = serializer(value); if (chunk !== void 0) { controller.enqueue(chunk); } } if (done) { controller.close(); } }, cancel() { iterator.return?.(); } }) ); } function resolveCorsOptions(options = {}) { const defaultOptions = { origin: "*", methods: "*", allowHeaders: "*", exposeHeaders: "*", credentials: false, maxAge: false, preflight: { statusCode: 204 } }; return defu.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; } async function getRequestFingerprint(event, opts = {}) { const fingerprint = []; if (opts.ip !== false) { fingerprint.push( getRequestIP(event, { xForwardedFor: opts.xForwardedFor }) ); } if (opts.method === true) { fingerprint.push(event.method); } if (opts.path === true) { fingerprint.push(event.path); } if (opts.userAgent === true) { fingerprint.push(getRequestHeader(event, "user-agent")); } const fingerprintString = fingerprint.filter(Boolean).join("|"); if (!fingerprintString) { return null; } if (opts.hash === false) { return fingerprintString; } const buffer = await crypto__default.subtle.digest( opts.hash || "SHA-1", new TextEncoder().encode(fingerprintString) ); const hash = [...new Uint8Array(buffer)].map((b) => b.toString(16).padStart(2, "0")).join(""); return hash; } const PayloadMethods = /* @__PURE__ */ new Set(["PATCH", "POST", "PUT", "DELETE"]); const ignoredHeaders = /* @__PURE__ */ new Set([ "transfer-encoding", "connection", "keep-alive", "upgrade", "expect", "host", "accept" ]); 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 = {}) { let response; try { response = await _getFetch(opts.fetch)(target, { headers: opts.headers, ignoreResponseError: true, // make $ofetch.raw transparent ...opts.fetchOptions }); } catch (error) { throw createError({ status: 502, statusMessage: "Bad Gateway", cause: error }); } 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 getSessionPromise = Symbol("getSession"); 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: () => { clearSession(event, config); return Promise.resolve(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); } const existingSession = event.context.sessions[sessionName]; if (existingSession) { return existingSession[getSessionPromise] || existingSession; } 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 promise = unsealSession(event, config, sealedSession).catch(() => { }).then((unsealed) => { Object.assign(session, unsealed); delete event.context.sessions[sessionName][getSessionPromise]; return session; }); event.context.sessions[sessionName][getSessionPromise] = promise; await promise; } if (!session.id) { session.id = config.generateId?.() ?? (config.crypto || crypto__default).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 ironWebcrypto.seal(config.crypto || crypto__default, session, config.password, { ...ironWebcrypto.defaults, ttl: config.maxAge ? config.maxAge * 1e3 : 0, ...config.seal }); return sealed; } async function unsealSession(_event, config, sealed) { const unsealed = await ironWebcrypto.unseal( config.crypto || crypto__default, sealed, config.password, { ...ironWebcrypto.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; } function clearSession(event, config) { const sessionName = config.name || DEFAULT_NAME; if (event.context.sessions?.[sessionName]) { delete event.context.sessions[sessionName]; } setCookie(event, sessionName, "", { ...DEFAULT_COOKIE, ...config.cookie }); return Promise.resolve(); } function formatEventStreamMessage(message) { let result = ""; if (message.id) { result += `id: ${message.id} `; } if (message.event) { result += `event: ${message.event} `; } if (typeof message.retry === "number" && Number.isInteger(message.retry)) { result += `retry: ${message.retry} `; } result += `data: ${message.data} `; return result; } function formatEventStreamMessages(messages) { let result = ""; for (const msg of messages) { result += formatEventStreamMessage(msg); } return result; } function setEventStreamHeaders(event) { const headers = { "Content-Type": "text/event-stream", "Cache-Control": "private, no-cache, no-store, no-transform, must-revalidate, max-age=0", "X-Accel-Buffering": "no" // prevent nginx from buffering the response }; if (!isHttp2Request(event)) { headers.Connection = "keep-alive"; } setResponseHeaders(event, headers); } function isHttp2Request(event) { return getHeader(event, ":path") !== void 0 && getHeader(event, ":method") !== void 0; } var __defProp$1 = Object.defineProperty; var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField$1 = (obj, key, value) => { __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; class EventStream { constructor(event, opts = {}) { __publicField$1(this, "_h3Event"); __publicField$1(this, "_transformStream", new TransformStream()); __publicField$1(this, "_writer"); __publicField$1(this, "_encoder", new TextEncoder()); __publicField$1(this, "_writerIsClosed", false); __publicField$1(this, "_paused", false); __publicField$1(this, "_unsentData"); __publicField$1(this, "_disposed", false); __publicField$1(this, "_handled", false); this._h3Event = event; this._writer = this._transformStream.writable.getWriter(); this._writer.closed.then(() => { this._writerIsClosed = true; }); if (opts.autoclose !== false) { this._h3Event.node.req.on("close", () => this.close()); } } async push(message) { if (typeof message === "string") { await this._sendEvent({ data: message }); return; } if (Array.isArray(message)) { if (message.length === 0) { return; } if (typeof message[0] === "string") { const msgs = []; for (const item of message) { msgs.push({ data: item }); } await this._sendEvents(msgs); return; } await this._sendEvents(message); return; } await this._sendEvent(message); } async _sendEvent(message) { if (this._writerIsClosed) { return; } if (this._paused && !this._unsentData) { this._unsentData = formatEventStreamMessage(message); return; } if (this._paused) { this._unsentData += formatEventStreamMessage(message); return; } await this._writer.write(this._encoder.encode(formatEventStreamMessage(message))).catch(); } async _sendEvents(messages) { if (this._writerIsClosed) { return; } const payload = formatEventStreamMessages(messages); if (this._paused && !this._unsentData) { this._unsentData = payload; return; } if (this._paused) { this._unsentData += payload; return; } await this._writer.write(this._encoder.encode(payload)).catch(); } pause() { this._paused = true; } get isPaused() { return this._paused; } async resume() { this._paused = false; await this.flush(); } async flush() { if (this._writerIsClosed) { return; } if (this._unsentData?.length) { await this._writer.write(this._encoder.encode(this._unsentData)); this._unsentData = void 0; } } /** * Close the stream and the connection if the stream is being sent to the client */ async close() { if (this._disposed) { return; } if (!this._writerIsClosed) { try { await this._writer.close(); } catch { } } if (this._h3Event._handled && this._handled && !this._h3Event.node.res.closed) { this._h3Event.node.res.end(); } this._disposed = true; } /** * Triggers callback when the writable stream is closed. * It is also triggered after calling the `close()` method. */ onClosed(cb) { this._writer.closed.then(cb); } async send() { setEventStreamHeaders(this._h3Event); setResponseStatus(this._h3Event, 200); this._h3Event._handled = true; this._handled = true; await sendStream(this._h3Event, this._transformStream.readable); } } function createEventStream(event, opts) { return new EventStream(event, opts); } 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 = ufo.decodePath( ufo.withLeadingSlash(ufo.withoutTrailingSlash(ufo.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; } if (meta.etag && !getResponseHeader(event, "etag")) { setResponseHeader(event, "etag", meta.etag); } 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.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; } function defineWebSocket(hooks) { return hooks; } function defineWebSocketHandler(hooks) { return defineEventHandler({ handler() { throw createError({ statusCode: 426, statusMessage: "Upgrade Required" }); }, websocket: hooks }); } var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; class H3Event { constructor(req, res) { __publicField(this, "__is_event__", true); // Context __publicField(this, "node"); // Node __publicField(this, "web"); // Web __publicField(this, "context", {}); // Shared // Request __publicField(this, "_method"); __publicField(this, "_path"); __publicField(this, "_headers"); __publicField(this, "_requestBody"); // Response __publicField(this, "_handled", false); // Hooks __publicField(this, "_onBeforeResponseCalled"); __publicField(this, "_onAfterResponseCalled"); 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 hasProp(input, "__is_event__"); } 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") { handler.__is_handler__ = true; return handler; } const _hooks = { onRequest: _normalizeArray(handler.onRequest), onBeforeResponse: _normalizeArray(handler.onBeforeResponse) }; const _handler = (event) => { return _callHandler(event, handler.handler, _hooks); }; _handler.__is_handler__ = true; _handler.__resolve__ = handler.handler.__resolve__; _handler.__websocket__ = handler.websocket; return _handler; } 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 hasProp(input, "__is_handler__"); } 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 handler2 = r.default || r; if (typeof handler2 !== "function") { throw new TypeError( "Invalid lazy handler result. It should be a function:", handler2 ); } _resolved = { handler: toEventHandler(r.default || r) }; return _resolved; }); } return _promise; }; const handler = eventHandler((event) => { if (_resolved) { return _resolved.handler(event); } return resolveHandler().then((r) => r.handler(event)); }); handler.__resolve__ = resolveHandler; return handler; } const lazyEventHandler = defineLazyEventHandler; const H3Headers = globalThis.Headers; const H3Response = globalThis.Response; function createApp(options = {}) { const stack = []; const handler = createAppEventHandler(stack, options); const resolve = createResolver(stack); handler.__resolve__ = resolve; const getWebsocket = cachedFn(() => websocketOptions(resolve, options)); const app = { // @ts-expect-error use: (arg1, arg2, arg3) => use(app, arg1, arg2, arg3), resolve, handler, stack, options, get websocket() { return getWebsocket(); } }; 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, 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) { event._onBeforeResponseCalled = true; await options.onBeforeResponse(event, _response); } await handleHandlerResponse(event, _response.body, spacing); if (options.onAfterResponse) { event._onAfterResponseCalled = true; await options.onAfterResponse(event, _response); } return; } if (event.handled) { if (options.onAfterResponse) { event._onAfterResponseCalled = true; 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) { event._onAfterResponseCalled = true; await options.onAfterResponse(event, void 0); } }); } function createResolver(stack) { return async (path) => { let _layerPath; for (const layer of stack) { if (layer.route === "/" && !layer.handler.__resolve__) { continue; } if (!path.startsWith(layer.route)) { continue; } _layerPath = path.slice(layer.route.length) || "/"; if (layer.match && !layer.match(_layerPath, void 0)) { continue; } let res = { route: layer.route, handler: layer.handler }; if (res.handler.__resolve__) { const _res = await res.handler.__resolve__(_layerPath); if (!_res) { continue; } res = { ...res, ..._res, route: ufo.joinURL(res.route || "/", _res.route || "/") }; } return res; } }; } 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: ufo.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.` }); } function cachedFn(fn) { let cache; return () => { if (!cache) { cache = fn(); } return cache; }; } function websocketOptions(evResolver, appOptions) { return { ...appOptions.websocket, async resolve(info) { const url = info.request?.url || info.url || "/"; const { pathname } = typeof url === "string" ? ufo.parseURL(url) : url; const resolved = await evResolver(pathname); return resolved?.handler?.__websocket__ || {}; } }; } const RouterMethods = [ "connect", "delete", "get", "head", "options", "post", "put", "trace", "patch" ]; function createRouter(opts = {}) { const _router = radix3.createRouter({}); 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); } const matchHandler = (path = "/", method = "get") => { const qIndex = path.indexOf("?"); if (qIndex !== -1) { path = path.slice(0, Math.max(0, qIndex)); } const matched = _router.lookup(path); if (!matched || !matched.handlers) { return { error: createError({ statusCode: 404, name: "Not Found", statusMessage: `Cannot find any route matching ${path || "/"}.` }) }; } let handler = matched.handlers[method] || matched.handlers.all; if (!handler) { if (!_matcher) { _matcher = radix3.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) { return { error: createError({ statusCode: 405, name: "Method Not Allowed", statusMessage: `Method ${method} is not allowed on this route.` }) }; } return { matched, handler }; }; const isPreemptive = opts.preemptive || opts.preemtive; router.handler = eventHandler((event) => { const match = matchHandler( event.path, event.method.toLowerCase() ); if ("error" in match) { if (isPreemptive) { throw match.error; } else { return; } } event.context.matchedRoute = match.matched; const params = match.matched.params || {}; event.context.params = params; return Promise.resolve(match.handler(event)).then((res) => { if (res === void 0 && isPreemptive) { return null; } return res; }); }); router.handler.__resolve__ = async (path) => { path = ufo.withLeadingSlash(path); const match = matchHandler(path); if ("error" in match) { return; } let res = { route: match.matched.path, handler: match.handler }; if (match.handler.__resolve__) { const _res = await match.handler.__resolve__(path); if (!_res) { return; } res = { ...res, ..._res }; } 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; } setResponseStatus(event, error.statusCode, error.statusMessage); 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); } if (app.options.onBeforeResponse && !event._onBeforeResponseCalled) { await app.options.onBeforeResponse(event, { body: error }); } await sendError(event, error, !!app.options.debug); if (app.options.onAfterResponse && !event._onAfterResponseCalled) { await app.options.onAfterResponse(event, { body: error }); } } }; 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 index.IncomingMessage(); const nodeRes = new index.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 }); } exports.H3Error = H3Error; exports.H3Event = H3Event; exports.H3Headers = H3Headers; exports.H3Response = H3Response; exports.MIMES = MIMES; exports.appendCorsHeaders = appendCorsHeaders; exports.appendCorsPreflightHeaders = appendCorsPreflightHeaders; exports.appendHeader = appendHeader; exports.appendHeaders = appendHeaders; exports.appendResponseHeader = appendResponseHeader; exports.appendResponseHeaders = appendResponseHeaders; exports.assertMethod = assertMethod; exports.callNodeListener = callNodeListener; exports.clearResponseHeaders = clearResponseHeaders; exports.clearSession = clearSession; exports.createApp = createApp; exports.createAppEventHandler = createAppEventHandler; exports.createError = createError; exports.createEvent = createEvent; exports.createEventStream = createEventStream; exports.createRouter = createRouter; exports.defaultContentType = defaultContentType; exports.defineEventHandler = defineEventHandler; exports.defineLazyEventHandler = defineLazyEventHandler; exports.defineNodeListener = defineNodeListener; exports.defineNodeMiddleware = defineNodeMiddleware; exports.defineRequestMiddleware = defineRequestMiddleware; exports.defineResponseMiddleware = defineResponseMiddleware; exports.defineWebSocket = defineWebSocket; exports.defineWebSocketHandler = defineWebSocketHandler; exports.deleteCookie = deleteCookie; exports.dynamicEventHandler = dynamicEventHandler; exports.eventHandler = eventHandler; exports.fetchWithEvent = fetchWithEvent; exports.fromNodeMiddleware = fromNodeMiddleware; exports.fromPlainHandler = fromPlainHandler; exports.fromWebHandler = fromWebHandler; exports.getCookie = getCookie; exports.getHeader = getHeader; exports.getHeaders = getHeaders; exports.getMethod = getMethod; exports.getProxyRequestHeaders = getProxyRequestHeaders; exports.getQuery = getQuery; exports.getRequestFingerprint = getRequestFingerprint; exports.getRequestHeader = getRequestHeader; exports.getRequestHeaders = getRequestHeaders; exports.getRequestHost = getRequestHost; exports.getRequestIP = getRequestIP; exports.getRequestPath = getRequestPath; exports.getRequestProtocol = getRequestProtocol; exports.getRequestURL = getRequestURL; exports.getRequestWebStream = getRequestWebStream; exports.getResponseHeader = getResponseHeader; exports.getResponseHeaders = getResponseHeaders; exports.getResponseStatus = getResponseStatus; exports.getResponseStatusText = getResponseStatusText; exports.getRouterParam = getRouterParam; exports.getRouterParams = getRouterParams; exports.getSession = getSession; exports.getValidatedQuery = getValidatedQuery; exports.getValidatedRouterParams = getValidatedRouterParams; exports.handleCacheHeaders = handleCacheHeaders; exports.handleCors = handleCors; exports.isCorsOriginAllowed = isCorsOriginAllowed; exports.isError = isError; exports.isEvent = isEvent; exports.isEventHandler = isEventHandler; exports.isMethod = isMethod; exports.isPreflightRequest = isPreflightRequest; exports.isStream = isStream; exports.isWebResponse = isWebResponse; exports.lazyEventHandler = lazyEventHandler; exports.parseCookies = parseCookies; exports.promisifyNodeListener = promisifyNodeListener; exports.proxyRequest = proxyRequest; exports.readBody = readBody; exports.readFormData = readFormData; exports.readMultipartFormData = readMultipartFormData; exports.readRawBody = readRawBody; exports.readValidatedBody = readValidatedBody; exports.removeResponseHeader = removeResponseHeader; exports.sanitizeStatusCode = sanitizeStatusCode; exports.sanitizeStatusMessage = sanitizeStatusMessage; exports.sealSession = sealSession; exports.send = send; exports.sendError = sendError; exports.sendIterable = sendIterable; exports.sendNoContent = sendNoContent; exports.sendProxy = sendProxy; exports.sendRedirect = sendRedirect; exports.sendStream = sendStream; exports.sendWebResponse = sendWebResponse; exports.serveStatic = serveStatic; exports.setCookie = setCookie; exports.setHeader = setHeader; exports.setHeaders = setHeaders; exports.setResponseHeader = setResponseHeader; exports.setResponseHeaders = setResponseHeaders; exports.setResponseStatus = setResponseStatus; exports.splitCookiesString = splitCookiesString; exports.toEventHandler = toEventHandler; exports.toNodeListener = toNodeListener; exports.toPlainHandler = toPlainHandler; exports.toWebHandler = toWebHandler; exports.toWebRequest = toWebRequest; exports.unsealSession = unsealSession; exports.updateSession = updateSession; exports.use = use; exports.useBase = useBase; exports.useSession = useSession; exports.writeEarlyHints = writeEarlyHints;