@stacksjs/rpx 0.11.38 → 0.11.39
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/dist/bin/cli.js
CHANGED
|
@@ -219,7 +219,7 @@ Write-Host "Root CA trusted successfully!"
|
|
|
219
219
|
`,z=G0(Wj.tmpdir(),"rpx-trust.ps1");if(await B0.writeFile(z,f),Uj(`powershell -ExecutionPolicy Bypass -File "${z}"`),w.verbose)H.success("Successfully added certificate to Windows trust store");Q=!0}catch(f){if(w.verbose)H.warn(`Could not trust certificate: ${f}`)}else try{await GW(j,J.certificate,Y),Q=!0}catch(f){if(w.verbose)H.warn(`Could not add certificate to trust store: ${f}`)}if(M$={key:j.privateKey,cert:j.certificate,ca:J.certificate},w.verbose)H.success(`Certificate generated successfully for ${$.length} domain${$.length>1?"s":""}`);if(!Q&&w.verbose)H.warn('If you see certificate warnings in Chrome/Arc, type "thisisunsafe" on the warning page'),H.warn("This will bypass the warning and you should only need to do it once")}function Qj(){M$=null}async function y$(w){if(!w)return null;if(M$)return M$;let $=RY(w);try{let[Y,A,X]=await Promise.all([B0.access($.keyPath).then(()=>!0).catch(()=>!1),B0.access($.certPath).then(()=>!0).catch(()=>!1),$.caCertPath?B0.access($.caCertPath).then(()=>!0).catch(()=>!1):Promise.resolve(!1)]);if(!Y||!A)return E("ssl",`Certificate files don't exist: key=${Y}, cert=${A}, paths: ${$.keyPath}, ${$.certPath}`,w.verbose),null;let J="regenerateUntrustedCerts"in w,U=w.regenerateUntrustedCerts,j=J?U!==!1:!0;E("ssl",`Trust check: hasFlag=${J}, flagValue=${U}, shouldCheckTrust=${j}`,w.verbose);let W=$.basePath||G0(i1(),".stacks","ssl"),Q=j2(W);if(!(j?await z2(Q.caCertPath,w):!0))return E("ssl","Root CA exists but is not trusted, will regenerate",w.verbose),null;let[z,B,G]=await Promise.all([B0.readFile($.keyPath,"utf8"),B0.readFile($.certPath,"utf8"),X&&$.caCertPath?B0.readFile($.caCertPath,"utf8"):Promise.resolve(void 0)]);if(G&&!G.includes("-----BEGIN CERTIFICATE-----"))return E("ssl","Invalid root CA certificate content, will regenerate",w.verbose),null;if(b$(w))try{let{X509Certificate:T}=await import("node:crypto"),F=new T(B).subjectAltName||"",R=w.proxies.map((L)=>L.to).filter((L)=>!F.includes(`DNS:${L}`));if(R.length>0)return E("ssl",`Certificate missing SANs for: ${R.join(", ")}, will regenerate`,w.verbose),null}catch(T){E("ssl",`Could not verify cert SANs: ${T}`,w.verbose)}return E("ssl","Successfully loaded existing certificates",w.verbose),M$={key:z,cert:B,ca:G},M$}catch(Y){return E("ssl",`Error checking existing certificates: ${Y}`,w.verbose),null}}function RY(w,$){let Y=f8(w);E("ssl",`Primary domain: ${Y}`,$);let A=Q2(w),X=G0(i1(),".stacks","ssl");if(typeof w.https==="object"){let J=w.https.basePath&&w.https.basePath.trim()!==""?w.https.basePath:X,U={domain:Y,hostCertCN:Y,basePath:J,caCertPath:w.https.caCertPath||A.caCertPath,certPath:w.https.certPath||A.certPath,keyPath:w.https.keyPath||A.keyPath,altNameIPs:["127.0.0.1","::1"],altNameURIs:[],commonName:w.https.commonName||Y,organizationName:w.https.organizationName||"Local Development",countryName:w.https.countryName||"US",stateName:w.https.stateName||"California",localityName:w.https.localityName||"Playa Vista",validityDays:w.https.validityDays||825,verbose:$||!1,subjectAltNames:Array.from(X2(w)).map((j)=>({type:2,value:j}))};if(oW(w.https.rootCA))U.rootCA=w.https.rootCA;return U}return{domain:Y,hostCertCN:Y,basePath:X,...A,altNameIPs:["127.0.0.1","::1"],altNameURIs:[],commonName:Y,organizationName:"Local Development",countryName:"US",stateName:"California",localityName:"Playa Vista",validityDays:825,verbose:$||!1,subjectAltNames:Array.from(X2(w)).map((J)=>({type:2,value:J}))}}async function f2(w,$){let Y=Q2({to:w,verbose:$}),A=[Y.caCertPath,Y.certPath,Y.keyPath];E("certificates","Attempting to clean up relating certificates",$),await Promise.all(A.map((X)=>wj(X,$)))}async function z2(w,$){try{if(E("ssl",`Checking if certificate is trusted: ${w}`,$?.verbose),R$.platform==="darwin"){if($?.serverName)return Xj(w,$.serverName,{verbose:$?.verbose});return E8(w,{verbose:$?.verbose})}else if(R$.platform==="win32")try{let Y=EW(w)||"";if(!Y)return E("ssl","Could not extract certificate subject",$?.verbose),!1;let A=`powershell -Command "Get-ChildItem -Path Cert:\\LocalMachine\\Root | Where-Object { $_.Subject -like '*${Y}*' } | Select-Object Subject"`;if(Uj(A).toString().includes(Y))return E("ssl","Certificate found in trusted root store",$?.verbose),!0;return E("ssl","Certificate not found in trusted root store",$?.verbose),!1}catch(Y){return E("ssl",`Error checking certificate trust on Windows: ${Y}`,$?.verbose),!1}else if(R$.platform==="linux")try{let Y=P$(w);if(!Y)return E("ssl","Could not extract certificate fingerprint",$?.verbose),!1;let A=["/etc/ssl/certs","/etc/pki/tls/certs"];for(let X of A){let J;try{J=await B0.readdir(X)}catch{continue}for(let U of J){let j=P$(J2.join(X,U));if(j&&j===Y)return E("ssl",`Certificate fingerprint found in ${X}`,$?.verbose),!0}}return E("ssl","Certificate not found in system trust stores",$?.verbose),!1}catch(Y){return E("ssl",`Error checking certificate trust on Linux: ${Y}`,$?.verbose),!1}return E("ssl",`Platform ${R$.platform} not supported for certificate trust check`,$?.verbose),!1}catch(Y){return E("ssl",`Error checking if certificate is trusted: ${Y}`,$?.verbose),!1}}var M$=null,jj,Sq="rpx-root-ca.crt",hq="rpx-root-ca.key";var fj=a(()=>{_X();x$();PA();Jj();z0();Jj();DX();DX();jj=G0(i1(),".stacks","ssl","rpx.localhost.crt")});function _q(w){return{enabled:w?.enabled??!1,path:w?.path??"/",interval:w?.interval??1e4,timeout:w?.timeout??2000,healthyThreshold:w?.healthyThreshold??2,unhealthyThreshold:w?.unhealthyThreshold??3}}function Dq(w){return(Array.isArray(w)?w:[w]).map((Y)=>{let A=typeof Y==="string"?{url:Y}:Y;return{url:A.url,weight:A.weight&&A.weight>0?A.weight:1,healthy:!0,activeConnections:0,consecutiveFailures:0,consecutiveSuccesses:0}})}function B2(w){return(Array.isArray(w)?w:[w]).map((Y)=>typeof Y==="string"?Y:Y.url)}function u$(w){if(!w)return"localhost:5173";let $=Array.isArray(w)?w[0]:w;if(!$)return"localhost:5173";return typeof $==="string"?$:$.url}function MY(w,$){return{upstreams:Dq(w),strategy:$?.strategy??"round-robin",healthCheck:_q($?.healthCheck),cursor:0,wrrCurrentWeights:[],healthCheckTimer:null}}function xq(w){let $=w.upstreams.filter((Y)=>Y.healthy);if($.length===0&&w.upstreams.length===1)return w.upstreams;return $}function kq(w,$){let Y=w.cursor%$.length;return w.cursor=(w.cursor+1)%$.length,$[Y]}function Zq(w,$){if(w.wrrCurrentWeights.length!==$.length)w.wrrCurrentWeights=$.map(()=>0);let Y=$.reduce((X,J)=>X+J.weight,0),A=0;for(let X=0;X<$.length;X++)if(w.wrrCurrentWeights[X]+=$[X].weight,w.wrrCurrentWeights[X]>w.wrrCurrentWeights[A])A=X;return w.wrrCurrentWeights[A]-=Y,$[A]}function Pq(w){let $=w[0];for(let Y=1;Y<w.length;Y++)if(w[Y].activeConnections<$.activeConnections)$=w[Y];return $}function G2(w){let $=xq(w);if($.length===0)return;if($.length===1)return $[0];switch(w.strategy){case"weighted-round-robin":return Zq(w,$);case"least-connections":return Pq($);case"round-robin":default:return kq(w,$)}}function K8(w,$){if($.consecutiveFailures=0,$.consecutiveSuccesses+=1,!$.healthy&&$.consecutiveSuccesses>=w.healthCheck.healthyThreshold)$.healthy=!0}function yY(w,$){if($.consecutiveSuccesses=0,$.consecutiveFailures+=1,$.healthy&&$.consecutiveFailures>=w.healthCheck.unhealthyThreshold)$.healthy=!1}function iq(w){let $=w.lastIndexOf(":");if($===-1)return{hostname:w,port:80};let Y=Number(w.slice($+1));return{hostname:w.slice(0,$),port:Number.isFinite(Y)?Y:80}}async function bq(w,$){let{hostname:Y,port:A}=iq($.url),X=new AbortController,J=setTimeout(()=>X.abort(),w.healthCheck.timeout);try{(await fetch(`http://${Y}:${A}${w.healthCheck.path}`,{signal:X.signal,redirect:"manual"})).body?.cancel().catch(()=>{}),K8(w,$)}catch{yY(w,$)}finally{clearTimeout(J)}}function IY(w){if(!w.healthCheck.enabled||w.healthCheckTimer||w.upstreams.length<2)return;let $=setInterval(()=>{for(let Y of w.upstreams)bq(w,Y)},w.healthCheck.interval);$.unref?.(),w.healthCheckTimer=$}function b1(w){if(w.healthCheckTimer)clearInterval(w.healthCheckTimer),w.healthCheckTimer=null}import{createHash as K2,timingSafeEqual as E2}from"node:crypto";import{readFileSync as uq}from"node:fs";function CY(w,$){let Y=Buffer.from(w,"utf8"),A=Buffer.from($,"utf8");if(Y.length!==A.length)return E2(Y,Y),!1;return E2(Y,A)}function vq(w,$){let Y=(B)=>K2("md5").update(B).digest(),A=Buffer.from(w,"utf8"),X=Buffer.from($,"utf8"),J=Y(Buffer.concat([A,X,A])),U=[A,Buffer.from("$apr1$","utf8"),X];for(let B=A.length;B>0;B-=16)U.push(J.subarray(0,Math.min(B,16)));for(let B=A.length;B>0;B>>=1)U.push(B&1?Buffer.from([0]):A.subarray(0,1));let j=Y(Buffer.concat(U));for(let B=0;B<1000;B++){let G=[];if(G.push(B&1?A:j.subarray(0,16)),B%3)G.push(X);if(B%7)G.push(A);G.push(B&1?j.subarray(0,16):A),j=Y(Buffer.concat(G))}let W="./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",Q=(B,G)=>{let T="",K=B;for(let F=0;F<G;F++)T+=W[K&63],K>>=6;return T},f=j,z="";return z+=Q(f[0]<<16|f[6]<<8|f[12],4),z+=Q(f[1]<<16|f[7]<<8|f[13],4),z+=Q(f[2]<<16|f[8]<<8|f[14],4),z+=Q(f[3]<<16|f[9]<<8|f[15],4),z+=Q(f[4]<<16|f[10]<<8|f[5],4),z+=Q(f[11],2),`$apr1$${$}$${z}`}function mq(w,$){if(/^\$2[aby]?\$/.test($))try{return Bun.password.verifySync(w,$)}catch{return!1}if($.startsWith("$apr1$")){let A=$.split("$")[2]??"";return CY(vq(w,A),$)}if($.startsWith("{SHA}")){let Y=K2("sha1").update(w,"utf8").digest("base64");return CY(`{SHA}${Y}`,$)}return CY(w,$)}function gq(w){let $=new Map;for(let Y of w.split(`
|
|
220
220
|
`)){let A=Y.trim();if(!A||A.startsWith("#"))continue;let X=A.indexOf(":");if(X<=0)continue;$.set(A.slice(0,X),A.slice(X+1))}return $}function VY(w){if(!w)return;let $=(w.realm??"").trim()||"Restricted",Y=[];if(w.username)Y.push({username:w.username,password:w.password??""});for(let J of w.users??[])if(J?.username)Y.push({username:J.username,password:J.password??""});let A;if(w.htpasswdFile)try{A=gq(uq(w.htpasswdFile,"utf8"))}catch{A=void 0}if(Y.length===0&&(!A||A.size===0))return;return{realm:$,verify:(J,U)=>{let j=!1;for(let W of Y)if(CY(J,W.username)&&CY(U,W.password))j=!0;if(A){let W=A.get(J);if(W&&mq(U,W))j=!0}return j}}}function F2(w,$,Y){if($.startsWith(cq))return;let A=w.headers.get("authorization")??"",X=A.indexOf(" "),J=X===-1?A:A.slice(0,X),U=X===-1?"":A.slice(X+1).trim();if(J.toLowerCase()==="basic"&&U){let W="";try{W=Buffer.from(U,"base64").toString("utf8")}catch{W=""}let Q=W.indexOf(":");if(Q>=0&&Y.verify(W.slice(0,Q),W.slice(Q+1)))return}let j=Y.realm.replace(/["\\]/g,"");return new Response("401 Unauthorized",{status:401,headers:{"www-authenticate":`Basic realm="${j}", charset="UTF-8"`,"content-type":"text/plain; charset=utf-8"}})}var cq="/.well-known/acme-challenge/";var F8=()=>{};function lq(w){return nq.has(w.toUpperCase())}function pq(){let w=Number.parseInt(process.env.RPX_QUEUE_WAIT_MS??"",10);return Number.isFinite(w)&&w>0?w:30000}function rq(w){let $=Number.parseInt(process.env.RPX_MAX_QUEUED??"",10);return Number.isFinite($)&&$>0?$:w*8}function tq(){let w=Number.parseInt(process.env.RPX_CHECKOUT_IDLE_MS??"",10);return Number.isFinite(w)&&w>0?w:0}function aq(){let w=Number.parseInt(process.env.RPX_UPSTREAM_TIMEOUT??"",10);return Number.isFinite(w)&&w>0?w:0}function sq(){let w=Number.parseInt(process.env.RPX_MAX_UPSTREAM_CONNS??"",10);return Number.isFinite(w)&&w>0?w:256}class R2{socket=null;buf=new Uint8Array(oq);len=0;pos=0;closed=!1;fresh=!0;timedOut=!1;idleSince=0;bodyQueue=null;bodyRemaining=0;queuedBytes=0;streamingBody=!1;paused=!1;lastActivityAt=0;waiter=null;drainWaiter=null;async writeAll(w){let $=this.socket,Y=$.write(w);while(Y<w.length){if(this.closed)throw T8;await new Promise((A)=>{this.drainWaiter=A}),Y+=$.write(w.subarray(Y))}}wakeDrain(){let w=this.drainWaiter;if(w)this.drainWaiter=null,w()}push(w){if(this.bodyQueue){let $=w.length<=this.bodyRemaining?w.length:this.bodyRemaining;if($>0)this.bodyQueue.push(w.slice(0,$)),this.queuedBytes+=$;if(this.bodyRemaining-=$,$<w.length)this.appendToBuf(w.subarray($));this.maybePause(),this.lastActivityAt=Date.now(),this.wake();return}this.appendToBuf(w),this.maybePause(),this.lastActivityAt=Date.now(),this.wake()}bufferedBodyBytes(){return this.bodyQueue?this.queuedBytes:this.len-this.pos}maybePause(){if(this.streamingBody&&!this.paused&&this.bufferedBodyBytes()>eq)this.paused=!0,this.socket?.pause?.()}resumeIfDrained(){if(this.lastActivityAt=Date.now(),this.paused&&this.bufferedBodyBytes()<=wH)this.paused=!1,this.socket?.resume?.()}clearStreaming(){if(this.streamingBody=!1,this.queuedBytes=0,this.paused)this.paused=!1,this.socket?.resume?.()}appendToBuf(w){let $=this.len+w.length;if($>this.buf.length){let Y=new Uint8Array(Math.max(this.buf.length*2,$));Y.set(this.buf.subarray(0,this.len)),this.buf=Y}this.buf.set(w,this.len),this.len=$}markClosed(){this.closed=!0,this.wake(),this.wakeDrain()}markTimedOut(){this.timedOut=!0,this.markClosed()}wake(){let w=this.waiter;if(w)this.waiter=null,w()}waitForData(w){if(this.len>w||this.closed)return Promise.resolve();return new Promise(($)=>{this.waiter=$})}waitForBody(){if(this.bodyQueue!==null&&this.bodyQueue.length>0||this.closed)return Promise.resolve();return new Promise((w)=>{this.waiter=w})}compact(){if(this.pos===0)return;if(this.pos===this.len){this.pos=0,this.len=0;return}this.buf.copyWithin(0,this.pos,this.len),this.len-=this.pos,this.pos=0}destroy(){this.closed=!0;try{this.socket?.end()}catch{}this.socket=null}}class M2{host;port;maxTotal;key;idle=[];waiters=[];open=0;sweeper=null;queueWaitMs=pq();maxWaiters;checkoutIdleMs=tq();inUse=new Set;constructor(w,$,Y,A=""){this.host=w;this.port=$;this.maxTotal=Y;this.key=A;this.maxWaiters=rq(Y)}trackCheckout(w){if(w.lastActivityAt=Date.now(),this.checkoutIdleMs>0)this.inUse.add(w),this.ensureSweeper()}dial(){let w=new R2;return dq({hostname:this.host,port:this.port,socket:{data:($,Y)=>w.push(Y),drain:()=>w.wakeDrain(),close:()=>w.markClosed(),end:()=>w.markClosed(),error:()=>w.markClosed(),connectError:()=>w.markClosed(),timeout:()=>w.markTimedOut()}}).then(($)=>{let Y=$;Y.setNoDelay?.(!0);let A=aq();if(A>0)Y.timeout?.(A);return w.socket=$,w})}acquireIdleSync(){while(this.idle.length){let w=this.idle.pop();if(!w.closed)return w.socket?.ref(),w.pos=0,w.len=0,this.trackCheckout(w),w;this.open--}return null}async acquireOrDial(){if(this.open>=this.maxTotal){if(this.waiters.length>=this.maxWaiters)throw c$;let w=await this.waitForSlot();if(w===c$)throw c$;if(w)return w.pos=0,w.len=0,this.trackCheckout(w),w}this.open++;try{let w=await this.dial();return this.trackCheckout(w),w}catch(w){throw this.open--,this.wakeWaiter(),w}}waitForSlot(){return new Promise((w)=>{let $=!1,Y=(J)=>{if($)return;$=!0,clearTimeout(X),w(J)},A=(J)=>Y(J),X=setTimeout(()=>{let J=this.waiters.indexOf(A);if(J!==-1)this.waiters.splice(J,1);Y(c$)},this.queueWaitMs);X.unref?.(),this.waiters.push(A)})}release(w){if(this.inUse.delete(w),w.closed){this.open--,this.wakeWaiter();return}w.clearStreaming(),w.compact(),w.fresh=!1;let $=this.waiters.shift();if($){w.socket?.ref(),this.trackCheckout(w),$(w);return}w.idleSince=Date.now(),w.socket?.unref(),this.idle.push(w),this.ensureSweeper()}destroy(w){this.inUse.delete(w),w.destroy(),this.open--,this.wakeWaiter()}wakeWaiter(){let w=this.waiters.shift();if(w)w(null)}ensureSweeper(){if(this.sweeper)return;let w=this.checkoutIdleMs>0?Math.min(zj,this.checkoutIdleMs):zj;this.sweeper=setInterval(()=>this.sweep(),w),this.sweeper.unref?.()}sweep(){let w=Date.now(),$=w-zj;if(this.idle.length){let Y=[];for(let A of this.idle)if(A.closed||A.idleSince<=$)A.destroy(),this.open--;else Y.push(A);this.idle=Y}if(this.checkoutIdleMs>0&&this.inUse.size){let Y=w-this.checkoutIdleMs;for(let A of this.inUse)if(!A.closed&&A.lastActivityAt>0&&A.lastActivityAt<=Y)A.markTimedOut()}if(this.idle.length===0&&this.inUse.size===0&&this.sweeper){if(clearInterval(this.sweeper),this.sweeper=null,this.key&&N8.get(this.key)===this)N8.delete(this.key)}}}function JH(w,$){let Y=N8.get(w);if(!Y){let A=w.lastIndexOf(":"),X=A===-1?w:w.slice(0,A),J=A===-1?80:Number(w.slice(A+1));Y=new M2(X,J,$,w),N8.set(w,Y)}return Y}function T2(w,$,Y){for(let A=Y+3;A<$;A++)if(w[A]===10&&w[A-1]===13&&w[A-2]===10&&w[A-3]===13)return A-3;return-1}function UH(w,$,Y){let A=H2.decode(w.subarray($,Y)),X=A.indexOf(`\r
|
|
221
221
|
`),J=X===-1?A:A.slice(0,X),U=J.indexOf(" "),j=Number.parseInt(J.slice(U+1,U+5),10),W=J.startsWith("HTTP/1.0"),Q=[],f=-1,z=!1,B=W,G=!1,T=!1,K=X===-1?A.length:X+2;while(K<A.length){let F=A.indexOf(`\r
|
|
222
|
-
`,K);if(F===-1)F=A.length;let N=A.slice(K,F);if(K=F+2,N==="")continue;let R=N.indexOf(":");if(R===-1)continue;let L=N.slice(0,R),V=N.slice(R+1).trim(),q=L.charCodeAt(0)|32;if(q===99||q===116||q===107){let M=L.toLowerCase();if(M==="content-length"){let C=/^\d+$/.test(V)?Number(V):Number.NaN;if(!Number.isSafeInteger(C)||f>=0&&f!==C)T=!0;else f=C;continue}if(M==="transfer-encoding"){if(V.toLowerCase().includes("chunked"))z=!0;continue}if(M==="connection"){let C=V.toLowerCase();if(C.includes("close"))B=!0;if(C.includes("keep-alive"))G=!0;continue}if(M==="keep-alive")continue}Q.push([L,V])}if(W&&G)B=!1;if(z&&f>=0)f=-1;return{status:j,headerEnd:Y,headers:Q,contentLength:f,chunked:z,closeConn:B,malformed:T}}function WH(w,$){return $||w===204||w===304}async function y2(w){let{hostPort:$,method:Y,path:A,reqHeaders:X,forwardedHost:J,originOverride:U,body:j}=w,W=Y==="HEAD";if(X.get("expect")||X.get("upgrade"))throw LY;let Q=null;if(j){let G=X.get("content-length"),T=G?Number.parseInt(G,10):Number.NaN;if(Number.isNaN(T)||T>N2)throw LY;if(Q=await EH(j),Q===null)throw LY}let f=GH(Y,A,X,$,J,U,Q),z=f;if(Q&&Q.length)z=new Uint8Array(f.length+Q.length),z.set(f),z.set(Q,f.length);let B=JH($,w.maxPerHost??sq());for(let G=0;;G++){if(G>0)await new Promise((K)=>{setTimeout(K,G)});let T;try{T=B.acquireIdleSync()??await B.acquireOrDial()}catch(K){if(K!==c$&&G<4)continue;throw K}try{let K=T.socket.write(z);if(K<z.length)await T.writeAll(z.subarray(K));return await QH(B,T,W,w.signal)}catch(K){let F=K===T8&&G<4&&(T.fresh||lq(Y));if(B.destroy(T),F)continue;if(K===T8)throw Error("upstream closed connection");throw K}}}async function jH(w){let $=T2(w.buf,w.len,w.pos);while($===-1){if(w.closed){if(w.timedOut)throw Bj;if(w.len===w.pos)throw T8;throw Error("upstream closed mid-header")}if(w.len-w.pos>q2)throw Error("upstream header block too large");await w.waitForData(w.len),$=T2(w.buf,w.len,w.pos)}return $}async function QH(w,$,Y,A){let X;for(;;){let U=await jH($);if(X=UH($.buf,$.pos,U),$.pos=U+4,X.status>=100&&X.status<200)continue;break}if(X.malformed)throw w.destroy($),Error("upstream sent malformed response framing");let J=X.headers;if(WH(X.status,Y))return I2(w,$,X.closeConn),new Response(null,{status:X.status,headers:J});if(X.chunked)return new Response(BH(w,$,X.closeConn,A),{status:X.status,headers:J});if(X.contentLength>=0){if($.len-$.pos>=X.contentLength){let j=$.pos+X.contentLength,W=$.len>j,Q=null;if(X.contentLength>=$H)Q=$.buf.subarray($.pos,j),$.buf=new Uint8Array(YH);else if(X.contentLength>0)Q=$.buf.slice($.pos,j);if($.pos=0,$.len=0,X.closeConn||W)w.destroy($);else w.release($);return new Response(Q,{status:X.status,headers:J})}return new Response(fH(w,$,X.contentLength,X.closeConn),{status:X.status,headers:J})}return new Response(zH($),{status:X.status,headers:J})}function I2(w,$,Y){if(Y||$.pos!==$.len)w.destroy($);else w.release($)}function fH(w,$,Y,A){let X=$.len-$.pos,J=X>0?$.buf.slice($.pos,$.len):null;$.pos=0,$.len=0,$.bodyQueue=[],$.bodyRemaining=Y-X,$.queuedBytes=0,$.streamingBody=!0;let U=(j)=>{if($.bodyQueue=null,A||$.len>0)w.destroy($);else w.release($);j.close()};return new ReadableStream({start(j){if(J)j.enqueue(J);if($.bodyRemaining===0&&$.bodyQueue.length===0)U(j)},async pull(j){for(;;){let W=$.bodyQueue;if(W&&W.length>0){let Q=W.shift();if($.queuedBytes-=Q.length,$.resumeIfDrained(),j.enqueue(Q),$.bodyRemaining===0&&W.length===0)U(j);return}if($.bodyRemaining===0){if($.bodyQueue)U(j);return}if($.closed){$.bodyQueue=null,w.destroy($),j.error(Error("upstream closed mid-body"));return}await $.waitForBody()}},cancel(){$.bodyQueue=null,w.destroy($)}})}function zH(w){return w.streamingBody=!0,new ReadableStream({async pull($){for(;;){if(w.len>w.pos){$.enqueue(w.buf.slice(w.pos,w.len)),w.pos=w.len,w.resumeIfDrained();return}if(w.closed){w.destroy(),$.close();return}await w.waitForData(w.len)}},cancel(){w.destroy()}})}function BH(w,$,Y,A){$.streamingBody=!0;let X=0,J=!1,U=!1,j,W=()=>A?.removeEventListener("abort",Q),Q=()=>{if(U)return;U=!0,w.destroy($);try{j?.close()}catch{}};async function f(){for(;;){for(let
|
|
222
|
+
`,K);if(F===-1)F=A.length;let N=A.slice(K,F);if(K=F+2,N==="")continue;let R=N.indexOf(":");if(R===-1)continue;let L=N.slice(0,R),V=N.slice(R+1).trim(),q=L.charCodeAt(0)|32;if(q===99||q===116||q===107){let M=L.toLowerCase();if(M==="content-length"){let C=/^\d+$/.test(V)?Number(V):Number.NaN;if(!Number.isSafeInteger(C)||f>=0&&f!==C)T=!0;else f=C;continue}if(M==="transfer-encoding"){if(V.toLowerCase().includes("chunked"))z=!0;continue}if(M==="connection"){let C=V.toLowerCase();if(C.includes("close"))B=!0;if(C.includes("keep-alive"))G=!0;continue}if(M==="keep-alive")continue}Q.push([L,V])}if(W&&G)B=!1;if(z&&f>=0)f=-1;return{status:j,headerEnd:Y,headers:Q,contentLength:f,chunked:z,closeConn:B,malformed:T}}function WH(w,$){return $||w===204||w===304}async function y2(w){let{hostPort:$,method:Y,path:A,reqHeaders:X,forwardedHost:J,originOverride:U,body:j}=w,W=Y==="HEAD";if(X.get("expect")||X.get("upgrade"))throw LY;let Q=null;if(j){let G=X.get("content-length"),T=G?Number.parseInt(G,10):Number.NaN;if(Number.isNaN(T)||T>N2)throw LY;if(Q=await EH(j),Q===null)throw LY}let f=GH(Y,A,X,$,J,U,Q),z=f;if(Q&&Q.length)z=new Uint8Array(f.length+Q.length),z.set(f),z.set(Q,f.length);let B=JH($,w.maxPerHost??sq());for(let G=0;;G++){if(G>0)await new Promise((K)=>{setTimeout(K,G)});let T;try{T=B.acquireIdleSync()??await B.acquireOrDial()}catch(K){if(K!==c$&&G<4)continue;throw K}try{let K=T.socket.write(z);if(K<z.length)await T.writeAll(z.subarray(K));return await QH(B,T,W,w.signal)}catch(K){let F=K===T8&&G<4&&(T.fresh||lq(Y));if(B.destroy(T),F)continue;if(K===T8)throw Error("upstream closed connection");throw K}}}async function jH(w){let $=T2(w.buf,w.len,w.pos);while($===-1){if(w.closed){if(w.timedOut)throw Bj;if(w.len===w.pos)throw T8;throw Error("upstream closed mid-header")}if(w.len-w.pos>q2)throw Error("upstream header block too large");await w.waitForData(w.len),$=T2(w.buf,w.len,w.pos)}return $}async function QH(w,$,Y,A){let X;for(;;){let U=await jH($);if(X=UH($.buf,$.pos,U),$.pos=U+4,X.status>=100&&X.status<200)continue;break}if(X.malformed)throw w.destroy($),Error("upstream sent malformed response framing");let J=X.headers;if(WH(X.status,Y))return I2(w,$,X.closeConn),new Response(null,{status:X.status,headers:J});if(X.chunked)return new Response(BH(w,$,X.closeConn,A),{status:X.status,headers:J});if(X.contentLength>=0){if($.len-$.pos>=X.contentLength){let j=$.pos+X.contentLength,W=$.len>j,Q=null;if(X.contentLength>=$H)Q=$.buf.subarray($.pos,j),$.buf=new Uint8Array(YH);else if(X.contentLength>0)Q=$.buf.slice($.pos,j);if($.pos=0,$.len=0,X.closeConn||W)w.destroy($);else w.release($);return new Response(Q,{status:X.status,headers:J})}return new Response(fH(w,$,X.contentLength,X.closeConn),{status:X.status,headers:J})}return new Response(zH($),{status:X.status,headers:J})}function I2(w,$,Y){if(Y||$.pos!==$.len)w.destroy($);else w.release($)}function fH(w,$,Y,A){let X=$.len-$.pos,J=X>0?$.buf.slice($.pos,$.len):null;$.pos=0,$.len=0,$.bodyQueue=[],$.bodyRemaining=Y-X,$.queuedBytes=0,$.streamingBody=!0;let U=(j)=>{if($.bodyQueue=null,A||$.len>0)w.destroy($);else w.release($);j.close()};return new ReadableStream({start(j){if(J)j.enqueue(J);if($.bodyRemaining===0&&$.bodyQueue.length===0)U(j)},async pull(j){for(;;){let W=$.bodyQueue;if(W&&W.length>0){let Q=W.shift();if($.queuedBytes-=Q.length,$.resumeIfDrained(),j.enqueue(Q),$.bodyRemaining===0&&W.length===0)U(j);return}if($.bodyRemaining===0){if($.bodyQueue)U(j);return}if($.closed){$.bodyQueue=null,w.destroy($),j.error(Error("upstream closed mid-body"));return}await $.waitForBody()}},cancel(){$.bodyQueue=null,w.destroy($)}})}function zH(w){return w.streamingBody=!0,new ReadableStream({async pull($){for(;;){if(w.len>w.pos){$.enqueue(w.buf.slice(w.pos,w.len)),w.pos=w.len,w.resumeIfDrained();return}if(w.closed){w.destroy(),$.close();return}await w.waitForData(w.len)}},cancel(){w.destroy()}})}function BH(w,$,Y,A){$.streamingBody=!0;let X=0,J=!1,U=!1,j,W=()=>A?.removeEventListener("abort",Q),Q=()=>{if(U)return;U=!0,w.destroy($);try{j?.close()}catch{}};async function f(z=!1){for(;;){for(let B=$.pos;B+1<$.len;B++)if($.buf[B]===13&&$.buf[B+1]===10){let G=H2.decode($.buf.subarray($.pos,B));return $.pos=B+2,G}if($.closed){if(z&&$.pos===$.len)return null;throw Error("upstream closed mid-chunk-header")}if($.len-$.pos>q2)throw Error("upstream chunk header too large");await $.waitForData($.len)}}return new ReadableStream({start(z){if(j=z,A?.aborted)Q();else A?.addEventListener("abort",Q,{once:!0})},async pull(z){try{for(;;){if(X>0){if($.len===$.pos){if($.closed){w.destroy($),z.error(Error("upstream closed mid-chunk"));return}await $.waitForData($.len);continue}let K=Math.min(X,$.len-$.pos);if(z.enqueue($.buf.slice($.pos,$.pos+K)),$.pos+=K,X-=K,$.resumeIfDrained(),X===0)J=!0;return}if(J)await f(),J=!1;let B=await f(!0);if(B===null){w.destroy($),W(),z.close();return}let G=B.indexOf(";"),T=Number.parseInt(G===-1?B:B.slice(0,G),16);if(!Number.isInteger(T)||T<0){w.destroy($),z.error(Error("upstream sent malformed chunk size"));return}if(T===0){for(;;)if(await f()==="")break;I2(w,$,Y),W(),z.close();return}X=T}}catch(B){if(w.destroy($),W(),!U)z.error(B)}},cancel(){U=!0,W(),w.destroy($)}})}function GH(w,$,Y,A,X,J,U){let j=`${w} ${$} HTTP/1.1\r
|
|
223
223
|
host: ${A}\r
|
|
224
224
|
x-forwarded-for: 127.0.0.1\r
|
|
225
225
|
x-forwarded-proto: https\r
|
|
@@ -333,6 +333,6 @@ Run \`${this.name??"cli"} --help\` for usage.`;W$.stderr.write(`${J}${X}${U}
|
|
|
333
333
|
`);this.output.write(A[Y]),this._prevFrame=w,this.output.write(Zw.move(0,A.length-Y-1));return}if($&&$?.length>1){let Y=$[0];this.output.write(Zw.move(0,Y)),this.output.write(JJ.down());let X=w.split(`
|
|
334
334
|
`).slice(Y);this.output.write(X.join(`
|
|
335
335
|
`)),this._prevFrame=w;return}this.output.write(JJ.down())}if(this.output.write(w),this.state==="initial")this.state="active";this._prevFrame=w}}function vG(w,$){if(w===void 0)return 0;if($.length===0)return 0;let A=$.findIndex((X)=>X.value===w);return A!==-1?A:0}function mG(w,$){return($.label??String($.value)).toLowerCase().includes(w.toLowerCase())}function gG(w,$){if(!$)return;if(w)return $;return $[0]}class dG extends zJ{filteredOptions;multiple;isNavigating=!1;selectedValues=[];focusedValue;#w=0;#Y="";#A;#$;get cursor(){return this.#w}get userInputWithCursor(){if(!this.userInput)return aY.inverse(aY.hidden("_"));if(this._cursor>=this.userInput.length)return`${this.userInput}█`;let w=this.userInput.slice(0,this._cursor),[$,...Y]=this.userInput.slice(this._cursor);return`${w}${aY.inverse($)}${Y.join("")}`}get options(){if(typeof this.#$==="function")return this.#$();return this.#$}constructor(w){super(w);this.#$=w.options;let $=this.options;this.filteredOptions=[...$],this.multiple=w.multiple===!0,this.#A=w.filter??mG;let Y;if(w.initialValue&&Array.isArray(w.initialValue))if(this.multiple)Y=w.initialValue;else Y=w.initialValue.slice(0,1);else if(!this.multiple&&this.options.length>0)Y=[this.options[0].value];if(Y)for(let A of Y){let X=$.findIndex((J)=>J.value===A);if(X!==-1)this.toggleSelected(A),this.#w=X}this.focusedValue=this.options[this.#w]?.value,this.on("key",(A,X)=>this.#X(A,X)),this.on("userInput",(A)=>this.#J(A))}_isActionKey(w,$){return w==="\t"||this.multiple&&this.isNavigating&&$.name==="space"&&w!==void 0&&w!==""}#X(w,$){let Y=$.name==="up",A=$.name==="down",X=$.name==="return";if(Y||A){if(this.#w=Math.max(0,Math.min(this.#w+(Y?-1:1),this.filteredOptions.length-1)),this.focusedValue=this.filteredOptions[this.#w]?.value,!this.multiple)this.selectedValues=[this.focusedValue];this.isNavigating=!0}else if(X)this.value=gG(this.multiple,this.selectedValues);else if(this.multiple)if(this.focusedValue!==void 0&&($.name==="tab"||this.isNavigating&&$.name==="space"))this.toggleSelected(this.focusedValue);else this.isNavigating=!1;else{if(this.focusedValue)this.selectedValues=[this.focusedValue];this.isNavigating=!1}}deselectAll(){this.selectedValues=[]}toggleSelected(w){if(this.filteredOptions.length===0)return;if(this.multiple)if(this.selectedValues.includes(w))this.selectedValues=this.selectedValues.filter(($)=>$!==w);else this.selectedValues=[...this.selectedValues,w];else this.selectedValues=[w]}#J(w){if(w!==this.#Y){this.#Y=w;let $=this.options;if(w)this.filteredOptions=$.filter((Y)=>this.#A(w,Y));else this.filteredOptions=[...$];if(this.#w=vG(this.focusedValue,this.filteredOptions),this.focusedValue=this.filteredOptions[this.#w]?.value,!this.multiple)if(this.focusedValue!==void 0)this.toggleSelected(this.focusedValue);else this.deselectAll()}}}class nG extends zJ{options;cursor=0;#w;getGroupItems(w){return this.options.filter(($)=>$.group===w)}isGroupSelected(w){let $=this.getGroupItems(w),Y=this.value;if(Y===void 0)return!1;return $.every((A)=>Y.includes(A.value))}toggleValue(){let w=this.options[this.cursor];if(this.value===void 0)this.value=[];if(w.group===!0){let $=String(w.value),Y=this.getGroupItems($);if(this.isGroupSelected($))this.value=this.value.filter((A)=>Y.findIndex((X)=>X.value===A)===-1);else this.value=[...this.value,...Y.map((A)=>A.value)];this.value=Array.from(new Set(this.value))}else{let $=this.value.includes(w.value);this.value=$?this.value.filter((Y)=>Y!==w.value):[...this.value,w.value]}}constructor(w){super(w,!1);let{options:$}=w;this.#w=w.selectableGroups!==!1,this.options=Object.entries($).flatMap(([Y,A])=>[{value:Y,group:!0,label:Y},...A.map((X)=>({...X,group:Y}))]),this.value=[...w.initialValues??[]],this.cursor=Math.max(this.options.findIndex(({value:Y})=>Y===w.cursorAt),this.#w?0:1),this.on("cursor",(Y)=>{switch(Y){case"left":case"up":{this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;let A=this.options[this.cursor]?.group===!0;if(!this.#w&&A)this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;break}case"down":case"right":{this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;let A=this.options[this.cursor]?.group===!0;if(!this.#w&&A)this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;break}case"space":this.toggleValue();break}})}}var lG=wG();var w0=(w,$)=>lG?w:$,WM=w0("◆","*"),jM=w0("■","x"),QM=w0("▲","x"),fM=w0("◇","o"),zM=w0("┌","T"),pG=w0("│","|"),BM=w0("└","—"),GM=w0("●",">"),EM=w0("○"," "),KM=w0("◻","[•]"),FM=w0("◼","[+]"),TM=w0("◻","[ ]"),NM=w0("▪","•"),qM=w0("─","-"),HM=w0("╮","+"),RM=w0("├","+"),MM=w0("╯","+"),yM=w0("●","•"),IM=w0("◆","*"),CM=w0("▲","!"),VM=w0("■","x");var LM={light:w0("─","-"),heavy:w0("━","="),block:w0("█","#")};function rG(){return`${aY.gray(pG)} `}var SM=rG();PA();UU();P8();x$();z0();import{spawn as RR}from"node:child_process";import*as m8 from"node:process";class g8{processes=new Map;isShuttingDown=!1;async startProcess(w,$,Y){if(this.processes.has(w)){E("start",`Process ${w} is already running`,Y);return}let[A,...X]=$.command.split(" "),J=$.cwd||m8.cwd();E("start",`Starting process ${w}:`,Y),E("start",` Command: ${A} ${X.join(" ")}`,Y),E("start",` Working directory: ${J}`,Y),E("start",` Environment variables: ${A$($.env)}`,Y);let U=RR(A,X,{cwd:J,env:{...m8.env,...$.env},shell:!0,stdio:"inherit"});return this.processes.set(w,{command:$.command,cwd:J,process:U,env:$.env}),new Promise((j,W)=>{if(U.on("error",(Q)=>{if(!this.isShuttingDown)E("start",`Process ${w} failed to start: ${Q}`,Y),this.processes.delete(w),W(Q)}),U.on("exit",(Q)=>{if(!this.isShuttingDown&&Q!==null&&Q!==0)E("start",`Process ${w} exited with code ${Q}; leaving the proxy running`,Y),this.processes.delete(w),W(Error(`Process ${w} exited with code ${Q}`))}),Y)U.stdout?.on("data",(Q)=>{E("process",`[${w}] ${Q.toString().trim()}`,!0)}),U.stderr?.on("data",(Q)=>{E("process",`[${w}] ERR: ${Q.toString().trim()}`,!0)});setTimeout(()=>{if(!this.isShuttingDown&&U.killed)this.processes.delete(w),W(Error(`Process ${w} was killed during startup`));else E("start",`Process ${w} started successfully`,Y),j()},1000)})}async stopProcess(w,$){let Y=this.processes.get(w);if(!Y?.process){E("start",`No process found for ${w}`,$);return}return E("start",`Stopping process ${w}`,$),new Promise((A)=>{if(!Y.process){A();return}Y.process.once("exit",()=>{this.processes.delete(w),E("start",`Process ${w} stopped`,$),A()});try{Y.process.kill("SIGTERM"),setTimeout(()=>{if(Y.process){E("start",`Force killing process ${w}`,$);try{Y.process.kill("SIGKILL")}catch(X){}}},3000)}catch(X){E("start",`Error stopping process ${w}: ${X}`,$),this.processes.delete(w),A()}})}async stopAll(w){if(this.isShuttingDown){E("start","Already shutting down, skipping duplicate stopAll call",w);return}this.isShuttingDown=!0,E("start","Stopping all processes",w);let $=Array.from(this.processes.keys()).map((Y)=>this.stopProcess(Y,w).catch((A)=>{H.error(`Failed to stop process ${Y}:`,A)}));await Promise.allSettled($),this.processes.clear(),this.isShuttingDown=!1}isRunning(w){let $=this.processes.get(w);return!!$?.process&&!$.process.killed}}var d8=new g8;g$();x$();import{execSync as SR}from"node:child_process";import*as s8 from"node:http";import*as h6 from"node:net";import*as O6 from"node:os";import*as _6 from"node:path";import*as t from"node:process";var n8=(w,$)=>(Y)=>`\x1B[${w}m${Y}\x1B[${$}m`,Ow={bold:n8(1,22),dim:n8(2,22),green:n8(32,39),cyan:n8(36,39)};PA();P8();x$();g$();z0();import*as I6 from"node:fs";import*as C6 from"node:path";import*as _w from"node:process";function MR(w,$){let A=($&&$!=="/"?`${w}${$}`:w).replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,128);return A.length>0?A:"rpx"}async function mj(w){if(w.proxies.length===0)throw Error("runViaDaemon: no proxies provided");let $=w.verbose??!1,Y=w.registryDir,A=new Set,X=w.proxies.map((B)=>{let G=B.id??MR(B.to,B.path);if(!C$(G))throw Error(`invalid registry id "${G}" derived from to="${B.to}"`);if(A.has(G))throw Error(`duplicate registry id "${G}" — set an explicit \`id\` on one of the proxies`);return A.add(G),{...B,id:G}}),J=new Date().toISOString();for(let B of X)await c1({id:B.id,from:B.from,to:B.to,path:B.path,pid:w.persistent?void 0:_w.pid,cwd:_w.cwd(),createdAt:J,cleanUrls:B.cleanUrls,changeOrigin:B.changeOrigin,pathRewrites:B.pathRewrites,static:B.static,loadBalancer:B.loadBalancer},Y,$);let U=await v8({rpxDir:w.rpxDir,verbose:$,spawnCommand:w.spawnCommand,startupTimeoutMs:w.startupTimeoutMs,spawnEnv:w.spawnEnv});for(let B of X){let G=B.static?`static ${typeof B.static==="string"?B.static:B.static.dir}`:B.from;H.success(`https://${B.to} → ${G}`)}if(H.info(`(via rpx daemon pid=${U.pid}; \`rpx daemon:status\` to inspect)`),w.detached)return;let j=!1,W=Y??$w(),Q=X.map((B)=>B.id),f=async()=>{if(j)return;j=!0;for(let B of Q)await m$(B,Y,$).catch((G)=>{E("runner",`removeEntry(${B}) failed: ${G}`,$)})},z=(B)=>{E("runner",`received ${B}, unregistering ${Q.length} entries`,$),f().finally(()=>_w.exit(0))};_w.once("SIGINT",z),_w.once("SIGTERM",z),_w.once("exit",()=>{if(j)return;for(let B of Q)try{I6.unlinkSync(C6.join(W,`${B}.json`))}catch{}}),await new Promise(()=>{})}hj();fj();Hj();import{timingSafeEqual as yR}from"node:crypto";function IR(w,$){if(w==null)return!1;let Y=Buffer.from(w),A=Buffer.from($);if(Y.length!==A.length)return!1;return yR(Y,A)}function l8(w){return w.toLowerCase().replace(/\.$/,"")}var CR=["/.well-known/acme-challenge/"];function VR(w){let $=w.headers.get("host");if($)return l8($.split(":")[0]);try{return l8(new URL(w.url).hostname)}catch{return""}}function V6(w){let $=w.header.toLowerCase(),Y=new Set,A=[];for(let W of w.hosts){let Q=l8(W);if(Q.startsWith("*."))A.push(Q);else Y.add(Q)}let X=w.exemptPaths??CR,J=w.forbiddenMessage??`Forbidden: direct origin access is not allowed; requests must arrive via the CDN.
|
|
336
|
-
`,U=(W)=>{let Q=l8(W);return Y.has(Q)||A.some((f)=>$Y(Q,f))},j=(W)=>{let Q=VR(W);if(!U(Q))return;let f="/";try{f=new URL(W.url).pathname}catch{}if(X.some((z)=>f.startsWith(z)))return;if(IR(W.headers.get($),w.value))return;return new Response(J,{status:403,headers:{"content-type":"text/plain"}})};return j.protects=U,j}Gj();Ej();F8();M8();Tj();Nj();H8();z0();var L6={name:"@stacksjs/rpx",type:"module",version:"0.11.38",description:"A modern and smart reverse proxy.",author:"Chris Breuer <chris@stacksjs.org>",license:"MIT",homepage:"https://github.com/stacksjs/rpx",repository:{type:"git",url:"git+https://github.com/stacksjs/rpx.git"},bugs:{url:"https://github.com/stacksjs/rpx/issues"},keywords:["reverse proxy","ssl","development","environment","proxy","bun","stacks","typescript","javascript"],exports:{".":{types:"./dist/index.d.ts",bun:"./dist/index.js",import:"./dist/index.js"}},module:"./dist/index.js",types:"./dist/index.d.ts",bin:{rpx:"./dist/bin/cli.js","reverse-proxy":"./dist/bin/cli.js"},files:["README.md","dist"],scripts:{build:"bun build.ts && bun build ./bin/cli.ts --compile --minify --outfile bin/rpx",compile:"bun build ./bin/cli.ts --compile --minify --outfile bin/rpx","compile:all":"bun run compile:linux-x64 && bun run compile:linux-arm64 && bun run compile:windows-x64 && bun run compile:darwin-x64 && bun run compile:darwin-arm64","compile:linux-x64":"bun build ./bin/cli.ts --compile --minify --target=bun-linux-x64 --outfile bin/rpx-linux-x64","compile:linux-arm64":"bun build ./bin/cli.ts --compile --minify --target=bun-linux-arm64 --outfile bin/rpx-linux-arm64","compile:windows-x64":"bun build ./bin/cli.ts --compile --minify --target=bun-windows-x64 --outfile bin/rpx-windows-x64.exe","compile:darwin-x64":"bun build ./bin/cli.ts --compile --minify --target=bun-darwin-x64 --outfile bin/rpx-darwin-x64","compile:darwin-arm64":"bun build ./bin/cli.ts --compile --minify --target=bun-darwin-arm64 --outfile bin/rpx-darwin-arm64",bench:"bun run bench/run.ts","bench:html":"bun run bench/run.ts --html","bench:latency":"bun run bench/run.ts --latency","bench:throughput":"bun run bench/run.ts --throughput",lint:"bunx --bun pickier .","lint:fix":"bunx --bun pickier . --fix",fresh:"bunx rimraf node_modules/ bun.lock && bun i",changelog:"changelogen --output CHANGELOG.md",prepublishOnly:"bun build.ts","release:binaries":"bun run compile:all && bun run zip",test:"bun test",typecheck:"bunx tsc --noEmit",zip:"bun run zip:all","zip:all":"bun run zip:linux-x64 && bun run zip:linux-arm64 && bun run zip:windows-x64 && bun run zip:darwin-x64 && bun run zip:darwin-arm64","zip:linux-x64":"zip -j bin/rpx-linux-x64.zip bin/rpx-linux-x64","zip:linux-arm64":"zip -j bin/rpx-linux-arm64.zip bin/rpx-linux-arm64","zip:windows-x64":"zip -j bin/rpx-windows-x64.zip bin/rpx-windows-x64.exe","zip:darwin-x64":"zip -j bin/rpx-darwin-x64.zip bin/rpx-darwin-x64","zip:darwin-arm64":"zip -j bin/rpx-darwin-arm64.zip bin/rpx-darwin-arm64"},dependencies:{"@stacksjs/clapp":"^0.2.10","@stacksjs/tlsx":"^0.13.13"},devDependencies:{bunfig:"^0.15.6",mitata:"^1.0.34",typescript:"^7.0.2"},"simple-git-hooks":{"pre-commit":"bunx lint-staged"},"lint-staged":{"*.{js,ts}":"bunx --bun pickier . --fix"}};var dY=L6.version;var r8=new g8,hR=new C8("0.0.0.0"),nY=new Set,a8=new Set,gj=!1,p8=null,dj=null;async function o8(w){if(gj)return E("cleanup","Cleanup already in progress, skipping",w?.verbose),dj||Promise.resolve();gj=!0,E("cleanup","Starting cleanup process",w?.verbose),dj=new Promise(($)=>{p8=$});try{await r8.stopAll(w?.verbose),H.info("Shutting down proxy servers...");let $=[],Y=Array.from(nY).map((A)=>new Promise((X)=>{let J=A;try{if(typeof J.stop==="function")J.stop(!0),E("cleanup","Bun server stopped",w?.verbose),X();else if(typeof J.close==="function")J.close(()=>{E("cleanup","Server closed successfully",w?.verbose),X()});else X()}catch(U){E("cleanup",`Error stopping server: ${U}`,w?.verbose),X()}}));$.push(...Y),nY.clear();for(let A of a8)b1(A);if(a8.clear(),w?.hosts&&w.domains?.length){E("cleanup","Cleaning up hosts file entries",w?.verbose),E("cleanup",`Original domains for cleanup: ${JSON.stringify(w.domains)}`,w?.verbose);let A=w.domains.filter((X)=>{if(X==="test.local")return!0;return X!=="localhost"&&!X.startsWith("localhost.")&&X!=="127.0.0.1"});if(E("cleanup",`Filtered domains for cleanup: ${JSON.stringify(A)}`,w?.verbose),A.length>0)H.info("Cleaning up hosts file entries..."),$.push(a2(A,w?.verbose).then(()=>{E("cleanup",`Removed hosts entries for ${A.join(", ")}`,w?.verbose)}).catch((X)=>{E("cleanup",`Failed to remove hosts entries: ${X}`,w?.verbose),H.warn(`Failed to clean up hosts file entries for ${A.join(", ")}:`,X)}))}if(w?.certs&&w.domains?.length){E("cleanup","Cleaning up SSL certificates",w?.verbose),H.info("Cleaning up SSL certificates...");let A=w.domains.map(async(X)=>{try{await f2(X,w?.verbose),E("cleanup",`Removed certificates for ${X}`,w?.verbose)}catch(J){E("cleanup",`Failed to remove certificates for ${X}: ${J}`,w?.verbose),H.warn(`Failed to clean up certificates for ${X}:`,J)}});$.push(...A)}await Promise.allSettled($),E("cleanup","All cleanup tasks completed successfully",w?.verbose),H.success("All cleanup tasks completed successfully")}catch($){E("cleanup",`Error during cleanup: ${$}`,w?.verbose),H.error("Error during cleanup:",$)}finally{if(p8)p8();p8=null,gj=!1;let $=w&&"vitePluginUsage"in w&&w.vitePluginUsage===!0;if(t.env.NODE_ENV!=="test"&&t.env.BUN_ENV!=="test"&&!$)t.exit(0)}return dj}var nj=!1;function D6(w){if(nj){E("signal",`Received second ${w} signal, forcing exit`,!0),t.exit(1);return}nj=!0,E("signal",`Received ${w} signal, initiating cleanup`,!0),o8().catch(($)=>{E("signal",`Cleanup failed after ${w}: ${$}`,!0),t.exit(1)}).finally(()=>{nj=!1})}t.once("SIGINT",()=>D6("SIGINT"));t.once("SIGTERM",()=>D6("SIGTERM"));t.on("uncaughtException",(w)=>{H.error("Uncaught exception (continuing):",w)});t.on("unhandledRejection",(w)=>{H.error("Unhandled rejection (continuing):",w)});async function lY(w,$,Y,A=5){E("connection",`Testing connection to ${w}:${$} (retries left: ${A})`,Y);let X=15000,J=Date.now();if(t.env.RPX_BYPASS_CONNECTION_TEST==="true"){E("connection",`Bypassing connection test for ${w}:${$} due to RPX_BYPASS_CONNECTION_TEST flag`,Y);return}let U=()=>new Promise((j,W)=>{let Q=h6.connect({host:w,port:$,timeout:3000});Q.once("connect",()=>{E("connection",`Successfully connected to ${w}:${$}`,Y),Q.end(),j()}),Q.once("timeout",()=>{E("connection",`Connection to ${w}:${$} timed out`,Y),Q.destroy(),W(Error("Connection timed out"))}),Q.once("error",(f)=>{E("connection",`Failed to connect to ${w}:${$}: ${f}`,Y),Q.destroy(),W(f)})});try{await U()}catch(j){let W=j;if(Date.now()-J>X){E("connection",`Connection test timed out after ${X}ms, but continuing anyway`,Y),H.warn(`Connection test to ${w}:${$} timed out, but RPX will try to proceed anyway.`);return}if(W.code==="ECONNREFUSED"&&A>0)return E("connection",`Connection refused, server might be starting up. Retrying in 2 seconds... (${A} retries left)`,Y),await new Promise((f)=>setTimeout(f,2000)),lY(w,$,Y,A-1);if(A>0)try{E("connection",`Trying HTTP request to ${w}:${$}`,Y),await new Promise((f,z)=>{let B=s8.request({hostname:w,port:$,path:"/",method:"HEAD",timeout:5000},(G)=>{E("connection",`Received HTTP response with status: ${G.statusCode}`,Y),f()});B.on("error",(G)=>z(G)),B.on("timeout",()=>{B.destroy(),z(Error("HTTP request timed out"))}),B.end()}),E("connection",`HTTP request to ${w}:${$} succeeded`,Y);return}catch(f){return E("connection",`HTTP request to ${w}:${$} failed: ${f}`,Y),E("connection",`Retrying socket connection in 2 seconds... (${A} retries left)`,Y),await new Promise((z)=>setTimeout(z,2000)),lY(w,$,Y,A-1)}let Q=`Failed to connect to ${w}:${$} after ${5-A} attempts: ${W.message}`;E("connection",`${Q}. To bypass this check set RPX_BYPASS_CONNECTION_TEST=true`,Y),H.warn(Q),H.warn("RPX will try to continue anyway. If you're sure this is correct, you can set RPX_BYPASS_CONNECTION_TEST=true to skip this check.")}}async function x6(w){E("server",`Starting server with options: ${A$(w)}`,w.verbose);let $=u$(w.from),Y=new URL($.startsWith("http")?$:`http://${$}`),A=new URL((w.to?.startsWith("http")?w.to:`http://${w.to}`)||"rpx.localhost"),X=Number.parseInt(Y.port)||(Y.protocol.includes("https:")?443:80),J=[A.hostname];if(lj(w)&&!A.hostname.includes("localhost")&&!A.hostname.includes("127.0.0.1")){E("hosts",`Checking if hosts file entry exists for: ${A.hostname}`,w?.verbose);try{if(!(await uY(J,w.verbose))[0]){H.info(`Adding ${A.hostname} to hosts file...`),H.info("This may require sudo/administrator privileges");try{await bY(J,w.verbose)}catch(W){if(H.error("Failed to add hosts entry:",W.message),H.warn("You can manually add this entry to your hosts file:"),H.warn(`127.0.0.1 ${A.hostname}`),H.warn(`::1 ${A.hostname}`),t.platform==="win32")H.warn("On Windows:"),H.warn("1. Run notepad as administrator"),H.warn("2. Open C:\\Windows\\System32\\drivers\\etc\\hosts");else H.warn("On Unix systems:"),H.warn("sudo nano /etc/hosts")}}else E("hosts",`Host entry already exists for ${A.hostname}`,w.verbose)}catch(j){H.error("Failed to check hosts file:",j.message)}}try{await lY(Y.hostname,X,w.verbose)}catch(j){E("server",`Connection test failed: ${j}`,w.verbose),H.error(j.message),H.warn("Continuing with proxy setup despite connection test failure..."),H.info("If you need to bypass connection testing, set environment variable RPX_BYPASS_CONNECTION_TEST=true")}let U=w._cachedSSLConfig||null;if(w.https)try{if(w.https===!0)w.https=RY({...w,to:A.hostname});if(U=await y$({...w,to:A.hostname,https:w.https}),!U){if(E("ssl",`Generating new certificates for ${A.hostname}`,w.verbose),await HY({...w,from:Y.toString(),to:A.hostname,https:w.https}),U=await y$({...w,to:A.hostname,https:w.https}),!U)throw Error(`Failed to load SSL configuration after generating certificates for ${A.hostname}`)}}catch(j){throw E("server",`SSL setup failed: ${j}`,w.verbose),j}E("server",`Setting up reverse proxy with SSL config for ${A.hostname}`,w.verbose),await _R({...w,from:$,originalFrom:w.from||$,to:A.hostname,fromPort:X,sourceUrl:{hostname:Y.hostname,host:Y.host},ssl:U})}async function OR(w,$,Y,A,X,J,U,j,W,Q,f,z){E("proxy",`Creating proxy server ${w} -> ${$} with cleanUrls: ${j}`,U);let B=MY(f??A.host,z);IY(B);let G=[{host:$,route:{sourceHost:A.host,upstreamPool:B,cleanUrls:j||!1,changeOrigin:W||!1,basePath:"/",auth:Q}}];if(!t8({routeEntries:G,listenPort:Y,sslConfig:X,originGuard:null,verbose:U??!1}))throw b1(B),Error(`Failed to start proxy server for ${$} on port ${Y}`);a8.add(B),xR({from:w,to:$,vitePluginUsage:J,listenPort:Y,ssl:!!X,cleanUrls:j,verbose:U})}async function _R(w){E("setup",`Setting up reverse proxy: ${A$(w)}`,w.verbose);let{from:$,originalFrom:Y,to:A,sourceUrl:X,ssl:J,verbose:U,cleanup:j,vitePluginUsage:W,changeOrigin:Q,cleanUrls:f}=w,z=80,B=443,G="0.0.0.0",T=w.portManager||hR,K=lj(w);try{if(K&&A&&!A.includes("localhost")&&!A.includes("127.0.0.1")){if(!(await uY([A],U))[0]){H.warn(`The hostname ${A} isn't in your hosts file. Adding it now...`);try{await bY([A],U),H.success(`Added ${A} to your hosts file.`)}catch(V){H.error(`Failed to add ${A} to your hosts file: ${V}`),H.info(`You may need to manually add '127.0.0.1 ${A}' to your /etc/hosts file.`)}}}else if(K&&t.platform!=="darwin"&&A&&A.includes("localhost")&&!A.match(/^(localhost|127\.0\.0\.1)$/)){if(!(await uY([A],U))[0]){E("hosts",`${A} not found in hosts file, adding...`,U);try{await bY([A],U)}catch(V){E("hosts",`Failed to add ${A} to hosts file: ${V}`,U)}}}if(J&&!T.usedPorts.has(z)){if(!await I$(z,G,U))E("setup","Starting HTTP redirect server",U),k6(U),T.usedPorts.add(z);else if(E("setup","Port 80 is in use, skipping HTTP redirect",U),U)H.warn("Port 80 is in use, HTTP to HTTPS redirect will not be available")}let F=J?B:z,N=await I$(F,G,U),R;if(N){if(E("setup",`Port ${F} is already in use`,U),U)H.warn(`Port ${F} is already in use. This may be another instance of rpx or another service.`);if(F===443){if(R=await T.getNextAvailablePort(3443,!0),E("setup",`Using port ${R} instead of ${F}`,U),U)H.info(`Using port ${R} instead. Access your site at https://${A}:${R}`)}else if(R=await T.getNextAvailablePort(F+1000,!0),E("setup",`Using port ${R} instead of ${F}`,U),U)H.info(`Using port ${R} instead. Access your site at http://${A}:${R}`)}else R=F,T.usedPorts.add(R),E("setup",`Using standard ${F===443?"HTTPS":"HTTP"} port ${F} for ${A}`,U);await OR($,A,R,X,J,W,U,f,Q,VY(w.auth),Y,w.loadBalancer)}catch(F){E("setup",`Setup failed: ${F}`,U),H.error(`Failed to setup reverse proxy: ${F.message}`),o8({domains:[A],hosts:typeof j==="boolean"?j:j?.hosts,certs:typeof j==="boolean"?j:j?.certs,verbose:U,vitePluginUsage:W})}}function k6(w,$=80,Y=443,A,X){E("redirect",`Starting HTTP redirect server on port ${$}`,w);let J=s8.createServer((U,j)=>{let W=U.url?U.url.split("?",1)[0]:"";if(W.startsWith("/.well-known/acme-challenge/")){if(X){let B=X.challengeStore.handlePath(W);if(B!==void 0){E("redirect",`Serving on-demand ACME challenge ${W}`,w),j.writeHead(200,{"content-type":"text/plain"}),j.end(B);return}}if(A){let B=R8(A,W);if(B!=null){E("redirect",`Serving ACME challenge ${W}`,w),j.writeHead(200,{"content-type":"text/plain"}),j.end(B);return}}if(X){j.writeHead(404,{"content-type":"text/plain"}),j.end("challenge not found");return}}let Q=U.headers.host||"",f=Q.includes(":")?Q.slice(0,Q.indexOf(":")):Q;if(X&&f&&!X.hasCert(f))X.ensureCert(f).catch(()=>{});let z=Y===443?f:`${f}:${Y}`;E("redirect",`Redirecting request from ${Q}${U.url} to https://${z}`,w),j.writeHead(301,{Location:`https://${z}${U.url}`}),j.end()}).listen($);nY.add(J),E("redirect","HTTP redirect server started",w)}function Z6(w){let $={...mw,...w};if(E("proxy",`Starting proxy with options: ${A$($)}`,$?.verbose),$.viaDaemon){if(!$.from||!$.to){H.error("viaDaemon mode requires both `from` and `to`");return}mj({proxies:[{id:$.id,from:$.from,to:$.to,path:$.path,cleanUrls:$.cleanUrls,changeOrigin:$.changeOrigin,pathRewrites:$.pathRewrites}],verbose:$.verbose}).catch((W)=>{H.error(`Failed to register with rpx daemon: ${W.message}`),t.exit(1)});return}let Y=$.to||"",A=Y.split(".").pop()?.toLowerCase()||"",X=t.platform==="darwin"&&Y&&!Y.includes("localhost")&&!Y.includes("127.0.0.1"),J=["dev","app","page","new","day","foo"],U=["test","localhost","local","example","invalid"];if(X&&J.includes(A)&&$?.verbose)H.warn(`The .${A} TLD may not work reliably for local development`),H.info(` Google owns .${A} with HSTS preloading, which can bypass local DNS`),H.info(" Consider using a reserved TLD: .test, .localhost, or .local");if(X)Promise.resolve().then(() => (gY(),Z8)).then(({setupDevelopmentDns:W})=>{W({domains:[Y],verbose:$.verbose}).then((Q)=>{if(Q)Promise.resolve().then(()=>{if($.verbose)if(U.includes(A))H.success(`DNS server started for .${A} domains`);else H.success(`DNS server started for .${A} domains (hosts file entry also added)`)});else E("dns",`Could not start DNS server - ${Y} may not resolve in browser`,$.verbose)})}).catch((W)=>{E("dns",`Failed to start DNS server: ${W}`,$.verbose)});let j={from:$.from,to:$.to,cleanUrls:$.cleanUrls,https:RY($),cleanup:$.cleanup,vitePluginUsage:$.vitePluginUsage,changeOrigin:$.changeOrigin,verbose:$.verbose,regenerateUntrustedCerts:$.regenerateUntrustedCerts};E("proxy",`Server options: ${A$(j)}`,$.verbose),x6(j).catch((W)=>{E("proxy",`Failed to start proxy: ${W}`,$.verbose),H.error(`Failed to start proxy: ${W.message}`),o8({domains:[$.to],hosts:typeof $.cleanup==="boolean"?$.cleanup:$.cleanup?.hosts,certs:typeof $.cleanup==="boolean"?$.cleanup:$.cleanup?.certs,verbose:$.verbose})})}function DR(w){return w?.verbose||!1}function lj(w){if(w?.hostsManagement===!1)return!1;let $=w?.cleanup;if($===!1)return!1;if($&&typeof $==="object"&&$.hosts===!1)return!1;return!0}async function P6(w){let $={from:"localhost:5173",to:"rpx.localhost",https:!1,cleanup:{hosts:!0,certs:!1},vitePluginUsage:!1,verbose:!1,cleanUrls:!1,changeOrigin:!1,regenerateUntrustedCerts:!0};if(w)$={...$,...w};let Y=DR($),A=lj($);if(E("config",`Starting with config: ${A$($,2)}`,Y),E("config",`Is multi-proxy? ${"proxies"in $}`,Y),E("config",`Hosts management enabled? ${A}`,Y),$.viaDaemon){let M="proxies"in $&&Array.isArray($.proxies)?$.proxies.map((C)=>({id:C.id,from:C.from,to:C.to,path:C.path,cleanUrls:C.cleanUrls??$.cleanUrls,changeOrigin:C.changeOrigin??$.changeOrigin,pathRewrites:C.pathRewrites})):[{id:$.id,from:$.from,to:$.to??"rpx.localhost",path:$.path,cleanUrls:$.cleanUrls,changeOrigin:$.changeOrigin,pathRewrites:$.pathRewrites}];await mj({proxies:M,verbose:Y});return}if("proxies"in $&&Array.isArray($.proxies)){E("servers",`Found ${$.proxies.length} proxies in config`,Y);for(let q of $.proxies)if(q.start){let M=`${q.from}-${q.to}`;try{E("watch",`Starting command for ${M} with command: ${q.start.command}`,Y),H.info(`Starting command for ${M}...`),await r8.startProcess(M,q.start,Y);let C=u$(q.from),O=new URL(C.startsWith("http")?C:`http://${C}`),J0=O.hostname||"localhost",u0=Number(O.port)||80;try{await lY(J0,u0,Y),E("watch",`Dev server is ready at ${J0}:${u0}`,Y)}catch(Gw){E("watch",`Connection check failed, but continuing with proxy setup: ${Gw}`,Y),H.warn("Dev server connection check failed. RPX will try to proceed anyway...")}}catch(C){throw E("watch",`Failed to start command for ${M}: ${C}`,Y),Error(`Failed to start command for ${M}: ${C}`)}}else E("watch",`No start command for proxy ${q.from} -> ${q.to}`,Y)}else if("start"in $&&$.start){E("watch","Found start command in single proxy config",Y);let q=`${$.from}-${$.to}`;try{if($.start)E("watch",`Starting command: ${$.start.command}`,Y),await r8.startProcess(q,$.start,Y);let M=u$($.from),C=new URL(M.startsWith("http")?M:`http://${M}`),O=C.hostname||"localhost",J0=Number(C.port)||80;try{await lY(O,J0,Y),E("watch",`Dev server is ready at ${O}:${J0}`,Y)}catch(u0){E("watch",`Connection check failed, but continuing with proxy setup: ${u0}`,Y),H.warn("Dev server connection check failed. RPX will try to proceed anyway...")}}catch(M){throw E("watch",`Failed to run start command: ${M}`,Y),Error(`Failed to run start command: ${M}`)}}else E("watch","No start command found in config",Y);let X="proxies"in $&&Array.isArray($.proxies)?$.proxies[0]?.to:("to"in $)?$.to:"rpx.localhost";if(t.platform!=="win32"&&($.https||A)){if(!T$())try{E("sudo","Pre-acquiring sudo credentials for privileged operations",Y),SR("sudo -v",{stdio:"inherit"})}catch{E("sudo","Could not pre-acquire sudo credentials",Y)}}let J=[];if($.productionCerts){if(J=await xY($.productionCerts,Y),J.length>0)E("ssl",`Using ${J.length} production SNI cert(s): ${J.map((q)=>q.serverName).join(", ")}`,Y)}if($.https){let q=J.length>0?null:await y$($);if(!q&&J.length===0){if(E("ssl",`No valid or trusted certificates found for ${X}, generating new ones`,$.verbose),await HY($),q=await y$($),!q)throw Error(`Failed to load SSL certificates after generation for ${X}`)}else E("ssl",`Using existing and trusted certificates for ${X}`,$.verbose);$._cachedSSLConfig=q}let U="proxies"in $&&Array.isArray($.proxies)?$.proxies.map((q)=>({...q,https:$.https,cleanup:$.cleanup,cleanUrls:q.cleanUrls??("cleanUrls"in $?$.cleanUrls:!1),vitePluginUsage:$.vitePluginUsage,changeOrigin:q.changeOrigin??$.changeOrigin,verbose:Y,_cachedSSLConfig:$._cachedSSLConfig})):[{from:"from"in $?$.from:"localhost:5173",to:"to"in $?$.to:"rpx.localhost",cleanUrls:"cleanUrls"in $?$.cleanUrls:!1,https:$.https,cleanup:$.cleanup,vitePluginUsage:$.vitePluginUsage,start:"start"in $?$.start:void 0,changeOrigin:$.changeOrigin,auth:"auth"in $?$.auth:void 0,verbose:Y,_cachedSSLConfig:$._cachedSSLConfig}],j=U.map((q)=>q.to||"rpx.localhost"),W=J.length>0?J:$._cachedSSLConfig??null,Q=j.filter((q)=>q&&!q.includes("localhost")&&!q.includes("127.0.0.1")),f=["dev","app","page","new","day","foo"],z=["test","localhost","local","example","invalid"],B=[...new Set(Q.map((q)=>q.split(".").pop()?.toLowerCase()))],G=B.filter((q)=>!!q&&f.includes(q));if(G.length>0&&Y)H.warn(`The following TLDs may not work reliably for local development: ${G.map((q)=>`.${q}`).join(", ")}`),H.info(" These TLDs have HSTS preloading which can bypass local DNS"),H.info(" Consider using reserved TLDs: .test, .localhost, or .local");if(A&&t.platform==="darwin"&&Q.length>0){let{setupDevelopmentDns:q}=await Promise.resolve().then(() => (gY(),Z8));if(await q({domains:Q,verbose:Y})){if(Y)if(B.every((O)=>!!O&&z.includes(O)))H.success(`DNS server started for ${B.map((O)=>`.${O}`).join(", ")} domains`);else H.success(`DNS server started for ${B.map((O)=>`.${O}`).join(", ")} domains (hosts file entries also added)`)}else E("dns","Could not start DNS server - custom domains may not resolve",Y)}let T=async()=>{E("cleanup","Starting cleanup handler",$.verbose);try{let{tearDownDevelopmentDns:q}=await Promise.resolve().then(() => (gY(),Z8));await q({verbose:$.verbose})}catch(q){E("cleanup",`Error stopping DNS server: ${q}`,$.verbose)}try{await r8.stopAll($.verbose)}catch(q){E("cleanup",`Error stopping processes: ${q}`,$.verbose)}await o8({domains:j,hosts:typeof $.cleanup==="boolean"?$.cleanup:$.cleanup?.hosts,certs:typeof $.cleanup==="boolean"?$.cleanup:$.cleanup?.certs,verbose:$.verbose||!1})};t.on("SIGINT",T),t.on("SIGTERM",T);let K=$.singlePortMode===!0,F=$.httpsPort??443,N=$.httpPort??80,R=$.originGuard?V6($.originGuard):null,L=!!W&&(U.length>1||K||J.length>0),V=!W&&K&&U.length>0;if(L&&W){E("proxies",`Creating shared HTTPS server for ${U.length} domains on port ${F}`,Y);let q=await S6(U,A,Y),M=null,C=$.onDemandTls,O=C?.enabled?new u1({config:C,certsDir:C.certsDir??$.productionCerts?.certsDir??_6.join(O6.homedir(),".stacks","rpx","on-demand-certs"),initial:J,verbose:Y,onCertAdded:(Ew)=>{if(kY()==="restart")E("on-demand","certificate installed; restarting supervised gateway to reload TLS",Y),setTimeout(()=>t.kill(t.pid,"SIGTERM"),10).unref();else Gw(Ew)}}):null,J0=null,u0=!1;async function Gw(Ew){if(J0=Ew,u0)return;u0=!0;try{while(J0){let kw=J0;if(J0=null,E("proxies",`rebuilding :${F} with ${kw.length} SNI cert(s)`,Y),M)nY.delete(M),M.stop(!1);let t0=!1;for(let _=0;!t0&&_<60;_++){let Cw=t8({routeEntries:q,listenPort:F,sslConfig:kw,originGuard:R,verbose:Y});if(Cw){M=Cw,t0=!0;break}await new Promise((n1)=>setTimeout(n1,Math.min(25*2**Math.min(_,4),500)))}if(!t0)H.error(`rpx: CRITICAL — could not rebind :${F} after cert issuance; HTTPS unbound until the next cert event or a gateway restart`)}}finally{u0=!1}}if(!await I$(N,"0.0.0.0",Y))k6(Y,N,F,$.acmeChallengeWebroot,O);if(await I$(F,"0.0.0.0",Y)){if(E("proxies",`Port ${F} is already in use, cannot start shared proxy`,Y),Y)H.warn(`Port ${F} is in use. Shared HTTPS proxy cannot start.`);return}let xw=O&&O.sniEntries().length>0?O.sniEntries():W;if(M=t8({routeEntries:q,listenPort:F,sslConfig:xw,originGuard:R,verbose:Y}),!M){H.error(`Shared HTTPS proxy failed to bind :${F}; not exiting`);return}}else if(V){E("proxies",`Creating shared HTTP server for ${U.length} domains on port ${N}`,Y);let q=await S6(U,A,Y);if(await I$(N,"0.0.0.0",Y)){if(E("proxies",`Port ${N} is already in use, cannot start shared proxy`,Y),Y)H.warn(`Port ${N} is in use. Shared HTTP proxy cannot start.`);return}if(!t8({routeEntries:q,listenPort:N,sslConfig:null,originGuard:R,verbose:Y})){H.error(`Shared HTTP proxy failed to bind :${N}; not exiting`);return}}else for(let q of U)try{let M=q.to||"rpx.localhost";E("proxy",`Starting proxy for ${M} with SSL config: ${!!W}`,q.verbose),await x6({from:q.from||"localhost:5173",to:M,cleanUrls:q.cleanUrls||!1,https:q.https||!1,cleanup:q.cleanup||!1,vitePluginUsage:q.vitePluginUsage||!1,verbose:q.verbose||!1,_cachedSSLConfig:$._cachedSSLConfig,changeOrigin:q.changeOrigin||!1,loadBalancer:q.loadBalancer,auth:q.auth,path:q.path,pathRewrites:q.pathRewrites})}catch(M){E("proxies",`Failed to start proxy for ${q.to}: ${M}`,q.verbose),H.error(`Failed to start proxy for ${q.to}:`,M)}}async function S6(w,$,Y){let A=[],X=new Set;for(let J of w){let U=J.to||"rpx.localhost",j=J.cleanUrls||!1,W=J.path,Q=X$(W),f=VY(J.auth);if(J.redirect){let z=V2(J.redirect);A.push({host:U,path:W,route:{redirect:z,basePath:Q,auth:f}}),E("proxies",`Route: ${U}${W??""} → redirect ${z.status} ${z.to}${f?" (auth)":""}`,Y)}else if(J.static)A.push({host:U,path:W,route:{static:q8(J.static,j),cleanUrls:j,basePath:Q,auth:f}}),E("proxies",`Route: ${U}${W??""} → static ${typeof J.static==="string"?J.static:J.static.dir}${f?" (auth)":""}`,Y);else{let z=u$(J.from),B=new URL(z.startsWith("http")?z:`http://${z}`),G=MY(J.from??B.host,J.loadBalancer);IY(G),a8.add(G),A.push({host:U,path:W,route:{sourceHost:B.host,upstreamPool:G,cleanUrls:j,changeOrigin:J.changeOrigin||!1,pathRewrites:J.pathRewrites,basePath:Q,auth:f}}),E("proxies",`Route: ${U}${W??""} → ${B.host}${f?" (auth)":""}`,Y)}if(X.has(U))continue;if(X.add(U),$&&!j1(U)&&!U.includes("localhost")&&!U.includes("127.0.0.1"))try{if(!(await uY([U],Y))[0])await bY([U],Y)}catch{E("hosts",`Could not add hosts entry for ${U}`,Y)}}return A}function t8(w){let{routeEntries:$,listenPort:Y,sslConfig:A,originGuard:X,verbose:J}=w,U=OY($),j=SY((f,z)=>_Y(U,f,z),J),W=X?(f,z)=>X(f)??j(f,z):j,Q=hY(J);try{let f=Bun.serve({port:Y,hostname:"0.0.0.0",reusePort:NY(),...A?{tls:v$(Array.isArray(A)?A.map((z)=>({serverName:z.serverName,key:z.key,cert:z.cert})):{key:A.key,cert:A.cert,ca:A.ca,requestCert:!1,rejectUnauthorized:!1})}:{},fetch(z,B){return W(z,B)},websocket:Q,error(z){return E("server",`Shared proxy server error: ${z}`,J),new Response(`Server Error: ${z.message}`,{status:500})}});return nY.add(f),E("proxies",`Shared ${A?"HTTPS":"HTTP"} proxy listening on port ${Y} for ${U.size} domains`,J),f}catch(f){return E("proxies",`Failed to start shared proxy: ${f}`,J),console.error("Failed to start shared proxy:",f),null}}function xR(w){if(w?.vitePluginUsage||!w?.verbose)return;if(console.log(""),console.log(` ${Ow.green(Ow.bold("rpx"))} ${Ow.green(`v${dY}`)}`),console.log(` ${Ow.green("➜")} ${Ow.dim(w?.from??"")} ${Ow.dim("➜")} ${Ow.cyan(w?.ssl?`https://${w?.to}`:`http://${w?.to}`)}`),w?.listenPort!==(w?.ssl?443:80))console.log(` ${Ow.green("➜")} Listening on port ${w?.listenPort}`);if(w?.cleanUrls)console.log(` ${Ow.green("➜")} Clean URLs enabled`)}z0();P0.chdir(ZR(kR(PR(import.meta.url)),".."));var b0=new fJ("rpx");b0.command("start","Start the Reverse Proxy Server").option("--from <from>","The URL to proxy from").option("--to <to>","The URL to proxy to").option("--key-path <path>","Absolute path to the SSL key").option("--cert-path <path>","Absolute path to the SSL certificate").option("--ca-cert-path <path>","Absolute path to the SSL CA certificate").option("--hosts-cleanup","Cleanup /etc/hosts on exit").option("--certs-cleanup","Cleanup SSL certificates on exit").option("--start-command <command>","Command to start the dev server").option("--start-cwd <path>","Current working directory for the dev server").option("--start-env <env>","Environment variables for the dev server").option("--change-origin","Change the origin of the host header to the target URL").option("--single-port-mode","Route all proxies through one shared listener (by Host header) instead of a port per proxy").option("--http-port <port>","Port for the shared HTTP listener / redirect (default: 80)").option("--https-port <port>","Port for the shared HTTPS listener (default: 443)").option("--via-daemon","Route through the shared rpx daemon instead of binding :443 directly").option("--id <id>","Stable id used when registering with the daemon (auto-derived from --to)").option("--verbose","Enable verbose logging").example("rpx start --from localhost:5173 --to my-project.localhost").example("rpx start --from localhost:3000 --to my-project.localhost/api").example("rpx start --from localhost:3000 --to localhost:3001").example("rpx start --from localhost:5173 --to my-project.test --key-path /absolute/path/to/key --cert-path /absolute/path/to/cert").example("rpx start --from localhost:5173 --to my-project.localhost --change-origin").example("rpx start --single-port-mode --https-port 8443").action(async(w)=>{if(!w?.from||!w.to)return P6(mw);let $={from:w.from,to:w.to,https:{keyPath:w.keyPath,certPath:w.certPath,caCertPath:w.caCertPath},cleanup:{certs:w.certsCleanup||!1,hosts:w.hostsCleanup||!1},verbose:w.verbose||!1,changeOrigin:w.changeOrigin||!1,singlePortMode:w.singlePortMode||!1,httpPort:w.httpPort!==void 0?Number(w.httpPort):void 0,httpsPort:w.httpsPort!==void 0?Number(w.httpsPort):void 0,viaDaemon:w.viaDaemon||!1,id:w.id};if(w.startCommand){let Y={command:w.startCommand};if(w.startCwd)Y.cwd=w.startCwd;if(w.startEnv)try{Y.env=JSON.parse(w.startEnv)}catch(A){console.error("Failed to parse start-env JSON:",A),P0.exit(1)}$.start=Y}return Z6($)});b0.command("watch:start <proxy>","Start the dev server for a specific proxy").option("--verbose","Enable verbose logging").action(async(w,$)=>{let Y=z8(mw)?mw.proxies.find((A)=>A.to===w||`${A.from}-${A.to}`===w):mw.to===w?mw:null;if(!Y?.start)console.error(`No watch configuration found for proxy: ${w}`),P0.exit(1);try{await d8.startProcess(w,Y.start,$.verbose),console.log(`Started dev server for ${w}`)}catch(A){console.error(`Failed to start dev server for ${w}:`,A),P0.exit(1)}});b0.command("watch:stop <proxy>","Stop the dev server for a specific proxy").option("--verbose","Enable verbose logging").action(async(w,$)=>{try{await d8.stopProcess(w,$.verbose),console.log(`Stopped dev server for ${w}`)}catch(Y){console.error(`Failed to stop dev server for ${w}:`,Y),P0.exit(1)}});b0.command("watch:stopall","Stop all running dev servers").option("--verbose","Enable verbose logging").action(async(w)=>{try{await d8.stopAll(w.verbose),console.log("Stopped all dev servers")}catch($){console.error("Failed to stop all dev servers:",$),P0.exit(1)}});async function i6(w){let Y={...(await Pf().catch(()=>null))?.onDemand??{},enabled:!0};if(w){let A=w.split(",").map((X)=>X.trim()).filter(Boolean);if(A.length>0)Y.roots=A}return Y}b0.command("daemon:start","Start the rpx daemon in the foreground (binds :443 + :80)").option("--rpx-dir <path>","Override the rpx state dir (default ~/.stacks/rpx)").option("--registry-dir <path>","Override the registry dir (default <rpx-dir>/registry.d)").option("--https-port <port>","HTTPS port (default 443)",{default:443}).option("--http-port <port>","HTTP redirect port; 0 to disable (default 80)",{default:80}).option("--hostname <host>","Bind address (default 0.0.0.0)",{default:"0.0.0.0"}).option("--certs-dir <path>","Directory of real PEM certs for per-domain SNI (<domain>.crt/.key, _wildcard.<apex>.crt/.key)").option("--workers <n>","Run as a multi-core cluster of N worker processes (default 1; also RPX_WORKERS)").option("--on-demand","Lazily boot a project's dev server the first time its host is visited (reads config `onDemand`)").option("--roots <dirs>","Comma-separated roots scanned for on-demand sites (overrides config; default ~/Code)").option("--verbose","Enable verbose logging").example("rpx daemon:start --on-demand").example("rpx daemon:start --on-demand --roots ~/Code,~/work").action(async(w)=>{try{let $=w.onDemand?await i6(w.roots):void 0;await(await R6({rpxDir:w.rpxDir,registryDir:w.registryDir,httpsPort:typeof w.httpsPort==="string"?Number.parseInt(w.httpsPort,10):w.httpsPort,httpPort:typeof w.httpPort==="string"?Number.parseInt(w.httpPort,10):w.httpPort,hostname:w.hostname,productionCerts:w.certsDir?{certsDir:w.certsDir}:void 0,workers:w.workers===void 0?void 0:typeof w.workers==="string"?Number.parseInt(w.workers,10):w.workers,onDemandSites:$,verbose:w.verbose??!0})).done,P0.exit(0)}catch($){console.error(`Failed to start rpx daemon: ${$.message}`),P0.exit(1)}});b0.command("daemon:stop","Stop the running rpx daemon (SIGTERM, escalates to SIGKILL)").option("--rpx-dir <path>","Override the rpx state dir (default ~/.stacks/rpx)").option("--timeout <ms>","Max ms to wait for graceful shutdown",{default:5000}).option("--no-force","Do not escalate to SIGKILL after timeout").option("--verbose","Enable verbose logging").action(async(w)=>{let $=typeof w.timeout==="string"?Number.parseInt(w.timeout,10):w.timeout,Y=await M6({rpxDir:w.rpxDir,timeoutMs:$,forceAfterTimeout:w.force!==!1,verbose:w.verbose});if(!Y.stopped&&Y.pid===null){console.log("rpx daemon is not running");return}if(!Y.stopped){console.log(`rpx daemon pid=${Y.pid} was already gone (cleaned stale lock)`);return}console.log(`rpx daemon pid=${Y.pid} stopped${Y.forced?" (SIGKILL)":""}`)});b0.command("daemon:status","Print daemon state and currently registered hosts").option("--rpx-dir <path>","Override the rpx state dir (default ~/.stacks/rpx)").option("--registry-dir <path>","Override the registry dir (default <rpx-dir>/registry.d)").option("--json","Emit machine-readable JSON instead of a human summary").action(async(w)=>{let $=w.rpxDir??V0(),Y=await L$($),A=await c8($),X=w.registryDir,J=await Bw(X).catch(()=>[]);if(w.json){console.log(JSON.stringify({running:A,pid:Y,pidFile:S$($),rpxDir:$,registryDir:X??$w(),entries:J},null,2));return}if(!A){if(console.log("rpx daemon: not running"),Y!==null)console.log(`(stale pid file at ${S$($)} → pid ${Y})`)}else console.log(`rpx daemon: running (pid=${Y})`),console.log(`pid file: ${S$($)}`);if(console.log(`registry: ${X??$w()}`),J.length===0){console.log("no registered hosts");return}console.log(`registered hosts (${J.length}):`);for(let U of J){let j=U.pid!==void 0?`, pid=${U.pid}`:"";console.log(` https://${U.to} → ${U.from} (id=${U.id}${j})`)}});b0.command("register","Register an upstream app with the rpx daemon").option("--id <id>","Unique id for this entry (a-z, 0-9, dot, dash, underscore)").option("--from <host:port>","Upstream host:port (e.g. localhost:5173)").option("--to <host>","Public hostname (e.g. pet-store.localhost)").option("--cwd <path>","Working directory of the upstream (informational)").option("--clean-urls","Strip .html and 301 to the clean URL").option("--change-origin","Rewrite Origin to the upstream").option("--rpx-dir <path>","Override the rpx state dir (default ~/.stacks/rpx)").option("--registry-dir <path>","Override the registry dir (default <rpx-dir>/registry.d)").option("--skip-spawn","Do not lazy-spawn the daemon if it is not already running").option("--verbose","Enable verbose logging").example("rpx register --id pet-store --from localhost:5173 --to pet-store.localhost").action(async(w)=>{if(!w.id||!w.from||!w.to)console.error("rpx register requires --id, --from, and --to"),P0.exit(1);if(!C$(w.id))console.error(`invalid id: ${JSON.stringify(w.id)} (must match /^[a-zA-Z0-9._-]+$/, ≤128 chars)`),P0.exit(1);try{await c1({id:w.id,from:w.from,to:w.to,cwd:w.cwd,createdAt:new Date().toISOString(),cleanUrls:w.cleanUrls,changeOrigin:w.changeOrigin},w.registryDir,w.verbose)}catch($){console.error(`failed to write registry entry: ${$.message}`),P0.exit(1)}if(w.skipSpawn){console.log(`registered ${w.to} → ${w.from} (daemon spawn skipped)`);return}try{let $=await v8({rpxDir:w.rpxDir,verbose:w.verbose}),Y=$.spawned?"spawned":"attached to";console.log(`registered https://${w.to} → ${w.from} (${Y} daemon pid=${$.pid})`)}catch($){console.error(`registered entry but daemon spawn failed: ${$.message}`),console.error("the entry remains in the registry; start the daemon manually with `rpx daemon:start`"),P0.exit(1)}});b0.command("unregister <id>","Remove a previously registered app from the rpx daemon").option("--registry-dir <path>","Override the registry dir (default ~/.stacks/rpx/registry.d)").option("--verbose","Enable verbose logging").action(async(w,$)=>{if(!C$(w))console.error(`invalid id: ${JSON.stringify(w)}`),P0.exit(1);let Y=await yj(w,$.registryDir,$.verbose);if(!Y){console.log(`no registry entry for id=${w}`);return}await m$(w,$.registryDir,$.verbose),console.log(`unregistered ${Y.to} (id=${w})`)});b0.command("dns:reconcile","Remove stale macOS DNS overrides left by crashed dev sessions").option("--rpx-dir <path>","Override the rpx state dir (default ~/.stacks/rpx)").option("--verbose","Enable verbose logging").action(async(w)=>{let $=w.rpxDir??V0();if(await c8($)){console.log("rpx daemon is running — DNS overrides are managed by the daemon");return}await y6({rpxDir:$,verbose:w.verbose}),console.log("DNS reconcile complete")});b0.command("sites","List the on-demand sites rpx can boot (and which are currently live)").option("--roots <dirs>","Comma-separated roots to scan (overrides config; default ~/Code)").option("--registry-dir <path>","Override the registry dir (default ~/.stacks/rpx/registry.d)").option("--json","Emit machine-readable JSON").action(async(w)=>{let $=await i6(w.roots),Y=gf($),A=await Bw(w.registryDir).catch(()=>[]),X=new Set(A.map((j)=>j.to)),J=Y.map((j)=>({host:j.host,dir:j.dir,command:j.command,source:j.source,live:X.has(j.host)}));if(w.json){console.log(JSON.stringify(J,null,2));return}let U=$.roots??["~/Code"];if(J.length===0){console.log("no on-demand sites found"),console.log(`scanned roots: ${U.join(", ")}`),console.log("start the daemon with `rpx daemon:start --on-demand` to serve them");return}console.log(`on-demand sites (${J.length}) — scanned ${U.join(", ")}:`);for(let j of J)console.log(` ${j.live?"●":"○"} https://${j.host} ${j.live?"(live)":"(idle)"}`),console.log(` ${j.command} ${j.dir}`)});b0.command("logs <host>","Show an on-demand site's boot log (e.g. rpx logs myapp.localhost)").option("--rpx-dir <path>","Override the rpx state dir (default ~/.stacks/rpx)").option("--lines <n>","Show only the last N lines (default: all)").action(async(w,$)=>{let{readFile:Y}=await import("node:fs/promises"),{join:A}=await import("node:path"),X=$.rpxDir??V0(),J=A(X,"sites",`${iA(w.split(":")[0])}.log`),U;try{U=await Y(J,"utf8")}catch{console.log(`no log for ${w} (it may not have been booted yet) — looked in ${J}`);return}let j=$.lines===void 0?void 0:typeof $.lines==="string"?Number.parseInt($.lines,10):$.lines,W=U.split(`
|
|
336
|
+
`,U=(W)=>{let Q=l8(W);return Y.has(Q)||A.some((f)=>$Y(Q,f))},j=(W)=>{let Q=VR(W);if(!U(Q))return;let f="/";try{f=new URL(W.url).pathname}catch{}if(X.some((z)=>f.startsWith(z)))return;if(IR(W.headers.get($),w.value))return;return new Response(J,{status:403,headers:{"content-type":"text/plain"}})};return j.protects=U,j}Gj();Ej();F8();M8();Tj();Nj();H8();z0();var L6={name:"@stacksjs/rpx",type:"module",version:"0.11.39",description:"A modern and smart reverse proxy.",author:"Chris Breuer <chris@stacksjs.org>",license:"MIT",homepage:"https://github.com/stacksjs/rpx",repository:{type:"git",url:"git+https://github.com/stacksjs/rpx.git"},bugs:{url:"https://github.com/stacksjs/rpx/issues"},keywords:["reverse proxy","ssl","development","environment","proxy","bun","stacks","typescript","javascript"],exports:{".":{types:"./dist/index.d.ts",bun:"./dist/index.js",import:"./dist/index.js"}},module:"./dist/index.js",types:"./dist/index.d.ts",bin:{rpx:"./dist/bin/cli.js","reverse-proxy":"./dist/bin/cli.js"},files:["README.md","dist"],scripts:{build:"bun build.ts && bun build ./bin/cli.ts --compile --minify --outfile bin/rpx",compile:"bun build ./bin/cli.ts --compile --minify --outfile bin/rpx","compile:all":"bun run compile:linux-x64 && bun run compile:linux-arm64 && bun run compile:windows-x64 && bun run compile:darwin-x64 && bun run compile:darwin-arm64","compile:linux-x64":"bun build ./bin/cli.ts --compile --minify --target=bun-linux-x64 --outfile bin/rpx-linux-x64","compile:linux-arm64":"bun build ./bin/cli.ts --compile --minify --target=bun-linux-arm64 --outfile bin/rpx-linux-arm64","compile:windows-x64":"bun build ./bin/cli.ts --compile --minify --target=bun-windows-x64 --outfile bin/rpx-windows-x64.exe","compile:darwin-x64":"bun build ./bin/cli.ts --compile --minify --target=bun-darwin-x64 --outfile bin/rpx-darwin-x64","compile:darwin-arm64":"bun build ./bin/cli.ts --compile --minify --target=bun-darwin-arm64 --outfile bin/rpx-darwin-arm64",bench:"bun run bench/run.ts","bench:html":"bun run bench/run.ts --html","bench:latency":"bun run bench/run.ts --latency","bench:throughput":"bun run bench/run.ts --throughput",lint:"bunx --bun pickier .","lint:fix":"bunx --bun pickier . --fix",fresh:"bunx rimraf node_modules/ bun.lock && bun i",changelog:"changelogen --output CHANGELOG.md",prepublishOnly:"bun build.ts","release:binaries":"bun run compile:all && bun run zip",test:"bun test",typecheck:"bunx tsc --noEmit",zip:"bun run zip:all","zip:all":"bun run zip:linux-x64 && bun run zip:linux-arm64 && bun run zip:windows-x64 && bun run zip:darwin-x64 && bun run zip:darwin-arm64","zip:linux-x64":"zip -j bin/rpx-linux-x64.zip bin/rpx-linux-x64","zip:linux-arm64":"zip -j bin/rpx-linux-arm64.zip bin/rpx-linux-arm64","zip:windows-x64":"zip -j bin/rpx-windows-x64.zip bin/rpx-windows-x64.exe","zip:darwin-x64":"zip -j bin/rpx-darwin-x64.zip bin/rpx-darwin-x64","zip:darwin-arm64":"zip -j bin/rpx-darwin-arm64.zip bin/rpx-darwin-arm64"},dependencies:{"@stacksjs/clapp":"^0.2.10","@stacksjs/tlsx":"^0.13.13"},devDependencies:{bunfig:"^0.15.6",mitata:"^1.0.34",typescript:"^7.0.2"},"simple-git-hooks":{"pre-commit":"bunx lint-staged"},"lint-staged":{"*.{js,ts}":"bunx --bun pickier . --fix"}};var dY=L6.version;var r8=new g8,hR=new C8("0.0.0.0"),nY=new Set,a8=new Set,gj=!1,p8=null,dj=null;async function o8(w){if(gj)return E("cleanup","Cleanup already in progress, skipping",w?.verbose),dj||Promise.resolve();gj=!0,E("cleanup","Starting cleanup process",w?.verbose),dj=new Promise(($)=>{p8=$});try{await r8.stopAll(w?.verbose),H.info("Shutting down proxy servers...");let $=[],Y=Array.from(nY).map((A)=>new Promise((X)=>{let J=A;try{if(typeof J.stop==="function")J.stop(!0),E("cleanup","Bun server stopped",w?.verbose),X();else if(typeof J.close==="function")J.close(()=>{E("cleanup","Server closed successfully",w?.verbose),X()});else X()}catch(U){E("cleanup",`Error stopping server: ${U}`,w?.verbose),X()}}));$.push(...Y),nY.clear();for(let A of a8)b1(A);if(a8.clear(),w?.hosts&&w.domains?.length){E("cleanup","Cleaning up hosts file entries",w?.verbose),E("cleanup",`Original domains for cleanup: ${JSON.stringify(w.domains)}`,w?.verbose);let A=w.domains.filter((X)=>{if(X==="test.local")return!0;return X!=="localhost"&&!X.startsWith("localhost.")&&X!=="127.0.0.1"});if(E("cleanup",`Filtered domains for cleanup: ${JSON.stringify(A)}`,w?.verbose),A.length>0)H.info("Cleaning up hosts file entries..."),$.push(a2(A,w?.verbose).then(()=>{E("cleanup",`Removed hosts entries for ${A.join(", ")}`,w?.verbose)}).catch((X)=>{E("cleanup",`Failed to remove hosts entries: ${X}`,w?.verbose),H.warn(`Failed to clean up hosts file entries for ${A.join(", ")}:`,X)}))}if(w?.certs&&w.domains?.length){E("cleanup","Cleaning up SSL certificates",w?.verbose),H.info("Cleaning up SSL certificates...");let A=w.domains.map(async(X)=>{try{await f2(X,w?.verbose),E("cleanup",`Removed certificates for ${X}`,w?.verbose)}catch(J){E("cleanup",`Failed to remove certificates for ${X}: ${J}`,w?.verbose),H.warn(`Failed to clean up certificates for ${X}:`,J)}});$.push(...A)}await Promise.allSettled($),E("cleanup","All cleanup tasks completed successfully",w?.verbose),H.success("All cleanup tasks completed successfully")}catch($){E("cleanup",`Error during cleanup: ${$}`,w?.verbose),H.error("Error during cleanup:",$)}finally{if(p8)p8();p8=null,gj=!1;let $=w&&"vitePluginUsage"in w&&w.vitePluginUsage===!0;if(t.env.NODE_ENV!=="test"&&t.env.BUN_ENV!=="test"&&!$)t.exit(0)}return dj}var nj=!1;function D6(w){if(nj){E("signal",`Received second ${w} signal, forcing exit`,!0),t.exit(1);return}nj=!0,E("signal",`Received ${w} signal, initiating cleanup`,!0),o8().catch(($)=>{E("signal",`Cleanup failed after ${w}: ${$}`,!0),t.exit(1)}).finally(()=>{nj=!1})}t.once("SIGINT",()=>D6("SIGINT"));t.once("SIGTERM",()=>D6("SIGTERM"));t.on("uncaughtException",(w)=>{H.error("Uncaught exception (continuing):",w)});t.on("unhandledRejection",(w)=>{H.error("Unhandled rejection (continuing):",w)});async function lY(w,$,Y,A=5){E("connection",`Testing connection to ${w}:${$} (retries left: ${A})`,Y);let X=15000,J=Date.now();if(t.env.RPX_BYPASS_CONNECTION_TEST==="true"){E("connection",`Bypassing connection test for ${w}:${$} due to RPX_BYPASS_CONNECTION_TEST flag`,Y);return}let U=()=>new Promise((j,W)=>{let Q=h6.connect({host:w,port:$,timeout:3000});Q.once("connect",()=>{E("connection",`Successfully connected to ${w}:${$}`,Y),Q.end(),j()}),Q.once("timeout",()=>{E("connection",`Connection to ${w}:${$} timed out`,Y),Q.destroy(),W(Error("Connection timed out"))}),Q.once("error",(f)=>{E("connection",`Failed to connect to ${w}:${$}: ${f}`,Y),Q.destroy(),W(f)})});try{await U()}catch(j){let W=j;if(Date.now()-J>X){E("connection",`Connection test timed out after ${X}ms, but continuing anyway`,Y),H.warn(`Connection test to ${w}:${$} timed out, but RPX will try to proceed anyway.`);return}if(W.code==="ECONNREFUSED"&&A>0)return E("connection",`Connection refused, server might be starting up. Retrying in 2 seconds... (${A} retries left)`,Y),await new Promise((f)=>setTimeout(f,2000)),lY(w,$,Y,A-1);if(A>0)try{E("connection",`Trying HTTP request to ${w}:${$}`,Y),await new Promise((f,z)=>{let B=s8.request({hostname:w,port:$,path:"/",method:"HEAD",timeout:5000},(G)=>{E("connection",`Received HTTP response with status: ${G.statusCode}`,Y),f()});B.on("error",(G)=>z(G)),B.on("timeout",()=>{B.destroy(),z(Error("HTTP request timed out"))}),B.end()}),E("connection",`HTTP request to ${w}:${$} succeeded`,Y);return}catch(f){return E("connection",`HTTP request to ${w}:${$} failed: ${f}`,Y),E("connection",`Retrying socket connection in 2 seconds... (${A} retries left)`,Y),await new Promise((z)=>setTimeout(z,2000)),lY(w,$,Y,A-1)}let Q=`Failed to connect to ${w}:${$} after ${5-A} attempts: ${W.message}`;E("connection",`${Q}. To bypass this check set RPX_BYPASS_CONNECTION_TEST=true`,Y),H.warn(Q),H.warn("RPX will try to continue anyway. If you're sure this is correct, you can set RPX_BYPASS_CONNECTION_TEST=true to skip this check.")}}async function x6(w){E("server",`Starting server with options: ${A$(w)}`,w.verbose);let $=u$(w.from),Y=new URL($.startsWith("http")?$:`http://${$}`),A=new URL((w.to?.startsWith("http")?w.to:`http://${w.to}`)||"rpx.localhost"),X=Number.parseInt(Y.port)||(Y.protocol.includes("https:")?443:80),J=[A.hostname];if(lj(w)&&!A.hostname.includes("localhost")&&!A.hostname.includes("127.0.0.1")){E("hosts",`Checking if hosts file entry exists for: ${A.hostname}`,w?.verbose);try{if(!(await uY(J,w.verbose))[0]){H.info(`Adding ${A.hostname} to hosts file...`),H.info("This may require sudo/administrator privileges");try{await bY(J,w.verbose)}catch(W){if(H.error("Failed to add hosts entry:",W.message),H.warn("You can manually add this entry to your hosts file:"),H.warn(`127.0.0.1 ${A.hostname}`),H.warn(`::1 ${A.hostname}`),t.platform==="win32")H.warn("On Windows:"),H.warn("1. Run notepad as administrator"),H.warn("2. Open C:\\Windows\\System32\\drivers\\etc\\hosts");else H.warn("On Unix systems:"),H.warn("sudo nano /etc/hosts")}}else E("hosts",`Host entry already exists for ${A.hostname}`,w.verbose)}catch(j){H.error("Failed to check hosts file:",j.message)}}try{await lY(Y.hostname,X,w.verbose)}catch(j){E("server",`Connection test failed: ${j}`,w.verbose),H.error(j.message),H.warn("Continuing with proxy setup despite connection test failure..."),H.info("If you need to bypass connection testing, set environment variable RPX_BYPASS_CONNECTION_TEST=true")}let U=w._cachedSSLConfig||null;if(w.https)try{if(w.https===!0)w.https=RY({...w,to:A.hostname});if(U=await y$({...w,to:A.hostname,https:w.https}),!U){if(E("ssl",`Generating new certificates for ${A.hostname}`,w.verbose),await HY({...w,from:Y.toString(),to:A.hostname,https:w.https}),U=await y$({...w,to:A.hostname,https:w.https}),!U)throw Error(`Failed to load SSL configuration after generating certificates for ${A.hostname}`)}}catch(j){throw E("server",`SSL setup failed: ${j}`,w.verbose),j}E("server",`Setting up reverse proxy with SSL config for ${A.hostname}`,w.verbose),await _R({...w,from:$,originalFrom:w.from||$,to:A.hostname,fromPort:X,sourceUrl:{hostname:Y.hostname,host:Y.host},ssl:U})}async function OR(w,$,Y,A,X,J,U,j,W,Q,f,z){E("proxy",`Creating proxy server ${w} -> ${$} with cleanUrls: ${j}`,U);let B=MY(f??A.host,z);IY(B);let G=[{host:$,route:{sourceHost:A.host,upstreamPool:B,cleanUrls:j||!1,changeOrigin:W||!1,basePath:"/",auth:Q}}];if(!t8({routeEntries:G,listenPort:Y,sslConfig:X,originGuard:null,verbose:U??!1}))throw b1(B),Error(`Failed to start proxy server for ${$} on port ${Y}`);a8.add(B),xR({from:w,to:$,vitePluginUsage:J,listenPort:Y,ssl:!!X,cleanUrls:j,verbose:U})}async function _R(w){E("setup",`Setting up reverse proxy: ${A$(w)}`,w.verbose);let{from:$,originalFrom:Y,to:A,sourceUrl:X,ssl:J,verbose:U,cleanup:j,vitePluginUsage:W,changeOrigin:Q,cleanUrls:f}=w,z=80,B=443,G="0.0.0.0",T=w.portManager||hR,K=lj(w);try{if(K&&A&&!A.includes("localhost")&&!A.includes("127.0.0.1")){if(!(await uY([A],U))[0]){H.warn(`The hostname ${A} isn't in your hosts file. Adding it now...`);try{await bY([A],U),H.success(`Added ${A} to your hosts file.`)}catch(V){H.error(`Failed to add ${A} to your hosts file: ${V}`),H.info(`You may need to manually add '127.0.0.1 ${A}' to your /etc/hosts file.`)}}}else if(K&&t.platform!=="darwin"&&A&&A.includes("localhost")&&!A.match(/^(localhost|127\.0\.0\.1)$/)){if(!(await uY([A],U))[0]){E("hosts",`${A} not found in hosts file, adding...`,U);try{await bY([A],U)}catch(V){E("hosts",`Failed to add ${A} to hosts file: ${V}`,U)}}}if(J&&!T.usedPorts.has(z)){if(!await I$(z,G,U))E("setup","Starting HTTP redirect server",U),k6(U),T.usedPorts.add(z);else if(E("setup","Port 80 is in use, skipping HTTP redirect",U),U)H.warn("Port 80 is in use, HTTP to HTTPS redirect will not be available")}let F=J?B:z,N=await I$(F,G,U),R;if(N){if(E("setup",`Port ${F} is already in use`,U),U)H.warn(`Port ${F} is already in use. This may be another instance of rpx or another service.`);if(F===443){if(R=await T.getNextAvailablePort(3443,!0),E("setup",`Using port ${R} instead of ${F}`,U),U)H.info(`Using port ${R} instead. Access your site at https://${A}:${R}`)}else if(R=await T.getNextAvailablePort(F+1000,!0),E("setup",`Using port ${R} instead of ${F}`,U),U)H.info(`Using port ${R} instead. Access your site at http://${A}:${R}`)}else R=F,T.usedPorts.add(R),E("setup",`Using standard ${F===443?"HTTPS":"HTTP"} port ${F} for ${A}`,U);await OR($,A,R,X,J,W,U,f,Q,VY(w.auth),Y,w.loadBalancer)}catch(F){E("setup",`Setup failed: ${F}`,U),H.error(`Failed to setup reverse proxy: ${F.message}`),o8({domains:[A],hosts:typeof j==="boolean"?j:j?.hosts,certs:typeof j==="boolean"?j:j?.certs,verbose:U,vitePluginUsage:W})}}function k6(w,$=80,Y=443,A,X){E("redirect",`Starting HTTP redirect server on port ${$}`,w);let J=s8.createServer((U,j)=>{let W=U.url?U.url.split("?",1)[0]:"";if(W.startsWith("/.well-known/acme-challenge/")){if(X){let B=X.challengeStore.handlePath(W);if(B!==void 0){E("redirect",`Serving on-demand ACME challenge ${W}`,w),j.writeHead(200,{"content-type":"text/plain"}),j.end(B);return}}if(A){let B=R8(A,W);if(B!=null){E("redirect",`Serving ACME challenge ${W}`,w),j.writeHead(200,{"content-type":"text/plain"}),j.end(B);return}}if(X){j.writeHead(404,{"content-type":"text/plain"}),j.end("challenge not found");return}}let Q=U.headers.host||"",f=Q.includes(":")?Q.slice(0,Q.indexOf(":")):Q;if(X&&f&&!X.hasCert(f))X.ensureCert(f).catch(()=>{});let z=Y===443?f:`${f}:${Y}`;E("redirect",`Redirecting request from ${Q}${U.url} to https://${z}`,w),j.writeHead(301,{Location:`https://${z}${U.url}`}),j.end()}).listen($);nY.add(J),E("redirect","HTTP redirect server started",w)}function Z6(w){let $={...mw,...w};if(E("proxy",`Starting proxy with options: ${A$($)}`,$?.verbose),$.viaDaemon){if(!$.from||!$.to){H.error("viaDaemon mode requires both `from` and `to`");return}mj({proxies:[{id:$.id,from:$.from,to:$.to,path:$.path,cleanUrls:$.cleanUrls,changeOrigin:$.changeOrigin,pathRewrites:$.pathRewrites}],verbose:$.verbose}).catch((W)=>{H.error(`Failed to register with rpx daemon: ${W.message}`),t.exit(1)});return}let Y=$.to||"",A=Y.split(".").pop()?.toLowerCase()||"",X=t.platform==="darwin"&&Y&&!Y.includes("localhost")&&!Y.includes("127.0.0.1"),J=["dev","app","page","new","day","foo"],U=["test","localhost","local","example","invalid"];if(X&&J.includes(A)&&$?.verbose)H.warn(`The .${A} TLD may not work reliably for local development`),H.info(` Google owns .${A} with HSTS preloading, which can bypass local DNS`),H.info(" Consider using a reserved TLD: .test, .localhost, or .local");if(X)Promise.resolve().then(() => (gY(),Z8)).then(({setupDevelopmentDns:W})=>{W({domains:[Y],verbose:$.verbose}).then((Q)=>{if(Q)Promise.resolve().then(()=>{if($.verbose)if(U.includes(A))H.success(`DNS server started for .${A} domains`);else H.success(`DNS server started for .${A} domains (hosts file entry also added)`)});else E("dns",`Could not start DNS server - ${Y} may not resolve in browser`,$.verbose)})}).catch((W)=>{E("dns",`Failed to start DNS server: ${W}`,$.verbose)});let j={from:$.from,to:$.to,cleanUrls:$.cleanUrls,https:RY($),cleanup:$.cleanup,vitePluginUsage:$.vitePluginUsage,changeOrigin:$.changeOrigin,verbose:$.verbose,regenerateUntrustedCerts:$.regenerateUntrustedCerts};E("proxy",`Server options: ${A$(j)}`,$.verbose),x6(j).catch((W)=>{E("proxy",`Failed to start proxy: ${W}`,$.verbose),H.error(`Failed to start proxy: ${W.message}`),o8({domains:[$.to],hosts:typeof $.cleanup==="boolean"?$.cleanup:$.cleanup?.hosts,certs:typeof $.cleanup==="boolean"?$.cleanup:$.cleanup?.certs,verbose:$.verbose})})}function DR(w){return w?.verbose||!1}function lj(w){if(w?.hostsManagement===!1)return!1;let $=w?.cleanup;if($===!1)return!1;if($&&typeof $==="object"&&$.hosts===!1)return!1;return!0}async function P6(w){let $={from:"localhost:5173",to:"rpx.localhost",https:!1,cleanup:{hosts:!0,certs:!1},vitePluginUsage:!1,verbose:!1,cleanUrls:!1,changeOrigin:!1,regenerateUntrustedCerts:!0};if(w)$={...$,...w};let Y=DR($),A=lj($);if(E("config",`Starting with config: ${A$($,2)}`,Y),E("config",`Is multi-proxy? ${"proxies"in $}`,Y),E("config",`Hosts management enabled? ${A}`,Y),$.viaDaemon){let M="proxies"in $&&Array.isArray($.proxies)?$.proxies.map((C)=>({id:C.id,from:C.from,to:C.to,path:C.path,cleanUrls:C.cleanUrls??$.cleanUrls,changeOrigin:C.changeOrigin??$.changeOrigin,pathRewrites:C.pathRewrites})):[{id:$.id,from:$.from,to:$.to??"rpx.localhost",path:$.path,cleanUrls:$.cleanUrls,changeOrigin:$.changeOrigin,pathRewrites:$.pathRewrites}];await mj({proxies:M,verbose:Y});return}if("proxies"in $&&Array.isArray($.proxies)){E("servers",`Found ${$.proxies.length} proxies in config`,Y);for(let q of $.proxies)if(q.start){let M=`${q.from}-${q.to}`;try{E("watch",`Starting command for ${M} with command: ${q.start.command}`,Y),H.info(`Starting command for ${M}...`),await r8.startProcess(M,q.start,Y);let C=u$(q.from),O=new URL(C.startsWith("http")?C:`http://${C}`),J0=O.hostname||"localhost",u0=Number(O.port)||80;try{await lY(J0,u0,Y),E("watch",`Dev server is ready at ${J0}:${u0}`,Y)}catch(Gw){E("watch",`Connection check failed, but continuing with proxy setup: ${Gw}`,Y),H.warn("Dev server connection check failed. RPX will try to proceed anyway...")}}catch(C){throw E("watch",`Failed to start command for ${M}: ${C}`,Y),Error(`Failed to start command for ${M}: ${C}`)}}else E("watch",`No start command for proxy ${q.from} -> ${q.to}`,Y)}else if("start"in $&&$.start){E("watch","Found start command in single proxy config",Y);let q=`${$.from}-${$.to}`;try{if($.start)E("watch",`Starting command: ${$.start.command}`,Y),await r8.startProcess(q,$.start,Y);let M=u$($.from),C=new URL(M.startsWith("http")?M:`http://${M}`),O=C.hostname||"localhost",J0=Number(C.port)||80;try{await lY(O,J0,Y),E("watch",`Dev server is ready at ${O}:${J0}`,Y)}catch(u0){E("watch",`Connection check failed, but continuing with proxy setup: ${u0}`,Y),H.warn("Dev server connection check failed. RPX will try to proceed anyway...")}}catch(M){throw E("watch",`Failed to run start command: ${M}`,Y),Error(`Failed to run start command: ${M}`)}}else E("watch","No start command found in config",Y);let X="proxies"in $&&Array.isArray($.proxies)?$.proxies[0]?.to:("to"in $)?$.to:"rpx.localhost";if(t.platform!=="win32"&&($.https||A)){if(!T$())try{E("sudo","Pre-acquiring sudo credentials for privileged operations",Y),SR("sudo -v",{stdio:"inherit"})}catch{E("sudo","Could not pre-acquire sudo credentials",Y)}}let J=[];if($.productionCerts){if(J=await xY($.productionCerts,Y),J.length>0)E("ssl",`Using ${J.length} production SNI cert(s): ${J.map((q)=>q.serverName).join(", ")}`,Y)}if($.https){let q=J.length>0?null:await y$($);if(!q&&J.length===0){if(E("ssl",`No valid or trusted certificates found for ${X}, generating new ones`,$.verbose),await HY($),q=await y$($),!q)throw Error(`Failed to load SSL certificates after generation for ${X}`)}else E("ssl",`Using existing and trusted certificates for ${X}`,$.verbose);$._cachedSSLConfig=q}let U="proxies"in $&&Array.isArray($.proxies)?$.proxies.map((q)=>({...q,https:$.https,cleanup:$.cleanup,cleanUrls:q.cleanUrls??("cleanUrls"in $?$.cleanUrls:!1),vitePluginUsage:$.vitePluginUsage,changeOrigin:q.changeOrigin??$.changeOrigin,verbose:Y,_cachedSSLConfig:$._cachedSSLConfig})):[{from:"from"in $?$.from:"localhost:5173",to:"to"in $?$.to:"rpx.localhost",cleanUrls:"cleanUrls"in $?$.cleanUrls:!1,https:$.https,cleanup:$.cleanup,vitePluginUsage:$.vitePluginUsage,start:"start"in $?$.start:void 0,changeOrigin:$.changeOrigin,auth:"auth"in $?$.auth:void 0,verbose:Y,_cachedSSLConfig:$._cachedSSLConfig}],j=U.map((q)=>q.to||"rpx.localhost"),W=J.length>0?J:$._cachedSSLConfig??null,Q=j.filter((q)=>q&&!q.includes("localhost")&&!q.includes("127.0.0.1")),f=["dev","app","page","new","day","foo"],z=["test","localhost","local","example","invalid"],B=[...new Set(Q.map((q)=>q.split(".").pop()?.toLowerCase()))],G=B.filter((q)=>!!q&&f.includes(q));if(G.length>0&&Y)H.warn(`The following TLDs may not work reliably for local development: ${G.map((q)=>`.${q}`).join(", ")}`),H.info(" These TLDs have HSTS preloading which can bypass local DNS"),H.info(" Consider using reserved TLDs: .test, .localhost, or .local");if(A&&t.platform==="darwin"&&Q.length>0){let{setupDevelopmentDns:q}=await Promise.resolve().then(() => (gY(),Z8));if(await q({domains:Q,verbose:Y})){if(Y)if(B.every((O)=>!!O&&z.includes(O)))H.success(`DNS server started for ${B.map((O)=>`.${O}`).join(", ")} domains`);else H.success(`DNS server started for ${B.map((O)=>`.${O}`).join(", ")} domains (hosts file entries also added)`)}else E("dns","Could not start DNS server - custom domains may not resolve",Y)}let T=async()=>{E("cleanup","Starting cleanup handler",$.verbose);try{let{tearDownDevelopmentDns:q}=await Promise.resolve().then(() => (gY(),Z8));await q({verbose:$.verbose})}catch(q){E("cleanup",`Error stopping DNS server: ${q}`,$.verbose)}try{await r8.stopAll($.verbose)}catch(q){E("cleanup",`Error stopping processes: ${q}`,$.verbose)}await o8({domains:j,hosts:typeof $.cleanup==="boolean"?$.cleanup:$.cleanup?.hosts,certs:typeof $.cleanup==="boolean"?$.cleanup:$.cleanup?.certs,verbose:$.verbose||!1})};t.on("SIGINT",T),t.on("SIGTERM",T);let K=$.singlePortMode===!0,F=$.httpsPort??443,N=$.httpPort??80,R=$.originGuard?V6($.originGuard):null,L=!!W&&(U.length>1||K||J.length>0),V=!W&&K&&U.length>0;if(L&&W){E("proxies",`Creating shared HTTPS server for ${U.length} domains on port ${F}`,Y);let q=await S6(U,A,Y),M=null,C=$.onDemandTls,O=C?.enabled?new u1({config:C,certsDir:C.certsDir??$.productionCerts?.certsDir??_6.join(O6.homedir(),".stacks","rpx","on-demand-certs"),initial:J,verbose:Y,onCertAdded:(Ew)=>{if(kY()==="restart")E("on-demand","certificate installed; restarting supervised gateway to reload TLS",Y),setTimeout(()=>t.kill(t.pid,"SIGTERM"),10).unref();else Gw(Ew)}}):null,J0=null,u0=!1;async function Gw(Ew){if(J0=Ew,u0)return;u0=!0;try{while(J0){let kw=J0;if(J0=null,E("proxies",`rebuilding :${F} with ${kw.length} SNI cert(s)`,Y),M)nY.delete(M),M.stop(!1);let t0=!1;for(let _=0;!t0&&_<60;_++){let Cw=t8({routeEntries:q,listenPort:F,sslConfig:kw,originGuard:R,verbose:Y});if(Cw){M=Cw,t0=!0;break}await new Promise((n1)=>setTimeout(n1,Math.min(25*2**Math.min(_,4),500)))}if(!t0)H.error(`rpx: CRITICAL — could not rebind :${F} after cert issuance; HTTPS unbound until the next cert event or a gateway restart`)}}finally{u0=!1}}if(!await I$(N,"0.0.0.0",Y))k6(Y,N,F,$.acmeChallengeWebroot,O);if(await I$(F,"0.0.0.0",Y)){if(E("proxies",`Port ${F} is already in use, cannot start shared proxy`,Y),Y)H.warn(`Port ${F} is in use. Shared HTTPS proxy cannot start.`);return}let xw=O&&O.sniEntries().length>0?O.sniEntries():W;if(M=t8({routeEntries:q,listenPort:F,sslConfig:xw,originGuard:R,verbose:Y}),!M){H.error(`Shared HTTPS proxy failed to bind :${F}; not exiting`);return}}else if(V){E("proxies",`Creating shared HTTP server for ${U.length} domains on port ${N}`,Y);let q=await S6(U,A,Y);if(await I$(N,"0.0.0.0",Y)){if(E("proxies",`Port ${N} is already in use, cannot start shared proxy`,Y),Y)H.warn(`Port ${N} is in use. Shared HTTP proxy cannot start.`);return}if(!t8({routeEntries:q,listenPort:N,sslConfig:null,originGuard:R,verbose:Y})){H.error(`Shared HTTP proxy failed to bind :${N}; not exiting`);return}}else for(let q of U)try{let M=q.to||"rpx.localhost";E("proxy",`Starting proxy for ${M} with SSL config: ${!!W}`,q.verbose),await x6({from:q.from||"localhost:5173",to:M,cleanUrls:q.cleanUrls||!1,https:q.https||!1,cleanup:q.cleanup||!1,vitePluginUsage:q.vitePluginUsage||!1,verbose:q.verbose||!1,_cachedSSLConfig:$._cachedSSLConfig,changeOrigin:q.changeOrigin||!1,loadBalancer:q.loadBalancer,auth:q.auth,path:q.path,pathRewrites:q.pathRewrites})}catch(M){E("proxies",`Failed to start proxy for ${q.to}: ${M}`,q.verbose),H.error(`Failed to start proxy for ${q.to}:`,M)}}async function S6(w,$,Y){let A=[],X=new Set;for(let J of w){let U=J.to||"rpx.localhost",j=J.cleanUrls||!1,W=J.path,Q=X$(W),f=VY(J.auth);if(J.redirect){let z=V2(J.redirect);A.push({host:U,path:W,route:{redirect:z,basePath:Q,auth:f}}),E("proxies",`Route: ${U}${W??""} → redirect ${z.status} ${z.to}${f?" (auth)":""}`,Y)}else if(J.static)A.push({host:U,path:W,route:{static:q8(J.static,j),cleanUrls:j,basePath:Q,auth:f}}),E("proxies",`Route: ${U}${W??""} → static ${typeof J.static==="string"?J.static:J.static.dir}${f?" (auth)":""}`,Y);else{let z=u$(J.from),B=new URL(z.startsWith("http")?z:`http://${z}`),G=MY(J.from??B.host,J.loadBalancer);IY(G),a8.add(G),A.push({host:U,path:W,route:{sourceHost:B.host,upstreamPool:G,cleanUrls:j,changeOrigin:J.changeOrigin||!1,pathRewrites:J.pathRewrites,basePath:Q,auth:f}}),E("proxies",`Route: ${U}${W??""} → ${B.host}${f?" (auth)":""}`,Y)}if(X.has(U))continue;if(X.add(U),$&&!j1(U)&&!U.includes("localhost")&&!U.includes("127.0.0.1"))try{if(!(await uY([U],Y))[0])await bY([U],Y)}catch{E("hosts",`Could not add hosts entry for ${U}`,Y)}}return A}function t8(w){let{routeEntries:$,listenPort:Y,sslConfig:A,originGuard:X,verbose:J}=w,U=OY($),j=SY((f,z)=>_Y(U,f,z),J),W=X?(f,z)=>X(f)??j(f,z):j,Q=hY(J);try{let f=Bun.serve({port:Y,hostname:"0.0.0.0",reusePort:NY(),...A?{tls:v$(Array.isArray(A)?A.map((z)=>({serverName:z.serverName,key:z.key,cert:z.cert})):{key:A.key,cert:A.cert,ca:A.ca,requestCert:!1,rejectUnauthorized:!1})}:{},fetch(z,B){return W(z,B)},websocket:Q,error(z){return E("server",`Shared proxy server error: ${z}`,J),new Response(`Server Error: ${z.message}`,{status:500})}});return nY.add(f),E("proxies",`Shared ${A?"HTTPS":"HTTP"} proxy listening on port ${Y} for ${U.size} domains`,J),f}catch(f){return E("proxies",`Failed to start shared proxy: ${f}`,J),console.error("Failed to start shared proxy:",f),null}}function xR(w){if(w?.vitePluginUsage||!w?.verbose)return;if(console.log(""),console.log(` ${Ow.green(Ow.bold("rpx"))} ${Ow.green(`v${dY}`)}`),console.log(` ${Ow.green("➜")} ${Ow.dim(w?.from??"")} ${Ow.dim("➜")} ${Ow.cyan(w?.ssl?`https://${w?.to}`:`http://${w?.to}`)}`),w?.listenPort!==(w?.ssl?443:80))console.log(` ${Ow.green("➜")} Listening on port ${w?.listenPort}`);if(w?.cleanUrls)console.log(` ${Ow.green("➜")} Clean URLs enabled`)}z0();P0.chdir(ZR(kR(PR(import.meta.url)),".."));var b0=new fJ("rpx");b0.command("start","Start the Reverse Proxy Server").option("--from <from>","The URL to proxy from").option("--to <to>","The URL to proxy to").option("--key-path <path>","Absolute path to the SSL key").option("--cert-path <path>","Absolute path to the SSL certificate").option("--ca-cert-path <path>","Absolute path to the SSL CA certificate").option("--hosts-cleanup","Cleanup /etc/hosts on exit").option("--certs-cleanup","Cleanup SSL certificates on exit").option("--start-command <command>","Command to start the dev server").option("--start-cwd <path>","Current working directory for the dev server").option("--start-env <env>","Environment variables for the dev server").option("--change-origin","Change the origin of the host header to the target URL").option("--single-port-mode","Route all proxies through one shared listener (by Host header) instead of a port per proxy").option("--http-port <port>","Port for the shared HTTP listener / redirect (default: 80)").option("--https-port <port>","Port for the shared HTTPS listener (default: 443)").option("--via-daemon","Route through the shared rpx daemon instead of binding :443 directly").option("--id <id>","Stable id used when registering with the daemon (auto-derived from --to)").option("--verbose","Enable verbose logging").example("rpx start --from localhost:5173 --to my-project.localhost").example("rpx start --from localhost:3000 --to my-project.localhost/api").example("rpx start --from localhost:3000 --to localhost:3001").example("rpx start --from localhost:5173 --to my-project.test --key-path /absolute/path/to/key --cert-path /absolute/path/to/cert").example("rpx start --from localhost:5173 --to my-project.localhost --change-origin").example("rpx start --single-port-mode --https-port 8443").action(async(w)=>{if(!w?.from||!w.to)return P6(mw);let $={from:w.from,to:w.to,https:{keyPath:w.keyPath,certPath:w.certPath,caCertPath:w.caCertPath},cleanup:{certs:w.certsCleanup||!1,hosts:w.hostsCleanup||!1},verbose:w.verbose||!1,changeOrigin:w.changeOrigin||!1,singlePortMode:w.singlePortMode||!1,httpPort:w.httpPort!==void 0?Number(w.httpPort):void 0,httpsPort:w.httpsPort!==void 0?Number(w.httpsPort):void 0,viaDaemon:w.viaDaemon||!1,id:w.id};if(w.startCommand){let Y={command:w.startCommand};if(w.startCwd)Y.cwd=w.startCwd;if(w.startEnv)try{Y.env=JSON.parse(w.startEnv)}catch(A){console.error("Failed to parse start-env JSON:",A),P0.exit(1)}$.start=Y}return Z6($)});b0.command("watch:start <proxy>","Start the dev server for a specific proxy").option("--verbose","Enable verbose logging").action(async(w,$)=>{let Y=z8(mw)?mw.proxies.find((A)=>A.to===w||`${A.from}-${A.to}`===w):mw.to===w?mw:null;if(!Y?.start)console.error(`No watch configuration found for proxy: ${w}`),P0.exit(1);try{await d8.startProcess(w,Y.start,$.verbose),console.log(`Started dev server for ${w}`)}catch(A){console.error(`Failed to start dev server for ${w}:`,A),P0.exit(1)}});b0.command("watch:stop <proxy>","Stop the dev server for a specific proxy").option("--verbose","Enable verbose logging").action(async(w,$)=>{try{await d8.stopProcess(w,$.verbose),console.log(`Stopped dev server for ${w}`)}catch(Y){console.error(`Failed to stop dev server for ${w}:`,Y),P0.exit(1)}});b0.command("watch:stopall","Stop all running dev servers").option("--verbose","Enable verbose logging").action(async(w)=>{try{await d8.stopAll(w.verbose),console.log("Stopped all dev servers")}catch($){console.error("Failed to stop all dev servers:",$),P0.exit(1)}});async function i6(w){let Y={...(await Pf().catch(()=>null))?.onDemand??{},enabled:!0};if(w){let A=w.split(",").map((X)=>X.trim()).filter(Boolean);if(A.length>0)Y.roots=A}return Y}b0.command("daemon:start","Start the rpx daemon in the foreground (binds :443 + :80)").option("--rpx-dir <path>","Override the rpx state dir (default ~/.stacks/rpx)").option("--registry-dir <path>","Override the registry dir (default <rpx-dir>/registry.d)").option("--https-port <port>","HTTPS port (default 443)",{default:443}).option("--http-port <port>","HTTP redirect port; 0 to disable (default 80)",{default:80}).option("--hostname <host>","Bind address (default 0.0.0.0)",{default:"0.0.0.0"}).option("--certs-dir <path>","Directory of real PEM certs for per-domain SNI (<domain>.crt/.key, _wildcard.<apex>.crt/.key)").option("--workers <n>","Run as a multi-core cluster of N worker processes (default 1; also RPX_WORKERS)").option("--on-demand","Lazily boot a project's dev server the first time its host is visited (reads config `onDemand`)").option("--roots <dirs>","Comma-separated roots scanned for on-demand sites (overrides config; default ~/Code)").option("--verbose","Enable verbose logging").example("rpx daemon:start --on-demand").example("rpx daemon:start --on-demand --roots ~/Code,~/work").action(async(w)=>{try{let $=w.onDemand?await i6(w.roots):void 0;await(await R6({rpxDir:w.rpxDir,registryDir:w.registryDir,httpsPort:typeof w.httpsPort==="string"?Number.parseInt(w.httpsPort,10):w.httpsPort,httpPort:typeof w.httpPort==="string"?Number.parseInt(w.httpPort,10):w.httpPort,hostname:w.hostname,productionCerts:w.certsDir?{certsDir:w.certsDir}:void 0,workers:w.workers===void 0?void 0:typeof w.workers==="string"?Number.parseInt(w.workers,10):w.workers,onDemandSites:$,verbose:w.verbose??!0})).done,P0.exit(0)}catch($){console.error(`Failed to start rpx daemon: ${$.message}`),P0.exit(1)}});b0.command("daemon:stop","Stop the running rpx daemon (SIGTERM, escalates to SIGKILL)").option("--rpx-dir <path>","Override the rpx state dir (default ~/.stacks/rpx)").option("--timeout <ms>","Max ms to wait for graceful shutdown",{default:5000}).option("--no-force","Do not escalate to SIGKILL after timeout").option("--verbose","Enable verbose logging").action(async(w)=>{let $=typeof w.timeout==="string"?Number.parseInt(w.timeout,10):w.timeout,Y=await M6({rpxDir:w.rpxDir,timeoutMs:$,forceAfterTimeout:w.force!==!1,verbose:w.verbose});if(!Y.stopped&&Y.pid===null){console.log("rpx daemon is not running");return}if(!Y.stopped){console.log(`rpx daemon pid=${Y.pid} was already gone (cleaned stale lock)`);return}console.log(`rpx daemon pid=${Y.pid} stopped${Y.forced?" (SIGKILL)":""}`)});b0.command("daemon:status","Print daemon state and currently registered hosts").option("--rpx-dir <path>","Override the rpx state dir (default ~/.stacks/rpx)").option("--registry-dir <path>","Override the registry dir (default <rpx-dir>/registry.d)").option("--json","Emit machine-readable JSON instead of a human summary").action(async(w)=>{let $=w.rpxDir??V0(),Y=await L$($),A=await c8($),X=w.registryDir,J=await Bw(X).catch(()=>[]);if(w.json){console.log(JSON.stringify({running:A,pid:Y,pidFile:S$($),rpxDir:$,registryDir:X??$w(),entries:J},null,2));return}if(!A){if(console.log("rpx daemon: not running"),Y!==null)console.log(`(stale pid file at ${S$($)} → pid ${Y})`)}else console.log(`rpx daemon: running (pid=${Y})`),console.log(`pid file: ${S$($)}`);if(console.log(`registry: ${X??$w()}`),J.length===0){console.log("no registered hosts");return}console.log(`registered hosts (${J.length}):`);for(let U of J){let j=U.pid!==void 0?`, pid=${U.pid}`:"";console.log(` https://${U.to} → ${U.from} (id=${U.id}${j})`)}});b0.command("register","Register an upstream app with the rpx daemon").option("--id <id>","Unique id for this entry (a-z, 0-9, dot, dash, underscore)").option("--from <host:port>","Upstream host:port (e.g. localhost:5173)").option("--to <host>","Public hostname (e.g. pet-store.localhost)").option("--cwd <path>","Working directory of the upstream (informational)").option("--clean-urls","Strip .html and 301 to the clean URL").option("--change-origin","Rewrite Origin to the upstream").option("--rpx-dir <path>","Override the rpx state dir (default ~/.stacks/rpx)").option("--registry-dir <path>","Override the registry dir (default <rpx-dir>/registry.d)").option("--skip-spawn","Do not lazy-spawn the daemon if it is not already running").option("--verbose","Enable verbose logging").example("rpx register --id pet-store --from localhost:5173 --to pet-store.localhost").action(async(w)=>{if(!w.id||!w.from||!w.to)console.error("rpx register requires --id, --from, and --to"),P0.exit(1);if(!C$(w.id))console.error(`invalid id: ${JSON.stringify(w.id)} (must match /^[a-zA-Z0-9._-]+$/, ≤128 chars)`),P0.exit(1);try{await c1({id:w.id,from:w.from,to:w.to,cwd:w.cwd,createdAt:new Date().toISOString(),cleanUrls:w.cleanUrls,changeOrigin:w.changeOrigin},w.registryDir,w.verbose)}catch($){console.error(`failed to write registry entry: ${$.message}`),P0.exit(1)}if(w.skipSpawn){console.log(`registered ${w.to} → ${w.from} (daemon spawn skipped)`);return}try{let $=await v8({rpxDir:w.rpxDir,verbose:w.verbose}),Y=$.spawned?"spawned":"attached to";console.log(`registered https://${w.to} → ${w.from} (${Y} daemon pid=${$.pid})`)}catch($){console.error(`registered entry but daemon spawn failed: ${$.message}`),console.error("the entry remains in the registry; start the daemon manually with `rpx daemon:start`"),P0.exit(1)}});b0.command("unregister <id>","Remove a previously registered app from the rpx daemon").option("--registry-dir <path>","Override the registry dir (default ~/.stacks/rpx/registry.d)").option("--verbose","Enable verbose logging").action(async(w,$)=>{if(!C$(w))console.error(`invalid id: ${JSON.stringify(w)}`),P0.exit(1);let Y=await yj(w,$.registryDir,$.verbose);if(!Y){console.log(`no registry entry for id=${w}`);return}await m$(w,$.registryDir,$.verbose),console.log(`unregistered ${Y.to} (id=${w})`)});b0.command("dns:reconcile","Remove stale macOS DNS overrides left by crashed dev sessions").option("--rpx-dir <path>","Override the rpx state dir (default ~/.stacks/rpx)").option("--verbose","Enable verbose logging").action(async(w)=>{let $=w.rpxDir??V0();if(await c8($)){console.log("rpx daemon is running — DNS overrides are managed by the daemon");return}await y6({rpxDir:$,verbose:w.verbose}),console.log("DNS reconcile complete")});b0.command("sites","List the on-demand sites rpx can boot (and which are currently live)").option("--roots <dirs>","Comma-separated roots to scan (overrides config; default ~/Code)").option("--registry-dir <path>","Override the registry dir (default ~/.stacks/rpx/registry.d)").option("--json","Emit machine-readable JSON").action(async(w)=>{let $=await i6(w.roots),Y=gf($),A=await Bw(w.registryDir).catch(()=>[]),X=new Set(A.map((j)=>j.to)),J=Y.map((j)=>({host:j.host,dir:j.dir,command:j.command,source:j.source,live:X.has(j.host)}));if(w.json){console.log(JSON.stringify(J,null,2));return}let U=$.roots??["~/Code"];if(J.length===0){console.log("no on-demand sites found"),console.log(`scanned roots: ${U.join(", ")}`),console.log("start the daemon with `rpx daemon:start --on-demand` to serve them");return}console.log(`on-demand sites (${J.length}) — scanned ${U.join(", ")}:`);for(let j of J)console.log(` ${j.live?"●":"○"} https://${j.host} ${j.live?"(live)":"(idle)"}`),console.log(` ${j.command} ${j.dir}`)});b0.command("logs <host>","Show an on-demand site's boot log (e.g. rpx logs myapp.localhost)").option("--rpx-dir <path>","Override the rpx state dir (default ~/.stacks/rpx)").option("--lines <n>","Show only the last N lines (default: all)").action(async(w,$)=>{let{readFile:Y}=await import("node:fs/promises"),{join:A}=await import("node:path"),X=$.rpxDir??V0(),J=A(X,"sites",`${iA(w.split(":")[0])}.log`),U;try{U=await Y(J,"utf8")}catch{console.log(`no log for ${w} (it may not have been booted yet) — looked in ${J}`);return}let j=$.lines===void 0?void 0:typeof $.lines==="string"?Number.parseInt($.lines,10):$.lines,W=U.split(`
|
|
337
337
|
`);P0.stdout.write((j&&j>0?W.slice(-j):W).join(`
|
|
338
338
|
`))});b0.command("version","Show the version of the Reverse Proxy CLI").action(()=>{console.log(dY)});b0.version(dY);b0.help();b0.run();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ab as a,bb as b,cb as c,db as d,eb as e,fb as f,gb as g,hb as h,ib as i,jb as j,kb as k,lb as l,mb as m,nb as n,ob as o}from"./chunk-
|
|
1
|
+
import{ab as a,bb as b,cb as c,db as d,eb as e,fb as f,gb as g,hb as h,ib as i,jb as j,kb as k,lb as l,mb as m,nb as n,ob as o}from"./chunk-mgh5b3ak.js";import"./chunk-w4t1nar9.js";export{m as tearDownDevelopmentDns,l as syncDevelopmentDnsFromRegistry,d as stopDnsServer,c as startDnsServer,i as setupResolver,k as setupDevelopmentDns,g as resolverFilePath,n as removeResolver,j as removeLegacyTldResolvers,o as reconcileStaleDevelopmentDns,f as isRpxDevelopmentDnsAnswering,e as isDnsServerRunning,h as contentLooksLikeRpxResolver,b as RPX_RESOLVER_MARKER,a as DNS_PORT};
|