2024-05-29 18:43:41 +00:00
import './shims.js' ;
import fs$1 , { createReadStream } from 'node:fs' ;
import path from 'node:path' ;
import * as fs from 'fs' ;
import { readdirSync , statSync } from 'fs' ;
import { resolve , join , normalize } from 'path' ;
import * as qs from 'querystring' ;
import { fileURLToPath } from 'node:url' ;
import { Readable } from 'node:stream' ;
import { Server } from './server/index.js' ;
import { manifest , prerendered , base } from './server/manifest.js' ;
import { env } from './env.js' ;
function totalist ( dir , callback , pre = '' ) {
dir = resolve ( '.' , dir ) ;
let arr = readdirSync ( dir ) ;
let i = 0 , abs , stats ;
for ( ; i < arr . length ; i ++ ) {
abs = join ( dir , arr [ i ] ) ;
stats = statSync ( abs ) ;
stats . isDirectory ( )
? totalist ( abs , callback , join ( pre , arr [ i ] ) )
: callback ( join ( pre , arr [ i ] ) , abs , stats ) ;
}
}
/ * *
* @ typedef ParsedURL
2024-05-29 19:16:25 +00:00
* @ type { import ( '.' ) . ParsedURL }
2024-05-29 18:43:41 +00:00
* /
/ * *
* @ typedef Request
* @ property { string } url
* @ property { ParsedURL } _parsedUrl
* /
/ * *
* @ param { Request } req
* @ returns { ParsedURL | void }
* /
function parse$1 ( req ) {
let raw = req . url ;
if ( raw == null ) return ;
let prev = req . _parsedUrl ;
if ( prev && prev . raw === raw ) return prev ;
let pathname = raw , search = '' , query ;
if ( raw . length > 1 ) {
let idx = raw . indexOf ( '?' , 1 ) ;
if ( idx !== - 1 ) {
search = raw . substring ( idx ) ;
pathname = raw . substring ( 0 , idx ) ;
if ( search . length > 1 ) {
query = qs . parse ( search . substring ( 1 ) ) ;
}
}
}
return req . _parsedUrl = { pathname , search , query , raw } ;
}
const mimes = {
"3g2" : "video/3gpp2" ,
"3gp" : "video/3gpp" ,
"3gpp" : "video/3gpp" ,
"3mf" : "model/3mf" ,
"aac" : "audio/aac" ,
"ac" : "application/pkix-attr-cert" ,
"adp" : "audio/adpcm" ,
"adts" : "audio/aac" ,
"ai" : "application/postscript" ,
"aml" : "application/automationml-aml+xml" ,
"amlx" : "application/automationml-amlx+zip" ,
"amr" : "audio/amr" ,
"apng" : "image/apng" ,
"appcache" : "text/cache-manifest" ,
"appinstaller" : "application/appinstaller" ,
"appx" : "application/appx" ,
"appxbundle" : "application/appxbundle" ,
"asc" : "application/pgp-keys" ,
"atom" : "application/atom+xml" ,
"atomcat" : "application/atomcat+xml" ,
"atomdeleted" : "application/atomdeleted+xml" ,
"atomsvc" : "application/atomsvc+xml" ,
"au" : "audio/basic" ,
"avci" : "image/avci" ,
"avcs" : "image/avcs" ,
"avif" : "image/avif" ,
"aw" : "application/applixware" ,
"bdoc" : "application/bdoc" ,
"bin" : "application/octet-stream" ,
"bmp" : "image/bmp" ,
"bpk" : "application/octet-stream" ,
"btf" : "image/prs.btif" ,
"btif" : "image/prs.btif" ,
"buffer" : "application/octet-stream" ,
"ccxml" : "application/ccxml+xml" ,
"cdfx" : "application/cdfx+xml" ,
"cdmia" : "application/cdmi-capability" ,
"cdmic" : "application/cdmi-container" ,
"cdmid" : "application/cdmi-domain" ,
"cdmio" : "application/cdmi-object" ,
"cdmiq" : "application/cdmi-queue" ,
"cer" : "application/pkix-cert" ,
"cgm" : "image/cgm" ,
"cjs" : "application/node" ,
"class" : "application/java-vm" ,
"coffee" : "text/coffeescript" ,
"conf" : "text/plain" ,
"cpl" : "application/cpl+xml" ,
"cpt" : "application/mac-compactpro" ,
"crl" : "application/pkix-crl" ,
"css" : "text/css" ,
"csv" : "text/csv" ,
"cu" : "application/cu-seeme" ,
"cwl" : "application/cwl" ,
"cww" : "application/prs.cww" ,
"davmount" : "application/davmount+xml" ,
"dbk" : "application/docbook+xml" ,
"deb" : "application/octet-stream" ,
"def" : "text/plain" ,
"deploy" : "application/octet-stream" ,
"dib" : "image/bmp" ,
"disposition-notification" : "message/disposition-notification" ,
"dist" : "application/octet-stream" ,
"distz" : "application/octet-stream" ,
"dll" : "application/octet-stream" ,
"dmg" : "application/octet-stream" ,
"dms" : "application/octet-stream" ,
"doc" : "application/msword" ,
"dot" : "application/msword" ,
"dpx" : "image/dpx" ,
"drle" : "image/dicom-rle" ,
"dsc" : "text/prs.lines.tag" ,
"dssc" : "application/dssc+der" ,
"dtd" : "application/xml-dtd" ,
"dump" : "application/octet-stream" ,
"dwd" : "application/atsc-dwd+xml" ,
"ear" : "application/java-archive" ,
"ecma" : "application/ecmascript" ,
"elc" : "application/octet-stream" ,
"emf" : "image/emf" ,
"eml" : "message/rfc822" ,
"emma" : "application/emma+xml" ,
"emotionml" : "application/emotionml+xml" ,
"eps" : "application/postscript" ,
"epub" : "application/epub+zip" ,
"exe" : "application/octet-stream" ,
"exi" : "application/exi" ,
"exp" : "application/express" ,
"exr" : "image/aces" ,
"ez" : "application/andrew-inset" ,
"fdf" : "application/fdf" ,
"fdt" : "application/fdt+xml" ,
"fits" : "image/fits" ,
"g3" : "image/g3fax" ,
"gbr" : "application/rpki-ghostbusters" ,
"geojson" : "application/geo+json" ,
"gif" : "image/gif" ,
"glb" : "model/gltf-binary" ,
"gltf" : "model/gltf+json" ,
"gml" : "application/gml+xml" ,
"gpx" : "application/gpx+xml" ,
"gram" : "application/srgs" ,
"grxml" : "application/srgs+xml" ,
"gxf" : "application/gxf" ,
"gz" : "application/gzip" ,
"h261" : "video/h261" ,
"h263" : "video/h263" ,
"h264" : "video/h264" ,
"heic" : "image/heic" ,
"heics" : "image/heic-sequence" ,
"heif" : "image/heif" ,
"heifs" : "image/heif-sequence" ,
"hej2" : "image/hej2k" ,
"held" : "application/atsc-held+xml" ,
"hjson" : "application/hjson" ,
"hlp" : "application/winhlp" ,
"hqx" : "application/mac-binhex40" ,
"hsj2" : "image/hsj2" ,
"htm" : "text/html" ,
"html" : "text/html" ,
"ics" : "text/calendar" ,
"ief" : "image/ief" ,
"ifb" : "text/calendar" ,
"iges" : "model/iges" ,
"igs" : "model/iges" ,
"img" : "application/octet-stream" ,
"in" : "text/plain" ,
"ini" : "text/plain" ,
"ink" : "application/inkml+xml" ,
"inkml" : "application/inkml+xml" ,
"ipfix" : "application/ipfix" ,
"iso" : "application/octet-stream" ,
"its" : "application/its+xml" ,
"jade" : "text/jade" ,
"jar" : "application/java-archive" ,
"jhc" : "image/jphc" ,
"jls" : "image/jls" ,
"jp2" : "image/jp2" ,
"jpe" : "image/jpeg" ,
"jpeg" : "image/jpeg" ,
"jpf" : "image/jpx" ,
"jpg" : "image/jpeg" ,
"jpg2" : "image/jp2" ,
"jpgm" : "image/jpm" ,
"jpgv" : "video/jpeg" ,
"jph" : "image/jph" ,
"jpm" : "image/jpm" ,
"jpx" : "image/jpx" ,
"js" : "text/javascript" ,
"json" : "application/json" ,
"json5" : "application/json5" ,
"jsonld" : "application/ld+json" ,
"jsonml" : "application/jsonml+json" ,
"jsx" : "text/jsx" ,
"jt" : "model/jt" ,
"jxr" : "image/jxr" ,
"jxra" : "image/jxra" ,
"jxrs" : "image/jxrs" ,
"jxs" : "image/jxs" ,
"jxsc" : "image/jxsc" ,
"jxsi" : "image/jxsi" ,
"jxss" : "image/jxss" ,
"kar" : "audio/midi" ,
"ktx" : "image/ktx" ,
"ktx2" : "image/ktx2" ,
"less" : "text/less" ,
"lgr" : "application/lgr+xml" ,
"list" : "text/plain" ,
"litcoffee" : "text/coffeescript" ,
"log" : "text/plain" ,
"lostxml" : "application/lost+xml" ,
"lrf" : "application/octet-stream" ,
"m1v" : "video/mpeg" ,
"m21" : "application/mp21" ,
"m2a" : "audio/mpeg" ,
"m2v" : "video/mpeg" ,
"m3a" : "audio/mpeg" ,
"m4a" : "audio/mp4" ,
"m4p" : "application/mp4" ,
"m4s" : "video/iso.segment" ,
"ma" : "application/mathematica" ,
"mads" : "application/mads+xml" ,
"maei" : "application/mmt-aei+xml" ,
"man" : "text/troff" ,
"manifest" : "text/cache-manifest" ,
"map" : "application/json" ,
"mar" : "application/octet-stream" ,
"markdown" : "text/markdown" ,
"mathml" : "application/mathml+xml" ,
"mb" : "application/mathematica" ,
"mbox" : "application/mbox" ,
"md" : "text/markdown" ,
"mdx" : "text/mdx" ,
"me" : "text/troff" ,
"mesh" : "model/mesh" ,
"meta4" : "application/metalink4+xml" ,
"metalink" : "application/metalink+xml" ,
"mets" : "application/mets+xml" ,
"mft" : "application/rpki-manifest" ,
"mid" : "audio/midi" ,
"midi" : "audio/midi" ,
"mime" : "message/rfc822" ,
"mj2" : "video/mj2" ,
"mjp2" : "video/mj2" ,
"mjs" : "text/javascript" ,
"mml" : "text/mathml" ,
"mods" : "application/mods+xml" ,
"mov" : "video/quicktime" ,
"mp2" : "audio/mpeg" ,
"mp21" : "application/mp21" ,
"mp2a" : "audio/mpeg" ,
"mp3" : "audio/mpeg" ,
"mp4" : "video/mp4" ,
"mp4a" : "audio/mp4" ,
"mp4s" : "application/mp4" ,
"mp4v" : "video/mp4" ,
"mpd" : "application/dash+xml" ,
"mpe" : "video/mpeg" ,
"mpeg" : "video/mpeg" ,
"mpf" : "application/media-policy-dataset+xml" ,
"mpg" : "video/mpeg" ,
"mpg4" : "video/mp4" ,
"mpga" : "audio/mpeg" ,
"mpp" : "application/dash-patch+xml" ,
"mrc" : "application/marc" ,
"mrcx" : "application/marcxml+xml" ,
"ms" : "text/troff" ,
"mscml" : "application/mediaservercontrol+xml" ,
"msh" : "model/mesh" ,
"msi" : "application/octet-stream" ,
"msix" : "application/msix" ,
"msixbundle" : "application/msixbundle" ,
"msm" : "application/octet-stream" ,
"msp" : "application/octet-stream" ,
"mtl" : "model/mtl" ,
"musd" : "application/mmt-usd+xml" ,
"mxf" : "application/mxf" ,
"mxmf" : "audio/mobile-xmf" ,
"mxml" : "application/xv+xml" ,
"n3" : "text/n3" ,
"nb" : "application/mathematica" ,
"nq" : "application/n-quads" ,
"nt" : "application/n-triples" ,
"obj" : "model/obj" ,
"oda" : "application/oda" ,
"oga" : "audio/ogg" ,
"ogg" : "audio/ogg" ,
"ogv" : "video/ogg" ,
"ogx" : "application/ogg" ,
"omdoc" : "application/omdoc+xml" ,
"onepkg" : "application/onenote" ,
"onetmp" : "application/onenote" ,
"onetoc" : "application/onenote" ,
"onetoc2" : "application/onenote" ,
"opf" : "application/oebps-package+xml" ,
"opus" : "audio/ogg" ,
"otf" : "font/otf" ,
"owl" : "application/rdf+xml" ,
"oxps" : "application/oxps" ,
"p10" : "application/pkcs10" ,
"p7c" : "application/pkcs7-mime" ,
"p7m" : "application/pkcs7-mime" ,
"p7s" : "application/pkcs7-signature" ,
"p8" : "application/pkcs8" ,
"pdf" : "application/pdf" ,
"pfr" : "application/font-tdpfr" ,
"pgp" : "application/pgp-encrypted" ,
"pkg" : "application/octet-stream" ,
"pki" : "application/pkixcmp" ,
"pkipath" : "application/pkix-pkipath" ,
"pls" : "application/pls+xml" ,
"png" : "image/png" ,
"prc" : "model/prc" ,
"prf" : "application/pics-rules" ,
"provx" : "application/provenance+xml" ,
"ps" : "application/postscript" ,
"pskcxml" : "application/pskc+xml" ,
"pti" : "image/prs.pti" ,
"qt" : "video/quicktime" ,
"raml" : "application/raml+yaml" ,
"rapd" : "application/route-apd+xml" ,
"rdf" : "application/rdf+xml" ,
"relo" : "application/p2p-overlay+xml" ,
"rif" : "application/reginfo+xml" ,
"rl" : "application/resource-lists+xml" ,
"rld" : "application/resource-lists-diff+xml" ,
"rmi" : "audio/midi" ,
"rnc" : "application/relax-ng-compact-syntax" ,
"rng" : "application/xml" ,
"roa" : "application/rpki-roa" ,
"roff" : "text/troff" ,
"rq" : "application/sparql-query" ,
"rs" : "application/rls-services+xml" ,
"rsat" : "application/atsc-rsat+xml" ,
"rsd" : "application/rsd+xml" ,
"rsheet" : "application/urc-ressheet+xml" ,
"rss" : "application/rss+xml" ,
"rtf" : "text/rtf" ,
"rtx" : "text/richtext" ,
"rusd" : "application/route-usd+xml" ,
"s3m" : "audio/s3m" ,
"sbml" : "application/sbml+xml" ,
"scq" : "application/scvp-cv-request" ,
"scs" : "application/scvp-cv-response" ,
"sdp" : "application/sdp" ,
"senmlx" : "application/senml+xml" ,
"sensmlx" : "application/sensml+xml" ,
"ser" : "application/java-serialized-object" ,
"setpay" : "application/set-payment-initiation" ,
"setreg" : "application/set-registration-initiation" ,
"sgi" : "image/sgi" ,
"sgm" : "text/sgml" ,
"sgml" : "text/sgml" ,
"shex" : "text/shex" ,
"shf" : "application/shf+xml" ,
"shtml" : "text/html" ,
"sieve" : "application/sieve" ,
"sig" : "application/pgp-signature" ,
"sil" : "audio/silk" ,
"silo" : "model/mesh" ,
"siv" : "application/sieve" ,
"slim" : "text/slim" ,
"slm" : "text/slim" ,
"sls" : "application/route-s-tsid+xml" ,
"smi" : "application/smil+xml" ,
"smil" : "application/smil+xml" ,
"snd" : "audio/basic" ,
"so" : "application/octet-stream" ,
"spdx" : "text/spdx" ,
"spp" : "application/scvp-vp-response" ,
"spq" : "application/scvp-vp-request" ,
"spx" : "audio/ogg" ,
"sql" : "application/sql" ,
"sru" : "application/sru+xml" ,
"srx" : "application/sparql-results+xml" ,
"ssdl" : "application/ssdl+xml" ,
"ssml" : "application/ssml+xml" ,
"stk" : "application/hyperstudio" ,
"stl" : "model/stl" ,
"stpx" : "model/step+xml" ,
"stpxz" : "model/step-xml+zip" ,
"stpz" : "model/step+zip" ,
"styl" : "text/stylus" ,
"stylus" : "text/stylus" ,
"svg" : "image/svg+xml" ,
"svgz" : "image/svg+xml" ,
"swidtag" : "application/swid+xml" ,
"t" : "text/troff" ,
"t38" : "image/t38" ,
"td" : "application/urc-targetdesc+xml" ,
"tei" : "application/tei+xml" ,
"teicorpus" : "application/tei+xml" ,
"text" : "text/plain" ,
"tfi" : "application/thraud+xml" ,
"tfx" : "image/tiff-fx" ,
"tif" : "image/tiff" ,
"tiff" : "image/tiff" ,
"toml" : "application/toml" ,
"tr" : "text/troff" ,
"trig" : "application/trig" ,
"ts" : "video/mp2t" ,
"tsd" : "application/timestamped-data" ,
"tsv" : "text/tab-separated-values" ,
"ttc" : "font/collection" ,
"ttf" : "font/ttf" ,
"ttl" : "text/turtle" ,
"ttml" : "application/ttml+xml" ,
"txt" : "text/plain" ,
"u3d" : "model/u3d" ,
"u8dsn" : "message/global-delivery-status" ,
"u8hdr" : "message/global-headers" ,
"u8mdn" : "message/global-disposition-notification" ,
"u8msg" : "message/global" ,
"ubj" : "application/ubjson" ,
"uri" : "text/uri-list" ,
"uris" : "text/uri-list" ,
"urls" : "text/uri-list" ,
"vcard" : "text/vcard" ,
"vrml" : "model/vrml" ,
"vtt" : "text/vtt" ,
"vxml" : "application/voicexml+xml" ,
"war" : "application/java-archive" ,
"wasm" : "application/wasm" ,
"wav" : "audio/wav" ,
"weba" : "audio/webm" ,
"webm" : "video/webm" ,
"webmanifest" : "application/manifest+json" ,
"webp" : "image/webp" ,
"wgsl" : "text/wgsl" ,
"wgt" : "application/widget" ,
"wif" : "application/watcherinfo+xml" ,
"wmf" : "image/wmf" ,
"woff" : "font/woff" ,
"woff2" : "font/woff2" ,
"wrl" : "model/vrml" ,
"wsdl" : "application/wsdl+xml" ,
"wspolicy" : "application/wspolicy+xml" ,
"x3d" : "model/x3d+xml" ,
"x3db" : "model/x3d+fastinfoset" ,
"x3dbz" : "model/x3d+binary" ,
"x3dv" : "model/x3d-vrml" ,
"x3dvz" : "model/x3d+vrml" ,
"x3dz" : "model/x3d+xml" ,
"xaml" : "application/xaml+xml" ,
"xav" : "application/xcap-att+xml" ,
"xca" : "application/xcap-caps+xml" ,
"xcs" : "application/calendar+xml" ,
"xdf" : "application/xcap-diff+xml" ,
"xdssc" : "application/dssc+xml" ,
"xel" : "application/xcap-el+xml" ,
"xenc" : "application/xenc+xml" ,
"xer" : "application/patch-ops-error+xml" ,
"xfdf" : "application/xfdf" ,
"xht" : "application/xhtml+xml" ,
"xhtml" : "application/xhtml+xml" ,
"xhvml" : "application/xv+xml" ,
"xlf" : "application/xliff+xml" ,
"xm" : "audio/xm" ,
"xml" : "text/xml" ,
"xns" : "application/xcap-ns+xml" ,
"xop" : "application/xop+xml" ,
"xpl" : "application/xproc+xml" ,
"xsd" : "application/xml" ,
"xsf" : "application/prs.xsf+xml" ,
"xsl" : "application/xml" ,
"xslt" : "application/xml" ,
"xspf" : "application/xspf+xml" ,
"xvm" : "application/xv+xml" ,
"xvml" : "application/xv+xml" ,
"yaml" : "text/yaml" ,
"yang" : "application/yang" ,
"yin" : "application/yin+xml" ,
"yml" : "text/yaml" ,
"zip" : "application/zip"
} ;
function lookup ( extn ) {
let tmp = ( '' + extn ) . trim ( ) . toLowerCase ( ) ;
let idx = tmp . lastIndexOf ( '.' ) ;
return mimes [ ! ~ idx ? tmp : tmp . substring ( ++ idx ) ] ;
}
const noop = ( ) => { } ;
function isMatch ( uri , arr ) {
for ( let i = 0 ; i < arr . length ; i ++ ) {
if ( arr [ i ] . test ( uri ) ) return true ;
}
}
function toAssume ( uri , extns ) {
let i = 0 , x , len = uri . length - 1 ;
if ( uri . charCodeAt ( len ) === 47 ) {
uri = uri . substring ( 0 , len ) ;
}
let arr = [ ] , tmp = ` ${ uri } /index ` ;
for ( ; i < extns . length ; i ++ ) {
x = extns [ i ] ? ` . ${ extns [ i ] } ` : '' ;
if ( uri ) arr . push ( uri + x ) ;
arr . push ( tmp + x ) ;
}
return arr ;
}
function viaCache ( cache , uri , extns ) {
let i = 0 , data , arr = toAssume ( uri , extns ) ;
for ( ; i < arr . length ; i ++ ) {
if ( data = cache [ arr [ i ] ] ) return data ;
}
}
function viaLocal ( dir , isEtag , uri , extns ) {
let i = 0 , arr = toAssume ( uri , extns ) ;
let abs , stats , name , headers ;
for ( ; i < arr . length ; i ++ ) {
abs = normalize ( join ( dir , name = arr [ i ] ) ) ;
if ( abs . startsWith ( dir ) && fs . existsSync ( abs ) ) {
stats = fs . statSync ( abs ) ;
if ( stats . isDirectory ( ) ) continue ;
headers = toHeaders ( name , stats , isEtag ) ;
headers [ 'Cache-Control' ] = isEtag ? 'no-cache' : 'no-store' ;
return { abs , stats , headers } ;
}
}
}
function is404 ( req , res ) {
return ( res . statusCode = 404 , res . end ( ) ) ;
}
function send ( req , res , file , stats , headers ) {
let code = 200 , tmp , opts = { } ;
headers = { ... headers } ;
for ( let key in headers ) {
tmp = res . getHeader ( key ) ;
if ( tmp ) headers [ key ] = tmp ;
}
if ( tmp = res . getHeader ( 'content-type' ) ) {
headers [ 'Content-Type' ] = tmp ;
}
if ( req . headers . range ) {
code = 206 ;
let [ x , y ] = req . headers . range . replace ( 'bytes=' , '' ) . split ( '-' ) ;
let end = opts . end = parseInt ( y , 10 ) || stats . size - 1 ;
let start = opts . start = parseInt ( x , 10 ) || 0 ;
if ( end >= stats . size ) {
end = stats . size - 1 ;
}
if ( start >= stats . size ) {
res . setHeader ( 'Content-Range' , ` bytes */ ${ stats . size } ` ) ;
res . statusCode = 416 ;
return res . end ( ) ;
}
headers [ 'Content-Range' ] = ` bytes ${ start } - ${ end } / ${ stats . size } ` ;
headers [ 'Content-Length' ] = ( end - start + 1 ) ;
headers [ 'Accept-Ranges' ] = 'bytes' ;
}
res . writeHead ( code , headers ) ;
fs . createReadStream ( file , opts ) . pipe ( res ) ;
}
const ENCODING = {
'.br' : 'br' ,
'.gz' : 'gzip' ,
} ;
function toHeaders ( name , stats , isEtag ) {
let enc = ENCODING [ name . slice ( - 3 ) ] ;
let ctype = lookup ( name . slice ( 0 , enc && - 3 ) ) || '' ;
if ( ctype === 'text/html' ) ctype += ';charset=utf-8' ;
let headers = {
'Content-Length' : stats . size ,
'Content-Type' : ctype ,
'Last-Modified' : stats . mtime . toUTCString ( ) ,
} ;
if ( enc ) headers [ 'Content-Encoding' ] = enc ;
if ( isEtag ) headers [ 'ETag' ] = ` W/" ${ stats . size } - ${ stats . mtime . getTime ( ) } " ` ;
return headers ;
}
function sirv ( dir , opts = { } ) {
dir = resolve ( dir || '.' ) ;
let isNotFound = opts . onNoMatch || is404 ;
let setHeaders = opts . setHeaders || noop ;
let extensions = opts . extensions || [ 'html' , 'htm' ] ;
let gzips = opts . gzip && extensions . map ( x => ` ${ x } .gz ` ) . concat ( 'gz' ) ;
let brots = opts . brotli && extensions . map ( x => ` ${ x } .br ` ) . concat ( 'br' ) ;
const FILES = { } ;
let fallback = '/' ;
let isEtag = ! ! opts . etag ;
let isSPA = ! ! opts . single ;
if ( typeof opts . single === 'string' ) {
let idx = opts . single . lastIndexOf ( '.' ) ;
fallback += ! ! ~ idx ? opts . single . substring ( 0 , idx ) : opts . single ;
}
let ignores = [ ] ;
if ( opts . ignores !== false ) {
ignores . push ( /[/]([A-Za-z\s\d~$._-]+\.\w+){1,}$/ ) ; // any extn
if ( opts . dotfiles ) ignores . push ( /\/\.\w/ ) ;
else ignores . push ( /\/\.well-known/ ) ;
[ ] . concat ( opts . ignores || [ ] ) . forEach ( x => {
ignores . push ( new RegExp ( x , 'i' ) ) ;
} ) ;
}
let cc = opts . maxAge != null && ` public,max-age= ${ opts . maxAge } ` ;
if ( cc && opts . immutable ) cc += ',immutable' ;
else if ( cc && opts . maxAge === 0 ) cc += ',must-revalidate' ;
if ( ! opts . dev ) {
totalist ( dir , ( name , abs , stats ) => {
if ( /\.well-known[\\+\/]/ . test ( name ) ) ; // keep
else if ( ! opts . dotfiles && /(^\.|[\\+|\/+]\.)/ . test ( name ) ) return ;
let headers = toHeaders ( name , stats , isEtag ) ;
if ( cc ) headers [ 'Cache-Control' ] = cc ;
FILES [ '/' + name . normalize ( ) . replace ( /\\+/g , '/' ) ] = { abs , stats , headers } ;
} ) ;
}
let lookup = opts . dev ? viaLocal . bind ( 0 , dir , isEtag ) : viaCache . bind ( 0 , FILES ) ;
return function ( req , res , next ) {
let extns = [ '' ] ;
let pathname = parse$1 ( req ) . pathname ;
let val = req . headers [ 'accept-encoding' ] || '' ;
if ( gzips && val . includes ( 'gzip' ) ) extns . unshift ( ... gzips ) ;
if ( brots && /(br|brotli)/i . test ( val ) ) extns . unshift ( ... brots ) ;
extns . push ( ... extensions ) ; // [...br, ...gz, orig, ...exts]
if ( pathname . indexOf ( '%' ) !== - 1 ) {
try { pathname = decodeURI ( pathname ) ; }
catch ( err ) { /* malform uri */ }
}
let data = lookup ( pathname , extns ) || isSPA && ! isMatch ( pathname , ignores ) && lookup ( fallback , extns ) ;
if ( ! data ) return next ? next ( ) : isNotFound ( req , res ) ;
if ( isEtag && req . headers [ 'if-none-match' ] === data . headers [ 'ETag' ] ) {
res . writeHead ( 304 ) ;
return res . end ( ) ;
}
if ( gzips || brots ) {
res . setHeader ( 'Vary' , 'Accept-Encoding' ) ;
}
setHeaders ( res , pathname , data . stats ) ;
send ( req , res , data . abs , data . stats , data . headers ) ;
} ;
}
var setCookie = { exports : { } } ;
var defaultParseOptions = {
decodeValues : true ,
map : false ,
silent : false ,
} ;
function isNonEmptyString ( str ) {
return typeof str === "string" && ! ! str . trim ( ) ;
}
function parseString ( setCookieValue , options ) {
var parts = setCookieValue . split ( ";" ) . filter ( isNonEmptyString ) ;
var nameValuePairStr = parts . shift ( ) ;
var parsed = parseNameValuePair ( nameValuePairStr ) ;
var name = parsed . name ;
var value = parsed . value ;
options = options
? Object . assign ( { } , defaultParseOptions , options )
: defaultParseOptions ;
try {
value = options . decodeValues ? decodeURIComponent ( value ) : value ; // decode cookie value
} catch ( e ) {
console . error (
"set-cookie-parser encountered an error while decoding a cookie with value '" +
value +
"'. Set options.decodeValues to false to disable this feature." ,
e
) ;
}
var cookie = {
name : name ,
value : value ,
} ;
parts . forEach ( function ( part ) {
var sides = part . split ( "=" ) ;
var key = sides . shift ( ) . trimLeft ( ) . toLowerCase ( ) ;
var value = sides . join ( "=" ) ;
if ( key === "expires" ) {
cookie . expires = new Date ( value ) ;
} else if ( key === "max-age" ) {
cookie . maxAge = parseInt ( value , 10 ) ;
} else if ( key === "secure" ) {
cookie . secure = true ;
} else if ( key === "httponly" ) {
cookie . httpOnly = true ;
} else if ( key === "samesite" ) {
cookie . sameSite = value ;
} else {
cookie [ key ] = value ;
}
} ) ;
return cookie ;
}
function parseNameValuePair ( nameValuePairStr ) {
// Parses name-value-pair according to rfc6265bis draft
var name = "" ;
var value = "" ;
var nameValueArr = nameValuePairStr . split ( "=" ) ;
if ( nameValueArr . length > 1 ) {
name = nameValueArr . shift ( ) ;
value = nameValueArr . join ( "=" ) ; // everything after the first =, joined by a "=" if there was more than one part
} else {
value = nameValuePairStr ;
}
return { name : name , value : value } ;
}
function parse ( input , options ) {
options = options
? Object . assign ( { } , defaultParseOptions , options )
: defaultParseOptions ;
if ( ! input ) {
if ( ! options . map ) {
return [ ] ;
} else {
return { } ;
}
}
if ( input . headers ) {
if ( typeof input . headers . getSetCookie === "function" ) {
// for fetch responses - they combine headers of the same type in the headers array,
// but getSetCookie returns an uncombined array
input = input . headers . getSetCookie ( ) ;
} else if ( input . headers [ "set-cookie" ] ) {
// fast-path for node.js (which automatically normalizes header names to lower-case
input = input . headers [ "set-cookie" ] ;
} else {
// slow-path for other environments - see #25
var sch =
input . headers [
Object . keys ( input . headers ) . find ( function ( key ) {
return key . toLowerCase ( ) === "set-cookie" ;
} )
] ;
// warn if called on a request-like object with a cookie header rather than a set-cookie header - see #34, 36
if ( ! sch && input . headers . cookie && ! options . silent ) {
console . warn (
"Warning: set-cookie-parser appears to have been called on a request object. It is designed to parse Set-Cookie headers from responses, not Cookie headers from requests. Set the option {silent: true} to suppress this warning."
) ;
}
input = sch ;
}
}
if ( ! Array . isArray ( input ) ) {
input = [ input ] ;
}
options = options
? Object . assign ( { } , defaultParseOptions , options )
: defaultParseOptions ;
if ( ! options . map ) {
return input . filter ( isNonEmptyString ) . map ( function ( str ) {
return parseString ( str , options ) ;
} ) ;
} else {
var cookies = { } ;
return input . filter ( isNonEmptyString ) . reduce ( function ( cookies , str ) {
var cookie = parseString ( str , options ) ;
cookies [ cookie . name ] = cookie ;
return cookies ;
} , cookies ) ;
}
}
/ *
Set - Cookie header field - values are sometimes comma joined in one string . This splits them without choking on commas
that are within a single set - cookie field - value , such as in the Expires portion .
This is uncommon , but explicitly allowed - see https : //tools.ietf.org/html/rfc2616#section-4.2
Node . js does this for every header * except * set - cookie - see https : //github.com/nodejs/node/blob/d5e363b77ebaf1caf67cd7528224b651c86815c1/lib/_http_incoming.js#L128
React Native ' s fetch does this for * every * header , including set - cookie .
Based on : https : //github.com/google/j2objc/commit/16820fdbc8f76ca0c33472810ce0cb03d20efe25
Credits to : https : //github.com/tomball for original and https://github.com/chrusart for JavaScript implementation
* /
function splitCookiesString ( cookiesString ) {
if ( Array . isArray ( cookiesString ) ) {
return cookiesString ;
}
if ( typeof cookiesString !== "string" ) {
return [ ] ;
}
var cookiesStrings = [ ] ;
var pos = 0 ;
var start ;
var ch ;
var lastComma ;
var nextStart ;
var cookiesSeparatorFound ;
function skipWhitespace ( ) {
while ( pos < cookiesString . length && /\s/ . test ( cookiesString . charAt ( pos ) ) ) {
pos += 1 ;
}
return pos < cookiesString . length ;
}
function 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 === "," ) {
// ',' is a cookie separator if we have later first '=', not ';' or ','
lastComma = pos ;
pos += 1 ;
skipWhitespace ( ) ;
nextStart = pos ;
while ( pos < cookiesString . length && notSpecialChar ( ) ) {
pos += 1 ;
}
// currently special character
if ( pos < cookiesString . length && cookiesString . charAt ( pos ) === "=" ) {
// we found cookies separator
cookiesSeparatorFound = true ;
// pos is inside the next cookie, so back up and return it.
pos = nextStart ;
cookiesStrings . push ( cookiesString . substring ( start , lastComma ) ) ;
start = pos ;
} else {
// in param ',' or param separator ';',
// we continue from that comma
pos = lastComma + 1 ;
}
} else {
pos += 1 ;
}
}
if ( ! cookiesSeparatorFound || pos >= cookiesString . length ) {
cookiesStrings . push ( cookiesString . substring ( start , cookiesString . length ) ) ;
}
}
return cookiesStrings ;
}
setCookie . exports = parse ;
setCookie . exports . parse = parse ;
setCookie . exports . parseString = parseString ;
var splitCookiesString _1 = setCookie . exports . splitCookiesString = splitCookiesString ;
/ * *
* An error that was thrown from within the SvelteKit runtime that is not fatal and doesn ' t result in a 500 , such as a 404.
* ` SvelteKitError ` goes through ` handleError ` .
* @ extends Error
* /
class SvelteKitError extends Error {
/ * *
* @ param { number } status
* @ param { string } text
* @ param { string } message
* /
constructor ( status , text , message ) {
super ( message ) ;
this . status = status ;
this . text = text ;
}
}
/ * *
* @ param { import ( 'http' ) . IncomingMessage } req
* @ param { number } [ body _size _limit ]
* /
function get _raw _body ( req , body _size _limit ) {
const h = req . headers ;
if ( ! h [ 'content-type' ] ) {
return null ;
}
const content _length = Number ( h [ 'content-length' ] ) ;
// check if no request body
if (
( req . httpVersionMajor === 1 && isNaN ( content _length ) && h [ 'transfer-encoding' ] == null ) ||
content _length === 0
) {
return null ;
}
if ( req . destroyed ) {
const readable = new ReadableStream ( ) ;
readable . cancel ( ) ;
return readable ;
}
let size = 0 ;
let cancelled = false ;
return new ReadableStream ( {
start ( controller ) {
if ( body _size _limit !== undefined && content _length > body _size _limit ) {
let message = ` Content-length of ${ content _length } exceeds limit of ${ body _size _limit } bytes. ` ;
if ( body _size _limit === 0 ) {
// https://github.com/sveltejs/kit/pull/11589
// TODO this exists to aid migration — remove in a future version
message += ' To disable body size limits, specify Infinity rather than 0.' ;
}
const error = new SvelteKitError ( 413 , 'Payload Too Large' , message ) ;
controller . error ( error ) ;
return ;
}
req . on ( 'error' , ( error ) => {
cancelled = true ;
controller . error ( error ) ;
} ) ;
req . on ( 'end' , ( ) => {
if ( cancelled ) return ;
controller . close ( ) ;
} ) ;
req . on ( 'data' , ( chunk ) => {
if ( cancelled ) return ;
size += chunk . length ;
if ( size > content _length ) {
cancelled = true ;
const constraint = content _length ? 'content-length' : 'BODY_SIZE_LIMIT' ;
const message = ` request body size exceeded ${ constraint } of ${ content _length } ` ;
const error = new SvelteKitError ( 413 , 'Payload Too Large' , message ) ;
controller . error ( error ) ;
return ;
}
controller . enqueue ( chunk ) ;
if ( controller . desiredSize === null || controller . desiredSize <= 0 ) {
req . pause ( ) ;
}
} ) ;
} ,
pull ( ) {
req . resume ( ) ;
} ,
cancel ( reason ) {
cancelled = true ;
req . destroy ( reason ) ;
}
} ) ;
}
/ * *
* @ param { {
* request : import ( 'http' ) . IncomingMessage ;
* base : string ;
* bodySizeLimit ? : number ;
* } } options
* @ returns { Promise < Request > }
* /
async function getRequest ( { request , base , bodySizeLimit } ) {
return new Request ( base + request . url , {
// @ts-expect-error
duplex : 'half' ,
method : request . method ,
headers : /** @type {Record<string, string>} */ ( request . headers ) ,
body :
request . method === 'GET' || request . method === 'HEAD'
? undefined
: get _raw _body ( request , bodySizeLimit )
} ) ;
}
/ * *
* @ param { import ( 'http' ) . ServerResponse } res
* @ param { Response } response
* @ returns { Promise < void > }
* /
async function setResponse ( res , response ) {
for ( const [ key , value ] of response . headers ) {
try {
res . setHeader (
key ,
key === 'set-cookie'
? splitCookiesString _1 (
// This is absurd but necessary, TODO: investigate why
/** @type {string}*/ ( response . headers . get ( key ) )
)
: value
) ;
} catch ( error ) {
res . getHeaderNames ( ) . forEach ( ( name ) => res . removeHeader ( name ) ) ;
res . writeHead ( 500 ) . end ( String ( error ) ) ;
return ;
}
}
res . writeHead ( response . status ) ;
if ( ! response . body ) {
res . end ( ) ;
return ;
}
if ( response . body . locked ) {
res . end (
'Fatal error: Response body is locked. ' +
"This can happen when the response was already read (for example through 'response.json()' or 'response.text()')."
) ;
return ;
}
const reader = response . body . getReader ( ) ;
if ( res . destroyed ) {
reader . cancel ( ) ;
return ;
}
const cancel = ( /** @type {Error|undefined} */ error ) => {
res . off ( 'close' , cancel ) ;
res . off ( 'error' , cancel ) ;
// If the reader has already been interrupted with an error earlier,
// then it will appear here, it is useless, but it needs to be catch.
reader . cancel ( error ) . catch ( ( ) => { } ) ;
if ( error ) res . destroy ( error ) ;
} ;
res . on ( 'close' , cancel ) ;
res . on ( 'error' , cancel ) ;
next ( ) ;
async function next ( ) {
try {
for ( ; ; ) {
const { done , value } = await reader . read ( ) ;
if ( done ) break ;
if ( ! res . write ( value ) ) {
res . once ( 'drain' , next ) ;
return ;
}
}
res . end ( ) ;
} catch ( error ) {
cancel ( error instanceof Error ? error : new Error ( String ( error ) ) ) ;
}
}
}
/ * *
* Converts a file on disk to a readable stream
* @ param { string } file
* @ returns { ReadableStream }
* @ since 2.4 . 0
* /
function createReadableStream ( file ) {
return /** @type {ReadableStream} */ ( Readable . toWeb ( createReadStream ( file ) ) ) ;
}
/* global "" */
const server = new Server ( manifest ) ;
const origin = env ( 'ORIGIN' , undefined ) ;
const xff _depth = parseInt ( env ( 'XFF_DEPTH' , '1' ) ) ;
const address _header = env ( 'ADDRESS_HEADER' , '' ) . toLowerCase ( ) ;
const protocol _header = env ( 'PROTOCOL_HEADER' , '' ) . toLowerCase ( ) ;
const host _header = env ( 'HOST_HEADER' , 'host' ) . toLowerCase ( ) ;
const port _header = env ( 'PORT_HEADER' , '' ) . toLowerCase ( ) ;
const body _size _limit = Number ( env ( 'BODY_SIZE_LIMIT' , '524288' ) ) ;
if ( isNaN ( body _size _limit ) ) {
throw new Error (
` Invalid BODY_SIZE_LIMIT: ' ${ env ( 'BODY_SIZE_LIMIT' ) } '. Please provide a numeric value. `
) ;
}
const dir = path . dirname ( fileURLToPath ( import . meta . url ) ) ;
const asset _dir = ` ${ dir } /client ${ base } ` ;
await server . init ( {
env : process . env ,
read : ( file ) => createReadableStream ( ` ${ asset _dir } / ${ file } ` )
} ) ;
/ * *
* @ param { string } path
* @ param { boolean } client
* /
function serve ( path , client = false ) {
return (
fs$1 . existsSync ( path ) &&
sirv ( path , {
etag : true ,
gzip : true ,
brotli : true ,
setHeaders :
client &&
( ( res , pathname ) => {
// only apply to build directory, not e.g. version.json
if ( pathname . startsWith ( ` / ${ manifest . appPath } /immutable/ ` ) && res . statusCode === 200 ) {
res . setHeader ( 'cache-control' , 'public,max-age=31536000,immutable' ) ;
}
} )
} )
) ;
}
// required because the static file server ignores trailing slashes
/** @returns {import('polka').Middleware} */
function serve _prerendered ( ) {
const handler = serve ( path . join ( dir , 'prerendered' ) ) ;
return ( req , res , next ) => {
let { pathname , search , query } = parse$1 ( req ) ;
try {
pathname = decodeURIComponent ( pathname ) ;
} catch {
// ignore invalid URI
}
if ( prerendered . has ( pathname ) ) {
return handler ( req , res , next ) ;
}
// remove or add trailing slash as appropriate
let location = pathname . at ( - 1 ) === '/' ? pathname . slice ( 0 , - 1 ) : pathname + '/' ;
if ( prerendered . has ( location ) ) {
if ( query ) location += search ;
res . writeHead ( 308 , { location } ) . end ( ) ;
} else {
next ( ) ;
}
} ;
}
/** @type {import('polka').Middleware} */
const ssr = async ( req , res ) => {
/** @type {Request} */
let request ;
try {
request = await getRequest ( {
base : origin || get _origin ( req . headers ) ,
request : req ,
bodySizeLimit : body _size _limit
} ) ;
} catch {
res . statusCode = 400 ;
res . end ( 'Bad Request' ) ;
return ;
}
setResponse (
res ,
await server . respond ( request , {
platform : { req } ,
getClientAddress : ( ) => {
if ( address _header ) {
if ( ! ( address _header in req . headers ) ) {
throw new Error (
` Address header was specified with ${
"" + 'ADDRESS_HEADER'
} = $ { address _header } but is absent from request `
) ;
}
const value = /** @type {string} */ ( req . headers [ address _header ] ) || '' ;
if ( address _header === 'x-forwarded-for' ) {
const addresses = value . split ( ',' ) ;
if ( xff _depth < 1 ) {
throw new Error ( ` ${ "" + 'XFF_DEPTH' } must be a positive integer ` ) ;
}
if ( xff _depth > addresses . length ) {
throw new Error (
` ${ "" + 'XFF_DEPTH' } is ${ xff _depth } , but only found ${
addresses . length
} addresses `
) ;
}
return addresses [ addresses . length - xff _depth ] . trim ( ) ;
}
return value ;
}
return (
req . connection ? . remoteAddress ||
// @ts-expect-error
req . connection ? . socket ? . remoteAddress ||
req . socket ? . remoteAddress ||
// @ts-expect-error
req . info ? . remoteAddress
) ;
}
} )
) ;
} ;
/** @param {import('polka').Middleware[]} handlers */
function sequence ( handlers ) {
/** @type {import('polka').Middleware} */
return ( req , res , next ) => {
/ * *
* @ param { number } i
* @ returns { ReturnType < import ( 'polka' ) . Middleware > }
* /
function handle ( i ) {
if ( i < handlers . length ) {
return handlers [ i ] ( req , res , ( ) => handle ( i + 1 ) ) ;
} else {
return next ( ) ;
}
}
return handle ( 0 ) ;
} ;
}
/ * *
* @ param { import ( 'http' ) . IncomingHttpHeaders } headers
* @ returns
* /
function get _origin ( headers ) {
const protocol = ( protocol _header && headers [ protocol _header ] ) || 'https' ;
const host = headers [ host _header ] ;
const port = port _header && headers [ port _header ] ;
if ( port ) {
return ` ${ protocol } :// ${ host } : ${ port } ` ;
} else {
return ` ${ protocol } :// ${ host } ` ;
}
}
const handler = sequence (
[
serve ( path . join ( dir , 'client' ) , true ) ,
serve ( path . join ( dir , 'static' ) ) ,
serve _prerendered ( ) ,
ssr
] . filter ( Boolean )
) ;
export { handler } ;