@stacksjs/ts-cloud 0.16.92 → 0.16.94
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/aws/index.js +1 -1
- package/dist/bin/{chunk-eb591jvz.js → chunk-1g2bnvz2.js} +1 -1
- package/dist/bin/{chunk-v72zwetk.js → chunk-d892pf66.js} +38 -38
- package/dist/bin/{chunk-00arzt4m.js → chunk-x9pqayv4.js} +1 -1
- package/dist/bin/cli.js +4 -4
- package/dist/bin/dashboard-server.js +1 -1
- package/dist/{chunk-zwayp7fz.js → chunk-1kagq9vp.js} +2 -2
- package/dist/{chunk-72p2tdpe.js → chunk-316cbffs.js} +105 -105
- package/dist/{chunk-sjfc9my3.js → chunk-3t5a5gr2.js} +5 -5
- package/dist/{chunk-1087xz1v.js → chunk-4afz3x6w.js} +14 -14
- package/dist/{chunk-fne338jr.js → chunk-4bysqvjh.js} +7 -7
- package/dist/{chunk-zjkxhvn0.js → chunk-71p77ycz.js} +93 -93
- package/dist/{chunk-hcbrgyfz.js → chunk-7y1pknej.js} +2 -2
- package/dist/{chunk-q7yf28cc.js → chunk-7zjf2493.js} +1 -1
- package/dist/chunk-92tv9xar.js +1 -0
- package/dist/{chunk-a77mhcs7.js → chunk-93xknw91.js} +3 -3
- package/dist/{chunk-cjm5j58y.js → chunk-bxn4n4qa.js} +146 -146
- package/dist/{chunk-xs9m0v31.js → chunk-bxq80y73.js} +4 -4
- package/dist/{chunk-1psh3mhm.js → chunk-byt4re1c.js} +1 -1
- package/dist/chunk-bzdfx427.js +138 -0
- package/dist/{chunk-by7hejhg.js → chunk-cjqw4ns4.js} +2 -2
- package/dist/chunk-cq34675f.js +1 -0
- package/dist/chunk-jntyd5zv.js +2 -0
- package/dist/{chunk-zy3kr4s2.js → chunk-kken64a9.js} +3 -3
- package/dist/chunk-m580e9rp.js +1 -0
- package/dist/{chunk-fe6m7wb0.js → chunk-n6tgqd7d.js} +19 -19
- package/dist/{chunk-tpvmpmxj.js → chunk-p1agrrnq.js} +2 -2
- package/dist/chunk-q1a389m2.js +1 -0
- package/dist/{chunk-qra9k26f.js → chunk-r2gtxb7n.js} +1 -1
- package/dist/{chunk-h6kptkce.js → chunk-sp0ne2ax.js} +2 -2
- package/dist/{chunk-c6kb5c5v.js → chunk-stqg97mh.js} +4 -4
- package/dist/{chunk-kcem6dzk.js → chunk-tes38y8x.js} +2 -2
- package/dist/{chunk-7d9ek8rc.js → chunk-vaczx33v.js} +4 -4
- package/dist/chunk-vpce5x9x.js +1 -0
- package/dist/chunk-w2gsveze.js +1 -0
- package/dist/{chunk-4m5gpp6w.js → chunk-wggeew90.js} +5 -5
- package/dist/{chunk-jz6dacx1.js → chunk-wnevarf9.js} +2 -2
- package/dist/{chunk-sxqe8e9c.js → chunk-x1k1sbgf.js} +7 -7
- package/dist/{chunk-byr76jcy.js → chunk-xa2ahxxa.js} +19 -19
- package/dist/{chunk-9f18pebj.js → chunk-z1087cap.js} +5 -5
- package/dist/chunk-z5dqpsek.js +1 -0
- package/dist/{chunk-x7cnfn7y.js → chunk-zpkf2jt2.js} +2 -2
- package/dist/deploy/index.js +1 -1
- package/dist/dns/index.js +1 -1
- package/dist/drivers/index.d.ts +2 -2
- package/dist/drivers/index.js +1 -1
- package/dist/drivers/shared/rpx-gateway.d.ts +14 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +37 -37
- package/dist/mail/index.js +2 -2
- package/dist/operations/index.js +1 -1
- package/dist/protection/index.js +1 -1
- package/dist/spend/index.js +1 -1
- package/dist/ui/404.html +1 -1
- package/dist/ui/accept-invitation.html +1 -1
- package/dist/ui/access-denied.html +1 -1
- package/dist/ui/account/automation.html +5 -5
- package/dist/ui/account/security.html +2 -2
- package/dist/ui/applications/compose.html +6 -6
- package/dist/ui/applications/new.html +1 -1
- package/dist/ui/data/backups.html +6 -6
- package/dist/ui/data/services.html +6 -6
- package/dist/ui/data/volumes.html +6 -6
- package/dist/ui/index.html +6 -6
- package/dist/ui/infrastructure/topology.html +5 -5
- package/dist/ui/integrations.html +2 -2
- package/dist/ui/operations/alerts.html +6 -6
- package/dist/ui/operations/configuration.html +6 -6
- package/dist/ui/operations/jobs.html +5 -5
- package/dist/ui/operations/maintenance.html +6 -6
- package/dist/ui/operations/observability.html +6 -6
- package/dist/ui/operations/previews.html +6 -6
- package/dist/ui/operations/queue.html +5 -5
- package/dist/ui/operations/regions.html +6 -6
- package/dist/ui/operations/releases.html +5 -5
- package/dist/ui/operations/spend.html +6 -6
- package/dist/ui/operations/workloads.html +6 -6
- package/dist/ui/security.html +2 -2
- package/dist/ui/server/actions.html +6 -6
- package/dist/ui/server/activity.html +1 -1
- package/dist/ui/server/capacity.html +6 -6
- package/dist/ui/server/database.html +6 -6
- package/dist/ui/server/deployments.html +5 -5
- package/dist/ui/server/diagnostics.html +2 -2
- package/dist/ui/server/firewall.html +6 -6
- package/dist/ui/server/fleet.html +6 -6
- package/dist/ui/server/logs.html +6 -6
- package/dist/ui/server/metrics.html +5 -5
- package/dist/ui/server/security.html +2 -2
- package/dist/ui/server/services.html +2 -2
- package/dist/ui/server/sites.html +6 -6
- package/dist/ui/server/ssh-keys.html +6 -6
- package/dist/ui/server/team.html +6 -6
- package/dist/ui/server/terminal.html +2 -2
- package/dist/ui/serverless/alarms.html +6 -6
- package/dist/ui/serverless/assets.html +2 -2
- package/dist/ui/serverless/cost.html +2 -2
- package/dist/ui/serverless/data.html +6 -6
- package/dist/ui/serverless/deployments.html +2 -2
- package/dist/ui/serverless/firewall.html +1 -1
- package/dist/ui/serverless/functions.html +6 -6
- package/dist/ui/serverless/logs.html +5 -5
- package/dist/ui/serverless/metrics.html +2 -2
- package/dist/ui/serverless/queues.html +6 -6
- package/dist/ui/serverless/secrets.html +6 -6
- package/dist/ui/serverless/traces.html +6 -6
- package/dist/ui/serverless.html +5 -5
- package/package.json +3 -3
- package/dist/chunk-0v77443p.js +0 -1
- package/dist/chunk-4khjjgd3.js +0 -1
- package/dist/chunk-872fdms3.js +0 -1
- package/dist/chunk-bazah9w0.js +0 -1
- package/dist/chunk-dmv3vryr.js +0 -138
- package/dist/chunk-qs7814ya.js +0 -1
- package/dist/chunk-sd5a61c6.js +0 -1
- package/dist/chunk-t1wq0ckc.js +0 -2
- package/dist/chunk-zft558kj.js +0 -1
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import{WI,JI,ul,Dm,gl,pl,ln,H,l,ml,Pm,W,fl,p,gy,b,tt,Yi,un,w,le,hl,py,r,t,XI,YI,m,ZI,eb,tb,rb,nb,ur,Al,Zi,my,ib,sb,_e,rt,yl,km,Nm,ob,Bm,xm,Om,Ko,Sl,ue,gn,Nt,Wo,Bt,ve,fy,Il,Jo,_m,Lm,$m,Mm,bl,ab,J,E,hy,Ay,Fm,a,S,nt,f,Um,it,X,xt,cb,Qm,Y,Cl,Le,gr,Xo,ge,El,pe,yy,Yo,Z,ee,L,es,D,pr,Ot,te,e,pn,db,Hm,vl,lb,ub,gb,pb,Tl,mb,fb,hb,Ab,yb,Sb,Ib,bb,Cb,Eb,vb,Tb,Sy,Iy,by,Cy,Ey,vy,Ty,mr,Gm,Vm,fr,ts,wb,qm,Rb,Db,mn,Pb,kb,Nb,Bb,xb,Ob,jm,k,Te,rs,Zo,fn,ns,ea,wy,_b,is,ta,Ry,Dy,Py,ky,Ny,By,ss,Lb,ra,xy,zm,q,Oy,st,na,_t,_y,Km,Ly,$y,My,Wm,j,$b,Mb,Fb,Ub,Fy,Qb,re,Hb,Gb,Vb,wl,Rl,Uy,Jm,Xm,qb,jb,zb,Kb,Wb,Qy,Dl,Jb,Xb,Yb,Zb,eC,Hy,Gy,tC,rC,nC,iC,sC,oC,aC,Vy,cC,Ym,Pl,dC,lC,uC,gC,Zm,ef,tf,pC,kl,qy,jy,zy,Ky,Nl,Wy,mC,fC,hC,AC,yC,SC,IC,Jy,Xy,Yy,Zy,rf,bC,CC,EC,vC,TC,wC,RC,DC,PC,eS,tS,rS,nS,kC,NC,nf,BC,sf,of,af,cf,xC,OC,_C,LC,$e,os,$C,MC,FC,UC,QC,HC,GC,VC,iS,qC,jC,df,lf,zC,KC,sS,WC,JC,XC,YC,ZC,eE,tE,rE,nE,iE,sE,oE,aE,cE,dE,lE,uE,gE,pE,mE,fE,Bl,oS,xl,aS,Ol,cS,_l,dS,Ll,lS,hr,uS,$l,gS,as,pS,Ml,mS,Fl,fS,uf,hE,gf,AE,Ul,hS,Ar,AS,pf,yE,Ql,yS,mf,SE,ff,IE,hf,bE,Hl,SS,Af,CE,yf,EE,Sf,vE,If,TE,Gl,IS,Vl,bS,bf,wE,Cf,RE,Ef,DE,vf,PE,Tf,kE,wf,NE,ql,CS,Rf,BE,Df,xE,Pf,OE,kf,_E,Nf,LE,Bf,$E,xf,Of,_f,Lf,$f,ME,Mf,FE,Ff,UE,Uf,QE,ot,Qf,ES,ia,jl,hn,vS,cs,yr,zl,Kl,TS,wS,Wl,Hf,Jl,Gf,HE,GE,sa,oa,aa,Xl,An,Yl,Zl,ds,ca,da,Vf,RS,DS,PS,VE,qf,jf,qE,kS,zf,NS,BS,xS,jE,Kf,yn,eu,Wf,zE,OS,la,ua,tu,Jf,KE,ru,_S,Xf,WE,Yf,JE,Zf,XE}from"./chunk-72p2tdpe.js";import{nu,N,iu,ga,MS,eh,FS,US,QS,HS,GS,VS,qS,jS}from"./chunk-sjfc9my3.js";import{ya,th,gu,KS,ms,pu,Sa,rh,nh,In,Ia,mu,at,fu,hu,Au,fs,bn,ih,ct,yu,ba,Ca,Ea,Su,Iu,bu,hs,va,Cu,Cn,As,Ta,Lt,sh,Eu,vu,oh,Tu,wa,Sr,wu,Ru,Du,Pu,dt,ah,ku,Ir,Ra,Da,Pa,ys,Ss,En,Nu,Is,ka,Na,vn,Bu,Tn,ch,dh,$t,wn,Ba,lh,xu,Ou,xa,_u,Lu,$u,WS,me,Mu,JS,XS,bs,Cs,YS,we,uh,Mt,Me,Fe,Ft,Fu,Uu,Qu,Hu,gh,ph,Oa,mh,fh,_a,Gu,Vu,hh,fe,Ah,qu,Re,La,lt,Ut,Qt,Rn,Es,ju,ZS,yh,Sh,Ih,bh,$a,Ma,zu,Fa,Dn,Ua,Ku,vs,Wu,Qa,Ha,Ga,Va,qa,ja,za,Ka,Ju,br,Wa,Xu,Pn,Yu,Ja,Xa,Zu,Cr,Ya,Za,Ch,eI,Ts,tI,rI,nI,ec,tc,eg,Eh,rc,kn,tg,ws,rg,ng,ig,nc,vh,ic,sg,Th,sc,wh,Nn,Bn,og,Rs,gg,hg,Ag,yg,Sg,Ig,Ps,Er,ac,xn,On,vr,ut,Oh,dc,_h,Lh,Eg,lc,Ns,vg,uc,Gt,Qe,Tg,gc,pc,wg,mc,fc,hc,Ac,Rg,Dg,Ln,yc,Sc,Ic,bc,Pg,Bs,$n,xs,Cc}from"./chunk-cjm5j58y.js";import{uu}from"./chunk-kcem6dzk.js";import{Q,Wr,wd,Eo,d,rm,nm}from"./chunk-x7cnfn7y.js";import{wt,Td,_i,Li,$i,tm}from"./chunk-xs9m0v31.js";import{gA,pA,mA,Lp,$p,Mp,fA,Fp,Up,hA,AA,Qp,yA,SA,Ed,Hp,IA,bA,Gp,CA,EA,vA,TA,wA,RA,DA,Oi,vd,Vp,qp,jp,zp,Kp,Wp,Jp,Xp,Yp,Zp,em,PA,kA,NA,BA,xA,OA,_A}from"./chunk-byr76jcy.js";import{Ye,Rd,Ee,ir,Dd,O,sr,Rt,Pd,vo,MA,im,kd,s,Mi,To,Jr,Nd,Bd,Xr,FA,xd,qI,jI,wo,zI,KI,Od,_d,Fi,Ro,de,c,Do,Ze,Yr,UA,QA,Ld,HA,Po,GA,VA,ko,$d,Zr,No,qA,or,Md,sm,ar,en,jA,Bo,zA,_,KA,Fd,om,xo,WA,JA,XA,Ud,Ui,Oo,Qd,cr,Dt,YA,tn,ZA,Hd,am,cm,_o,Gd,Vd,Pt,dm,Qi,qd,Lo,rn,lm,Hi,jd,zd,Gi,um,Kd,Wd,Jd,Xd,$o,gm,pm,ey,mm,Yd,Vi,fm,hm,Mo,nn,Fo,Uo,Qo,Zd,Am,el,Ho,ty,dr,ry,tl,sn,rl,ny,nl,ym,qi,on,Go,ji,et,iy,Sm,Im,lr,il,sl,ol,zi,Ki,Wi,sy,oy,kt,Vo,ay,an,bm,qo,cy,Cm,Em,vm,dy,Tm,wm,ly,i,uy,Ji,jo,zo}from"./chunk-fe6m7wb0.js";import{n}from"./chunk-7d9ek8rc.js";import{ke,ft,Wn}from"./chunk-zy3kr4s2.js";import{De,Us,Qs,Bg,Rc,Dc,Hs,Pc,gt,ne,pt,he,T,Pe,Hn,kc,He,Nc,Gn,Vn,qn,Vt,qt,jt,jn,wr,Bc,Rr,Dr,mt,zn,Oc,zt,Pr,Kn,Lc,$g,Mg,$c,kr}from"./chunk-fne338jr.js";import{o}from"./chunk-1psh3mhm.js";import{A}from"./chunk-by7hejhg.js";import{jr,Oe}from"./chunk-a77mhcs7.js";import{Mc,Ge,I}from"./chunk-1087xz1v.js";import{C}from"./chunk-tpvmpmxj.js";import{ht,Se,qe,Fg,Kt,Fc,Uc,Wt,G,Jn,Jt,Xt,Xn,js,zs,Nr,Br,Yn,xr,Ks,Ws,Js,Xs,se,Ys,Zs,eo,to,Ie,ro,no,io,B,Qc,Zn,Hc,be,Or}from"./chunk-zwayp7fz.js";import{Ve}from"./chunk-hcbrgyfz.js";import{ie}from"./chunk-h6kptkce.js";import{Ug,Gc,Qg,oe,Vc,ei,Hg,qc,Gg,Vg,Wh,Lr,oo,F,Mr,ri,ni,Fr,Qr,ii,ao,si,oi,ai,ci,li,Hr,Ce,Kc,yt,Wc,Gr,Yc,ze,ae,Ke,We,It,pi,mi,Ne,Ct,Be,hi,Ai,yi,Si,Ii,bi,Ci,Vr,U,go,hp,ad,vi,Ti,wi,Ri,pd,mo,md,rr,Pi,fo,ho,fd,ki,hd}from"./chunk-zjkxhvn0.js";import{yo,Io,bo,x,xe}from"./chunk-dmv3vryr.js";import{su,pa,us,gs,ma,ou,au,ps,fa,ha,du,lu,Aa}from"./chunk-qra9k26f.js";import{Ec,Mn,Un,Os,_s,Ls,$s,vc,Tc}from"./chunk-9f18pebj.js";import{wc}from"./chunk-c6kb5c5v.js";import{ce,Ni,Ao,Ad,Bi,kp}from"./chunk-4m5gpp6w.js";import{al,cn,cl,dl,ll,Xi,Rm,dn}from"./chunk-sxqe8e9c.js";import{LS,ls}from"./chunk-jz6dacx1.js";var eR="https://api.machines.dev/v1";class So extends Error{status;path;constructor(u,g,h){super(u);this.status=g;this.path=h;this.name="FlyApiError"}}function sd(u){let g=u.ip.includes(":");if(g&&u.ip.startsWith("fdaa:"))return"private_v6";if(u.shared)return"shared_v4";return g?"v6":"v4"}class Cp{token;fetch;constructor(u){if(!u.token)throw Error("Fly.io needs an API token: set FLY_API_TOKEN (`fly tokens create deploy` makes one scoped to an app).");this.token=u.token,this.fetch=u.fetch??fetch}async rest(u,g,h={}){let y=new URL(`https://api.machines.dev/v1${g}`);for(let[M,V]of Object.entries(h.query??{}))y.searchParams.set(M,V);let R=await this.fetch(y,{method:u,headers:{Authorization:`Bearer ${this.token}`,...h.body!==void 0?{"Content-Type":"application/json"}:{},...h.headers},body:h.body===void 0?void 0:JSON.stringify(h.body)});if(R.status===404&&h.notFound==="null")return null;let v=await R.text(),P=v?Sp(v):void 0;if(!R.ok){let M=typeof P==="object"&&P&&"error"in P?String(P.error):v||R.statusText;throw new So(`Fly.io ${u} ${g} failed (${R.status}): ${M}`,R.status,g)}return P}getApp(u){return this.rest("GET",`/apps/${encodeURIComponent(u)}`,{notFound:"null"})}createApp(u,g){return this.rest("POST","/apps",{body:{app_name:u,org_slug:g}})}async listMachines(u){return await this.rest("GET",`/apps/${encodeURIComponent(u)}/machines`)??[]}createMachine(u,g){return this.rest("POST",`/apps/${encodeURIComponent(u)}/machines`,{body:g})}updateMachine(u,g,h,y){return this.rest("POST",`/apps/${encodeURIComponent(u)}/machines/${encodeURIComponent(g)}`,{body:h,headers:{"fly-machine-lease-nonce":y}})}async acquireLease(u,g,h=120){let R=(await this.rest("POST",`/apps/${encodeURIComponent(u)}/machines/${encodeURIComponent(g)}/lease`,{body:{ttl:h}})).data?.nonce;if(!R)throw new So(`Fly.io gave no lease nonce for Machine ${g}`,200,`/machines/${g}/lease`);return R}async releaseLease(u,g,h){await this.rest("DELETE",`/apps/${encodeURIComponent(u)}/machines/${encodeURIComponent(g)}/lease`,{headers:{"fly-machine-lease-nonce":h}})}async waitForState(u,g,h,y={}){await this.rest("GET",`/apps/${encodeURIComponent(u)}/machines/${encodeURIComponent(g)}/wait`,{query:{state:h,timeout:String(y.timeoutSeconds??60),...y.instanceId?{instance_id:y.instanceId}:{}}})}async listVolumes(u){return await this.rest("GET",`/apps/${encodeURIComponent(u)}/volumes`)??[]}createVolume(u,g){return this.rest("POST",`/apps/${encodeURIComponent(u)}/volumes`,{body:{...g,encrypted:!0}})}async setSecrets(u,g){return(await this.rest("POST",`/apps/${encodeURIComponent(u)}/secrets`,{body:{values:g}})).version}async listIpAssignments(u){return(await this.rest("GET",`/apps/${encodeURIComponent(u)}/ip_assignments`)).ips??[]}async assignIp(u,g){let h=await this.rest("POST",`/apps/${encodeURIComponent(u)}/ip_assignments`,{body:{type:g}});if(!h.ip)throw new So(`Fly.io assigned no ${g} address to ${u}`,200,`/apps/${u}/ip_assignments`);return{ip:h.ip,shared:h.shared??g==="shared_v4",region:h.region}}async requestCertificate(u,g){return this.rest("POST",`/apps/${encodeURIComponent(u)}/certificates/acme`,{body:{hostname:g}})}}function Sp(u){try{return JSON.parse(u)}catch{return}}var zc="ts-cloud-role";function nR(u,g){let h=u.fly??{},y=h.app??`${u.project.slug}-${g.environment}`;if(!/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/.test(y))throw Error(`"${y}" is not a valid Fly app name: lowercase letters, digits and dashes, 3 to 63 characters.`);let R=h.internalPort??3000,v=h.count??1;if(!Number.isInteger(v)||v<1)throw Error(`fly.count must be a whole number of at least 1, got ${v}.`);return{app:y,org:h.org??"personal",image:g.image,regions:h.regions?.length?h.regions:["iad"],count:v,internalPort:R,healthPath:h.healthPath??"/",vm:{cpuKind:h.vm?.cpuKind??"shared",cpus:h.vm?.cpus??1,memoryMb:h.vm?.memoryMb??512},...h.volume?{volume:{name:h.volume.name??"data",sizeGb:h.volume.sizeGb,path:h.volume.path}}:{},autoStop:h.autoStop??!1,env:{...g.env,PORT:String(R)},secrets:g.secrets??{},hostnames:h.hostnames??[],release:g.release}}function Rp(u){return{image:u.image,env:u.env,guest:{cpu_kind:u.vm.cpuKind,cpus:u.vm.cpus,memory_mb:u.vm.memoryMb},services:[{protocol:"tcp",internal_port:u.internalPort,ports:[{port:80,handlers:["http"],force_https:!0},{port:443,handlers:["tls","http"]}],autostop:u.autoStop?"stop":"off",autostart:!0,...u.autoStop?{min_machines_running:0}:{},checks:[{type:"http",method:"GET",path:u.healthPath,interval:"15s",timeout:"5s",grace_period:"10s"}]}],restart:{policy:"on-failure",max_retries:10},metadata:{[zc]:"app","ts-cloud-release":u.release}}}async function iR(u,g,h=()=>{}){let y=!1;if(!await u.getApp(g.app))h(`Creating Fly app ${g.app} in ${g.org}`),await u.createApp(g.app,g.org),y=!0;let R;if(Object.keys(g.secrets).length>0)R=await u.setSecrets(g.app,g.secrets),h(`Set ${Object.keys(g.secrets).length} secret(s) (version ${R})`);let v=await u.listIpAssignments(g.app),P=new Set(v.map(sd));if(!P.has("v6"))v.push(await u.assignIp(g.app,"v6")),h("Assigned a dedicated IPv6");if(!P.has("v4")&&!P.has("shared_v4"))v.push(await u.assignIp(g.app,"shared_v4")),h("Assigned a shared IPv4");let M=Rp(g),V=(await u.listMachines(g.app)).filter((Ue)=>{let je=Ue.config?.metadata??{};if(je[zc])return je[zc]==="app";return!je.fly_process_group||je.fly_process_group==="app"}),Ae=g.volume?await u.listVolumes(g.app):[],ye=[],At=[],Tt=new Set;for(let Ue of g.regions){let je=V.filter((K)=>K.region===Ue).sort((K,St)=>K.id.localeCompare(St.id));for(let K=0;K<g.count;K++){let St=je[K];if(St){Tt.add(St.id),await bp(u,g.app,St,M,R),At.push(St.id),h(`Updated ${St.id} (${Ue}) to ${g.image}`);continue}let z=g.volume?[{volume:(await Ap(u,g,Ue,Ae)).id,path:g.volume.path}]:void 0,Xe=await u.createMachine(g.app,{region:Ue,name:`${g.app}-${Ue}-${K+1}`,config:{...M,...z?{mounts:z}:{}},...R!==void 0?{min_secrets_version:R}:{}});await u.waitForState(g.app,Xe.id,"started",{instanceId:Xe.instance_id}),ye.push(Xe.id),h(`Created ${Xe.id} in ${Ue}`)}}let Je=[];for(let Ue of g.hostnames)Je.push(await u.requestCertificate(g.app,Ue));return{app:g.app,url:`https://${g.app}.fly.dev`,createdApp:y,created:ye,updated:At,untouched:V.filter((Ue)=>!Tt.has(Ue.id)).map((Ue)=>Ue.id),ips:v,certificates:Je}}async function bp(u,g,h,y,R){let v=await u.acquireLease(g,h.id);try{let P=await u.updateMachine(g,h.id,{config:{...y,...h.config?.mounts?.length?{mounts:h.config.mounts}:{}},...R!==void 0?{min_secrets_version:R}:{}},v);await u.waitForState(g,h.id,"started",{instanceId:P.instance_id})}finally{await u.releaseLease(g,h.id,v)}}async function Ap(u,g,h,y){let R=y.find((P)=>P.region===h&&P.name===g.volume.name&&!P.attached_machine_id);if(R)return R.attached_machine_id="pending",R;let v=await u.createVolume(g.app,{name:g.volume.name,region:h,size_gb:g.volume.sizeGb});return y.push({...v,attached_machine_id:"pending"}),v}import{createHmac as cd,createSign as Pp,timingSafeEqual as Tp}from"node:crypto";function Di(u){let g=u instanceof Date?u.getTime():u;if(!Number.isFinite(g))throw TypeError("Media access timestamp must be finite");return Math.floor(g/1000)}function ld(u){return Buffer.from(u).toString("base64").replaceAll("+","-").replaceAll("=","_").replaceAll("/","~")}function wp(u){return Buffer.from(u).toString("base64url")}function ud(u,g=!1){if(!u||/[\r\n]/.test(u))throw TypeError("Media resource is invalid");let h=g?u.replaceAll("*","wildcard"):u,y=new URL(h);if(!["http:","https:"].includes(y.protocol))throw TypeError("Media resource must use HTTP or HTTPS");if(!g&&u.includes("*"))throw TypeError("Canned policies cannot contain wildcards")}function dd(u,g){let h=Pp("RSA-SHA1");return h.update(u),h.end(),ld(h.sign(g))}function gd(u){ud(u.resource,!0);let h={DateLessThan:{"AWS:EpochTime":Di(u.expiresAt)}};if(u.activeAt!==void 0)h.DateGreaterThan={"AWS:EpochTime":Di(u.activeAt)};if(u.ipAddress)h.IpAddress={"AWS:SourceIp":u.ipAddress};return JSON.stringify({Statement:[{Resource:u.resource,Condition:h}]})}function uR(u,g,h){ud(u);let y=Di(g),R=u.includes("?")?"&":"?",v=gd({resource:u,expiresAt:g}),P=dd(v,h.privateKey);return`${u}${R}Expires=${y}&Signature=${encodeURIComponent(P)}&Key-Pair-Id=${encodeURIComponent(h.keyPairId)}`}function dR(u,g){let h=gd(u);return{"CloudFront-Key-Pair-Id":g.keyPairId,"CloudFront-Policy":ld(Buffer.from(h)),"CloudFront-Signature":dd(h,g.privateKey)}}function pR(u){if(!u.resource||/[\r\n]/.test(u.resource))throw TypeError("Media resource is invalid");let g=wp(Buffer.from(JSON.stringify({resource:u.resource,expiresAt:Di(u.expiresAt),...u.keyId?{keyId:u.keyId}:{},...u.audience?{audience:u.audience}:{}}))),h=cd("sha256",u.secret).update(g).digest("base64url");return`${g}.${h}`}function mR(u,g){let[h,y,R]=u.split(".");if(!h||!y||R)return null;let v=cd("sha256",g.secret).update(h).digest(),P;try{P=Buffer.from(y,"base64url")}catch{return null}if(v.length!==P.length||!Tp(v,P))return null;try{let M=JSON.parse(Buffer.from(h,"base64url").toString("utf8")),V=Di(g.now??Date.now());if(!M.resource||!Number.isSafeInteger(M.expiresAt)||M.expiresAt<=V)return null;if(g.resource&&M.resource!==g.resource)return null;if(g.audience&&M.audience!==g.audience)return null;return M}catch{return null}}function gR(u,g=!1){let h=new URL(u,"https://media.invalid").pathname.toLowerCase(),y=/\.(?:m3u8|mpd|vtt|json)$/.test(h),R=/\.(?:key|license)$/.test(h)||h.includes("/keys/")||h.includes("/licenses/");return{"Accept-Ranges":"bytes","Cache-Control":g&&(y||R)?"private, no-store":y?"public, max-age=5, s-maxage=30, stale-while-revalidate=30":"public, max-age=31536000, immutable","Cross-Origin-Resource-Policy":"cross-origin","X-Content-Type-Options":"nosniff"}}function fR(u){if(!/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(u.bucket))throw TypeError("Media bucket is invalid");if(!/^[a-z0-9-]+$/.test(u.region))throw TypeError("Media bucket region is invalid");let g=u.prefix?`/${u.prefix.replace(/^\/+|\/+$/g,"")}`:"",h=u.protected??!1,y="public, max-age=31536000, immutable",R=h?"private, no-store":"public, max-age=5, s-maxage=30, stale-while-revalidate=30";return{originDomain:`${u.bucket}.s3.${u.region}.amazonaws.com`,originPath:g,domain:u.domain,trustedKeyGroupIds:[...new Set(u.trustedKeyGroupIds??[])],behaviors:[{pathPattern:"*.m3u8",cacheControl:R,compress:!0,signed:h},{pathPattern:"*.mpd",cacheControl:R,compress:!0,signed:h},{pathPattern:"*.vtt",cacheControl:R,compress:!0,signed:h},{pathPattern:"*",cacheControl:y,compress:!1,signed:h}],responseHeaders:{"Access-Control-Allow-Headers":"Range, If-Range","Access-Control-Allow-Methods":"GET, HEAD, OPTIONS","Access-Control-Allow-Origin":"*","Access-Control-Expose-Headers":"Accept-Ranges, Content-Length, Content-Range, ETag","X-Content-Type-Options":"nosniff"}}}class Ip{builder;config;environment;mergedConfig;serverEipLogicalIds=new Map;storageFailoverReplicas=[];constructor(u){this.config=u.config,this.environment=u.environment,this.builder=new hl(`${this.config.project.name} - ${this.environment}`),this.mergedConfig=this.mergeEnvironmentConfig()}mergeEnvironmentConfig(){let g=this.config.environments[this.environment]?.infrastructure;if(!g)return this.config;return{...this.config,infrastructure:{...this.config.infrastructure,...g,compute:{...this.config.infrastructure?.compute,...g.compute},storage:{...this.config.infrastructure?.storage,...g.storage},functions:{...this.config.infrastructure?.functions,...g.functions},servers:{...this.config.infrastructure?.servers,...g.servers},databases:{...this.config.infrastructure?.databases,...g.databases},cdn:{...this.config.infrastructure?.cdn,...g.cdn},sftp:g.sftp??this.config.infrastructure?.sftp,queues:{...this.config.infrastructure?.queues,...g.queues},redirects:{...this.config.infrastructure?.redirects,...g.redirects},realtime:{...this.config.infrastructure?.realtime,...g.realtime},cache:{...this.config.infrastructure?.cache,...g.cache},fileSystem:{...this.config.infrastructure?.fileSystem,...g.fileSystem},email:{...this.config.infrastructure?.email,...g.email},search:{...this.config.infrastructure?.search,...g.search},ai:{...this.config.infrastructure?.ai,...g.ai}}}}shouldDeploy(u){if(u.environments&&!u.environments.includes(this.environment))return!1;if(u.requiresFeatures){let g=this.config.features||{};if(!u.requiresFeatures.every((y)=>g[y]===!0))return!1}if(u.regions){let g=this.config.environments[this.environment]?.region||this.config.project.region;if(!u.regions.includes(g))return!1}if(u.condition&&typeof u.condition==="function")return u.condition(this.config,this.environment);return!0}resolveApiOriginPort(){let u=this.mergedConfig.infrastructure?.api?.port??this.mergedConfig.ports?.api,g=Number(u||3008);return Number.isFinite(g)&&g>0?g:3008}defaultComputeCachePathPatterns(){return["/api/*","/auth/*","/publisher/api/*","/publisher/*","/publish","/publish/*","/analytics/*","/packages/*","/npm/*","/zig/*","/php/*","/commits/*","/health","/dashboard/*","/search","/login","/logout","/signup","/account","/account/*"]}shouldRouteStorageBucketToCompute(u,g){return u==="public"||g.routeCompute===!0}storageBucketName(u,g,h){return this.mergedConfig.infrastructure?.storage?.[h]?.bucket??`${u}-${g}-${h}`}fileSystemLogicalId(u,g,h){return t(r({slug:`${u}-${h}`,environment:g,resourceType:"efs"}))}resolveSftpStorage(u,g,h){let y=u.storage;if(y?.type==="efs"&&y.fileSystem){if(!this.mergedConfig.infrastructure?.fileSystem?.[y.fileSystem])throw Error(`sftp: storage.fileSystem "${y.fileSystem}" is not defined in infrastructure.fileSystem`);return{type:"efs",fileSystemId:{Ref:this.fileSystemLogicalId(g,h,y.fileSystem)},posixProfile:y.posixProfile}}if(y?.type==="s3"&&y.storageBucket){if(!this.mergedConfig.infrastructure?.storage?.[y.storageBucket])throw Error(`sftp: storage.storageBucket "${y.storageBucket}" is not defined in infrastructure.storage`);return{type:"s3",bucket:this.storageBucketName(g,h,y.storageBucket)}}return}resolveComputeCachePathPatterns(u){if(u&&u.length>0)return u;return this.defaultComputeCachePathPatterns()}createComputeCacheBehavior(u,g){return{PathPattern:u,TargetOriginId:g,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS","PUT","POST","PATCH","DELETE"],CachedMethods:["GET","HEAD"],Compress:!0,CachePolicyId:"4135ea2d-6df8-44a3-9df3-4b5a84be39ad",OriginRequestPolicyId:"b689b0a8-53d0-40ab-baf2-68738e2966ac"}}appendComputeAppOrigin(u,g,h,y,R,v){let P=this.serverEipLogicalIds.get("app"),M=this.serverEipLogicalIds.get("appInstance");if(!P)return;let V=`EC2-${y}-${R}-api`,Ae=this.mergedConfig.infrastructure?.servers?.app?.region||this.mergedConfig.project?.region||"us-east-1",ye=Ae==="us-east-1"?".compute-1.amazonaws.com":`.${Ae}.compute.amazonaws.com`,At={"Fn::Join":["",["ec2-",{"Fn::Join":["-",{"Fn::Split":[".",{Ref:P}]}]},ye]]},Tt=this.resolveApiOriginPort();if(u.push({Id:V,DomainName:At,CustomOriginConfig:{HTTPPort:Tt,HTTPSPort:443,OriginProtocolPolicy:"http-only",OriginSSLProtocols:["TLSv1.2"]}}),h.push(P),M)h.push(M);for(let Je of v)g.push(this.createComputeCacheBehavior(Je,V))}buildCaddyfile(u){let g=u.filter(([,R])=>typeof R.domain==="string"&&R.domain&&typeof R.port==="number");if(g.length===0)return;let h=new Map;for(let[,R]of g){let v=h.get(R.domain)??[];v.push({port:R.port,path:R.path}),h.set(R.domain,v)}let y=[];for(let[R,v]of h){let M=[...v].sort((V,Ae)=>{let ye=!V.path||V.path==="/",At=!Ae.path||Ae.path==="/";if(ye&&!At)return 1;if(!ye&&At)return-1;return(Ae.path?.length??0)-(V.path?.length??0)}).map((V)=>{let Ae=!V.path||V.path==="/",ye=`reverse_proxy localhost:${V.port}`;return Ae?` handle {
|
|
2
|
-
${
|
|
3
|
-
}`:` handle ${
|
|
4
|
-
${
|
|
5
|
-
}`});
|
|
1
|
+
import{JI,XI,gl,Pm,pl,ml,ln,H,l,fl,km,J,hl,p,py,C,rt,Zi,un,R,ge,Al,my,r,t,YI,ZI,m,eb,tb,rb,nb,ib,ur,yl,es,fy,sb,ob,$e,nt,Sl,Nm,Bm,ab,xm,Om,_m,Wo,Il,pe,gn,Nt,Jo,Bt,we,hy,bl,Xo,Lm,$m,Mm,Fm,Cl,cb,X,v,Ay,yy,Um,a,I,it,f,Qm,st,Y,xt,db,Hm,Z,El,Me,gr,Yo,me,vl,fe,Sy,Zo,ee,te,L,ts,D,pr,Ot,re,e,pn,lb,Gm,Tl,ub,gb,pb,mb,wl,fb,hb,Ab,yb,Sb,Ib,bb,Cb,Eb,vb,Tb,wb,Iy,by,Cy,Ey,vy,Ty,wy,mr,Vm,qm,fr,rs,Rb,jm,Db,Pb,mn,kb,Nb,Bb,xb,Ob,_b,zm,k,Re,ns,ea,fn,is,ta,Ry,Lb,ss,ra,Dy,Py,ky,Ny,By,xy,os,$b,na,Oy,Km,q,_y,ot,ia,_t,Ly,Wm,$y,My,Fy,Jm,j,Mb,Fb,Ub,Qb,Uy,Hb,ne,Gb,Vb,qb,Rl,Dl,Qy,Xm,Ym,jb,zb,Kb,Wb,Jb,Hy,Pl,Xb,Yb,Zb,eC,tC,Gy,Vy,rC,nC,iC,sC,oC,aC,cC,qy,dC,Zm,kl,lC,uC,gC,pC,ef,tf,rf,mC,Nl,jy,zy,Ky,Wy,Bl,Jy,fC,hC,AC,yC,SC,IC,bC,Xy,Yy,Zy,eS,nf,CC,EC,vC,TC,wC,RC,DC,PC,kC,tS,rS,nS,iS,NC,BC,sf,xC,of,af,cf,df,OC,_C,LC,$C,Fe,as,MC,FC,UC,QC,HC,GC,VC,qC,sS,jC,zC,lf,uf,KC,WC,oS,JC,XC,YC,ZC,eE,tE,rE,nE,iE,sE,oE,aE,cE,dE,lE,uE,gE,pE,mE,fE,hE,xl,aS,Ol,cS,_l,dS,Ll,lS,$l,uS,hr,gS,Ml,pS,cs,mS,Fl,fS,Ul,hS,gf,AE,pf,yE,Ql,AS,Ar,yS,mf,SE,Hl,SS,ff,IE,hf,bE,Af,CE,Gl,IS,yf,EE,Sf,vE,If,TE,bf,wE,Vl,bS,ql,CS,Cf,RE,Ef,DE,vf,PE,Tf,kE,wf,NE,Rf,BE,jl,ES,Df,xE,Pf,OE,kf,_E,Nf,LE,Bf,$E,xf,ME,Of,_f,Lf,$f,Mf,FE,Ff,UE,Uf,QE,Qf,HE,at,Hf,vS,sa,zl,hn,TS,ds,yr,Kl,Wl,wS,RS,Jl,Gf,Xl,Vf,GE,VE,oa,aa,ca,Yl,An,Zl,eu,ls,da,la,qf,DS,PS,kS,qE,jf,zf,jE,NS,Kf,BS,xS,OS,zE,Wf,yn,tu,Jf,KE,_S,ua,ga,ru,Xf,WE,nu,LS,Yf,JE,Zf,XE,eh,YE}from"./chunk-316cbffs.js";import{iu,N,su,pa,FS,th,US,QS,HS,GS,VS,qS,jS,zS}from"./chunk-3t5a5gr2.js";import{Sa,rh,pu,WS,fs,mu,Ia,nh,ih,In,ba,fu,ct,hu,Au,yu,hs,bn,sh,dt,Su,Ca,Ea,va,Iu,bu,Cu,As,Ta,Eu,Cn,ys,wa,Lt,oh,vu,Tu,ah,wu,Ra,Sr,Ru,Du,Pu,ku,lt,ch,Nu,Ir,Da,Pa,ka,Ss,Is,En,Bu,bs,Na,Ba,vn,xu,Tn,dh,lh,$t,wn,xa,uh,Ou,_u,Oa,Lu,$u,Mu,JS,he,Fu,XS,YS,Cs,Es,ZS,De,gh,Mt,Ue,Qe,Ft,Uu,Qu,Hu,Gu,ph,mh,_a,fh,hh,La,Vu,qu,Ah,Ae,yh,ju,Pe,$a,ut,Ut,Qt,Rn,vs,zu,eI,Sh,Ih,bh,Ch,Ma,Fa,Ku,Ua,Dn,Qa,Wu,Ts,Ju,Ha,Ga,Va,qa,ja,za,Ka,Wa,Xu,br,Ja,Yu,Pn,Zu,Xa,Ya,eg,Cr,Za,ec,Eh,tI,ws,rI,nI,iI,tc,rc,tg,vh,nc,kn,rg,Rs,ng,ig,sg,ic,Th,sc,og,wh,oc,Rh,Nn,Bn,ag,Ds,pg,Ag,yg,Sg,Ig,bg,ks,Er,cc,xn,On,vr,gt,_h,lc,Lh,$h,vg,uc,Bs,Tg,gc,Gt,Ge,wg,pc,mc,Rg,fc,hc,Ac,yc,Dg,Pg,Ln,Sc,Ic,bc,Cc,kg,xs,$n,Os,Ec}from"./chunk-bxn4n4qa.js";import{gu}from"./chunk-tes38y8x.js";import{Q,Wr,Rd,vo,d,nm,im}from"./chunk-zpkf2jt2.js";import{wt,wd,Li,$i,Mi,rm}from"./chunk-bxq80y73.js";import{pA,mA,fA,$p,Mp,Fp,hA,Up,Qp,AA,yA,Hp,SA,IA,vd,Gp,bA,CA,Vp,EA,vA,TA,wA,RA,DA,PA,_i,Td,qp,jp,zp,Kp,Wp,Jp,Xp,Yp,Zp,em,tm,kA,NA,BA,xA,OA,_A,LA}from"./chunk-xa2ahxxa.js";import{Ze,Dd,Te,ir,Pd,O,sr,Rt,kd,To,FA,sm,Nd,s,Fi,wo,Jr,Bd,xd,Xr,UA,Od,jI,zI,Ro,KI,WI,_d,Ld,Ui,Do,ue,c,Po,et,Yr,QA,HA,$d,GA,ko,VA,qA,No,Md,Zr,Bo,jA,or,Fd,om,ar,en,zA,xo,KA,_,WA,Ud,am,Oo,JA,XA,YA,Qd,Qi,_o,Hd,cr,Dt,ZA,tn,ey,Gd,cm,dm,Lo,Vd,qd,Pt,lm,Hi,jd,$o,rn,um,Gi,zd,Kd,Vi,gm,Wd,Jd,Xd,Yd,Mo,pm,mm,ty,fm,Zd,qi,hm,Am,Fo,nn,Uo,Qo,Ho,el,ym,tl,Go,ry,dr,ny,rl,sn,nl,iy,il,Sm,ji,on,Vo,zi,tt,sy,Im,bm,lr,sl,ol,al,Ki,Wi,Ji,oy,ay,kt,qo,cy,an,Cm,jo,dy,Em,vm,Tm,ly,wm,Rm,uy,i,gy,Xi,zo,Ko}from"./chunk-n6tgqd7d.js";import{n}from"./chunk-vaczx33v.js";import{Be,ht,Wn}from"./chunk-kken64a9.js";import{ke,Qs,Hs,xg,Dc,Pc,Gs,kc,pt,ie,mt,ye,w,Ne,Hn,Nc,Ve,Bc,Gn,Vn,qn,Vt,qt,jt,jn,wr,xc,Rr,Dr,ft,zn,_c,zt,Pr,Kn,$c,Mg,Fg,Mc,kr}from"./chunk-4bysqvjh.js";import{o}from"./chunk-byt4re1c.js";import{y}from"./chunk-cjqw4ns4.js";import{jr,Le}from"./chunk-93xknw91.js";import{Fc,qe,b}from"./chunk-4afz3x6w.js";import{E}from"./chunk-p1agrrnq.js";import{At,be,ze,Ug,Kt,Uc,Qc,Wt,G,Jn,Jt,Xt,Xn,zs,Ks,Nr,Br,Yn,xr,Ws,Js,Xs,Ys,oe,Zs,eo,to,ro,Ce,no,io,so,B,Hc,Zn,Gc,Ee,Or}from"./chunk-1kagq9vp.js";import{je}from"./chunk-7y1pknej.js";import{se}from"./chunk-sp0ne2ax.js";import{Qg,Vc,Hg,ae,qc,ei,Gg,jc,Vg,qg,Jh,Lr,ao,F,Mr,ri,ni,Fr,Qr,ii,co,si,oi,ai,ci,li,Hr,ve,Wc,St,Jc,Gr,Zc,We,ce,Je,Xe,bt,pi,mi,xe,Et,Oe,hi,Ai,yi,Si,Ii,bi,Ci,Vr,U,po,Ap,cd,vi,Ti,wi,Ri,md,fo,fd,rr,Pi,ho,Ao,hd,ki,Ad}from"./chunk-71p77ycz.js";import{So,bo,Co,x,Op,_e}from"./chunk-bzdfx427.js";import{ou,ma,gs,ps,fa,au,cu,ms,ha,Aa,lu,uu,ya}from"./chunk-r2gtxb7n.js";import{vc,Mn,Un,_s,Ls,$s,Ms,Tc,wc}from"./chunk-z1087cap.js";import{Rc}from"./chunk-stqg97mh.js";import{de,Ni,yo,yd,Bi,Np}from"./chunk-wggeew90.js";import{cl,cn,dl,ll,ul,Yi,Dm,dn}from"./chunk-x1k1sbgf.js";import{$S,us}from"./chunk-wnevarf9.js";var tR="https://api.machines.dev/v1";class Io extends Error{status;path;constructor(u,g,h){super(u);this.status=g;this.path=h;this.name="FlyApiError"}}function sd(u){let g=u.ip.includes(":");if(g&&u.ip.startsWith("fdaa:"))return"private_v6";if(u.shared)return"shared_v4";return g?"v6":"v4"}class hp{token;fetch;constructor(u){if(!u.token)throw Error("Fly.io needs an API token: set FLY_API_TOKEN (`fly tokens create deploy` makes one scoped to an app).");this.token=u.token,this.fetch=u.fetch??fetch}async rest(u,g,h={}){let S=new URL(`https://api.machines.dev/v1${g}`);for(let[M,W]of Object.entries(h.query??{}))S.searchParams.set(M,W);let A=await this.fetch(S,{method:u,headers:{Authorization:`Bearer ${this.token}`,...h.body!==void 0?{"Content-Type":"application/json"}:{},...h.headers},body:h.body===void 0?void 0:JSON.stringify(h.body)});if(A.status===404&&h.notFound==="null")return null;let T=await A.text(),P=T?yp(T):void 0;if(!A.ok){let M=typeof P==="object"&&P&&"error"in P?String(P.error):T||A.statusText;throw new Io(`Fly.io ${u} ${g} failed (${A.status}): ${M}`,A.status,g)}return P}getApp(u){return this.rest("GET",`/apps/${encodeURIComponent(u)}`,{notFound:"null"})}createApp(u,g){return this.rest("POST","/apps",{body:{app_name:u,org_slug:g}})}async listMachines(u){return await this.rest("GET",`/apps/${encodeURIComponent(u)}/machines`)??[]}createMachine(u,g){return this.rest("POST",`/apps/${encodeURIComponent(u)}/machines`,{body:g})}updateMachine(u,g,h,S){return this.rest("POST",`/apps/${encodeURIComponent(u)}/machines/${encodeURIComponent(g)}`,{body:h,headers:{"fly-machine-lease-nonce":S}})}async acquireLease(u,g,h=120){let A=(await this.rest("POST",`/apps/${encodeURIComponent(u)}/machines/${encodeURIComponent(g)}/lease`,{body:{ttl:h}})).data?.nonce;if(!A)throw new Io(`Fly.io gave no lease nonce for Machine ${g}`,200,`/machines/${g}/lease`);return A}async releaseLease(u,g,h){await this.rest("DELETE",`/apps/${encodeURIComponent(u)}/machines/${encodeURIComponent(g)}/lease`,{headers:{"fly-machine-lease-nonce":h}})}async waitForState(u,g,h,S={}){await this.rest("GET",`/apps/${encodeURIComponent(u)}/machines/${encodeURIComponent(g)}/wait`,{query:{state:h,timeout:String(S.timeoutSeconds??60),...S.instanceId?{instance_id:S.instanceId}:{}}})}async listVolumes(u){return await this.rest("GET",`/apps/${encodeURIComponent(u)}/volumes`)??[]}createVolume(u,g){return this.rest("POST",`/apps/${encodeURIComponent(u)}/volumes`,{body:{...g,encrypted:!0}})}async setSecrets(u,g){return(await this.rest("POST",`/apps/${encodeURIComponent(u)}/secrets`,{body:{values:g}})).version}async listIpAssignments(u){return(await this.rest("GET",`/apps/${encodeURIComponent(u)}/ip_assignments`)).ips??[]}async assignIp(u,g){let h=await this.rest("POST",`/apps/${encodeURIComponent(u)}/ip_assignments`,{body:{type:g}});if(!h.ip)throw new Io(`Fly.io assigned no ${g} address to ${u}`,200,`/apps/${u}/ip_assignments`);return{ip:h.ip,shared:h.shared??g==="shared_v4",region:h.region}}async requestCertificate(u,g){return this.rest("POST",`/apps/${encodeURIComponent(u)}/certificates/acme`,{body:{hostname:g}})}}function yp(u){try{return JSON.parse(u)}catch{return}}var Lc="ts-cloud-role";function iR(u,g){let h=u.fly??{},S=h.app??`${u.project.slug}-${g.environment}`;if(!/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/.test(S))throw Error(`"${S}" is not a valid Fly app name: lowercase letters, digits and dashes, 3 to 63 characters.`);let A=h.internalPort??3000,T=h.count??1;if(!Number.isInteger(T)||T<1)throw Error(`fly.count must be a whole number of at least 1, got ${T}.`);return{app:S,org:h.org??"personal",image:g.image,regions:h.regions?.length?h.regions:["iad"],count:T,internalPort:A,healthPath:h.healthPath??"/",vm:{cpuKind:h.vm?.cpuKind??"shared",cpus:h.vm?.cpus??1,memoryMb:h.vm?.memoryMb??512},...h.volume?{volume:{name:h.volume.name??"data",sizeGb:h.volume.sizeGb,path:h.volume.path}}:{},autoStop:h.autoStop??!1,env:{...g.env,PORT:String(A)},secrets:g.secrets??{},hostnames:h.hostnames??[],release:g.release}}function Cp(u){return{image:u.image,env:u.env,guest:{cpu_kind:u.vm.cpuKind,cpus:u.vm.cpus,memory_mb:u.vm.memoryMb},services:[{protocol:"tcp",internal_port:u.internalPort,ports:[{port:80,handlers:["http"],force_https:!0},{port:443,handlers:["tls","http"]}],autostop:u.autoStop?"stop":"off",autostart:!0,...u.autoStop?{min_machines_running:0}:{},checks:[{type:"http",method:"GET",path:u.healthPath,interval:"15s",timeout:"5s",grace_period:"10s"}]}],restart:{policy:"on-failure",max_retries:10},metadata:{[Lc]:"app","ts-cloud-release":u.release}}}async function oR(u,g,h=()=>{}){let S=!1;if(!await u.getApp(g.app))h(`Creating Fly app ${g.app} in ${g.org}`),await u.createApp(g.app,g.org),S=!0;let A;if(Object.keys(g.secrets).length>0)A=await u.setSecrets(g.app,g.secrets),h(`Set ${Object.keys(g.secrets).length} secret(s) (version ${A})`);let T=await u.listIpAssignments(g.app),P=new Set(T.map(sd));if(!P.has("v6"))T.push(await u.assignIp(g.app,"v6")),h("Assigned a dedicated IPv6");if(!P.has("v4")&&!P.has("shared_v4"))T.push(await u.assignIp(g.app,"shared_v4")),h("Assigned a shared IPv4");let M=Cp(g),W=(await u.listMachines(g.app)).filter((Se)=>{let Ie=Se.config?.metadata??{};if(Ie[Lc])return Ie[Lc]==="app";return!Ie.fly_process_group||Ie.fly_process_group==="app"}),le=g.volume?await u.listVolumes(g.app):[],K=[],Ct=[],Tt=new Set;for(let Se of g.regions){let Ie=W.filter((z)=>z.region===Se).sort((z,Ye)=>z.id.localeCompare(Ye.id));for(let z=0;z<g.count;z++){let Ye=Ie[z];if(Ye){Tt.add(Ye.id),await Sp(u,g.app,Ye,M,A),Ct.push(Ye.id),h(`Updated ${Ye.id} (${Se}) to ${g.image}`);continue}let V=g.volume?[{volume:(await Rp(u,g,Se,le)).id,path:g.volume.path}]:void 0,Ke=await u.createMachine(g.app,{region:Se,name:`${g.app}-${Se}-${z+1}`,config:{...M,...V?{mounts:V}:{}},...A!==void 0?{min_secrets_version:A}:{}});await u.waitForState(g.app,Ke.id,"started",{instanceId:Ke.instance_id}),K.push(Ke.id),h(`Created ${Ke.id} in ${Se}`)}}let He=[];for(let Se of g.hostnames)He.push(await u.requestCertificate(g.app,Se));return{app:g.app,url:`https://${g.app}.fly.dev`,createdApp:S,created:K,updated:Ct,untouched:W.filter((Se)=>!Tt.has(Se.id)).map((Se)=>Se.id),ips:T,certificates:He}}async function Sp(u,g,h,S,A){let T=await u.acquireLease(g,h.id);try{let P=await u.updateMachine(g,h.id,{config:{...S,...h.config?.mounts?.length?{mounts:h.config.mounts}:{}},...A!==void 0?{min_secrets_version:A}:{}},T);await u.waitForState(g,h.id,"started",{instanceId:P.instance_id})}finally{await u.releaseLease(g,h.id,T)}}async function Rp(u,g,h,S){let A=S.find((P)=>P.region===h&&P.name===g.volume.name&&!P.attached_machine_id);if(A)return A.attached_machine_id="pending",A;let T=await u.createVolume(g.app,{name:g.volume.name,region:h,size_gb:g.volume.sizeGb});return S.push({...T,attached_machine_id:"pending"}),T}import{createHmac as ad,createSign as bp,timingSafeEqual as Pp}from"node:crypto";function Di(u){let g=u instanceof Date?u.getTime():u;if(!Number.isFinite(g))throw TypeError("Media access timestamp must be finite");return Math.floor(g/1000)}function ld(u){return Buffer.from(u).toString("base64").replaceAll("+","-").replaceAll("=","_").replaceAll("/","~")}function Tp(u){return Buffer.from(u).toString("base64url")}function ud(u,g=!1){if(!u||/[\r\n]/.test(u))throw TypeError("Media resource is invalid");let h=g?u.replaceAll("*","wildcard"):u,S=new URL(h);if(!["http:","https:"].includes(S.protocol))throw TypeError("Media resource must use HTTP or HTTPS");if(!g&&u.includes("*"))throw TypeError("Canned policies cannot contain wildcards")}function dd(u,g){let h=bp("RSA-SHA1");return h.update(u),h.end(),ld(h.sign(g))}function pd(u){ud(u.resource,!0);let h={DateLessThan:{"AWS:EpochTime":Di(u.expiresAt)}};if(u.activeAt!==void 0)h.DateGreaterThan={"AWS:EpochTime":Di(u.activeAt)};if(u.ipAddress)h.IpAddress={"AWS:SourceIp":u.ipAddress};return JSON.stringify({Statement:[{Resource:u.resource,Condition:h}]})}function dR(u,g,h){ud(u);let S=Di(g),A=u.includes("?")?"&":"?",T=pd({resource:u,expiresAt:g}),P=dd(T,h.privateKey);return`${u}${A}Expires=${S}&Signature=${encodeURIComponent(P)}&Key-Pair-Id=${encodeURIComponent(h.keyPairId)}`}function pR(u,g){let h=pd(u);return{"CloudFront-Key-Pair-Id":g.keyPairId,"CloudFront-Policy":ld(Buffer.from(h)),"CloudFront-Signature":dd(h,g.privateKey)}}function mR(u){if(!u.resource||/[\r\n]/.test(u.resource))throw TypeError("Media resource is invalid");let g=Tp(Buffer.from(JSON.stringify({resource:u.resource,expiresAt:Di(u.expiresAt),...u.keyId?{keyId:u.keyId}:{},...u.audience?{audience:u.audience}:{}}))),h=ad("sha256",u.secret).update(g).digest("base64url");return`${g}.${h}`}function gR(u,g){let[h,S,A]=u.split(".");if(!h||!S||A)return null;let T=ad("sha256",g.secret).update(h).digest(),P;try{P=Buffer.from(S,"base64url")}catch{return null}if(T.length!==P.length||!Pp(T,P))return null;try{let M=JSON.parse(Buffer.from(h,"base64url").toString("utf8")),W=Di(g.now??Date.now());if(!M.resource||!Number.isSafeInteger(M.expiresAt)||M.expiresAt<=W)return null;if(g.resource&&M.resource!==g.resource)return null;if(g.audience&&M.audience!==g.audience)return null;return M}catch{return null}}function fR(u,g=!1){let h=new URL(u,"https://media.invalid").pathname.toLowerCase(),S=/\.(?:m3u8|mpd|vtt|json)$/.test(h),A=/\.(?:key|license)$/.test(h)||h.includes("/keys/")||h.includes("/licenses/");return{"Accept-Ranges":"bytes","Cache-Control":g&&(S||A)?"private, no-store":S?"public, max-age=5, s-maxage=30, stale-while-revalidate=30":"public, max-age=31536000, immutable","Cross-Origin-Resource-Policy":"cross-origin","X-Content-Type-Options":"nosniff"}}function hR(u){if(!/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(u.bucket))throw TypeError("Media bucket is invalid");if(!/^[a-z0-9-]+$/.test(u.region))throw TypeError("Media bucket region is invalid");let g=u.prefix?`/${u.prefix.replace(/^\/+|\/+$/g,"")}`:"",h=u.protected??!1,S="public, max-age=31536000, immutable",A=h?"private, no-store":"public, max-age=5, s-maxage=30, stale-while-revalidate=30";return{originDomain:`${u.bucket}.s3.${u.region}.amazonaws.com`,originPath:g,domain:u.domain,trustedKeyGroupIds:[...new Set(u.trustedKeyGroupIds??[])],behaviors:[{pathPattern:"*.m3u8",cacheControl:A,compress:!0,signed:h},{pathPattern:"*.mpd",cacheControl:A,compress:!0,signed:h},{pathPattern:"*.vtt",cacheControl:A,compress:!0,signed:h},{pathPattern:"*",cacheControl:S,compress:!1,signed:h}],responseHeaders:{"Access-Control-Allow-Headers":"Range, If-Range","Access-Control-Allow-Methods":"GET, HEAD, OPTIONS","Access-Control-Allow-Origin":"*","Access-Control-Expose-Headers":"Accept-Ranges, Content-Length, Content-Range, ETag","X-Content-Type-Options":"nosniff"}}}class wp{builder;config;environment;mergedConfig;serverEipLogicalIds=new Map;storageFailoverReplicas=[];constructor(u){this.config=u.config,this.environment=u.environment,this.builder=new Al(`${this.config.project.name} - ${this.environment}`),this.mergedConfig=this.mergeEnvironmentConfig()}mergeEnvironmentConfig(){let g=this.config.environments[this.environment]?.infrastructure;if(!g)return this.config;return{...this.config,infrastructure:{...this.config.infrastructure,...g,compute:{...this.config.infrastructure?.compute,...g.compute},storage:{...this.config.infrastructure?.storage,...g.storage},functions:{...this.config.infrastructure?.functions,...g.functions},servers:{...this.config.infrastructure?.servers,...g.servers},databases:{...this.config.infrastructure?.databases,...g.databases},cdn:{...this.config.infrastructure?.cdn,...g.cdn},sftp:g.sftp??this.config.infrastructure?.sftp,queues:{...this.config.infrastructure?.queues,...g.queues},redirects:{...this.config.infrastructure?.redirects,...g.redirects},realtime:{...this.config.infrastructure?.realtime,...g.realtime},cache:{...this.config.infrastructure?.cache,...g.cache},fileSystem:{...this.config.infrastructure?.fileSystem,...g.fileSystem},email:{...this.config.infrastructure?.email,...g.email},search:{...this.config.infrastructure?.search,...g.search},ai:{...this.config.infrastructure?.ai,...g.ai}}}}shouldDeploy(u){if(u.environments&&!u.environments.includes(this.environment))return!1;if(u.requiresFeatures){let g=this.config.features||{};if(!u.requiresFeatures.every((S)=>g[S]===!0))return!1}if(u.regions){let g=this.config.environments[this.environment]?.region||this.config.project.region;if(!u.regions.includes(g))return!1}if(u.condition&&typeof u.condition==="function")return u.condition(this.config,this.environment);return!0}resolveApiOriginPort(){let u=this.mergedConfig.infrastructure?.api?.port??this.mergedConfig.ports?.api,g=Number(u||3008);return Number.isFinite(g)&&g>0?g:3008}defaultComputeCachePathPatterns(){return["/api/*","/auth/*","/publisher/api/*","/publisher/*","/publish","/publish/*","/analytics/*","/packages/*","/npm/*","/zig/*","/php/*","/commits/*","/health","/dashboard/*","/search","/login","/logout","/signup","/account","/account/*"]}shouldRouteStorageBucketToCompute(u,g){return u==="public"||g.routeCompute===!0}storageBucketName(u,g,h){return this.mergedConfig.infrastructure?.storage?.[h]?.bucket??`${u}-${g}-${h}`}fileSystemLogicalId(u,g,h){return t(r({slug:`${u}-${h}`,environment:g,resourceType:"efs"}))}resolveSftpStorage(u,g,h){let S=u.storage;if(S?.type==="efs"&&S.fileSystem){if(!this.mergedConfig.infrastructure?.fileSystem?.[S.fileSystem])throw Error(`sftp: storage.fileSystem "${S.fileSystem}" is not defined in infrastructure.fileSystem`);return{type:"efs",fileSystemId:{Ref:this.fileSystemLogicalId(g,h,S.fileSystem)},posixProfile:S.posixProfile}}if(S?.type==="s3"&&S.storageBucket){if(!this.mergedConfig.infrastructure?.storage?.[S.storageBucket])throw Error(`sftp: storage.storageBucket "${S.storageBucket}" is not defined in infrastructure.storage`);return{type:"s3",bucket:this.storageBucketName(g,h,S.storageBucket)}}return}resolveComputeCachePathPatterns(u){if(u&&u.length>0)return u;return this.defaultComputeCachePathPatterns()}createComputeCacheBehavior(u,g){return{PathPattern:u,TargetOriginId:g,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS","PUT","POST","PATCH","DELETE"],CachedMethods:["GET","HEAD"],Compress:!0,CachePolicyId:"4135ea2d-6df8-44a3-9df3-4b5a84be39ad",OriginRequestPolicyId:"b689b0a8-53d0-40ab-baf2-68738e2966ac"}}appendComputeAppOrigin(u,g,h,S,A,T){let P=this.serverEipLogicalIds.get("app"),M=this.serverEipLogicalIds.get("appInstance");if(!P)return;let W=`EC2-${S}-${A}-api`,le=this.mergedConfig.infrastructure?.servers?.app?.region||this.mergedConfig.project?.region||"us-east-1",K=le==="us-east-1"?".compute-1.amazonaws.com":`.${le}.compute.amazonaws.com`,Ct={"Fn::Join":["",["ec2-",{"Fn::Join":["-",{"Fn::Split":[".",{Ref:P}]}]},K]]},Tt=this.resolveApiOriginPort();if(u.push({Id:W,DomainName:Ct,CustomOriginConfig:{HTTPPort:Tt,HTTPSPort:443,OriginProtocolPolicy:"http-only",OriginSSLProtocols:["TLSv1.2"]}}),h.push(P),M)h.push(M);for(let He of T)g.push(this.createComputeCacheBehavior(He,W))}buildCaddyfile(u){let g=u.filter(([,A])=>typeof A.domain==="string"&&A.domain&&typeof A.port==="number");if(g.length===0)return;let h=new Map;for(let[,A]of g){let T=h.get(A.domain)??[];T.push({port:A.port,path:A.path}),h.set(A.domain,T)}let S=[];for(let[A,T]of h){let M=[...T].sort((W,le)=>{let K=!W.path||W.path==="/",Ct=!le.path||le.path==="/";if(K&&!Ct)return 1;if(!K&&Ct)return-1;return(le.path?.length??0)-(W.path?.length??0)}).map((W)=>{let le=!W.path||W.path==="/",K=`reverse_proxy localhost:${W.port}`;return le?` handle {
|
|
2
|
+
${K}
|
|
3
|
+
}`:` handle ${W.path} {
|
|
4
|
+
${K}
|
|
5
|
+
}`});S.push(`${A} {
|
|
6
6
|
${M.join(`
|
|
7
7
|
`)}
|
|
8
|
-
}`)}return
|
|
8
|
+
}`)}return S.join(`
|
|
9
9
|
|
|
10
|
-
`)}normalizeMountPath(u){let g=u?.mountPath||u?.path;if(!g||g==="/")return;let h=`/${g}`.replace(/\/+/g,"/").replace(/\/$/,"");return h==="/"?void 0:h}storageBucketLogicalId(u,g,h){return`${u}-${g}-s3-${h}`.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((y)=>y.charAt(0).toUpperCase()+y.slice(1)).join("")}pathMountRewriteFunctionCode(u,g="directory"){return`function handler(event) { var request = event.request; var prefix = ${JSON.stringify(u)}; var rewriteStyle = ${JSON.stringify(g)}; var uri = request.uri; if (uri === prefix) { uri = '/'; } else if (uri.indexOf(prefix + '/') === 0) { uri = uri.substring(prefix.length); } if (uri === '' || uri === '/') { request.uri = '/index.html'; return request; } if (uri.endsWith('/')) { request.uri = uri + 'index.html'; return request; } var lastSegment = uri.substring(uri.lastIndexOf('/') + 1); if (lastSegment.indexOf('.') === -1) { request.uri = rewriteStyle === 'flat' ? uri + '.html' : uri + '/index.html'; return request; } request.uri = uri; return request; }`}generate(){let u=this.mergedConfig.project.slug,g=this.environment,h=!!(this.mergedConfig.infrastructure?.functions&&Object.keys(this.mergedConfig.infrastructure.functions).length>0||this.mergedConfig.infrastructure?.api),y=!!(this.mergedConfig.infrastructure?.servers&&Object.keys(this.mergedConfig.infrastructure.servers).length>0),R=!!this.mergedConfig.infrastructure?.compute;if(!!((this.mergedConfig.mode||"server")==="serverless"&&this.mergedConfig.infrastructure?.containers&&Object.keys(this.mergedConfig.infrastructure.containers).length>0))this.generateNetworkInfrastructure(u,g),this.generateContainerInfrastructure(u,g);if(h)this.generateServerless(u,g);if(y)this.generateServer(u,g);if(R)this.generateComputeApp(u,g);if(this.mergedConfig.infrastructure?.jumpBox)this.generateJumpBox(u,g);if(this.storageFailoverReplicas=[],this.generateSharedInfrastructure(u,g),this.storageFailoverReplicas.length>0)this.builder.addMetadata(Il,this.storageFailoverReplicas);if(this.config.tags)this.applyGlobalTags(this.config.tags);return this}getStorageFailoverReplicas(){return[...this.storageFailoverReplicas]}applyGlobalTags(u){}generateNetworkInfrastructure(u,g){if(this.builder.hasResource("VPC"))return;let y=this.mergedConfig.infrastructure?.network?.cidr||"10.0.0.0/16",{vpc:R,logicalId:v}=S.createVpc({slug:u,environment:g,cidr:y,enableDnsHostnames:!0,enableDnsSupport:!0});this.builder.addResource("VPC",R);let P=`${u}${g}IGW`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(P,{Type:"AWS::EC2::InternetGateway",Properties:{Tags:[{Key:"Name",Value:`${u}-${g}-igw`},{Key:"Environment",Value:g}]}});let M=`${u}${g}IGWAttach`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(M,{Type:"AWS::EC2::VPCGatewayAttachment",Properties:{VpcId:{Ref:"VPC"},InternetGatewayId:{Ref:P}}});let V=`${u}${g}PublicRT`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(V,{Type:"AWS::EC2::RouteTable",Properties:{VpcId:{Ref:"VPC"},Tags:[{Key:"Name",Value:`${u}-${g}-public-rt`},{Key:"Environment",Value:g}]}}),this.builder.addResource(`${u}${g}PublicRoute`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::EC2::Route",Properties:{RouteTableId:{Ref:V},DestinationCidrBlock:"0.0.0.0/0",GatewayId:{Ref:P}},DependsOn:M});let Ae=this.mergedConfig.environments[g]?.region||this.mergedConfig.project.region||"us-east-1",ye=["a","b"];for(let At=0;At<2;At++){let Tt=`PublicSubnet${At+1}`;this.builder.addResource(Tt,{Type:"AWS::EC2::Subnet",Properties:{VpcId:{Ref:"VPC"},CidrBlock:`10.0.${At}.0/24`,AvailabilityZone:`${Ae}${ye[At]}`,MapPublicIpOnLaunch:!0,Tags:[{Key:"Name",Value:`${u}-${g}-public-${ye[At]}`},{Key:"Environment",Value:g}]}}),this.builder.addResource(`${Tt}RTAssoc`,{Type:"AWS::EC2::SubnetRouteTableAssociation",Properties:{SubnetId:{Ref:Tt},RouteTableId:{Ref:V}}})}}generateContainerInfrastructure(u,g){let h=this.mergedConfig.infrastructure?.containers;if(!h)return;let y=this.mergedConfig.infrastructure?.loadBalancer,R=this.mergedConfig.infrastructure?.ssl,v=this.mergedConfig.infrastructure?.dns,P;if(R?.enabled&&R.domains?.length)if(R.certificateArn);else{let ye=R.domains[0],At=R.domains.slice(1).map((Ue)=>{if(Ue.includes(".")&&Ue.endsWith(ye))return Ue.replace(`.${ye}`,"");return Ue}),{certificate:Tt,logicalId:Je}=E.createCertificate({domain:ye,subdomains:At,slug:u,environment:g,validationMethod:"DNS",hostedZoneId:v?.hostedZoneId});P=Je,this.builder.addResource(Je,Tt)}let M="ECSCluster";this.builder.addResource(M,{Type:"AWS::ECS::Cluster",Properties:{ClusterName:`${u}-${g}`,ClusterSettings:[{Name:"containerInsights",Value:"enabled"}],Tags:[{Key:"Name",Value:`${u}-${g}`},{Key:"Environment",Value:g}]}});let V=`${u}${g}ALBSecurityGroup`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(V,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`ALB security group for ${u}-${g}`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:80,ToPort:80,CidrIp:"0.0.0.0/0",Description:"HTTP"},{IpProtocol:"tcp",FromPort:443,ToPort:443,CidrIp:"0.0.0.0/0",Description:"HTTPS"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${u}-${g}-alb-sg`},{Key:"Environment",Value:g}]}});let Ae=`${u}${g}ECSSecurityGroup`.replace(/[^a-zA-Z0-9]/g,"");for(let[ye,At]of Object.entries(h)){let Tt=At.port||3000;this.builder.addResource(Ae,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`ECS tasks security group for ${u}-${g}`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:Tt,ToPort:Tt,SourceSecurityGroupId:{Ref:V},Description:"Allow traffic from ALB"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${u}-${g}-ecs-sg`},{Key:"Environment",Value:g}]}});let Je=`${u}${g}TaskExecRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Je,{Type:"AWS::IAM::Role",Properties:{RoleName:`${u}-${g}-ecs-exec-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},ManagedPolicyArns:["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"],Policies:[{PolicyName:"ECRPullPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["ecr:GetAuthorizationToken","ecr:BatchCheckLayerAvailability","ecr:GetDownloadUrlForLayer","ecr:BatchGetImage","logs:CreateLogStream","logs:PutLogEvents"],Resource:"*"}]}}]}});let Ue=`${u}${g}TaskRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Ue,{Type:"AWS::IAM::Role",Properties:{RoleName:`${u}-${g}-ecs-task-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},Policies:[{PolicyName:"TaskPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["logs:CreateLogStream","logs:PutLogEvents"],Resource:"*"},{Effect:"Allow",Action:["s3:GetObject","s3:PutObject","s3:ListBucket"],Resource:"*"}]}}]}});let je=`${u}${g}${ye}LogGroup`.replace(/[^a-zA-Z0-9]/g,""),K=`/ecs/${u}-${g}-${ye}`;this.builder.addResource(je,{Type:"AWS::Logs::LogGroup",Properties:{LogGroupName:K,RetentionInDays:30}});let St=`${u}${g}${ye}TaskDef`.replace(/[^a-zA-Z0-9]/g,""),z=String(At.cpu||512),Xe=String(At.memory||1024);this.builder.addResource(St,{Type:"AWS::ECS::TaskDefinition",Properties:{Family:`${u}-${g}-${ye}`,NetworkMode:"awsvpc",RequiresCompatibilities:["FARGATE"],Cpu:z,Memory:Xe,ExecutionRoleArn:{"Fn::GetAtt":[Je,"Arn"]},TaskRoleArn:{"Fn::GetAtt":[Ue,"Arn"]},ContainerDefinitions:[{Name:ye,Image:{"Fn::Sub":`\${AWS::AccountId}.dkr.ecr.\${AWS::Region}.amazonaws.com/${u}:latest`},Essential:!0,PortMappings:[{ContainerPort:Tt,Protocol:"tcp"}],LogConfiguration:{LogDriver:"awslogs",Options:{"awslogs-group":K,"awslogs-region":{Ref:"AWS::Region"},"awslogs-stream-prefix":ye}},HealthCheck:{Command:["CMD-SHELL",`curl -f http://localhost:${Tt}${At.healthCheck||"/health"} || exit 1`],Interval:30,Timeout:5,Retries:3,StartPeriod:60}}],Tags:[{Key:"Name",Value:`${u}-${g}-${ye}`},{Key:"Environment",Value:g}]},DependsOn:[Je,Ue,je]});let bt=`${u}${g}ALB`.replace(/[^a-zA-Z0-9]/g,"");if(y?.enabled!==!1){this.builder.addResource(bt,{Type:"AWS::ElasticLoadBalancingV2::LoadBalancer",Properties:{Name:`${u}-${g}-alb`,Scheme:"internet-facing",Type:"application",Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],SecurityGroups:[{Ref:V}],Tags:[{Key:"Name",Value:`${u}-${g}-alb`},{Key:"Environment",Value:g}]}});let vt=`${u}${g}TargetGroup`.replace(/[^a-zA-Z0-9]/g,""),Et=y?.healthCheck?.path||At.healthCheck||"/health";this.builder.addResource(vt,{Type:"AWS::ElasticLoadBalancingV2::TargetGroup",Properties:{Name:`${u}-${g}-tg`,Port:Tt,Protocol:"HTTP",VpcId:{Ref:"VPC"},TargetType:"ip",HealthCheckPath:Et,HealthCheckIntervalSeconds:y?.healthCheck?.interval||30,HealthyThresholdCount:y?.healthCheck?.healthyThreshold||2,UnhealthyThresholdCount:y?.healthCheck?.unhealthyThreshold||5,HealthCheckTimeoutSeconds:10,Tags:[{Key:"Name",Value:`${u}-${g}-tg`},{Key:"Environment",Value:g}]}});let Ht=`${u}${g}HTTPListener`.replace(/[^a-zA-Z0-9]/g,"");if(R?.enabled&&R?.redirectHttp)this.builder.addResource(Ht,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:bt},Port:80,Protocol:"HTTP",DefaultActions:[{Type:"redirect",RedirectConfig:{Protocol:"HTTPS",Port:"443",StatusCode:"HTTP_301"}}]}});else this.builder.addResource(Ht,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:bt},Port:80,Protocol:"HTTP",DefaultActions:[{Type:"forward",TargetGroupArn:{Ref:vt}}]}});if(R?.enabled){let Yt=R.certificateArn||(P?{Ref:P}:void 0);if(Yt){let tr=`${u}${g}HTTPSListener`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(tr,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:bt},Port:443,Protocol:"HTTPS",Certificates:[{CertificateArn:Yt}],DefaultActions:[{Type:"forward",TargetGroupArn:{Ref:vt}}],SslPolicy:"ELBSecurityPolicy-TLS13-1-2-2021-06"}})}}let Zt=`${u}${g}${ye}Service`.replace(/[^a-zA-Z0-9]/g,""),er=At.desiredCount||1;this.builder.addResource(Zt,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${u}-${g}-${ye}`,Cluster:{Ref:M},TaskDefinition:{Ref:St},DesiredCount:er,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:Ae}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},LoadBalancers:[{ContainerName:ye,ContainerPort:Tt,TargetGroupArn:{Ref:vt}}],HealthCheckGracePeriodSeconds:120,Tags:[{Key:"Name",Value:`${u}-${g}-${ye}`},{Key:"Environment",Value:g}]},DependsOn:[St,Ht,vt]});let Tr=At.autoScaling;if(Tr){let Yt=`${u}${g}${ye}ScalableTarget`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(Yt,{Type:"AWS::ApplicationAutoScaling::ScalableTarget",Properties:{MaxCapacity:Tr.max||10,MinCapacity:Tr.min||1,ResourceId:{"Fn::Sub":`service/\${${M}}/${u}-${g}-${ye}`},ScalableDimension:"ecs:service:DesiredCount",ServiceNamespace:"ecs",RoleARN:{"Fn::Sub":"arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService"}},DependsOn:Zt}),Tr.targetCpuUtilization)this.builder.addResource(`${u}${g}${ye}CPUScaling`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${u}-${g}-${ye}-cpu-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:Yt},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageCPUUtilization"},TargetValue:Tr.targetCpuUtilization,ScaleInCooldown:300,ScaleOutCooldown:60}}});if(Tr.targetMemoryUtilization)this.builder.addResource(`${u}${g}${ye}MemoryScaling`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${u}-${g}-${ye}-memory-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:Yt},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageMemoryUtilization"},TargetValue:Tr.targetMemoryUtilization,ScaleInCooldown:300,ScaleOutCooldown:60}}})}if(v?.domain&&v?.hostedZoneId)this.builder.addResource(`${u}${g}ApiDnsRecord`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:v.hostedZoneId,Name:`api.${v.domain}`,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[bt,"DNSName"]},HostedZoneId:{"Fn::GetAtt":[bt,"CanonicalHostedZoneID"]}}}});this.builder.addOutput("ECSClusterArn",{Description:"ECS Cluster ARN",Value:{"Fn::GetAtt":[M,"Arn"]},Export:{Name:{"Fn::Sub":"${AWS::StackName}-ecs-cluster-arn"}}}),this.builder.addOutput("ECSServiceName",{Description:"ECS Service Name",Value:`${u}-${g}-${ye}`,Export:{Name:{"Fn::Sub":"${AWS::StackName}-ecs-service-name"}}}),this.builder.addOutput("LoadBalancerDNS",{Description:"Application Load Balancer DNS Name",Value:{"Fn::GetAtt":[bt,"DNSName"]},Export:{Name:{"Fn::Sub":"${AWS::StackName}-alb-dns"}}})}else{let vt=`${u}${g}${ye}Service`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(vt,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${u}-${g}-${ye}`,Cluster:{Ref:M},TaskDefinition:{Ref:St},DesiredCount:At.desiredCount||1,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:Ae}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},Tags:[{Key:"Name",Value:`${u}-${g}-${ye}`},{Key:"Environment",Value:g}]},DependsOn:[St]})}break}}generateServerless(u,g){if(this.mergedConfig.infrastructure?.functions)for(let[h,y]of Object.entries(this.mergedConfig.infrastructure.functions)){if(!this.shouldDeploy(y))continue;let{role:R,logicalId:v}=ge.createRole({slug:u,environment:g,roleName:`${u}-${g}-${h}-role`,servicePrincipal:"lambda.amazonaws.com",managedPolicyArns:["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]});this.builder.addResource(v,R);let{lambdaFunction:P,logicalId:M}=a.createLambdaFunction({slug:u,environment:g,functionName:`${u}-${g}-${h}`,handler:y.handler||"index.handler",runtime:y.runtime||"nodejs20.x",code:{zipFile:y.code||"export const handler = async () => ({ statusCode: 200 });"},role:v,timeout:y.timeout,memorySize:y.memorySize});this.builder.addResource(M,P)}if(this.mergedConfig.infrastructure?.api){let{restApi:h,logicalId:y}=El.createRestApi({slug:u,environment:g,apiName:`${u}-${g}-api`});this.builder.addResource(y,h)}}generateServer(u,g){if(!this.mergedConfig.infrastructure?.servers)return;let h=this.mergedConfig.infrastructure.servers,y=this.mergedConfig.infrastructure?.compute,R=this.mergedConfig.infrastructure?.ssl;if(Object.values(h).some((P)=>!P.privateNetwork||P.privateNetwork==="create"))this.generateNetworkInfrastructure(u,g);for(let[P,M]of Object.entries(h)){let V=M.instanceType||M.size||y?.size,ye=(V?a.InstanceSize.specs[V]:void 0)?.instanceType||V||"t3.micro",At=M.type||"app",Tt=M.userData||M.startupScript;if(!Tt)Tt=a.UserData.generateAppServerScript({runtime:"bun",runtimeVersion:M.bunVersion||"latest",webServer:"none",domain:M.domain,enableSsl:!!R?.enabled,sslEmail:R?.letsEncrypt?.email,installRedis:At==="cache",installDatabaseClients:!!M.database});let Je=M.privateNetwork&&M.privateNetwork!=="create"?M.privateNetwork:{Ref:"VPC"},Ue=M.subnet||{Ref:"PublicSubnet1"},K=!!this.mergedConfig.infrastructure?.email?.server?.enabled,St=[22,80,443,this.resolveApiOriginPort()];if(K)St.push(25,465,587,143,993);let z=a.createServerModeStack({slug:`${u}-${P}`,environment:g,vpcId:Je,subnetId:Ue,instanceType:ye,keyName:M.keyName||`${u}-${g}`,domain:M.domain,userData:Tt,volumeSize:M.diskSize||20,imageId:M.image,allowedPorts:St});for(let[Xe,bt]of Object.entries(z.resources))this.builder.addResource(Xe,bt);this.serverEipLogicalIds.set(P,z.outputs.eipLogicalId),this.serverEipLogicalIds.set(`${P}Instance`,z.outputs.instanceLogicalId),this.builder.addOutput(`${P}InstanceId`,{Value:{Ref:z.outputs.instanceLogicalId},Description:`Instance ID for ${P} server`}),this.builder.addOutput(`${P}PublicIp`,{Value:{Ref:z.outputs.eipLogicalId},Description:`Public IP for ${P} server`})}}generateComputeApp(u,g){let h=this.mergedConfig.infrastructure?.compute;if(!h)return;let y=this.mergedConfig.sites||{},R=Object.entries(y),P=this.mergedConfig.infrastructure?.dns?.domain,M=this.mergedConfig.infrastructure?.database,V=h.size,ye=(V?a.InstanceSize.specs[V]:void 0)?.instanceType||V||"t3.micro";if(!this.builder.hasResource("VPC"))this.generateNetworkInfrastructure(u,g);let At=this.buildCaddyfile(R),Tt=a.UserData.generateBunAppScript({runtime:h.runtime||"bun",runtimeVersion:h.runtimeVersion||"latest",systemPackages:h.systemPackages,database:M,caddyfile:At}),Je=oo(h.appUpdates),Ue=Je.length>0?`${Tt}
|
|
11
|
-
${
|
|
10
|
+
`)}normalizeMountPath(u){let g=u?.mountPath||u?.path;if(!g||g==="/")return;let h=`/${g}`.replace(/\/+/g,"/").replace(/\/$/,"");return h==="/"?void 0:h}storageBucketLogicalId(u,g,h){return`${u}-${g}-s3-${h}`.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((S)=>S.charAt(0).toUpperCase()+S.slice(1)).join("")}pathMountRewriteFunctionCode(u,g="directory"){return`function handler(event) { var request = event.request; var prefix = ${JSON.stringify(u)}; var rewriteStyle = ${JSON.stringify(g)}; var uri = request.uri; if (uri === prefix) { uri = '/'; } else if (uri.indexOf(prefix + '/') === 0) { uri = uri.substring(prefix.length); } if (uri === '' || uri === '/') { request.uri = '/index.html'; return request; } if (uri.endsWith('/')) { request.uri = uri + 'index.html'; return request; } var lastSegment = uri.substring(uri.lastIndexOf('/') + 1); if (lastSegment.indexOf('.') === -1) { request.uri = rewriteStyle === 'flat' ? uri + '.html' : uri + '/index.html'; return request; } request.uri = uri; return request; }`}generate(){let u=this.mergedConfig.project.slug,g=this.environment,h=!!(this.mergedConfig.infrastructure?.functions&&Object.keys(this.mergedConfig.infrastructure.functions).length>0||this.mergedConfig.infrastructure?.api),S=!!(this.mergedConfig.infrastructure?.servers&&Object.keys(this.mergedConfig.infrastructure.servers).length>0),A=!!this.mergedConfig.infrastructure?.compute;if(!!((this.mergedConfig.mode||"server")==="serverless"&&this.mergedConfig.infrastructure?.containers&&Object.keys(this.mergedConfig.infrastructure.containers).length>0))this.generateNetworkInfrastructure(u,g),this.generateContainerInfrastructure(u,g);if(h)this.generateServerless(u,g);if(S)this.generateServer(u,g);if(A)this.generateComputeApp(u,g);if(this.mergedConfig.infrastructure?.jumpBox)this.generateJumpBox(u,g);if(this.storageFailoverReplicas=[],this.generateSharedInfrastructure(u,g),this.storageFailoverReplicas.length>0)this.builder.addMetadata(bl,this.storageFailoverReplicas);if(this.config.tags)this.applyGlobalTags(this.config.tags);return this}getStorageFailoverReplicas(){return[...this.storageFailoverReplicas]}applyGlobalTags(u){}generateNetworkInfrastructure(u,g){if(this.builder.hasResource("VPC"))return;let S=this.mergedConfig.infrastructure?.network?.cidr||"10.0.0.0/16",{vpc:A,logicalId:T}=I.createVpc({slug:u,environment:g,cidr:S,enableDnsHostnames:!0,enableDnsSupport:!0});this.builder.addResource("VPC",A);let P=`${u}${g}IGW`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(P,{Type:"AWS::EC2::InternetGateway",Properties:{Tags:[{Key:"Name",Value:`${u}-${g}-igw`},{Key:"Environment",Value:g}]}});let M=`${u}${g}IGWAttach`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(M,{Type:"AWS::EC2::VPCGatewayAttachment",Properties:{VpcId:{Ref:"VPC"},InternetGatewayId:{Ref:P}}});let W=`${u}${g}PublicRT`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(W,{Type:"AWS::EC2::RouteTable",Properties:{VpcId:{Ref:"VPC"},Tags:[{Key:"Name",Value:`${u}-${g}-public-rt`},{Key:"Environment",Value:g}]}}),this.builder.addResource(`${u}${g}PublicRoute`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::EC2::Route",Properties:{RouteTableId:{Ref:W},DestinationCidrBlock:"0.0.0.0/0",GatewayId:{Ref:P}},DependsOn:M});let le=this.mergedConfig.environments[g]?.region||this.mergedConfig.project.region||"us-east-1",K=["a","b"];for(let Ct=0;Ct<2;Ct++){let Tt=`PublicSubnet${Ct+1}`;this.builder.addResource(Tt,{Type:"AWS::EC2::Subnet",Properties:{VpcId:{Ref:"VPC"},CidrBlock:`10.0.${Ct}.0/24`,AvailabilityZone:`${le}${K[Ct]}`,MapPublicIpOnLaunch:!0,Tags:[{Key:"Name",Value:`${u}-${g}-public-${K[Ct]}`},{Key:"Environment",Value:g}]}}),this.builder.addResource(`${Tt}RTAssoc`,{Type:"AWS::EC2::SubnetRouteTableAssociation",Properties:{SubnetId:{Ref:Tt},RouteTableId:{Ref:W}}})}}generateContainerInfrastructure(u,g){let h=this.mergedConfig.infrastructure?.containers;if(!h)return;let S=this.mergedConfig.infrastructure?.loadBalancer,A=this.mergedConfig.infrastructure?.ssl,T=this.mergedConfig.infrastructure?.dns,P;if(A?.enabled&&A.domains?.length)if(A.certificateArn);else{let K=A.domains[0],Ct=A.domains.slice(1).map((Se)=>{if(Se.includes(".")&&Se.endsWith(K))return Se.replace(`.${K}`,"");return Se}),{certificate:Tt,logicalId:He}=v.createCertificate({domain:K,subdomains:Ct,slug:u,environment:g,validationMethod:"DNS",hostedZoneId:T?.hostedZoneId});P=He,this.builder.addResource(He,Tt)}let M="ECSCluster";this.builder.addResource(M,{Type:"AWS::ECS::Cluster",Properties:{ClusterName:`${u}-${g}`,ClusterSettings:[{Name:"containerInsights",Value:"enabled"}],Tags:[{Key:"Name",Value:`${u}-${g}`},{Key:"Environment",Value:g}]}});let W=`${u}${g}ALBSecurityGroup`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(W,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`ALB security group for ${u}-${g}`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:80,ToPort:80,CidrIp:"0.0.0.0/0",Description:"HTTP"},{IpProtocol:"tcp",FromPort:443,ToPort:443,CidrIp:"0.0.0.0/0",Description:"HTTPS"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${u}-${g}-alb-sg`},{Key:"Environment",Value:g}]}});let le=`${u}${g}ECSSecurityGroup`.replace(/[^a-zA-Z0-9]/g,"");for(let[K,Ct]of Object.entries(h)){let Tt=Ct.port||3000;this.builder.addResource(le,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`ECS tasks security group for ${u}-${g}`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:Tt,ToPort:Tt,SourceSecurityGroupId:{Ref:W},Description:"Allow traffic from ALB"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${u}-${g}-ecs-sg`},{Key:"Environment",Value:g}]}});let He=`${u}${g}TaskExecRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(He,{Type:"AWS::IAM::Role",Properties:{RoleName:`${u}-${g}-ecs-exec-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},ManagedPolicyArns:["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"],Policies:[{PolicyName:"ECRPullPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["ecr:GetAuthorizationToken","ecr:BatchCheckLayerAvailability","ecr:GetDownloadUrlForLayer","ecr:BatchGetImage","logs:CreateLogStream","logs:PutLogEvents"],Resource:"*"}]}}]}});let Se=`${u}${g}TaskRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Se,{Type:"AWS::IAM::Role",Properties:{RoleName:`${u}-${g}-ecs-task-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},Policies:[{PolicyName:"TaskPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["logs:CreateLogStream","logs:PutLogEvents"],Resource:"*"},{Effect:"Allow",Action:["s3:GetObject","s3:PutObject","s3:ListBucket"],Resource:"*"}]}}]}});let Ie=`${u}${g}${K}LogGroup`.replace(/[^a-zA-Z0-9]/g,""),z=`/ecs/${u}-${g}-${K}`;this.builder.addResource(Ie,{Type:"AWS::Logs::LogGroup",Properties:{LogGroupName:z,RetentionInDays:30}});let Ye=`${u}${g}${K}TaskDef`.replace(/[^a-zA-Z0-9]/g,""),V=String(Ct.cpu||512),Ke=String(Ct.memory||1024);this.builder.addResource(Ye,{Type:"AWS::ECS::TaskDefinition",Properties:{Family:`${u}-${g}-${K}`,NetworkMode:"awsvpc",RequiresCompatibilities:["FARGATE"],Cpu:V,Memory:Ke,ExecutionRoleArn:{"Fn::GetAtt":[He,"Arn"]},TaskRoleArn:{"Fn::GetAtt":[Se,"Arn"]},ContainerDefinitions:[{Name:K,Image:{"Fn::Sub":`\${AWS::AccountId}.dkr.ecr.\${AWS::Region}.amazonaws.com/${u}:latest`},Essential:!0,PortMappings:[{ContainerPort:Tt,Protocol:"tcp"}],LogConfiguration:{LogDriver:"awslogs",Options:{"awslogs-group":z,"awslogs-region":{Ref:"AWS::Region"},"awslogs-stream-prefix":K}},HealthCheck:{Command:["CMD-SHELL",`curl -f http://localhost:${Tt}${Ct.healthCheck||"/health"} || exit 1`],Interval:30,Timeout:5,Retries:3,StartPeriod:60}}],Tags:[{Key:"Name",Value:`${u}-${g}-${K}`},{Key:"Environment",Value:g}]},DependsOn:[He,Se,Ie]});let yt=`${u}${g}ALB`.replace(/[^a-zA-Z0-9]/g,"");if(S?.enabled!==!1){this.builder.addResource(yt,{Type:"AWS::ElasticLoadBalancingV2::LoadBalancer",Properties:{Name:`${u}-${g}-alb`,Scheme:"internet-facing",Type:"application",Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],SecurityGroups:[{Ref:W}],Tags:[{Key:"Name",Value:`${u}-${g}-alb`},{Key:"Environment",Value:g}]}});let It=`${u}${g}TargetGroup`.replace(/[^a-zA-Z0-9]/g,""),vt=S?.healthCheck?.path||Ct.healthCheck||"/health";this.builder.addResource(It,{Type:"AWS::ElasticLoadBalancingV2::TargetGroup",Properties:{Name:`${u}-${g}-tg`,Port:Tt,Protocol:"HTTP",VpcId:{Ref:"VPC"},TargetType:"ip",HealthCheckPath:vt,HealthCheckIntervalSeconds:S?.healthCheck?.interval||30,HealthyThresholdCount:S?.healthCheck?.healthyThreshold||2,UnhealthyThresholdCount:S?.healthCheck?.unhealthyThreshold||5,HealthCheckTimeoutSeconds:10,Tags:[{Key:"Name",Value:`${u}-${g}-tg`},{Key:"Environment",Value:g}]}});let Ht=`${u}${g}HTTPListener`.replace(/[^a-zA-Z0-9]/g,"");if(A?.enabled&&A?.redirectHttp)this.builder.addResource(Ht,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:yt},Port:80,Protocol:"HTTP",DefaultActions:[{Type:"redirect",RedirectConfig:{Protocol:"HTTPS",Port:"443",StatusCode:"HTTP_301"}}]}});else this.builder.addResource(Ht,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:yt},Port:80,Protocol:"HTTP",DefaultActions:[{Type:"forward",TargetGroupArn:{Ref:It}}]}});if(A?.enabled){let Yt=A.certificateArn||(P?{Ref:P}:void 0);if(Yt){let tr=`${u}${g}HTTPSListener`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(tr,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:yt},Port:443,Protocol:"HTTPS",Certificates:[{CertificateArn:Yt}],DefaultActions:[{Type:"forward",TargetGroupArn:{Ref:It}}],SslPolicy:"ELBSecurityPolicy-TLS13-1-2-2021-06"}})}}let Zt=`${u}${g}${K}Service`.replace(/[^a-zA-Z0-9]/g,""),er=Ct.desiredCount||1;this.builder.addResource(Zt,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${u}-${g}-${K}`,Cluster:{Ref:M},TaskDefinition:{Ref:Ye},DesiredCount:er,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:le}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},LoadBalancers:[{ContainerName:K,ContainerPort:Tt,TargetGroupArn:{Ref:It}}],HealthCheckGracePeriodSeconds:120,Tags:[{Key:"Name",Value:`${u}-${g}-${K}`},{Key:"Environment",Value:g}]},DependsOn:[Ye,Ht,It]});let Tr=Ct.autoScaling;if(Tr){let Yt=`${u}${g}${K}ScalableTarget`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(Yt,{Type:"AWS::ApplicationAutoScaling::ScalableTarget",Properties:{MaxCapacity:Tr.max||10,MinCapacity:Tr.min||1,ResourceId:{"Fn::Sub":`service/\${${M}}/${u}-${g}-${K}`},ScalableDimension:"ecs:service:DesiredCount",ServiceNamespace:"ecs",RoleARN:{"Fn::Sub":"arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService"}},DependsOn:Zt}),Tr.targetCpuUtilization)this.builder.addResource(`${u}${g}${K}CPUScaling`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${u}-${g}-${K}-cpu-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:Yt},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageCPUUtilization"},TargetValue:Tr.targetCpuUtilization,ScaleInCooldown:300,ScaleOutCooldown:60}}});if(Tr.targetMemoryUtilization)this.builder.addResource(`${u}${g}${K}MemoryScaling`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${u}-${g}-${K}-memory-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:Yt},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageMemoryUtilization"},TargetValue:Tr.targetMemoryUtilization,ScaleInCooldown:300,ScaleOutCooldown:60}}})}if(T?.domain&&T?.hostedZoneId)this.builder.addResource(`${u}${g}ApiDnsRecord`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:T.hostedZoneId,Name:`api.${T.domain}`,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[yt,"DNSName"]},HostedZoneId:{"Fn::GetAtt":[yt,"CanonicalHostedZoneID"]}}}});this.builder.addOutput("ECSClusterArn",{Description:"ECS Cluster ARN",Value:{"Fn::GetAtt":[M,"Arn"]},Export:{Name:{"Fn::Sub":"${AWS::StackName}-ecs-cluster-arn"}}}),this.builder.addOutput("ECSServiceName",{Description:"ECS Service Name",Value:`${u}-${g}-${K}`,Export:{Name:{"Fn::Sub":"${AWS::StackName}-ecs-service-name"}}}),this.builder.addOutput("LoadBalancerDNS",{Description:"Application Load Balancer DNS Name",Value:{"Fn::GetAtt":[yt,"DNSName"]},Export:{Name:{"Fn::Sub":"${AWS::StackName}-alb-dns"}}})}else{let It=`${u}${g}${K}Service`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(It,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${u}-${g}-${K}`,Cluster:{Ref:M},TaskDefinition:{Ref:Ye},DesiredCount:Ct.desiredCount||1,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:le}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},Tags:[{Key:"Name",Value:`${u}-${g}-${K}`},{Key:"Environment",Value:g}]},DependsOn:[Ye]})}break}}generateServerless(u,g){if(this.mergedConfig.infrastructure?.functions)for(let[h,S]of Object.entries(this.mergedConfig.infrastructure.functions)){if(!this.shouldDeploy(S))continue;let{role:A,logicalId:T}=me.createRole({slug:u,environment:g,roleName:`${u}-${g}-${h}-role`,servicePrincipal:"lambda.amazonaws.com",managedPolicyArns:["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]});this.builder.addResource(T,A);let{lambdaFunction:P,logicalId:M}=a.createLambdaFunction({slug:u,environment:g,functionName:`${u}-${g}-${h}`,handler:S.handler||"index.handler",runtime:S.runtime||"nodejs20.x",code:{zipFile:S.code||"export const handler = async () => ({ statusCode: 200 });"},role:T,timeout:S.timeout,memorySize:S.memorySize});this.builder.addResource(M,P)}if(this.mergedConfig.infrastructure?.api){let{restApi:h,logicalId:S}=vl.createRestApi({slug:u,environment:g,apiName:`${u}-${g}-api`});this.builder.addResource(S,h)}}generateServer(u,g){if(!this.mergedConfig.infrastructure?.servers)return;let h=this.mergedConfig.infrastructure.servers,S=this.mergedConfig.infrastructure?.compute,A=this.mergedConfig.infrastructure?.ssl;if(Object.values(h).some((P)=>!P.privateNetwork||P.privateNetwork==="create"))this.generateNetworkInfrastructure(u,g);for(let[P,M]of Object.entries(h)){let W=M.instanceType||M.size||S?.size,K=(W?a.InstanceSize.specs[W]:void 0)?.instanceType||W||"t3.micro",Ct=M.type||"app",Tt=M.userData||M.startupScript;if(!Tt)Tt=a.UserData.generateAppServerScript({runtime:"bun",runtimeVersion:M.bunVersion||"latest",webServer:"none",domain:M.domain,enableSsl:!!A?.enabled,sslEmail:A?.letsEncrypt?.email,installRedis:Ct==="cache",installDatabaseClients:!!M.database});let He=M.privateNetwork&&M.privateNetwork!=="create"?M.privateNetwork:{Ref:"VPC"},Se=M.subnet||{Ref:"PublicSubnet1"},z=!!this.mergedConfig.infrastructure?.email?.server?.enabled,Ye=[22,80,443,this.resolveApiOriginPort()];if(z)Ye.push(25,465,587,143,993);let V=a.createServerModeStack({slug:`${u}-${P}`,environment:g,vpcId:He,subnetId:Se,instanceType:K,keyName:M.keyName||`${u}-${g}`,domain:M.domain,userData:Tt,volumeSize:M.diskSize||20,imageId:M.image,allowedPorts:Ye});for(let[Ke,yt]of Object.entries(V.resources))this.builder.addResource(Ke,yt);this.serverEipLogicalIds.set(P,V.outputs.eipLogicalId),this.serverEipLogicalIds.set(`${P}Instance`,V.outputs.instanceLogicalId),this.builder.addOutput(`${P}InstanceId`,{Value:{Ref:V.outputs.instanceLogicalId},Description:`Instance ID for ${P} server`}),this.builder.addOutput(`${P}PublicIp`,{Value:{Ref:V.outputs.eipLogicalId},Description:`Public IP for ${P} server`})}}generateComputeApp(u,g){let h=this.mergedConfig.infrastructure?.compute;if(!h)return;let S=this.mergedConfig.sites||{},A=Object.entries(S),P=this.mergedConfig.infrastructure?.dns?.domain,M=this.mergedConfig.infrastructure?.database,W=h.size,K=(W?a.InstanceSize.specs[W]:void 0)?.instanceType||W||"t3.micro";if(!this.builder.hasResource("VPC"))this.generateNetworkInfrastructure(u,g);let Ct=this.buildCaddyfile(A),Tt=a.UserData.generateBunAppScript({runtime:h.runtime||"bun",runtimeVersion:h.runtimeVersion||"latest",systemPackages:h.systemPackages,database:M,caddyfile:Ct}),He=ao(h.appUpdates),Se=He.length>0?`${Tt}
|
|
11
|
+
${He.join(`
|
|
12
12
|
`)}
|
|
13
|
-
`:Tt,
|
|
14
|
-
else if (!uri.includes('.')) { request.uri += '.html'; } return request; }`}});let fi=[{Id:Qn,DomainName:{"Fn::Sub":`\${${Ht}}.s3.${oc}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:K}}],uo=[],cc=[];if(this.shouldRouteStorageBucketToCompute(z,Xe)){this.appendComputeAppOrigin(fi,uo,cc,u,g,this.resolveComputeCachePathPatterns(Xe.computeRoutes));for(let nr of St){let Co=`S3-${u}-${g}-${nr.name}`,_c=`${u}${g}${nr.name}PathMountRewrite`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(_c,{Type:"AWS::CloudFront::Function",Properties:{Name:`${u}-${g}-${nr.name}-path-mount-rewrite`,AutoPublish:!0,FunctionConfig:{Comment:`Path mount rewrite for ${u} ${g} ${nr.name} at ${nr.mountPath}`,Runtime:"cloudfront-js-2.0"},FunctionCode:this.pathMountRewriteFunctionCode(nr.mountPath,nr.rewriteStyle)}}),fi.push({Id:Co,DomainName:{"Fn::Sub":`\${${nr.logicalId}}.s3.${oc}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:K}}),cc.push(nr.logicalId,_c);for(let yp of[nr.mountPath,`${nr.mountPath}/*`])uo.push({PathPattern:yp,TargetOriginId:Co,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:!0,CachePolicyId:"4135ea2d-6df8-44a3-9df3-4b5a84be39ad",FunctionAssociations:[{EventType:"viewer-request",FunctionARN:{"Fn::GetAtt":[_c,"FunctionARN"]}}]})}}let po,od=Qn;if(er){let nr=`${Qn}-failover`;Object.assign(fi[0],er.primaryConnection),fi.push({Id:nr,DomainName:Lm(er.replicaBucket,er.replicaRegion),OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:K}}),po=gn({primaryOriginId:Qn,secondaryOriginId:nr,statusCodes:er.statusCodes}),od=po.Items[0].Id}let xc={Type:"AWS::CloudFront::Distribution",DependsOn:[Ht,K,...ti?[ti]:[],...cc],Properties:{DistributionConfig:{Enabled:!0,Comment:`${u} ${g} ${z} site`,DefaultRootObject:"index.html",Origins:fi,...po?{OriginGroups:po}:{},DefaultCacheBehavior:{TargetOriginId:od,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:!0,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6",...ti?{FunctionAssociations:[{EventType:"viewer-request",FunctionARN:{"Fn::GetAtt":[ti,"FunctionARN"]}}]}:{}},...uo.length>0?{CacheBehaviors:uo}:{},...tr.length>0?{Aliases:tr}:{},ViewerCertificate:qr,PriceClass:"PriceClass_100",HttpVersion:"http2and3",IPV6Enabled:!0,CustomErrorResponses:$r}}};if(M)xc.DependsOn.push(M);if(er)Bt(xc.Properties.DistributionConfig,`infrastructure.storage.${z}`),this.storageFailoverReplicas.push({name:z,primaryBucket:vt,primaryRegion:oc,replicaBucket:er.replicaBucket,replicaRegion:er.replicaRegion,replicate:er.replicate,distributionArnOutput:`${z}CloudFrontDistributionArn`}),this.builder.addOutput(`${z}CloudFrontDistributionArn`,{Value:{"Fn::Sub":`arn:\${AWS::Partition}:cloudfront::\${AWS::AccountId}:distribution/\${${Yt}}`},Description:`CloudFront distribution ARN for ${z} (scopes the bucket policy of the failover replica)`}),this.builder.addOutput(`${z}FailoverBucketName`,{Value:er.replicaBucket,Description:`Failover replica bucket for ${z} (${er.replicaRegion}), outside this stack`});this.builder.addResource(Yt,xc);let fp=`${Ht}CloudFrontPolicy`;if(this.builder.addResource(fp,{Type:"AWS::S3::BucketPolicy",DependsOn:[Ht,Yt],Properties:{Bucket:{Ref:Ht},PolicyDocument:{Version:"2012-10-17",Statement:[{Sid:"AllowCloudFrontServicePrincipal",Effect:"Allow",Principal:{Service:"cloudfront.amazonaws.com"},Action:"s3:GetObject",Resource:{"Fn::Sub":`arn:aws:s3:::\${${Ht}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${Yt}}`}}}}]}}}),this.shouldRouteStorageBucketToCompute(z,Xe))for(let nr of St){let Co=`${nr.logicalId}CloudFrontPolicy`;this.builder.addResource(Co,{Type:"AWS::S3::BucketPolicy",DependsOn:[nr.logicalId,Yt],Properties:{Bucket:{Ref:nr.logicalId},PolicyDocument:{Version:"2012-10-17",Statement:[{Sid:"AllowCloudFrontServicePrincipal",Effect:"Allow",Principal:{Service:"cloudfront.amazonaws.com"},Action:"s3:GetObject",Resource:{"Fn::Sub":`arn:aws:s3:::\${${nr.logicalId}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${Yt}}`}}}}]}}})}if(tr.length>0)P.push({name:z,bucketLogicalId:Ht,distLogicalId:Yt,oacLogicalId:K,aliases:tr});this.builder.addOutput(`${z}CloudFrontDomain`,{Value:{"Fn::GetAtt":[Yt,"DomainName"]},Description:`CloudFront domain for ${z}`}),this.builder.addOutput(`${z}CloudFrontDistributionId`,{Value:{Ref:Yt},Description:`CloudFront distribution ID for ${z}`})}if(this.builder.addOutput(`${z}BucketName`,{Value:{Ref:Ht},Description:`S3 bucket name for ${z}`}),z==="public")this.builder.addOutput("FrontendBucketName",{Value:{Ref:Ht},Description:"Frontend S3 bucket name"});if(z==="docs")this.builder.addOutput("DocsBucketName",{Value:{Ref:Ht},Description:"Documentation S3 bucket name"});if(z==="blog")this.builder.addOutput("BlogBucketName",{Value:{Ref:Ht},Description:"Blog S3 bucket name"})}}if(this.mergedConfig.infrastructure?.sftp){let je=this.mergedConfig.infrastructure.sftp,K=Cl.create({...je,storage:this.resolveSftpStorage(je,u,g)??je.storage,slug:u,environment:g});for(let[St,z]of Object.entries(K.resources))this.builder.addResource(St,z);this.builder.addOutput("SftpServerId",{Value:{Ref:K.serverLogicalId},Description:"AWS Transfer Family SFTP server ID"}),this.builder.addOutput("SftpEndpoint",{Value:{"Fn::Sub":`\${${K.serverLogicalId}}.server.transfer.\${AWS::Region}.amazonaws.com`},Description:"AWS Transfer Family SFTP endpoint"}),this.builder.addOutput("SftpStorageDomain",{Value:K.domain,Description:"Storage backing the SFTP server (EFS or S3)"})}if(v&&P.length>0)for(let{name:je,distLogicalId:K,aliases:St}of P)for(let z of St){let Xe=z.replace(/\./g,"").replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${Xe}ARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[K],Properties:{HostedZoneId:v,Name:z,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[K,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}}),this.builder.addResource(`${Xe}AAAARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[K],Properties:{HostedZoneId:v,Name:z,Type:"AAAA",AliasTarget:{DNSName:{"Fn::GetAtt":[K,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}})}if(this.mergedConfig.infrastructure?.databases){for(let[je,K]of Object.entries(this.mergedConfig.infrastructure.databases))if(K.engine==="dynamodb"){let{table:St,logicalId:z}=gr.createTable({slug:u,environment:g,tableName:`${u}-${g}-${je}`,partitionKey:K.partitionKey||{name:"id",type:"S"},sortKey:K.sortKey});this.builder.addResource(z,St)}else if(K.engine==="postgres"){let{dbInstance:St,logicalId:z}=gr.createPostgres({slug:u,environment:g,dbInstanceIdentifier:`${u}-${g}-${je}`,masterUsername:K.username||"admin",masterUserPassword:K.password||"changeme123",allocatedStorage:K.storage||20,dbInstanceClass:K.instanceClass||"db.t3.micro"});this.builder.addResource(z,St)}else if(K.engine==="mysql"){let{dbInstance:St,logicalId:z}=gr.createMysql({slug:u,environment:g,dbInstanceIdentifier:`${u}-${g}-${je}`,masterUsername:K.username||"admin",masterUserPassword:K.password||"changeme123",allocatedStorage:K.storage||20,dbInstanceClass:K.instanceClass||"db.t3.micro"});this.builder.addResource(z,St)}}if(this.mergedConfig.infrastructure?.cdn)for(let[je,K]of Object.entries(this.mergedConfig.infrastructure.cdn)){if(!K.origin)continue;let St=typeof K.customDomain==="string"?K.customDomain:K.customDomain?.domain||K.domain,z=typeof K.customDomain==="object"?K.customDomain?.certificateArn:K.certificateArn,Xe=z||V,bt=`${u}${g}${je}CDN`.replace(/[^a-zA-Z0-9]/g,""),vt=`S3-${u}-${g}-${je}-cdn`,Et=[{Id:vt,DomainName:K.origin,OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginShield:K.originShield?{Enabled:!0,OriginShieldRegion:K.originShieldRegion||this.mergedConfig.environments[g]?.region||this.mergedConfig.project.region||"us-east-1"}:{Enabled:!1},...ue(K,`infrastructure.cdn.${je}`)}],Ht=[],Zt=[],er=["GET","HEAD","OPTIONS"],Tr=vt,Yt;if(K.failoverOrigin){Nt(er,`the default cache behavior of infrastructure.cdn.${je}`);let zr=`${vt}-failover`,$r=typeof K.failoverOrigin==="string"?{domain:K.failoverOrigin}:K.failoverOrigin;if(!$r.domain)throw Error(`infrastructure.cdn.${je}.failoverOrigin.domain is required`);if($r.domain===K.origin)throw Error(`infrastructure.cdn.${je}: failoverOrigin must differ from origin (both are ${K.origin})`);Et.push({Id:zr,DomainName:$r.domain,OriginPath:$r.originPath||"",...Wo($r.domain)?{S3OriginConfig:{OriginAccessIdentity:""}}:{CustomOriginConfig:{HTTPPort:80,HTTPSPort:443,OriginProtocolPolicy:"https-only"}},OriginShield:{Enabled:!1},...ue($r,`infrastructure.cdn.${je}.failoverOrigin`)}),Yt=gn({primaryOriginId:vt,secondaryOriginId:zr,statusCodes:K.failoverStatusCodes}),Tr=Yt.Items[0].Id}else if(K.failoverStatusCodes)throw Error(`infrastructure.cdn.${je}: failoverStatusCodes needs failoverOrigin to fail over to`);if(K.routeCompute)this.appendComputeAppOrigin(Et,Ht,Zt,u,g,this.resolveComputeCachePathPatterns(K.computeRoutes));let tr=Xe&&St?{AcmCertificateArn:M&&!z?{Ref:M}:Xe,SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:{CloudFrontDefaultCertificate:!0},_r=[...Zt];if(M&&!z&&St)_r.push(M);let Kr={Type:"AWS::CloudFront::Distribution",..._r.length>0?{DependsOn:_r}:{},Properties:{DistributionConfig:{Enabled:!0,Comment:`${u} ${g} ${je} CDN`,DefaultRootObject:"index.html",Origins:Et,...Yt?{OriginGroups:Yt}:{},DefaultCacheBehavior:{TargetOriginId:Tr,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:er,CachedMethods:["GET","HEAD","OPTIONS"],Compress:K.compress!==!1,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6"},...Ht.length>0?{CacheBehaviors:Ht}:{},...St&&Xe?{Aliases:[St]}:{},ViewerCertificate:tr,PriceClass:"PriceClass_100",HttpVersion:K.http3?"http2and3":"http2"}}};if(Bt(Kr.Properties.DistributionConfig,`infrastructure.cdn.${je}`),this.builder.addResource(bt,Kr),v&&St&&Xe){let zr=St.replace(/\./g,"").replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${zr}CdnARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[bt,...M&&!z?[M]:[]],Properties:{HostedZoneId:v,Name:St,Type:"A",AliasTarget:{HostedZoneId:"Z2FDTNDATAQYW2",DNSName:{"Fn::GetAtt":[bt,"DomainName"]}}}})}}if(this.mergedConfig.infrastructure?.queues)for(let[je,K]of Object.entries(this.mergedConfig.infrastructure.queues)){if(!this.shouldDeploy(K))continue;let{queue:St,logicalId:z}=X.createQueue({slug:u,environment:g,name:`${u}-${g}-${je}`,fifo:K.fifo,visibilityTimeout:K.visibilityTimeout,messageRetentionPeriod:K.messageRetentionPeriod,delaySeconds:K.delaySeconds,maxMessageSize:K.maxMessageSize,receiveMessageWaitTime:K.receiveMessageWaitTime,contentBasedDeduplication:K.contentBasedDeduplication,encrypted:K.encrypted,kmsKeyId:K.kmsKeyId});this.builder.addResource(z,St);let Xe;if(K.deadLetterQueue){let{deadLetterQueue:bt,updatedSourceQueue:vt,deadLetterLogicalId:Et}=X.createDeadLetterQueue(z,{slug:u,environment:g,maxReceiveCount:K.maxReceiveCount});Xe=Et,this.builder.addResource(Et,bt);let Zt=this.builder.getResources()[z];if(Zt?.Properties)Zt.Properties.RedrivePolicy=vt.Properties?.RedrivePolicy}if(K.trigger){let bt=K.trigger,vt=`${u}${g}${bt.functionName}`.replace(/[^a-zA-Z0-9]/g,""),Et={Type:"AWS::Lambda::EventSourceMapping",Properties:{EventSourceArn:{"Fn::GetAtt":[z,"Arn"]},FunctionName:{Ref:vt},BatchSize:bt.batchSize||10,MaximumBatchingWindowInSeconds:bt.batchWindow||0,Enabled:!0,...bt.reportBatchItemFailures!==!1&&{FunctionResponseTypes:["ReportBatchItemFailures"]},...bt.maxConcurrency&&{ScalingConfig:{MaximumConcurrency:bt.maxConcurrency}},...bt.filterPattern&&{FilterCriteria:{Filters:[{Pattern:JSON.stringify(bt.filterPattern)}]}}},DependsOn:[z,vt]};this.builder.addResource(`${z}Trigger`,Et)}if(K.alarms?.enabled){let bt=K.alarms,vt=bt.notificationTopicArn;if(!vt&&bt.notificationEmails?.length){let Zt=`${z}AlarmTopic`;this.builder.addResource(Zt,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${u}-${g}-${je}-alarms`,DisplayName:`${je} Queue Alarms`}}),bt.notificationEmails.forEach((er,Tr)=>{this.builder.addResource(`${Zt}Sub${Tr}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:Zt},Protocol:"email",Endpoint:er}})}),vt={Ref:Zt}}let Et=bt.queueDepthThreshold||1000;this.builder.addResource(`${z}DepthAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${u}-${g}-${je}-queue-depth`,AlarmDescription:`Queue ${je} depth exceeds ${Et} messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Average",Period:300,EvaluationPeriods:2,Threshold:Et,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[z,"QueueName"]}}],...vt&&{AlarmActions:[vt],OKActions:[vt]}}});let Ht=bt.messageAgeThreshold||3600;if(this.builder.addResource(`${z}AgeAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${u}-${g}-${je}-message-age`,AlarmDescription:`Queue ${je} oldest message exceeds ${Ht} seconds`,MetricName:"ApproximateAgeOfOldestMessage",Namespace:"AWS/SQS",Statistic:"Maximum",Period:300,EvaluationPeriods:2,Threshold:Ht,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[z,"QueueName"]}}],...vt&&{AlarmActions:[vt],OKActions:[vt]}}}),Xe&&bt.dlqAlarm!==!1)this.builder.addResource(`${Xe}Alarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${u}-${g}-${je}-dlq-messages`,AlarmDescription:`Dead letter queue for ${je} has messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Sum",Period:300,EvaluationPeriods:1,Threshold:0,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[Xe,"QueueName"]}}],...vt&&{AlarmActions:[vt]}}})}if(K.subscribe){let bt=K.subscribe,vt=bt.topicArn;if(!vt&&bt.topicName)vt={Ref:bt.topicName};if(vt){this.builder.addResource(`${z}SnsPolicy`,{Type:"AWS::SQS::QueuePolicy",Properties:{Queues:[{Ref:z}],PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"sns.amazonaws.com"},Action:"sqs:SendMessage",Resource:{"Fn::GetAtt":[z,"Arn"]},Condition:{ArnEquals:{"aws:SourceArn":vt}}}]}}});let Et={TopicArn:vt,Protocol:"sqs",Endpoint:{"Fn::GetAtt":[z,"Arn"]},RawMessageDelivery:bt.rawMessageDelivery||!1};if(bt.filterPolicy)Et.FilterPolicy=bt.filterPolicy,Et.FilterPolicyScope=bt.filterPolicyScope||"MessageAttributes";this.builder.addResource(`${z}SnsSub`,{Type:"AWS::SNS::Subscription",Properties:Et,DependsOn:`${z}SnsPolicy`})}}}if(this.mergedConfig.infrastructure?.realtime?.enabled)if((this.mergedConfig.infrastructure.realtime.mode||"serverless")==="server")this.generateRealtimeServerResources(u,g);else this.generateRealtimeResources(u,g);let Ae=this.mergedConfig.infrastructure?.redirects;if(Ae){let je=Ae.target||this.mergedConfig.infrastructure?.dns?.domain||"",K=Ae.protocol||"https";if(Ae.domains?.length&&je)for(let St of Ae.domains){let{bucket:z,bucketPolicy:Xe,logicalId:bt,policyLogicalId:vt}=Ot.createDomainRedirectBucket({slug:u,environment:g,sourceDomain:St,targetDomain:je,protocol:K});this.builder.addResource(bt,z),this.builder.addResource(vt,Xe)}if(Ae.paths&&Object.keys(Ae.paths).length>0){let St=Ot.fromMapping(Ae.paths,{statusCode:Ae.statusCode||301}),{function:z,logicalId:Xe}=Ot.createPathRedirectFunction({slug:u,environment:g,rules:St});this.builder.addResource(Xe,z)}}if(this.mergedConfig.infrastructure?.monitoring?.alarms)for(let[je,K]of Object.entries(this.mergedConfig.infrastructure.monitoring.alarms)){let{alarm:St,logicalId:z}=Yo.createAlarm({slug:u,environment:g,alarmName:`${u}-${g}-${je}`,metricName:K.metricName||"Errors",namespace:K.namespace||"AWS/Lambda",threshold:K.threshold||1,comparisonOperator:K.comparisonOperator||"GreaterThanThreshold"});this.builder.addResource(z,St)}let ye=this.mergedConfig.infrastructure?.cache;if(ye){let je=ye.type||"redis";if(je==="redis"){this.generateNetworkInfrastructure(u,g);let K=ye.redis||{},{replicationGroup:St,subnetGroup:z,logicalId:Xe,subnetGroupId:bt}=Xo.createRedis({slug:u,environment:g,nodeType:K.nodeType||ye.nodeType||"cache.t3.micro",engineVersion:K.engineVersion||"7.1",port:K.port||6379,numCacheClusters:K.numCacheNodes||2,automaticFailover:K.automaticFailoverEnabled!==!1,atRestEncryption:!0,transitEncryption:!0,snapshotRetentionDays:K.snapshotRetentionLimit||7,snapshotWindow:K.snapshotWindow,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(Xe,St),z&&bt)this.builder.addResource(bt,z);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[Xe,"PrimaryEndPoint.Address"]},Description:"Redis primary endpoint address"}),this.builder.addOutput("CachePort",{Value:{"Fn::GetAtt":[Xe,"PrimaryEndPoint.Port"]},Description:"Redis primary endpoint port"})}else if(je==="memcached"){this.generateNetworkInfrastructure(u,g);let K=ye.elasticache||{},{cluster:St,subnetGroup:z,logicalId:Xe,subnetGroupId:bt}=Xo.createMemcached({slug:u,environment:g,nodeType:K.nodeType||ye.nodeType||"cache.t3.micro",engineVersion:K.engineVersion||"1.6.22",numCacheNodes:K.numCacheNodes||2,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(Xe,St),z&&bt)this.builder.addResource(bt,z);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[Xe,"ConfigurationEndpoint.Address"]},Description:"Memcached configuration endpoint address"})}}let At=this.mergedConfig.infrastructure?.email;if(At){let je=At.domain||this.mergedConfig.infrastructure?.dns?.domain;if(je){let{emailIdentity:K,logicalId:St}=f.verifyDomain({domain:je,slug:u,environment:g,enableDkim:At.enableDkim!==!1,dkimKeyLength:At.dkimKeyLength||"RSA_2048_BIT"});if(this.builder.addResource(St,K),At.configurationSet!==!1){let{configurationSet:bt,logicalId:vt}=f.createConfigurationSet({slug:u,environment:g});this.builder.addResource(vt,bt)}let z=At.hostedZoneId||this.mergedConfig.infrastructure?.dns?.hostedZoneId;if(z){if(At.enableDkim!==!1)for(let Zt=1;Zt<=3;Zt++){let er=`DkimRecord${Zt}${je.replace(/\./g,"")}`;this.builder.addResource(er,{Type:"AWS::Route53::RecordSet",DependsOn:[St],Properties:{HostedZoneId:z,Name:{"Fn::GetAtt":[St,`DkimDNSTokenName${Zt}`]},Type:"CNAME",TTL:1800,ResourceRecords:[{"Fn::GetAtt":[St,`DkimDNSTokenValue${Zt}`]}]}})}let{record:bt,logicalId:vt}=f.createSpfRecord(je,z);this.builder.addResource(vt,bt);let{record:Et,logicalId:Ht}=f.createDmarcRecord(je,z,{policy:"none",reportingEmail:At.dmarcReportingEmail||`dmarc-reports@${je}`});this.builder.addResource(Ht,Et)}this.builder.addOutput("EmailDomain",{Value:je,Description:"SES verified email domain"});let Xe=At.server;if(Xe?.enabled&&z){let bt=this.mergedConfig.environments[g]?.region||this.mergedConfig.project.region||"us-east-1",vt=`${u}-${g}-email`,{role:Et,policy:Ht,roleLogicalId:Zt,policyLogicalId:er}=f.createEmailLambdaRole({slug:u,environment:g,s3BucketArn:`arn:aws:s3:::${vt}`,sesIdentityArn:`arn:aws:ses:${bt}:*:identity/${je}`});this.builder.addResource(Zt,Et),this.builder.addResource(er,Ht);let{function:Tr,permission:Yt,logicalId:tr,permissionLogicalId:_r}=f.createInboundEmailLambda({slug:u,environment:g,roleArn:{"Fn::GetAtt":[Zt,"Arn"]},s3BucketName:vt,organizedPrefix:"mailboxes/"});this.builder.addResource(tr,Tr),this.builder.addResource(_r,Yt);let Kr=f.createInboundEmailSetup({slug:u,environment:g,domain:je,s3BucketName:vt,s3KeyPrefix:"inbox/",region:bt,hostedZoneId:z,lambdaFunctionArn:{"Fn::GetAtt":[tr,"Arn"]}});for(let[$r,qr]of Object.entries(Kr.resources)){if(qr.Type==="AWS::SES::ReceiptRule")qr.DependsOn=[_r,tr];this.builder.addResource($r,qr)}this.builder.addOutput("InboundEmailLambda",{Value:{Ref:tr},Description:"Inbound email processing Lambda function"}),this.builder.addOutput("EmailBucket",{Value:vt,Description:"S3 bucket for email storage"});let zr=this.serverEipLogicalIds.get("app");if(zr&&z){let qr=`${Xe?.subdomain||"mail"}.${je}`,Qn=qr.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${Qn}ARecord`,{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:z,Name:qr,Type:"A",TTL:"300",ResourceRecords:[{Ref:zr}]}})}this.builder.addOutput("MailHost",{Value:`mail.${je}`,Description:"Mail server hostname for SMTP/IMAP clients"})}}}let Tt=this.mergedConfig.infrastructure?.search;if(Tt){let je=Tt.vpc?{subnetIds:[{Ref:"PublicSubnet1"}],securityGroupIds:[]}:void 0;if(je)this.generateNetworkInfrastructure(u,g);let{domain:K,logicalId:St}=pr.createDomain({slug:u,environment:g,engineVersion:Tt.engineVersion||"OpenSearch_2.11",instanceType:Tt.instanceType||"t3.small.search",instanceCount:Tt.instanceCount||1,volumeSize:Tt.volumeSize||10,volumeType:Tt.volumeType||"gp3",dedicatedMaster:Tt.dedicatedMaster||!1,dedicatedMasterType:Tt.dedicatedMasterType,dedicatedMasterCount:Tt.dedicatedMasterCount||3,multiAz:Tt.multiAz||!1,encryption:Tt.encryption||{atRest:!0,nodeToNode:!0},advancedSecurity:Tt.advancedSecurity,autoTune:Tt.autoTune!==!1,vpc:je});this.builder.addResource(St,K),this.builder.addOutput("SearchDomainEndpoint",{Value:{"Fn::GetAtt":[St,"DomainEndpoint"]},Description:"OpenSearch domain endpoint"}),this.builder.addOutput("SearchDomainArn",{Value:{"Fn::GetAtt":[St,"Arn"]},Description:"OpenSearch domain ARN"})}let Je=this.mergedConfig.infrastructure?.fileSystem;if(Je&&Object.keys(Je).length>0){this.generateNetworkInfrastructure(u,g);for(let[je,K]of Object.entries(Je)){let{fileSystem:St,logicalId:z}=nt.createFileSystem({slug:`${u}-${je}`,environment:g,encrypted:K.encrypted!==!1,performanceMode:K.performanceMode||"generalPurpose",throughputMode:K.throughputMode||"bursting",enableBackup:!0});this.builder.addResource(z,St);let{securityGroup:Xe,logicalId:bt}=nt.createEfsSecurityGroup({slug:`${u}-${je}`,environment:g,vpcId:{Ref:"VPC"},sourceCidrBlocks:["10.0.0.0/16"]});this.builder.addResource(bt,Xe);let{mountTargets:vt,logicalIds:Et}=nt.createMultiAzMountTargets(z,{slug:`${u}-${je}`,environment:g,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],securityGroupId:{Ref:bt}});for(let Ht=0;Ht<vt.length;Ht++)this.builder.addResource(Et[Ht],vt[Ht]);this.builder.addOutput(`${je}FileSystemId`,{Value:{Ref:z},Description:`EFS file system ID for ${je}`})}}let Ue=this.mergedConfig.infrastructure?.ai;if(Ue){let je=Ue.service||"ecs",K=Ue.models||["*"],St=Ue.allowStreaming!==!1,z;if(je==="ecs")z=Le.enableBedrockForEcs({slug:u,environment:g,models:K,allowStreaming:St});else if(je==="ec2")z=Le.enableBedrockForEc2({slug:u,environment:g,models:K,allowStreaming:St});else if(je==="lambda")z=Le.enableBedrockForLambda({slug:u,environment:g,models:K,allowStreaming:St});else z=Le.createBedrockRole(je,{slug:u,environment:g,models:K,allowStreaming:St});if(this.builder.addResource(z.logicalId,z.role),Ue.allowAsync){let{policy:Xe,logicalId:bt}=Le.createBedrockPolicy({slug:u,environment:g,models:K,allowStreaming:St,allowAsync:!0});this.builder.addResource(bt,Xe)}this.builder.addOutput("BedrockRoleArn",{Value:{"Fn::GetAtt":[z.logicalId,"Arn"]},Description:"IAM role ARN with Bedrock permissions"})}}generateRealtimeResources(u,g){let h=this.mergedConfig.infrastructure?.realtime;if(!h)return;let y=h.name||`${u}-${g}-realtime`,R=h.scaling||{},v=h.storage||{type:"dynamodb"},P=R.handlerMemory||256,M=R.handlerTimeout||30,V=`${u}${g}RealtimeConnections`.replace(/[^a-zA-Z0-9]/g,""),Ae=`${u}${g}RealtimeChannels`.replace(/[^a-zA-Z0-9]/g,"");if(v.type==="dynamodb"){let Et=v.dynamodb||{},Ht=Et.billingMode||"PAY_PER_REQUEST";this.builder.addResource(V,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${u}-${g}-realtime-connections`,BillingMode:Ht,AttributeDefinitions:[{AttributeName:"connectionId",AttributeType:"S"},{AttributeName:"userId",AttributeType:"S"}],KeySchema:[{AttributeName:"connectionId",KeyType:"HASH"}],GlobalSecondaryIndexes:[{IndexName:"userId-index",KeySchema:[{AttributeName:"userId",KeyType:"HASH"}],Projection:{ProjectionType:"ALL"},...Ht==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:Et.readCapacity||5,WriteCapacityUnits:Et.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...Ht==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:Et.readCapacity||5,WriteCapacityUnits:Et.writeCapacity||5}},...Et.pointInTimeRecovery&&{PointInTimeRecoverySpecification:{PointInTimeRecoveryEnabled:!0}},Tags:[{Key:"Name",Value:`${u}-${g}-realtime-connections`},{Key:"Environment",Value:g}]}}),this.builder.addResource(Ae,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${u}-${g}-realtime-channels`,BillingMode:Ht,AttributeDefinitions:[{AttributeName:"channel",AttributeType:"S"},{AttributeName:"connectionId",AttributeType:"S"}],KeySchema:[{AttributeName:"channel",KeyType:"HASH"},{AttributeName:"connectionId",KeyType:"RANGE"}],GlobalSecondaryIndexes:[{IndexName:"connectionId-index",KeySchema:[{AttributeName:"connectionId",KeyType:"HASH"}],Projection:{ProjectionType:"ALL"},...Ht==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:Et.readCapacity||5,WriteCapacityUnits:Et.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...Ht==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:Et.readCapacity||5,WriteCapacityUnits:Et.writeCapacity||5}},Tags:[{Key:"Name",Value:`${u}-${g}-realtime-channels`},{Key:"Environment",Value:g}]}})}let ye=`${u}${g}RealtimeRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(ye,{Type:"AWS::IAM::Role",Properties:{RoleName:`${u}-${g}-realtime-handler-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"lambda.amazonaws.com"},Action:"sts:AssumeRole"}]},ManagedPolicyArns:["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"],Policies:[{PolicyName:"RealtimeHandlerPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["dynamodb:GetItem","dynamodb:PutItem","dynamodb:DeleteItem","dynamodb:Query","dynamodb:Scan","dynamodb:UpdateItem"],Resource:[{"Fn::GetAtt":[V,"Arn"]},{"Fn::Sub":`\${${V}.Arn}/index/*`},{"Fn::GetAtt":[Ae,"Arn"]},{"Fn::Sub":`\${${Ae}.Arn}/index/*`}]},{Effect:"Allow",Action:"execute-api:ManageConnections",Resource:{"Fn::Sub":"arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*"}}]}}]}});let At=`${u}${g}RealtimeConnect`.replace(/[^a-zA-Z0-9]/g,""),Tt=`${u}${g}RealtimeDisconnect`.replace(/[^a-zA-Z0-9]/g,""),Je=`${u}${g}RealtimeMessage`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(At,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${u}-${g}-realtime-connect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[ye,"Arn"]},MemorySize:P,Timeout:M,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:V},CHANNELS_TABLE:{Ref:Ae},ENVIRONMENT:g}},Code:{ZipFile:this.generateConnectHandlerCode()}},DependsOn:[ye,V]}),this.builder.addResource(Tt,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${u}-${g}-realtime-disconnect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[ye,"Arn"]},MemorySize:P,Timeout:M,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:V},CHANNELS_TABLE:{Ref:Ae},ENVIRONMENT:g}},Code:{ZipFile:this.generateDisconnectHandlerCode()}},DependsOn:[ye,V,Ae]}),this.builder.addResource(Je,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${u}-${g}-realtime-message`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[ye,"Arn"]},MemorySize:P,Timeout:M,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:V},CHANNELS_TABLE:{Ref:Ae},ENVIRONMENT:g}},Code:{ZipFile:this.generateMessageHandlerCode()}},DependsOn:[ye,V,Ae]});let Ue=`${u}${g}RealtimeApi`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Ue,{Type:"AWS::ApiGatewayV2::Api",Properties:{Name:y,ProtocolType:"WEBSOCKET",RouteSelectionExpression:"$request.body.action",Tags:{Name:y,Environment:g}}});let je=`${At}Permission`,K=`${Tt}Permission`,St=`${Je}Permission`;this.builder.addResource(je,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:At},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${Ue}}/*/$connect`}}}),this.builder.addResource(K,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:Tt},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${Ue}}/*/$disconnect`}}}),this.builder.addResource(St,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:Je},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${Ue}}/*/$default`}}});let z=`${Ue}ConnectInteg`,Xe=`${Ue}DisconnectInteg`,bt=`${Ue}MessageInteg`;this.builder.addResource(z,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:Ue},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${At}.Arn}/invocations`}}}),this.builder.addResource(Xe,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:Ue},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${Tt}.Arn}/invocations`}}}),this.builder.addResource(bt,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:Ue},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${Je}.Arn}/invocations`}}}),this.builder.addResource(`${Ue}ConnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:Ue},RouteKey:"$connect",AuthorizationType:"NONE",Target:{"Fn::Sub":`integrations/\${${z}}`}}}),this.builder.addResource(`${Ue}DisconnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:Ue},RouteKey:"$disconnect",Target:{"Fn::Sub":`integrations/\${${Xe}}`}}}),this.builder.addResource(`${Ue}DefaultRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:Ue},RouteKey:"$default",Target:{"Fn::Sub":`integrations/\${${bt}}`}}});let vt=`${Ue}Stage`;if(this.builder.addResource(vt,{Type:"AWS::ApiGatewayV2::Stage",Properties:{ApiId:{Ref:Ue},StageName:g,AutoDeploy:!0,DefaultRouteSettings:{ThrottlingBurstLimit:R.messagesPerSecond||1000,ThrottlingRateLimit:R.messagesPerSecond||1000}}}),h.monitoring?.enabled){let Et=h.monitoring,Ht=Et.notificationTopicArn;if(!Ht&&Et.notificationEmails?.length){let Zt=`${Ue}AlarmTopic`;this.builder.addResource(Zt,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${u}-${g}-realtime-alarms`,DisplayName:"Realtime WebSocket Alarms"}}),Et.notificationEmails.forEach((er,Tr)=>{this.builder.addResource(`${Zt}Sub${Tr}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:Zt},Protocol:"email",Endpoint:er}})}),Ht={Ref:Zt}}if(Et.connectionThreshold)this.builder.addResource(`${Ue}ConnectionAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${u}-${g}-realtime-connections`,AlarmDescription:`WebSocket connections exceed ${Et.connectionThreshold}`,MetricName:"ConnectCount",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:300,EvaluationPeriods:2,Threshold:Et.connectionThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:Ue}}],...Ht&&{AlarmActions:[Ht]}}});if(Et.errorThreshold)this.builder.addResource(`${Ue}ErrorAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${u}-${g}-realtime-errors`,AlarmDescription:`WebSocket errors exceed ${Et.errorThreshold}/min`,MetricName:"ExecutionError",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:60,EvaluationPeriods:3,Threshold:Et.errorThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:Ue}}],...Ht&&{AlarmActions:[Ht]}}})}this.builder.addOutput(`${Ue}Endpoint`,{Description:"WebSocket API endpoint URL",Value:{"Fn::Sub":`wss://\${${Ue}}.execute-api.\${AWS::Region}.amazonaws.com/${g}`},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-endpoint"}}}),this.builder.addOutput(`${Ue}Id`,{Description:"WebSocket API ID",Value:{Ref:Ue},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-api-id"}}})}generateConnectHandlerCode(){return`
|
|
13
|
+
`:Tt,Ie=A.map(([,vt])=>vt.port).filter((vt)=>typeof vt==="number"&&![80,443].includes(vt)),z=this.resolveApiOriginPort(),Ye=`${u}${g}DeployBucket`.replace(/[^a-zA-Z0-9]/g,""),V=`${u}-${g}-deploy`;this.builder.addResource(Ye,{Type:"AWS::S3::Bucket",Properties:{BucketName:V,PublicAccessBlockConfiguration:{BlockPublicAcls:!0,BlockPublicPolicy:!0,IgnorePublicAcls:!0,RestrictPublicBuckets:!0},LifecycleConfiguration:{Rules:[{Id:"expire-old-releases",Status:"Enabled",ExpirationInDays:7,Prefix:"releases/"}]},Tags:[{Key:"Project",Value:u},{Key:"Environment",Value:g},{Key:"ManagedBy",Value:"ts-cloud"}]}});let Ke=a.createServerModeStack({slug:`${u}-app`,environment:g,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},instanceType:K,keyName:h.allowSsh?`${u}-${g}`:void 0,domain:P,userData:Se,volumeSize:h.disk?.size||20,imageId:h.image,allowedPorts:[...h.allowSsh?[22]:[],80,443,z,...Ie]}),yt=Ke.resources[Ke.outputs.instanceLogicalId];if(yt?.Properties){let vt=yt.Properties.Tags||[];yt.Properties.Tags=[...vt,{Key:"Project",Value:u},{Key:"Environment",Value:g},{Key:"Role",Value:"app"},{Key:"ManagedBy",Value:"ts-cloud"}]}let It=Ke.resources[Ke.outputs.roleLogicalId];if(It?.Properties)It.Properties.Policies=It.Properties.Policies||[],It.Properties.Policies.push({PolicyName:"DeployBucketRead",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["s3:GetObject","s3:ListBucket"],Resource:[`arn:aws:s3:::${V}`,`arn:aws:s3:::${V}/*`]}]}});for(let[vt,Ht]of Object.entries(Ke.resources))this.builder.addResource(vt,Ht);this.builder.addOutput("deployBucketName",{Value:{Ref:Ye},Description:"S3 bucket where release tarballs are uploaded"}),this.builder.addOutput("appInstanceId",{Value:{Ref:Ke.outputs.instanceLogicalId},Description:"EC2 instance ID for the app server"}),this.builder.addOutput("appPublicIp",{Value:{Ref:Ke.outputs.eipLogicalId},Description:"Public IP for the app server"}),this.serverEipLogicalIds.set("app",Ke.outputs.eipLogicalId),this.serverEipLogicalIds.set("appInstance",Ke.outputs.instanceLogicalId)}jumpBoxFileSystemId(u,g,h){let S=Object.keys(this.mergedConfig.infrastructure?.fileSystem??{});if(typeof h==="string")return S.includes(h)?{Ref:this.fileSystemLogicalId(u,g,h)}:h;if(S.length===1)return{Ref:this.fileSystemLogicalId(u,g,S[0])};throw Error(S.length===0?"infrastructure.jumpBox.mountEfs is true, but infrastructure.fileSystem declares no file system. Declare one, or set mountEfs to an EFS id.":`infrastructure.jumpBox.mountEfs is true, but infrastructure.fileSystem declares ${S.length} (${S.join(", ")}). Set mountEfs to the name of the one to mount.`)}generateJumpBox(u,g){let h=this.mergedConfig.infrastructure?.jumpBox;if(!h)return;let S=h===!0?{}:h;if(S.enabled===!1)return;this.generateNetworkInfrastructure(u,g);let A=S.size||"micro",P=a.InstanceSize.specs[A]?.instanceType||A||"t3.micro",M=a.createJumpBox({slug:u,environment:g,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:S.keyName,instanceType:P,allowedCidrs:S.allowedCidrs,mountEfs:S.mountEfs?{fileSystemId:this.jumpBoxFileSystemId(u,g,S.mountEfs),mountPath:S.mountPath||"/mnt/efs"}:void 0,databaseTools:S.databaseTools});for(let[W,le]of Object.entries(M.resources))this.builder.addResource(W,le);this.builder.addOutput("JumpBoxInstanceId",{Value:{Ref:M.instanceLogicalId},Description:"The ID of the JumpBox EC2 instance (use with SSM Session Manager or SSH)"})}generateSharedInfrastructure(u,g){let h=this.mergedConfig.infrastructure?.ssl,S=this.mergedConfig.infrastructure?.dns,A=S?.domain,T=S?.hostedZoneId,P=[],M,W;if(h?.certificateArn)W=h.certificateArn;else if(h?.enabled&&A&&T){let Ie=h.domains||[A],z=Ie[0],Ye=Ie.slice(1).map((yt)=>{if(yt.includes(".")&&yt.endsWith(z))return yt.replace(`.${z}`,"");return yt}),{certificate:V,logicalId:Ke}=v.createCertificate({domain:z,subdomains:Ye,slug:u,environment:g,validationMethod:"DNS",hostedZoneId:T});M=Ke,this.builder.addResource(Ke,V)}if(this.mergedConfig.infrastructure?.storage){let Ie=Object.entries(this.mergedConfig.infrastructure.storage).some(([,V])=>V.website),z;if(Ie&&(M||W))z=`${u}${g}CloudFrontOAC`.replace(/[^a-zA-Z0-9]/g,""),this.builder.addResource(z,{Type:"AWS::CloudFront::OriginAccessControl",Properties:{OriginAccessControlConfig:{Name:`${u}-${g}-s3-oac`,Description:`OAC for ${u} ${g} S3 website buckets`,OriginAccessControlOriginType:"s3",SigningBehavior:"always",SigningProtocol:"sigv4"}}});let Ye=Object.entries(this.mergedConfig.infrastructure.storage).map(([V,Ke])=>({name:V,config:Ke,mountPath:this.normalizeMountPath(Ke),rewriteStyle:Ke.pathRewriteStyle||"directory",logicalId:this.storageBucketLogicalId(u,g,V)})).filter((V)=>V.name!=="public"&&V.config.website&&V.mountPath);for(let[V,Ke]of Object.entries(this.mergedConfig.infrastructure.storage)){let yt=!!(Ke.website&&z),It=this.storageBucketName(u,g,V),{bucket:vt,logicalId:Ht}=$e.createBucket({slug:u,name:V,environment:g,bucketName:It,versioning:Ke.versioning,encryption:Ke.encryption,public:yt?!1:Ke.public}),Zt=this.normalizeMountPath(Ke),er;if(Ke.failover){let Yt=`infrastructure.storage.${V}`;if(!yt||!A||Zt&&V!=="public")throw Error(`${Yt}.failover needs this bucket to be served by its own CloudFront distribution: a website bucket (website: true), not mounted under the path of another site, with infrastructure.dns.domain set and SSL configured (infrastructure.ssl.certificateArn, or ssl.enabled with dns.hostedZoneId).`);if(er=Lm({failover:Ke.failover,primaryBucket:It,primaryRegion:this.mergedConfig.project.region||"us-east-1",where:Yt}),er.replicate&&vt.Properties){let tr=`${Ht}FailoverReplicationRole`;this.builder.addResource(tr,Mm({primaryBucket:It,replicaBucket:er.replicaBucket}));let _r=vt.Properties;_r.VersioningConfiguration={Status:"Enabled"},_r.ReplicationConfiguration=Fm({replicaBucket:er.replicaBucket,roleArn:{"Fn::GetAtt":[tr,"Arn"]}})}}if(yt&&vt.Properties)vt.Properties.PublicAccessBlockConfiguration={BlockPublicAcls:!0,IgnorePublicAcls:!0,BlockPublicPolicy:!1,RestrictPublicBuckets:!1};if(this.builder.addResource(Ht,vt),Ke.website){let Yt=typeof Ke.website==="object"?Ke.website:{},tr=$e.enableWebsiteHosting(vt,Yt.indexDocument||"index.html",Yt.errorDocument);this.builder.addResource(Ht,tr)}let Tr=this.normalizeMountPath(Ke);if(yt&&z&&A&&!(Tr&&V!=="public")){let Yt=`${u}${g}${V}CDN`.replace(/[^a-zA-Z0-9]/g,""),tr=[];if(Ke.aliases&&Ke.aliases.length>0)tr.push(...Ke.aliases);else if(V==="public"){if(tr.push(A),h?.domains?.includes(`www.${A}`))tr.push(`www.${A}`)}else if(V==="docs")tr.push(`docs.${A}`);else if(V==="blog")tr.push(`blog.${A}`);let _r=typeof Ke.website==="object"?Ke.website:{},Kr=Ke.spa===!0,zr=_r.errorDocument||(Kr?"index.html":"404.html"),$r=[];if(Kr)$r.push({ErrorCode:403,ResponseCode:200,ResponsePagePath:"/index.html",ErrorCachingMinTTL:300},{ErrorCode:404,ResponseCode:200,ResponsePagePath:"/index.html",ErrorCachingMinTTL:300});else $r.push({ErrorCode:403,ResponseCode:404,ResponsePagePath:`/${zr}`,ErrorCachingMinTTL:300},{ErrorCode:404,ResponseCode:404,ResponsePagePath:`/${zr}`,ErrorCachingMinTTL:300});let qr=W&&tr.length>0?{AcmCertificateArn:W,SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:M&&tr.length>0?{AcmCertificateArn:{Ref:M},SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:{CloudFrontDefaultCertificate:!0},Qn=`S3-${u}-${g}-${V}`,qs=this.mergedConfig.project.region||"us-east-1",ti;if(!Kr)ti=`${u}${g}${V}UrlRewrite`.replace(/[^a-zA-Z0-9]/g,""),this.builder.addResource(ti,{Type:"AWS::CloudFront::Function",Properties:{Name:`${u}-${g}-${V}-url-rewrite`,AutoPublish:!0,FunctionConfig:{Comment:`URL rewrite for ${u} ${g} ${V} - appends .html to extensionless paths`,Runtime:"cloudfront-js-2.0"},FunctionCode:`function handler(event) { var request = event.request; var uri = request.uri; if (uri.endsWith('/')) { request.uri += 'index.html'; }
|
|
14
|
+
else if (!uri.includes('.')) { request.uri += '.html'; } return request; }`}});let fi=[{Id:Qn,DomainName:{"Fn::Sub":`\${${Ht}}.s3.${qs}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:z}}],uo=[],ac=[];if(this.shouldRouteStorageBucketToCompute(V,Ke)){this.appendComputeAppOrigin(fi,uo,ac,u,g,this.resolveComputeCachePathPatterns(Ke.computeRoutes));for(let nr of Ye){let go=`S3-${u}-${g}-${nr.name}`,Oc=`${u}${g}${nr.name}PathMountRewrite`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Oc,{Type:"AWS::CloudFront::Function",Properties:{Name:`${u}-${g}-${nr.name}-path-mount-rewrite`,AutoPublish:!0,FunctionConfig:{Comment:`Path mount rewrite for ${u} ${g} ${nr.name} at ${nr.mountPath}`,Runtime:"cloudfront-js-2.0"},FunctionCode:this.pathMountRewriteFunctionCode(nr.mountPath,nr.rewriteStyle)}}),fi.push({Id:go,DomainName:{"Fn::Sub":`\${${nr.logicalId}}.s3.${qs}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:z}}),ac.push(nr.logicalId,Oc);for(let fp of[nr.mountPath,`${nr.mountPath}/*`])uo.push({PathPattern:fp,TargetOriginId:go,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:!0,CachePolicyId:"4135ea2d-6df8-44a3-9df3-4b5a84be39ad",FunctionAssociations:[{EventType:"viewer-request",FunctionARN:{"Fn::GetAtt":[Oc,"FunctionARN"]}}]})}}let mo,od=Qn;if(er){let nr=`${Qn}-failover`;Object.assign(fi[0],er.primaryConnection),fi.push({Id:nr,DomainName:$m(er.replicaBucket,er.replicaRegion),OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:z}}),mo=gn({primaryOriginId:Qn,secondaryOriginId:nr,statusCodes:er.statusCodes}),od=mo.Items[0].Id}let dc={Type:"AWS::CloudFront::Distribution",DependsOn:[Ht,z,...ti?[ti]:[],...ac],Properties:{DistributionConfig:{Enabled:!0,Comment:`${u} ${g} ${V} site`,DefaultRootObject:"index.html",Origins:fi,...mo?{OriginGroups:mo}:{},DefaultCacheBehavior:{TargetOriginId:od,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:!0,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6",...ti?{FunctionAssociations:[{EventType:"viewer-request",FunctionARN:{"Fn::GetAtt":[ti,"FunctionARN"]}}]}:{}},...uo.length>0?{CacheBehaviors:uo}:{},...tr.length>0?{Aliases:tr}:{},ViewerCertificate:qr,PriceClass:"PriceClass_100",HttpVersion:"http2and3",IPV6Enabled:!0,CustomErrorResponses:$r}}};if(M)dc.DependsOn.push(M);if(er)Bt(dc.Properties.DistributionConfig,`infrastructure.storage.${V}`),this.storageFailoverReplicas.push({name:V,primaryBucket:It,primaryRegion:qs,replicaBucket:er.replicaBucket,replicaRegion:er.replicaRegion,replicate:er.replicate,distributionArnOutput:`${V}CloudFrontDistributionArn`}),this.builder.addOutput(`${V}CloudFrontDistributionArn`,{Value:{"Fn::Sub":`arn:\${AWS::Partition}:cloudfront::\${AWS::AccountId}:distribution/\${${Yt}}`},Description:`CloudFront distribution ARN for ${V} (scopes the bucket policy of the failover replica)`}),this.builder.addOutput(`${V}FailoverBucketName`,{Value:er.replicaBucket,Description:`Failover replica bucket for ${V} (${er.replicaRegion}), outside this stack`});this.builder.addResource(Yt,dc);let gp=`${Ht}CloudFrontPolicy`;if(this.builder.addResource(gp,{Type:"AWS::S3::BucketPolicy",DependsOn:[Ht,Yt],Properties:{Bucket:{Ref:Ht},PolicyDocument:{Version:"2012-10-17",Statement:[{Sid:"AllowCloudFrontServicePrincipal",Effect:"Allow",Principal:{Service:"cloudfront.amazonaws.com"},Action:"s3:GetObject",Resource:{"Fn::Sub":`arn:aws:s3:::\${${Ht}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${Yt}}`}}}}]}}}),this.shouldRouteStorageBucketToCompute(V,Ke))for(let nr of Ye){let go=`${nr.logicalId}CloudFrontPolicy`;this.builder.addResource(go,{Type:"AWS::S3::BucketPolicy",DependsOn:[nr.logicalId,Yt],Properties:{Bucket:{Ref:nr.logicalId},PolicyDocument:{Version:"2012-10-17",Statement:[{Sid:"AllowCloudFrontServicePrincipal",Effect:"Allow",Principal:{Service:"cloudfront.amazonaws.com"},Action:"s3:GetObject",Resource:{"Fn::Sub":`arn:aws:s3:::\${${nr.logicalId}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${Yt}}`}}}}]}}})}if(tr.length>0)P.push({name:V,bucketLogicalId:Ht,distLogicalId:Yt,oacLogicalId:z,aliases:tr});this.builder.addOutput(`${V}CloudFrontDomain`,{Value:{"Fn::GetAtt":[Yt,"DomainName"]},Description:`CloudFront domain for ${V}`}),this.builder.addOutput(`${V}CloudFrontDistributionId`,{Value:{Ref:Yt},Description:`CloudFront distribution ID for ${V}`})}if(this.builder.addOutput(`${V}BucketName`,{Value:{Ref:Ht},Description:`S3 bucket name for ${V}`}),V==="public")this.builder.addOutput("FrontendBucketName",{Value:{Ref:Ht},Description:"Frontend S3 bucket name"});if(V==="docs")this.builder.addOutput("DocsBucketName",{Value:{Ref:Ht},Description:"Documentation S3 bucket name"});if(V==="blog")this.builder.addOutput("BlogBucketName",{Value:{Ref:Ht},Description:"Blog S3 bucket name"})}}if(this.mergedConfig.infrastructure?.sftp){let Ie=this.mergedConfig.infrastructure.sftp,z=El.create({...Ie,storage:this.resolveSftpStorage(Ie,u,g)??Ie.storage,slug:u,environment:g});for(let[Ye,V]of Object.entries(z.resources))this.builder.addResource(Ye,V);this.builder.addOutput("SftpServerId",{Value:{Ref:z.serverLogicalId},Description:"AWS Transfer Family SFTP server ID"}),this.builder.addOutput("SftpEndpoint",{Value:{"Fn::Sub":`\${${z.serverLogicalId}}.server.transfer.\${AWS::Region}.amazonaws.com`},Description:"AWS Transfer Family SFTP endpoint"}),this.builder.addOutput("SftpStorageDomain",{Value:z.domain,Description:"Storage backing the SFTP server (EFS or S3)"})}if(T&&P.length>0)for(let{name:Ie,distLogicalId:z,aliases:Ye}of P)for(let V of Ye){let Ke=V.replace(/\./g,"").replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${Ke}ARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[z],Properties:{HostedZoneId:T,Name:V,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[z,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}}),this.builder.addResource(`${Ke}AAAARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[z],Properties:{HostedZoneId:T,Name:V,Type:"AAAA",AliasTarget:{DNSName:{"Fn::GetAtt":[z,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}})}if(this.mergedConfig.infrastructure?.databases){for(let[Ie,z]of Object.entries(this.mergedConfig.infrastructure.databases))if(z.engine==="dynamodb"){let{table:Ye,logicalId:V}=gr.createTable({slug:u,environment:g,tableName:`${u}-${g}-${Ie}`,partitionKey:z.partitionKey||{name:"id",type:"S"},sortKey:z.sortKey});this.builder.addResource(V,Ye)}else if(z.engine==="postgres"){let{dbInstance:Ye,logicalId:V}=gr.createPostgres({slug:u,environment:g,dbInstanceIdentifier:`${u}-${g}-${Ie}`,masterUsername:z.username||"admin",masterUserPassword:z.password||"changeme123",allocatedStorage:z.storage||20,dbInstanceClass:z.instanceClass||"db.t3.micro"});this.builder.addResource(V,Ye)}else if(z.engine==="mysql"){let{dbInstance:Ye,logicalId:V}=gr.createMysql({slug:u,environment:g,dbInstanceIdentifier:`${u}-${g}-${Ie}`,masterUsername:z.username||"admin",masterUserPassword:z.password||"changeme123",allocatedStorage:z.storage||20,dbInstanceClass:z.instanceClass||"db.t3.micro"});this.builder.addResource(V,Ye)}}if(this.mergedConfig.infrastructure?.cdn)for(let[Ie,z]of Object.entries(this.mergedConfig.infrastructure.cdn)){if(!z.origin)continue;let Ye=typeof z.customDomain==="string"?z.customDomain:z.customDomain?.domain||z.domain,V=typeof z.customDomain==="object"?z.customDomain?.certificateArn:z.certificateArn,Ke=V||W,yt=`${u}${g}${Ie}CDN`.replace(/[^a-zA-Z0-9]/g,""),It=`S3-${u}-${g}-${Ie}-cdn`,vt=[{Id:It,DomainName:z.origin,OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginShield:z.originShield?{Enabled:!0,OriginShieldRegion:z.originShieldRegion||this.mergedConfig.environments[g]?.region||this.mergedConfig.project.region||"us-east-1"}:{Enabled:!1},...pe(z,`infrastructure.cdn.${Ie}`)}],Ht=[],Zt=[],er=["GET","HEAD","OPTIONS"],Tr=It,Yt;if(z.failoverOrigin){Nt(er,`the default cache behavior of infrastructure.cdn.${Ie}`);let zr=`${It}-failover`,$r=typeof z.failoverOrigin==="string"?{domain:z.failoverOrigin}:z.failoverOrigin;if(!$r.domain)throw Error(`infrastructure.cdn.${Ie}.failoverOrigin.domain is required`);if($r.domain===z.origin)throw Error(`infrastructure.cdn.${Ie}: failoverOrigin must differ from origin (both are ${z.origin})`);vt.push({Id:zr,DomainName:$r.domain,OriginPath:$r.originPath||"",...Jo($r.domain)?{S3OriginConfig:{OriginAccessIdentity:""}}:{CustomOriginConfig:{HTTPPort:80,HTTPSPort:443,OriginProtocolPolicy:"https-only"}},OriginShield:{Enabled:!1},...pe($r,`infrastructure.cdn.${Ie}.failoverOrigin`)}),Yt=gn({primaryOriginId:It,secondaryOriginId:zr,statusCodes:z.failoverStatusCodes}),Tr=Yt.Items[0].Id}else if(z.failoverStatusCodes)throw Error(`infrastructure.cdn.${Ie}: failoverStatusCodes needs failoverOrigin to fail over to`);if(z.routeCompute)this.appendComputeAppOrigin(vt,Ht,Zt,u,g,this.resolveComputeCachePathPatterns(z.computeRoutes));let tr=Ke&&Ye?{AcmCertificateArn:M&&!V?{Ref:M}:Ke,SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:{CloudFrontDefaultCertificate:!0},_r=[...Zt];if(M&&!V&&Ye)_r.push(M);let Kr={Type:"AWS::CloudFront::Distribution",..._r.length>0?{DependsOn:_r}:{},Properties:{DistributionConfig:{Enabled:!0,Comment:`${u} ${g} ${Ie} CDN`,DefaultRootObject:"index.html",Origins:vt,...Yt?{OriginGroups:Yt}:{},DefaultCacheBehavior:{TargetOriginId:Tr,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:er,CachedMethods:["GET","HEAD","OPTIONS"],Compress:z.compress!==!1,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6"},...Ht.length>0?{CacheBehaviors:Ht}:{},...Ye&&Ke?{Aliases:[Ye]}:{},ViewerCertificate:tr,PriceClass:"PriceClass_100",HttpVersion:z.http3?"http2and3":"http2"}}};if(Bt(Kr.Properties.DistributionConfig,`infrastructure.cdn.${Ie}`),this.builder.addResource(yt,Kr),T&&Ye&&Ke){let zr=Ye.replace(/\./g,"").replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${zr}CdnARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[yt,...M&&!V?[M]:[]],Properties:{HostedZoneId:T,Name:Ye,Type:"A",AliasTarget:{HostedZoneId:"Z2FDTNDATAQYW2",DNSName:{"Fn::GetAtt":[yt,"DomainName"]}}}})}}if(this.mergedConfig.infrastructure?.queues)for(let[Ie,z]of Object.entries(this.mergedConfig.infrastructure.queues)){if(!this.shouldDeploy(z))continue;let{queue:Ye,logicalId:V}=Y.createQueue({slug:u,environment:g,name:`${u}-${g}-${Ie}`,fifo:z.fifo,visibilityTimeout:z.visibilityTimeout,messageRetentionPeriod:z.messageRetentionPeriod,delaySeconds:z.delaySeconds,maxMessageSize:z.maxMessageSize,receiveMessageWaitTime:z.receiveMessageWaitTime,contentBasedDeduplication:z.contentBasedDeduplication,encrypted:z.encrypted,kmsKeyId:z.kmsKeyId});this.builder.addResource(V,Ye);let Ke;if(z.deadLetterQueue){let{deadLetterQueue:yt,updatedSourceQueue:It,deadLetterLogicalId:vt}=Y.createDeadLetterQueue(V,{slug:u,environment:g,maxReceiveCount:z.maxReceiveCount});Ke=vt,this.builder.addResource(vt,yt);let Zt=this.builder.getResources()[V];if(Zt?.Properties)Zt.Properties.RedrivePolicy=It.Properties?.RedrivePolicy}if(z.trigger){let yt=z.trigger,It=`${u}${g}${yt.functionName}`.replace(/[^a-zA-Z0-9]/g,""),vt={Type:"AWS::Lambda::EventSourceMapping",Properties:{EventSourceArn:{"Fn::GetAtt":[V,"Arn"]},FunctionName:{Ref:It},BatchSize:yt.batchSize||10,MaximumBatchingWindowInSeconds:yt.batchWindow||0,Enabled:!0,...yt.reportBatchItemFailures!==!1&&{FunctionResponseTypes:["ReportBatchItemFailures"]},...yt.maxConcurrency&&{ScalingConfig:{MaximumConcurrency:yt.maxConcurrency}},...yt.filterPattern&&{FilterCriteria:{Filters:[{Pattern:JSON.stringify(yt.filterPattern)}]}}},DependsOn:[V,It]};this.builder.addResource(`${V}Trigger`,vt)}if(z.alarms?.enabled){let yt=z.alarms,It=yt.notificationTopicArn;if(!It&&yt.notificationEmails?.length){let Zt=`${V}AlarmTopic`;this.builder.addResource(Zt,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${u}-${g}-${Ie}-alarms`,DisplayName:`${Ie} Queue Alarms`}}),yt.notificationEmails.forEach((er,Tr)=>{this.builder.addResource(`${Zt}Sub${Tr}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:Zt},Protocol:"email",Endpoint:er}})}),It={Ref:Zt}}let vt=yt.queueDepthThreshold||1000;this.builder.addResource(`${V}DepthAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${u}-${g}-${Ie}-queue-depth`,AlarmDescription:`Queue ${Ie} depth exceeds ${vt} messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Average",Period:300,EvaluationPeriods:2,Threshold:vt,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[V,"QueueName"]}}],...It&&{AlarmActions:[It],OKActions:[It]}}});let Ht=yt.messageAgeThreshold||3600;if(this.builder.addResource(`${V}AgeAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${u}-${g}-${Ie}-message-age`,AlarmDescription:`Queue ${Ie} oldest message exceeds ${Ht} seconds`,MetricName:"ApproximateAgeOfOldestMessage",Namespace:"AWS/SQS",Statistic:"Maximum",Period:300,EvaluationPeriods:2,Threshold:Ht,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[V,"QueueName"]}}],...It&&{AlarmActions:[It],OKActions:[It]}}}),Ke&&yt.dlqAlarm!==!1)this.builder.addResource(`${Ke}Alarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${u}-${g}-${Ie}-dlq-messages`,AlarmDescription:`Dead letter queue for ${Ie} has messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Sum",Period:300,EvaluationPeriods:1,Threshold:0,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[Ke,"QueueName"]}}],...It&&{AlarmActions:[It]}}})}if(z.subscribe){let yt=z.subscribe,It=yt.topicArn;if(!It&&yt.topicName)It={Ref:yt.topicName};if(It){this.builder.addResource(`${V}SnsPolicy`,{Type:"AWS::SQS::QueuePolicy",Properties:{Queues:[{Ref:V}],PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"sns.amazonaws.com"},Action:"sqs:SendMessage",Resource:{"Fn::GetAtt":[V,"Arn"]},Condition:{ArnEquals:{"aws:SourceArn":It}}}]}}});let vt={TopicArn:It,Protocol:"sqs",Endpoint:{"Fn::GetAtt":[V,"Arn"]},RawMessageDelivery:yt.rawMessageDelivery||!1};if(yt.filterPolicy)vt.FilterPolicy=yt.filterPolicy,vt.FilterPolicyScope=yt.filterPolicyScope||"MessageAttributes";this.builder.addResource(`${V}SnsSub`,{Type:"AWS::SNS::Subscription",Properties:vt,DependsOn:`${V}SnsPolicy`})}}}if(this.mergedConfig.infrastructure?.realtime?.enabled)if((this.mergedConfig.infrastructure.realtime.mode||"serverless")==="server")this.generateRealtimeServerResources(u,g);else this.generateRealtimeResources(u,g);let le=this.mergedConfig.infrastructure?.redirects;if(le){let Ie=le.target||this.mergedConfig.infrastructure?.dns?.domain||"",z=le.protocol||"https";if(le.domains?.length&&Ie)for(let Ye of le.domains){let{bucket:V,bucketPolicy:Ke,logicalId:yt,policyLogicalId:It}=Ot.createDomainRedirectBucket({slug:u,environment:g,sourceDomain:Ye,targetDomain:Ie,protocol:z});this.builder.addResource(yt,V),this.builder.addResource(It,Ke)}if(le.paths&&Object.keys(le.paths).length>0){let Ye=Ot.fromMapping(le.paths,{statusCode:le.statusCode||301}),{function:V,logicalId:Ke}=Ot.createPathRedirectFunction({slug:u,environment:g,rules:Ye});this.builder.addResource(Ke,V)}}if(this.mergedConfig.infrastructure?.monitoring?.alarms)for(let[Ie,z]of Object.entries(this.mergedConfig.infrastructure.monitoring.alarms)){let{alarm:Ye,logicalId:V}=Zo.createAlarm({slug:u,environment:g,alarmName:`${u}-${g}-${Ie}`,metricName:z.metricName||"Errors",namespace:z.namespace||"AWS/Lambda",threshold:z.threshold||1,comparisonOperator:z.comparisonOperator||"GreaterThanThreshold"});this.builder.addResource(V,Ye)}let K=this.mergedConfig.infrastructure?.cache;if(K){let Ie=K.type||"redis";if(Ie==="redis"){this.generateNetworkInfrastructure(u,g);let z=K.redis||{},{replicationGroup:Ye,subnetGroup:V,logicalId:Ke,subnetGroupId:yt}=Yo.createRedis({slug:u,environment:g,nodeType:z.nodeType||K.nodeType||"cache.t3.micro",engineVersion:z.engineVersion||"7.1",port:z.port||6379,numCacheClusters:z.numCacheNodes||2,automaticFailover:z.automaticFailoverEnabled!==!1,atRestEncryption:!0,transitEncryption:!0,snapshotRetentionDays:z.snapshotRetentionLimit||7,snapshotWindow:z.snapshotWindow,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(Ke,Ye),V&&yt)this.builder.addResource(yt,V);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[Ke,"PrimaryEndPoint.Address"]},Description:"Redis primary endpoint address"}),this.builder.addOutput("CachePort",{Value:{"Fn::GetAtt":[Ke,"PrimaryEndPoint.Port"]},Description:"Redis primary endpoint port"})}else if(Ie==="memcached"){this.generateNetworkInfrastructure(u,g);let z=K.elasticache||{},{cluster:Ye,subnetGroup:V,logicalId:Ke,subnetGroupId:yt}=Yo.createMemcached({slug:u,environment:g,nodeType:z.nodeType||K.nodeType||"cache.t3.micro",engineVersion:z.engineVersion||"1.6.22",numCacheNodes:z.numCacheNodes||2,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(Ke,Ye),V&&yt)this.builder.addResource(yt,V);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[Ke,"ConfigurationEndpoint.Address"]},Description:"Memcached configuration endpoint address"})}}let Ct=this.mergedConfig.infrastructure?.email;if(Ct){let Ie=Ct.domain||this.mergedConfig.infrastructure?.dns?.domain;if(Ie){let{emailIdentity:z,logicalId:Ye}=f.verifyDomain({domain:Ie,slug:u,environment:g,enableDkim:Ct.enableDkim!==!1,dkimKeyLength:Ct.dkimKeyLength||"RSA_2048_BIT"});if(this.builder.addResource(Ye,z),Ct.configurationSet!==!1){let{configurationSet:yt,logicalId:It}=f.createConfigurationSet({slug:u,environment:g});this.builder.addResource(It,yt)}let V=Ct.hostedZoneId||this.mergedConfig.infrastructure?.dns?.hostedZoneId;if(V){if(Ct.enableDkim!==!1)for(let Zt=1;Zt<=3;Zt++){let er=`DkimRecord${Zt}${Ie.replace(/\./g,"")}`;this.builder.addResource(er,{Type:"AWS::Route53::RecordSet",DependsOn:[Ye],Properties:{HostedZoneId:V,Name:{"Fn::GetAtt":[Ye,`DkimDNSTokenName${Zt}`]},Type:"CNAME",TTL:1800,ResourceRecords:[{"Fn::GetAtt":[Ye,`DkimDNSTokenValue${Zt}`]}]}})}let{record:yt,logicalId:It}=f.createSpfRecord(Ie,V);this.builder.addResource(It,yt);let{record:vt,logicalId:Ht}=f.createDmarcRecord(Ie,V,{policy:"none",reportingEmail:Ct.dmarcReportingEmail||`dmarc-reports@${Ie}`});this.builder.addResource(Ht,vt)}this.builder.addOutput("EmailDomain",{Value:Ie,Description:"SES verified email domain"});let Ke=Ct.server;if(Ke?.enabled&&V){let yt=this.mergedConfig.environments[g]?.region||this.mergedConfig.project.region||"us-east-1",It=`${u}-${g}-email`,{role:vt,policy:Ht,roleLogicalId:Zt,policyLogicalId:er}=f.createEmailLambdaRole({slug:u,environment:g,s3BucketArn:`arn:aws:s3:::${It}`,sesIdentityArn:`arn:aws:ses:${yt}:*:identity/${Ie}`});this.builder.addResource(Zt,vt),this.builder.addResource(er,Ht);let{function:Tr,permission:Yt,logicalId:tr,permissionLogicalId:_r}=f.createInboundEmailLambda({slug:u,environment:g,roleArn:{"Fn::GetAtt":[Zt,"Arn"]},s3BucketName:It,organizedPrefix:"mailboxes/"});this.builder.addResource(tr,Tr),this.builder.addResource(_r,Yt);let Kr=f.createInboundEmailSetup({slug:u,environment:g,domain:Ie,s3BucketName:It,s3KeyPrefix:"inbox/",region:yt,hostedZoneId:V,lambdaFunctionArn:{"Fn::GetAtt":[tr,"Arn"]}});for(let[$r,qr]of Object.entries(Kr.resources)){if(qr.Type==="AWS::SES::ReceiptRule")qr.DependsOn=[_r,tr];this.builder.addResource($r,qr)}this.builder.addOutput("InboundEmailLambda",{Value:{Ref:tr},Description:"Inbound email processing Lambda function"}),this.builder.addOutput("EmailBucket",{Value:It,Description:"S3 bucket for email storage"});let zr=this.serverEipLogicalIds.get("app");if(zr&&V){let qr=`${Ke?.subdomain||"mail"}.${Ie}`,Qn=qr.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${Qn}ARecord`,{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:V,Name:qr,Type:"A",TTL:"300",ResourceRecords:[{Ref:zr}]}})}this.builder.addOutput("MailHost",{Value:`mail.${Ie}`,Description:"Mail server hostname for SMTP/IMAP clients"})}}}let Tt=this.mergedConfig.infrastructure?.search;if(Tt){let Ie=Tt.vpc?{subnetIds:[{Ref:"PublicSubnet1"}],securityGroupIds:[]}:void 0;if(Ie)this.generateNetworkInfrastructure(u,g);let{domain:z,logicalId:Ye}=pr.createDomain({slug:u,environment:g,engineVersion:Tt.engineVersion||"OpenSearch_2.11",instanceType:Tt.instanceType||"t3.small.search",instanceCount:Tt.instanceCount||1,volumeSize:Tt.volumeSize||10,volumeType:Tt.volumeType||"gp3",dedicatedMaster:Tt.dedicatedMaster||!1,dedicatedMasterType:Tt.dedicatedMasterType,dedicatedMasterCount:Tt.dedicatedMasterCount||3,multiAz:Tt.multiAz||!1,encryption:Tt.encryption||{atRest:!0,nodeToNode:!0},advancedSecurity:Tt.advancedSecurity,autoTune:Tt.autoTune!==!1,vpc:Ie});this.builder.addResource(Ye,z),this.builder.addOutput("SearchDomainEndpoint",{Value:{"Fn::GetAtt":[Ye,"DomainEndpoint"]},Description:"OpenSearch domain endpoint"}),this.builder.addOutput("SearchDomainArn",{Value:{"Fn::GetAtt":[Ye,"Arn"]},Description:"OpenSearch domain ARN"})}let He=this.mergedConfig.infrastructure?.fileSystem;if(He&&Object.keys(He).length>0){this.generateNetworkInfrastructure(u,g);for(let[Ie,z]of Object.entries(He)){let{fileSystem:Ye,logicalId:V}=it.createFileSystem({slug:`${u}-${Ie}`,environment:g,encrypted:z.encrypted!==!1,performanceMode:z.performanceMode||"generalPurpose",throughputMode:z.throughputMode||"bursting",enableBackup:!0});this.builder.addResource(V,Ye);let{securityGroup:Ke,logicalId:yt}=it.createEfsSecurityGroup({slug:`${u}-${Ie}`,environment:g,vpcId:{Ref:"VPC"},sourceCidrBlocks:["10.0.0.0/16"]});this.builder.addResource(yt,Ke);let{mountTargets:It,logicalIds:vt}=it.createMultiAzMountTargets(V,{slug:`${u}-${Ie}`,environment:g,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],securityGroupId:{Ref:yt}});for(let Ht=0;Ht<It.length;Ht++)this.builder.addResource(vt[Ht],It[Ht]);this.builder.addOutput(`${Ie}FileSystemId`,{Value:{Ref:V},Description:`EFS file system ID for ${Ie}`})}}let Se=this.mergedConfig.infrastructure?.ai;if(Se){let Ie=Se.service||"ecs",z=Se.models||["*"],Ye=Se.allowStreaming!==!1,V;if(Ie==="ecs")V=Me.enableBedrockForEcs({slug:u,environment:g,models:z,allowStreaming:Ye});else if(Ie==="ec2")V=Me.enableBedrockForEc2({slug:u,environment:g,models:z,allowStreaming:Ye});else if(Ie==="lambda")V=Me.enableBedrockForLambda({slug:u,environment:g,models:z,allowStreaming:Ye});else V=Me.createBedrockRole(Ie,{slug:u,environment:g,models:z,allowStreaming:Ye});if(this.builder.addResource(V.logicalId,V.role),Se.allowAsync){let{policy:Ke,logicalId:yt}=Me.createBedrockPolicy({slug:u,environment:g,models:z,allowStreaming:Ye,allowAsync:!0});this.builder.addResource(yt,Ke)}this.builder.addOutput("BedrockRoleArn",{Value:{"Fn::GetAtt":[V.logicalId,"Arn"]},Description:"IAM role ARN with Bedrock permissions"})}}generateRealtimeResources(u,g){let h=this.mergedConfig.infrastructure?.realtime;if(!h)return;let S=h.name||`${u}-${g}-realtime`,A=h.scaling||{},T=h.storage||{type:"dynamodb"},P=A.handlerMemory||256,M=A.handlerTimeout||30,W=`${u}${g}RealtimeConnections`.replace(/[^a-zA-Z0-9]/g,""),le=`${u}${g}RealtimeChannels`.replace(/[^a-zA-Z0-9]/g,"");if(T.type==="dynamodb"){let vt=T.dynamodb||{},Ht=vt.billingMode||"PAY_PER_REQUEST";this.builder.addResource(W,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${u}-${g}-realtime-connections`,BillingMode:Ht,AttributeDefinitions:[{AttributeName:"connectionId",AttributeType:"S"},{AttributeName:"userId",AttributeType:"S"}],KeySchema:[{AttributeName:"connectionId",KeyType:"HASH"}],GlobalSecondaryIndexes:[{IndexName:"userId-index",KeySchema:[{AttributeName:"userId",KeyType:"HASH"}],Projection:{ProjectionType:"ALL"},...Ht==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:vt.readCapacity||5,WriteCapacityUnits:vt.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...Ht==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:vt.readCapacity||5,WriteCapacityUnits:vt.writeCapacity||5}},...vt.pointInTimeRecovery&&{PointInTimeRecoverySpecification:{PointInTimeRecoveryEnabled:!0}},Tags:[{Key:"Name",Value:`${u}-${g}-realtime-connections`},{Key:"Environment",Value:g}]}}),this.builder.addResource(le,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${u}-${g}-realtime-channels`,BillingMode:Ht,AttributeDefinitions:[{AttributeName:"channel",AttributeType:"S"},{AttributeName:"connectionId",AttributeType:"S"}],KeySchema:[{AttributeName:"channel",KeyType:"HASH"},{AttributeName:"connectionId",KeyType:"RANGE"}],GlobalSecondaryIndexes:[{IndexName:"connectionId-index",KeySchema:[{AttributeName:"connectionId",KeyType:"HASH"}],Projection:{ProjectionType:"ALL"},...Ht==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:vt.readCapacity||5,WriteCapacityUnits:vt.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...Ht==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:vt.readCapacity||5,WriteCapacityUnits:vt.writeCapacity||5}},Tags:[{Key:"Name",Value:`${u}-${g}-realtime-channels`},{Key:"Environment",Value:g}]}})}let K=`${u}${g}RealtimeRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(K,{Type:"AWS::IAM::Role",Properties:{RoleName:`${u}-${g}-realtime-handler-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"lambda.amazonaws.com"},Action:"sts:AssumeRole"}]},ManagedPolicyArns:["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"],Policies:[{PolicyName:"RealtimeHandlerPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["dynamodb:GetItem","dynamodb:PutItem","dynamodb:DeleteItem","dynamodb:Query","dynamodb:Scan","dynamodb:UpdateItem"],Resource:[{"Fn::GetAtt":[W,"Arn"]},{"Fn::Sub":`\${${W}.Arn}/index/*`},{"Fn::GetAtt":[le,"Arn"]},{"Fn::Sub":`\${${le}.Arn}/index/*`}]},{Effect:"Allow",Action:"execute-api:ManageConnections",Resource:{"Fn::Sub":"arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*"}}]}}]}});let Ct=`${u}${g}RealtimeConnect`.replace(/[^a-zA-Z0-9]/g,""),Tt=`${u}${g}RealtimeDisconnect`.replace(/[^a-zA-Z0-9]/g,""),He=`${u}${g}RealtimeMessage`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Ct,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${u}-${g}-realtime-connect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[K,"Arn"]},MemorySize:P,Timeout:M,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:W},CHANNELS_TABLE:{Ref:le},ENVIRONMENT:g}},Code:{ZipFile:this.generateConnectHandlerCode()}},DependsOn:[K,W]}),this.builder.addResource(Tt,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${u}-${g}-realtime-disconnect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[K,"Arn"]},MemorySize:P,Timeout:M,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:W},CHANNELS_TABLE:{Ref:le},ENVIRONMENT:g}},Code:{ZipFile:this.generateDisconnectHandlerCode()}},DependsOn:[K,W,le]}),this.builder.addResource(He,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${u}-${g}-realtime-message`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[K,"Arn"]},MemorySize:P,Timeout:M,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:W},CHANNELS_TABLE:{Ref:le},ENVIRONMENT:g}},Code:{ZipFile:this.generateMessageHandlerCode()}},DependsOn:[K,W,le]});let Se=`${u}${g}RealtimeApi`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Se,{Type:"AWS::ApiGatewayV2::Api",Properties:{Name:S,ProtocolType:"WEBSOCKET",RouteSelectionExpression:"$request.body.action",Tags:{Name:S,Environment:g}}});let Ie=`${Ct}Permission`,z=`${Tt}Permission`,Ye=`${He}Permission`;this.builder.addResource(Ie,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:Ct},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${Se}}/*/$connect`}}}),this.builder.addResource(z,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:Tt},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${Se}}/*/$disconnect`}}}),this.builder.addResource(Ye,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:He},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${Se}}/*/$default`}}});let V=`${Se}ConnectInteg`,Ke=`${Se}DisconnectInteg`,yt=`${Se}MessageInteg`;this.builder.addResource(V,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:Se},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${Ct}.Arn}/invocations`}}}),this.builder.addResource(Ke,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:Se},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${Tt}.Arn}/invocations`}}}),this.builder.addResource(yt,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:Se},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${He}.Arn}/invocations`}}}),this.builder.addResource(`${Se}ConnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:Se},RouteKey:"$connect",AuthorizationType:"NONE",Target:{"Fn::Sub":`integrations/\${${V}}`}}}),this.builder.addResource(`${Se}DisconnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:Se},RouteKey:"$disconnect",Target:{"Fn::Sub":`integrations/\${${Ke}}`}}}),this.builder.addResource(`${Se}DefaultRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:Se},RouteKey:"$default",Target:{"Fn::Sub":`integrations/\${${yt}}`}}});let It=`${Se}Stage`;if(this.builder.addResource(It,{Type:"AWS::ApiGatewayV2::Stage",Properties:{ApiId:{Ref:Se},StageName:g,AutoDeploy:!0,DefaultRouteSettings:{ThrottlingBurstLimit:A.messagesPerSecond||1000,ThrottlingRateLimit:A.messagesPerSecond||1000}}}),h.monitoring?.enabled){let vt=h.monitoring,Ht=vt.notificationTopicArn;if(!Ht&&vt.notificationEmails?.length){let Zt=`${Se}AlarmTopic`;this.builder.addResource(Zt,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${u}-${g}-realtime-alarms`,DisplayName:"Realtime WebSocket Alarms"}}),vt.notificationEmails.forEach((er,Tr)=>{this.builder.addResource(`${Zt}Sub${Tr}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:Zt},Protocol:"email",Endpoint:er}})}),Ht={Ref:Zt}}if(vt.connectionThreshold)this.builder.addResource(`${Se}ConnectionAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${u}-${g}-realtime-connections`,AlarmDescription:`WebSocket connections exceed ${vt.connectionThreshold}`,MetricName:"ConnectCount",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:300,EvaluationPeriods:2,Threshold:vt.connectionThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:Se}}],...Ht&&{AlarmActions:[Ht]}}});if(vt.errorThreshold)this.builder.addResource(`${Se}ErrorAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${u}-${g}-realtime-errors`,AlarmDescription:`WebSocket errors exceed ${vt.errorThreshold}/min`,MetricName:"ExecutionError",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:60,EvaluationPeriods:3,Threshold:vt.errorThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:Se}}],...Ht&&{AlarmActions:[Ht]}}})}this.builder.addOutput(`${Se}Endpoint`,{Description:"WebSocket API endpoint URL",Value:{"Fn::Sub":`wss://\${${Se}}.execute-api.\${AWS::Region}.amazonaws.com/${g}`},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-endpoint"}}}),this.builder.addOutput(`${Se}Id`,{Description:"WebSocket API ID",Value:{Ref:Se},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-api-id"}}})}generateConnectHandlerCode(){return`
|
|
15
15
|
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
|
|
16
16
|
const { DynamoDBDocumentClient, PutCommand } = require('@aws-sdk/lib-dynamodb');
|
|
17
17
|
|
|
@@ -170,7 +170,7 @@ catch (error) {
|
|
|
170
170
|
return { statusCode: 500, body: JSON.stringify({ error: 'Internal error' }) };
|
|
171
171
|
}
|
|
172
172
|
};
|
|
173
|
-
`.trim()}generateRealtimeServerResources(u,g){let h=this.mergedConfig.infrastructure?.realtime;if(!h)return;let
|
|
173
|
+
`.trim()}generateRealtimeServerResources(u,g){let h=this.mergedConfig.infrastructure?.realtime;if(!h)return;let S=h.server||{},A=S.port||6001,T=S.instances||1,P=`${u}${g}RealtimeSG`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(P,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`Security group for ${u} realtime WebSocket server`,GroupName:`${u}-${g}-realtime-sg`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:A,ToPort:A,CidrIp:"0.0.0.0/0",Description:"WebSocket connections"},{IpProtocol:"tcp",FromPort:443,ToPort:443,CidrIp:"0.0.0.0/0",Description:"HTTPS/WSS connections"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${u}-${g}-realtime-sg`},{Key:"Environment",Value:g}]}});let M=`${u}${g}RealtimeTaskDef`.replace(/[^a-zA-Z0-9]/g,""),W=`${u}${g}RealtimeTaskRole`.replace(/[^a-zA-Z0-9]/g,""),le=`${u}${g}RealtimeExecRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(le,{Type:"AWS::IAM::Role",Properties:{RoleName:`${u}-${g}-realtime-exec-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},ManagedPolicyArns:["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"]}}),this.builder.addResource(W,{Type:"AWS::IAM::Role",Properties:{RoleName:`${u}-${g}-realtime-task-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},Policies:[{PolicyName:"RealtimeTaskPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["logs:CreateLogStream","logs:PutLogEvents"],Resource:"*"},...S.redis?.enabled?[{Effect:"Allow",Action:["elasticache:DescribeCacheClusters","elasticache:DescribeReplicationGroups"],Resource:"*"}]:[]]}}]}});let K=[{Name:"BROADCAST_HOST",Value:S.host||"0.0.0.0"},{Name:"BROADCAST_PORT",Value:String(A)},{Name:"NODE_ENV",Value:g==="production"?"production":"development"}];if(S.redis?.enabled){if(S.redis.useElastiCache)K.push({Name:"REDIS_HOST",Value:{"Fn::GetAtt":["CacheCluster","RedisEndpoint.Address"]}}),K.push({Name:"REDIS_PORT",Value:{"Fn::GetAtt":["CacheCluster","RedisEndpoint.Port"]}});else K.push({Name:"REDIS_HOST",Value:S.redis.host||"localhost"}),K.push({Name:"REDIS_PORT",Value:String(S.redis.port||6379)});if(S.redis.keyPrefix)K.push({Name:"REDIS_KEY_PREFIX",Value:S.redis.keyPrefix})}this.builder.addResource(M,{Type:"AWS::ECS::TaskDefinition",Properties:{Family:`${u}-${g}-realtime`,NetworkMode:"awsvpc",RequiresCompatibilities:["FARGATE"],Cpu:"512",Memory:"1024",ExecutionRoleArn:{"Fn::GetAtt":[le,"Arn"]},TaskRoleArn:{"Fn::GetAtt":[W,"Arn"]},ContainerDefinitions:[{Name:"realtime",Image:{"Fn::Sub":`\${AWS::AccountId}.dkr.ecr.\${AWS::Region}.amazonaws.com/${u}-realtime:latest`},Essential:!0,PortMappings:[{ContainerPort:A,Protocol:"tcp"}],Environment:K,LogConfiguration:{LogDriver:"awslogs",Options:{"awslogs-group":`/ecs/${u}-${g}-realtime`,"awslogs-region":{Ref:"AWS::Region"},"awslogs-stream-prefix":"realtime"}},HealthCheck:{Command:["CMD-SHELL",`curl -f http://localhost:${A}${S.healthCheckPath||"/health"} || exit 1`],Interval:30,Timeout:5,Retries:3,StartPeriod:60}}],Tags:[{Key:"Name",Value:`${u}-${g}-realtime`},{Key:"Environment",Value:g}]},DependsOn:[le,W]});let Ct=`${u}${g}RealtimeLogs`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Ct,{Type:"AWS::Logs::LogGroup",Properties:{LogGroupName:`/ecs/${u}-${g}-realtime`,RetentionInDays:30}});let Tt=`${u}${g}RealtimeService`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(Tt,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${u}-${g}-realtime`,Cluster:{Ref:"ECSCluster"},TaskDefinition:{Ref:M},DesiredCount:T,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:P}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},HealthCheckGracePeriodSeconds:60,Tags:[{Key:"Name",Value:`${u}-${g}-realtime`},{Key:"Environment",Value:g}]},DependsOn:[M,P,Ct]}),S.autoScaling){let He=S.autoScaling,Se=`${u}${g}RealtimeScalableTarget`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(Se,{Type:"AWS::ApplicationAutoScaling::ScalableTarget",Properties:{MaxCapacity:He.max||10,MinCapacity:He.min||1,ResourceId:{"Fn::Sub":`service/\${ECSCluster}/${u}-${g}-realtime`},RoleARN:{"Fn::Sub":"arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService"},ScalableDimension:"ecs:service:DesiredCount",ServiceNamespace:"ecs"},DependsOn:Tt}),He.targetCPU)this.builder.addResource(`${u}${g}RealtimeCPUPolicy`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${u}-${g}-realtime-cpu-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:Se},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageCPUUtilization"},TargetValue:He.targetCPU,ScaleInCooldown:300,ScaleOutCooldown:60}}})}if(h.monitoring?.enabled){let He=h.monitoring,Se=He.notificationTopicArn;if(!Se&&He.notificationEmails?.length){let Ie=`${u}${g}RealtimeAlarmTopic`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Ie,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${u}-${g}-realtime-alarms`,DisplayName:"Realtime Server Alarms"}}),He.notificationEmails.forEach((z,Ye)=>{this.builder.addResource(`${Ie}Sub${Ye}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:Ie},Protocol:"email",Endpoint:z}})}),Se={Ref:Ie}}this.builder.addResource(`${u}${g}RealtimeCPUAlarm`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${u}-${g}-realtime-high-cpu`,AlarmDescription:"Realtime server CPU utilization is high",MetricName:"CPUUtilization",Namespace:"AWS/ECS",Statistic:"Average",Period:300,EvaluationPeriods:2,Threshold:80,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ClusterName",Value:{Ref:"ECSCluster"}},{Name:"ServiceName",Value:`${u}-${g}-realtime`}],...Se&&{AlarmActions:[Se]}},DependsOn:Tt}),this.builder.addResource(`${u}${g}RealtimeMemoryAlarm`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${u}-${g}-realtime-high-memory`,AlarmDescription:"Realtime server memory utilization is high",MetricName:"MemoryUtilization",Namespace:"AWS/ECS",Statistic:"Average",Period:300,EvaluationPeriods:2,Threshold:80,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ClusterName",Value:{Ref:"ECSCluster"}},{Name:"ServiceName",Value:`${u}-${g}-realtime`}],...Se&&{AlarmActions:[Se]}},DependsOn:Tt})}this.builder.addOutput(`${u}${g}RealtimeEndpoint`.replace(/[^a-zA-Z0-9]/g,""),{Description:"Realtime WebSocket server endpoint",Value:{"Fn::Sub":`wss://${u}-${g}-realtime.\${AWS::Region}.elb.amazonaws.com:${A}`},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-endpoint"}}})}generateBroadcastConfig(){let u=this.mergedConfig.infrastructure?.realtime;if(!u||u.mode!=="server")return"";let g=u.server||{};return`import type { BroadcastConfig } from 'ts-broadcasting'
|
|
174
174
|
|
|
175
175
|
export default {
|
|
176
176
|
verbose: ${this.environment!=="production"},
|
|
@@ -219,7 +219,7 @@ ${g.loadManagement?.enabled?`
|
|
|
219
219
|
},
|
|
220
220
|
`:""}
|
|
221
221
|
} satisfies BroadcastConfig
|
|
222
|
-
`}toYAML(){return this.builder.toYAML()}toJSON(){return this.builder.toJSON()}getBuilder(){return this.builder}}function
|
|
222
|
+
`}toYAML(){return this.builder.toYAML()}toJSON(){return this.builder.toJSON()}getBuilder(){return this.builder}}function Ip(u){let g=[],h=[];if(!u.AWSTemplateFormatVersion)g.push({path:"AWSTemplateFormatVersion",message:"AWSTemplateFormatVersion is required",severity:"error"});else if(u.AWSTemplateFormatVersion!=="2010-09-09")h.push({path:"AWSTemplateFormatVersion",message:'AWSTemplateFormatVersion should be "2010-09-09"',severity:"warning"});if(!u.Resources||Object.keys(u.Resources).length===0)g.push({path:"Resources",message:"At least one resource is required",severity:"error"});else for(let[S,A]of Object.entries(u.Resources))Dp(S,A,g,h);if(u.Resources){let S=Ep(u.Resources);if(S.length>0)g.push({path:"Resources",message:`Circular dependencies detected: ${S.join(" -> ")}`,severity:"error"})}if(u.Parameters)for(let[S,A]of Object.entries(u.Parameters))kp(S,A,g,h);if(u.Outputs)for(let[S,A]of Object.entries(u.Outputs))vp(S,A,g,h);return{valid:g.length===0,errors:g,warnings:h}}function Dp(u,g,h,S){if(!g.Type)h.push({path:`Resources.${u}.Type`,message:"Resource Type is required",severity:"error"});if(!/^[a-zA-Z0-9]+$/.test(u))S.push({path:`Resources.${u}`,message:"Logical ID should only contain alphanumeric characters",severity:"warning"});if(g.Type&&!g.Type.startsWith("AWS::")&&!g.Type.startsWith("Custom::"))h.push({path:`Resources.${u}.Type`,message:'Resource Type must start with "AWS::" or "Custom::"',severity:"error"});if(g.Properties){for(let[A,T]of Object.entries(g.Properties))if(T===void 0||T===null)S.push({path:`Resources.${u}.Properties.${A}`,message:"Property has undefined or null value",severity:"warning"})}if(g.DependsOn){if(typeof g.DependsOn==="string"){if(g.DependsOn===u)h.push({path:`Resources.${u}.DependsOn`,message:"Resource cannot depend on itself",severity:"error"})}else if(Array.isArray(g.DependsOn)){if(g.DependsOn.includes(u))h.push({path:`Resources.${u}.DependsOn`,message:"Resource cannot depend on itself",severity:"error"})}}}function kp(u,g,h,S){if(!g.Type)h.push({path:`Parameters.${u}.Type`,message:"Parameter Type is required",severity:"error"});let A=["String","Number","List<Number>","CommaDelimitedList","AWS::SSM::Parameter::Value<String>"];if(g.Type&&!A.includes(g.Type)&&!g.Type.startsWith("AWS::"))S.push({path:`Parameters.${u}.Type`,message:`Uncommon parameter type: ${g.Type}`,severity:"warning"});if(g.NoEcho&&g.Default)S.push({path:`Parameters.${u}`,message:"NoEcho parameters should not have default values",severity:"warning"})}function vp(u,g,h,S){if(!g.Value)h.push({path:`Outputs.${u}.Value`,message:"Output Value is required",severity:"error"})}function Ep(u){let g={};for(let[P,M]of Object.entries(u)){if(g[P]=[],M.DependsOn){if(typeof M.DependsOn==="string")g[P].push(M.DependsOn);else if(Array.isArray(M.DependsOn))g[P].push(...M.DependsOn)}let W=zc(M);g[P].push(...W)}let h=new Set,S=new Set,A=[];function T(P,M){h.add(P),S.add(P),M.push(P);let W=g[P]||[];for(let le of W)if(!h.has(le)){if(T(le,M))return!0}else if(S.has(le)){let K=M.indexOf(le);return A.push(...M.slice(K),le),!0}return M.pop(),S.delete(P),!1}for(let P of Object.keys(g))if(!h.has(P)){if(T(P,[]))return A}return[]}function zc(u,g=[]){if(typeof u!=="object"||u===null)return g;if(Array.isArray(u)){for(let h of u)zc(h,g);return g}if(u.Ref&&typeof u.Ref==="string"&&!u.Ref.startsWith("AWS::"))g.push(u.Ref);if(u["Fn::GetAtt"]){let h=u["Fn::GetAtt"];if(Array.isArray(h)&&h.length>0)g.push(h[0])}for(let h of Object.values(u))zc(h,g);return g}function xp(u){let g=[],h=[],S=new TextEncoder().encode(u).length,A=51200,T=460800;if(S>51200)if(S>460800)g.push({path:"template",message:`Template size (${S} bytes) exceeds maximum allowed size of 460800 bytes`,severity:"error"});else h.push({path:"template",message:`Template size (${S} bytes) exceeds direct upload limit (51200 bytes). You must upload to S3 first.`,severity:"warning"});return{valid:g.length===0,errors:g,warnings:h}}function Lp(u){let g=[],h=[],S=u.Resources?Object.keys(u.Resources).length:0,A=u.Parameters?Object.keys(u.Parameters).length:0,T=u.Outputs?Object.keys(u.Outputs).length:0;if(S>500)g.push({path:"Resources",message:`Template has ${S} resources, exceeding the limit of 500`,severity:"error"});else if(S>200)h.push({path:"Resources",message:`Template has ${S} resources. Consider using nested stacks for better organization.`,severity:"warning"});if(A>200)g.push({path:"Parameters",message:`Template has ${A} parameters, exceeding the limit of 200`,severity:"error"});if(T>200)g.push({path:"Outputs",message:`Template has ${T} outputs, exceeding the limit of 200`,severity:"error"});return{valid:g.length===0,errors:g,warnings:h}}function vR(u){let{domains:g,email:h,staging:S=!1,challengeType:A="http-01",certPath:T="/etc/letsencrypt/live",autoRenew:P=!0}=u,M=g[0],W=g.map((K)=>`-d ${K}`).join(" "),le=S?"--staging":"";if(A==="http-01")return _p({domains:g,email:h,staging:S,certPath:T,autoRenew:P,domainFlags:W,stagingFlag:le,primaryDomain:M});else{let K=u.dnsProvider?.provider||(u.hostedZoneId?"route53":void 0);return Bp({domains:g,email:h,staging:S,certPath:T,autoRenew:P,domainFlags:W,stagingFlag:le,primaryDomain:M,hostedZoneId:u.hostedZoneId,dnsProvider:u.dnsProvider,dnsProviderType:K})}}function _p(u){let{email:g,certPath:h,autoRenew:S,domainFlags:A,stagingFlag:T,primaryDomain:P}=u;return`
|
|
223
223
|
# ==========================================
|
|
224
224
|
# Let's Encrypt Certificate Setup (HTTP-01)
|
|
225
225
|
# ==========================================
|
|
@@ -236,8 +236,8 @@ certbot certonly \\
|
|
|
236
236
|
--non-interactive \\
|
|
237
237
|
--agree-tos \\
|
|
238
238
|
--email ${g} \\
|
|
239
|
-
${
|
|
240
|
-
${
|
|
239
|
+
${T} \\
|
|
240
|
+
${A}
|
|
241
241
|
|
|
242
242
|
# Check if certificate was obtained
|
|
243
243
|
if [ -f "${h}/${P}/fullchain.pem" ]; then
|
|
@@ -253,11 +253,11 @@ else
|
|
|
253
253
|
echo "Failed to obtain certificate!"
|
|
254
254
|
fi
|
|
255
255
|
|
|
256
|
-
${
|
|
256
|
+
${S?Kc(P):"# Auto-renewal disabled"}
|
|
257
257
|
|
|
258
258
|
# Restart the application
|
|
259
259
|
systemctl start stacks
|
|
260
|
-
`}function
|
|
260
|
+
`}function Bp(u){let{email:g,certPath:h,autoRenew:S,domainFlags:A,stagingFlag:T,primaryDomain:P,hostedZoneId:M,dnsProvider:W,dnsProviderType:le}=u;if(le==="route53"||M){if(!M)throw Error("hostedZoneId is required for DNS-01 challenge with Route53");return`
|
|
261
261
|
# ==========================================
|
|
262
262
|
# Let's Encrypt Certificate Setup (DNS-01 via Route53)
|
|
263
263
|
# ==========================================
|
|
@@ -271,8 +271,8 @@ certbot certonly \\
|
|
|
271
271
|
--non-interactive \\
|
|
272
272
|
--agree-tos \\
|
|
273
273
|
--email ${g} \\
|
|
274
|
-
${
|
|
275
|
-
${
|
|
274
|
+
${T} \\
|
|
275
|
+
${A}
|
|
276
276
|
|
|
277
277
|
# Check if certificate was obtained
|
|
278
278
|
if [ -f "${h}/${P}/fullchain.pem" ]; then
|
|
@@ -288,31 +288,31 @@ else
|
|
|
288
288
|
echo "Failed to obtain certificate!"
|
|
289
289
|
fi
|
|
290
290
|
|
|
291
|
-
${
|
|
292
|
-
`}if(!
|
|
291
|
+
${S?Kc(P):"# Auto-renewal disabled"}
|
|
292
|
+
`}if(!W)throw Error("dnsProvider configuration is required for DNS-01 challenge with external DNS providers");let K=W.provider.toUpperCase(),Ct=cg(W);return`
|
|
293
293
|
# ==========================================
|
|
294
|
-
# Let's Encrypt Certificate Setup (DNS-01 via ${
|
|
294
|
+
# Let's Encrypt Certificate Setup (DNS-01 via ${K})
|
|
295
295
|
# ==========================================
|
|
296
296
|
|
|
297
297
|
# Install certbot
|
|
298
298
|
dnf install -y certbot jq curl
|
|
299
299
|
|
|
300
|
-
${
|
|
300
|
+
${Ct}
|
|
301
301
|
|
|
302
302
|
# Create DNS challenge hook scripts directory
|
|
303
303
|
mkdir -p /etc/letsencrypt/hooks
|
|
304
304
|
|
|
305
|
-
# Create authenticator hook for ${
|
|
305
|
+
# Create authenticator hook for ${K}
|
|
306
306
|
cat > /etc/letsencrypt/hooks/auth-hook.sh << 'AUTHHOOK'
|
|
307
307
|
#!/bin/bash
|
|
308
|
-
# DNS-01 authenticator hook for ${
|
|
308
|
+
# DNS-01 authenticator hook for ${K}
|
|
309
309
|
# This creates the TXT record for ACME challenge
|
|
310
310
|
|
|
311
311
|
DOMAIN="$CERTBOT_DOMAIN"
|
|
312
312
|
VALIDATION="$CERTBOT_VALIDATION"
|
|
313
313
|
RECORD_NAME="_acme-challenge.$DOMAIN"
|
|
314
314
|
|
|
315
|
-
${lg(
|
|
315
|
+
${lg(W)}
|
|
316
316
|
|
|
317
317
|
# Wait for DNS propagation
|
|
318
318
|
echo "Waiting 60 seconds for DNS propagation..."
|
|
@@ -321,17 +321,17 @@ AUTHHOOK
|
|
|
321
321
|
|
|
322
322
|
chmod +x /etc/letsencrypt/hooks/auth-hook.sh
|
|
323
323
|
|
|
324
|
-
# Create cleanup hook for ${
|
|
324
|
+
# Create cleanup hook for ${K}
|
|
325
325
|
cat > /etc/letsencrypt/hooks/cleanup-hook.sh << 'CLEANUPHOOK'
|
|
326
326
|
#!/bin/bash
|
|
327
|
-
# DNS-01 cleanup hook for ${
|
|
327
|
+
# DNS-01 cleanup hook for ${K}
|
|
328
328
|
# This removes the TXT record after validation
|
|
329
329
|
|
|
330
330
|
DOMAIN="$CERTBOT_DOMAIN"
|
|
331
331
|
VALIDATION="$CERTBOT_VALIDATION"
|
|
332
332
|
RECORD_NAME="_acme-challenge.$DOMAIN"
|
|
333
333
|
|
|
334
|
-
${ug(
|
|
334
|
+
${ug(W)}
|
|
335
335
|
CLEANUPHOOK
|
|
336
336
|
|
|
337
337
|
chmod +x /etc/letsencrypt/hooks/cleanup-hook.sh
|
|
@@ -345,8 +345,8 @@ certbot certonly \\
|
|
|
345
345
|
--non-interactive \\
|
|
346
346
|
--agree-tos \\
|
|
347
347
|
--email ${g} \\
|
|
348
|
-
${
|
|
349
|
-
${
|
|
348
|
+
${T} \\
|
|
349
|
+
${A}
|
|
350
350
|
|
|
351
351
|
# Check if certificate was obtained
|
|
352
352
|
if [ -f "${h}/${P}/fullchain.pem" ]; then
|
|
@@ -362,7 +362,7 @@ else
|
|
|
362
362
|
echo "Failed to obtain certificate!"
|
|
363
363
|
fi
|
|
364
364
|
|
|
365
|
-
${
|
|
365
|
+
${S?Kc(P):"# Auto-renewal disabled"}
|
|
366
366
|
`}function cg(u){switch(u.provider){case"porkbun":return`
|
|
367
367
|
# Porkbun API credentials
|
|
368
368
|
export PORKBUN_API_KEY="${u.apiKey}"
|
|
@@ -470,7 +470,7 @@ fi
|
|
|
470
470
|
|
|
471
471
|
curl -s -X DELETE "$API_URL/v1/domains/$ROOT_DOMAIN/records/TXT/$RECORD_NAME" \\
|
|
472
472
|
-H "Authorization: sso-key $GODADDY_API_KEY:$GODADDY_API_SECRET"
|
|
473
|
-
`;default:return'echo "Unsupported DNS provider"'}}function
|
|
473
|
+
`;default:return'echo "Unsupported DNS provider"'}}function Kc(u){return`
|
|
474
474
|
# ==========================================
|
|
475
475
|
# Certificate Auto-Renewal Setup
|
|
476
476
|
# ==========================================
|
|
@@ -515,11 +515,11 @@ systemctl enable certbot-renewal.timer
|
|
|
515
515
|
systemctl start certbot-renewal.timer
|
|
516
516
|
|
|
517
517
|
echo "Certificate auto-renewal configured"
|
|
518
|
-
`}function
|
|
518
|
+
`}function ER(u){let{httpPort:g=80,httpsPort:h=443,certPath:S="/etc/ssl/stacks",redirectHttp:A=!0}=u;return`
|
|
519
519
|
// HTTPS Server with Let's Encrypt certificates
|
|
520
520
|
import { existsSync } from 'node:fs'
|
|
521
521
|
|
|
522
|
-
const CERT_PATH = '${
|
|
522
|
+
const CERT_PATH = '${S}'
|
|
523
523
|
const HTTP_PORT = ${g}
|
|
524
524
|
const HTTPS_PORT = ${h}
|
|
525
525
|
|
|
@@ -541,7 +541,7 @@ if (hasCerts) {
|
|
|
541
541
|
})
|
|
542
542
|
console.log(\`HTTPS server running on port \${HTTPS_PORT}\`)
|
|
543
543
|
|
|
544
|
-
${
|
|
544
|
+
${A?`
|
|
545
545
|
// Start HTTP server for redirect
|
|
546
546
|
const httpServer = Bun.serve({
|
|
547
547
|
port: HTTP_PORT,
|
|
@@ -601,5 +601,5 @@ async function handleRequest(request: Request): Promise<Response> {
|
|
|
601
601
|
// Implement your request handling here
|
|
602
602
|
return new Response('Hello from Stacks!')
|
|
603
603
|
}
|
|
604
|
-
`}async function dg(u,g,h){let
|
|
605
|
-
`).filter((v)=>!v.startsWith("-----")).join(""),g=Buffer.from(u,"base64"),h=g.length-65,y=g.subarray(h+1,h+33),R=g.subarray(h+33,h+65);return{kty:"EC",crv:"P-256",x:this.base64UrlEncode(y),y:this.base64UrlEncode(R)}}getJwkThumbprint(){let u=this.getJwk(),g=JSON.stringify({crv:u.crv,kty:u.kty,x:u.x,y:u.y}),h=Cd("sha256").update(g).digest();return this.base64UrlEncode(h)}base64UrlEncode(u){return(typeof u==="string"?Buffer.from(u):u).toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async signPayload(u,g){let h=await this.getNonce(),y=this.getJwk(),R={alg:"ES256",nonce:h,url:u};if(this.accountUrl)R.kid=this.accountUrl;else R.jwk=y;let v=this.base64UrlEncode(JSON.stringify(R)),P=g===""?"":this.base64UrlEncode(JSON.stringify(g)),M=`${v}.${P}`,V=fg("SHA256");V.update(M);let Ae=V.sign(this.accountKey),ye=Ae.subarray(4,4+Ae[3]),At=4+Ae[3]+2,Tt=Ae.subarray(At,At+Ae[At-1]),Je=Buffer.alloc(32),Ue=Buffer.alloc(32);ye.copy(Je,32-ye.length),Tt.copy(Ue,32-Tt.length);let je=Buffer.concat([Je,Ue]);return JSON.stringify({protected:v,payload:P,signature:this.base64UrlEncode(je)})}async acmeRequest(u,g){let h=await this.signPayload(u,g),y=await fetch(u,{method:"POST",headers:{"Content-Type":"application/jose+json"},body:h}),R=y.headers.get("replay-nonce");if(R)this.nonce=R;let v,P=y.headers.get("content-type");if(P?.includes("application/json")||P?.includes("application/problem+json"))v=await y.json();else v=await y.text();if(!y.ok)throw Error(`ACME request failed: ${JSON.stringify(v)}`);return{body:v,headers:y.headers}}async registerAccount(){let u=await this.getDirectory(),{body:g,headers:h}=await this.acmeRequest(u.newAccount,{termsOfServiceAgreed:!0,contact:[`mailto:${this.email}`]}),y=h.get("location");if(!y)throw Error("No account URL returned");return this.accountUrl=y,y}async createOrder(u){if(!this.accountUrl)await this.registerAccount();let g=await this.getDirectory(),{body:h,headers:y}=await this.acmeRequest(g.newOrder,{identifiers:u.map((v)=>({type:"dns",value:v}))}),R=y.get("location");if(!R)throw Error("No order URL returned");return{orderUrl:R,authorizations:h.authorizations,finalize:h.finalize}}async getAuthorization(u){let{body:g}=await this.acmeRequest(u,""),h=g.identifier.value,y=this.getJwkThumbprint(),R=g.challenges.filter((v)=>v.type==="http-01"||v.type==="dns-01").map((v)=>{let P=`${v.token}.${y}`;if(v.type==="http-01")return{type:"http-01",token:v.token,keyAuthorization:P,identifier:`/.well-known/acme-challenge/${v.token}`};else{let M=this.base64UrlEncode(Cd("sha256").update(P).digest());return{type:"dns-01",token:v.token,keyAuthorization:P,identifier:`_acme-challenge.${h}`,dnsValue:M}}});return{domain:h,challenges:R}}async respondToChallenge(u){await this.acmeRequest(u,{})}async waitForAuthorization(u,g=30){for(let h=0;h<g;h++){let{body:y}=await this.acmeRequest(u,"");if(y.status==="valid")return;if(y.status==="invalid")throw Error(`Authorization failed: ${JSON.stringify(y)}`);await new Promise((R)=>setTimeout(R,2000))}throw Error("Authorization timed out")}generateCsr(u){let{privateKey:g}=Sd("rsa",{modulusLength:2048}),h=g.export({type:"pkcs8",format:"pem"});return{csr:this.createSimpleCsr(u,g),privateKey:h}}createSimpleCsr(u,g){throw Error("CSR generation requires additional implementation. Consider using the shell-based certbot approach instead.")}async finalizeOrder(u,g){let{csr:h,privateKey:y}=this.generateCsr(g),{body:R}=await this.acmeRequest(u,{csr:this.base64UrlEncode(Buffer.from(h,"base64"))}),v=R.certificate;if(!v)throw Error("Certificate not immediately available - polling not implemented");let M=await(await fetch(v)).text(),V=M.split(/(?=-----BEGIN CERTIFICATE-----)/g),Ae=V[0],ye=V.slice(1).join("");return{certificate:Ae,privateKey:y,chain:ye,fullchain:M,expiresAt:new Date(Date.now()+7776000000)}}getAccountKey(){return this.accountKey}}var Ur=(u)=>u===void 0?void 0:u?"on":"off";function Jc(u){let g={ssl:u.ssl,always_use_https:Ur(u.alwaysUseHttps),automatic_https_rewrites:Ur(u.automaticHttpsRewrites),min_tls_version:u.minTlsVersion,tls_1_3:Ur(u.tls13),brotli:Ur(u.brotli),http3:Ur(u.http3),"0rtt":Ur(u.zeroRtt),early_hints:Ur(u.earlyHints),browser_cache_ttl:u.browserCacheTtl,always_online:Ur(u.alwaysOnline),websockets:Ur(u.websockets),email_obfuscation:Ur(u.emailObfuscation)};if(u.hsts)g.security_header={strict_transport_security:{enabled:u.hsts.enabled,max_age:u.hsts.maxAge??31536000,include_subdomains:u.hsts.includeSubdomains??!1,preload:u.hsts.preload??!1,nosniff:u.hsts.noSniff??!0}};for(let h of Object.keys(g))if(g[h]===void 0)delete g[h];return{...g,...u.raw??{}}}var Ei={ssl:"strict",alwaysUseHttps:!0,automaticHttpsRewrites:!0,minTlsVersion:"1.2",tls13:!0,brotli:!0,http3:!0,earlyHints:!0,alwaysOnline:!0,emailObfuscation:!1,browserCacheTtl:0};import{connect as bg}from"node:tls";async function Xc(u){let{address:g,serverName:h,port:y=443,timeoutMs:R=5000}=u;return new Promise((v)=>{let P=!1,M=(Ae)=>{if(P)return;P=!0;try{V.destroy()}catch{}v(Ae)},V=bg({host:g,port:y,servername:h,rejectUnauthorized:!1,timeout:R});V.once("secureConnect",()=>{if(V.authorized){M({reachable:!0,trusted:!0});return}M({reachable:!0,trusted:!1,reason:V.authorizationError?String(V.authorizationError):"origin certificate is not trusted for this hostname"})}),V.once("timeout",()=>{M({reachable:!1,trusted:!1,reason:`no TLS handshake within ${R}ms`})}),V.once("error",(Ae)=>{M({reachable:!1,trusted:!1,reason:Ae.message})})})}function kg(u,g){let h=g.replace(/\.$/,"").toLowerCase();return u.some((y)=>{let R=y.replace(/\.$/,"").toLowerCase();if(R===h)return!0;if(!R.startsWith("*."))return!1;let v=R.slice(2);if(!h.endsWith(`.${v}`))return!1;return!h.slice(0,h.length-v.length-1).includes(".")})}async function Og(u){let{provider:g,zone:h,hosts:y,ipv4:R,ipv6:v,proxied:P=!0,settings:M=Ei,cache:V,originGuard:Ae,purge:ye=!0,ttl:At=300,skipOriginProbe:Tt=!1,skipEdgeCertificateCheck:Je=!1}=u,Ue={records:[],settingsChanged:[],cacheRules:0,originGuard:!1,purged:!1,deferredProxy:[],warnings:[]};if(y.length===0)return Ue;let je=[],K=P&&!Je?await g.listEdgeCertificateHosts(h):null;for(let z of y){let Xe=P;if(Xe&&K&&!kg(K,z)){Xe=!1;let vt=K.length===0?"the zone has no active edge certificate yet":`no active edge certificate covers it (${K.join(", ")})`;Ue.deferredProxy.push({host:z,reason:vt}),Ue.warnings.push(`${z}: publishing DNS-only — ${vt}. Proxying it would make Cloudflare terminate TLS with no certificate for the name, which fails the handshake outright rather than degrading. Universal SSL covers the apex and ONE level of subdomain, so a deeper name needs its own certificate; otherwise re-run the deploy once the certificate is active.`)}if(Xe&&!Tt){let vt=await Xc({address:R,serverName:z});if(!vt.trusted){Xe=!1;let Et=vt.reason||"origin is not serving a trusted certificate";Ue.deferredProxy.push({host:z,reason:Et}),Ue.warnings.push(`${z}: publishing DNS-only for now — the origin does not yet serve a certificate this proxy can verify (${Et}). The origin issues certificates over HTTP-01, which needs the name to reach it directly; re-run the deploy once it has one and the record will be proxied.`)}}if(Xe)je.push(z);let bt=[{type:"A",content:R}];if(v&&ei(z))bt.push({type:"AAAA",content:v});for(let{type:vt,content:Et}of bt){let Ht=await g.upsertRecord(h,{name:z,type:vt,content:Et,ttl:At,proxied:Xe});if(Ht.success)Ue.records.push({host:z,type:vt,content:Et,proxied:Xe});else Ue.warnings.push(`${z} ${vt} → ${Et} failed: ${Ht.message||"unknown provider error"}`)}}if(je.length===0){if(P)Ue.warnings.push("No host is proxied yet, so zone settings, cache rules and purge were skipped — they only take effect through the proxy.");return Ue}try{let z=Jc(M),Xe=await g.applyZoneSettings(h,z);Ue.settingsChanged=Xe.changed;for(let bt of Xe.failed)Ue.warnings.push(`zone setting '${bt.id}' could not be applied: ${bt.error}`)}catch(z){Ue.warnings.push(`zone settings could not be read: ${z instanceof Error?z.message:String(z)}`)}let St=Fc(je,V);if(St.length>0){let z=await g.putManagedPhaseRules(h,"http_request_cache_settings",St);if(z.success)Ue.cacheRules=St.length;else Ue.warnings.push(`cache rules could not be written: ${z.message}`)}if(Ae){let z=Uc(je,Ae.header,Ae.value);if(z.length>0){let Xe=await g.putManagedPhaseRules(h,"http_request_late_transform",z);if(Xe.success)Ue.originGuard=!0;else Ue.warnings.push(`origin guard header could not be written: ${Xe.message}. `+"The gateway will reject requests that lack it — disable the guard or grant the token Transform Rules: Edit.")}}if(ye){if(Ue.purged=await g.purgeCache(h,{hosts:je}),!Ue.purged)Ue.warnings.push("edge cache purge failed — new content may take until TTL expiry to appear.")}return Ue}var Ng=(u)=>process.env[u];function Id(u,g){let h=u.replace(/\.$/,"").toLowerCase();if(g){let y=g.replace(/\.$/,"").toLowerCase();if(h===y||h.endsWith(`.${y}`))return y}return h.split(".").slice(-2).join(".")}function xg(u,g={}){let h=g.env??Ng,y=u.infrastructure?.compute?.proxy?.cdn,R=[];if(!y||y.provider!=="cloudflare")return{plan:null,errors:R};if(y.originDomain)R.push(`compute.proxy.cdn.originDomain (${y.originDomain}) is ignored for Cloudflare: the proxy forwards to the address in the record it fronts, `+"so no separate origin hostname is needed — and publishing one gives clients a documented way around the edge.");let v=h("CLOUDFLARE_API_TOKEN");if(!v)return R.push("CLOUDFLARE_API_TOKEN is not set — the Cloudflare CDN cannot be reconciled."),{plan:null,errors:R};let P=u.sites??{},M=y.frontedHosts?.length?[...new Set(y.frontedHosts.map((Tt)=>Tt.toLowerCase()))]:bo(P,{autoWww:u.infrastructure?.compute?.proxy?.autoWww!==!1});if(M.length===0)return R.push("No hostnames to front — set compute.proxy.cdn.frontedHosts, or give a site a domain."),{plan:null,errors:R};let V=u.infrastructure?.dns?.domain,Ae=new Set(M.map((Tt)=>Id(Tt,V)));if(Ae.size>1)return R.push(`Fronted hosts span more than one zone (${[...Ae].join(", ")}). A Cloudflare zone is configured as a unit, so run one CDN config per zone.`),{plan:null,errors:R};let ye=y.cloudflare??{},At=y.secret?{header:y.secretHeader??"X-Origin-Verify",value:y.secret}:void 0;return{plan:{zone:[...Ae][0],hosts:M,apiToken:v,zoneId:ye.zoneId??h("CLOUDFLARE_ZONE_ID"),accountId:ye.accountId??h("CLOUDFLARE_ACCOUNT_ID"),proxied:ye.proxied!==!1,settings:ye.settings??Ei,cache:ye.cache,originGuard:At,purge:ye.purgeOnDeploy!==!1,skipOriginProbe:ye.skipOriginProbe===!0},errors:R}}var Lg=26214400;var _g=new Set(["/_headers","/_redirects","/_routes.json","/_worker.js"]);class ep{name="cloudflare-pages";apiToken;accountId;constructor(u){if(!u.apiToken)throw Error("Cloudflare Pages needs an API token");if(!u.accountId)throw Error("Cloudflare Pages needs an account id");this.apiToken=u.apiToken,this.accountId=u.accountId}async request(u,g,h){return Ds(u,`/accounts/${this.accountId}${g}`,`Bearer ${this.apiToken}`,h)}async getProject(u){try{return await this.request("GET",`/pages/projects/${u}`)}catch(g){if(g instanceof Error&&/\b8000007\b|not found/i.test(g.message))return null;throw g}}async ensureProject(u,g="main"){let h=await this.getProject(u);if(h)return h;return this.request("POST","/pages/projects",{name:u,production_branch:g})}async uploadToken(u){return(await this.request("GET",`/pages/projects/${u}/upload-token`)).jwt}async missingHashes(u,g){let h=await Ds("POST","/pages/assets/check-missing",`Bearer ${u}`,{hashes:g});return new Set(h)}async upsertHashes(u,g){await Ds("POST","/pages/assets/upsert-hashes",`Bearer ${u}`,{hashes:g})}async uploadBatch(u,g){let h=g.map((y)=>({key:y.hash,value:y.base64,metadata:{contentType:zg(y.extension)},base64:!0}));await Ds("POST","/pages/assets/upload",`Bearer ${u}`,h)}async createDeployment(u,g,h){let y={};for(let v of g)y[v.path]=v.hash;let R=new FormData;if(R.append("manifest",JSON.stringify(y)),h)R.append("branch",h);return this.request("POST",`/pages/projects/${u}/deployments`,R)}async deployDirectory(u){let g=await rp(u.directory);if(g.length===0)throw Error(`No files to deploy in ${u.directory}`);let h=await this.uploadToken(u.project),y=await this.missingHashes(h,g.map((V)=>V.hash)),R=g.filter((V)=>y.has(V.hash)),v=[];for(let V=0;V<R.length;V+=100)v.push(R.slice(V,V+100));let P=0;for(let V=0;V<v.length;V+=4){let Ae=v.slice(V,V+4);await Promise.all(Ae.map((ye)=>this.uploadBatch(h,ye))),P+=Ae.reduce((ye,At)=>ye+At.length,0),u.onProgress?.(P,R.length)}return await this.upsertHashes(h,g.map((V)=>V.hash)),{deployment:await this.createDeployment(u.project,g,u.branch),total:g.length,uploaded:R.length,uploadedBytes:R.reduce((V,Ae)=>V+Ae.size,0)}}async listCustomDomains(u){return(await this.request("GET",`/pages/projects/${u}/domains`)).map((h)=>h.name)}async attachCustomDomain(u,g){if((await this.listCustomDomains(u)).includes(g))return!1;return await this.request("POST",`/pages/projects/${u}/domains`,{name:g}),!0}}function Wg(u,g){return{type:"CNAME",name:u,value:g.endsWith(".pages.dev")?g:`${g}.pages.dev`,ttl:1,proxied:!0}}async function tp(u,g){let{blake3:h}=await import("./chunk-q7yf28cc.js").then((m)=>LS(m.default,1));return(await h(u+g)).slice(0,32)}async function rp(u){let{readdir:g}=await import("node:fs/promises"),{join:h,relative:y,sep:R}=await import("node:path"),v=[];async function P(M){let V=await g(M,{withFileTypes:!0});for(let Ae of V){let ye=h(M,Ae.name);if(Ae.isDirectory()){await P(ye);continue}if(!Ae.isFile())continue;let At=`/${y(u,ye).split(R).join("/")}`;if(_g.has(At))continue;let Tt=await Bun.file(ye).bytes();if(Tt.byteLength>26214400)throw Error(`${At} is ${Tt.byteLength} bytes, over the Cloudflare Pages limit of 26214400`);let Je=Buffer.from(Tt).toString("base64"),Ue=Ae.name.lastIndexOf("."),je=Ue>0?Ae.name.slice(Ue+1):"";v.push({path:At,hash:await tp(Je,je),base64:Je,extension:je,size:Tt.byteLength})}}return await P(u),v.sort((M,V)=>M.path.localeCompare(V.path)),v}function zg(u){return{html:"text/html; charset=utf-8",css:"text/css; charset=utf-8",js:"application/javascript; charset=utf-8",mjs:"application/javascript; charset=utf-8",json:"application/json; charset=utf-8",map:"application/json; charset=utf-8",xml:"application/xml; charset=utf-8",txt:"text/plain; charset=utf-8",svg:"image/svg+xml",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",avif:"image/avif",ico:"image/x-icon",woff:"font/woff",woff2:"font/woff2",ttf:"font/ttf",otf:"font/otf",pdf:"application/pdf",webmanifest:"application/manifest+json"}[u.toLowerCase()]||"application/octet-stream"}async function Ds(u,g,h,y){let R={Authorization:h},v;if(y instanceof FormData)v=y;else if(y!==void 0)R["Content-Type"]="application/json",v=JSON.stringify(y);let P=await fetch(`https://api.cloudflare.com/client/v4${g}`,{method:u,headers:R,body:v}),M=await P.text(),V;try{V=JSON.parse(M)}catch{throw Error(`Cloudflare Pages API returned ${P.status} with a non-JSON body: ${M.slice(0,200)}`)}if(!V.success){let Ae=(V.errors??[]).map((ye)=>`${ye.code}: ${ye.message}`).join(", ");throw Error(`Cloudflare Pages API error (${P.status}): ${Ae||M.slice(0,200)}`)}return V.result}import{createHash as jg}from"node:crypto";var ip="https://api.cloudflare.com/client/v4",op=10042,Kg=10006,qg=10059;class _n extends Error{status;errors;constructor(u,g,h=[]){super(u);this.name="R2ApiError",this.status=g,this.errors=h}hasCode(u){return this.errors.some((g)=>g.code===u)}}class Sn extends _n{accountId;constructor(u,g,h=[]){super(`R2 is not enabled on Cloudflare account ${u}. Enable it once in the Cloudflare dashboard (R2 Object Storage, then follow the prompt to activate it), then re-run the deploy.`,g,h);this.name="R2NotEnabledError",this.accountId=u}}class ui{name="cloudflare-r2";apiToken;accountId;fetchImpl;constructor(u){if(!u.apiToken)throw Error("Cloudflare R2 needs an API token");if(!u.accountId)throw Error("Cloudflare R2 needs an account id");this.apiToken=u.apiToken,this.accountId=u.accountId,this.fetchImpl=u.fetch??((g,h)=>fetch(g,h))}async requestEnvelope(u,g,h,y={}){let R={Authorization:`Bearer ${this.apiToken}`};if(h!==void 0)R["Content-Type"]="application/json";if(y.jurisdiction&&y.jurisdiction!=="default")R["cf-r2-jurisdiction"]=y.jurisdiction;let v=await this.fetchImpl(`${ip}/accounts/${this.accountId}/r2${g}`,{method:u,headers:R,body:h===void 0?void 0:JSON.stringify(h)}),P=await v.text(),M;try{M=JSON.parse(P)}catch{throw new _n(`Cloudflare R2 API returned ${v.status} with a non-JSON body: ${P.slice(0,200)}`,v.status)}if(!M.success){let V=M.errors??[];if(V.some((ye)=>ye.code===op))throw new Sn(this.accountId,v.status,V);let Ae=V.map((ye)=>`${ye.code}: ${ye.message}`).join(", ");throw new _n(`Cloudflare R2 API error (${v.status}) on ${u} ${g}: ${Ae||P.slice(0,200)}`,v.status,V)}return M}async request(u,g,h,y){return(await this.requestEnvelope(u,g,h,y)).result}bucketPath(u){return`/buckets/${encodeURIComponent(u)}`}async listBuckets(u={}){let g=[],h;do{let y=new URLSearchParams({per_page:"1000"});if(h)y.set("cursor",h);let R=await this.requestEnvelope("GET",`/buckets?${y}`,void 0,u),v=R.result?.buckets??[];g.push(...v),h=v.length>0?R.result_info?.cursor||void 0:void 0}while(h);return g}async getBucket(u,g={}){try{return await this.request("GET",this.bucketPath(u),void 0,g)}catch(h){if(h instanceof Sn)throw h;if(h instanceof _n&&(h.hasCode(Kg)||h.status===404))return null;throw h}}async ensureBucket(u,g={}){let h={jurisdiction:g.jurisdiction},y=await this.getBucket(u,h);if(y)return{bucket:y,created:!1};let R={name:u};if(g.locationHint)R.locationHint=g.locationHint;let v=await this.request("POST","/buckets",R,h);return{bucket:{...v,name:v?.name||u},created:!0}}async getCors(u,g={}){try{return(await this.request("GET",`${this.bucketPath(u)}/cors`,void 0,g))?.rules??[]}catch(h){if(h instanceof _n&&!(h instanceof Sn)&&(h.hasCode(qg)||h.status===404))return[];throw h}}async putCors(u,g,h={}){if(g.length===0){await this.request("DELETE",`${this.bucketPath(u)}/cors`,void 0,h);return}await this.request("PUT",`${this.bucketPath(u)}/cors`,{rules:g},h)}async ensureCors(u,g,h={}){let y=await this.getCors(u,h);if(ks(y,g))return!1;return await this.putCors(u,g,h),!0}async getLifecycle(u,g={}){return(await this.request("GET",`${this.bucketPath(u)}/lifecycle`,void 0,g))?.rules??[]}async putLifecycle(u,g,h={}){await this.request("PUT",`${this.bucketPath(u)}/lifecycle`,{rules:g},h)}async ensureLifecycle(u,g,h={}){let y=await this.getLifecycle(u,h);if(Fs(y,g))return!1;return await this.putLifecycle(u,g,h),!0}async listCustomDomains(u,g={}){return(await this.request("GET",`${this.bucketPath(u)}/domains/custom`,void 0,g))?.domains??[]}async attachCustomDomain(u,g,h={}){return this.request("POST",`${this.bucketPath(u)}/domains/custom`,{domain:g.domain,zoneId:g.zoneId,enabled:g.enabled??!0,minTLS:g.minTLS??"1.2"},h)}async updateCustomDomain(u,g,h,y={}){return this.request("PUT",`${this.bucketPath(u)}/domains/custom/${encodeURIComponent(g)}`,h,y)}async ensureCustomDomain(u,g,h={},y){let R=g.minTLS??"1.2",P=(y??await this.listCustomDomains(u,h)).find((Ae)=>Ae.domain.toLowerCase()===g.domain.toLowerCase());if(!P){let Ae=await this.attachCustomDomain(u,{...g,minTLS:R,enabled:!0},h);return{action:"attached",domain:{...Ae,domain:Ae?.domain||g.domain}}}let M=P.minTLS??"1.0";if(P.enabled&&M===R)return{action:"unchanged",domain:P};let V=await this.updateCustomDomain(u,P.domain,{enabled:!0,minTLS:R},h);return{action:"updated",domain:{...P,...V}}}async getManagedDomain(u,g={}){return this.request("GET",`${this.bucketPath(u)}/domains/managed`,void 0,g)}async setManagedDomain(u,g,h={}){let y=await this.getManagedDomain(u,h);if(y?.enabled===g)return{changed:!1,domain:y};return{changed:!0,domain:await this.request("PUT",`${this.bucketPath(u)}/domains/managed`,{enabled:g},h)}}}function sp(u){return`https://${u}.r2.cloudflarestorage.com`}async function Zg(u){let{apiToken:g,accountId:h}=u;if(!g)throw Error("r2S3Credentials needs an API token");if(!h)throw Error("r2S3Credentials needs an account id");let y=u.fetch??((P,M)=>fetch(P,M)),R=[],v;for(let P of[`/accounts/${h}/tokens/verify`,"/user/tokens/verify"]){let M=await y(`${ip}${P}`,{method:"GET",headers:{Authorization:`Bearer ${g}`}}),V=await M.json().catch(()=>null);if(M.ok&&V?.success&&V.result?.id){v=V.result.id;break}let Ae=V?.errors?.map((ye)=>`${ye.code}: ${ye.message}`).join(", ");R.push(`${P} -> ${M.status}${Ae?` (${Ae})`:""}`)}if(!v)throw Error(`Could not verify the Cloudflare API token to derive R2 credentials: ${R.join("; ")}`);return{accessKeyId:v,secretAccessKey:jg("sha256").update(g).digest("hex"),endpoint:sp(h),region:"auto"}}function xi(u){if(Array.isArray(u))return`[${u.map(xi).join(",")}]`;if(u&&typeof u==="object")return`{${Object.entries(u).filter(([,h])=>h!==void 0).sort(([h],[y])=>h.localeCompare(y)).map(([h,y])=>`${JSON.stringify(h)}:${xi(y)}`).join(",")}}`;return JSON.stringify(u)}function np(u,g){let h=(y)=>y&&y.length>0?[...y].map(String).sort():void 0;return{id:g?u.id:void 0,allowed:{origins:h(u.allowed?.origins),methods:h(u.allowed?.methods?.map((y)=>y.toUpperCase())),headers:h(u.allowed?.headers?.map((y)=>y.toLowerCase()))},exposeHeaders:h(u.exposeHeaders?.map((y)=>y.toLowerCase())),maxAgeSeconds:u.maxAgeSeconds}}function ks(u,g){if(u.length!==g.length)return!1;return g.every((h,y)=>{let R=h.id!==void 0;return xi(np(u[y],R))===xi(np(h,R))})}function Fs(u,g){if(u.length!==g.length)return!1;let h=(y)=>xi([...y].map((R)=>({...R,storageClassTransitions:R.storageClassTransitions?.length?R.storageClassTransitions:void 0})).sort((R,v)=>R.id.localeCompare(v.id)));return h(u)===h(g)}var Jg="r2 cache",Yg=/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;async function Xg(u,g={}){let h=g.log??(()=>{}),y={buckets:[],warnings:[]},R=u.infrastructure?.r2??u.r2,v=Object.entries(R?.buckets??{});if(v.length===0)return h("R2: no buckets declared, skipping."),y;if(!g.apiToken||!g.accountId){let ye=[!g.apiToken&&"CLOUDFLARE_API_TOKEN",!g.accountId&&"CLOUDFLARE_ACCOUNT_ID"].filter(Boolean).join(" and "),At=`R2: ${v.length} bucket(s) declared but ${ye} is not set, skipping.`;return h(At),y.warnings.push(At),y}let P=g.dryRun===!0,M=new ui({apiToken:g.apiToken,accountId:g.accountId,fetch:g.fetch}),V=new G(g.apiToken,{zoneId:g.zoneId,accountId:g.accountId}),Ae=new Map;for(let[ye,At]of v){let Tt=At?.name;if(!Tt||!Yg.test(Tt)){y.warnings.push(`R2: bucket '${ye}' has an invalid name '${Tt??""}' (3-63 chars: lowercase letters, digits, hyphens), skipping.`);continue}h(`R2: ${P?"checking":"reconciling"} bucket ${Tt}...`);let Je={name:Tt,changes:[],domains:[]};if(y.buckets.push(Je),await Rh(At,{r2:M,cloudflare:V,dryRun:P,summary:Je,warnings:y.warnings,claimed:Ae,log:h}),Je.changes.length===0)h(`R2: ${Tt} is in sync.`);else for(let Ue of Je.changes)h(`R2: ${Tt}: ${Ue}`)}return y}async function Rh(u,g){let{r2:h,dryRun:y,summary:R,warnings:v}=g,P=u.name,M={jurisdiction:u.jurisdiction},V=(Je,Ue)=>{let je=y?Ue&&`would ${Ue}`:Je;if(je)R.changes.push(je)},Ae;if(y)Ae=await h.getBucket(P,M)!==null;else{let{created:Je}=await h.ensureBucket(P,{locationHint:u.locationHint,jurisdiction:u.jurisdiction});if(Ae=!0,Je)V("created","")}if(!Ae)V("","create bucket");let ye=$h(u.customDomains);await so(g,`cors on ${P}`,async()=>{if(!u.cors)return;if(!Ae){V("cors updated","set cors");return}let Je=await h.getCors(P,M);if(ks(Je,u.cors))return;if(!y)await h.putCors(P,u.cors,M);V(u.cors.length===0?"cors removed":"cors updated",u.cors.length===0?"remove cors":"update cors")}),await so(g,`lifecycle on ${P}`,async()=>{if(!u.lifecycle)return;let Je=u.lifecycle.map(lp);if(Ae){let Ue=await h.getLifecycle(P,M);if(Fs(Ue,Je))return}if(!y)await h.putLifecycle(P,Je,M);V("lifecycle updated","update lifecycle")}),await so(g,`r2.dev URL on ${P}`,async()=>{let Je=u.publicDevUrl===!0;if(!Ae){if(Je)V("r2.dev URL enabled","enable r2.dev URL");return}if((await h.getManagedDomain(P,M))?.enabled===Je)return;if(!y)await h.setManagedDomain(P,Je,M);V(Je?"r2.dev URL enabled":"r2.dev URL disabled",Je?"enable r2.dev URL":"disable r2.dev URL")});let At=[];if(ye.length>0&&Ae)try{At=await h.listCustomDomains(P,M)}catch(Je){if(Je instanceof Sn)throw Je;v.push(`R2: could not list custom domains on ${P}: ${td(Je)}`)}let Tt=new Map;for(let{domain:Je,minTLS:Ue}of ye){let je=g.claimed.get(Je);if(je&&je!==P){v.push(`R2: ${Je} is declared on both ${je} and ${P}; leaving it on ${je}.`);continue}g.claimed.set(Je,P);let K=At.find((z)=>z.domain.toLowerCase()===Je),St=Ue??"1.2";if(K&&K.enabled&&(K.minTLS??"1.0")===St){if(R.domains.push({domain:Je,status:ed(K)}),K.zoneId)Tt.set(Je,K.zoneId);continue}await so(g,`custom domain ${Je} on ${P}`,async()=>{if(K){if(!y)await h.updateCustomDomain(P,K.domain,{enabled:!0,minTLS:St},M);if(V(`custom domain ${Je} updated`,`update custom domain ${Je}`),R.domains.push({domain:Je,status:ed({...K,enabled:!0})}),K.zoneId)Tt.set(Je,K.zoneId);return}let z=await g.cloudflare.zoneIdFor(Je);if(Tt.set(Je,z),!y)await h.ensureCustomDomain(P,{domain:Je,zoneId:z,minTLS:St},M,At);V(`custom domain ${Je} attached`,`attach custom domain ${Je}`),R.domains.push({domain:Je,status:y?"not attached":"pending"})},()=>R.domains.push({domain:Je,status:"error"}))}if(u.cache&&ye.length>0)await Ph(u,ye.map((Je)=>Je.domain).filter((Je)=>g.claimed.get(Je)===P),Tt,g,V)}async function Ph(u,g,h,y,R){let v=ap(u.cache);if(!v||g.length===0)return;let P=new Map;for(let M of g){let V=h.get(M);if(!V)try{V=await y.cloudflare.zoneIdFor(M)}catch(Ae){y.warnings.push(`R2: could not resolve the zone for ${M}, cache rule skipped: ${td(Ae)}`);continue}P.set(V,[...P.get(V)??[],M])}for(let M of P.values()){let V={action:"set_cache_settings",description:`${Wt} ${Jg}`,expression:`(${Kt(M)})`,enabled:!0,action_parameters:v};await so(y,`cache rule for ${M.join(", ")}`,async()=>{let Ae=await y.cloudflare.getPhaseRules(M[0],"http_request_cache_settings");if(cp(Ae,V))return;if(!y.dryRun){let ye=await y.cloudflare.putManagedPhaseRules(M[0],"http_request_cache_settings",[V]);if(!ye.success)throw Error(ye.message||"cache rule write failed")}R(`cache rule updated for ${M.join(", ")}`,`update cache rule for ${M.join(", ")}`)})}}function ap(u){if(u.edgeTtl===void 0&&u.browserTtl===void 0)return null;let g={cache:!0};if(u.edgeTtl!==void 0)g.edge_ttl={mode:"override_origin",default:u.edgeTtl};if(u.browserTtl!==void 0)g.browser_ttl={mode:"override_origin",default:u.browserTtl};return g}function cp(u,g){let h=Jt(g.expression),y=Jn(g.expression),R=(M)=>(M.description||"").startsWith(Wt),v=(M)=>M.action===g.action&&M.description===g.description&&(M.enabled??!0)===(g.enabled??!0)&&Jn(M.expression)===y&&Ms(M.action_parameters)===Ms(g.action_parameters),P=!1;for(let M of u){if(!R(M))continue;let V=Jt(M.expression);if(!h.some((ye)=>V.includes(ye)))continue;if(v(M)&&h.every((ye)=>V.includes(ye))&&!P){P=!0;continue}return!1}return P}function lp(u){let g={id:u.id,enabled:u.enabled??!0,conditions:{prefix:u.prefix??""}};if(u.expireAfterDays!==void 0)g.deleteObjectsTransition={condition:{type:"Age",maxAge:cu(u.expireAfterDays)}};else if(u.expireOn!==void 0)g.deleteObjectsTransition={condition:{type:"Date",date:u.expireOn}};if(u.abortMultipartUploadsAfterDays!==void 0)g.abortMultipartUploadsTransition={condition:{type:"Age",maxAge:cu(u.abortMultipartUploadsAfterDays)}};if(u.infrequentAccessAfterDays!==void 0)g.storageClassTransitions=[{storageClass:"InfrequentAccess",condition:{type:"Age",maxAge:cu(u.infrequentAccessAfterDays)}}];return g}function cu(u){return Math.round(u*86400)}function $h(u){let g=new Set,h=[];for(let y of u??[]){let R=typeof y==="string"?{domain:y}:y,v=R.domain?.trim().replace(/\.$/,"").toLowerCase();if(!v||g.has(v))continue;g.add(v),h.push({domain:v,minTLS:R.minTLS})}return h}function ed(u){if(!u.enabled)return"disabled";let g=u.status?.ownership,h=u.status?.ssl;if(g==="active"&&h==="active")return"active";for(let y of[g,h])if(y==="error"||y==="blocked"||y==="deactivated")return y;return"pending"}async function so(u,g,h,y){try{await h()}catch(R){if(R instanceof Sn)throw R;u.warnings.push(`R2: ${g} failed: ${td(R)}`),y?.()}}function td(u){return u instanceof Error?u.message:String(u)}function Ms(u){if(Array.isArray(u))return`[${u.map(Ms).join(",")}]`;if(u&&typeof u==="object")return`{${Object.entries(u).filter(([,h])=>h!==void 0).sort(([h],[y])=>h.localeCompare(y)).map(([h,y])=>`${JSON.stringify(h)}:${Ms(y)}`).join(",")}}`;return JSON.stringify(u)}import{basename as Bh,extname as Vh}from"node:path";import{createHash as Dh}from"node:crypto";var kh="https://api.cloudflare.com/client/v4",Fn="TS_CLOUD_CONTENT_HASH",co="2025-09-01",nd="application/javascript+module",Nh=10007,xh=[1e4,9109];class di extends Error{status;errors;constructor(u,g,h=[]){super(u);this.name="WorkersApiError",this.status=g,this.errors=h}hasCode(u){return this.errors.some((g)=>g.code===u)}}class Vs extends di{permission;constructor(u,g,h,y=[]){super(`The Cloudflare API token is missing the "${u}" permission (${g}). Edit the token in the Cloudflare dashboard (My Profile, API Tokens), add it, then re-run the deploy.`,h,y);this.name="WorkersPermissionError",this.permission=u}}class Gs{name="cloudflare-workers";apiToken;accountId;fetchImpl;constructor(u){if(!u.apiToken)throw Error("Cloudflare Workers needs an API token");if(!u.accountId)throw Error("Cloudflare Workers needs an account id");this.apiToken=u.apiToken,this.accountId=u.accountId,this.fetchImpl=u.fetch??((g,h)=>fetch(g,h))}async request(u,g,h){let y={Authorization:`Bearer ${this.apiToken}`},R;if(h instanceof FormData)R=h;else if(h!==void 0)y["Content-Type"]="application/json",R=JSON.stringify(h);let v=await this.fetchImpl(`${kh}/accounts/${this.accountId}/workers${g}`,{method:u,headers:y,body:R}),P=await v.text(),M;try{M=JSON.parse(P)}catch{throw new di(`Cloudflare Workers API returned ${v.status} with a non-JSON body: ${P.slice(0,200)}`,v.status)}if(!M.success){let V=M.errors??[],Ae=V.map((ye)=>`${ye.code}: ${ye.message}`).join(", ");if(Fh(v.status,V))throw new Vs(Mh(g),`${u} ${g} -> ${v.status}${Ae?`, ${Ae}`:""}`,v.status,V);throw new di(`Cloudflare Workers API error (${v.status}) on ${u} ${g}: ${Ae||P.slice(0,200)}`,v.status,V)}return M.result}scriptPath(u){return`/scripts/${encodeURIComponent(u)}`}async getSettings(u){try{return await this.request("GET",`${this.scriptPath(u)}/settings`)}catch(g){if(g instanceof Vs)throw g;if(g instanceof di&&(g.hasCode(Nh)||g.status===404))return null;throw g}}async getScript(u){return this.getSettings(u)}async uploadModuleScript(u,g){return this.request("PUT",this.scriptPath(u),dp(g))}async ensureModuleScript(u,g){let h=lo(g),y=await this.getSettings(u);if(y&&qs(y,g,h))return{action:"unchanged",hash:h};return await this.uploadModuleScript(u,pp(g,h)),{action:y?"updated":"created",hash:h}}async listCustomDomains(u={}){let g=new URLSearchParams;if(u.service)g.set("service",u.service);if(u.hostname)g.set("hostname",u.hostname);if(u.zoneId)g.set("zone_id",u.zoneId);let h=g.toString(),y=h?`?${h}`:"";return await this.request("GET",`/domains${y}`)??[]}async attachCustomDomain(u,g){let h={hostname:u,service:g.service,environment:g.environment??"production"};if(g.zoneId)h.zone_id=g.zoneId;return this.request("PUT","/domains",h)}async detachCustomDomain(u){await this.request("DELETE",`/domains/${encodeURIComponent(u)}`)}async ensureCustomDomain(u,g,h){let R=(h??await this.listCustomDomains({hostname:u})).find((P)=>P.hostname.toLowerCase()===u.toLowerCase());if(R&&R.service===g.service)return{action:"unchanged",domain:R};if(R)throw Error(`${u} is attached to the Worker '${R.service}'. Detach it there first (Workers & Pages, ${R.service}, Settings, Domains & Routes); ts-cloud does not move it.`);let v=await this.attachCustomDomain(u,g);return{action:"attached",domain:{...v,hostname:v?.hostname||u}}}}function Fh(u,g){if(g.some((h)=>xh.includes(h.code)))return!0;return(u===401||u===403)&&g.length===0}function Mh(u){if(u.startsWith("/domains"))return"Workers Routes: Edit (zone, for Workers custom domains) and Workers Scripts: Edit (account)";return"Workers Scripts: Edit (account)"}function dp(u){if(!u.modules.some((y)=>y.name===u.mainModule))throw Error(`Worker main module '${u.mainModule}' is not one of the uploaded modules`);let g={main_module:u.mainModule,bindings:u.bindings??[],compatibility_date:u.compatibilityDate??co,compatibility_flags:u.compatibilityFlags??[],keep_bindings:["secret_text"]},h=new FormData;h.append("metadata",JSON.stringify(g));for(let y of u.modules)h.append(y.name,new Blob([y.content],{type:y.type}),y.name);return h}function lo(u){let g={mainModule:u.mainModule,modules:[...u.modules].sort((h,y)=>h.name.localeCompare(y.name)).map((h)=>({name:h.name,type:h.type,sha256:up(h.content)})),bindings:[...u.bindings??[]].filter((h)=>h.name!==Fn).sort((h,y)=>h.name.localeCompare(y.name)),compatibilityDate:u.compatibilityDate??co,compatibilityFlags:[...u.compatibilityFlags??[]].sort()};return up(rd(g))}function pp(u,g=lo(u)){return{...u,bindings:[...(u.bindings??[]).filter((h)=>h.name!==Fn),{type:"plain_text",name:Fn,text:g}]}}function qs(u,g,h){let y=u.bindings??[],R=y.find((v)=>v.name===Fn&&v.type==="plain_text");if(!R||R.text!==h)return!1;return(g.bindings??[]).every((v)=>{let P=y.find((V)=>V.name===v.name);if(!P||P.type!==v.type)return!1;let M=v.type==="r2_bucket"?"bucket_name":v.type==="plain_text"?"text":void 0;return!M||P[M]===v[M]})}function up(u){return Dh("sha256").update(u).digest("hex")}function rd(u){if(Array.isArray(u))return`[${u.map(rd).join(",")}]`;if(u&&typeof u==="object")return`{${Object.entries(u).filter(([,h])=>h!==void 0).sort(([h],[y])=>h.localeCompare(y)).map(([h,y])=>`${JSON.stringify(h)}:${rd(y)}`).join(",")}}`;return JSON.stringify(u)}async function id(u){if(!await Bun.file(u).exists())throw Error(`Worker entry not found: ${u}`);let g;try{g=await Bun.build({entrypoints:[u],target:"browser",format:"esm",minify:!0,external:["cloudflare:*"]})}catch(R){throw Error(`Bundling Worker ${u} failed: ${zh(R)}`)}if(!g.success)throw Error(`Bundling Worker ${u} failed: ${g.logs.map((R)=>String(R.message??R)).join("; ")}`);if(g.outputs.length!==1)throw Error(`Bundling Worker ${u} produced ${g.outputs.length} files (${g.outputs.map((R)=>R.path).join(", ")}); a Worker is uploaded as a single module, so remove the dynamic imports or asset imports that split it.`);let h=g.outputs[0];if(h.kind!=="entry-point")throw Error(`Bundling Worker ${u} produced a ${h.kind} instead of an entry module`);let y=await h.text();return{name:`${Bh(u,Vh(u))}.js`,content:y,type:nd,size:new TextEncoder().encode(y).byteLength}}function zh(u){if(u instanceof AggregateError&&u.errors.length>0)return u.errors.map((g)=>g instanceof Error?g.message:String(g?.message??g)).join("; ");return u instanceof Error?u.message:String(u)}import{isAbsolute as Hh,resolve as Uh}from"node:path";var Gh="https://api.cloudflare.com/client/v4",jh=/^[a-z0-9][a-z0-9_-]{0,62}$/;async function Kh(u,g={}){let h=g.log??(()=>{}),y={workers:[],warnings:[]},R=Object.entries(u.infrastructure?.workers??u.workers??{});if(R.length===0)return h("Workers: none declared, skipping."),y;if(!g.apiToken||!g.accountId){let M=[!g.apiToken&&"CLOUDFLARE_API_TOKEN",!g.accountId&&"CLOUDFLARE_ACCOUNT_ID"].filter(Boolean).join(" and "),V=`Workers: ${R.length} Worker(s) declared but ${M} is not set, skipping.`;return h(V),y.warnings.push(V),y}let v=u.infrastructure?.r2??u.r2,P={workers:new Gs({apiToken:g.apiToken,accountId:g.accountId,fetch:g.fetch}),r2:new ui({apiToken:g.apiToken,accountId:g.accountId,fetch:g.fetch}),cloudflare:new G(g.apiToken,{zoneId:g.zoneId,accountId:g.accountId}),apiToken:g.apiToken,fetch:g.fetch??((M,V)=>fetch(M,V)),projectRoot:g.projectRoot??process.cwd(),dryRun:g.dryRun===!0,warnings:y.warnings,log:h,r2ConfigDomains:Qh(v),r2Buckets:v,claimed:new Map};for(let[M,V]of R){let Ae=V?.name;if(!Ae||!jh.test(Ae)){y.warnings.push(`Workers: '${M}' has an invalid name '${Ae??""}' (1-63 chars: lowercase letters, digits, '-' and '_'), skipping.`);continue}if(!V.entry){y.warnings.push(`Workers: ${Ae} has no entry, skipping.`);continue}h(`Workers: ${P.dryRun?"checking":"reconciling"} ${Ae}...`);let ye={name:Ae,changes:[],domains:[]};y.workers.push(ye),await qh(V,ye,P);for(let At of ye.changes)h(`Workers: ${Ae}: ${At}`)}return y}async function qh(u,g,h){let y=u.name,R=Hh(u.entry)?u.entry:Uh(h.projectRoot,u.entry),v;try{v=await id(R)}catch(M){throw Error(`Worker ${y}: ${gi(M)}`)}g.changes.push(`bundled ${zS(v.size)}`);let P={mainModule:v.name,modules:[{name:v.name,content:v.content,type:v.type}],bindings:mp(u,h),compatibilityDate:u.compatibilityDate??co,compatibilityFlags:u.compatibilityFlags??[]};if(h.dryRun){let M=await h.workers.getSettings(y);g.changes.push(M&&qs(M,P,lo(P))?"unchanged":"would upload")}else{let{action:M}=await h.workers.ensureModuleScript(y,P);g.changes.push(M==="unchanged"?"unchanged":"uploaded")}for(let M of $S(u.customDomains))await Zh(y,M,g,h)}async function Zh(u,g,h,y){let R=(Je)=>{h.domains.push({domain:g,status:Je})},v=(Je)=>{y.warnings.push(`Workers: ${Je}`),R("conflict")},P=y.claimed.get(g);if(P&&P!==u){v(`${g} is declared on both ${P} and ${u}; leaving it on ${P}.`);return}y.claimed.set(g,u);let M=y.r2ConfigDomains.get(g);if(M){v(`${g} is also declared as a custom domain of the R2 bucket '${M}' (r2.buckets). A hostname serves either a bucket or a Worker. Remove it from the bucket's customDomains and detach it in the dashboard (R2, ${M}, Settings, Custom Domains) first; to serve the bucket through the Worker, bind it with bindings.r2Buckets instead.`);return}let V=await Jh(y);if(V===null){R("error");return}let Ae=V.find((Je)=>Je.hostname.toLowerCase()===g);if(Ae?.service===u){R("active");return}if(Ae){v(`${g} is attached to the Worker '${Ae.service}'. Detach it there first (Workers & Pages, ${Ae.service}, Settings, Domains & Routes); it is not moved automatically.`);return}let ye=(await Yh(y))?.get(g);if(ye){v(`${g} is attached to the R2 bucket '${ye}' as a custom domain. Detach it first (R2, ${ye}, Settings, Custom Domains, then re-run the deploy); it is not detached automatically. To serve the bucket through the Worker, bind it with bindings.r2Buckets.`);return}let At;try{At=await y.cloudflare.zoneIdFor(g)}catch(Je){y.warnings.push(`Workers: could not resolve the zone for ${g}, not attached: ${gi(Je)}`),R("error");return}let Tt=await Xh(y,At,g);if(Tt.length>0){let Je=Tt.map((Ue)=>`${Ue.type} ${Ue.content}`).join(", ");v(`${g} already has a DNS record (${Je}). A Worker custom domain creates its own record, so Cloudflare will not attach over it. Delete the record first (DNS, Records), then re-run the deploy; it is not deleted automatically.`);return}if(y.dryRun){h.changes.push(`would attach custom domain ${g}`),R("not attached");return}try{await y.workers.ensureCustomDomain(g,{service:u,zoneId:At},V),h.changes.push(`custom domain ${g} attached`),V.push({id:"",hostname:g,service:u,zone_id:At}),R("pending")}catch(Je){y.warnings.push(`Workers: custom domain ${g} on ${u} failed: ${gi(Je)}`),R("error")}}async function Jh(u){if(u.attached!==void 0)return u.attached;try{u.attached=await u.workers.listCustomDomains()}catch(g){u.warnings.push(`Workers: could not list Worker custom domains, none attached: ${gi(g)}`),u.attached=null}return u.attached}async function Yh(u){if(u.r2AccountDomains!==void 0)return u.r2AccountDomains;let g=new Map;try{let h=(await u.r2.listBuckets()).map((y)=>({name:y.name}));for(let y of Object.values(u.r2Buckets?.buckets??{}))if(y.jurisdiction&&y.jurisdiction!=="default")h.push({name:y.name,jurisdiction:y.jurisdiction});for(let y of h){let R=await u.r2.listCustomDomains(y.name,{jurisdiction:y.jurisdiction}).catch(()=>[]);for(let v of R)g.set(v.domain.toLowerCase(),y.name)}u.r2AccountDomains=g}catch(h){u.log(`Workers: could not check R2 custom domains (${gi(h)}); relying on the DNS check.`),u.r2AccountDomains=null}return u.r2AccountDomains}async function Xh(u,g,h){try{let y=await u.fetch(`${Gh}/zones/${g}/dns_records?name=${encodeURIComponent(h)}&per_page=100`,{method:"GET",headers:{Authorization:`Bearer ${u.apiToken}`}}),R=await y.json();if(!R?.success)throw Error(`HTTP ${y.status}`);return(R.result??[]).filter((v)=>v.name?.toLowerCase()===h).map((v)=>({type:v.type??"?",content:v.content??""}))}catch(y){return u.log(`Workers: could not read DNS records for ${h} (${gi(y)}); attaching anyway.`),[]}}function mp(u,g){let h=[],y=new Set,R=(P)=>{if(P===Fn)return g.warnings.push(`Workers: ${u.name}: binding name ${Fn} is reserved for ts-cloud, ignored.`),!1;if(y.has(P))return g.warnings.push(`Workers: ${u.name}: binding ${P} is declared twice; the first one wins.`),!1;return y.add(P),!0},v=new Map;for(let P of Object.values(g.r2Buckets?.buckets??{}))if(P.jurisdiction&&P.jurisdiction!=="default")v.set(P.name,P.jurisdiction);for(let[P,M]of Object.entries(u.bindings?.r2Buckets??{})){if(!R(P))continue;let V=v.get(M);h.push(V?{type:"r2_bucket",name:P,bucket_name:M,jurisdiction:V}:{type:"r2_bucket",name:P,bucket_name:M})}for(let[P,M]of Object.entries(u.bindings?.vars??{}))if(R(P))h.push({type:"plain_text",name:P,text:String(M)});return h}function Qh(u){let g=new Map;for(let h of Object.values(u?.buckets??{}))for(let y of h?.customDomains??[]){let R=gp(typeof y==="string"?y:y.domain);if(R)g.set(R,h.name)}return g}function $S(u){return[...new Set((u??[]).map(gp).filter(Boolean))]}function gp(u){return(u??"").trim().replace(/\.$/,"").toLowerCase()}function zS(u){if(u<1024)return`${u} B`;if(u<1048576)return`${(u/1024).toFixed(1)} KB`;return`${(u/1024/1024).toFixed(1)} MB`}function gi(u){return u instanceof Error?u.message:String(u)}export{Ml as ABTestManager,be as ACMClient,Or as ACMDnsValidator,bd as ACME_DIRECTORIES,Le as AI,bu as API_IDEMPOTENCY_TTL_MS,Su as API_TOKEN_DEFAULT_TTL_MS,Iu as API_TOKEN_MAX_TTL_MS,Is as API_VERSION,Ee as AUTHORIZATION_CAPABILITIES,fu as AUTH_ACTION_TOKEN_TTL_MS,hu as AUTH_MFA_CHALLENGE_TTL_MS,Au as AUTH_OIDC_TRANSACTION_TTL_MS,at as AUTH_SESSION_ABSOLUTE_TTL_MS,mu as AUTH_SESSION_IDLE_TTL_MS,Ly as AWSAPIError,n as AWSClient,A as AWSCloudFormationClient,I as AWSCloudFrontClient,Ol as AWSConfigManager,db as AWS_PSEUDO_PARAMETERS,$e as AWS_REGIONS,Cg as AcmeClient,on as AlertEvaluator,qi as AlertStore,Mo as AnomalyConfigStore,El as ApiGateway,i as ApiServiceError,Oc as ApplicationAutoScalingClient,pn as Arn,L as AssetHasher,Z as Auth,fs as AuthenticationStore,Ji as AutomationApiService,hs as AutomationIdentityStore,Ha as AwsAuroraDataAdapter,za as AwsAuroraTransport,uc as AwsDatabaseBackupSource,Mr as AwsDriver,Ga as AwsElastiCacheDataAdapter,Ka as AwsElastiCacheTransport,lh as AwsIamCapabilityScanner,hc as AwsInfrastructureBackupSource,Qa as AwsRdsDataAdapter,ja as AwsRdsTransport,Eh as AwsRegionalDriver,yc as AwsSecretsManagerConfigurationBackend,Tm as AwsSpendTransport,Sc as AwsSsmConfigurationBackend,Ns as BackupCoordinator,Bl as BackupManager,dc as BackupStore,Ff as BatchProcessingManager,qn as BedrockClient,Vn as BedrockRuntimeClient,Du as BitbucketSourceAdapter,$l as BlueGreenManager,Pf as BounceComplaintHandler,If as BuildOptimizationManager,Ju as BunDockerRuntime,wg as BunDockerVolumeRuntime,Tg as BunFilesystemArchiveRuntime,ve as CDN,oa as CHAIN_HEADER,Wt as CLOUDFLARE_MANAGED_RULE_PREFIX,yl as CLOUDFRONT_FAILOVER_STATUS_CODES,JC as COMMON_CROSS_ACCOUNT_ROLES,Fn as CONTENT_HASH_BINDING,Ye as CONTROL_PLANE_SCHEMA_VERSION,jm as CREDENTIAL_PROCESS_TIMEOUT_MS,Xo as Cache,as as CanaryManager,Af as CertificateManager,qA as CloudBlockVolumeDriver,go as CloudDriverFactory,st as CloudError,Hm as CloudFormationBuilder,A as CloudFormationClient,I as CloudFrontClient,_l as CloudTrailManager,Pe as CloudWatchLogsClient,ep as CloudflarePagesProvider,G as CloudflareProvider,nS as CommandHistory,te as Communication,en as ComposeApplicationService,ar as ComposeApplicationStore,Vt as ComprehendClient,a as Compute,wm as ComputeSpendTransport,na as ConfigurationError,bc as ConfigurationService,Ac as ConfigurationStore,Hn as ConnectClient,qa as ContainerDataAdapter,Gl as ContainerRegistryManager,fc as ControlPlaneBackupSource,sc as ControlPlaneCleanupDriver,Mi as ControlPlaneStore,_o as CounterTracker,_t as CredentialError,k as CredentialProcessError,sf as CrossRegionReferenceManager,Iy as DASHBOARD_ENTRY,by as DASHBOARD_PORT_BASE,Cy as DASHBOARD_PORT_SPAN,Sy as DASHBOARD_STATE_DIR,fa as DEFAULT_ATTACK_MODE_HOURS,co as DEFAULT_COMPATIBILITY_DATE,al as DEFAULT_DDOS_THRESHOLDS,km as DEFAULT_FAILOVER_STATUS_CODES,Hd as DEFAULT_METER_MAPPINGS,Ud as DEFAULT_PRICE_BOOK,An as DEFAULT_RECURSION_LIMITS,yo as DEFAULT_RPX_CERTS_DIR,re as DEFAULT_SERVICE_LIMITS,ul as DEFAULT_STATE_DIR,Fg as DEFAULT_STATIC_ASSET_EXTENSIONS,fy as DEFAULT_STORAGE_FAILOVER_STATUS_CODES,iy as DEFAULT_TELEMETRY_POLICY,dm as DEFAULT_THRESHOLDS,UA as DEPLOYMENT_QUEUE_KINDS,sa as DEPTH_HEADER,nn as DETECTABLE_SIGNALS,qu as DIAGNOSTIC_PRESETS,Mf as DLQMonitoringManager,J as DNS,Rf as DNSSECManager,vs as DataServiceLifecycle,Ua as DataServiceStore,gr as Database,ff as DatabaseUserManager,j as DebugLogger,my as DependencyGraph,ee as Deployment,_y as DeploymentError,xl as DisasterRecoveryManager,Zn as DnsProviderFactory,br as DockerDataTransport,Oa as DockerDiscoveryAdapter,Zr as DockerNamedVolumeDriver,mc as DockerVolumeBackupSource,c as DurableOperationQueue,Do as DurableQueueWorker,He as DynamoDBClient,jr as EC2Client,Ve as ECRClient,gt as ECSClient,kr as EFSClient,kc as ELBv2Client,zA as ENFORCEMENT_ACTIONS,Gi as ENFORCEMENT_DESCRIPTIONS,_ as ENFORCEMENT_SEVERITY,$p as ENVIRONMENT_LABEL,Gu as EcsRuntimeAdapter,ma as EdgeRateLimiter,mt as ElastiCacheClient,f as Email,xf as EmailAdvanced,kf as EmailAnalyticsManager,Wn as EmailClient,Of as EmailHandlers,Bf as EmailTemplateManager,Wa as EncryptedDataSecretStore,$y as ErrorCodes,Dr as EventBridgeClient,Ih as EventBridgeJobAdapter,Ic as ExternalConfigurationBackend,Jo as FAILOVER_REPLICA_POLICY_SID,$f as FIFOQueueManager,eR as FLY_MACHINES_API,Am as FOCUS_VERSION,wl as FileCache,nt as FileSystem,gc as FilesystemBackupSource,Xa as FleetService,Ja as FleetStore,So as FlyApiError,Cp as FlyClient,e as Fn,Pu as GiteaSourceAdapter,wu as GithubSourceAdapter,Ru as GitlabSourceAdapter,of as GlobalResourceManager,qe as GoDaddyProvider,Ll as GuardDutyManager,wt as HOST_SITES_DIR,Qy as HashCache,Xf as HealthCheckManager,et as HealthCheckRunner,Gr as HetznerClient,Ne as HetznerDriver,ke as IAMClient,Vc as IPV6_EXCLUDED_HOST_LABELS,Sf as ImageScanningManager,Ip as InfrastructureGenerator,Pd as InvalidOperationTransitionError,xt as JobLoader,bh as JobProviderReconciler,Ma as JobService,Fa as JobStore,Rr as KendraClient,Wf as LARAVEL_SERVERLESS_BUILD_STEPS,T as LambdaClient,Ef as LambdaConcurrencyManager,wf as LambdaDLQManager,vf as LambdaDestinationsManager,bf as LambdaLayersManager,Vu as LambdaRuntimeAdapter,Tf as LambdaVPCManager,Cf as LambdaVersionsManager,Ln as LocalEncryptedConfigurationBackend,pc as LogicalDatabaseBackupSource,Ul as LogsManager,vS as MANAGED_NODE_VERSIONS,pd as MANAGEMENT_DASHBOARD_SITE,Gm as MANAGEMENT_DASHBOARD_SITE_PREFIX,ps as MAX_CONTROL_HOURS,kd as MAX_CONTROL_PLANE_ERROR_BYTES,im as MAX_CONTROL_PLANE_JSON_BYTES,Bo as METERS,jA as METER_KEYS,KA as MICRO_CENTS,lm as MIN_PROJECTION_CONFIDENCE,Vg as MTA_STS_PORT,ic as MaintenanceService,nc as MaintenanceStore,pe as Messaging,gf as MetricsManager,pf as MigrationManager,Zm as MockCloudFormation,tf as MockDynamoDB,ef as MockS3,Yo as Monitoring,df as MultiAccountManager,nf as MultiRegionManager,Xy as MultiStepProgress,S as Network,Yf as NetworkSecurityManager,ji as NotificationRouter,xm as ORIGIN_CONNECTION_ATTEMPTS,Om as ORIGIN_CONNECTION_TIMEOUT,Nm as ORIGIN_GROUP_ALLOWED_METHODS,ob as ORIGIN_GROUP_ID_SUFFIX,Bm as ORIGIN_GROUP_SELECTION_CRITERIA,Nc as OpenSearchClient,Rt as OptimisticConcurrencyError,lf as OrganizationManager,Lg as PAGES_MAX_FILE_BYTES,OS as PHP_DEFAULT_EXCLUDES,TS as PHP_LAYER_EXTENSIONS,Lp as PROJECT_LABEL,ht as PROXIABLE_RECORD_TYPES,D as ParameterStore,mf as PerformanceManager,ge as Permissions,Bc as PersonalizeClient,it as Phone,_f as PhoneAdvanced,Um as PhoneHandlers,Za as PlacementService,Ya as PlacementStore,jn as PollyClient,Se as PorkbunProvider,Tn as PreDeployScanner,kl as PreviewEnvironmentManager,Yr as PreviewEnvironmentService,Ze as PreviewEnvironmentStore,Nl as PreviewNotificationService,Jy as ProgressBar,Fl as ProgressiveDeploymentManager,ha as ProtectionControlStore,py as Pseudo,X as Queue,Fi as QueueCancellationError,Uf as QueueManagementManager,WI as QueuePresets,Ro as QueueTimeoutError,_n as R2ApiError,Sn as R2NotEnabledError,ui as R2Provider,op as R2_NOT_ENABLED_ERROR_CODE,su as RATE_LIMIT_KEY_SEPARATOR,ie as RDSClient,sS as RECOMMENDED_ACCOUNT_STRUCTURES,WC as RECOMMENDED_SCPS,oh as RELEASE_QUEUE_KINDS,eS as REPL,rS as REPLCommandBuilder,tS as REPLContext,Mp as ROLE_LABEL,Hy as RateLimiter,JI as RealtimePresets,ym as RecordingSpendTransport,ds as RecursionGuard,Ot as Redirects,af as RegionPairManager,tc as RegionService,ec as RegionStore,rt as Registry,qt as RekognitionClient,Lt as ReleaseService,Ta as ReleaseStore,Ch as RemoteBuildService,Ql as ReplicaManager,Zf as ResourceManagementManager,de as RetryableOperationError,C as Route53Client,Pc as Route53DomainsClient,se as Route53Provider,Df as Route53ResolverManager,ql as Route53RoutingManager,Re as RuntimeOperationService,La as RuntimeStreamRegistry,lc as S3BackupDestinationAdapter,o as S3Client,q as S3Error,Bu as SECRET_PATTERNS,fm as SENSITIVITY_THRESHOLDS,ft as SESClient,Y as SMS,pt as SNSClient,vm as SPEND_CYCLE_SECONDS,tl as SPEND_GATE_SETTING,cy as SPEND_LEASE_SETTING,he as SQSClient,Be as SSH_BOOTSTRAP_VERSION,yi as SSH_PREFLIGHT_SCRIPT,Oe as SSMClient,Dm as STATE_DIR_ENV_VAR,Ei as STATIC_SITE_ZONE_SETTINGS,Il as STORAGE_FAILOVER_METADATA_KEY,De as STSClient,zn as SchedulerClient,pr as Search,wn as SecretFindingScanner,es as Secrets,Hl as SecretsManager,ne as SecretsManagerClient,hf as SecretsRotationManager,E as Security,hr as SecurityHubManager,vn as SecurityPostureStore,$t as SecurityScannerRunner,yf as SecurityScanningManager,Nf as SenderReputationManager,Sh as ServerCronJobAdapter,Va as ServerDataAdapter,No as ServerPathVolumeDriver,Vl as ServiceMeshManager,Cl as Sftp,Uo as SignalSource,Lf as SmsAdvanced,zt as SmsClient,Qm as SmsHandlers,$c as SmsVoiceClient,wa as SourceConnectionStore,Sr as SourceProviderError,rl as SpendCapError,sn as SpendGate,Cm as SpendLoopLease,qo as SpendNotificationRouter,Em as SpendRunner,dr as SpendService,Qi as SpendStore,Yy as Spinner,Vr as SshDriver,Pn as SshFleetDriver,cf as StackDependencyManager,cb as StacksIntegration,Jf as StaticSiteManager,_e as Storage,ru as StorageAdvancedManager,Kn as SupportClient,Ar as SyntheticsManager,Xu as SystemFleetSshTransport,Ct as SystemSshTransport,_a as SystemdDiscoveryAdapter,aa as TRACE_HEADER,Zy as TaskList,kt as TelemetryStore,hl as TemplateBuilder,Rl as TemplateCache,jt as TextractClient,Gn as TranscribeClient,wr as TranslateClient,wh as TransportDisasterRecoveryDriver,Th as TransportPlatformMaintenanceDriver,Ts as TransportRemoteBuildDriver,Ba as TrivyImageScanner,va as TsCloudApiError,Cu as TsCloudClient,ay as URGENT_SPEND_EVENTS,um as USER_VISIBLE_ACTIONS,rc as UnavailableRegionalDriver,Ie as UnifiedDnsValidator,vo as UnsupportedSchemaVersionError,Km as ValidationError,Pr as VoiceClient,ko as VolumeService,or as VolumeStore,nd as WORKER_MODULE_TYPE,di as WorkersApiError,Vs as WorkersPermissionError,Gs as WorkersProvider,yy as Workflow,uf as XRayManager,mS as abTestManager,Eu as activateImmutableRelease,dg as addDns01ChallengeRecord,Nn as addSiteToCloudConfig,Ft as ageSeconds,Vd as aggregateDeltas,tm as allocateSitePorts,Fm as amazonLinuxImageId,Ay as amazonLinuxImageParameter,ib as analyzeStackDiff,Vi as anomalyOptionsForSignal,Aa as applyControlsToDdos,lu as applyControlsToRateLimits,Js as applyDeclaredZoneSettings,mi as applyHetznerVolumes,dn as applyMitigationFactor,bA as applyPlan,eo as applyZoneMigration,jl as artifactKey,uu as assertComputeResidency,Nt as assertOriginGroupMethods,As as assertReleaseStrategy,ny as assertSpendAllows,wA as attachConflicts,RA as attachIsViable,TA as attachPreconditions,$u as attachProvenanceToRelease,xa as attachSbomToRelease,_u as attachVulnerabilitySummary,ih as authEncryptionKeyFile,O as authorizeOrganization,Ah as authorizeRuntimePath,TC as autocomplete,cS as awsConfigManager,Eg as backupCredentialStatus,oS as backupManager,Jb as batch,UE as batchProcessingManager,Ca as beginOidcAuthorization,gS as blueGreenManager,OE as bounceComplaintHandler,Dt as budgetWindow,wc as buildAndPushServerlessImage,GE as buildBunRuntimeLayerZip,xA as buildCertificatePackScript,kA as buildCertificateStateScript,OA as buildCertificateUnpackScript,vl as buildCloudFormationTemplate,ad as buildCloudInitFirstBoot,Od as buildComposeLogsCommand,zI as buildComposeRuntimeCommand,KI as buildComposeScaleCommand,_d as buildComposeShellCommand,ni as buildComputeStorageInstallScript,Fr as buildComputeStorageSiteScript,Yp as buildDrainSourceScript,gA as buildDrainedSiteScanScript,bl as buildFailoverReplicaPolicyStatement,Er as buildFunctionEnv,Xp as buildHealthGateScript,si as buildHostCleanupDeployScript,ao as buildHostCleanupInstallScript,ii as buildHostCleanupScript,Li as buildHostSitePortsScript,Yi as buildMailEnv,fo as buildManagementDashboardArtifact,fR as buildMediaCdnPlan,Gg as buildMtaStsPolicy,Wh as buildMtaStsServer,HE as buildNodeRuntimeLayerZip,TE as buildOptimizationManager,Sl as buildOriginGroup,gn as buildOriginGroups,Uc as buildOriginGuardRule,Vp as buildPauseWorkersScript,zE as buildPhpRuntimeLayerZip,Mm as buildReplicationConfiguration,$m as buildReplicationRole,Kp as buildRestoreScript,x as buildRpxConfig,xe as buildRpxFragmentRefreshScript,EA as buildSetHostnameScript,ai as buildSiteDeployScript,zp as buildSnapshotScript,Zp as buildSourceStateScript,li as buildSshArgs,hi as buildSshBootstrapScript,Fc as buildStaticSiteCacheRules,ci as buildStaticSiteDeployScript,Wp as buildTargetStateScript,dp as buildUploadForm,qp as buildWorkersStateScript,id as bundleWorker,Fu as bytes,ap as cacheRuleParameters,pS as canaryManager,kn as canonicalJson,Mt as capabilities,Cr as capacity,sC as categorizeChanges,CE as certificateManager,BA as certificatesMatch,Ub as checkIAMPermissions,qf as checkInvocation,Hb as checkServiceQuotas,Gy as chunk,OR as cleanupDns01Challenge,_b as clearCredentialProcessCache,GS as clearSigningKeyCache,Da as cloneSourceBinding,Vy as cloudConfigSchema,hp as cloudDrivers,dS as cloudTrailManager,rp as collectAssets,ua as collectPhpAppEntries,Ug as collectServerDnsDomains,sm as completeComposeVolumeDeletion,Ea as completeOidcAuthorization,Rb as composePresets,wo as composeProjectName,Kl as composeServerlessAppTemplate,ly as compositeSpendTransport,Gt as compressBackup,KS as config,Mc as configOrigins,Ku as connectionGuidance,IS as containerRegistryManager,MA as controlPlaneDatabaseFile,Rd as controlPlaneMigrations,fb as createApiBackendPreset,Na as createApiV1Handler,nI as createAsgBuildDriver,vg as createBackupQueueHandlers,xS as createCliHandler,U as createCloudDriver,gd as createCloudFrontPolicy,Lb as createCredentialProvider,Tb as createDashboardSite,Ib as createDataPipelinePreset,Wu as createDataServiceQueueHandlers,Ld as createDeploymentQueueHandlers,B as createDnsProvider,rI as createEcsBuildDriver,nl as createEnforcementHandlers,Wm as createError,Tl as createExistingStaticFullStackPreset,Yu as createFleetQueueHandlers,mb as createFullStackAppPreset,no as createGoDaddyValidator,jE as createHandlers,NS as createHttpHandler,Ab as createJamstackPreset,zu as createJobQueueHandlers,Eb as createLaravelPreset,bb as createMLApiPreset,sg as createMaintenanceQueueHandlers,pR as createMediaAccessToken,yb as createMicroservicesPreset,pC as createMockAWS,ub as createNodeJsServerPreset,gb as createNodeJsServerlessPreset,Hs as createObjectStorageClient,ro as createPorkbunValidator,Db as createPreset,ga as createPresignedUrl,MS as createPresignedUrlAsync,BS as createQueueHandler,Sb as createRealtimeAppPreset,eg as createRegionQueueHandlers,Lu as createReleaseProvenance,Tu as createReleaseQueueHandlers,eI as createRemoteBuildQueueHandlers,io as createRoute53Validator,hh as createRuntimeAdapters,Oy as createS3Client,tI as createServerBuildDriver,vb as createServerlessLaravelPreset,pb as createServerlessNodePreset,Mg as createSmsInfrastructure,dt as createSourceAdapter,lb as createStaticSitePreset,Cb as createTraditionalWebAppPreset,$d as createVolumeQueueHandlers,hb as createWordPressPreset,ot as createZip,xC as crossRegionReferenceManager,$n as dashboardActions,mo as dashboardCredentialsFile,Dn as dataServiceCapabilities,IE as databaseUserManager,pu as decodeBase32,Qe as decompressBackup,Lh as decryptBackup,ya as defaultConfig,Ym as defaultLocalConfig,au as defaultRateLimitRules,xr as delegateZone,Ks as delegateZoneFromConfig,Sg as deleteFailoverReplicaBuckets,vc as deleteStaticSite,fd as deployAllComputeSites,ac as deployServerlessApp,Ig as deploySite,ho as deploySiteRelease,_s as deployStaticSite,Tc as deployStaticSiteFull,Mn as deployStaticSiteWithExternalDns,Un as deployStaticSiteWithExternalDnsFull,iR as deployToFly,fl as deploymentCoexistenceError,Ey as deriveManagementDashboardPort,Ws as describeDelegation,du as describePosture,Xs as describeZoneSettings,yh as desiredJobProviderState,mm as detectAnomalies,Pm as detectDeploymentTargets,Qc as detectDnsProvider,Yd as detectLatestAnomaly,Mb as detectMisconfigurations,nu as detectServiceRegion,Bd as diffCompose,rC as diffTemplates,Ra as discoverGitRefs,ba as discoverOidcProvider,Uu as discoverRuntimeInventory,FE as dlqMonitoringManager,Hc as dnsProviders,BE as dnssecManager,ph as dockerWorkloads,ws as documentDigest,ed as domainStatus,aS as drManager,Ke as driverStatePath,Qu as ecsWorkloads,sr as effectiveCapabilities,_E as emailAnalyticsManager,$E as emailTemplateManager,ms as encodeBase32,_h as encryptBackup,Mu as ensureDefaultSecurityPolicies,hg as ensureFailoverReplicaBuckets,Ti as ensureFirewall,Pi as ensureManagementDashboard,wi as ensureServer,vi as ensureSshKey,ce as escapeSecLang,jd as evaluateBudget,Ii as evaluatePreflight,Vo as evaluateTelemetryAlertRules,ju as eventBridgeScheduleInput,kS as eventToRequest,ey as ewma,Nd as exportCompose,Ys as exportRoute53Zone,qm as extendPreset,ME as fifoQueueManager,Wb as findChangedFiles,am as findMapping,om as findPriceEntry,nR as flyDeployOptions,sd as flyIpType,Rp as flyMachineConfig,eE as formatAccountStructure,DA as formatAttachPlan,AC as formatBytes,an as formatCents,sb as formatDiff,mA as formatDrainedSiteRefusal,yC as formatDuration,RC as formatFlagSuggestions,kC as formatHistory,NC as formatHistoryStats,yA as formatInventory,IC as formatKeyValue,SC as formatList,IA as formatPlan,Ci as formatPreflightFindings,hC as formatProgressBar,iS as formatRegion,qC as formatRegionList,dh as formatScanResults,bC as formatSuggestion,mC as formatTable,rn as formatTimeToExhaustion,fC as formatTree,Py as fromCredentialProcess,ky as fromEC2Metadata,Ny as fromECSMetadata,Ry as fromEnvironment,Dy as fromSharedCredentials,By as fromWebIdentity,Xl as functionFingerprint,bo as gatewayHostnames,Jl as generateAppImageDockerfile,mE as generateApprovalConfig,Qf as generateBootstrap,zy as generateCleanupWorkflow,Ky as generateCostReportWorkflow,YC as generateCrossAccountRoleCF,xu as generateCycloneDxSbom,uE as generateDeploymentConfig,oE as generateDeploymentPipeline,tE as generateDeploymentWorkflow,Ec as generateExternalDnsStaticSiteTemplate,vR as generateHttpsServerCode,Ou as generateImageSbom,kR as generateLetsEncryptUserData,t as generateLogicalId,lE as generateManualPipeline,sE as generateMatrixWorkflow,gE as generateMultiEnvConfig,aE as generateMultiEnvPipeline,rE as generateMultiEnvWorkflow,nE as generatePRPreviewWorkflow,fE as generateParallelConfig,Kf as generatePhpFpmConfig,Hf as generatePhpLayerDockerfile,cE as generatePreviewPipeline,jy as generatePreviewWorkflow,r as generateResourceName,pE as generateScheduledConfig,dE as generateScheduledPipeline,iE as generateScheduledWorkflow,Os as generateStaticSiteTemplate,F as generateUbuntuAppCloudInit,zm as getAccountId,$C as getAllRegions,HC as getClosestRegion,DC as getCommandUsage,FA as getComposeTemplate,th as getConfig,EC as getContextualHelp,ss as getCredentials,oC as getDeploymentStrategy,aC as getDiffStats,nC as getDiffSummary,My as getErrorDetails,Pl as getLocalConfig,uC as getLocalCredentials,lC as getLocalEndpoint,gC as getLocalEnvVars,Gb as getQuotaUsageSummary,XC as getRecommendedStructure,os as getRegion,jC as getRegionStats,FC as getRegionsByCompliance,MC as getRegionsByLocation,UC as getRegionsByPricingTier,Fy as getRequiredPermissions,VS as getSigningKeyCacheSize,$g as getSmsInfrastructureStatus,XI as getTimestamp,pa as globMatches,Ao as globToRegex,OC as globalResourceManager,Hi as governingLimitCents,Ag as grantFailoverReplicaAccess,lS as guardDutyManager,jo as handleSpendRequest,Io as hasAutoWwwVariant,mr as hasManagementDashboardSite,wd as hasProxyUpstream,qb as hashBuffer,jb as hashDirectory,Jm as hashFile,zb as hashManifest,Xm as hashString,WE as healthCheckManager,ei as hostAcceptsIpv6,Kt as hostCondition,Sa as hotp,tn as hourBucket,ZA as hourBucketsBetween,vE as imageScanningManager,Ps as infraEnvFromOutputs,Zl as inspectInvocation,DS as installRecursionFetch,$s as invalidateCache,Vf as invocationContext,hy as isArmInstanceType,CC as isLikelyTypo,Lr as isLocalDatabase,dC as isLocalDevelopment,fr as isManagementDashboardSiteName,Nr as isNameserverRegistrar,qS as isNodeCryptoAvailable,Xd as isOperationAllowed,Go as isQuietHours,Wo as isS3RestEndpoint,Ni as isSafePattern,pl as isStateDirConfigured,ml as isStatePath,cn as isValidCidr,QC as isValidRegion,Ui as isValidTimeZone,jS as isWebCryptoAvailable,Br as isZoneHost,Rn as jobProviderCapability,Qs as keyMatchesFilters,DE as lambdaConcurrencyManager,NE as lambdaDLQManager,PE as lambdaDestinationsManager,wE as lambdaLayersManager,kE as lambdaVPCManager,RE as lambdaVersionsManager,Hu as lambdaWorkloads,eu as laravelServerlessEnvDefaults,Xr as listComposeTemplates,Ss as listSourceReferences,gu as loadCloudConfig,is as loadProfileCredentials,ta as loadProfileCredentialsSync,ns as loadStaticProfileCredentials,lr as loadTelemetryPolicy,hS as logsManager,Zd as lookbackHoursForSignal,gy as mailEnabled,tt as mailFirewallPorts,un as mailWebmailSites,vh as maintenanceWindowOpen,eh as makeAWSRequest,US as makeAWSRequestAsync,FS as makeAWSRequestOnce,cp as managedRuleInSync,Vm as managementDashboardSiteName,tg as manifestDigest,In as matchTotpCounter,gR as mediaObjectHeaders,$o as median,gm as medianAbsoluteDeviation,zd as mergeDecisions,mn as mergeInfrastructure,XA as mergePriceBooks,Gd as meterTelemetry,AE as metricsManager,Bg as migrateObjectStorage,yE as migrationManager,zC as multiAccountManager,BC as multiRegionManager,NR as needsRenewal,JE as networkSecurityManager,Ut as nextScheduleRuns,oe as normalizePublicIpv6,Me as normalizeRuntimeStatus,lt as normalizeScheduleExpression,ah as normalizeSourceEvent,ze as normalizeSshPublicKey,Sm as normalizeTelemetryPolicy,_i as occupiedHostPorts,ka as openApiDocument,Qo as optionsForSignal,KC as organizationManager,tu as packagePhpApp,hn as packageServerlessApp,tp as pagesAssetHash,Wg as pagesDnsRecord,Dl as parallel,Xb as parallelMap,Yb as parallelWithRetry,NA as parseCertificateState,Yl as parseChain,Jr as parseCompose,qI as parseComposeCatalog,gh as parseDockerInspect,Rg as parseDotenv,pA as parseDrainedSites,$i as parseHostSiteFragments,HS as parseJSONResponse,Si as parsePreflightFacts,em as parseSourceDrained,mh as parseSystemdRecords,Jp as parseTargetReady,Td as parseUpstreamPort,jp as parseWorkersPaused,QS as parseXMLResponse,Ki as pathTemplate,Ed as pendingSteps,Lo as percentOfLimit,SE as performanceManager,Wl as phpLayerBuildStage,wS as phpLayerPackages,yn as phpRuntimeLayerAssets,jI as planComposeTemplateUpgrade,Kd as planEnforcement,pi as planHetznerVolumes,Hp as planIsDestructive,Rm as planMitigation,CA as planServerRename,_A as planSiteMove,Zs as planZoneMigration,Xt as policyTag,bi as preflightFailed,qy as previewManager,Wy as previewNotifications,Qt as previewSchedule,YA as previousBudgetWindow,xo as priceUsage,WA as priceUsageLines,hA as probeHostRoutes,Xc as probeOriginTls,tC as processInChunks,ku as processSourceWebhook,Cs as productionChangeReview,ea as profileCredentialProcess,fS as progressiveDeploymentManager,qd as projectSpend,ca as propagationHeaders,Rc as providerEndpoint,rg as publicKeyFingerprint,Xn as qualifyName,QE as queueManagementManager,Kb as quickHash,sp as r2Endpoint,Zg as r2S3Credentials,ou as rateLimitHeaders,gs as rateLimitKey,We as readDriverState,fn as readSharedProfile,AA as reconcile,Hg as reconcileAddressRecords,Og as reconcileCloudflareCdn,Kh as reconcileCloudflareWorkers,zs as reconcileDeclaredRecords,ZS as reconcileJobObservation,Xg as reconcileR2Buckets,En as reconcileSourceWebhook,bs as recordDashboardHostPosture,JS as recordPreDeploySecretScan,XS as recordSkippedSecretScan,da as recursionBlockedResponse,Fe as redactRuntimeConfig,ol as redactTelemetryText,zi as redactTelemetryValue,xn as redeployServerlessApp,_C as regionPairManager,Cn as releaseStrategyCapabilities,sh as releaseTrafficPlan,ki as reloadRpxGateway,Us as remapKey,Qg as removeAddressRecords,pg as removeDns01ChallengeRecord,Bn as removeSiteFromCloudConfig,Nu as removeSourceWebhook,Gc as removeStaleServerAddressRecords,xd as renderComposeTemplate,Xi as renderDdosInstallScript,ll as renderNftablesRuleset,Es as renderServerCron,dl as renderSysctlFile,Ad as renderWafConfig,Bi as renderWafInstallScript,hd as renewRpxCertificates,yS as replicaManager,uy as requestHash,iC as requiresReplacement,VE as resetRecursionRuntime,qE as resolveApp,p as resolveAppDatabase,vA as resolveAttachTarget,ct as resolveAuthEncryptionKey,le as resolveCloudProvider,xg as resolveCloudflareCdnPlan,ri as resolveComputeVolumes,ra as resolveCredentials,xs as resolveDashboardAction,md as resolveDashboardAuth,vy as resolveDashboardDomain,Ty as resolveDashboardDomains,js as resolveDeclaredRecords,nb as resolveDeployBucketName,W as resolveDeploymentMode,oi as resolveExecStart,Ko as resolveFailoverStatusCodes,Yc as resolveHetznerApiToken,Qr as resolveHostCleanupConfig,w as resolveMailProxy,b as resolveMailService,wb as resolveManagementDashboardSite,ts as resolveManagementDashboardSites,Dc as resolveObjectStorage,ue as resolveOriginConnection,SA as resolvePlan,Te as resolveProfileName,m as resolveProjectStackName,Wr as resolveProxyUpstreams,zl as resolveQueueNames,QA as resolveQueuedDeploymentCommand,yr as resolveQueues,RS as resolveRecursionConfig,xy as resolveRegion,fe as resolveRuntimeInventory,ur as resolveServerlessAppStackName,Al as resolveServerlessArtifactBucketName,Zi as resolveServerlessAssetBucketName,cs as resolveServerlessRuntime,tb as resolveSiteBucketName,Eo as resolveSiteDeployTarget,d as resolveSiteKind,eb as resolveSiteResourceName,ZI as resolveSiteStackName,Ai as resolveSshSettings,l as resolveStatePath,rb as resolveStorageBucketName,_m as resolveStorageFailover,rr as resolveUiSource,Id as resolveZoneApex,XE as resourceManagementManager,zf as responseToResult,pm as robustZScore,ir as roleCapabilities,vu as rollbackImmutableRelease,On as rollbackServerlessApp,JA as roundCents,xE as route53ResolverManager,CS as route53RoutingManager,hm as routeMatches,Fp as routesFromFragments,us as ruleMatches,ES as runBuildHooks,Wd as runEnforcement,la as runPhpBuildHooks,ut as runRemoteCommand,we as runtimeId,Ge as s3OriginBucket,Lm as s3RegionalDomain,ks as sameCorsRules,Fs as sameLifecycleRules,Yn as sameNameservers,Bs as sanitizeCloudConfig,s as sanitizeControlPlaneValue,YI as sanitizeName,yu as sanitizeOidcReturnPath,il as saveTelemetryPolicy,ch as scanForSecrets,Dd as scopeContains,Pt as scopeKey,Kc as scpUpload,vC as searchCommands,To as searchControlPlane,SS as secretsManager,bE as secretsRotationManager,YS as secureContainerRelease,uS as securityHubManager,EE as securityScanningManager,me as securityScope,yg as seedFailoverReplicas,bn as sendAuthenticationEmail,LE as senderReputationManager,Zb as sequence,Dg as serializeDotenv,Ri as serverPublicIpv4,bS as serviceMeshManager,gg as setAttachToInCloudConfig,vr as setMaintenance,Rs as setSitePropertyInCloudConfig,gl as setStateDir,ER as setupDns01Challenge,Lc as setupSmsInfrastructure,ia as sha256,Zo as sharedConfigPath,rs as sharedCredentialsPath,PS as sharedRecursionGuard,Gf as sharedRuntimeLoop,nm as shipsARelease,dR as signCloudFrontCookies,uR as signCloudFrontUrl,N as signRequest,iu as signRequestAsync,Fo as signalDefinition,Q as siteInstallBase,Oi as siteMoveArchivePath,PA as siteMoveCertArchivePath,vd as siteUnitGlob,bm as spendNotificationText,zo as spendOpenApiPaths,wy as splitCommandLine,Hr as sshExec,Ce as sshExecOrThrow,LC as stackDependencyManager,Cc as startLocalDashboardServer,dy as startSpendLoop,ln as stateDir,H as statePath,KE as staticSiteManager,_S as storageAdvancedManager,ab as storageFailoverReplicasFromTemplate,ty as streamFocusJsonl,Jd as strongestActiveAction,rf as suggestCommand,wC as suggestFlags,Qb as suggestIAMPolicy,Vb as suggestQuotaIncrease,VC as suggestRegionPairs,GC as suggestRegions,ys as syncSourceRepositories,Md as synchronizeComposeVolumes,Pg as synchronizeConfiguredConfiguration,$a as synchronizeConfiguredJobs,AS as syntheticsManager,cl as sysctlHardening,fh as systemdWorkloads,oy as telemetryBucketLabel,Wi as telemetryCursor,sl as telemetryEstimatedMonthlyCost,sy as telemetryPercentile,Im as telemetryPolicyKey,Uy as templateCache,Up as tenantsOf,Pa as testSourceConnection,Fd as tieredMicroCents,lp as toApiLifecycleRule,Jc as toCloudflareZoneSettings,Ho as toFocusJsonl,el as toFocusRecords,fA as toInventoryServer,rh as totp,Ia as totpUri,Qp as unaccountedSites,uh as unsupportedCapabilities,HA as unsupportedVolumeCapabilities,ig as updateCompatibility,og as updateSiteInCloudConfig,Ls as uploadStaticFiles,cm as usageDeltaKey,cr as utcInstantOfLocal,ZC as validateAccountStructure,cC as validateAgainstSchema,VA as validateAttachment,Oh as validateBackupDestination,PC as validateCommand,$b as validateConfiguration,Fb as validateCredentials,rm as validateDeploymentConfig,GA as validateMountPath,Bt as validateOriginGroups,_p as validateResourceLimits,Gp as validateServerName,Dp as validateTemplate,xp as validateTemplateSize,qc as verifyAddressRecord,WS as verifyArtifactSignature,mR as verifyMediaAccessToken,nh as verifyTotp,ng as verifyUpdateSignature,to as verifyZoneParity,Po as volumeCapabilities,kp as wafEventTelemetry,Wc as waitForCloudInit,yt as waitForSsh,Ir as webhookEndpoint,xb as withCDN,Bb as withCache,pp as withContentHash,Nb as withDatabase,jf as withInvocation,Pb as withMonitoring,Ob as withQueue,kb as withSecurity,eC as withTimeout,mp as workerBindings,lo as workerContentHash,qs as workerInSync,ae as wrapCloudInitUserData,It as writeDriverState,hE as xrayManager,Zu as zeroCapacity,ry as zeroFill,Qd as zoneOffsetMs,Oo as zonedParts};
|
|
604
|
+
`}async function dg(u,g,h){let S=`_acme-challenge.${g}`;if(await gg(u,g,S,h))return;let A=await u.createRecord(g,{name:S,type:"TXT",content:h,ttl:60});if(!A.success)throw Error(`Failed to create DNS challenge record: ${A.message}`)}async function mg(u,g,h){let S=await u.deleteRecord(g,{name:`_acme-challenge.${g}`,type:"TXT",content:h});if(!S.success)console.warn(`Failed to delete DNS challenge record: ${S.message}`)}async function gg(u,g,h,S){try{let A=await u.listRecords(g,"TXT"),T=h.replace(/\.$/,"").toLowerCase();return(A.records??[]).some((P)=>P.name.replace(/\.$/,"").toLowerCase()===T&&P.content.replace(/^"|"$/g,"")===S)}catch{return!1}}function gd(u){let{hostedZoneId:g,dnsProvider:h,region:S="us-east-1"}=u;if(h)return B(h);if(g)return new oe(S,g);throw Error("Either dnsProvider or hostedZoneId must be provided")}async function OR(u){await dg(gd(u),u.domain,u.challengeValue)}async function NR(u){await mg(gd(u),u.domain,u.challengeValue)}function xR(u){try{let{execSync:g}=us("node:child_process"),h=g(`openssl x509 -checkend 2592000 -noout -in ${u}/cert.pem`,{encoding:"utf-8",stdio:["pipe","pipe","pipe"]});return!1}catch{return!0}}import{createHash as Cd,createSign as fg,generateKeyPairSync as Sd}from"node:crypto";var bd={production:"https://acme-v02.api.letsencrypt.org/directory",staging:"https://acme-staging-v02.api.letsencrypt.org/directory"};class hg{directoryUrl;email;accountKey;accountUrl=null;directory=null;nonce=null;constructor(u){this.directoryUrl=u.staging?bd.staging:bd.production,this.email=u.email,this.accountKey=u.accountKey||this.generateAccountKey()}generateAccountKey(){let{privateKey:u}=Sd("ec",{namedCurve:"P-256"});return u.export({type:"pkcs8",format:"pem"})}async getDirectory(){if(this.directory)return this.directory;let u=await fetch(this.directoryUrl);if(!u.ok)throw Error(`Failed to fetch ACME directory: ${u.status}`);return this.directory=await u.json(),this.directory}async getNonce(){if(this.nonce){let S=this.nonce;return this.nonce=null,S}let u=await this.getDirectory(),h=(await fetch(u.newNonce,{method:"HEAD"})).headers.get("replay-nonce");if(!h)throw Error("Failed to get nonce from ACME server");return h}getJwk(){let u=this.accountKey.split(`
|
|
605
|
+
`).filter((T)=>!T.startsWith("-----")).join(""),g=Buffer.from(u,"base64"),h=g.length-65,S=g.subarray(h+1,h+33),A=g.subarray(h+33,h+65);return{kty:"EC",crv:"P-256",x:this.base64UrlEncode(S),y:this.base64UrlEncode(A)}}getJwkThumbprint(){let u=this.getJwk(),g=JSON.stringify({crv:u.crv,kty:u.kty,x:u.x,y:u.y}),h=Cd("sha256").update(g).digest();return this.base64UrlEncode(h)}base64UrlEncode(u){return(typeof u==="string"?Buffer.from(u):u).toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async signPayload(u,g){let h=await this.getNonce(),S=this.getJwk(),A={alg:"ES256",nonce:h,url:u};if(this.accountUrl)A.kid=this.accountUrl;else A.jwk=S;let T=this.base64UrlEncode(JSON.stringify(A)),P=g===""?"":this.base64UrlEncode(JSON.stringify(g)),M=`${T}.${P}`,W=fg("SHA256");W.update(M);let le=W.sign(this.accountKey),K=le.subarray(4,4+le[3]),Ct=4+le[3]+2,Tt=le.subarray(Ct,Ct+le[Ct-1]),He=Buffer.alloc(32),Se=Buffer.alloc(32);K.copy(He,32-K.length),Tt.copy(Se,32-Tt.length);let Ie=Buffer.concat([He,Se]);return JSON.stringify({protected:T,payload:P,signature:this.base64UrlEncode(Ie)})}async acmeRequest(u,g){let h=await this.signPayload(u,g),S=await fetch(u,{method:"POST",headers:{"Content-Type":"application/jose+json"},body:h}),A=S.headers.get("replay-nonce");if(A)this.nonce=A;let T,P=S.headers.get("content-type");if(P?.includes("application/json")||P?.includes("application/problem+json"))T=await S.json();else T=await S.text();if(!S.ok)throw Error(`ACME request failed: ${JSON.stringify(T)}`);return{body:T,headers:S.headers}}async registerAccount(){let u=await this.getDirectory(),{body:g,headers:h}=await this.acmeRequest(u.newAccount,{termsOfServiceAgreed:!0,contact:[`mailto:${this.email}`]}),S=h.get("location");if(!S)throw Error("No account URL returned");return this.accountUrl=S,S}async createOrder(u){if(!this.accountUrl)await this.registerAccount();let g=await this.getDirectory(),{body:h,headers:S}=await this.acmeRequest(g.newOrder,{identifiers:u.map((T)=>({type:"dns",value:T}))}),A=S.get("location");if(!A)throw Error("No order URL returned");return{orderUrl:A,authorizations:h.authorizations,finalize:h.finalize}}async getAuthorization(u){let{body:g}=await this.acmeRequest(u,""),h=g.identifier.value,S=this.getJwkThumbprint(),A=g.challenges.filter((T)=>T.type==="http-01"||T.type==="dns-01").map((T)=>{let P=`${T.token}.${S}`;if(T.type==="http-01")return{type:"http-01",token:T.token,keyAuthorization:P,identifier:`/.well-known/acme-challenge/${T.token}`};else{let M=this.base64UrlEncode(Cd("sha256").update(P).digest());return{type:"dns-01",token:T.token,keyAuthorization:P,identifier:`_acme-challenge.${h}`,dnsValue:M}}});return{domain:h,challenges:A}}async respondToChallenge(u){await this.acmeRequest(u,{})}async waitForAuthorization(u,g=30){for(let h=0;h<g;h++){let{body:S}=await this.acmeRequest(u,"");if(S.status==="valid")return;if(S.status==="invalid")throw Error(`Authorization failed: ${JSON.stringify(S)}`);await new Promise((A)=>setTimeout(A,2000))}throw Error("Authorization timed out")}generateCsr(u){let{privateKey:g}=Sd("rsa",{modulusLength:2048}),h=g.export({type:"pkcs8",format:"pem"});return{csr:this.createSimpleCsr(u,g),privateKey:h}}createSimpleCsr(u,g){throw Error("CSR generation requires additional implementation. Consider using the shell-based certbot approach instead.")}async finalizeOrder(u,g){let{csr:h,privateKey:S}=this.generateCsr(g),{body:A}=await this.acmeRequest(u,{csr:this.base64UrlEncode(Buffer.from(h,"base64"))}),T=A.certificate;if(!T)throw Error("Certificate not immediately available - polling not implemented");let M=await(await fetch(T)).text(),W=M.split(/(?=-----BEGIN CERTIFICATE-----)/g),le=W[0],K=W.slice(1).join("");return{certificate:le,privateKey:S,chain:K,fullchain:M,expiresAt:new Date(Date.now()+7776000000)}}getAccountKey(){return this.accountKey}}var Ur=(u)=>u===void 0?void 0:u?"on":"off";function Yc(u){let g={ssl:u.ssl,always_use_https:Ur(u.alwaysUseHttps),automatic_https_rewrites:Ur(u.automaticHttpsRewrites),min_tls_version:u.minTlsVersion,tls_1_3:Ur(u.tls13),brotli:Ur(u.brotli),http3:Ur(u.http3),"0rtt":Ur(u.zeroRtt),early_hints:Ur(u.earlyHints),browser_cache_ttl:u.browserCacheTtl,always_online:Ur(u.alwaysOnline),websockets:Ur(u.websockets),email_obfuscation:Ur(u.emailObfuscation)};if(u.hsts)g.security_header={strict_transport_security:{enabled:u.hsts.enabled,max_age:u.hsts.maxAge??31536000,include_subdomains:u.hsts.includeSubdomains??!1,preload:u.hsts.preload??!1,nosniff:u.hsts.noSniff??!0}};for(let h of Object.keys(g))if(g[h]===void 0)delete g[h];return{...g,...u.raw??{}}}var Ei={ssl:"strict",alwaysUseHttps:!0,automaticHttpsRewrites:!0,minTlsVersion:"1.2",tls13:!0,brotli:!0,http3:!0,earlyHints:!0,alwaysOnline:!0,emailObfuscation:!1,browserCacheTtl:0};import{connect as Cg}from"node:tls";async function Xc(u){let{address:g,serverName:h,port:S=443,timeoutMs:A=5000}=u;return new Promise((T)=>{let P=!1,M=(le)=>{if(P)return;P=!0;try{W.destroy()}catch{}T(le)},W=Cg({host:g,port:S,servername:h,rejectUnauthorized:!1,timeout:A});W.once("secureConnect",()=>{if(W.authorized){M({reachable:!0,trusted:!0});return}M({reachable:!0,trusted:!1,reason:W.authorizationError?String(W.authorizationError):"origin certificate is not trusted for this hostname"})}),W.once("timeout",()=>{M({reachable:!1,trusted:!1,reason:`no TLS handshake within ${A}ms`})}),W.once("error",(le)=>{M({reachable:!1,trusted:!1,reason:le.message})})})}function $g(u,g){let h=g.replace(/\.$/,"").toLowerCase();return u.some((S)=>{let A=S.replace(/\.$/,"").toLowerCase();if(A===h)return!0;if(!A.startsWith("*."))return!1;let T=A.slice(2);if(!h.endsWith(`.${T}`))return!1;return!h.slice(0,h.length-T.length-1).includes(".")})}async function Eg(u){let{provider:g,zone:h,hosts:S,ipv4:A,ipv6:T,proxied:P=!0,settings:M=Ei,cache:W,originGuard:le,purge:K=!0,ttl:Ct=300,skipOriginProbe:Tt=!1,skipEdgeCertificateCheck:He=!1}=u,Se={records:[],settingsChanged:[],cacheRules:0,originGuard:!1,purged:!1,deferredProxy:[],warnings:[]};if(S.length===0)return Se;let Ie=[],z=P&&!He?await g.listEdgeCertificateHosts(h):null;for(let V of S){let Ke=P;if(Ke&&z&&!$g(z,V)){Ke=!1;let It=z.length===0?"the zone has no active edge certificate yet":`no active edge certificate covers it (${z.join(", ")})`;Se.deferredProxy.push({host:V,reason:It}),Se.warnings.push(`${V}: publishing DNS-only — ${It}. Proxying it would make Cloudflare terminate TLS with no certificate for the name, which fails the handshake outright rather than degrading. Universal SSL covers the apex and ONE level of subdomain, so a deeper name needs its own certificate; otherwise re-run the deploy once the certificate is active.`)}if(Ke&&!Tt){let It=await Xc({address:A,serverName:V});if(!It.trusted){Ke=!1;let vt=It.reason||"origin is not serving a trusted certificate";Se.deferredProxy.push({host:V,reason:vt}),Se.warnings.push(`${V}: publishing DNS-only for now — the origin does not yet serve a certificate this proxy can verify (${vt}). The origin issues certificates over HTTP-01, which needs the name to reach it directly; re-run the deploy once it has one and the record will be proxied.`)}}if(Ke)Ie.push(V);let yt=[{type:"A",content:A}];if(T&&ei(V))yt.push({type:"AAAA",content:T});for(let{type:It,content:vt}of yt){let Ht=await g.upsertRecord(h,{name:V,type:It,content:vt,ttl:Ct,proxied:Ke});if(Ht.success)Se.records.push({host:V,type:It,content:vt,proxied:Ke});else Se.warnings.push(`${V} ${It} → ${vt} failed: ${Ht.message||"unknown provider error"}`)}}if(Ie.length===0){if(P)Se.warnings.push("No host is proxied yet, so zone settings, cache rules and purge were skipped — they only take effect through the proxy.");return Se}try{let V=Yc(M),Ke=await g.applyZoneSettings(h,V);Se.settingsChanged=Ke.changed;for(let yt of Ke.failed)Se.warnings.push(`zone setting '${yt.id}' could not be applied: ${yt.error}`)}catch(V){Se.warnings.push(`zone settings could not be read: ${V instanceof Error?V.message:String(V)}`)}let Ye=Uc(Ie,W);if(Ye.length>0){let V=await g.putManagedPhaseRules(h,"http_request_cache_settings",Ye);if(V.success)Se.cacheRules=Ye.length;else Se.warnings.push(`cache rules could not be written: ${V.message}`)}if(le){let V=Qc(Ie,le.header,le.value);if(V.length>0){let Ke=await g.putManagedPhaseRules(h,"http_request_late_transform",V);if(Ke.success)Se.originGuard=!0;else Se.warnings.push(`origin guard header could not be written: ${Ke.message}. `+"The gateway will reject requests that lack it — disable the guard or grant the token Transform Rules: Edit.")}}if(K){if(Se.purged=await g.purgeCache(h,{hosts:Ie}),!Se.purged)Se.warnings.push("edge cache purge failed — new content may take until TTL expiry to appear.")}return Se}var Og=(u)=>process.env[u];function Id(u,g){let h=u.replace(/\.$/,"").toLowerCase();if(g){let S=g.replace(/\.$/,"").toLowerCase();if(h===S||h.endsWith(`.${S}`))return S}return h.split(".").slice(-2).join(".")}function Ng(u,g={}){let h=g.env??Og,S=u.infrastructure?.compute?.proxy?.cdn,A=[];if(!S||S.provider!=="cloudflare")return{plan:null,errors:A};if(S.originDomain)A.push(`compute.proxy.cdn.originDomain (${S.originDomain}) is ignored for Cloudflare: the proxy forwards to the address in the record it fronts, `+"so no separate origin hostname is needed — and publishing one gives clients a documented way around the edge.");let T=h("CLOUDFLARE_API_TOKEN");if(!T)return A.push("CLOUDFLARE_API_TOKEN is not set — the Cloudflare CDN cannot be reconciled."),{plan:null,errors:A};let P=u.sites??{},M=S.frontedHosts?.length?[...new Set(S.frontedHosts.map((Tt)=>Tt.toLowerCase()))]:Co(P,{autoWww:u.infrastructure?.compute?.proxy?.autoWww!==!1});if(M.length===0)return A.push("No hostnames to front — set compute.proxy.cdn.frontedHosts, or give a site a domain."),{plan:null,errors:A};let W=u.infrastructure?.dns?.domain,le=new Set(M.map((Tt)=>Id(Tt,W)));if(le.size>1)return A.push(`Fronted hosts span more than one zone (${[...le].join(", ")}). A Cloudflare zone is configured as a unit, so run one CDN config per zone.`),{plan:null,errors:A};let K=S.cloudflare??{},Ct=S.secret?{header:S.secretHeader??"X-Origin-Verify",value:S.secret}:void 0;return{plan:{zone:[...le][0],hosts:M,apiToken:T,zoneId:K.zoneId??h("CLOUDFLARE_ZONE_ID"),accountId:K.accountId??h("CLOUDFLARE_ACCOUNT_ID"),proxied:K.proxied!==!1,settings:K.settings??Ei,cache:K.cache,originGuard:Ct,purge:K.purgeOnDeploy!==!1,skipOriginProbe:K.skipOriginProbe===!0},errors:A}}var Lg=26214400;var _g=new Set(["/_headers","/_redirects","/_routes.json","/_worker.js"]);class Ed{name="cloudflare-pages";apiToken;accountId;constructor(u){if(!u.apiToken)throw Error("Cloudflare Pages needs an API token");if(!u.accountId)throw Error("Cloudflare Pages needs an account id");this.apiToken=u.apiToken,this.accountId=u.accountId}async request(u,g,h){return Eo(u,`/accounts/${this.accountId}${g}`,`Bearer ${this.apiToken}`,h)}async getProject(u){try{return await this.request("GET",`/pages/projects/${u}`)}catch(g){if(g instanceof Error&&/\b8000007\b|not found/i.test(g.message))return null;throw g}}async ensureProject(u,g="main"){let h=await this.getProject(u);if(h)return h;return this.request("POST","/pages/projects",{name:u,production_branch:g})}async uploadToken(u){return(await this.request("GET",`/pages/projects/${u}/upload-token`)).jwt}async missingHashes(u,g){let h=await Eo("POST","/pages/assets/check-missing",`Bearer ${u}`,{hashes:g});return new Set(h)}async upsertHashes(u,g){await Eo("POST","/pages/assets/upsert-hashes",`Bearer ${u}`,{hashes:g})}async uploadBatch(u,g){let h=g.map((S)=>({key:S.hash,value:S.base64,metadata:{contentType:Wg(S.extension)},base64:!0}));await Eo("POST","/pages/assets/upload",`Bearer ${u}`,h)}async createDeployment(u,g,h){let S={};for(let T of g)S[T.path]=T.hash;let A=new FormData;if(A.append("manifest",JSON.stringify(S)),h)A.append("branch",h);return this.request("POST",`/pages/projects/${u}/deployments`,A)}async deployDirectory(u){let g=await tp(u.directory);if(g.length===0)throw Error(`No files to deploy in ${u.directory}`);let h=await this.uploadToken(u.project),S=await this.missingHashes(h,g.map((W)=>W.hash)),A=g.filter((W)=>S.has(W.hash)),T=[];for(let W=0;W<A.length;W+=100)T.push(A.slice(W,W+100));let P=0;for(let W=0;W<T.length;W+=4){let le=T.slice(W,W+4);await Promise.all(le.map((K)=>this.uploadBatch(h,K))),P+=le.reduce((K,Ct)=>K+Ct.length,0),u.onProgress?.(P,A.length)}return await this.upsertHashes(h,g.map((W)=>W.hash)),{deployment:await this.createDeployment(u.project,g,u.branch),total:g.length,uploaded:A.length,uploadedBytes:A.reduce((W,le)=>W+le.size,0)}}async listCustomDomains(u){return(await this.request("GET",`/pages/projects/${u}/domains`)).map((h)=>h.name)}async attachCustomDomain(u,g){if((await this.listCustomDomains(u)).includes(g))return!1;return await this.request("POST",`/pages/projects/${u}/domains`,{name:g}),!0}}function Bg(u,g){return{type:"CNAME",name:u,value:g.endsWith(".pages.dev")?g:`${g}.pages.dev`,ttl:1,proxied:!0}}async function ep(u,g){let{blake3:h}=await import("./chunk-7zjf2493.js").then((m)=>$S(m.default,1));return(await h(u+g)).slice(0,32)}async function tp(u){let{readdir:g}=await import("node:fs/promises"),{join:h,relative:S,sep:A}=await import("node:path"),T=[];async function P(M){let W=await g(M,{withFileTypes:!0});for(let le of W){let K=h(M,le.name);if(le.isDirectory()){await P(K);continue}if(!le.isFile())continue;let Ct=`/${S(u,K).split(A).join("/")}`;if(_g.has(Ct))continue;let Tt=await Bun.file(K).bytes();if(Tt.byteLength>26214400)throw Error(`${Ct} is ${Tt.byteLength} bytes, over the Cloudflare Pages limit of 26214400`);let He=Buffer.from(Tt).toString("base64"),Se=le.name.lastIndexOf("."),Ie=Se>0?le.name.slice(Se+1):"";T.push({path:Ct,hash:await ep(He,Ie),base64:He,extension:Ie,size:Tt.byteLength})}}return await P(u),T.sort((M,W)=>M.path.localeCompare(W.path)),T}function Wg(u){return{html:"text/html; charset=utf-8",css:"text/css; charset=utf-8",js:"application/javascript; charset=utf-8",mjs:"application/javascript; charset=utf-8",json:"application/json; charset=utf-8",map:"application/json; charset=utf-8",xml:"application/xml; charset=utf-8",txt:"text/plain; charset=utf-8",svg:"image/svg+xml",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",avif:"image/avif",ico:"image/x-icon",woff:"font/woff",woff2:"font/woff2",ttf:"font/ttf",otf:"font/otf",pdf:"application/pdf",webmanifest:"application/manifest+json"}[u.toLowerCase()]||"application/octet-stream"}async function Eo(u,g,h,S){let A={Authorization:h},T;if(S instanceof FormData)T=S;else if(S!==void 0)A["Content-Type"]="application/json",T=JSON.stringify(S);let P=await fetch(`https://api.cloudflare.com/client/v4${g}`,{method:u,headers:A,body:T}),M=await P.text(),W;try{W=JSON.parse(M)}catch{throw Error(`Cloudflare Pages API returned ${P.status} with a non-JSON body: ${M.slice(0,200)}`)}if(!W.success){let le=(W.errors??[]).map((K)=>`${K.code}: ${K.message}`).join(", ");throw Error(`Cloudflare Pages API error (${P.status}): ${le||M.slice(0,200)}`)}return W.result}import{createHash as zg}from"node:crypto";var np="https://api.cloudflare.com/client/v4",ip=10042,jg=10006,Kg=10059;class _n extends Error{status;errors;constructor(u,g,h=[]){super(u);this.name="R2ApiError",this.status=g,this.errors=h}hasCode(u){return this.errors.some((g)=>g.code===u)}}class Sn extends _n{accountId;constructor(u,g,h=[]){super(`R2 is not enabled on Cloudflare account ${u}. Enable it once in the Cloudflare dashboard (R2 Object Storage, then follow the prompt to activate it), then re-run the deploy.`,g,h);this.name="R2NotEnabledError",this.accountId=u}}class ui{name="cloudflare-r2";apiToken;accountId;fetchImpl;constructor(u){if(!u.apiToken)throw Error("Cloudflare R2 needs an API token");if(!u.accountId)throw Error("Cloudflare R2 needs an account id");this.apiToken=u.apiToken,this.accountId=u.accountId,this.fetchImpl=u.fetch??((g,h)=>fetch(g,h))}async requestEnvelope(u,g,h,S={}){let A={Authorization:`Bearer ${this.apiToken}`};if(h!==void 0)A["Content-Type"]="application/json";if(S.jurisdiction&&S.jurisdiction!=="default")A["cf-r2-jurisdiction"]=S.jurisdiction;let T=await this.fetchImpl(`${np}/accounts/${this.accountId}/r2${g}`,{method:u,headers:A,body:h===void 0?void 0:JSON.stringify(h)}),P=await T.text(),M;try{M=JSON.parse(P)}catch{throw new _n(`Cloudflare R2 API returned ${T.status} with a non-JSON body: ${P.slice(0,200)}`,T.status)}if(!M.success){let W=M.errors??[];if(W.some((K)=>K.code===ip))throw new Sn(this.accountId,T.status,W);let le=W.map((K)=>`${K.code}: ${K.message}`).join(", ");throw new _n(`Cloudflare R2 API error (${T.status}) on ${u} ${g}: ${le||P.slice(0,200)}`,T.status,W)}return M}async request(u,g,h,S){return(await this.requestEnvelope(u,g,h,S)).result}bucketPath(u){return`/buckets/${encodeURIComponent(u)}`}async listBuckets(u={}){let g=[],h;do{let S=new URLSearchParams({per_page:"1000"});if(h)S.set("cursor",h);let A=await this.requestEnvelope("GET",`/buckets?${S}`,void 0,u),T=A.result?.buckets??[];g.push(...T),h=T.length>0?A.result_info?.cursor||void 0:void 0}while(h);return g}async getBucket(u,g={}){try{return await this.request("GET",this.bucketPath(u),void 0,g)}catch(h){if(h instanceof Sn)throw h;if(h instanceof _n&&(h.hasCode(jg)||h.status===404))return null;throw h}}async ensureBucket(u,g={}){let h={jurisdiction:g.jurisdiction},S=await this.getBucket(u,h);if(S)return{bucket:S,created:!1};let A={name:u};if(g.locationHint)A.locationHint=g.locationHint;let T=await this.request("POST","/buckets",A,h);return{bucket:{...T,name:T?.name||u},created:!0}}async getCors(u,g={}){try{return(await this.request("GET",`${this.bucketPath(u)}/cors`,void 0,g))?.rules??[]}catch(h){if(h instanceof _n&&!(h instanceof Sn)&&(h.hasCode(Kg)||h.status===404))return[];throw h}}async putCors(u,g,h={}){if(g.length===0){await this.request("DELETE",`${this.bucketPath(u)}/cors`,void 0,h);return}await this.request("PUT",`${this.bucketPath(u)}/cors`,{rules:g},h)}async ensureCors(u,g,h={}){let S=await this.getCors(u,h);if(Ps(S,g))return!1;return await this.putCors(u,g,h),!0}async getLifecycle(u,g={}){return(await this.request("GET",`${this.bucketPath(u)}/lifecycle`,void 0,g))?.rules??[]}async putLifecycle(u,g,h={}){await this.request("PUT",`${this.bucketPath(u)}/lifecycle`,{rules:g},h)}async ensureLifecycle(u,g,h={}){let S=await this.getLifecycle(u,h);if(Ns(S,g))return!1;return await this.putLifecycle(u,g,h),!0}async listCustomDomains(u,g={}){return(await this.request("GET",`${this.bucketPath(u)}/domains/custom`,void 0,g))?.domains??[]}async attachCustomDomain(u,g,h={}){return this.request("POST",`${this.bucketPath(u)}/domains/custom`,{domain:g.domain,zoneId:g.zoneId,enabled:g.enabled??!0,minTLS:g.minTLS??"1.2"},h)}async updateCustomDomain(u,g,h,S={}){return this.request("PUT",`${this.bucketPath(u)}/domains/custom/${encodeURIComponent(g)}`,h,S)}async ensureCustomDomain(u,g,h={},S){let A=g.minTLS??"1.2",P=(S??await this.listCustomDomains(u,h)).find((le)=>le.domain.toLowerCase()===g.domain.toLowerCase());if(!P){let le=await this.attachCustomDomain(u,{...g,minTLS:A,enabled:!0},h);return{action:"attached",domain:{...le,domain:le?.domain||g.domain}}}let M=P.minTLS??"1.0";if(P.enabled&&M===A)return{action:"unchanged",domain:P};let W=await this.updateCustomDomain(u,P.domain,{enabled:!0,minTLS:A},h);return{action:"updated",domain:{...P,...W}}}async getManagedDomain(u,g={}){return this.request("GET",`${this.bucketPath(u)}/domains/managed`,void 0,g)}async setManagedDomain(u,g,h={}){let S=await this.getManagedDomain(u,h);if(S?.enabled===g)return{changed:!1,domain:S};return{changed:!0,domain:await this.request("PUT",`${this.bucketPath(u)}/domains/managed`,{enabled:g},h)}}}function op(u){return`https://${u}.r2.cloudflarestorage.com`}async function Zg(u){let{apiToken:g,accountId:h}=u;if(!g)throw Error("r2S3Credentials needs an API token");if(!h)throw Error("r2S3Credentials needs an account id");let S=u.fetch??((P,M)=>fetch(P,M)),A=[],T;for(let P of[`/accounts/${h}/tokens/verify`,"/user/tokens/verify"]){let M=await S(`${np}${P}`,{method:"GET",headers:{Authorization:`Bearer ${g}`}}),W=await M.json().catch(()=>null);if(M.ok&&W?.success&&W.result?.id){T=W.result.id;break}let le=W?.errors?.map((K)=>`${K.code}: ${K.message}`).join(", ");A.push(`${P} -> ${M.status}${le?` (${le})`:""}`)}if(!T)throw Error(`Could not verify the Cloudflare API token to derive R2 credentials: ${A.join("; ")}`);return{accessKeyId:T,secretAccessKey:zg("sha256").update(g).digest("hex"),endpoint:op(h),region:"auto"}}function Oi(u){if(Array.isArray(u))return`[${u.map(Oi).join(",")}]`;if(u&&typeof u==="object")return`{${Object.entries(u).filter(([,h])=>h!==void 0).sort(([h],[S])=>h.localeCompare(S)).map(([h,S])=>`${JSON.stringify(h)}:${Oi(S)}`).join(",")}}`;return JSON.stringify(u)}function rp(u,g){let h=(S)=>S&&S.length>0?[...S].map(String).sort():void 0;return{id:g?u.id:void 0,allowed:{origins:h(u.allowed?.origins),methods:h(u.allowed?.methods?.map((S)=>S.toUpperCase())),headers:h(u.allowed?.headers?.map((S)=>S.toLowerCase()))},exposeHeaders:h(u.exposeHeaders?.map((S)=>S.toLowerCase())),maxAgeSeconds:u.maxAgeSeconds}}function Ps(u,g){if(u.length!==g.length)return!1;return g.every((h,S)=>{let A=h.id!==void 0;return Oi(rp(u[S],A))===Oi(rp(h,A))})}function Ns(u,g){if(u.length!==g.length)return!1;let h=(S)=>Oi([...S].map((A)=>({...A,storageClassTransitions:A.storageClassTransitions?.length?A.storageClassTransitions:void 0})).sort((A,T)=>A.id.localeCompare(T.id)));return h(u)===h(g)}var Jg="r2 cache",Yg=/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;async function Xg(u,g={}){let h=g.log??(()=>{}),S={buckets:[],warnings:[]},A=u.infrastructure?.r2??u.r2,T=Object.entries(A?.buckets??{});if(T.length===0)return h("R2: no buckets declared, skipping."),S;if(!g.apiToken||!g.accountId){let K=[!g.apiToken&&"CLOUDFLARE_API_TOKEN",!g.accountId&&"CLOUDFLARE_ACCOUNT_ID"].filter(Boolean).join(" and "),Ct=`R2: ${T.length} bucket(s) declared but ${K} is not set, skipping.`;return h(Ct),S.warnings.push(Ct),S}let P=g.dryRun===!0,M=new ui({apiToken:g.apiToken,accountId:g.accountId,fetch:g.fetch}),W=new G(g.apiToken,{zoneId:g.zoneId,accountId:g.accountId}),le=new Map;for(let[K,Ct]of T){let Tt=Ct?.name;if(!Tt||!Yg.test(Tt)){S.warnings.push(`R2: bucket '${K}' has an invalid name '${Tt??""}' (3-63 chars: lowercase letters, digits, hyphens), skipping.`);continue}h(`R2: ${P?"checking":"reconciling"} bucket ${Tt}...`);let He={name:Tt,changes:[],domains:[]};if(S.buckets.push(He),await Ph(Ct,{r2:M,cloudflare:W,dryRun:P,summary:He,warnings:S.warnings,claimed:le,log:h}),He.changes.length===0)h(`R2: ${Tt} is in sync.`);else for(let Se of He.changes)h(`R2: ${Tt}: ${Se}`)}return S}async function Ph(u,g){let{r2:h,dryRun:S,summary:A,warnings:T}=g,P=u.name,M={jurisdiction:u.jurisdiction},W=(He,Se)=>{let Ie=S?Se&&`would ${Se}`:He;if(Ie)A.changes.push(Ie)},le;if(S)le=await h.getBucket(P,M)!==null;else{let{created:He}=await h.ensureBucket(P,{locationHint:u.locationHint,jurisdiction:u.jurisdiction});if(le=!0,He)W("created","")}if(!le)W("","create bucket");let K=kh(u.customDomains);await xi(g,`cors on ${P}`,async()=>{if(!u.cors)return;if(!le){W("cors updated","set cors");return}let He=await h.getCors(P,M);if(Ps(He,u.cors))return;if(!S)await h.putCors(P,u.cors,M);W(u.cors.length===0?"cors removed":"cors updated",u.cors.length===0?"remove cors":"update cors")}),await xi(g,`lifecycle on ${P}`,async()=>{if(!u.lifecycle)return;let He=u.lifecycle.map(cp);if(le){let Se=await h.getLifecycle(P,M);if(Ns(Se,He))return}if(!S)await h.putLifecycle(P,He,M);W("lifecycle updated","update lifecycle")}),await xi(g,`r2.dev URL on ${P}`,async()=>{let He=u.publicDevUrl===!0;if(!le){if(He)W("r2.dev URL enabled","enable r2.dev URL");return}if((await h.getManagedDomain(P,M))?.enabled===He)return;if(!S)await h.setManagedDomain(P,He,M);W(He?"r2.dev URL enabled":"r2.dev URL disabled",He?"enable r2.dev URL":"disable r2.dev URL")});let Ct=[];if(K.length>0&&le)try{Ct=await h.listCustomDomains(P,M)}catch(He){if(He instanceof Sn)throw He;T.push(`R2: could not list custom domains on ${P}: ${td(He)}`)}let Tt=new Map;for(let{domain:He,minTLS:Se}of K){let Ie=g.claimed.get(He);if(Ie&&Ie!==P){T.push(`R2: ${He} is declared on both ${Ie} and ${P}; leaving it on ${Ie}.`);continue}g.claimed.set(He,P);let z=Ct.find((V)=>V.domain.toLowerCase()===He),Ye=Se??"1.2";if(z&&z.enabled&&(z.minTLS??"1.0")===Ye){if(A.domains.push({domain:He,status:ed(z)}),z.zoneId)Tt.set(He,z.zoneId);continue}await xi(g,`custom domain ${He} on ${P}`,async()=>{if(z){if(!S)await h.updateCustomDomain(P,z.domain,{enabled:!0,minTLS:Ye},M);if(W(`custom domain ${He} updated`,`update custom domain ${He}`),A.domains.push({domain:He,status:ed({...z,enabled:!0})}),z.zoneId)Tt.set(He,z.zoneId);return}let V=await g.cloudflare.zoneIdFor(He);if(Tt.set(He,V),!S)await h.ensureCustomDomain(P,{domain:He,zoneId:V,minTLS:Ye},M,Ct);W(`custom domain ${He} attached`,`attach custom domain ${He}`),A.domains.push({domain:He,status:S?"not attached":"pending"})},()=>A.domains.push({domain:He,status:"error"}))}if(u.cache&&K.length>0)await Dh(u,K.map((He)=>He.domain).filter((He)=>g.claimed.get(He)===P),Tt,g,W)}async function Dh(u,g,h,S,A){let T=sp(u.cache);if(!T||g.length===0)return;let P=new Map;for(let M of g){let W=h.get(M);if(!W)try{W=await S.cloudflare.zoneIdFor(M)}catch(le){S.warnings.push(`R2: could not resolve the zone for ${M}, cache rule skipped: ${td(le)}`);continue}P.set(W,[...P.get(W)??[],M])}for(let M of P.values()){let W={action:"set_cache_settings",description:`${Wt} ${Jg}`,expression:`(${Kt(M)})`,enabled:!0,action_parameters:T};await xi(S,`cache rule for ${M.join(", ")}`,async()=>{let le=await S.cloudflare.getPhaseRules(M[0],"http_request_cache_settings");if(ap(le,W))return;if(!S.dryRun){let K=await S.cloudflare.putManagedPhaseRules(M[0],"http_request_cache_settings",[W]);if(!K.success)throw Error(K.message||"cache rule write failed")}A(`cache rule updated for ${M.join(", ")}`,`update cache rule for ${M.join(", ")}`)})}}function sp(u){if(u.edgeTtl===void 0&&u.browserTtl===void 0)return null;let g={cache:!0};if(u.edgeTtl!==void 0)g.edge_ttl={mode:"override_origin",default:u.edgeTtl};if(u.browserTtl!==void 0)g.browser_ttl={mode:"override_origin",default:u.browserTtl};return g}function ap(u,g){let h=Jt(g.expression),S=Jn(g.expression),A=(M)=>(M.description||"").startsWith(Wt),T=(M)=>M.action===g.action&&M.description===g.description&&(M.enabled??!0)===(g.enabled??!0)&&Jn(M.expression)===S&&Fs(M.action_parameters)===Fs(g.action_parameters),P=!1;for(let M of u){if(!A(M))continue;let W=Jt(M.expression);if(!h.some((K)=>W.includes(K)))continue;if(T(M)&&h.every((K)=>W.includes(K))&&!P){P=!0;continue}return!1}return P}function cp(u){let g={id:u.id,enabled:u.enabled??!0,conditions:{prefix:u.prefix??""}};if(u.expireAfterDays!==void 0)g.deleteObjectsTransition={condition:{type:"Age",maxAge:du(u.expireAfterDays)}};else if(u.expireOn!==void 0)g.deleteObjectsTransition={condition:{type:"Date",date:u.expireOn}};if(u.abortMultipartUploadsAfterDays!==void 0)g.abortMultipartUploadsTransition={condition:{type:"Age",maxAge:du(u.abortMultipartUploadsAfterDays)}};if(u.infrequentAccessAfterDays!==void 0)g.storageClassTransitions=[{storageClass:"InfrequentAccess",condition:{type:"Age",maxAge:du(u.infrequentAccessAfterDays)}}];return g}function du(u){return Math.round(u*86400)}function kh(u){let g=new Set,h=[];for(let S of u??[]){let A=typeof S==="string"?{domain:S}:S,T=A.domain?.trim().replace(/\.$/,"").toLowerCase();if(!T||g.has(T))continue;g.add(T),h.push({domain:T,minTLS:A.minTLS})}return h}function ed(u){if(!u.enabled)return"disabled";let g=u.status?.ownership,h=u.status?.ssl;if(g==="active"&&h==="active")return"active";for(let S of[g,h])if(S==="error"||S==="blocked"||S==="deactivated")return S;return"pending"}async function xi(u,g,h,S){try{await h()}catch(A){if(A instanceof Sn)throw A;u.warnings.push(`R2: ${g} failed: ${td(A)}`),S?.()}}function td(u){return u instanceof Error?u.message:String(u)}function Fs(u){if(Array.isArray(u))return`[${u.map(Fs).join(",")}]`;if(u&&typeof u==="object")return`{${Object.entries(u).filter(([,h])=>h!==void 0).sort(([h],[S])=>h.localeCompare(S)).map(([h,S])=>`${JSON.stringify(h)}:${Fs(S)}`).join(",")}}`;return JSON.stringify(u)}import{basename as Wh,extname as Vh}from"node:path";import{createHash as Oh}from"node:crypto";var Nh="https://api.cloudflare.com/client/v4",Fn="TS_CLOUD_CONTENT_HASH",oo="2025-09-01",nd="application/javascript+module",xh=10007,Fh=[1e4,9109];class di extends Error{status;errors;constructor(u,g,h=[]){super(u);this.name="WorkersApiError",this.status=g,this.errors=h}hasCode(u){return this.errors.some((g)=>g.code===u)}}class Vs extends di{permission;constructor(u,g,h,S=[]){super(`The Cloudflare API token is missing the "${u}" permission (${g}). Edit the token in the Cloudflare dashboard (My Profile, API Tokens), add it, then re-run the deploy.`,h,S);this.name="WorkersPermissionError",this.permission=u}}class Us{name="cloudflare-workers";apiToken;accountId;fetchImpl;constructor(u){if(!u.apiToken)throw Error("Cloudflare Workers needs an API token");if(!u.accountId)throw Error("Cloudflare Workers needs an account id");this.apiToken=u.apiToken,this.accountId=u.accountId,this.fetchImpl=u.fetch??((g,h)=>fetch(g,h))}async request(u,g,h){let S={Authorization:`Bearer ${this.apiToken}`},A;if(h instanceof FormData)A=h;else if(h!==void 0)S["Content-Type"]="application/json",A=JSON.stringify(h);let T=await this.fetchImpl(`${Nh}/accounts/${this.accountId}/workers${g}`,{method:u,headers:S,body:A}),P=await T.text(),M;try{M=JSON.parse(P)}catch{throw new di(`Cloudflare Workers API returned ${T.status} with a non-JSON body: ${P.slice(0,200)}`,T.status)}if(!M.success){let W=M.errors??[],le=W.map((K)=>`${K.code}: ${K.message}`).join(", ");if(Mh(T.status,W))throw new Vs(Bh(g),`${u} ${g} -> ${T.status}${le?`, ${le}`:""}`,T.status,W);throw new di(`Cloudflare Workers API error (${T.status}) on ${u} ${g}: ${le||P.slice(0,200)}`,T.status,W)}return M.result}scriptPath(u){return`/scripts/${encodeURIComponent(u)}`}async getSettings(u){try{return await this.request("GET",`${this.scriptPath(u)}/settings`)}catch(g){if(g instanceof Vs)throw g;if(g instanceof di&&(g.hasCode(xh)||g.status===404))return null;throw g}}async getScript(u){return this.getSettings(u)}async uploadModuleScript(u,g){return this.request("PUT",this.scriptPath(u),up(g))}async ensureModuleScript(u,g){let h=lo(g),S=await this.getSettings(u);if(S&&js(S,g,h))return{action:"unchanged",hash:h};return await this.uploadModuleScript(u,dp(g,h)),{action:S?"updated":"created",hash:h}}async listCustomDomains(u={}){let g=new URLSearchParams;if(u.service)g.set("service",u.service);if(u.hostname)g.set("hostname",u.hostname);if(u.zoneId)g.set("zone_id",u.zoneId);let h=g.toString(),S=h?`?${h}`:"";return await this.request("GET",`/domains${S}`)??[]}async attachCustomDomain(u,g){let h={hostname:u,service:g.service,environment:g.environment??"production"};if(g.zoneId)h.zone_id=g.zoneId;return this.request("PUT","/domains",h)}async detachCustomDomain(u){await this.request("DELETE",`/domains/${encodeURIComponent(u)}`)}async ensureCustomDomain(u,g,h){let A=(h??await this.listCustomDomains({hostname:u})).find((P)=>P.hostname.toLowerCase()===u.toLowerCase());if(A&&A.service===g.service)return{action:"unchanged",domain:A};if(A)throw Error(`${u} is attached to the Worker '${A.service}'. Detach it there first (Workers & Pages, ${A.service}, Settings, Domains & Routes); ts-cloud does not move it.`);let T=await this.attachCustomDomain(u,g);return{action:"attached",domain:{...T,hostname:T?.hostname||u}}}}function Mh(u,g){if(g.some((h)=>Fh.includes(h.code)))return!0;return(u===401||u===403)&&g.length===0}function Bh(u){if(u.startsWith("/domains"))return"Workers Routes: Edit (zone, for Workers custom domains) and Workers Scripts: Edit (account)";return"Workers Scripts: Edit (account)"}function up(u){if(!u.modules.some((S)=>S.name===u.mainModule))throw Error(`Worker main module '${u.mainModule}' is not one of the uploaded modules`);let g={main_module:u.mainModule,bindings:u.bindings??[],compatibility_date:u.compatibilityDate??oo,compatibility_flags:u.compatibilityFlags??[],keep_bindings:["secret_text"]},h=new FormData;h.append("metadata",JSON.stringify(g));for(let S of u.modules)h.append(S.name,new Blob([S.content],{type:S.type}),S.name);return h}function lo(u){let g={mainModule:u.mainModule,modules:[...u.modules].sort((h,S)=>h.name.localeCompare(S.name)).map((h)=>({name:h.name,type:h.type,sha256:lp(h.content)})),bindings:[...u.bindings??[]].filter((h)=>h.name!==Fn).sort((h,S)=>h.name.localeCompare(S.name)),compatibilityDate:u.compatibilityDate??oo,compatibilityFlags:[...u.compatibilityFlags??[]].sort()};return lp(rd(g))}function dp(u,g=lo(u)){return{...u,bindings:[...(u.bindings??[]).filter((h)=>h.name!==Fn),{type:"plain_text",name:Fn,text:g}]}}function js(u,g,h){let S=u.bindings??[],A=S.find((T)=>T.name===Fn&&T.type==="plain_text");if(!A||A.text!==h)return!1;return(g.bindings??[]).every((T)=>{let P=S.find((W)=>W.name===T.name);if(!P||P.type!==T.type)return!1;let M=T.type==="r2_bucket"?"bucket_name":T.type==="plain_text"?"text":void 0;return!M||P[M]===T[M]})}function lp(u){return Oh("sha256").update(u).digest("hex")}function rd(u){if(Array.isArray(u))return`[${u.map(rd).join(",")}]`;if(u&&typeof u==="object")return`{${Object.entries(u).filter(([,h])=>h!==void 0).sort(([h],[S])=>h.localeCompare(S)).map(([h,S])=>`${JSON.stringify(h)}:${rd(S)}`).join(",")}}`;return JSON.stringify(u)}async function id(u){if(!await Bun.file(u).exists())throw Error(`Worker entry not found: ${u}`);let g;try{g=await Bun.build({entrypoints:[u],target:"browser",format:"esm",minify:!0,external:["cloudflare:*"]})}catch(A){throw Error(`Bundling Worker ${u} failed: ${zh(A)}`)}if(!g.success)throw Error(`Bundling Worker ${u} failed: ${g.logs.map((A)=>String(A.message??A)).join("; ")}`);if(g.outputs.length!==1)throw Error(`Bundling Worker ${u} produced ${g.outputs.length} files (${g.outputs.map((A)=>A.path).join(", ")}); a Worker is uploaded as a single module, so remove the dynamic imports or asset imports that split it.`);let h=g.outputs[0];if(h.kind!=="entry-point")throw Error(`Bundling Worker ${u} produced a ${h.kind} instead of an entry module`);let S=await h.text();return{name:`${Wh(u,Vh(u))}.js`,content:S,type:nd,size:new TextEncoder().encode(S).byteLength}}function zh(u){if(u instanceof AggregateError&&u.errors.length>0)return u.errors.map((g)=>g instanceof Error?g.message:String(g?.message??g)).join("; ");return u instanceof Error?u.message:String(u)}import{isAbsolute as Hh,resolve as Uh}from"node:path";var Gh="https://api.cloudflare.com/client/v4",jh=/^[a-z0-9][a-z0-9_-]{0,62}$/;async function Kh(u,g={}){let h=g.log??(()=>{}),S={workers:[],warnings:[]},A=Object.entries(u.infrastructure?.workers??u.workers??{});if(A.length===0)return h("Workers: none declared, skipping."),S;if(!g.apiToken||!g.accountId){let M=[!g.apiToken&&"CLOUDFLARE_API_TOKEN",!g.accountId&&"CLOUDFLARE_ACCOUNT_ID"].filter(Boolean).join(" and "),W=`Workers: ${A.length} Worker(s) declared but ${M} is not set, skipping.`;return h(W),S.warnings.push(W),S}let T=u.infrastructure?.r2??u.r2,P={workers:new Us({apiToken:g.apiToken,accountId:g.accountId,fetch:g.fetch}),r2:new ui({apiToken:g.apiToken,accountId:g.accountId,fetch:g.fetch}),cloudflare:new G(g.apiToken,{zoneId:g.zoneId,accountId:g.accountId}),apiToken:g.apiToken,fetch:g.fetch??((M,W)=>fetch(M,W)),projectRoot:g.projectRoot??process.cwd(),dryRun:g.dryRun===!0,warnings:S.warnings,log:h,r2ConfigDomains:MS(T),r2Buckets:T,claimed:new Map};for(let[M,W]of A){let le=W?.name;if(!le||!jh.test(le)){S.warnings.push(`Workers: '${M}' has an invalid name '${le??""}' (1-63 chars: lowercase letters, digits, '-' and '_'), skipping.`);continue}if(!W.entry){S.warnings.push(`Workers: ${le} has no entry, skipping.`);continue}h(`Workers: ${P.dryRun?"checking":"reconciling"} ${le}...`);let K={name:le,changes:[],domains:[]};S.workers.push(K),await qh(W,K,P);for(let Ct of K.changes)h(`Workers: ${le}: ${Ct}`)}return S}async function qh(u,g,h){let S=u.name,A=Hh(u.entry)?u.entry:Uh(h.projectRoot,u.entry),T;try{T=await id(A)}catch(M){throw Error(`Worker ${S}: ${gi(M)}`)}g.changes.push(`bundled ${eR(T.size)}`);let P={mainModule:T.name,modules:[{name:T.name,content:T.content,type:T.type}],bindings:pp(u,h),compatibilityDate:u.compatibilityDate??oo,compatibilityFlags:u.compatibilityFlags??[]};if(h.dryRun){let M=await h.workers.getSettings(S);g.changes.push(M&&js(M,P,lo(P))?"unchanged":"would upload")}else{let{action:M}=await h.workers.ensureModuleScript(S,P);g.changes.push(M==="unchanged"?"unchanged":"uploaded")}for(let M of KS(u.customDomains))await Zh(S,M,g,h)}async function Zh(u,g,h,S){let A=(He)=>{h.domains.push({domain:g,status:He})},T=(He)=>{S.warnings.push(`Workers: ${He}`),A("conflict")},P=S.claimed.get(g);if(P&&P!==u){T(`${g} is declared on both ${P} and ${u}; leaving it on ${P}.`);return}S.claimed.set(g,u);let M=S.r2ConfigDomains.get(g);if(M){T(`${g} is also declared as a custom domain of the R2 bucket '${M}' (r2.buckets). A hostname serves either a bucket or a Worker. Remove it from the bucket's customDomains and detach it in the dashboard (R2, ${M}, Settings, Custom Domains) first; to serve the bucket through the Worker, bind it with bindings.r2Buckets instead.`);return}let W=await Yh(S);if(W===null){A("error");return}let le=W.find((He)=>He.hostname.toLowerCase()===g);if(le?.service===u){A("active");return}if(le){T(`${g} is attached to the Worker '${le.service}'. Detach it there first (Workers & Pages, ${le.service}, Settings, Domains & Routes); it is not moved automatically.`);return}let K=(await Xh(S))?.get(g);if(K){T(`${g} is attached to the R2 bucket '${K}' as a custom domain. Detach it first (R2, ${K}, Settings, Custom Domains, then re-run the deploy); it is not detached automatically. To serve the bucket through the Worker, bind it with bindings.r2Buckets.`);return}let Ct;try{Ct=await S.cloudflare.zoneIdFor(g)}catch(He){S.warnings.push(`Workers: could not resolve the zone for ${g}, not attached: ${gi(He)}`),A("error");return}let Tt=await Qh(S,Ct,g);if(Tt.length>0){let He=Tt.map((Se)=>`${Se.type} ${Se.content}`).join(", ");T(`${g} already has a DNS record (${He}). A Worker custom domain creates its own record, so Cloudflare will not attach over it. Delete the record first (DNS, Records), then re-run the deploy; it is not deleted automatically.`);return}if(S.dryRun){h.changes.push(`would attach custom domain ${g}`),A("not attached");return}try{await S.workers.ensureCustomDomain(g,{service:u,zoneId:Ct},W),h.changes.push(`custom domain ${g} attached`),W.push({id:"",hostname:g,service:u,zone_id:Ct}),A("pending")}catch(He){S.warnings.push(`Workers: custom domain ${g} on ${u} failed: ${gi(He)}`),A("error")}}async function Yh(u){if(u.attached!==void 0)return u.attached;try{u.attached=await u.workers.listCustomDomains()}catch(g){u.warnings.push(`Workers: could not list Worker custom domains, none attached: ${gi(g)}`),u.attached=null}return u.attached}async function Xh(u){if(u.r2AccountDomains!==void 0)return u.r2AccountDomains;let g=new Map;try{let h=(await u.r2.listBuckets()).map((S)=>({name:S.name}));for(let S of Object.values(u.r2Buckets?.buckets??{}))if(S.jurisdiction&&S.jurisdiction!=="default")h.push({name:S.name,jurisdiction:S.jurisdiction});for(let S of h){let A=await u.r2.listCustomDomains(S.name,{jurisdiction:S.jurisdiction}).catch(()=>[]);for(let T of A)g.set(T.domain.toLowerCase(),S.name)}u.r2AccountDomains=g}catch(h){u.log(`Workers: could not check R2 custom domains (${gi(h)}); relying on the DNS check.`),u.r2AccountDomains=null}return u.r2AccountDomains}async function Qh(u,g,h){try{let S=await u.fetch(`${Gh}/zones/${g}/dns_records?name=${encodeURIComponent(h)}&per_page=100`,{method:"GET",headers:{Authorization:`Bearer ${u.apiToken}`}}),A=await S.json();if(!A?.success)throw Error(`HTTP ${S.status}`);return(A.result??[]).filter((T)=>T.name?.toLowerCase()===h).map((T)=>({type:T.type??"?",content:T.content??""}))}catch(S){return u.log(`Workers: could not read DNS records for ${h} (${gi(S)}); attaching anyway.`),[]}}function pp(u,g){let h=[],S=new Set,A=(P)=>{if(P===Fn)return g.warnings.push(`Workers: ${u.name}: binding name ${Fn} is reserved for ts-cloud, ignored.`),!1;if(S.has(P))return g.warnings.push(`Workers: ${u.name}: binding ${P} is declared twice; the first one wins.`),!1;return S.add(P),!0},T=new Map;for(let P of Object.values(g.r2Buckets?.buckets??{}))if(P.jurisdiction&&P.jurisdiction!=="default")T.set(P.name,P.jurisdiction);for(let[P,M]of Object.entries(u.bindings?.r2Buckets??{})){if(!A(P))continue;let W=T.get(M);h.push(W?{type:"r2_bucket",name:P,bucket_name:M,jurisdiction:W}:{type:"r2_bucket",name:P,bucket_name:M})}for(let[P,M]of Object.entries(u.bindings?.vars??{}))if(A(P))h.push({type:"plain_text",name:P,text:String(M)});return h}function MS(u){let g=new Map;for(let h of Object.values(u?.buckets??{}))for(let S of h?.customDomains??[]){let A=mp(typeof S==="string"?S:S.domain);if(A)g.set(A,h.name)}return g}function KS(u){return[...new Set((u??[]).map(mp).filter(Boolean))]}function mp(u){return(u??"").trim().replace(/\.$/,"").toLowerCase()}function eR(u){if(u<1024)return`${u} B`;if(u<1048576)return`${(u/1024).toFixed(1)} KB`;return`${(u/1024/1024).toFixed(1)} MB`}function gi(u){return u instanceof Error?u.message:String(u)}export{Fl as ABTestManager,Ee as ACMClient,Or as ACMDnsValidator,bd as ACME_DIRECTORIES,Me as AI,Cu as API_IDEMPOTENCY_TTL_MS,Iu as API_TOKEN_DEFAULT_TTL_MS,bu as API_TOKEN_MAX_TTL_MS,bs as API_VERSION,Te as AUTHORIZATION_CAPABILITIES,hu as AUTH_ACTION_TOKEN_TTL_MS,Au as AUTH_MFA_CHALLENGE_TTL_MS,yu as AUTH_OIDC_TRANSACTION_TTL_MS,ct as AUTH_SESSION_ABSOLUTE_TTL_MS,fu as AUTH_SESSION_IDLE_TTL_MS,$y as AWSAPIError,n as AWSClient,y as AWSCloudFormationClient,b as AWSCloudFrontClient,_l as AWSConfigManager,lb as AWS_PSEUDO_PARAMETERS,Fe as AWS_REGIONS,hg as AcmeClient,on as AlertEvaluator,ji as AlertStore,Fo as AnomalyConfigStore,vl as ApiGateway,i as ApiServiceError,_c as ApplicationAutoScalingClient,pn as Arn,L as AssetHasher,ee as Auth,hs as AuthenticationStore,Xi as AutomationApiService,As as AutomationIdentityStore,Ga as AwsAuroraDataAdapter,Ka as AwsAuroraTransport,gc as AwsDatabaseBackupSource,Mr as AwsDriver,Va as AwsElastiCacheDataAdapter,Wa as AwsElastiCacheTransport,uh as AwsIamCapabilityScanner,Ac as AwsInfrastructureBackupSource,Ha as AwsRdsDataAdapter,za as AwsRdsTransport,vh as AwsRegionalDriver,Sc as AwsSecretsManagerConfigurationBackend,wm as AwsSpendTransport,Ic as AwsSsmConfigurationBackend,Bs as BackupCoordinator,xl as BackupManager,lc as BackupStore,Uf as BatchProcessingManager,qn as BedrockClient,Vn as BedrockRuntimeClient,Pu as BitbucketSourceAdapter,Ml as BlueGreenManager,kf as BounceComplaintHandler,bf as BuildOptimizationManager,Xu as BunDockerRuntime,Rg as BunDockerVolumeRuntime,wg as BunFilesystemArchiveRuntime,we as CDN,aa as CHAIN_HEADER,Wt as CLOUDFLARE_MANAGED_RULE_PREFIX,Sl as CLOUDFRONT_FAILOVER_STATUS_CODES,XC as COMMON_CROSS_ACCOUNT_ROLES,Fn as CONTENT_HASH_BINDING,Ze as CONTROL_PLANE_SCHEMA_VERSION,zm as CREDENTIAL_PROCESS_TIMEOUT_MS,Yo as Cache,cs as CanaryManager,yf as CertificateManager,jA as CloudBlockVolumeDriver,po as CloudDriverFactory,ot as CloudError,Gm as CloudFormationBuilder,y as CloudFormationClient,b as CloudFrontClient,Ll as CloudTrailManager,Ne as CloudWatchLogsClient,Ed as CloudflarePagesProvider,G as CloudflareProvider,iS as CommandHistory,re as Communication,en as ComposeApplicationService,ar as ComposeApplicationStore,Vt as ComprehendClient,a as Compute,Rm as ComputeSpendTransport,ia as ConfigurationError,Cc as ConfigurationService,yc as ConfigurationStore,Hn as ConnectClient,ja as ContainerDataAdapter,Vl as ContainerRegistryManager,hc as ControlPlaneBackupSource,oc as ControlPlaneCleanupDriver,Fi as ControlPlaneStore,Lo as CounterTracker,_t as CredentialError,k as CredentialProcessError,of as CrossRegionReferenceManager,by as DASHBOARD_ENTRY,Cy as DASHBOARD_PORT_BASE,Ey as DASHBOARD_PORT_SPAN,Iy as DASHBOARD_STATE_DIR,ha as DEFAULT_ATTACK_MODE_HOURS,oo as DEFAULT_COMPATIBILITY_DATE,cl as DEFAULT_DDOS_THRESHOLDS,Nm as DEFAULT_FAILOVER_STATUS_CODES,Gd as DEFAULT_METER_MAPPINGS,Qd as DEFAULT_PRICE_BOOK,An as DEFAULT_RECURSION_LIMITS,So as DEFAULT_RPX_CERTS_DIR,ne as DEFAULT_SERVICE_LIMITS,gl as DEFAULT_STATE_DIR,Ug as DEFAULT_STATIC_ASSET_EXTENSIONS,hy as DEFAULT_STORAGE_FAILOVER_STATUS_CODES,sy as DEFAULT_TELEMETRY_POLICY,lm as DEFAULT_THRESHOLDS,QA as DEPLOYMENT_QUEUE_KINDS,oa as DEPTH_HEADER,nn as DETECTABLE_SIGNALS,ju as DIAGNOSTIC_PRESETS,Ff as DLQMonitoringManager,X as DNS,Df as DNSSECManager,Ts as DataServiceLifecycle,Qa as DataServiceStore,gr as Database,hf as DatabaseUserManager,j as DebugLogger,fy as DependencyGraph,te as Deployment,Ly as DeploymentError,Ol as DisasterRecoveryManager,Zn as DnsProviderFactory,br as DockerDataTransport,_a as DockerDiscoveryAdapter,Zr as DockerNamedVolumeDriver,fc as DockerVolumeBackupSource,c as DurableOperationQueue,Po as DurableQueueWorker,Ve as DynamoDBClient,jr as EC2Client,je as ECRClient,pt as ECSClient,kr as EFSClient,Nc as ELBv2Client,KA as ENFORCEMENT_ACTIONS,Vi as ENFORCEMENT_DESCRIPTIONS,_ as ENFORCEMENT_SEVERITY,Mp as ENVIRONMENT_LABEL,Vu as EcsRuntimeAdapter,fa as EdgeRateLimiter,ft as ElastiCacheClient,f as Email,Of as EmailAdvanced,Nf as EmailAnalyticsManager,Wn as EmailClient,_f as EmailHandlers,xf as EmailTemplateManager,Ja as EncryptedDataSecretStore,My as ErrorCodes,Dr as EventBridgeClient,bh as EventBridgeJobAdapter,bc as ExternalConfigurationBackend,Xo as FAILOVER_REPLICA_POLICY_SID,Mf as FIFOQueueManager,tR as FLY_MACHINES_API,ym as FOCUS_VERSION,Rl as FileCache,it as FileSystem,pc as FilesystemBackupSource,Ya as FleetService,Xa as FleetStore,Io as FlyApiError,hp as FlyClient,e as Fn,ku as GiteaSourceAdapter,Ru as GithubSourceAdapter,Du as GitlabSourceAdapter,af as GlobalResourceManager,ze as GoDaddyProvider,$l as GuardDutyManager,wt as HOST_SITES_DIR,Hy as HashCache,Yf as HealthCheckManager,tt as HealthCheckRunner,Gr as HetznerClient,xe as HetznerDriver,Be as IAMClient,qc as IPV6_EXCLUDED_HOST_LABELS,If as ImageScanningManager,wp as InfrastructureGenerator,kd as InvalidOperationTransitionError,xt as JobLoader,Ch as JobProviderReconciler,Fa as JobService,Ua as JobStore,Rr as KendraClient,Jf as LARAVEL_SERVERLESS_BUILD_STEPS,w as LambdaClient,vf as LambdaConcurrencyManager,Rf as LambdaDLQManager,Tf as LambdaDestinationsManager,Cf as LambdaLayersManager,qu as LambdaRuntimeAdapter,wf as LambdaVPCManager,Ef as LambdaVersionsManager,Ln as LocalEncryptedConfigurationBackend,mc as LogicalDatabaseBackupSource,Ql as LogsManager,TS as MANAGED_NODE_VERSIONS,md as MANAGEMENT_DASHBOARD_SITE,Vm as MANAGEMENT_DASHBOARD_SITE_PREFIX,ms as MAX_CONTROL_HOURS,Nd as MAX_CONTROL_PLANE_ERROR_BYTES,sm as MAX_CONTROL_PLANE_JSON_BYTES,xo as METERS,zA as METER_KEYS,WA as MICRO_CENTS,um as MIN_PROJECTION_CONFIDENCE,qg as MTA_STS_PORT,sc as MaintenanceService,ic as MaintenanceStore,fe as Messaging,pf as MetricsManager,mf as MigrationManager,ef as MockCloudFormation,rf as MockDynamoDB,tf as MockS3,Zo as Monitoring,lf as MultiAccountManager,sf as MultiRegionManager,Yy as MultiStepProgress,I as Network,Zf as NetworkSecurityManager,zi as NotificationRouter,Om as ORIGIN_CONNECTION_ATTEMPTS,_m as ORIGIN_CONNECTION_TIMEOUT,Bm as ORIGIN_GROUP_ALLOWED_METHODS,ab as ORIGIN_GROUP_ID_SUFFIX,xm as ORIGIN_GROUP_SELECTION_CRITERIA,Bc as OpenSearchClient,Rt as OptimisticConcurrencyError,uf as OrganizationManager,Lg as PAGES_MAX_FILE_BYTES,_S as PHP_DEFAULT_EXCLUDES,wS as PHP_LAYER_EXTENSIONS,$p as PROJECT_LABEL,At as PROXIABLE_RECORD_TYPES,D as ParameterStore,ff as PerformanceManager,me as Permissions,xc as PersonalizeClient,st as Phone,Lf as PhoneAdvanced,Qm as PhoneHandlers,ec as PlacementService,Za as PlacementStore,jn as PollyClient,be as PorkbunProvider,Tn as PreDeployScanner,Nl as PreviewEnvironmentManager,Yr as PreviewEnvironmentService,et as PreviewEnvironmentStore,Bl as PreviewNotificationService,Xy as ProgressBar,Ul as ProgressiveDeploymentManager,Aa as ProtectionControlStore,my as Pseudo,Y as Queue,Ui as QueueCancellationError,Qf as QueueManagementManager,JI as QueuePresets,Do as QueueTimeoutError,_n as R2ApiError,Sn as R2NotEnabledError,ui as R2Provider,ip as R2_NOT_ENABLED_ERROR_CODE,ou as RATE_LIMIT_KEY_SEPARATOR,se as RDSClient,oS as RECOMMENDED_ACCOUNT_STRUCTURES,JC as RECOMMENDED_SCPS,ah as RELEASE_QUEUE_KINDS,tS as REPL,nS as REPLCommandBuilder,rS as REPLContext,Fp as ROLE_LABEL,Gy as RateLimiter,XI as RealtimePresets,Sm as RecordingSpendTransport,ls as RecursionGuard,Ot as Redirects,cf as RegionPairManager,rc as RegionService,tc as RegionStore,nt as Registry,qt as RekognitionClient,Lt as ReleaseService,wa as ReleaseStore,Eh as RemoteBuildService,Hl as ReplicaManager,eh as ResourceManagementManager,ue as RetryableOperationError,E as Route53Client,kc as Route53DomainsClient,oe as Route53Provider,Pf as Route53ResolverManager,jl as Route53RoutingManager,Pe as RuntimeOperationService,$a as RuntimeStreamRegistry,uc as S3BackupDestinationAdapter,o as S3Client,q as S3Error,xu as SECRET_PATTERNS,hm as SENSITIVITY_THRESHOLDS,ht as SESClient,Z as SMS,mt as SNSClient,Tm as SPEND_CYCLE_SECONDS,rl as SPEND_GATE_SETTING,dy as SPEND_LEASE_SETTING,ye as SQSClient,Oe as SSH_BOOTSTRAP_VERSION,yi as SSH_PREFLIGHT_SCRIPT,Le as SSMClient,Pm as STATE_DIR_ENV_VAR,Ei as STATIC_SITE_ZONE_SETTINGS,bl as STORAGE_FAILOVER_METADATA_KEY,ke as STSClient,zn as SchedulerClient,pr as Search,wn as SecretFindingScanner,ts as Secrets,Gl as SecretsManager,ie as SecretsManagerClient,Af as SecretsRotationManager,v as Security,hr as SecurityHubManager,vn as SecurityPostureStore,$t as SecurityScannerRunner,Sf as SecurityScanningManager,Bf as SenderReputationManager,Ih as ServerCronJobAdapter,qa as ServerDataAdapter,Bo as ServerPathVolumeDriver,ql as ServiceMeshManager,El as Sftp,Qo as SignalSource,$f as SmsAdvanced,zt as SmsClient,Hm as SmsHandlers,Mc as SmsVoiceClient,Ra as SourceConnectionStore,Sr as SourceProviderError,nl as SpendCapError,sn as SpendGate,Em as SpendLoopLease,jo as SpendNotificationRouter,vm as SpendRunner,dr as SpendService,Hi as SpendStore,Zy as Spinner,Vr as SshDriver,Pn as SshFleetDriver,df as StackDependencyManager,db as StacksIntegration,Xf as StaticSiteManager,$e as Storage,nu as StorageAdvancedManager,Kn as SupportClient,Ar as SyntheticsManager,Yu as SystemFleetSshTransport,Et as SystemSshTransport,La as SystemdDiscoveryAdapter,ca as TRACE_HEADER,eS as TaskList,kt as TelemetryStore,Al as TemplateBuilder,Dl as TemplateCache,jt as TextractClient,Gn as TranscribeClient,wr as TranslateClient,Rh as TransportDisasterRecoveryDriver,wh as TransportPlatformMaintenanceDriver,ws as TransportRemoteBuildDriver,xa as TrivyImageScanner,Ta as TsCloudApiError,Eu as TsCloudClient,cy as URGENT_SPEND_EVENTS,gm as USER_VISIBLE_ACTIONS,nc as UnavailableRegionalDriver,Ce as UnifiedDnsValidator,To as UnsupportedSchemaVersionError,Wm as ValidationError,Pr as VoiceClient,No as VolumeService,or as VolumeStore,nd as WORKER_MODULE_TYPE,di as WorkersApiError,Vs as WorkersPermissionError,Us as WorkersProvider,Sy as Workflow,gf as XRayManager,fS as abTestManager,vu as activateImmutableRelease,dg as addDns01ChallengeRecord,Nn as addSiteToCloudConfig,Ft as ageSeconds,qd as aggregateDeltas,rm as allocateSitePorts,Um as amazonLinuxImageId,yy as amazonLinuxImageParameter,sb as analyzeStackDiff,qi as anomalyOptionsForSignal,ya as applyControlsToDdos,uu as applyControlsToRateLimits,Xs as applyDeclaredZoneSettings,mi as applyHetznerVolumes,dn as applyMitigationFactor,CA as applyPlan,to as applyZoneMigration,zl as artifactKey,gu as assertComputeResidency,Nt as assertOriginGroupMethods,ys as assertReleaseStrategy,iy as assertSpendAllows,RA as attachConflicts,DA as attachIsViable,wA as attachPreconditions,Mu as attachProvenanceToRelease,Oa as attachSbomToRelease,Lu as attachVulnerabilitySummary,sh as authEncryptionKeyFile,O as authorizeOrganization,yh as authorizeRuntimePath,wC as autocomplete,dS as awsConfigManager,vg as backupCredentialStatus,aS as backupManager,Xb as batch,QE as batchProcessingManager,Ea as beginOidcAuthorization,pS as blueGreenManager,_E as bounceComplaintHandler,Dt as budgetWindow,Rc as buildAndPushServerlessImage,VE as buildBunRuntimeLayerZip,OA as buildCertificatePackScript,NA as buildCertificateStateScript,_A as buildCertificateUnpackScript,Tl as buildCloudFormationTemplate,cd as buildCloudInitFirstBoot,_d as buildComposeLogsCommand,KI as buildComposeRuntimeCommand,WI as buildComposeScaleCommand,Ld as buildComposeShellCommand,ni as buildComputeStorageInstallScript,Fr as buildComputeStorageSiteScript,Zp as buildDrainSourceScript,pA as buildDrainedSiteScanScript,Cl as buildFailoverReplicaPolicyStatement,Er as buildFunctionEnv,Yp as buildHealthGateScript,si as buildHostCleanupDeployScript,co as buildHostCleanupInstallScript,ii as buildHostCleanupScript,$i as buildHostSitePortsScript,Zi as buildMailEnv,ho as buildManagementDashboardArtifact,hR as buildMediaCdnPlan,Vg as buildMtaStsPolicy,Jh as buildMtaStsServer,GE as buildNodeRuntimeLayerZip,wE as buildOptimizationManager,Il as buildOriginGroup,gn as buildOriginGroups,Qc as buildOriginGuardRule,qp as buildPauseWorkersScript,KE as buildPhpRuntimeLayerZip,Fm as buildReplicationConfiguration,Mm as buildReplicationRole,Wp as buildRestoreScript,x as buildRpxConfig,_e as buildRpxFragmentRefreshScript,Op as buildRpxUpgradeScript,vA as buildSetHostnameScript,ai as buildSiteDeployScript,Kp as buildSnapshotScript,em as buildSourceStateScript,li as buildSshArgs,hi as buildSshBootstrapScript,Uc as buildStaticSiteCacheRules,ci as buildStaticSiteDeployScript,Jp as buildTargetStateScript,up as buildUploadForm,jp as buildWorkersStateScript,id as bundleWorker,Uu as bytes,sp as cacheRuleParameters,mS as canaryManager,kn as canonicalJson,Mt as capabilities,Cr as capacity,oC as categorizeChanges,EE as certificateManager,xA as certificatesMatch,Qb as checkIAMPermissions,jf as checkInvocation,Gb as checkServiceQuotas,Vy as chunk,NR as cleanupDns01Challenge,Lb as clearCredentialProcessCache,VS as clearSigningKeyCache,Pa as cloneSourceBinding,qy as cloudConfigSchema,Ap as cloudDrivers,lS as cloudTrailManager,tp as collectAssets,ga as collectPhpAppEntries,Qg as collectServerDnsDomains,om as completeComposeVolumeDeletion,va as completeOidcAuthorization,Db as composePresets,Ro as composeProjectName,Wl as composeServerlessAppTemplate,uy as compositeSpendTransport,Gt as compressBackup,WS as config,Fc as configOrigins,Wu as connectionGuidance,bS as containerRegistryManager,FA as controlPlaneDatabaseFile,Dd as controlPlaneMigrations,hb as createApiBackendPreset,Ba as createApiV1Handler,iI as createAsgBuildDriver,Tg as createBackupQueueHandlers,OS as createCliHandler,U as createCloudDriver,pd as createCloudFrontPolicy,$b as createCredentialProvider,wb as createDashboardSite,bb as createDataPipelinePreset,Ju as createDataServiceQueueHandlers,$d as createDeploymentQueueHandlers,B as createDnsProvider,nI as createEcsBuildDriver,il as createEnforcementHandlers,Jm as createError,wl as createExistingStaticFullStackPreset,Zu as createFleetQueueHandlers,fb as createFullStackAppPreset,io as createGoDaddyValidator,zE as createHandlers,BS as createHttpHandler,yb as createJamstackPreset,Ku as createJobQueueHandlers,vb as createLaravelPreset,Cb as createMLApiPreset,og as createMaintenanceQueueHandlers,mR as createMediaAccessToken,Sb as createMicroservicesPreset,mC as createMockAWS,gb as createNodeJsServerPreset,pb as createNodeJsServerlessPreset,Gs as createObjectStorageClient,no as createPorkbunValidator,Pb as createPreset,pa as createPresignedUrl,FS as createPresignedUrlAsync,xS as createQueueHandler,Ib as createRealtimeAppPreset,tg as createRegionQueueHandlers,$u as createReleaseProvenance,wu as createReleaseQueueHandlers,tI as createRemoteBuildQueueHandlers,so as createRoute53Validator,Ah as createRuntimeAdapters,_y as createS3Client,rI as createServerBuildDriver,Tb as createServerlessLaravelPreset,mb as createServerlessNodePreset,Fg as createSmsInfrastructure,lt as createSourceAdapter,ub as createStaticSitePreset,Eb as createTraditionalWebAppPreset,Md as createVolumeQueueHandlers,Ab as createWordPressPreset,at as createZip,OC as crossRegionReferenceManager,$n as dashboardActions,fo as dashboardCredentialsFile,Dn as dataServiceCapabilities,bE as databaseUserManager,mu as decodeBase32,Ge as decompressBackup,$h as decryptBackup,Sa as defaultConfig,Zm as defaultLocalConfig,cu as defaultRateLimitRules,xr as delegateZone,Ws as delegateZoneFromConfig,Ig as deleteFailoverReplicaBuckets,Tc as deleteStaticSite,hd as deployAllComputeSites,cc as deployServerlessApp,bg as deploySite,Ao as deploySiteRelease,Ls as deployStaticSite,wc as deployStaticSiteFull,Mn as deployStaticSiteWithExternalDns,Un as deployStaticSiteWithExternalDnsFull,oR as deployToFly,hl as deploymentCoexistenceError,vy as deriveManagementDashboardPort,Js as describeDelegation,lu as describePosture,Ys as describeZoneSettings,Sh as desiredJobProviderState,fm as detectAnomalies,km as detectDeploymentTargets,Hc as detectDnsProvider,Zd as detectLatestAnomaly,Fb as detectMisconfigurations,iu as detectServiceRegion,xd as diffCompose,nC as diffTemplates,Da as discoverGitRefs,Ca as discoverOidcProvider,Qu as discoverRuntimeInventory,UE as dlqMonitoringManager,Gc as dnsProviders,xE as dnssecManager,mh as dockerWorkloads,Rs as documentDigest,ed as domainStatus,cS as drManager,Je as driverStatePath,Hu as ecsWorkloads,sr as effectiveCapabilities,LE as emailAnalyticsManager,ME as emailTemplateManager,fs as encodeBase32,Lh as encryptBackup,Fu as ensureDefaultSecurityPolicies,Ag as ensureFailoverReplicaBuckets,Ti as ensureFirewall,Pi as ensureManagementDashboard,wi as ensureServer,vi as ensureSshKey,de as escapeSecLang,zd as evaluateBudget,Ii as evaluatePreflight,qo as evaluateTelemetryAlertRules,zu as eventBridgeScheduleInput,NS as eventToRequest,ty as ewma,Bd as exportCompose,Zs as exportRoute53Zone,jm as extendPreset,FE as fifoQueueManager,Jb as findChangedFiles,cm as findMapping,am as findPriceEntry,iR as flyDeployOptions,sd as flyIpType,Cp as flyMachineConfig,tE as formatAccountStructure,PA as formatAttachPlan,yC as formatBytes,an as formatCents,ob as formatDiff,fA as formatDrainedSiteRefusal,SC as formatDuration,DC as formatFlagSuggestions,NC as formatHistory,BC as formatHistoryStats,SA as formatInventory,bC as formatKeyValue,IC as formatList,bA as formatPlan,Ci as formatPreflightFindings,AC as formatProgressBar,sS as formatRegion,jC as formatRegionList,lh as formatScanResults,CC as formatSuggestion,fC as formatTable,rn as formatTimeToExhaustion,hC as formatTree,ky as fromCredentialProcess,Ny as fromEC2Metadata,By as fromECSMetadata,Dy as fromEnvironment,Py as fromSharedCredentials,xy as fromWebIdentity,Yl as functionFingerprint,Co as gatewayHostnames,Xl as generateAppImageDockerfile,fE as generateApprovalConfig,Hf as generateBootstrap,Ky as generateCleanupWorkflow,Wy as generateCostReportWorkflow,ZC as generateCrossAccountRoleCF,Ou as generateCycloneDxSbom,gE as generateDeploymentConfig,aE as generateDeploymentPipeline,rE as generateDeploymentWorkflow,vc as generateExternalDnsStaticSiteTemplate,ER as generateHttpsServerCode,_u as generateImageSbom,vR as generateLetsEncryptUserData,t as generateLogicalId,uE as generateManualPipeline,oE as generateMatrixWorkflow,pE as generateMultiEnvConfig,cE as generateMultiEnvPipeline,nE as generateMultiEnvWorkflow,iE as generatePRPreviewWorkflow,hE as generateParallelConfig,Wf as generatePhpFpmConfig,Gf as generatePhpLayerDockerfile,dE as generatePreviewPipeline,zy as generatePreviewWorkflow,r as generateResourceName,mE as generateScheduledConfig,lE as generateScheduledPipeline,sE as generateScheduledWorkflow,_s as generateStaticSiteTemplate,F as generateUbuntuAppCloudInit,Km as getAccountId,MC as getAllRegions,GC as getClosestRegion,PC as getCommandUsage,UA as getComposeTemplate,rh as getConfig,vC as getContextualHelp,os as getCredentials,aC as getDeploymentStrategy,cC as getDiffStats,iC as getDiffSummary,Fy as getErrorDetails,kl as getLocalConfig,gC as getLocalCredentials,uC as getLocalEndpoint,pC as getLocalEnvVars,Vb as getQuotaUsageSummary,YC as getRecommendedStructure,as as getRegion,zC as getRegionStats,UC as getRegionsByCompliance,FC as getRegionsByLocation,QC as getRegionsByPricingTier,Uy as getRequiredPermissions,qS as getSigningKeyCacheSize,Mg as getSmsInfrastructureStatus,YI as getTimestamp,ma as globMatches,yo as globToRegex,_C as globalResourceManager,Gi as governingLimitCents,yg as grantFailoverReplicaAccess,uS as guardDutyManager,zo as handleSpendRequest,bo as hasAutoWwwVariant,mr as hasManagementDashboardSite,Rd as hasProxyUpstream,jb as hashBuffer,zb as hashDirectory,Xm as hashFile,Kb as hashManifest,Ym as hashString,JE as healthCheckManager,ei as hostAcceptsIpv6,Kt as hostCondition,Ia as hotp,tn as hourBucket,ey as hourBucketsBetween,TE as imageScanningManager,ks as infraEnvFromOutputs,eu as inspectInvocation,PS as installRecursionFetch,Ms as invalidateCache,qf as invocationContext,Ay as isArmInstanceType,EC as isLikelyTypo,Lr as isLocalDatabase,lC as isLocalDevelopment,fr as isManagementDashboardSiteName,Nr as isNameserverRegistrar,jS as isNodeCryptoAvailable,Yd as isOperationAllowed,Vo as isQuietHours,Jo as isS3RestEndpoint,Ni as isSafePattern,ml as isStateDirConfigured,fl as isStatePath,cn as isValidCidr,HC as isValidRegion,Qi as isValidTimeZone,zS as isWebCryptoAvailable,Br as isZoneHost,Rn as jobProviderCapability,Hs as keyMatchesFilters,PE as lambdaConcurrencyManager,BE as lambdaDLQManager,kE as lambdaDestinationsManager,RE as lambdaLayersManager,NE as lambdaVPCManager,DE as lambdaVersionsManager,Gu as lambdaWorkloads,tu as laravelServerlessEnvDefaults,Xr as listComposeTemplates,Is as listSourceReferences,pu as loadCloudConfig,ss as loadProfileCredentials,ra as loadProfileCredentialsSync,is as loadStaticProfileCredentials,lr as loadTelemetryPolicy,AS as logsManager,el as lookbackHoursForSignal,py as mailEnabled,rt as mailFirewallPorts,un as mailWebmailSites,Th as maintenanceWindowOpen,th as makeAWSRequest,QS as makeAWSRequestAsync,US as makeAWSRequestOnce,ap as managedRuleInSync,qm as managementDashboardSiteName,rg as manifestDigest,In as matchTotpCounter,fR as mediaObjectHeaders,Mo as median,pm as medianAbsoluteDeviation,Kd as mergeDecisions,mn as mergeInfrastructure,YA as mergePriceBooks,Vd as meterTelemetry,yE as metricsManager,xg as migrateObjectStorage,SE as migrationManager,KC as multiAccountManager,xC as multiRegionManager,xR as needsRenewal,XE as networkSecurityManager,Ut as nextScheduleRuns,ae as normalizePublicIpv6,Ue as normalizeRuntimeStatus,ut as normalizeScheduleExpression,ch as normalizeSourceEvent,We as normalizeSshPublicKey,Im as normalizeTelemetryPolicy,Li as occupiedHostPorts,Na as openApiDocument,Ho as optionsForSignal,WC as organizationManager,ru as packagePhpApp,hn as packageServerlessApp,ep as pagesAssetHash,Bg as pagesDnsRecord,Pl as parallel,Yb as parallelMap,Zb as parallelWithRetry,BA as parseCertificateState,Zl as parseChain,Jr as parseCompose,jI as parseComposeCatalog,ph as parseDockerInspect,Dg as parseDotenv,mA as parseDrainedSites,Mi as parseHostSiteFragments,GS as parseJSONResponse,Si as parsePreflightFacts,tm as parseSourceDrained,fh as parseSystemdRecords,Xp as parseTargetReady,wd as parseUpstreamPort,zp as parseWorkersPaused,HS as parseXMLResponse,Wi as pathTemplate,vd as pendingSteps,$o as percentOfLimit,IE as performanceManager,Jl as phpLayerBuildStage,RS as phpLayerPackages,yn as phpRuntimeLayerAssets,zI as planComposeTemplateUpgrade,Wd as planEnforcement,pi as planHetznerVolumes,Gp as planIsDestructive,Dm as planMitigation,EA as planServerRename,LA as planSiteMove,eo as planZoneMigration,Xt as policyTag,bi as preflightFailed,jy as previewManager,Jy as previewNotifications,Qt as previewSchedule,ZA as previousBudgetWindow,Oo as priceUsage,JA as priceUsageLines,AA as probeHostRoutes,Xc as probeOriginTls,rC as processInChunks,Nu as processSourceWebhook,Es as productionChangeReview,ta as profileCredentialProcess,hS as progressiveDeploymentManager,jd as projectSpend,da as propagationHeaders,Dc as providerEndpoint,ng as publicKeyFingerprint,Xn as qualifyName,HE as queueManagementManager,Wb as quickHash,op as r2Endpoint,Zg as r2S3Credentials,au as rateLimitHeaders,ps as rateLimitKey,Xe as readDriverState,fn as readSharedProfile,yA as reconcile,Gg as reconcileAddressRecords,Eg as reconcileCloudflareCdn,Kh as reconcileCloudflareWorkers,Ks as reconcileDeclaredRecords,eI as reconcileJobObservation,Xg as reconcileR2Buckets,En as reconcileSourceWebhook,Cs as recordDashboardHostPosture,XS as recordPreDeploySecretScan,YS as recordSkippedSecretScan,la as recursionBlockedResponse,Qe as redactRuntimeConfig,al as redactTelemetryText,Ki as redactTelemetryValue,xn as redeployServerlessApp,LC as regionPairManager,Cn as releaseStrategyCapabilities,oh as releaseTrafficPlan,ki as reloadRpxGateway,Qs as remapKey,Hg as removeAddressRecords,mg as removeDns01ChallengeRecord,Bn as removeSiteFromCloudConfig,Bu as removeSourceWebhook,Vc as removeStaleServerAddressRecords,Od as renderComposeTemplate,Yi as renderDdosInstallScript,ul as renderNftablesRuleset,vs as renderServerCron,ll as renderSysctlFile,yd as renderWafConfig,Bi as renderWafInstallScript,Ad as renewRpxCertificates,SS as replicaManager,gy as requestHash,sC as requiresReplacement,qE as resetRecursionRuntime,jE as resolveApp,p as resolveAppDatabase,TA as resolveAttachTarget,dt as resolveAuthEncryptionKey,ge as resolveCloudProvider,Ng as resolveCloudflareCdnPlan,ri as resolveComputeVolumes,na as resolveCredentials,Os as resolveDashboardAction,fd as resolveDashboardAuth,Ty as resolveDashboardDomain,wy as resolveDashboardDomains,zs as resolveDeclaredRecords,ib as resolveDeployBucketName,J as resolveDeploymentMode,oi as resolveExecStart,Wo as resolveFailoverStatusCodes,Zc as resolveHetznerApiToken,Qr as resolveHostCleanupConfig,R as resolveMailProxy,C as resolveMailService,Rb as resolveManagementDashboardSite,rs as resolveManagementDashboardSites,Pc as resolveObjectStorage,pe as resolveOriginConnection,IA as resolvePlan,Re as resolveProfileName,m as resolveProjectStackName,Wr as resolveProxyUpstreams,Kl as resolveQueueNames,HA as resolveQueuedDeploymentCommand,yr as resolveQueues,DS as resolveRecursionConfig,Oy as resolveRegion,Ae as resolveRuntimeInventory,ur as resolveServerlessAppStackName,yl as resolveServerlessArtifactBucketName,es as resolveServerlessAssetBucketName,ds as resolveServerlessRuntime,rb as resolveSiteBucketName,vo as resolveSiteDeployTarget,d as resolveSiteKind,tb as resolveSiteResourceName,eb as resolveSiteStackName,Ai as resolveSshSettings,l as resolveStatePath,nb as resolveStorageBucketName,Lm as resolveStorageFailover,rr as resolveUiSource,Id as resolveZoneApex,YE as resourceManagementManager,Kf as responseToResult,mm as robustZScore,ir as roleCapabilities,Tu as rollbackImmutableRelease,On as rollbackServerlessApp,XA as roundCents,OE as route53ResolverManager,ES as route53RoutingManager,Am as routeMatches,Up as routesFromFragments,gs as ruleMatches,vS as runBuildHooks,Jd as runEnforcement,ua as runPhpBuildHooks,gt as runRemoteCommand,De as runtimeId,qe as s3OriginBucket,$m as s3RegionalDomain,Ps as sameCorsRules,Ns as sameLifecycleRules,Yn as sameNameservers,xs as sanitizeCloudConfig,s as sanitizeControlPlaneValue,ZI as sanitizeName,Su as sanitizeOidcReturnPath,sl as saveTelemetryPolicy,dh as scanForSecrets,Pd as scopeContains,Pt as scopeKey,Wc as scpUpload,TC as searchCommands,wo as searchControlPlane,IS as secretsManager,CE as secretsRotationManager,ZS as secureContainerRelease,gS as securityHubManager,vE as securityScanningManager,he as securityScope,Sg as seedFailoverReplicas,bn as sendAuthenticationEmail,$E as senderReputationManager,eC as sequence,Pg as serializeDotenv,Ri as serverPublicIpv4,CS as serviceMeshManager,pg as setAttachToInCloudConfig,vr as setMaintenance,Ds as setSitePropertyInCloudConfig,pl as setStateDir,OR as setupDns01Challenge,$c as setupSmsInfrastructure,sa as sha256,ea as sharedConfigPath,ns as sharedCredentialsPath,kS as sharedRecursionGuard,Vf as sharedRuntimeLoop,im as shipsARelease,pR as signCloudFrontCookies,dR as signCloudFrontUrl,N as signRequest,su as signRequestAsync,Uo as signalDefinition,Q as siteInstallBase,_i as siteMoveArchivePath,kA as siteMoveCertArchivePath,Td as siteUnitGlob,Cm as spendNotificationText,Ko as spendOpenApiPaths,Ry as splitCommandLine,Hr as sshExec,ve as sshExecOrThrow,$C as stackDependencyManager,Ec as startLocalDashboardServer,ly as startSpendLoop,ln as stateDir,H as statePath,WE as staticSiteManager,LS as storageAdvancedManager,cb as storageFailoverReplicasFromTemplate,ry as streamFocusJsonl,Xd as strongestActiveAction,nf as suggestCommand,RC as suggestFlags,Hb as suggestIAMPolicy,qb as suggestQuotaIncrease,qC as suggestRegionPairs,VC as suggestRegions,Ss as syncSourceRepositories,Fd as synchronizeComposeVolumes,kg as synchronizeConfiguredConfiguration,Ma as synchronizeConfiguredJobs,yS as syntheticsManager,dl as sysctlHardening,hh as systemdWorkloads,ay as telemetryBucketLabel,Ji as telemetryCursor,ol as telemetryEstimatedMonthlyCost,oy as telemetryPercentile,bm as telemetryPolicyKey,Qy as templateCache,Qp as tenantsOf,ka as testSourceConnection,Ud as tieredMicroCents,cp as toApiLifecycleRule,Yc as toCloudflareZoneSettings,Go as toFocusJsonl,tl as toFocusRecords,hA as toInventoryServer,nh as totp,ba as totpUri,Hp as unaccountedSites,gh as unsupportedCapabilities,GA as unsupportedVolumeCapabilities,sg as updateCompatibility,ag as updateSiteInCloudConfig,$s as uploadStaticFiles,dm as usageDeltaKey,cr as utcInstantOfLocal,eE as validateAccountStructure,dC as validateAgainstSchema,qA as validateAttachment,_h as validateBackupDestination,kC as validateCommand,Mb as validateConfiguration,Ub as validateCredentials,nm as validateDeploymentConfig,VA as validateMountPath,Bt as validateOriginGroups,Lp as validateResourceLimits,Vp as validateServerName,Ip as validateTemplate,xp as validateTemplateSize,jc as verifyAddressRecord,JS as verifyArtifactSignature,gR as verifyMediaAccessToken,ih as verifyTotp,ig as verifyUpdateSignature,ro as verifyZoneParity,ko as volumeCapabilities,Np as wafEventTelemetry,Jc as waitForCloudInit,St as waitForSsh,Ir as webhookEndpoint,Ob as withCDN,xb as withCache,dp as withContentHash,Bb as withDatabase,zf as withInvocation,kb as withMonitoring,_b as withQueue,Nb as withSecurity,tC as withTimeout,pp as workerBindings,lo as workerContentHash,js as workerInSync,ce as wrapCloudInitUserData,bt as writeDriverState,AE as xrayManager,eg as zeroCapacity,ny as zeroFill,Hd as zoneOffsetMs,_o as zonedParts};
|