2026-01-27 16:47:33 +03:30
import { withoutTrailingSlash , withoutBase , getQuery as getQuery$1 , decode , decodePath , withLeadingSlash , parseURL , joinURL } from 'ufo' ;
import { parse as parse$1 , serialize , parseSetCookie } 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 , ServerResponse } from 'node-mock-http' ;
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 ;
}
});
}
function hasProp ( obj , prop ) {
try {
return prop in obj ;
} catch {
return false ;
}
}
class H3Error extends Error {
static __h3_error__ = true ;
statusCode = 500 ;
fatal = false ;
unhandled = false ;
statusMessage ;
data ;
cause ;
constructor ( message , opts = {}) {
super ( message , opts );
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 ;
}
}
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 getQuery$1 ( 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 ] = 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 _header = event . node . req . headers [ "x-forwarded-host" ];
const xForwardedHost = ( _header || "" ). split ( "," ). shift () ? . trim ();
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 ());
}
if ( _resolved instanceof FormData ) {
return new Response ( _resolved ). bytes (). then (( uint8arr ) => Buffer . from ( uint8arr ));
}
return Buffer . from ( _resolved );
});
return encoding ? promise2 . then (( buff ) => buff . toString ( encoding )) : promise2 ;
}
2026-02-01 17:38:39 +03:30
if ( ! Number . parseInt ( event . node . req . headers [ "content-length" ] || "" ) && ! /\bchunked\b/i . test (
String ( event . node . req . headers [ "transfer-encoding" ] ?? "" )
)) {
2026-01-27 16:47:33 +03:30
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 ( 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 ) >= 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 getDistinctCookieKey ( name , opts ) {
return [ name , opts . domain || "" , opts . path || "/" ]. join ( ";" );
}
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 = {}) {
if ( ! serializeOptions . path ) {
serializeOptions = { path : "/" , ... serializeOptions };
}
const newCookie = serialize ( name , value , serializeOptions );
const currentCookies = splitCookiesString (
event . node . res . getHeader ( "set-cookie" )
);
if ( currentCookies . length === 0 ) {
event . node . res . setHeader ( "set-cookie" , newCookie );
return ;
}
const newCookieKey = getDistinctCookieKey ( name , serializeOptions );
event . node . res . removeHeader ( "set-cookie" );
for ( const cookie of currentCookies ) {
const parsed = parseSetCookie ( cookie );
const key = getDistinctCookieKey ( parsed . name , parsed );
if ( key === newCookieKey ) {
continue ;
}
event . node . res . appendHeader ( "set-cookie" , cookie );
}
event . node . res . appendHeader ( "set-cookie" , newCookie );
}
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 = `<!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 ( 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 ( 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 . 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" ,
"accept-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 , { host : target . startsWith ( "/" ) }),
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 , opts ) {
const headers = /* @__PURE__ */ Object . create ( null );
const reqHeaders = getRequestHeaders ( event );
for ( const name in reqHeaders ) {
if ( ! ignoredHeaders . has ( name ) || name === "host" && opts ? . host ) {
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 , {
host : typeof req === "string" && req . startsWith ( "/" )
}),
... 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 ) {
const entries = Array . isArray ( input ) ? input : typeof input . entries === "function" ? input . entries () : Object . entries ( input );
for ( const [ key , value ] of entries ) {
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 ) => {
if ( ! isEvent ( event )) {
throw new Error ( "[h3] Cannot update read-only session." );
}
await updateSession ( event , config , update );
return sessionManager ;
},
clear : () => {
if ( ! isEvent ( event )) {
throw new Error ( "[h3] Cannot clear read-only session." );
}
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 = _getReqHeader ( event , headerName );
if ( typeof headerValue === "string" ) {
sealedSession = headerValue ;
}
}
if ( ! sealedSession ) {
const cookieHeader = _getReqHeader ( event , "cookie" );
if ( cookieHeader ) {
sealedSession = parse$1 ( cookieHeader + "" )[ 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 ) {
if ( ! isEvent ( event )) {
throw new Error (
"Cannot initialize a new session. Make sure using `useSession(event)` in main handler."
);
}
session . id = config . generateId ? .() ?? ( config . crypto || crypto ). randomUUID ();
session . createdAt = Date . now ();
await updateSession ( event , config );
}
return session ;
}
function _getReqHeader ( event , name ) {
if ( event . node ) {
return event . node ? . req . headers [ name ];
}
if ( event . request ) {
return event . request . headers ? . get ( name );
}
if ( event . headers ) {
return event . headers . get ( name );
}
}
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 );
2026-02-01 17:38:39 +03:30
const sealed = await seal (
config . crypto || crypto ,
session ,
config . password ,
{
... defaults ,
ttl : config . maxAge ? config . maxAge * 1e3 : 0 ,
... config . seal
}
);
2026-01-27 16:47:33 +03:30
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 ;
}
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 ;
}
class EventStream {
_h3Event ;
_transformStream = new TransformStream ();
_writer ;
_encoder = new TextEncoder ();
_writerIsClosed = false ;
_paused = false ;
_unsentData ;
_disposed = false ;
_handled = false ;
constructor ( event , opts = {}) {
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 = 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 ({ 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
});
}
class H3Event {
"__is_event__" = true ;
// Context
node ;
// Node
web ;
// Web
context = {};
// Shared
// Request
_method ;
_path ;
_headers ;
_requestBody ;
// Response
_handled = false ;
// Hooks
_onBeforeResponseCalled ;
_onAfterResponseCalled ;
constructor ( req , res ) {
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 : 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 : 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" ? 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 = 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 );
}
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 = 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 = 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 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 , createEventStream , createRouter , defaultContentType , defineEventHandler , defineLazyEventHandler , defineNodeListener , defineNodeMiddleware , defineRequestMiddleware , defineResponseMiddleware , defineWebSocket , defineWebSocketHandler , deleteCookie , dynamicEventHandler , eventHandler , fetchWithEvent , fromNodeMiddleware , fromPlainHandler , fromWebHandler , getCookie , getHeader , getHeaders , getMethod , getProxyRequestHeaders , getQuery , getRequestFingerprint , getRequestHeader , getRequestHeaders , getRequestHost , getRequestIP , getRequestPath , getRequestProtocol , getRequestURL , getRequestWebStream , getResponseHeader , getResponseHeaders , getResponseStatus , getResponseStatusText , getRouterParam , getRouterParams , getSession , getValidatedQuery , getValidatedRouterParams , 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 , sendIterable , sendNoContent , sendProxy , sendRedirect , sendStream , sendWebResponse , serveStatic , setCookie , setHeader , setHeaders , setResponseHeader , setResponseHeaders , setResponseStatus , splitCookiesString , toEventHandler , toNodeListener , toPlainHandler , toWebHandler , toWebRequest , unsealSession , updateSession , use , useBase , useSession , writeEarlyHints };