web-listener 1.2.1 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/run.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env -S node --disable-proto=delete --disallow-code-generation-from-strings --force-node-api-uncaught-exceptions-policy --no-addons --experimental-import-meta-resolve
2
- import{readFile as t,stat as e,realpath as n,readdir as o,access as r,constants as s}from"node:fs/promises";import{sep as i,join as c,resolve as a,dirname as f,extname as u,basename as p}from"node:path";import{spawn as l,spawnSync as h}from"node:child_process";import{fileURLToPath as d,pathToFileURL as w}from"node:url";import{readZip as m,Queue as g,Router as y,fileServer as $,staticContent as b,staticFileFinder as v,dynamicFileFinder as x,Negotiator as j,negotiateEncoding as E,zipFileFinder as k,requestHandler as S,addTeardown as O,getAbsolutePath as _,CONTINUE as N,proxy as M,assetServer as z,getSearch as A,getQuery as P,getPathParameter as J,WebListener as R,findCause as T,HTTPError as C,stringPredicate as B,compressFilesInDir as I,readMimeTypes as q,decompressMime as H,resetMime as U,registerMime as D}from"./index.js";import{text as G}from"node:stream/consumers";import{createServer as L}from"node:http";import{registerHooks as F}from"node:module";import{openSync as Z,constants as K,readSync as W,closeSync as Q}from"node:fs";import{inflateRawSync as V}from"node:zlib";const X=[];function Y(t){for(const e of X)if(t.startsWith(e.path)&&(t.length===e.path.length||t[e.path.length]===i)){const n=t.substring(e.path.length+1);return{...e,remaining:n?n.split(i):[]}}}async function tt(t,n){const o=Y(t);if(o)return o;const r=t.split(i);""===r[r.length-1]&&r.pop(),r[0]||(r.shift(),r.length>0&&(r[0]=i+r[0]));for(let t=r.length;t>0;--t){const o=c(...r.slice(0,t)),s=await e(o).catch(()=>null);if(!s)continue;if(!s.isFile())break;if(n&&t===r.length)return;const i={path:o,root:await m(o)};return X.push(i),{...i,remaining:r.slice(t)}}}async function et(e){const n=await tt(e,!0);if(n){const t=n.root.find(n.remaining);if(!t||t.isDirectory)throw new Error(`/${n.remaining.join("/")} not found in ${n.path}`);{const e=await t.open();try{return await G(e.createReadStream())}finally{await e.close()}}}return t(e,"utf-8")}function nt(t){const e=Y(t);return e?.root.find(e.remaining)}function ot(){X.length=0}const rt=new Map([["","dir"],["-a","host"],["-b","brotli"],["-c","config-file"],["-C","config-json"],["-d","dir"],["-e","ext"],["-g","gzip"],["-h","help"],["-H","header"],["-p","port"],["-P","proxy"],["-v","version"],["br","brotli"],["gz","gzip"],["zst","zstd"]]),st=new Map([["config-file",{type:"string"}],["config-json",{type:"string"}],["dir",{type:"string",multi:!0}],["exec",{type:"string",multi:!0}],["ext",{type:"string",multi:!0}],["port",{type:"number"}],["host",{type:"string"}],["zstd",{type:"boolean"}],["brotli",{type:"boolean"}],["gzip",{type:"boolean"}],["deflate",{type:"boolean"}],["proxy",{type:"string"}],["404",{type:"string"}],["spa",{type:"string"}],["header",{type:"string",multi:!0}],["dependencies",{type:"string"}],["mime",{type:"string",multi:!0}],["mime-types",{type:"string",multi:!0}],["redirect-map",{type:"string",multi:!0}],["write-compressed",{type:"boolean"}],["min-compress",{type:"number"}],["no-serve",{type:"boolean"}],["log",{type:"string"}],["help",{type:"boolean"}],["version",{type:"boolean"}]]);async function it(t,e,n){const o=new Map,r=async(t,e)=>{const n=await s(e.file,null);return e.includeMime&&(t.mime=[...ft(t.mime),...ft(n.mime)],n.mime=[]),e.includeBackgroundTasks&&(t.backgroundTasks.push(...n.backgroundTasks),n.backgroundTasks=[]),void 0!==e.serverPort?n.servers.filter(t=>t.port===e.serverPort):void 0!==e.serverIndex?e.serverIndex<0||e.serverIndex>=n.servers.length?[]:[n.servers[e.serverIndex]]:n.servers};async function s(t,e){const s=o.get(t);if(s){if(!0===s)throw new Error(`circular reference to ${t}`);return s}o.set(t,!0),null===e&&(e=await et(t));const i=n(JSON.parse(e),{file:t,path:""});for(let t=0;t<i.servers.length;){const e=i.servers[t];if(e.mount){for(let t=0;t<e.mount.length;++t){const n=e.mount[t];if("delegate"===n.type){const o=await r(i,n.config);if(1!==o.length)throw new Error(`${o.length>1?"multiple":"no"} servers found in ${n.config.file} matching requirements`);e.mount[t]={type:"nested",path:n.path,mount:o[0].mount}}}++t}else{const n=await r(i,e);if(!n)throw new Error(`no servers found in ${e.file} matching requirements`);i.servers.splice(t,1,...n),t+=n.length}}const c=i;return o.set(t,c),c}return s(a(e||"."),t)}function ct(t){const e=new Map;for(const[n,o=""]of t){const t=e.get(n);t?t.push(o):e.set(n,[o])}return Object.fromEntries(e.entries())}const at=new Map([["zstd",{value:"zstd",file:"{file}.zst"}],["brotli",{value:"br",file:"{file}.br"}],["gzip",{value:"gzip",file:"{file}.gz"}],["deflate",{value:"deflate",file:"{file}.deflate"}]]),ft=t=>Array.isArray(t)?t:t?[t]:[];const ut=t=>(e,n)=>{if(e!==t)throw new xt(`expected ${JSON.stringify(t)}`,n,8);return t},pt=t=>(e,n)=>{if(!t.has(e))throw new xt(`expected one of ${JSON.stringify(t)}`,n,7);return e},lt=t=>(e,n)=>{const o=[];let r=9;for(const s of t)try{return s(e,n)}catch(t){if(t instanceof xt){if(t.p>r)continue;t.p!==r&&(r=t.p,o.length=0)}else r=-1;o.push(t)}throw 1===o.length?o[0]:new AggregateError(o)},ht=(t,e)=>{if(null!==t)throw new xt("expected null, got "+typeof t,e);return null},dt=t=>(e,n)=>{if(!Array.isArray(e))throw new xt("expected list, got "+typeof e,n);return e.map((e,o)=>t(e,{...n,path:`${n.path}[${o}]`}))},wt=(t,e)=>{if("boolean"!=typeof t)throw new xt("expected boolean, got "+typeof t,e);return t},mt=(t,e,n)=>(o,r)=>{if("number"!=typeof o)throw new xt("expected number, got "+typeof o,r);if(t&&(0|o)!==o)throw new xt(`expected integer, got ${o}`,r);if("number"==typeof e&&o<e)throw new xt(`value cannot be less than ${e}`,r);if("number"==typeof n&&o>n)throw new xt(`value cannot be greater than ${n}`,r);return o},gt=()=>(t,e)=>{if("string"!=typeof t)throw new xt("expected string, got "+typeof t,e);try{return new RegExp(t)}catch{throw new xt("expected regular expression",e)}},yt=(t,e)=>(n,o)=>{if("string"!=typeof n)throw new xt("expected string, got "+typeof n,o);if(t&&!t.test(n))throw new xt(`expected string matching ${t}`,o);if("uri-reference"===e&&o.file){if(n.startsWith("file://"))return"file://"+a(f(o.file),n.substring(7));if(!n.includes("://"))return a(f(o.file),n)}return n},$t=(t,e,n)=>(o,r)=>{if("object"!=typeof o)throw new xt("expected object, got "+typeof o,r);if(!o)throw new xt("expected object, got null",r);if(Array.isArray(o))throw new xt("expected object, got list",r);const s=[],i=new Set;for(const[n,c]of Object.entries(o)){i.add(n);const o=(t.get(n)??e)(c,{...r,path:`${r.path}.${n}`});void 0!==o&&s.push([n,o])}for(const t of n)if(!i.has(t))throw new xt(`missing required property ${JSON.stringify(t)}`,r);for(const[e,n]of t)if(!i.has(e)){const t=n(void 0,{...r,path:`${r.path}.${e}`});void 0!==t&&s.push([e,t])}return Object.fromEntries(s)},bt=t=>t,vt=(t,e)=>{throw new xt("unknown property",e)};class xt extends Error{constructor(t,e,n=0){super(`${t} at ${e.path||"root"}`),this.p=n}}const jt=(t,e)=>Object.prototype.hasOwnProperty.call(t,e)?t[e]:void 0;class Et extends Error{}const kt=(t,e)=>Ot(t.imports??{},e),St=t=>t?t.startsWith("./")?t:"./"+t:void 0;function Ot(t,e){const n=new Map;for(const[o,r]of Object.entries(t)){const t=_t(r,e);void 0!==t&&n.set(o,t)}return n}function _t(t,e){if("string"==typeof t||null===t)return t;if(Array.isArray(t))for(const n of t){const t=_t(n,e);if(t)return t}else if(t)for(const[n,o]of Object.entries(t))if("default"===n||e.has(n)){const t=_t(o,e);if(t)return t}}const Nt=async(e,n=f(e))=>({isRoot:!1,id:e,dir:n,packageJson:JSON.parse(await t(e,{encoding:"utf-8"})),dependencies:new Map,getFilePaths:()=>(async t=>{const e=new Set,n=new g({t,o:"."});for(const t of n)for(const r of await o(t.t,{withFileTypes:!0,encoding:"utf-8"}))r.name.startsWith(".")||(r.isFile()?e.add(`${t.o}/${r.name}`):r.isDirectory()&&"node_modules"!==r.name&&n.push({t:c(t.t,r.name),o:`${t.o}/${r.name}`}));return e})(n)});async function Mt(t,e){const o=e&&w(e);try{return await Nt(d(import.meta.resolve(t+"/package.json",o)))}catch{}try{const e=await(async t=>{const e=new Set(["",i]);for(let o=await n(t,{encoding:"utf-8"});!e.has(o);o=f(o)){e.add(o);const t=c(o,"package.json");try{return await r(t,s.R_OK),await Nt(t)}catch{}}throw new Error(`package.json not found in ${t} or any parent folder`)})(f(d(import.meta.resolve(t,o))));if(e.packageJson.name===t)return e}catch{}return null}function zt(t,e,n,o){for(const[r,s]of e)t.set(n+r.substring(1),s?o+Tt(s.substring(1)):null)}function At(t){return Object.fromEntries([...t.entries()].filter(([t,e])=>null!==e).map(([t,e])=>[Tt(t),e]))}async function Pt(t,e,n,o){const r=new Map,s=[],i=[];for(const[e,c]of t){const t=e.indexOf("*"),a=c?.indexOf("*")??-1;if(!n(e)||null!==c&&(!o(c)||-1!==a!=(-1!==t)))throw new Error(`invalid entry: ${JSON.stringify(e)} => ${JSON.stringify(c)}`);if(-1!==t){const n=[e.length-1,e.substring(0,t),e.substring(t+1)],o=[t,e.length];c?s.push({i:n,u:[c.length-1,c.substring(0,a),c.substring(a+1)],l:o}):i.push({i:n,l:o})}else r.set(e,{u:c,l:[Number.POSITIVE_INFINITY,0]})}if(s.length){const t=await e();for(const e of t)for(const t of s){const n=Jt(t.u,e);if(!1!==n){const o=t.i[1]+n+t.i[2],s=r.get(o);(!s||Rt(t.l,s.l)>0)&&r.set(o,{u:e,l:t.l})}}}for(const t of i)for(const[e,n]of r)null!==n.u&&Rt(t.l,n.l)>0&&!1!==Jt(t.i,e)&&(n.u=null);return[...r].map(([t,e])=>[t,e.u])}const Jt=(t,e)=>!!(e.length>=t[0]&&e.startsWith(t[1])&&e.endsWith(t[2]))&&e.substring(t[1].length,e.length-t[2].length),Rt=(t,e)=>t[0]-e[0]||t[1]-e[1],Tt=t=>encodeURIComponent(t).replaceAll(/%2f/gi,"/").replaceAll(/%40/g,"@").replaceAll(/%23/g,"#");async function Ct(t,{environment:o=["browser","import","production"],mapFilePath:r="/importmap.json",sourcesBasePath:s="",modulesBasePath:i="/node_modules",...a}){const f=new y,u=await(async(t,e,n,o,r)=>{n.endsWith("/")&&(n=n.substring(0,n.length-1)),o.endsWith("/")&&(o=o.substring(0,o.length-1));const s=new Set,i=new Map,c=t.map(t=>{const e=r(t.packageJson,t.dir);let c=e;for(let t=2;s.has(c);++t)c=`${e}_${t}`;s.add(c);const a={h:t,m:[],$:t.isRoot?o:`${n}/${Tt(c)}`,v:t.isRoot?null:c};return i.set(t.id,a),a});await Promise.all(c.map(async t=>{t.m=await Pt(((t,e)=>Ot((t=>{const e=t.exports;if(void 0===e)return{".":{browser:St(t.browser),module:St(t.module),default:St(t.main)??"./index.js"},"./*":"./*"};if(Array.isArray(e)||"object"!=typeof e||!e)return{".":e};for(const t of Object.keys(e))if(t.startsWith("."))return e;return{".":e}})(t),e))(t.h.packageJson,e),async()=>t.j??=await t.h.getFilePaths(),t=>"."===t||t.startsWith("./"),t=>t.startsWith("./"))}));const a=[];let f=new Map;const u=[];for(const t of c){const n=new Map;t.h.packageJson.name&&t.h.packageJson.exports&&zt(n,t.m,t.h.packageJson.name,t.$);for(const[e,o]of t.h.dependencies){const t=i.get(o);if(!t)throw new Error("internal reference mismatch");zt(n,t.m,e,t.$)}const o=await Pt(kt(t.h.packageJson,e),async()=>(t.j??=await t.h.getFilePaths(),new Set([...n.keys(),...t.j])),t=>t.startsWith("#")&&t.length>1,t=>t.startsWith("./")||!t.startsWith("."));for(const[e,r]of o){if(!r){n.set(e,null);continue}const o=n.get(r);if(void 0!==o)n.set(e,o);else{if(!r.startsWith("./"))throw new Error(`unable to resolve import ${r}`);n.set(e,t.$+Tt(r.substring(1)))}}t.h.isRoot?f=n:u.push([t.$+"/",n]),a.push({dir:t.h.dir,subPath:t.v,imports:n})}const p=[];for(const[t,e]of u){const n=new Map(e);for(const[t,e]of f)n.get(t)===e&&n.delete(t);n.size&&p.push([t,At(n)])}return{packages:a,importMap:{imports:At(f),scopes:Object.fromEntries(p)}}})(await(async t=>{const o=await e(t);if(o.isDirectory())t=c(t,"package.json");else if(!o.isFile())throw new Error(`invalid package.json path: ${t}`);const r=await Nt(await n(t));r.isRoot=!0;const s=new Set([r.id]),i=[r];for(const t of i){const e=new Map;for(const n of Object.keys(t.packageJson.peerDependencies??{}))e.set(n,!1);for(const[n,o]of Object.entries(t.packageJson.peerDependenciesMeta??{}))e.has(n)&&e.set(n,o.optional??!1);for(const n of Object.keys(t.packageJson.optionalDependencies??{}))e.set(n,!0);for(const n of Object.keys(t.packageJson.dependencies??{}))e.set(n,!1);const n=c(t.dir,"a.js");await Promise.all([...e].map(async([e,o])=>{const r=await Mt(e,n);if(!r){if(o)return;throw new Error(`package ${e} not found (required by ${t.dir})`)}s.has(r.id)||(s.add(r.id),i.push(r)),t.dependencies.set(e,r.id)}))}return i})(t),new Set(o),i,s,Bt);for(const{dir:t,subPath:e}of u.packages)e&&f.mount("/"+e,await $(t,{...a,hide:[...a.hide??[],"node_modules"],indexFiles:[]}));const p=JSON.stringify(u.importMap);if(r){const t={headers:a.headers,encodings:["br","gzip"],minCompression:100};f.get(r,b(Buffer.from(p,"utf-8"),"application/importmap+json",t)),f.get(r+".js",b(Buffer.from(`const s=document.createElement('script');s.type='importmap';s.textContent=JSON.stringify(${p});document.head.append(s);`,"utf-8"),"text/javascript; charset=utf-8",t))}return Object.assign(f,{importMapJSON:p})}const Bt=t=>(t.name||"-")+(t.version?`@${t.version}`:""),It=new Map([[".mjs","module"],[".cjs","commonjs"],[".mts","module-typescript"],[".cts","commonjs-typescript"],[".json","json"],[".wasm","wasm"]]);F({resolve(t,e,n){if("web-listener"===t)t=c(f(d(import.meta.url)),"index.js");else{const n=URL.parse(t,e.parentURL);if(n&&"file:"===n.protocol&&nt(d(n)))return{url:n.toString(),format:It.get(u(n.pathname)),shortCircuit:!0}}return n(t,e)},load(t,e,n){if(t.startsWith("file://")){const e=d(t),n=nt(e);if(n&&!n.isDirectory)return{source:(t=>{const e=t.meta();if(!e.s)return Buffer.alloc(0);const n=Buffer.alloc(e.s),o=Z(t.zipFilePath,K.O_RDONLY);try{if(W(o,n,0,e.s,e.p)!==e.s)throw new Error("zip content has changed");return e.z?V(n,{maxOutputLength:t.stat().size}):n}finally{Q(o)}})(n),format:It.get(u(e))??"commonjs",shortCircuit:!0}}return n(t,e)}});const qt=new Set;async function Ht(t,e){qt.has(t)?e(`${t} has already been loaded into the module cache and will not be updated`):qt.add(t),await tt(t,!0);const n=await import(t),o=n?.default;if(!o||"function"!=typeof o&&"object"!=typeof o)throw new Error(`${t} must "export default" a request handler`);return o}async function Ut(t,n){const o=await e(t).catch(()=>null);if(o?.isDirectory())return"static-paths"===n.mode?v(t,n):x(t,n);const r=await tt(t,!1);if(!r)throw new Et(`content to serve not found at ${t}`);const s=r.root.find(r.remaining);if(!s?.isDirectory)throw new Error(`/${r.remaining.join("/")} in ${r.path} is not a directory`);const i=n;return i.negotiator||(i.negotiator=new j([E(["deflate"])])),k(s,i)}class Dt{constructor(t){this.k=t?t=>t:t=>t.toLowerCase(),this.S=new Map,this.O=[],this._=""}add(t,e){if("/"===t[0])this.S.set(this.k(t),e);else{if("~"!==t[0])throw new Error(`invalid URL: ${t}`);{const n="*"!==t[1],o=t.substring(n?1:2);this.O.push({N:new RegExp(o,n?"":"i"),u:e})}}}setDefault(t){this._=t}get(t){const e=this.S.get(this.k(t));if(void 0!==e)return e;for(const e of this.O){e.N.lastIndex=0;const n=e.N.exec(t);if(n)return e.u.replaceAll(/\$(?:([0-9]+)|([a-zA-Z][a-zA-Z0-9]*))/g,(t,e,o)=>e?n[Number.parseInt(e)]??"":n.groups&&Object.prototype.hasOwnProperty.call(n.groups,o)?n.groups[o]??"":"")}return this._}}function*Gt(t){let e=[];const n=/(\s+|#[^\n]*)|(;)|(?:"((?:[^"\\]+|\\.)*)")|(?:'((?:[^'\\]+|\\.)*)')|((?:[^#;\s\\"']+|\\.)+)/y;for(;n.lastIndex<t.length;){const o=n.exec(t);if(!o)throw new Error("invalid nginx syntax");const[,r,s,i,c,a]=o;if(!r)if(s)yield e,e=[];else if(a&&!a.includes("\\"))e.push({token:a,literal:!0});else{const t=i??c??a;if(void 0===t)throw new Error("nginx tokenisation error");e.push({token:t.replaceAll(/\\(.)/g,"$1"),literal:!1})}}if(e.length)throw new Error("unterminated statement - ensure all statements end with a semicolon")}const Lt=(t,e,n="raw")=>t.replaceAll(/\$\{(?:(raw|html|json|int|uri)\()?([^${}():]+)(?:(\))?:-((?:[^})\\]|\\.)*))?(\))?\}/g,(t,o,r,s,i,c)=>{if((s?1:0)+(c?1:0)!=(o?1:0))return t;const a=e(r),f=Ft[o??n];let u=a.M;return s&&u&&f&&(u=f(u)),u??=i?.replaceAll(/\\(.)/g,"$1")??"",c&&f&&(u=f(u)),!o&&f&&a.A!==n&&(u=f(u)),Zt(u)}),Ft={html:t=>Zt(t).replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;"),json:t=>JSON.stringify(Zt(t)),int:t=>{if(Array.isArray(t))return"0";const e=/^(?:\+|(-))?0*(\d+)$/.exec(t);return e?(e[1]??"")+e[2]:"0"},uri:t=>Array.isArray(t)?t.map(encodeURIComponent):encodeURIComponent(t)},Zt=t=>Array.isArray(t)?t.join("/"):t;async function Kt(t,e=()=>{},n){const o=new y;n&&o.use(S((t,e)=>{const o=Date.now();return O(t,()=>{const r=Date.now()-o;n({method:t.method??"GET",path:_(t),status:e.statusCode,duration:r})}),N}));for(const n of t)switch(n.type){case"nested":o.mount(n.path,await Kt(n.mount,e));break;case"headers":{const t=new Map(Object.entries(n.headers));o.mount(n.path,(e,n)=>(n.setHeaders(t),N));break}case"files":if("/dev/null"!==n.dir){const t=n.options.negotiation&&n.options.negotiation.length>0?new j(n.options.negotiation):void 0;o.mount(n.path,z(await Ut(n.dir,{...n.options,negotiator:t}),n.options))}break;case"proxy":o.mount(n.path,M(n.target,{...n.options,responseHeaders:[(t,e,o)=>({...o,...n.options.headers})]}));break;case"fixture":{const t=(t,e)=>{for(const[o,r]of Object.entries(n.headers))"string"==typeof r?e.setHeader(o,Lt(r,Wt(t))):"number"==typeof r?e.setHeader(o,r):e.setHeader(o,r.map(e=>Lt(e,Wt(t))));e.statusCode=n.status,e.end(Lt(n.body,Wt(t)))};"GET"===n.method&&o.onRequest("HEAD",n.path,t),o.onRequest(n.method,n.path,t);break}case"redirect":o.at(n.path,S((t,e)=>{let o=Lt(n.target,Wt(t),"uri");"/"===n.target[0]&&(o=o.replace(/^\/{2,}/,"/")),e.setHeader("location",o),e.statusCode=n.status,e.end()}));break;case"redirect-map":{const t=new Dt(n.options.caseSensitive);if("string"==typeof n.mapping){let e;try{e=await et(n.mapping)}catch(t){throw t&&"object"==typeof t&&"code"in t&&"ENOENT"===t.code?new Et(`redirect-map file not found at ${n.mapping}`):t}t:for(const n of Gt(e)){const e=n[0];if(e.literal)switch(e.token){case"default":if(2===n.length){t.setDefault(n[1].token);continue t}break;case"hostnames":throw new Error("redirect-map does not support hostnames");case"include":throw new Error("redirect-map does not support nested mapping files")}if(2!==n.length)throw new Error(`unknown statement in mapping file: ${n.map(t=>JSON.stringify(t)).join(" ")}`);t.add(e.token,n[1].token)}}else for(const[e,o]of Object.entries(n.mapping))t.add(e,o);o.use((e,o)=>{const r=t.get(e.url??"/");return r&&r!==(e.url??"/")?(o.setHeader("location",r),o.statusCode=n.status,o.end()):N});break}case"dependencies":o.mount(n.path,await Ct(n.package,{...n.options,modulesBasePath:n.path}));break;case"custom":{const t=await Ht(n.import,e);"string"==typeof n.method&&"get"===n.method.toLowerCase()?o.get(n.path,t):n.method?o.onRequest(n.method,n.path,t):o.mount(n.path,t);break}}return o}const Wt=t=>e=>"?"===e?{M:A(t)||void 0,A:"uri"}:"?"===e[0]?{M:P(t,e.substring(1)),A:"raw"}:{M:J(t,e),A:"raw"};class Qt{constructor(t,e){this.P=!1,this.J=!1,this.R=!1,this.T=t,this.C=e,this.B=new Map,this.I=new Set}async set(t,e,n){if(!this.J&&!this.R){clearTimeout(this.q),this.q=void 0;try{this.J=!0;const o=[],r=[];let s=!1;const i=this.I;this.I=new Set;t:for(let t=0;t<e.length;++t){const n=e[t];for(const t of i)if(te(t.config,n)){s||=t.isRunning(),this.I.add(t),i.delete(t);continue t}s=!0,r.push(async()=>{this.I.add(await this.H(n))})}for(const t of i)o.push(t.close);const c=new Set;for(let e=0;e<t.length;++e){const n=t[e],o=n.port;o<=0||o>65535?this.T(0,`servers[${e}] must have a specific port from 1 to 65535`):c.has(o)?this.T(0,`skipping servers[${e}] because port ${o} has already been defined`):(c.add(o),r.push(async()=>{const t=await this.U(n,this.B.get(o));t?this.B.set(o,t):this.B.delete(o)}))}this.P||=r.length>0;for(const[t,e]of this.B)c.has(t)||(r.push(e.close),this.B.delete(t));let a=!1;const f=async t=>{let e=[];if(await Promise.all(t.map(async t=>{try{await t()}catch(t){s&&!this.R&&t instanceof Et?(this.T(1,`${t.message} (retrying)`),a=!0):e.push(t)}})),e.length)throw await this.D(),e.length>1?new AggregateError(e):e[0]};await f(o),await f(r),this.R?this.D():a?this.q=setTimeout(()=>{ot(),this.set(t,e,n)},1e3):this.B.size?this.T(1,"all servers ready"):this.T(1,"no servers configured")}catch(t){n(t)}finally{this.J=!1}}}async H(t){const e=this.C("35",t.command),n=new AbortController;return new Promise((o,r)=>{this.T(2,`${e} ${this.C("2","starting")}`);let s=!0;const i=l(t.command,t.arguments,{cwd:t.cwd,env:{...process.env,TERM:"",COLORTERM:"",NO_COLOR:"1",...t.environment},killSignal:t.options.killSignal,uid:t.options.uid,gid:t.options.gid,stdio:["ignore","pipe","pipe"],signal:n.signal});t.options.displayStdout?Xt(i.stdout,`${e} ${this.C("2","[stdout]")} `,process.stderr):i.stdout.resume(),t.options.displayStderr?Xt(i.stderr,`${e} ${this.C("2","[stderr]")} `,process.stderr):i.stderr.resume(),i.addListener("error",t=>{this.T(0,`${e} startup failed: ${t.message}`),s=!1,r(t)}),i.addListener("exit",(t,n)=>{null!==t?this.T(2,`${e} closed ${this.C("2",`(exit code ${t})`)}`):this.T(2,`${e} closed ${this.C("2",`(exit signal ${n})`)}`),s=!1}),i.addListener("spawn",()=>o({config:t,isRunning:()=>s,close:()=>new Promise(n=>{null!==i.signalCode||null!==i.exitCode?n():(this.T(2,`${e} ${this.C("2",`closing (signal ${t.options.killSignal})`)}`),i.addListener("exit",n),i.kill(t.options.killSignal))})}))})}async U({port:t,host:e,options:n,mount:o},r){const s=this.C("34",`http://${e}:${t}`),i=await Kt(o,t=>this.T(1,`${s} ${this.C("33","warning")}: ${t}`),n.logRequests?t=>{const e=this.C("1",t.method.replaceAll(/[^a-zA-Z0-9\-_]/g,"?")),n=this.C(Vt[t.status/100|0]??"",String(t.status)),o=this.C("2",`(${t.duration}ms)`);this.T(0,`${s} ${e} ${t.path} ${n} ${o}`)}:void 0),c=new R(i);let a,f;if(c.addEventListener("error",t=>{t.preventDefault();const{error:e,context:n,request:o}=t.detail;(T(e,C)?.statusCode??500)>=500&&this.T(0,`${s} ${this.C("91","error")}: ${n} ${o?.url} ${e}`)}),r&&e===r.host&&((t,e)=>{for(const n of ee)if(t[n]!==e[n])return!1;return!0})(n,r.options))a=r.server,this.T(2,`${s} updated`),r.detach();else{if(r?(this.T(2,`${s} ${this.C("2","restarting (step 1: shutdown)")}`),await r.close(),this.T(2,`${s} ${this.C("2","restarting (step 2: start)")}`)):this.T(2,`${s} ${this.C("2","starting")}`),this.R)return;a=L(n),a.setTimeout(n.socketTimeout),f=Yt(a,t,e,n.backlog)}const u=c.attach(a,n);return f&&(await f,await new Promise(t=>setTimeout(t,1)),this.T(2,`${s} ready`)),{host:e,options:n,server:a,detach:()=>u("restart",n.restartTimeout),close:()=>new Promise(t=>{const e=u("shutdown",n.shutdownTimeout,!0,()=>{a.close(()=>{this.T(2,`${s} closed`),t()}),a.closeAllConnections()}).countConnections();e>0&&this.T(2,`${s} ${this.C("2",`closing (remaining connections: ${e})`)}`)})}}async D(){this.B.size&&(this.T(2,this.C("2","shutting down")),await Promise.all([...this.B.values(),...this.I].map(t=>t.close()))),this.P&&this.T(2,"shutdown complete")}shutdown(){this.R||(clearTimeout(this.q),this.q=void 0,this.R=!0,this.J||this.D())}}const Vt=["","37","32","36","31","41;97"];function Xt(t,e,n){let o="";const r=t=>n.write(e+t.replaceAll(/\x1b\[[\d;]*[A-K]/g,"").replaceAll(/[\x00-\x1f]/g,t=>`\\x${t.charCodeAt(0).toString(16).padStart(2,"0")}`)+"\n");t.addListener("data",t=>{o+=t;const e=o.split("\n");o=e.pop();for(const t of e)r(t)}),t.addListener("end",()=>{o&&r(o)})}const Yt=async(t,e,n,o=511)=>new Promise((r,s)=>{t.once("error",s),t.listen(e,n,o,()=>{t.off("error",s),r()})});function te(t,e){return t.command===e.command&&t.arguments.length===e.arguments.length&&t.arguments.every((t,n)=>t===e.arguments[n])&&t.cwd===e.cwd&&JSON.stringify(t.environment)===JSON.stringify(e.environment)&&t.options.uid===e.options.uid&&t.options.gid===e.options.gid&&t.options.killSignal===e.options.killSignal&&t.options.displayStdout===e.options.displayStdout&&t.options.displayStderr===e.options.displayStderr}const ee=["requestTimeout","keepAliveTimeout","keepAliveTimeoutBuffer","connectionsCheckingInterval","headersTimeout","highWaterMark","maxHeaderSize","noDelay","requireHostHeader","keepAlive","keepAliveInitialDelay","uniqueHeaders","backlog","socketTimeout"];function ne(t){return t.reduce((t,e)=>({rawSize:t.rawSize+e.rawSize,bestSize:t.bestSize+e.bestSize,created:t.created+e.created}),{rawSize:0,bestSize:0,created:0})}const oe=t=>t<2e3?`${t}B`.padStart(8," "):`${(t/1024).toFixed(1)}kB`.padStart(8," "),re=["none","ready","progress"];process.on("SIGUSR1",()=>{});let se=2;const ie=(t,e)=>t<=se&&process.stderr.write(e+"\n");function ce(t){process.stdin.destroy(),ie(0,t instanceof Error?t.message:String(t))}process.on("unhandledRejection",ce),process.on("uncaughtException",ce);const ae=process.stderr.isTTY&&!process.env.NO_COLOR?(t,e)=>t?`\x1b[${t}m${e}\x1b[0m`:e:(t,e)=>e,fe=(t=>{const e=[];for(let n=0;n<t.length;++n){const o=t[n];if("--"===o)continue;const r=/^--([^ =\-][^ =]*)=(.*)$/.exec(o);if(r){e.push([r[1],r[2]]);continue}const s=/^-([^ =]*)([^ =])=(.*)$/.exec(o);if(s&&"-"!==o[1]){for(const t of s[1])e.push(["-"+t,""]);e.push(["-"+s[2],s[3]]);continue}if("-"!==o[0]||"-"===o){if(0!==n)throw new Error(`value without key: ${o}`);e.push(["",o]);continue}let i=t[n+1];if(i&&"-"===i[0]&&i.length>1&&(i=void 0),void 0!==i&&++n,"-"===o[1])e.push([o.slice(2),i??""]);else{for(const t of o.slice(1,o.length-1))e.push(["-"+t,""]);e.push(["-"+o[o.length-1],i??""])}}const n=new Map;for(const[t,o]of e){const e=(rt.get(t)??t).toLowerCase(),r=st.get(e);if(!r)throw new Error(`unknown flag: ${t}`);let s;switch(r.type){case"string":s=o;break;case"number":s=Number.parseFloat(o);break;case"boolean":s=["","on","true","yes","y","1"].includes(o.toLowerCase())}if(r.multi){let t=n.get(e);t||(t=[],n.set(e,t)),t.push(s)}else{if(n.has(e))throw new Error(`multiple values for ${e}`);n.set(e,s)}}return n})(process.argv.slice(2)),ue=f(d(import.meta.url));if(fe.get("version")||fe.get("help")){let e={name:"web-listener",version:"unknown"};try{e=JSON.parse(await t(c(ue,"package.json"),"utf-8"))}catch{}fe.get("help")?h("man",["-M",ue,e.name],{stdio:["inherit","inherit","inherit"]}):process.stdout.write(`${e.name} ${e.version}\n`),process.exit(0)}(async()=>{const e=new Qt(ie,ae);process.on("unhandledRejection",()=>e.shutdown()),process.on("uncaughtException",()=>e.shutdown());const n=function(t){const e=n=>{const o=n.$ref;if(o&&o.startsWith("#/$defs/")){const e=jt(t.$defs,o.substring(8));if(!e)throw new Error(`unable to find ${o} in schema`);n={...e,...n}}const r=((t,e)=>{if(void 0!==t.const)return ut(t.const);if(t.enum)return pt(new Set(t.enum));if(t.anyOf)return lt(t.anyOf.map(e));switch(t.type){case"null":return ht;case"array":return dt(e(t.items));case"boolean":return wt;case"integer":return mt(!0,t.minimum,t.maximum);case"number":return mt(!1,t.minimum,t.maximum);case"object":const n=t.additionalProperties??!0,o=$t(new Map(Object.entries(t.properties??{}).map(([t,n])=>[t,e(n)])),"object"==typeof n?e(n):n?bt:vt,t.required??[]);if(t.$comment?.startsWith("flatten:")){const e=t.$comment.substring(8);return(t,n)=>o(t,n)[e]}return o;case"string":if("regex"===t.format)return gt();let r=null;return t.pattern&&(r=new RegExp(t.pattern)),yt(r,t.format??"");default:throw new Error(`unknown part type ${JSON.stringify(t)}`)}})(n,e);if(void 0!==n.default){const t=JSON.stringify(n.default);return(e,n)=>r(void 0===e?JSON.parse(t):e,n)}return(t,e)=>void 0===t?void 0:r(t,e)};return e(t)}(await(async()=>JSON.parse(await t(c(f(d(import.meta.url)),"schema.json"),"utf-8")))());function o(){process.stdin.destroy(),e.shutdown()}async function r(){ot();const t=await(async(t,e)=>{const n=(t,n=[])=>e.get(t)??n,o=t=>e.get(t),r=t=>e.get(t),s=o("config-file"),i=o("config-json"),c=r("port"),a=o("host"),f=n("dir"),u=n("exec").map(t=>t.split(" ")),p=n("ext").map(t=>t.startsWith(".")?t:`.${t}`),l=n("header").map(t=>(t=>{const e=t.match(/: ?/);return e?[t.substring(0,e.index),t.substring(e.index+e[0].length)]:[t]})(t)),h=o("404"),d=o("spa"),w=o("proxy"),m=o("dependencies"),g=r("min-compress"),y=n("mime"),$=n("mime-types"),b=n("redirect-map"),v=o("log");if(Number(Boolean(s))+Number(Boolean(i))+Number(Boolean(w))>1)throw new Error("multiple config files are not supported");let x;s?x=await it(null,s,t):i?x=await it(i,"",t):(f.length||f.push("."),x=t({servers:[{port:8080,mount:[]}]},{file:"",path:""}));for(const t of u)x.backgroundTasks.push({command:t[0],arguments:t.slice(1),cwd:process.cwd(),environment:{},options:{killSignal:"SIGTERM",displayStdout:!0,displayStderr:!0}});const j=1===x.servers.length?x.servers[0]:void 0;if(void 0!==c){if((0|c)!==c)throw new Error("port must be an integer");if(!j)throw new Error("cannot specify port on commandline when defining multiple servers");j.port=c}if(void 0!==a)for(const t of x.servers)t.host=a;if(f.length||d||h){if(!j)throw new Error("cannot specify dir, spa, or 404 on commandline when defining multiple servers");let t;d?t={statusCode:200,filePath:d}:h&&(t={statusCode:404,filePath:h});for(let e=0;e<f.length;++e){const n=f[e],o=e===f.length-1;j.mount.push({type:"files",path:"/",dir:n,options:{fallback:o?t:void 0}})}}if(w){if(!j)throw new Error("cannot specify proxy on commandline when defining multiple servers");j.mount.push({type:"proxy",path:"/",target:w,options:{}})}if(b.length>0)for(const t of x.servers)for(const e of b)t.mount.unshift({type:"redirect-map",mapping:e,status:307,options:{caseSensitive:!1}});if(void 0!==m)for(const t of x.servers)t.mount.push({type:"dependencies",path:"/node_modules",package:m,options:{}});const E=(t,e)=>{for(const n of x.servers)for(const o of n.mount)if("files"===o.type){o.options.negotiation??=[];let n=o.options.negotiation.find(e=>e.feature===t);n||(n={feature:t,options:[]},o.options.negotiation=[...o.options.negotiation,n]),n.options.find(t=>t.value===e.value)||n.options.push(e)}};for(const[t,n]of at)e.get(t)&&E("encoding",n);if(p.length)for(const t of x.servers)for(const e of t.mount)"files"===e.type&&(e.options.implicitSuffixes=p);if(l.length){const t={type:"headers",path:"/",headers:ct(l)};for(const e of x.servers)e.mount.unshift(t)}switch((y.length||$.length)&&(x.mime=[...ft(x.mime),...y,...$.map(t=>`file://${t}`)]),e.get("write-compressed")&&(x.writeCompressed=!0),void 0!==g&&(x.minCompress=g),e.get("no-serve")&&(x.noServe=!0),v){case"none":case"ready":case"progress":x.log=v;for(const t of x.servers)t.options.logRequests=!1;for(const t of x.backgroundTasks)t.options.displayStderr=!1,t.options.displayStdout=!1;break;case"full":x.log="progress"}return x})(n,fe);se=re.indexOf(t.log),await(async t=>{const e=[];for(const n of Array.isArray(t)?t:[t])"string"!=typeof n?e.push(new Map(Object.entries(n))):n.startsWith("file://")?e.push(q(await et(n.substring(7)))):e.push(H(n));U();for(const t of e)D(t)})(t.mime),t.writeCompressed&&await(async(t,e,n)=>{let o=0;for(const r of t)for(const t of r.mount)if("files"===t.type){const r=t.options.negotiation?.find(t=>"encoding"===t.feature);if(!r?.options?.length){n(2,`skipping ${t.dir} because no compression is configured`);continue}const s=r.match?` matching ${r.match}`:"";n(2,`compressing files in ${t.dir}${s} using ${r.options.map(t=>t.value).join(", ")}`);const i=B(r.match,!0),c=await I(t.dir,r.options,{minCompression:e,filter:(t,e)=>!["image","video","audio","font"].includes(e.split("/")[0])&&i(p(t))}),a=ne(c.filter(({mime:t})=>t.startsWith("text/"))),f=ne(c.filter(({mime:t})=>!t.startsWith("text/")));n(2,`text: ${oe(a.rawSize)} / ${oe(a.bestSize)} compressed`),n(2,`other: ${oe(f.rawSize)} / ${oe(f.bestSize)} compressed`),o+=a.created+f.created}n(2,`${((t,e,n=e+"s")=>1===t?`1 ${e}`:`${t} ${n}`)(o,"compressed file")} written`)})(t.servers,t.minCompress,ie),t.noServe?o():e.set(t.servers,t.backgroundTasks,t=>{if(t instanceof AggregateError)for(const e of t.errors)ie(0,e instanceof Error?e.message:String(e));else ie(0,t instanceof Error?t.message:String(t));process.stdin.destroy(),process.exit(1)})}function s(){return ie(2,"refreshing config"),r()}r(),process.on("SIGHUP",()=>s()),process.stdin.on("data",t=>{t.includes("\n")&&s()});let i=!1;process.on("SIGINT",()=>{i||(i=!0,ie(2,""),o())})})();
2
+ import{readFile as e,stat as t,realpath as n,readdir as o,access as r,constants as s}from"node:fs/promises";import{sep as i,join as a,resolve as c,dirname as f,extname as u,basename as p}from"node:path";import{spawn as l,spawnSync as d}from"node:child_process";import{fileURLToPath as w,pathToFileURL as m}from"node:url";import{readZip as h,Queue as g,Router as y,fileServer as b,staticContent as $,staticFileFinder as v,dynamicFileFinder as j,Negotiator as x,negotiateEncoding as k,zipFileFinder as S,Property as E,requestHandler as O,addTeardown as _,getAbsolutePath as N,CONTINUE as M,proxy as z,assetServer as J,getSearch as P,getQuery as C,getPathParameter as R,WebListener as T,findCause as A,HTTPError as B,stringPredicate as I,compressFilesInDir as q,readMimeTypes as H,decompressMime as U,resetMime as D,registerMime as G}from"./index.js";import{text as L}from"node:stream/consumers";import{createServer as F}from"node:http";import{registerHooks as Z}from"node:module";import{openSync as K,constants as W,readSync as Q,closeSync as V}from"node:fs";import{inflateRawSync as X}from"node:zlib";const Y=Array.isArray,ee=[];function te(e){for(const t of ee)if(e.startsWith(t.path)&&(e.length===t.path.length||e[t.path.length]===i)){const n=e.substring(t.path.length+1);return{...t,remaining:n?n.split(i):[]}}}async function ne(e,n){const o=te(e);if(o)return o;const r=e.split(i);""===r[r.length-1]&&r.pop(),r[0]||(r.shift(),r.length>0&&(r[0]=i+r[0]));for(let e=r.length;e>0;--e){const o=a(...r.slice(0,e)),s=await t(o).catch(()=>null);if(!s)continue;if(!s.isFile())break;if(n&&e===r.length)return;const i={path:o,root:await h(o)};return ee.push(i),{...i,remaining:r.slice(e)}}}async function oe(t){const n=await ne(t,!0);if(n){const e=n.root.find(n.remaining);if(!e||e.isDirectory)throw new Error(`/${n.remaining.join("/")} not found in ${n.path}`);{const t=await e.open();try{return await L(t.createReadStream())}finally{await t.close()}}}return e(t,"utf-8")}function re(e){const t=te(e);return t?.root.find(t.remaining)}function se(){ee.length=0}const ie=new Map([["","dir"],["-a","host"],["-b","brotli"],["-c","config-file"],["-C","config-json"],["-d","dir"],["-e","ext"],["-g","gzip"],["-h","help"],["-H","header"],["-p","port"],["-P","proxy"],["-v","version"],["br","brotli"],["gz","gzip"],["zst","zstd"]]),ae=new Map([["config-file",{type:"string"}],["config-json",{type:"string"}],["dir",{type:"string",multi:!0}],["exec",{type:"string",multi:!0}],["ext",{type:"string",multi:!0}],["port",{type:"number"}],["host",{type:"string"}],["zstd",{type:"boolean"}],["brotli",{type:"boolean"}],["gzip",{type:"boolean"}],["deflate",{type:"boolean"}],["proxy",{type:"string"}],["404",{type:"string"}],["spa",{type:"string"}],["header",{type:"string",multi:!0}],["dependencies",{type:"string"}],["mime",{type:"string",multi:!0}],["mime-types",{type:"string",multi:!0}],["redirect-map",{type:"string",multi:!0}],["write-compressed",{type:"boolean"}],["min-compress",{type:"number"}],["no-cache",{type:"boolean"}],["no-serve",{type:"boolean"}],["log",{type:"string"}],["log-format",{type:"string"}],["help",{type:"boolean"}],["version",{type:"boolean"}]]);function ce(e,t){const n=e=>{for(const o of e)t(o),"nested"===o.type&&n(o.mount)};for(const t of e)n(t.mount)}async function fe(e,t,n){const o=new Map,r=async(e,t)=>{const n=await s(t.file,null);return t.includeMime&&(e.mime=[...de(e.mime),...de(n.mime)],n.mime=[]),t.includeBackgroundTasks&&(e.backgroundTasks.push(...n.backgroundTasks),n.backgroundTasks=[]),void 0!==t.serverPort?n.servers.filter(e=>e.port===t.serverPort):void 0!==t.serverIndex?t.serverIndex<0||t.serverIndex>=n.servers.length?[]:[n.servers[t.serverIndex]]:n.servers};async function s(e,t){const s=o.get(e);if(s){if(!0===s)throw new Error(`circular reference to ${e}`);return s}o.set(e,!0),null===t&&(t=await oe(e));const i=n(JSON.parse(t),{file:e,path:""});for(let e=0;e<i.servers.length;){const t=i.servers[e];if(t.mount){for(let e=0;e<t.mount.length;++e){const n=t.mount[e];if("delegate"===n.type){const o=await r(i,n.config);if(1!==o.length)throw new Error(`${o.length>1?"multiple":"no"} servers found in ${n.config.file} matching requirements`);t.mount[e]={...n,type:"nested",mount:o[0].mount}}}++e}else{const n=await r(i,t);if(!n)throw new Error(`no servers found in ${t.file} matching requirements`);i.servers.splice(e,1,...n),e+=n.length}}const a=i;return o.set(e,a),a}return s(c(t||"."),e)}function ue(e,t){const n=new Map(Object.entries(e??{}).map(([e,t])=>[e,Array.isArray(t)?[...t]:[String(t)]]));for(const[e,o=""]of t){const t=n.get(e);t?t.push(o):n.set(e,[o])}return Object.fromEntries(n.entries())}function pe(e,t){const n=new Set(t.map(e=>e[0].toLowerCase()));return Object.fromEntries(Object.entries(e??{}).filter(e=>!n.has(e[0].toLowerCase())))}const le=new Map([["zstd",{value:"zstd",file:"{file}.zst"}],["brotli",{value:"br",file:"{file}.br"}],["gzip",{value:"gzip",file:"{file}.gz"}],["deflate",{value:"deflate",file:"{file}.deflate"}]]),de=e=>Y(e)?e:e?[e]:[];const we=e=>(t,n)=>{if(t!==e)throw new Ee(`expected ${JSON.stringify(e)}`,n,8);return e},me=e=>(t,n)=>{if(!e.has(t))throw new Ee(`expected one of ${JSON.stringify(e)}`,n,7);return t},he=e=>(t,n)=>{const o=[];let r=9;for(const s of e)try{return s(t,n)}catch(e){if(e instanceof Ee){if(e.p>r)continue;e.p!==r&&(r=e.p,o.length=0)}else r=-1;o.push(e)}throw 1===o.length?o[0]:new AggregateError(o)},ge=(e,t)=>{if(null!==e)throw new Ee("expected null, got "+typeof e,t);return null},ye=e=>(t,n)=>{if(!Y(t))throw new Ee("expected list, got "+typeof t,n);return t.map((t,o)=>e(t,{...n,path:`${n.path}[${o}]`}))},be=(e,t)=>{if("boolean"!=typeof e)throw new Ee("expected boolean, got "+typeof e,t);return e},$e=(e,t,n)=>(o,r)=>{if("number"!=typeof o)throw new Ee("expected number, got "+typeof o,r);if(e&&(0|o)!==o)throw new Ee(`expected integer, got ${o}`,r);if("number"==typeof t&&o<t)throw new Ee(`value cannot be less than ${t}`,r);if("number"==typeof n&&o>n)throw new Ee(`value cannot be greater than ${n}`,r);return o},ve=()=>(e,t)=>{if("string"!=typeof e)throw new Ee("expected string, got "+typeof e,t);try{return new RegExp(e)}catch{throw new Ee("expected regular expression",t)}},je=(e,t)=>(n,o)=>{if("string"!=typeof n)throw new Ee("expected string, got "+typeof n,o);if(e&&!e.test(n))throw new Ee(`expected string matching ${e}`,o);if("uri-reference"===t&&o.file){if(n.startsWith("file://"))return"file://"+c(f(o.file),n.substring(7));if(!n.includes("://"))return c(f(o.file),n)}return n},xe=(e,t,n)=>(o,r)=>{if("object"!=typeof o)throw new Ee("expected object, got "+typeof o,r);if(!o)throw new Ee("expected object, got null",r);if(Y(o))throw new Ee("expected object, got list",r);const s=[],i=new Set;for(const[n,a]of Object.entries(o)){i.add(n);const o=(e.get(n)??t)(a,{...r,path:`${r.path}.${n}`});void 0!==o&&s.push([n,o])}for(const e of n)if(!i.has(e))throw new Ee(`missing required property ${JSON.stringify(e)}`,r);for(const[t,n]of e)if(!i.has(t)){const e=n(void 0,{...r,path:`${r.path}.${t}`});void 0!==e&&s.push([t,e])}return Object.fromEntries(s)},ke=e=>e,Se=(e,t)=>{throw new Ee("unknown property",t)};class Ee extends Error{constructor(e,t,n=0){super(`${e} at ${t.path||"root"}`),this.p=n}}const Oe=(e,t)=>Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0;class _e extends Error{}const Ne=(e,t)=>ze(e.imports??{},t),Me=e=>e?e.startsWith("./")?e:"./"+e:void 0;function ze(e,t){const n=new Map;for(const[o,r]of Object.entries(e)){const e=Je(r,t);void 0!==e&&n.set(o,e)}return n}function Je(e,t){if("string"==typeof e||null===e)return e;if(Y(e))for(const n of e){const e=Je(n,t);if(e)return e}else if(e)for(const[n,o]of Object.entries(e))if("default"===n||t.has(n)){const e=Je(o,t);if(e)return e}}const Pe=async(t,n=f(t))=>({isRoot:!1,id:t,dir:n,packageJson:JSON.parse(await e(t,{encoding:"utf-8"})),dependencies:new Map,getFilePaths:()=>(async e=>{const t=new Set,n=new g({t:e,o:"."});for(const e of n)for(const r of await o(e.t,{withFileTypes:!0,encoding:"utf-8"}))r.name.startsWith(".")||(r.isFile()?t.add(`${e.o}/${r.name}`):r.isDirectory()&&"node_modules"!==r.name&&n.push({t:a(e.t,r.name),o:`${e.o}/${r.name}`}));return t})(n)});async function Ce(e,t){const o=t&&m(t);try{return await Pe(w(import.meta.resolve(e+"/package.json",o)))}catch{}try{const t=await(async e=>{const t=new Set(["",i]);for(let o=await n(e,{encoding:"utf-8"});!t.has(o);o=f(o)){t.add(o);const e=a(o,"package.json");try{return await r(e,s.R_OK),await Pe(e)}catch{}}throw new Error(`package.json not found in ${e} or any parent folder`)})(f(w(import.meta.resolve(e,o))));if(t.packageJson.name===e)return t}catch{}return null}function Re(e,t,n,o){for(const[r,s]of t)e.set(n+r.substring(1),s?o+qe(s.substring(1)):null)}function Te(e){return Object.fromEntries([...e.entries()].filter(([e,t])=>null!==t).map(([e,t])=>[qe(e),t]))}async function Ae(e,t,n,o){const r=new Map,s=[],i=[];for(const[t,a]of e){const e=t.indexOf("*"),c=a?.indexOf("*")??-1;if(!n(t)||null!==a&&(!o(a)||-1!==c!=(-1!==e)))throw new Error(`invalid entry: ${JSON.stringify(t)} => ${JSON.stringify(a)}`);if(-1!==e){const n=[t.length-1,t.substring(0,e),t.substring(e+1)],o=[e,t.length];a?s.push({i:n,u:[a.length-1,a.substring(0,c),a.substring(c+1)],l:o}):i.push({i:n,l:o})}else r.set(t,{u:a,l:[Number.POSITIVE_INFINITY,0]})}if(s.length){const e=await t();for(const t of e)for(const e of s){const n=Be(e.u,t);if(!1!==n){const o=e.i[1]+n+e.i[2],s=r.get(o);(!s||Ie(e.l,s.l)>0)&&r.set(o,{u:t,l:e.l})}}}for(const e of i)for(const[t,n]of r)null!==n.u&&Ie(e.l,n.l)>0&&!1!==Be(e.i,t)&&(n.u=null);return[...r].map(([e,t])=>[e,t.u])}const Be=(e,t)=>!!(t.length>=e[0]&&t.startsWith(e[1])&&t.endsWith(e[2]))&&t.substring(e[1].length,t.length-e[2].length),Ie=(e,t)=>e[0]-t[0]||e[1]-t[1],qe=e=>encodeURIComponent(e).replaceAll(/%2f/gi,"/").replaceAll(/%40/g,"@").replaceAll(/%23/g,"#");async function He(e,{environment:o=["browser","import","production"],mapFilePath:r="/importmap.json",sourcesBasePath:s="",modulesBasePath:i="/node_modules",...c}){const f=new y,u=await(async(e,t,n,o,r)=>{n.endsWith("/")&&(n=n.substring(0,n.length-1)),o.endsWith("/")&&(o=o.substring(0,o.length-1));const s=new Set,i=new Map,a=e.map(e=>{const t=r(e.packageJson,e.dir);let a=t;for(let e=2;s.has(a);++e)a=`${t}_${e}`;s.add(a);const c={m:e,h:[],$:e.isRoot?o:`${n}/${qe(a)}`,v:e.isRoot?null:a};return i.set(e.id,c),c});await Promise.all(a.map(async e=>{e.h=await Ae(((e,t)=>ze((e=>{const t=e.exports;if(void 0===t)return{".":{browser:Me(e.browser),module:Me(e.module),default:Me(e.main)??"./index.js"},"./*":"./*"};if(Y(t)||"object"!=typeof t||!t)return{".":t};for(const e of Object.keys(t))if(e.startsWith("."))return t;return{".":t}})(e),t))(e.m.packageJson,t),async()=>e.j??=await e.m.getFilePaths(),e=>"."===e||e.startsWith("./"),e=>e.startsWith("./"))}));const c=[];let f=new Map;const u=[];for(const e of a){const n=new Map;e.m.packageJson.name&&e.m.packageJson.exports&&Re(n,e.h,e.m.packageJson.name,e.$);for(const[t,o]of e.m.dependencies){const e=i.get(o);if(!e)throw new Error("internal reference mismatch");Re(n,e.h,t,e.$)}const o=await Ae(Ne(e.m.packageJson,t),async()=>(e.j??=await e.m.getFilePaths(),new Set([...n.keys(),...e.j])),e=>e.startsWith("#")&&e.length>1,e=>e.startsWith("./")||!e.startsWith("."));for(const[t,r]of o){if(!r){n.set(t,null);continue}const o=n.get(r);if(void 0!==o)n.set(t,o);else{if(!r.startsWith("./"))throw new Error(`unable to resolve import ${r}`);n.set(t,e.$+qe(r.substring(1)))}}e.m.isRoot?f=n:u.push([e.$+"/",n]),c.push({dir:e.m.dir,subPath:e.v,imports:n})}const p=[];for(const[e,t]of u){const n=new Map(t);for(const[e,t]of f)n.get(e)===t&&n.delete(e);n.size&&p.push([e,Te(n)])}return{packages:c,importMap:{imports:Te(f),scopes:Object.fromEntries(p)}}})(await(async e=>{const o=await t(e);if(o.isDirectory())e=a(e,"package.json");else if(!o.isFile())throw new Error(`invalid package.json path: ${e}`);const r=await Pe(await n(e));r.isRoot=!0;const s=new Set([r.id]),i=[r];for(const e of i){const t=new Map;for(const n of Object.keys(e.packageJson.peerDependencies??{}))t.set(n,!1);for(const[n,o]of Object.entries(e.packageJson.peerDependenciesMeta??{}))t.has(n)&&t.set(n,o.optional??!1);for(const n of Object.keys(e.packageJson.optionalDependencies??{}))t.set(n,!0);for(const n of Object.keys(e.packageJson.dependencies??{}))t.set(n,!1);const n=a(e.dir,"a.js");await Promise.all([...t].map(async([t,o])=>{const r=await Ce(t,n);if(!r){if(o)return;throw new Error(`package ${t} not found (required by ${e.dir})`)}s.has(r.id)||(s.add(r.id),i.push(r)),e.dependencies.set(t,r.id)}))}return i})(e),new Set(o),i,s,Ue);for(const{dir:e,subPath:t}of u.packages)t&&f.mount("/"+t,await b(e,{...c,hide:[...c.hide??[],"node_modules"],indexFiles:[]}));const p=JSON.stringify(u.importMap);if(r){const e={headers:c.headers,encodings:["br","gzip"],minCompression:100};f.get(r,$(Buffer.from(p,"utf-8"),"application/importmap+json",e)),f.get(r+".js",$(Buffer.from(`const s=document.createElement('script');s.type='importmap';s.textContent=JSON.stringify(${p});document.head.append(s);`,"utf-8"),"text/javascript; charset=utf-8",e))}return Object.assign(f,{importMapJSON:p})}const Ue=e=>(e.name||"-")+(e.version?`@${e.version}`:""),De=new Map([[".mjs","module"],[".cjs","commonjs"],[".mts","module-typescript"],[".cts","commonjs-typescript"],[".json","json"],[".wasm","wasm"]]);Z({resolve(e,t,n){if("web-listener"===e)e=a(f(w(import.meta.url)),"index.js");else{const n=URL.parse(e,t.parentURL);if(n&&"file:"===n.protocol&&re(w(n)))return{url:n.toString(),format:De.get(u(n.pathname)),shortCircuit:!0}}return n(e,t)},load(e,t,n){if(e.startsWith("file://")){const t=w(e),n=re(t);if(n&&!n.isDirectory)return{source:(e=>{const t=e.meta();if(!t.s)return Buffer.alloc(0);const n=Buffer.alloc(t.s),o=K(e.zipFilePath,W.O_RDONLY);try{if(Q(o,n,0,t.s,t.p)!==t.s)throw new Error("zip content has changed");return t.z?X(n,{maxOutputLength:e.stat().size}):n}finally{V(o)}})(n),format:De.get(u(t))??"commonjs",shortCircuit:!0}}return n(e,t)}});const Ge=new Set,Le=new Set;async function Fe(e,t,n){Le.has(e)||(Ge.has(e)&&n(`${e} has already been loaded into the module cache and will not be updated`),Le.add(e)),await ne(e,!0);const o=await import(e),r=o?.[t||"default"];if(!r||"function"!=typeof r&&"object"!=typeof r)throw new Error(`${e} must export a request handler ${t?`named ${JSON.stringify(t)}`:"as default"}`);return r}async function Ze(e,n){const o=await t(e).catch(()=>null);if(o?.isDirectory())return"static-paths"===n.mode?v(e,n):j(e,n);const r=await ne(e,!1);if(!r)throw new _e(`content to serve not found at ${e}`);const s=r.root.find(r.remaining);if(!s?.isDirectory)throw new Error(`/${r.remaining.join("/")} in ${r.path} is not a directory`);const i=n;return i.negotiator||(i.negotiator=new x([k(["gzip"])])),S(s,i)}class Ke{constructor(e){this.k=e?e=>e:e=>e.toLowerCase(),this.S=new Map,this.O=[],this._=""}add(e,t){if("/"===e[0])this.S.set(this.k(e),t);else{if("~"!==e[0])throw new Error(`invalid URL: ${e}`);{const n="*"!==e[1],o=e.substring(n?1:2);this.O.push({N:new RegExp(o,n?"":"i"),u:t})}}}setDefault(e){this._=e}get(e){const t=this.S.get(this.k(e));if(void 0!==t)return t;for(const t of this.O){t.N.lastIndex=0;const n=t.N.exec(e);if(n)return t.u.replaceAll(/\$(?:([0-9]+)|([a-zA-Z][a-zA-Z0-9]*))/g,(e,t,o)=>t?n[Number.parseInt(t)]??"":n.groups&&Object.prototype.hasOwnProperty.call(n.groups,o)?n.groups[o]??"":"")}return this._}}function*We(e){let t=[];const n=/(\s+|#[^\n]*)|(;)|(?:"((?:[^"\\]+|\\.)*)")|(?:'((?:[^'\\]+|\\.)*)')|((?:[^#;\s\\"']+|\\.)+)/y;for(;n.lastIndex<e.length;){const o=n.exec(e);if(!o)throw new Error("invalid nginx syntax");const[,r,s,i,a,c]=o;if(!r)if(s)yield t,t=[];else if(c&&!c.includes("\\"))t.push({token:c,literal:!0});else{const e=i??a??c;if(void 0===e)throw new Error("nginx tokenisation error");t.push({token:e.replaceAll(/\\(.)/g,"$1"),literal:!1})}}if(t.length)throw new Error("unterminated statement - ensure all statements end with a semicolon")}const Qe=(e,t,n="raw")=>e.replaceAll(/\$\{(?:(raw|html|json|int|uri)\()?([^${}():]+)(?:(\))?:-((?:[^})\\]|\\.)*))?(\))?\}/g,(e,o,r,s,i,a)=>{if((s?1:0)+(a?1:0)!=(o?1:0))return e;const c=t(r),f=Ve[o??n];let u=c.M;return s&&u&&f&&(u=f(u)),u??=i?.replaceAll(/\\(.)/g,"$1")??"",a&&f&&(u=f(u)),!o&&f&&c.J!==n&&(u=f(u)),Xe(u)}),Ve={html:e=>Xe(e).replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;"),json:e=>JSON.stringify(Xe(e)),int:e=>{if(Y(e))return"0";const t=/^(?:\+|(-))?0*(\d+)$/.exec(e);return t?(t[1]??"")+t[2]:"0"},uri:e=>Y(e)?e.map(encodeURIComponent):encodeURIComponent(e)},Xe=e=>Y(e)?e.join("/"):e,Ye=new E(0),et=e=>(Ye.set(e,e.url?.length??0),M);async function tt(e,t=()=>{},n){const o=new y;n&&o.use(O((e,t)=>{const o=Date.now();return _(e,()=>{const r=Date.now()-o;let s=N(e);const i=Ye.get(e);if(i>1){const e=s.length+1-i;s=s.substring(0,e)+("?"===s[e]?"?":"")+"[***]"}n({method:e.method??"GET",path:s,status:t.statusCode,duration:r})}),M}));for(const n of e)switch(n.type){case"nested":o.mount(n.path,n.maskSubpaths?et:null,await tt(n.mount,t));break;case"headers":{const e=new Map(Object.entries(n.headers));o.mount(n.path,(t,n)=>(n.setHeaders(e),M));break}case"files":if("/dev/null"!==n.dir){const e=n.options.negotiation&&n.options.negotiation.length>0?new x(n.options.negotiation):void 0;o.mount(n.path,J(await Ze(n.dir,{...n.options,negotiator:e}),n.options))}break;case"proxy":o.mount(n.path,z(n.target,{...n.options,responseHeaders:[(e,t,o)=>({...o,...n.options.headers})]}));break;case"fixture":{const e=(e,t)=>{for(const[o,r]of Object.entries(n.headers))"string"==typeof r?t.setHeader(o,Qe(r,nt(e))):"number"==typeof r?t.setHeader(o,r):t.setHeader(o,r.map(t=>Qe(t,nt(e))));t.statusCode=n.status,t.end(Qe(n.body,nt(e)))};"GET"===n.method&&o.onRequest("HEAD",n.path,e),o.onRequest(n.method,n.path,e);break}case"redirect":o.at(n.path,O((e,t)=>{let o=Qe(n.target,nt(e),"uri");"/"===n.target[0]&&(o=o.replace(/^\/{2,}/,"/")),t.setHeader("location",o),t.statusCode=n.status,t.end()}));break;case"redirect-map":{const e=new Ke(n.options.caseSensitive);if("string"==typeof n.mapping){let t;try{t=await oe(n.mapping)}catch(e){throw e&&"object"==typeof e&&"code"in e&&"ENOENT"===e.code?new _e(`redirect-map file not found at ${n.mapping}`):e}e:for(const n of We(t)){const t=n[0];if(t.literal)switch(t.token){case"default":if(2===n.length){e.setDefault(n[1].token);continue e}break;case"hostnames":throw new Error("redirect-map does not support hostnames");case"include":throw new Error("redirect-map does not support nested mapping files")}if(2!==n.length)throw new Error(`unknown statement in mapping file: ${n.map(e=>JSON.stringify(e)).join(" ")}`);e.add(t.token,n[1].token)}}else for(const[t,o]of Object.entries(n.mapping))e.add(t,o);o.use((t,o)=>{const r=e.get(t.url??"/");return r&&r!==(t.url??"/")?(o.setHeader("location",r),o.statusCode=n.status,o.end()):M});break}case"dependencies":o.mount(n.path,await He(n.package,{...n.options,modulesBasePath:n.path}));break;case"custom":{const e=await Fe(n.import,n.namedExport,t),r=n.maskSubpaths?et:null;"string"==typeof n.method&&"get"===n.method.toLowerCase()?o.get(n.path,r,e):n.method?o.onRequest(n.method,n.path,r,e):o.mount(n.path,r,e);break}}return o}const nt=e=>t=>"?"===t?{M:P(e)||void 0,J:"uri"}:"?"===t[0]?{M:C(e,t.substring(1)),J:"raw"}:{M:R(e,t),J:"raw"};class ot{constructor(){this.P=!1,this.C=!1,this.R=!1,this.T=new Map,this.A=new Set}async set(e,t,n,o){if(!this.C&&!this.R){clearTimeout(this.B),this.B=void 0;try{this.C=!0;const r=[],s=[];let i=!1;const a=this.A;this.A=new Set;e:for(let e=0;e<t.length;++e){const o=t[e];for(const e of a)if(it(e.config,o)){i||=e.isRunning(),this.A.add(e),a.delete(e);continue e}i=!0,s.push(async()=>{this.A.add(await this.I(o,n))})}for(const e of a)r.push(e.close);const c=new Set;for(let t=0;t<e.length;++t){const o=e[t],r=o.port;r<=0||r>65535?n(0,{type:"warn",message:`servers[${t}] must have a specific port from 1 to 65535`}):c.has(r)?n(0,{type:"warn",message:`skipping servers[${t}] because port ${r} has already been defined`}):(c.add(r),s.push(async()=>{const e=await this.q(o,this.T.get(r),n);e?this.T.set(r,e):this.T.delete(r)}))}this.P||=s.length>0;for(const[e,t]of this.T)c.has(e)||(s.push(t.close),this.T.delete(e));let f=!1;const u=async e=>{let t=[];if(await Promise.all(e.map(async e=>{try{await e()}catch(e){i&&!this.R&&e instanceof _e?(n(1,{type:"warn",message:`${e.message} (retrying)`}),f=!0):t.push(e)}})),t.length)throw await this.H(n),t.length>1?new AggregateError(t):t[0]};await u(r),await u(s),(()=>{for(const e of Le)Ge.add(e);Le.clear()})(),this.R?this.H(n):f?this.B=setTimeout(()=>{se(),this.set(e,t,n,o)},1e3):this.T.size?n(1,{message:"all servers ready"}):n(1,{message:"no servers configured"})}catch(e){o(e)}finally{this.C=!1}}}async I(e,t){const n={service:e.command,serviceCol:"35"},o=new AbortController;return new Promise((r,s)=>{t(2,{...n,type:"detail",message:"starting"});let i=!0;const a=l(e.command,e.arguments,{cwd:e.cwd,env:{...process.env,TERM:"",COLORTERM:"",NO_COLOR:"1",...e.environment},killSignal:e.options.killSignal,uid:e.options.uid,gid:e.options.gid,stdio:["ignore","pipe","pipe"],signal:o.signal});e.options.displayStdout?rt(a.stdout,e=>t(0,{...n,thread:"stdout",message:e})):a.stdout.resume(),e.options.displayStderr?rt(a.stderr,e=>t(0,{...n,thread:"stderr",message:e})):a.stderr.resume(),a.addListener("error",e=>{t(0,{...n,type:"error",message:`startup failed: ${e.message}`}),i=!1,s(e)}),a.addListener("exit",(e,o)=>{t(2,{...n,message:"closed",stats:null!==e?{code:e}:{signal:o}}),i=!1}),a.addListener("spawn",()=>r({config:e,isRunning:()=>i,close:()=>new Promise(o=>{null!==a.signalCode||null!==a.exitCode?o():(t(2,{...n,type:"detail",message:"closing",stats:{signal:e.options.killSignal}}),a.addListener("exit",o),a.kill(e.options.killSignal))})}))})}async q({port:e,host:t,options:n,mount:o},r,s){const i={service:`http://${t}:${e}`,serviceCol:"34"},a=await tt(o,e=>s(1,{...i,type:"warn",message:e}),n.logRequests?e=>s(0,{...i,method:e.method,path:e.path,status:e.status,stats:{duration:e.duration}}):void 0),c=new T(a);let f,u;if(c.addEventListener("error",e=>{e.preventDefault();const{error:t,context:n,request:o}=e.detail;(A(t,B)?.statusCode??500)>=500&&s(0,{...i,thread:n,type:"error",path:o?.url,message:String(t)})}),r&&t===r.host&&((e,t)=>{for(const n of at)if(e[n]!==t[n])return!1;return!0})(n,r.options))f=r.server,s(2,{...i,message:"updated"}),r.detach();else{if(r?(s(2,{...i,type:"detail",message:"restarting (step 1: shutdown)"}),await r.close(),s(2,{...i,type:"detail",message:"restarting (step 2: start)"})):s(2,{...i,type:"detail",message:"starting"}),this.R)return;f=F(n),f.setTimeout(n.socketTimeout),u=st(f,e,t,n.backlog)}const p=c.attach(f,n);return u&&(await u,await new Promise(e=>setTimeout(e,1)),s(2,{...i,message:"ready"})),{host:t,options:n,server:f,detach:()=>p("restart",n.restartTimeout),close:()=>new Promise(e=>{const t=p("shutdown",n.shutdownTimeout,!0,()=>{f.close(()=>{s(2,{...i,message:"closed"}),e()}),f.closeAllConnections()}).countConnections();t>0&&s(2,{...i,type:"detail",message:"closing",stats:{connections:t}})})}}async H(e){this.T.size&&(e(2,{type:"detail",message:"shutting down"}),await Promise.all([...this.T.values(),...this.A].map(e=>e.close()))),this.P&&e(2,{message:"shutdown complete"})}shutdown(e){this.R||(clearTimeout(this.B),this.B=void 0,this.R=!0,this.C||this.H(e))}}function rt(e,t){let n="";const o=e=>t(e.replaceAll(/\x1b\[[\d;]*[A-K]/g,"").replaceAll(/[\x00-\x1f]/g,e=>`\\x${e.charCodeAt(0).toString(16).padStart(2,"0")}`));e.addListener("data",e=>{n+=e;const t=n.split("\n");n=t.pop();for(const e of t)o(e)}),e.addListener("end",()=>{n&&o(n)})}const st=async(e,t,n,o=511)=>new Promise((r,s)=>{e.once("error",s),e.listen(t,n,o,()=>{e.off("error",s),r()})});function it(e,t){return e.command===t.command&&e.arguments.length===t.arguments.length&&e.arguments.every((e,n)=>e===t.arguments[n])&&e.cwd===t.cwd&&JSON.stringify(e.environment)===JSON.stringify(t.environment)&&e.options.uid===t.options.uid&&e.options.gid===t.options.gid&&e.options.killSignal===t.options.killSignal&&e.options.displayStdout===t.options.displayStdout&&e.options.displayStderr===t.options.displayStderr}const at=["requestTimeout","keepAliveTimeout","keepAliveTimeoutBuffer","connectionsCheckingInterval","headersTimeout","highWaterMark","maxHeaderSize","noDelay","requireHostHeader","keepAlive","keepAliveInitialDelay","uniqueHeaders","backlog","socketTimeout"];function ct(e){return e.reduce((e,t)=>({rawSize:e.rawSize+t.rawSize,bestSize:e.bestSize+t.bestSize,created:e.created+t.created}),{rawSize:0,bestSize:0,created:0})}const ft=(e,t,n=t+"s")=>1===e?`1 ${t}`:`${e} ${n}`,ut=e=>e<2e3?`${e}B`.padStart(8," "):`${(e/1024).toFixed(1)}kB`.padStart(8," "),pt=["none","ready","progress"],lt=(e,t)=>{const n=e.isTTY&&!process.env.NO_COLOR?(e,t)=>e?`\x1b[${e}m${t}\x1b[0m`:t:(e,t)=>t,o=pt.indexOf(t);return(t,r)=>{if(t>o)return;const s=[];if(r.service&&(r.serviceCol?s.push(n(r.serviceCol,r.service)):s.push(r.service)),r.thread&&s.push(n("2",`[${r.thread}]`)),"warn"===r.type?s.push(n("33","warning")+":"):"error"===r.type&&s.push(n("91","error")+":"),void 0!==r.method&&s.push(n("1",r.method.replaceAll(/[^a-zA-Z0-9\-_]/g,"?")||"?")),void 0!==r.path&&s.push(r.path),void 0!==r.status&&s.push(n(dt[r.status/100|0]??"",String(r.status))),r.message){const e=r.message instanceof Error?r.message.message:String(r.message);"detail"===r.type?s.push(n("2",e)):s.push(e)}r.stats&&s.push(n("2",`(${Object.entries(r.stats).map(([e,t])=>"duration"===e?`${t}ms`:`${e}=${t}`).join("; ")})`)),e.write(`${s.join(" ")}\n`)}},dt=["","37","32","36","31","41;97"];process.on("SIGUSR1",()=>{});const wt=process.stderr;let mt=lt(wt,"progress");function ht(e){process.stdin.destroy(),mt(0,{type:"error",message:e})}process.on("unhandledRejection",ht),process.on("uncaughtException",ht);const gt=(e=>{const t=[];for(let n=0;n<e.length;++n){const o=e[n];if("--"===o)continue;const r=/^--([^ =\-][^ =]*)=(.*)$/.exec(o);if(r){t.push([r[1],r[2]]);continue}const s=/^-([^ =]*)([^ =])=(.*)$/.exec(o);if(s&&"-"!==o[1]){for(const e of s[1])t.push(["-"+e,""]);t.push(["-"+s[2],s[3]]);continue}if("-"!==o[0]||"-"===o){if(0!==n)throw new Error(`value without key: ${o}`);t.push(["",o]);continue}let i=e[n+1];if(i&&"-"===i[0]&&i.length>1&&(i=void 0),void 0!==i&&++n,"-"===o[1])t.push([o.slice(2),i??""]);else{for(const e of o.slice(1,o.length-1))t.push(["-"+e,""]);t.push(["-"+o[o.length-1],i??""])}}const n=new Map;for(const[e,o]of t){const t=(ie.get(e)??e).toLowerCase(),r=ae.get(t);if(!r)throw new Error(`unknown flag: ${e}`);let s;switch(r.type){case"string":s=o;break;case"number":s=Number.parseFloat(o);break;case"boolean":s=["","on","true","yes","y","1"].includes(o.toLowerCase())}if(r.multi){let e=n.get(t);e||(e=[],n.set(t,e)),e.push(s)}else{if(n.has(t))throw new Error(`multiple values for ${t}`);n.set(t,s)}}return n})(process.argv.slice(2)),yt=f(w(import.meta.url));if(gt.get("version")||gt.get("help")){let t={name:"web-listener",version:"unknown"};try{t=JSON.parse(await e(a(yt,"package.json"),"utf-8"))}catch{}gt.get("help")?d("man",["-M",yt,t.name],{stdio:["inherit","inherit","inherit"]}):process.stdout.write(`${t.name} ${t.version}\n`),process.exit(0)}(async()=>{const t=new ot;process.on("unhandledRejection",()=>t.shutdown(mt)),process.on("uncaughtException",()=>t.shutdown(mt));const n=function(e){const t=n=>{const o=n.$ref;if(o&&o.startsWith("#/$defs/")){const t=Oe(e.$defs,o.substring(8));if(!t)throw new Error(`unable to find ${o} in schema`);n={...t,...n}}const r=((e,t)=>{if(void 0!==e.const)return we(e.const);if(e.enum)return me(new Set(e.enum));if(e.anyOf)return he(e.anyOf.map(t));switch(e.type){case"null":return ge;case"array":return ye(t(e.items));case"boolean":return be;case"integer":return $e(!0,e.minimum,e.maximum);case"number":return $e(!1,e.minimum,e.maximum);case"object":const n=e.additionalProperties??!0,o=xe(new Map(Object.entries(e.properties??{}).map(([e,n])=>[e,t(n)])),"object"==typeof n?t(n):n?ke:Se,e.required??[]);if(e.$comment?.startsWith("flatten:")){const t=e.$comment.substring(8);return(e,n)=>o(e,n)[t]}return o;case"string":if("regex"===e.format)return ve();let r=null;return e.pattern&&(r=new RegExp(e.pattern)),je(r,e.format??"");default:throw new Error(`unknown part type ${JSON.stringify(e)}`)}})(n,t);if(void 0!==n.default){const e=JSON.stringify(n.default);return(t,n)=>r(void 0===t?JSON.parse(e):t,n)}return(e,t)=>void 0===e?void 0:r(e,t)};return t(e)}(await(async()=>JSON.parse(await e(a(f(w(import.meta.url)),"schema.json"),"utf-8")))());function o(){process.stdin.destroy(),t.shutdown(mt)}async function r(){se();const e=await(async(e,t)=>{const n=(e,n=[])=>t.get(e)??n,o=e=>t.get(e),r=e=>t.get(e),s=o("config-file"),i=o("config-json"),a=r("port"),c=o("host"),f=n("dir"),u=n("exec").map(e=>e.split(" ")),p=n("ext").map(e=>e.startsWith(".")?e:`.${e}`),l=n("header").map(e=>(e=>{const t=e.match(/: ?/);return t?[e.substring(0,t.index),e.substring(t.index+t[0].length)]:[e]})(e)),d=o("404"),w=o("spa"),m=o("proxy"),h=o("dependencies"),g=r("min-compress"),y=n("mime"),b=n("mime-types"),$=n("redirect-map"),v=o("log"),j=o("log-format");if(Number(Boolean(s))+Number(Boolean(i))+Number(Boolean(m))>1)throw new Error("multiple config files are not supported");let x;s?x=await fe(null,s,e):i?x=await fe(i,"",e):(f.length||f.push("."),x=e({servers:[{port:8080,mount:[]}]},{file:"",path:""}));for(const e of u)x.backgroundTasks.push({command:e[0],arguments:e.slice(1),cwd:process.cwd(),environment:{},options:{killSignal:"SIGTERM",displayStdout:!0,displayStderr:!0}});const k=1===x.servers.length?x.servers[0]:void 0;if(void 0!==a){if((0|a)!==a)throw new Error("port must be an integer");if(!k)throw new Error("cannot specify port on commandline when defining multiple servers");k.port=a}if(void 0!==c)for(const e of x.servers)e.host=c;if(f.length||w||d){if(!k)throw new Error("cannot specify dir, spa, or 404 on commandline when defining multiple servers");let e;w?e={statusCode:200,filePath:w}:d&&(e={statusCode:404,filePath:d});for(let t=0;t<f.length;++t){const n=f[t],o=t===f.length-1;k.mount.push({type:"files",path:"/",dir:n,options:{fallback:o?e:void 0}})}}if(m){if(!k)throw new Error("cannot specify proxy on commandline when defining multiple servers");k.mount.push({type:"proxy",path:"/",target:m,options:{}})}if($.length>0)for(const e of x.servers)for(const t of $)e.mount.unshift({type:"redirect-map",mapping:t,status:307,options:{caseSensitive:!1}});if(void 0!==h)for(const e of x.servers)e.mount.push({type:"dependencies",path:"/node_modules",package:h,options:{}});const S=(e,t)=>ce(x.servers,n=>{if("files"===n.type){n.options.negotiation??=[];let o=n.options.negotiation.find(t=>t.feature===e);o||(o={feature:e,options:[]},n.options.negotiation=[...n.options.negotiation,o]),o.options.find(e=>e.value===t.value)||o.options.push(t)}});for(const[e,n]of le)t.get(e)&&S("encoding",n);if(p.length&&ce(x.servers,e=>{"files"===e.type&&(e.options.implicitSuffixes=p)}),t.get("no-cache")&&l.push(["cache-control","no-store, no-cache, max-age=0"]),l.length){const e={type:"headers",path:"/",headers:{}};ce(x.servers,e=>{switch(e.type){case"headers":case"fixture":e.headers=pe(e.headers,l);break;case"files":case"proxy":case"dependencies":e.options.headers=pe(e.options.headers,l)}});for(const t of x.servers){let n=t.mount[0];"headers"===n?.type&&"/"===n.path||(n={...e},t.mount.unshift(n)),n.headers=ue(n.headers,l)}}switch((y.length||b.length)&&(x.mime=[...de(x.mime),...y,...b.map(e=>`file://${e}`)]),t.get("write-compressed")&&(x.writeCompressed=!0),void 0!==g&&(x.minCompress=g),t.get("no-serve")&&(x.noServe=!0),v){case void 0:break;case"none":case"ready":case"progress":x.log=v;for(const e of x.servers)e.options.logRequests=!1;for(const e of x.backgroundTasks)e.options.displayStderr=!1,e.options.displayStdout=!1;break;case"full":x.log="progress";break;default:throw new Error(`unknown log level: ${v}`)}switch(j){case void 0:break;case"text":case"json":x.logFormat=j;break;default:throw new Error(`unknown log format: ${j}`)}return x})(n,gt);mt="json"===e.logFormat?((e,t)=>{const n=pt.indexOf(t);return(t,{serviceCol:o,message:r,...s})=>{t>n||(r instanceof Error?r=r.message:void 0!==r&&(r=String(r)),e.write(`${JSON.stringify({...s,message:r})}\n`))}})(wt,e.log):lt(wt,e.log),await(async e=>{const t=[];for(const n of Y(e)?e:[e])"string"!=typeof n?t.push(new Map(Object.entries(n))):n.startsWith("file://")?t.push(H(await oe(n.substring(7)))):t.push(U(n));D();for(const e of t)G(e)})(e.mime),e.writeCompressed&&await(async(e,t,n)=>{let o=0;for(const r of e)for(const e of r.mount)if("files"===e.type){const r=e.options.negotiation?.find(e=>"encoding"===e.feature);if(!r?.options?.length){n(2,{message:`skipping ${e.dir} because no compression is configured`});continue}const s=r.match?` matching ${r.match}`:"";n(2,{message:`compressing files in ${e.dir}${s} using ${r.options.map(e=>e.value).join(", ")}`});const i=I(r.match,!0),a=await q(e.dir,r.options,{...e.options,minCompression:t,filter:(e,t)=>!["image","video","audio","font"].includes(t.split("/")[0])&&i(p(e))}),c=ct(a.filter(({mime:e})=>e.startsWith("text/"))),f=ct(a.filter(({mime:e})=>!e.startsWith("text/")));n(2,{message:`text: ${ut(c.rawSize)} / ${ut(c.bestSize)} compressed`}),n(2,{message:`other: ${ut(f.rawSize)} / ${ut(f.bestSize)} compressed`}),o+=c.created+f.created}n(2,{message:`${ft(o,"compressed file")} written`})})(e.servers,e.minCompress,mt),e.noServe?o():t.set(e.servers,e.backgroundTasks,mt,e=>{if(e instanceof AggregateError)for(const t of e.errors)mt(0,{type:"error",message:t});else mt(0,{type:"error",message:e});process.stdin.destroy(),process.exit(1)})}function s(){return mt(2,{message:"refreshing config"}),r()}r(),process.on("SIGHUP",()=>s()),process.stdin.on("data",e=>{e.includes("\n")&&s()});let i=!1;process.on("SIGINT",()=>{i||(i=!0,wt.isTTY&&wt.write("\n"),o())})})();
package/schema.json CHANGED
@@ -1 +1 @@
1
- {"$schema":"https://json-schema.org/draft-07/schema#","title":"Configuration for running one or more servers via the web-listener CLI","type":"object","additionalProperties":false,"required":["servers"],"properties":{"$schema":{"type":"string"},"servers":{"type":"array","items":{"anyOf":[{"$ref":"#/$defs/server"},{"$ref":"#/$defs/server-ref"}]}},"backgroundTasks":{"type":"array","items":{"$ref":"#/$defs/task"},"default":[]},"mime":{"anyOf":[{"type":"array","items":{"$ref":"#/$defs/mime"}},{"$ref":"#/$defs/mime"}],"default":[]},"writeCompressed":{"title":"Generate and save zstd, brotli, gzip, or deflate files for each served file before starting","type":"boolean","default":false},"minCompress":{"title":"The minimum number of bytes that compression must save, otherwise the file is not written","type":"integer","default":300},"noServe":{"title":"Stop after validating the configuration and optionally writing any compressed files","type":"boolean","default":false},"log":{"title":"Set the logging level. \"ready\" prints only the \"all servers ready\" line, \"progress\" prints server startup and shutdown progress.","enum":["none","ready","progress"],"default":"progress"}},"$defs":{"server":{"title":"Configuration for one server","type":"object","additionalProperties":false,"defaultSnippets":[{"label":"server","description":"Defines a server on a given port","body":{"port":"^${1:80}","mount":[]}}],"required":["port","mount"],"properties":{"port":{"type":"integer","minimum":1,"maximum":65535},"host":{"type":"string","default":"localhost"},"options":{"type":"object","default":{},"additionalProperties":false,"properties":{"requestTimeout":{"type":"integer"},"keepAliveTimeout":{"type":"integer"},"keepAliveTimeoutBuffer":{"type":"integer"},"connectionsCheckingInterval":{"type":"integer"},"headersTimeout":{"type":"integer"},"highWaterMark":{"type":"integer"},"maxHeaderSize":{"type":"integer"},"noDelay":{"type":"boolean"},"requireHostHeader":{"type":"boolean"},"keepAlive":{"type":"boolean"},"keepAliveInitialDelay":{"type":"integer"},"uniqueHeaders":{"type":"array","items":{"type":"string"}},"backlog":{"type":"integer","default":511},"socketTimeout":{"type":"integer"},"rejectNonStandardExpect":{"type":"boolean","default":false},"autoContinue":{"type":"boolean","default":false},"logRequests":{"type":"boolean","default":true},"restartTimeout":{"type":"integer","default":2000},"shutdownTimeout":{"type":"integer","default":500}}},"mount":{"type":"array","items":{"title":"Service to mount on the server","defaultSnippets":[{"label":"files","body":{"type":"files","path":"${1:/}","dir":"${2:.}"}},{"label":"proxy","body":{"type":"proxy","path":"${1:/}","target":"${2:http://localhost:8080}"}},{"label":"fixture","body":{"type":"fixture","method":"${1:GET}","path":"${2:/}","status":"^${3:200}","body":"$4"}},{"label":"redirect","body":{"type":"redirect","path":"$1","target":"$2","status":"^${3:307}"}},{"label":"redirect-map","body":{"type":"redirect-map","mapping":"^${1:{}}","status":"^${2:307}","options":{"caseSensitive":"^${3:false}"}}},{"label":"dependencies","body":{"type":"dependencies","path":"${1:/node_modules}","package":"${2:./package.json}"}},{"label":"delegate","body":{"type":"delegate","path":"${1:/}","config":{"file":"$2"}}},{"label":"custom","body":{"type":"custom","path":"${1:/}","method":"^${2:null}","import":"$3"}}],"anyOf":[{"$ref":"#/$defs/mount-delegate"},{"$ref":"#/$defs/mount-headers"},{"$ref":"#/$defs/mount-files"},{"$ref":"#/$defs/mount-proxy"},{"$ref":"#/$defs/mount-fixture"},{"$ref":"#/$defs/mount-redirect"},{"$ref":"#/$defs/mount-redirect-map"},{"$ref":"#/$defs/mount-dependencies"},{"$ref":"#/$defs/mount-custom"}]}}}},"mount-delegate":{"type":"object","additionalProperties":false,"required":["type","config"],"properties":{"type":{"const":"delegate"},"path":{"type":"string","default":"/"},"config":{"$ref":"#/$defs/server-ref"}}},"mount-headers":{"type":"object","additionalProperties":false,"required":["type","headers"],"properties":{"type":{"const":"headers"},"path":{"type":"string","default":"/"},"headers":{"$ref":"#/$defs/headers"}}},"mount-files":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"const":"files"},"path":{"type":"string","default":"/"},"dir":{"type":"string","default":".","format":"uri-reference"},"options":{"type":"object","default":{},"additionalProperties":false,"properties":{"mode":{"enum":["dynamic","static-paths"],"default":"dynamic"},"fallback":{"type":"object","additionalProperties":false,"required":["filePath"],"properties":{"statusCode":{"$ref":"#/$defs/status","default":200},"filePath":{"type":"string"}}},"headers":{"$ref":"#/$defs/headers"},"dynamicHeaders":{"type":"array","items":{"enum":["etag","last-modified"]},"default":["etag","last-modified"]},"verbose":{"type":"boolean","default":false},"subDirectories":{"anyOf":[{"type":"boolean"},{"type":"integer","minimum":0}],"default":true},"caseSensitive":{"enum":["exact","filesystem","force-lowercase"],"default":"exact"},"allowAllDotfiles":{"type":"boolean","default":false},"allowAllTildefiles":{"type":"boolean","default":false},"allowDirectIndexAccess":{"type":"boolean","default":false},"hide":{"type":"array","items":{"$ref":"#/$defs/stringOrRegex"},"default":[]},"allow":{"type":"array","items":{"type":"string"},"default":[".well-known"]},"indexFiles":{"type":"array","items":{"type":"string"},"default":["index.htm","index.html"]},"implicitSuffixes":{"type":"array","items":{"type":"string"},"default":[]},"negotiation":{"type":"array","default":[],"items":{"type":"object","additionalProperties":false,"required":["feature","options"],"properties":{"feature":{"enum":["type","language","encoding"]},"match":{"$ref":"#/$defs/stringOrRegex"},"options":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["value","file"],"properties":{"value":{"type":"string"},"for":{"type":"string","format":"regex"},"file":{"type":"string"}}}}}}}}}}},"mount-proxy":{"type":"object","additionalProperties":false,"required":["type","target"],"properties":{"type":{"const":"proxy"},"path":{"type":"string","default":"/"},"target":{"type":"string"},"options":{"type":"object","default":{},"additionalProperties":false,"properties":{"noDelay":{"type":"boolean"},"keepAlive":{"type":"boolean"},"keepAliveInitialDelay":{"type":"integer"},"keepAliveMsecs":{"type":"integer"},"agentKeepAliveTimeoutBuffer":{"type":"integer"},"maxSockets":{"type":"integer"},"maxFreeSockets":{"type":"integer"},"timeout":{"type":"integer"},"headers":{"$ref":"#/$defs/headers"},"blockRequestHeaders":{"type":"array","items":{"type":"string"}},"blockResponseHeaders":{"type":"array","items":{"type":"string"}},"servername":{"type":"string"},"ca":{"type":"string"},"cert":{"type":"string"},"sigalgs":{"type":"string"},"ciphers":{"type":"string"},"crl":{"type":"string"},"ecdhCurve":{"type":"string"},"key":{"type":"string"},"passphrase":{"type":"string"},"pfx":{"type":"string"},"sessionTimeout":{"type":"integer"},"maxCachedSessions":{"type":"integer"}}}}},"mount-fixture":{"type":"object","additionalProperties":false,"required":["type","path","body"],"properties":{"type":{"const":"fixture"},"path":{"type":"string"},"method":{"type":"string","default":"GET"},"status":{"$ref":"#/$defs/status","default":200},"headers":{"$ref":"#/$defs/headers"},"body":{"type":"string"}}},"mount-redirect":{"type":"object","additionalProperties":false,"required":["type","path","target"],"properties":{"type":{"const":"redirect"},"path":{"type":"string"},"status":{"$ref":"#/$defs/status","default":307},"target":{"type":"string"}}},"mount-redirect-map":{"type":"object","additionalProperties":false,"required":["type","mapping"],"properties":{"type":{"const":"redirect-map"},"mapping":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"string","format":"uri-reference"}]},"status":{"$ref":"#/$defs/status","default":307},"options":{"type":"object","default":{},"additionalProperties":false,"properties":{"caseSensitive":{"type":"boolean","default":false}}}}},"mount-dependencies":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"const":"dependencies"},"path":{"type":"string","default":"/node_modules"},"package":{"type":"string","default":"./package.json","format":"uri-reference"},"options":{"type":"object","default":{},"additionalProperties":false,"properties":{"environment":{"type":"array","items":{"type":"string"},"default":["browser","import","production"]},"mapFilePath":{"type":"string","default":"/importmap.json"},"sourcesBasePath":{"type":"string","default":"/"},"modulesBasePath":{"type":"string","default":"/node_modules"},"mode":{"enum":["dynamic","static-paths"],"default":"dynamic"},"headers":{"$ref":"#/$defs/headers"},"dynamicHeaders":{"type":"array","items":{"enum":["etag","last-modified"]},"default":["etag","last-modified"]},"verbose":{"type":"boolean","default":false},"subDirectories":{"anyOf":[{"type":"boolean"},{"type":"integer"}],"default":true},"caseSensitive":{"enum":["exact","filesystem","force-lowercase"],"default":"exact"},"allowAllDotfiles":{"type":"boolean","default":false},"allowAllTildefiles":{"type":"boolean","default":false},"hide":{"type":"array","default":[],"items":{"$ref":"#/$defs/stringOrRegex"}},"allow":{"type":"array","items":{"type":"string"},"default":[".well-known"]}}}}},"mount-custom":{"type":"object","additionalProperties":false,"required":["type","import"],"properties":{"type":{"const":"custom"},"path":{"type":"string","default":"/"},"method":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"null"}],"default":null},"import":{"type":"string","format":"uri-reference"}}},"headers":{"type":"object","default":{},"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"array","items":{"type":"string"}}]}},"server-ref":{"type":"object","additionalProperties":false,"required":["file"],"properties":{"file":{"type":"string","format":"uri-reference"},"serverIndex":{"type":"integer","minimum":0},"serverPort":{"type":"integer","minimum":1,"maximum":65535},"includeMime":{"type":"boolean","default":true},"includeBackgroundTasks":{"type":"boolean","default":false}}},"task":{"title":"Configuration for a background task","type":"object","additionalProperties":false,"defaultSnippets":[{"label":"task","description":"Run an executable","body":{"command":"$1"}}],"required":["command"],"properties":{"command":{"anyOf":[{"type":"string","pattern":"[\\\\/]","format":"uri-reference"},{"type":"string","pattern":"^[^\\\\/]*$"}]},"arguments":{"type":"array","items":{"type":"string"},"default":[]},"cwd":{"type":"string","default":".","format":"uri-reference"},"environment":{"type":"object","additionalProperties":{"type":"string"},"default":{}},"options":{"type":"object","additionalProperties":false,"properties":{"uid":{"type":"integer","minimum":0},"gid":{"type":"integer","minimum":0},"killSignal":{"anyOf":[{"enum":["SIGABRT","SIGALRM","SIGBUS","SIGCHLD","SIGCONT","SIGFPE","SIGHUP","SIGILL","SIGINT","SIGIO","SIGIOT","SIGKILL","SIGPIPE","SIGPOLL","SIGPROF","SIGPWR","SIGQUIT","SIGSEGV","SIGSTKFLT","SIGSTOP","SIGSYS","SIGTERM","SIGTRAP","SIGTSTP","SIGTTIN","SIGTTOU","SIGUNUSED","SIGURG","SIGUSR1","SIGUSR2","SIGVTALRM","SIGWINCH","SIGXCPU","SIGXFSZ","SIGBREAK","SIGLOST","SIGINFO"]},{"type":"integer","minimum":1}],"default":"SIGTERM"},"displayStdout":{"type":"boolean","default":true},"displayStderr":{"type":"boolean","default":true}},"default":{}}}},"mime":{"anyOf":[{"type":"string","pattern":"^file://","format":"uri-reference"},{"type":"string","pattern":"^([^=;]+=[^/;]+/[^;]+(;|$))+$"},{"type":"object","additionalProperties":{"type":"string","pattern":"/"}}]},"stringOrRegex":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":false,"required":["pattern"],"properties":{"pattern":{"type":"string","format":"regex"}},"$comment":"flatten:pattern"}]},"status":{"type":"integer","minimum":200,"maximum":599}}}
1
+ {"$schema":"https://json-schema.org/draft-07/schema#","title":"Configuration for running one or more servers via the web-listener CLI","type":"object","additionalProperties":false,"required":["servers"],"properties":{"$schema":{"type":"string"},"servers":{"type":"array","items":{"anyOf":[{"$ref":"#/$defs/server"},{"$ref":"#/$defs/server-ref"}]}},"backgroundTasks":{"type":"array","items":{"$ref":"#/$defs/task"},"default":[]},"mime":{"anyOf":[{"type":"array","items":{"$ref":"#/$defs/mime"}},{"$ref":"#/$defs/mime"}],"default":[]},"writeCompressed":{"title":"Generate and save zstd, brotli, gzip, or deflate files for each served file before starting","type":"boolean","default":false},"minCompress":{"title":"The minimum number of bytes that compression must save, otherwise the file is not written","type":"integer","default":300},"noServe":{"title":"Stop after validating the configuration and optionally writing any compressed files","type":"boolean","default":false},"log":{"title":"Set the logging level. \"ready\" prints only the \"all servers ready\" line, \"progress\" prints server startup and shutdown progress.","enum":["none","ready","progress"],"default":"progress"},"logFormat":{"title":"Set the logging format. \"text\" is the most human-readable, \"json\" prints each log message as a machine-readable JSON document (one per line).","enum":["text","json"],"default":"text"}},"$defs":{"server":{"title":"Configuration for one server","type":"object","additionalProperties":false,"defaultSnippets":[{"label":"server","description":"Defines a server on a given port","body":{"port":"^${1:80}","mount":[]}}],"required":["port","mount"],"properties":{"port":{"type":"integer","minimum":1,"maximum":65535},"host":{"type":"string","default":"localhost"},"options":{"type":"object","default":{},"additionalProperties":false,"properties":{"requestTimeout":{"type":"integer"},"keepAliveTimeout":{"type":"integer"},"keepAliveTimeoutBuffer":{"type":"integer"},"connectionsCheckingInterval":{"type":"integer"},"headersTimeout":{"type":"integer"},"highWaterMark":{"type":"integer"},"maxHeaderSize":{"type":"integer"},"noDelay":{"type":"boolean"},"requireHostHeader":{"type":"boolean"},"keepAlive":{"type":"boolean"},"keepAliveInitialDelay":{"type":"integer"},"uniqueHeaders":{"type":"array","items":{"type":"string"}},"backlog":{"type":"integer","default":511},"socketTimeout":{"type":"integer"},"rejectNonStandardExpect":{"type":"boolean","default":false},"autoContinue":{"type":"boolean","default":false},"logRequests":{"type":"boolean","default":true},"restartTimeout":{"type":"integer","default":2000},"shutdownTimeout":{"type":"integer","default":500}}},"mount":{"type":"array","items":{"title":"Service to mount on the server","defaultSnippets":[{"label":"files","body":{"type":"files","path":"${1:/}","dir":"${2:.}"}},{"label":"proxy","body":{"type":"proxy","path":"${1:/}","target":"${2:http://localhost:8080}"}},{"label":"fixture","body":{"type":"fixture","method":"${1:GET}","path":"${2:/}","status":"^${3:200}","body":"$4"}},{"label":"redirect","body":{"type":"redirect","path":"$1","target":"$2","status":"^${3:307}"}},{"label":"redirect-map","body":{"type":"redirect-map","mapping":"^${1:{}}","status":"^${2:307}","options":{"caseSensitive":"^${3:false}"}}},{"label":"dependencies","body":{"type":"dependencies","path":"${1:/node_modules}","package":"${2:./package.json}"}},{"label":"delegate","body":{"type":"delegate","path":"${1:/}","config":{"file":"$2"}}},{"label":"custom","body":{"type":"custom","path":"${1:/}","method":"^${2:null}","import":"$3","namedExport":"^${4:null}"}}],"anyOf":[{"$ref":"#/$defs/mount-delegate"},{"$ref":"#/$defs/mount-headers"},{"$ref":"#/$defs/mount-files"},{"$ref":"#/$defs/mount-proxy"},{"$ref":"#/$defs/mount-fixture"},{"$ref":"#/$defs/mount-redirect"},{"$ref":"#/$defs/mount-redirect-map"},{"$ref":"#/$defs/mount-dependencies"},{"$ref":"#/$defs/mount-custom"}]}}}},"mount-delegate":{"type":"object","additionalProperties":false,"required":["type","config"],"properties":{"type":{"const":"delegate"},"path":{"type":"string","default":"/"},"config":{"$ref":"#/$defs/server-ref"},"maskSubpaths":{"type":"boolean","default":false}}},"mount-headers":{"type":"object","additionalProperties":false,"required":["type","headers"],"properties":{"type":{"const":"headers"},"path":{"type":"string","default":"/"},"headers":{"$ref":"#/$defs/headers"}}},"mount-files":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"const":"files"},"path":{"type":"string","default":"/"},"dir":{"type":"string","default":".","format":"uri-reference"},"options":{"type":"object","default":{},"additionalProperties":false,"properties":{"mode":{"enum":["dynamic","static-paths"],"default":"dynamic"},"fallback":{"type":"object","additionalProperties":false,"required":["filePath"],"properties":{"statusCode":{"$ref":"#/$defs/status","default":200},"filePath":{"type":"string"}}},"headers":{"$ref":"#/$defs/headers"},"dynamicHeaders":{"type":"array","items":{"enum":["etag","last-modified"]},"default":["etag","last-modified"]},"verbose":{"type":"boolean","default":false},"subDirectories":{"anyOf":[{"type":"boolean"},{"type":"integer","minimum":0}],"default":true},"caseSensitive":{"enum":["exact","filesystem","force-lowercase"],"default":"exact"},"allowAllDotfiles":{"type":"boolean","default":false},"allowAllTildefiles":{"type":"boolean","default":false},"allowDirectIndexAccess":{"type":"boolean","default":false},"hide":{"type":"array","items":{"$ref":"#/$defs/stringOrRegex"},"default":[]},"allow":{"type":"array","items":{"type":"string"},"default":[".well-known"]},"indexFiles":{"type":"array","items":{"type":"string"},"default":["index.htm","index.html"]},"implicitSuffixes":{"type":"array","items":{"type":"string"},"default":[]},"negotiation":{"type":"array","default":[],"items":{"type":"object","additionalProperties":false,"required":["feature","options"],"properties":{"feature":{"enum":["type","language","encoding"]},"match":{"$ref":"#/$defs/stringOrRegex"},"options":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["value","file"],"properties":{"value":{"type":"string"},"for":{"type":"string","format":"regex"},"file":{"type":"string"}}}}}}}}}}},"mount-proxy":{"type":"object","additionalProperties":false,"required":["type","target"],"properties":{"type":{"const":"proxy"},"path":{"type":"string","default":"/"},"target":{"type":"string"},"options":{"type":"object","default":{},"additionalProperties":false,"properties":{"noDelay":{"type":"boolean"},"keepAlive":{"type":"boolean"},"keepAliveInitialDelay":{"type":"integer"},"keepAliveMsecs":{"type":"integer"},"agentKeepAliveTimeoutBuffer":{"type":"integer"},"maxSockets":{"type":"integer"},"maxFreeSockets":{"type":"integer"},"timeout":{"type":"integer"},"headers":{"$ref":"#/$defs/headers"},"blockRequestHeaders":{"type":"array","items":{"type":"string"}},"blockResponseHeaders":{"type":"array","items":{"type":"string"}},"servername":{"type":"string"},"ca":{"type":"string"},"cert":{"type":"string"},"sigalgs":{"type":"string"},"ciphers":{"type":"string"},"crl":{"type":"string"},"ecdhCurve":{"type":"string"},"key":{"type":"string"},"passphrase":{"type":"string"},"pfx":{"type":"string"},"sessionTimeout":{"type":"integer"},"maxCachedSessions":{"type":"integer"}}}}},"mount-fixture":{"type":"object","additionalProperties":false,"required":["type","path","body"],"properties":{"type":{"const":"fixture"},"path":{"type":"string"},"method":{"type":"string","default":"GET"},"status":{"$ref":"#/$defs/status","default":200},"headers":{"$ref":"#/$defs/headers"},"body":{"type":"string"}}},"mount-redirect":{"type":"object","additionalProperties":false,"required":["type","path","target"],"properties":{"type":{"const":"redirect"},"path":{"type":"string"},"status":{"$ref":"#/$defs/status","default":307},"target":{"type":"string"}}},"mount-redirect-map":{"type":"object","additionalProperties":false,"required":["type","mapping"],"properties":{"type":{"const":"redirect-map"},"mapping":{"anyOf":[{"type":"object","additionalProperties":{"type":"string"}},{"type":"string","format":"uri-reference"}]},"status":{"$ref":"#/$defs/status","default":307},"options":{"type":"object","default":{},"additionalProperties":false,"properties":{"caseSensitive":{"type":"boolean","default":false}}}}},"mount-dependencies":{"type":"object","additionalProperties":false,"required":["type"],"properties":{"type":{"const":"dependencies"},"path":{"type":"string","default":"/node_modules"},"package":{"type":"string","default":"./package.json","format":"uri-reference"},"options":{"type":"object","default":{},"additionalProperties":false,"properties":{"environment":{"type":"array","items":{"type":"string"},"default":["browser","import","production"]},"mapFilePath":{"type":"string","default":"/importmap.json"},"sourcesBasePath":{"type":"string","default":"/"},"modulesBasePath":{"type":"string","default":"/node_modules"},"mode":{"enum":["dynamic","static-paths"],"default":"dynamic"},"headers":{"$ref":"#/$defs/headers"},"dynamicHeaders":{"type":"array","items":{"enum":["etag","last-modified"]},"default":["etag","last-modified"]},"verbose":{"type":"boolean","default":false},"subDirectories":{"anyOf":[{"type":"boolean"},{"type":"integer"}],"default":true},"caseSensitive":{"enum":["exact","filesystem","force-lowercase"],"default":"exact"},"allowAllDotfiles":{"type":"boolean","default":false},"allowAllTildefiles":{"type":"boolean","default":false},"hide":{"type":"array","default":[],"items":{"$ref":"#/$defs/stringOrRegex"}},"allow":{"type":"array","items":{"type":"string"},"default":[".well-known"]}}}}},"mount-custom":{"type":"object","additionalProperties":false,"required":["type","import"],"properties":{"type":{"const":"custom"},"path":{"type":"string","default":"/"},"method":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}},{"type":"null"}],"default":null},"import":{"type":"string","format":"uri-reference"},"namedExport":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null},"maskSubpaths":{"type":"boolean","default":false}}},"headers":{"type":"object","default":{},"additionalProperties":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"array","items":{"type":"string"}}]}},"server-ref":{"type":"object","additionalProperties":false,"required":["file"],"properties":{"file":{"type":"string","format":"uri-reference"},"serverIndex":{"type":"integer","minimum":0},"serverPort":{"type":"integer","minimum":1,"maximum":65535},"includeMime":{"type":"boolean","default":true},"includeBackgroundTasks":{"type":"boolean","default":false}}},"task":{"title":"Configuration for a background task","type":"object","additionalProperties":false,"defaultSnippets":[{"label":"task","description":"Run an executable","body":{"command":"$1"}}],"required":["command"],"properties":{"command":{"anyOf":[{"type":"string","pattern":"[\\\\/]","format":"uri-reference"},{"type":"string","pattern":"^[^\\\\/]*$"}]},"arguments":{"type":"array","items":{"type":"string"},"default":[]},"cwd":{"type":"string","default":".","format":"uri-reference"},"environment":{"type":"object","additionalProperties":{"type":"string"},"default":{}},"options":{"type":"object","additionalProperties":false,"properties":{"uid":{"type":"integer","minimum":0},"gid":{"type":"integer","minimum":0},"killSignal":{"anyOf":[{"enum":["SIGABRT","SIGALRM","SIGBUS","SIGCHLD","SIGCONT","SIGFPE","SIGHUP","SIGILL","SIGINT","SIGIO","SIGIOT","SIGKILL","SIGPIPE","SIGPOLL","SIGPROF","SIGPWR","SIGQUIT","SIGSEGV","SIGSTKFLT","SIGSTOP","SIGSYS","SIGTERM","SIGTRAP","SIGTSTP","SIGTTIN","SIGTTOU","SIGUNUSED","SIGURG","SIGUSR1","SIGUSR2","SIGVTALRM","SIGWINCH","SIGXCPU","SIGXFSZ","SIGBREAK","SIGLOST","SIGINFO"]},{"type":"integer","minimum":1}],"default":"SIGTERM"},"displayStdout":{"type":"boolean","default":true},"displayStderr":{"type":"boolean","default":true}},"default":{}}}},"mime":{"anyOf":[{"type":"string","pattern":"^file://","format":"uri-reference"},{"type":"string","pattern":"^([^=;]+=[^/;]+/[^;]+(;|$))+$"},{"type":"object","additionalProperties":{"type":"string","pattern":"/"}}]},"stringOrRegex":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":false,"required":["pattern"],"properties":{"pattern":{"type":"string","format":"regex"}},"$comment":"flatten:pattern"}]},"status":{"type":"integer","minimum":200,"maximum":599}}}