@stacksjs/ts-cloud 0.12.5 → 0.12.6

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.
Files changed (66) hide show
  1. package/dist/bin/{chunk-w8gjmqj4.js → chunk-j91jb1nx.js} +1 -1
  2. package/dist/bin/{chunk-jgftf5md.js → chunk-xdqq98wp.js} +48 -46
  3. package/dist/bin/{chunk-3xc7h1xh.js → chunk-z3qz1713.js} +1 -1
  4. package/dist/bin/cli.js +4 -4
  5. package/dist/bin/dashboard-server.js +1 -1
  6. package/dist/{chunk-c477dzxb.js → chunk-fxxjtz86.js} +1 -1
  7. package/dist/{chunk-rn0ggzv1.js → chunk-kp6mgc9b.js} +62 -60
  8. package/dist/deploy/index.js +1 -1
  9. package/dist/drivers/hetzner/driver.d.ts +14 -0
  10. package/dist/drivers/index.js +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/ui/404.html +1 -1
  13. package/dist/ui/accept-invitation.html +1 -1
  14. package/dist/ui/access-denied.html +1 -1
  15. package/dist/ui/account/automation.html +6 -6
  16. package/dist/ui/account/security.html +2 -2
  17. package/dist/ui/applications/compose.html +6 -6
  18. package/dist/ui/applications/new.html +1 -1
  19. package/dist/ui/data/backups.html +6 -6
  20. package/dist/ui/data/services.html +6 -6
  21. package/dist/ui/data/volumes.html +6 -6
  22. package/dist/ui/index.html +6 -6
  23. package/dist/ui/infrastructure/topology.html +6 -6
  24. package/dist/ui/integrations.html +2 -2
  25. package/dist/ui/operations/alerts.html +6 -6
  26. package/dist/ui/operations/configuration.html +6 -6
  27. package/dist/ui/operations/jobs.html +5 -5
  28. package/dist/ui/operations/maintenance.html +6 -6
  29. package/dist/ui/operations/observability.html +6 -6
  30. package/dist/ui/operations/previews.html +6 -6
  31. package/dist/ui/operations/queue.html +6 -6
  32. package/dist/ui/operations/regions.html +6 -6
  33. package/dist/ui/operations/releases.html +6 -6
  34. package/dist/ui/operations/spend.html +6 -6
  35. package/dist/ui/operations/workloads.html +6 -6
  36. package/dist/ui/security.html +2 -2
  37. package/dist/ui/server/actions.html +6 -6
  38. package/dist/ui/server/activity.html +1 -1
  39. package/dist/ui/server/capacity.html +6 -6
  40. package/dist/ui/server/database.html +6 -6
  41. package/dist/ui/server/deployments.html +6 -6
  42. package/dist/ui/server/diagnostics.html +2 -2
  43. package/dist/ui/server/firewall.html +6 -6
  44. package/dist/ui/server/fleet.html +6 -6
  45. package/dist/ui/server/logs.html +6 -6
  46. package/dist/ui/server/metrics.html +6 -6
  47. package/dist/ui/server/security.html +2 -2
  48. package/dist/ui/server/services.html +1 -1
  49. package/dist/ui/server/sites.html +6 -6
  50. package/dist/ui/server/ssh-keys.html +6 -6
  51. package/dist/ui/server/team.html +6 -6
  52. package/dist/ui/server/terminal.html +2 -2
  53. package/dist/ui/serverless/alarms.html +6 -6
  54. package/dist/ui/serverless/assets.html +2 -2
  55. package/dist/ui/serverless/cost.html +2 -2
  56. package/dist/ui/serverless/data.html +6 -6
  57. package/dist/ui/serverless/deployments.html +2 -2
  58. package/dist/ui/serverless/firewall.html +1 -1
  59. package/dist/ui/serverless/functions.html +5 -5
  60. package/dist/ui/serverless/logs.html +6 -6
  61. package/dist/ui/serverless/metrics.html +2 -2
  62. package/dist/ui/serverless/queues.html +6 -6
  63. package/dist/ui/serverless/secrets.html +6 -6
  64. package/dist/ui/serverless/traces.html +6 -6
  65. package/dist/ui/serverless.html +5 -5
  66. package/package.json +3 -3
@@ -1,4 +1,4 @@
1
- import{Xa as ob}from"./chunk-sj5csg49.js";import{Ab as ma,Bb as tb,Jc as rp,Ya as Vo,Za as xe,_a as Fy,bd as X,cb as Do,cc as tp,cd as Qa,dd as np,ed as ep,gb as Uy,hb as zy,ib as Vy,jb as qy,kb as Jy,lb as Hy,mb as Wy,ob as Oo,pb as Wl,qb as _o,rb as Gy,sb as Qy,tb as Yy,ub as Ei,vb as po,wb as Xy,xb as Zy,yb as gy,zb as eb}from"./chunk-tfbs8k8v.js";import{gd as Sn}from"./chunk-3aph4c1w.js";import{wd as Du}from"./chunk-82mj52xr.js";import{Ad as vh,xd as Sh}from"./chunk-7zhgmp1s.js";import{Bd as ff,Cd as Ii,Gd as fa}from"./chunk-e2bybahr.js";import{Hd as uo}from"./chunk-d7ke90zt.js";import{Id as co,Pd as Bh,Qd as jh,Rd as Ru,Sd as xu,Xd as lt,Yd as Fh}from"./chunk-kg61p5hw.js";import{_d as Vs}from"./chunk-c0bdafbx.js";import{$d as Dy,He as ca,Ke as nb,Le as ib,Me as fo,Ne as Au,Xe as ha,te as xr}from"./chunk-jgftf5md.js";import{rf as Ky}from"./chunk-xk08030d.js";import{Ef as bh,Ff as Zf,Jf as eh,Lf as gu,Pf as Iu,vf as hh,yf as yh}from"./chunk-x6f61g36.js";import{hg as rb}from"./chunk-xhwqpgwq.js";import{jg as Lh}from"./chunk-a7w13qfm.js";import{qg as hr}from"./chunk-btsyjhk7.js";import{rg as Oy,ug as _y,vg as Ny,wg as $y,xg as My,yg as By,zg as jy}from"./chunk-w32xv8n4.js";import{Ag as Fo,Bg as Nf,Cg as zt,Dg as mo,Eg as $e,Fg as $f,Gg as Ly}from"./chunk-dz1ec589.js";import{dh as ge}from"./chunk-ja1vjdn1.js";import{$h,Ah as Bo,Bh as at,Eh as Pr,Gh as it,Lh as Xd,no as Mh,yh as km}from"./chunk-4q6tad44.js";import{op as ct}from"./chunk-ehc63fvs.js";import{createHash as TT}from"node:crypto";import{existsSync as _r,mkdtempSync as Vu,readdirSync as vy,rmSync as Ln,statSync as sb}from"node:fs";import{readFile as ur,writeFile as $n}from"node:fs/promises";import{tmpdir as Sa}from"node:os";import{dirname as AT,extname as ho,join as Ie,normalize as kT,relative as RT,resolve as PT,sep as xT}from"node:path";import{fileURLToPath as DT}from"node:url";class Ra extends Error{status;code;requestId;constructor(e,t,r,n){super(r);this.status=e;this.code=t;this.requestId=n}}class ll{options;baseUrl;fetchFn;constructor(e){this.options=e;this.baseUrl=e.baseUrl.replace(/\/$/,""),this.fetchFn=e.fetch??fetch}async request(e,t={}){let r=await this.fetchFn(`${this.baseUrl}${e}`,{...t,headers:{accept:"application/json",authorization:`Bearer ${this.options.token}`,...t.headers}}),n=await r.json().catch(()=>({}));if(!r.ok)throw new Ra(r.status,n.error?.code??"api_error",n.error?.message??"API request failed.",n.error?.requestId??r.headers.get("x-request-id")??void 0);return n}listProjects(e={}){return this.request(`/api/v1/projects?${new URLSearchParams(Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0).map(([t,r])=>[t,String(r)])))}`)}listEnvironments(e,t={}){return this.request(`/api/v1/projects/${encodeURIComponent(e)}/environments?${new URLSearchParams(Object.fromEntries(Object.entries(t).filter(([,r])=>r!==void 0).map(([r,n])=>[r,String(n)])))}`)}listServices(e,t){return this.request(`/api/v1/services?${new URLSearchParams({projectId:e,...t?{environmentId:t}:{}})}`)}listOperations(e){return this.request(`/api/v1/operations${e?`?projectId=${encodeURIComponent(e)}`:""}`)}listComposeTemplates(){return this.request("/api/v1/compose-templates")}listComposeApplications(e,t){return this.request(`/api/v1/compose-applications?${new URLSearchParams({projectId:e,...t?{environmentId:t}:{}})}`)}previewCompose(e){return this.request("/api/v1/compose-applications/preview",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}createComposeApplication(e){return this.request("/api/v1/compose-applications",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}composeServices(e){return this.request(`/api/v1/compose-applications/${encodeURIComponent(e)}/services`)}composeAction(e,t,r={}){return this.request(`/api/v1/compose-applications/${encodeURIComponent(e)}/${t}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(r)})}releaseCapabilities(e,t={}){return this.request(`/api/v1/releases/capabilities?${new URLSearchParams({kind:e,health:String(!!t.health),replicas:String(t.replicas??1)})}`)}listReleases(e,t={}){return this.request(`/api/v1/releases?${new URLSearchParams({projectId:e,...t})}`)}createReleaseArtifact(e){return this.request("/api/v1/release-artifacts",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}createRelease(e){return this.request("/api/v1/releases",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}releaseAction(e,t,r={}){return this.request(`/api/v1/releases/${encodeURIComponent(e)}/${t}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(r)})}createDeployment(e,t){return this.request("/api/v1/deployments",{method:"POST",headers:{"content-type":"application/json","idempotency-key":t},body:JSON.stringify(e)})}detectApplication(e){return this.request("/api/v1/application-detections",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}planApplication(e){return this.request("/api/v1/application-plans",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}listApplicationDrafts(e){return this.request(`/api/v1/application-drafts?projectId=${encodeURIComponent(e)}`)}createApplicationDraft(e){return this.request("/api/v1/application-drafts",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}updateApplicationDraft(e){return this.request("/api/v1/application-drafts",{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}createApplication(e,t){return this.request("/api/v1/applications",{method:"POST",headers:{"content-type":"application/json","idempotency-key":t},body:JSON.stringify(e)})}uploadApplicationArtifact(e,t,r){return this.request("/api/v1/application-artifacts",{method:"POST",headers:{"content-type":"application/octet-stream","x-project-id":e,"x-artifact-filename":t},body:r})}listRegistryConnections(){return this.request("/api/v1/registry-connections")}createRegistryConnection(e){return this.request("/api/v1/registry-connections",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}updateRegistryConnection(e){return this.request("/api/v1/registry-connections",{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}disconnectRegistryConnection(e){return this.request("/api/v1/registry-connections",{method:"DELETE",headers:{"content-type":"application/json"},body:JSON.stringify({id:e})})}listQueue(e={}){let t=new URLSearchParams(Object.entries(e).filter(([,r])=>r!==void 0).map(([r,n])=>[r,String(n)]));return this.request(`/api/v1/queue?${t}`)}operationLogs(e,t={}){let r=new URLSearchParams(Object.entries(t).filter(([,n])=>n!==void 0).map(([n,i])=>[n,String(i)]));return this.request(`/api/v1/operations/${encodeURIComponent(e)}/logs?${r}`)}streamOperationLogs(e,t=0){return this.fetchFn(`${this.baseUrl}/api/v1/operations/${encodeURIComponent(e)}/logs/stream`,{headers:{accept:"text/event-stream",authorization:`Bearer ${this.options.token}`,"last-event-id":String(t)}})}cancelOperation(e){return this.request(`/api/v1/operations/${encodeURIComponent(e)}/cancel`,{method:"POST",headers:{"content-type":"application/json"},body:"{}"})}retryOperation(e,t,r=0){return this.request(`/api/v1/operations/${encodeURIComponent(e)}/retry`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({errorClass:t,delayMs:r})})}queueSettings(){return this.request("/api/v1/queue/settings")}updateQueueSettings(e){return this.request("/api/v1/queue/settings",{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify({confirm:"update queue limits",concurrency:e})})}listPreviews(e){return this.request(`/api/v1/previews${e?`?projectId=${encodeURIComponent(e)}`:""}`)}listPreviewDefinitions(e){return this.request(`/api/v1/preview-definitions?projectId=${encodeURIComponent(e)}`)}createPreviewDefinition(e){return this.request("/api/v1/preview-definitions",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}createPreview(e){return this.request("/api/v1/previews",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}destroyPreview(e,t){return this.request(`/api/v1/previews/${encodeURIComponent(e)}/destroy`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({confirm:t})})}extendPreview(e,t){return this.request(`/api/v1/previews/${encodeURIComponent(e)}/extend`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({hours:t})})}rebuildPreview(e){return this.request(`/api/v1/previews/${encodeURIComponent(e)}/rebuild`,{method:"POST",headers:{"content-type":"application/json"},body:"{}"})}usage(e={}){let t=new URLSearchParams;for(let[r,n]of Object.entries(e))if(n)t.set(r,String(n));return this.request(`/api/v1/usage${t.size>0?`?${t}`:""}`)}usageRollups(e){let t=new URLSearchParams({from:e.from,to:e.to});if(e.projectId)t.set("projectId",e.projectId);if(e.environmentId)t.set("environmentId",e.environmentId);if(e.limit)t.set("limit",String(e.limit));for(let r of e.meters??[])t.append("meter",r);return this.request(`/api/v1/usage/rollups?${t}`)}allowance(e="deploy",t={}){let r=new URLSearchParams({operation:e});if(t.projectId)r.set("projectId",t.projectId);if(t.environmentId)r.set("environmentId",t.environmentId);return this.request(`/api/v1/spend/allowance?${r}`)}budgets(e={}){let t=new URLSearchParams;for(let[r,n]of Object.entries(e))if(n)t.set(r,String(n));return this.request(`/api/v1/spend/budgets${t.size>0?`?${t}`:""}`)}anomalies(e={}){let t=new URLSearchParams;if(e.projectId)t.set("projectId",e.projectId);if(e.since)t.set("since",e.since);if(e.unacknowledged)t.set("unacknowledged","true");return this.request(`/api/v1/spend/anomalies${t.size>0?`?${t}`:""}`)}}import{createHash as pS}from"node:crypto";import{lstatSync as wb,readdirSync as Ib,readFileSync as Cb}from"node:fs";import{relative as Eb,resolve as pl,sep as Tb}from"node:path";function Ab(e){return new Map(e.map((t)=>[t.path.replace(/^\.\//,"").replace(/\\/g,"/"),t]))}function dn(e,t,r){return{path:e,reason:t,weight:r}}function gl(e){try{return e?.content?JSON.parse(e.content):{}}catch{return{}}}function Vn(e,t,r,n,i,o){return{framework:e,strategy:t,confidence:r,evidence:n,defaults:{build:i,runtime:{target:t==="static"?"serverless":t==="serverless"?"serverless":t==="dockerfile"||t==="prebuilt_image"?"container":"server",architecture:"x86_64",port:t==="static"||t==="serverless"?void 0:3000,healthCheck:t==="static"?void 0:{protocol:"http",path:"/health"}}},description:o}}function qn(e){let t=Ab(e.slice(0,1e4)),r=[];if(t.has("Dockerfile"))r.push(Vn("dockerfile","dockerfile",0.99,[dn("Dockerfile","A root Dockerfile defines an explicit reproducible build.",1)],{kind:"dockerfile",context:".",dockerfile:"Dockerfile"},"Build the repository with its Dockerfile and run the resulting OCI image."));let n=gl(t.get("composer.json"));if(t.has("artisan")&&n.require?.["laravel/framework"])r.push(Vn("laravel","server",0.98,[dn("artisan","Laravel Artisan entrypoint is present.",0.45),dn("composer.json","Requires laravel/framework.",0.55)],{kind:"server",runtime:"php",runtimeVersion:"8.3",installCommand:"composer install --no-dev --prefer-dist --optimize-autoloader",buildCommand:"php artisan config:cache && php artisan route:cache && php artisan view:cache",startCommand:"php artisan serve --host=0.0.0.0 --port=$PORT"},"Install a production Composer tree, warm Laravel caches, and run the HTTP application."));else if(t.has("composer.json"))r.push(Vn("php","server",0.82,[dn("composer.json","Composer metadata identifies a PHP application.",0.8)],{kind:"server",runtime:"php",runtimeVersion:"8.3",installCommand:"composer install --no-dev --prefer-dist --optimize-autoloader",startCommand:"php -S 0.0.0.0:$PORT -t public"},"Install Composer dependencies and run the configured PHP document root."));let i=gl(t.get("package.json"));if(t.has("package.json")){let o=t.has("bun.lock")||t.has("bun.lockb")||i.engines?.bun,s=o?"bun":"node",c=o?"bun":"node",u=[dn("package.json",`Defines ${i.scripts?.build?"build and ":""}${i.scripts?.start?"start ":""}scripts.`,0.55)];if(o)u.push(dn(t.has("bun.lock")?"bun.lock":t.has("bun.lockb")?"bun.lockb":"package.json","Pins the Bun package manager/runtime.",0.35));let a=!!(i.dependencies?.vite||i.devDependencies?.vite||i.dependencies?.["@stacksjs/bunpress"]||i.devDependencies?.["@stacksjs/bunpress"])&&!i.scripts?.start,p=a?{kind:"static",installCommand:`${s} install --frozen-lockfile`,buildCommand:`${s} run build`,publishDirectory:"dist"}:{kind:"server",runtime:s,installCommand:`${s} install --frozen-lockfile`,buildCommand:i.scripts?.build?`${s} run build`:void 0,startCommand:i.scripts?.start?`${s} run start`:s==="bun"?"bun run index.ts":"node index.js"};r.push(Vn(c,p.kind,Math.min(0.94,u.reduce((f,y)=>f+y.weight,0)),u,p,a?"Install dependencies, build static assets, and publish the output directory.":`Install dependencies, build if configured, and start the ${s} server.`))}if(!r.length&&(t.has("index.html")||t.has("public/index.html")))r.push(Vn("static","static",0.78,[dn(t.has("index.html")?"index.html":"public/index.html","A static HTML entrypoint is present.",0.78)],{kind:"static",publishDirectory:t.has("index.html")?".":"public"},"Publish the static directory without a runtime process."));if(!r.length)r.push(Vn("unknown","buildpack",0.2,[],{kind:"buildpack",runtime:"node"},"No supported framework signature was conclusive; choose commands and runtime manually."));return r.sort((o,s)=>s.confidence-o.confidence||o.framework.localeCompare(s.framework))}function Pa(e,t={}){let r=pl(e),n=t.maxFiles??1e4,i=t.maxContentBytes??262144,o=[],s=new Set(["Dockerfile","package.json","composer.json","index.html","public/index.html","bun.lock","bun.lockb","artisan"]),c=new Set([".git",".ts-cloud",".cache",".next",".nuxt",".output",".turbo",".bun","node_modules","vendor","pantry","dist","build","coverage"]),u=(a)=>{for(let p of Ib(a).sort()){if(c.has(p))continue;let f=pl(a,p),y=Eb(r,f).split(Tb).join("/");if(!y||y.startsWith("../"))throw Error("Detection path escaped the application root");let b=wb(f);if(b.isSymbolicLink())throw Error(`Detection refuses symbolic links: ${y}`);if(b.isDirectory())u(f);else if(b.isFile()){if(o.length>=n)throw Error("Application file-count limit exceeded");let C={path:y,size:b.size};if(s.has(y)&&b.size<=i)C.content=Cb(f,"utf8");o.push(C)}}};return u(r),o}function Da(e){if(Array.isArray(e))return`[${e.map(Da).join(",")}]`;if(e&&typeof e==="object")return`{${Object.entries(e).filter(([,t])=>t!==void 0).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>`${JSON.stringify(t)}:${Da(r)}`).join(",")}}`;return JSON.stringify(e)??"null"}function Pi(e){return!!e&&!e.startsWith("/")&&!e.split("/").includes("..")&&!/[\0\r\n]/.test(e)}function It(e,t,r,n){return{path:e,code:t,message:r,alternatives:n}}function xa(e){return JSON.parse(JSON.stringify(e))}function kb(e){let t=Object.fromEntries(Object.entries(e.environment??{}).map(([r,n])=>[r,typeof n==="string"?n:`\${${n.secretRef}}`]));if(e.build.kind==="prebuilt_image"||e.build.kind==="dockerfile")return xa({infrastructure:{containers:{[e.slug]:{source:e.source,build:e.build,port:e.runtime.port,cpu:e.runtime.cpu,memory:e.runtime.memoryMb,architecture:e.runtime.architecture,healthCheck:e.runtime.healthCheck,environment:t}}}});if(e.runtime.target==="serverless"||e.build.kind==="serverless")return xa({serverless:{apps:{[e.slug]:{source:e.source,build:e.build,architecture:e.runtime.architecture,environment:t,domain:e.domain}}}});return xa({sites:{[e.slug]:{source:e.source,build:e.build,port:e.runtime.port,architecture:e.runtime.architecture,healthCheck:e.runtime.healthCheck,env:t,domain:e.domain?.hostname}}})}function Ft(e,t=[]){let r=[],n=(C)=>/(?:secret|token|password|passwd|private[_-]?key|api[_-]?key)/i.test(C),i=Object.fromEntries(Object.entries(e.environment??{}).map(([C,T])=>{if(typeof T==="string"&&n(C))return r.push(It(`environment.${C}`,"write_only_secret_required",`${C} must be supplied through the write-only secrets boundary.`)),[C,{secretRef:C}];return[C,T]})),o=e.build;if(e.build.kind==="dockerfile"&&e.build.buildArgs){let C=Object.fromEntries(Object.entries(e.build.buildArgs).map(([T,R])=>{if(n(T))return r.push(It(`build.buildArgs.${T}`,"write_only_build_secret_required",`${T} must be declared in secretNames instead of buildArgs.`)),[T,"[REDACTED]"];return[T,R]}));o={...e.build,buildArgs:C}}if(!/^[a-z0-9][a-z0-9-]{1,62}$/.test(e.slug))r.push(It("slug","invalid_slug","Slug must contain 2-63 lowercase letters, numbers, or hyphens."));if(!Pi((e.source.kind==="git"?e.source.monorepoRoot:e.source.kind==="local"?e.source.root:".")??"."))r.push(It("source","unsafe_source_root","Source root must stay inside the source tree."));if(e.source.kind==="git"&&e.source.sparsePaths?.some((C)=>!Pi(C)))r.push(It("source.sparsePaths","unsafe_sparse_path","Sparse checkout paths must stay inside the repository."));if(e.build.kind==="dockerfile"&&(!Pi(e.build.context)||!Pi(e.build.dockerfile)))r.push(It("build","unsafe_docker_path","Docker context and Dockerfile must stay inside the source tree."));if(e.build.kind==="static"&&!Pi(e.build.publishDirectory))r.push(It("build.publishDirectory","invalid_publish_directory","A safe static publish directory is required."));if(e.build.kind==="server"&&!e.build.startCommand.trim())r.push(It("build.startCommand","missing_start_command","A server start command is required.",["Use static output","Use a Dockerfile"]));if(e.source.kind==="image"&&e.build.kind!=="prebuilt_image")r.push(It("build.kind","image_requires_prebuilt","An OCI image source must use the prebuilt-image strategy.",["Select prebuilt image"]));if(e.build.kind==="prebuilt_image"&&e.runtime.target!=="container")r.push(It("runtime.target","image_requires_container","Prebuilt images require the container runtime target.",["Select container target"]));if(e.runtime.target==="serverless"&&e.build.kind==="dockerfile")r.push(It("runtime.target","unsupported_serverless_dockerfile","Dockerfile builds currently target the container service path.",["Select container target","Select serverless package"]));if(e.runtime.port!==void 0&&(!Number.isInteger(e.runtime.port)||e.runtime.port<1||e.runtime.port>65535))r.push(It("runtime.port","invalid_port","Port must be an integer from 1 to 65535."));let s=e.runtime.healthCheck;if(s&&s.protocol!=="tcp"&&(!s.path||!s.path.startsWith("/")||/[\r\n]/.test(s.path)))r.push(It("runtime.healthCheck.path","invalid_health_path","HTTP health checks require a path beginning with /."));if(e.domain?.hostname&&(!/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/i.test(e.domain.hostname)||e.domain.hostname.includes("..")))r.push(It("domain.hostname","invalid_domain","Domain must be a valid DNS hostname."));let c=Object.values(i).flatMap((C)=>typeof C==="object"?[C.secretRef]:[]),u=[...new Set([...e.requiredSecretNames??[],...e.build.kind==="dockerfile"?e.build.secretNames??[]:[],...c])].sort(),a=new Set(t),p=u.filter((C)=>!a.has(C)),f={apiVersion:"ts-cloud.dev/v1",kind:"Application",metadata:{name:e.name,slug:e.slug,projectId:e.projectId,environmentId:e.environmentId},spec:{source:e.source,build:o,runtime:e.runtime,environment:i,domain:e.domain}},y=[...new Set([e.source.kind==="git"?"git:read":e.source.kind==="image"?"registry:pull":e.source.kind==="artifact"?"artifact:inspect":"filesystem:read",e.runtime.target==="container"?"containers":e.runtime.target==="serverless"?"serverless":"servers",e.domain?"dns":""])].filter(Boolean).sort(),b=[...new Set([e.runtime.target,e.runtime.cpu?`${e.runtime.cpu} vCPU`:"",e.runtime.memoryMb?`${e.runtime.memoryMb} MB memory`:"",(e.runtime.minInstances??0)>0?`${e.runtime.minInstances} minimum instances`:"scales from zero where supported",e.domain?"managed DNS/TLS":""])].filter(Boolean);return{valid:r.length===0&&p.length===0,issues:r,missingSecrets:p,manifest:f,configPatch:kb({...e,build:o,environment:i}),capabilityRequirements:y,costDrivers:b,serializedManifest:`${Da(f)}
1
+ import{Xa as ob}from"./chunk-sj5csg49.js";import{Ab as ma,Bb as tb,Jc as rp,Ya as Vo,Za as xe,_a as Fy,bd as X,cb as Do,cc as tp,cd as Qa,dd as np,ed as ep,gb as Uy,hb as zy,ib as Vy,jb as qy,kb as Jy,lb as Hy,mb as Wy,ob as Oo,pb as Wl,qb as _o,rb as Gy,sb as Qy,tb as Yy,ub as Ei,vb as po,wb as Xy,xb as Zy,yb as gy,zb as eb}from"./chunk-tfbs8k8v.js";import{gd as Sn}from"./chunk-3aph4c1w.js";import{wd as Du}from"./chunk-82mj52xr.js";import{Ad as vh,xd as Sh}from"./chunk-7zhgmp1s.js";import{Bd as ff,Cd as Ii,Gd as fa}from"./chunk-e2bybahr.js";import{Hd as uo}from"./chunk-d7ke90zt.js";import{Id as co,Pd as Bh,Qd as jh,Rd as Ru,Sd as xu,Xd as lt,Yd as Fh}from"./chunk-kg61p5hw.js";import{_d as Vs}from"./chunk-c0bdafbx.js";import{$d as Dy,He as ca,Ke as nb,Le as ib,Me as fo,Ne as Au,Xe as ha,te as xr}from"./chunk-xdqq98wp.js";import{rf as Ky}from"./chunk-xk08030d.js";import{Ef as bh,Ff as Zf,Jf as eh,Lf as gu,Pf as Iu,vf as hh,yf as yh}from"./chunk-x6f61g36.js";import{hg as rb}from"./chunk-xhwqpgwq.js";import{jg as Lh}from"./chunk-a7w13qfm.js";import{qg as hr}from"./chunk-btsyjhk7.js";import{rg as Oy,ug as _y,vg as Ny,wg as $y,xg as My,yg as By,zg as jy}from"./chunk-w32xv8n4.js";import{Ag as Fo,Bg as Nf,Cg as zt,Dg as mo,Eg as $e,Fg as $f,Gg as Ly}from"./chunk-dz1ec589.js";import{dh as ge}from"./chunk-ja1vjdn1.js";import{$h,Ah as Bo,Bh as at,Eh as Pr,Gh as it,Lh as Xd,no as Mh,yh as km}from"./chunk-4q6tad44.js";import{op as ct}from"./chunk-ehc63fvs.js";import{createHash as TT}from"node:crypto";import{existsSync as _r,mkdtempSync as Vu,readdirSync as vy,rmSync as Ln,statSync as sb}from"node:fs";import{readFile as ur,writeFile as $n}from"node:fs/promises";import{tmpdir as Sa}from"node:os";import{dirname as AT,extname as ho,join as Ie,normalize as kT,relative as RT,resolve as PT,sep as xT}from"node:path";import{fileURLToPath as DT}from"node:url";class Ra extends Error{status;code;requestId;constructor(e,t,r,n){super(r);this.status=e;this.code=t;this.requestId=n}}class ll{options;baseUrl;fetchFn;constructor(e){this.options=e;this.baseUrl=e.baseUrl.replace(/\/$/,""),this.fetchFn=e.fetch??fetch}async request(e,t={}){let r=await this.fetchFn(`${this.baseUrl}${e}`,{...t,headers:{accept:"application/json",authorization:`Bearer ${this.options.token}`,...t.headers}}),n=await r.json().catch(()=>({}));if(!r.ok)throw new Ra(r.status,n.error?.code??"api_error",n.error?.message??"API request failed.",n.error?.requestId??r.headers.get("x-request-id")??void 0);return n}listProjects(e={}){return this.request(`/api/v1/projects?${new URLSearchParams(Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0).map(([t,r])=>[t,String(r)])))}`)}listEnvironments(e,t={}){return this.request(`/api/v1/projects/${encodeURIComponent(e)}/environments?${new URLSearchParams(Object.fromEntries(Object.entries(t).filter(([,r])=>r!==void 0).map(([r,n])=>[r,String(n)])))}`)}listServices(e,t){return this.request(`/api/v1/services?${new URLSearchParams({projectId:e,...t?{environmentId:t}:{}})}`)}listOperations(e){return this.request(`/api/v1/operations${e?`?projectId=${encodeURIComponent(e)}`:""}`)}listComposeTemplates(){return this.request("/api/v1/compose-templates")}listComposeApplications(e,t){return this.request(`/api/v1/compose-applications?${new URLSearchParams({projectId:e,...t?{environmentId:t}:{}})}`)}previewCompose(e){return this.request("/api/v1/compose-applications/preview",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}createComposeApplication(e){return this.request("/api/v1/compose-applications",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}composeServices(e){return this.request(`/api/v1/compose-applications/${encodeURIComponent(e)}/services`)}composeAction(e,t,r={}){return this.request(`/api/v1/compose-applications/${encodeURIComponent(e)}/${t}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(r)})}releaseCapabilities(e,t={}){return this.request(`/api/v1/releases/capabilities?${new URLSearchParams({kind:e,health:String(!!t.health),replicas:String(t.replicas??1)})}`)}listReleases(e,t={}){return this.request(`/api/v1/releases?${new URLSearchParams({projectId:e,...t})}`)}createReleaseArtifact(e){return this.request("/api/v1/release-artifacts",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}createRelease(e){return this.request("/api/v1/releases",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}releaseAction(e,t,r={}){return this.request(`/api/v1/releases/${encodeURIComponent(e)}/${t}`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(r)})}createDeployment(e,t){return this.request("/api/v1/deployments",{method:"POST",headers:{"content-type":"application/json","idempotency-key":t},body:JSON.stringify(e)})}detectApplication(e){return this.request("/api/v1/application-detections",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}planApplication(e){return this.request("/api/v1/application-plans",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}listApplicationDrafts(e){return this.request(`/api/v1/application-drafts?projectId=${encodeURIComponent(e)}`)}createApplicationDraft(e){return this.request("/api/v1/application-drafts",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}updateApplicationDraft(e){return this.request("/api/v1/application-drafts",{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}createApplication(e,t){return this.request("/api/v1/applications",{method:"POST",headers:{"content-type":"application/json","idempotency-key":t},body:JSON.stringify(e)})}uploadApplicationArtifact(e,t,r){return this.request("/api/v1/application-artifacts",{method:"POST",headers:{"content-type":"application/octet-stream","x-project-id":e,"x-artifact-filename":t},body:r})}listRegistryConnections(){return this.request("/api/v1/registry-connections")}createRegistryConnection(e){return this.request("/api/v1/registry-connections",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}updateRegistryConnection(e){return this.request("/api/v1/registry-connections",{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}disconnectRegistryConnection(e){return this.request("/api/v1/registry-connections",{method:"DELETE",headers:{"content-type":"application/json"},body:JSON.stringify({id:e})})}listQueue(e={}){let t=new URLSearchParams(Object.entries(e).filter(([,r])=>r!==void 0).map(([r,n])=>[r,String(n)]));return this.request(`/api/v1/queue?${t}`)}operationLogs(e,t={}){let r=new URLSearchParams(Object.entries(t).filter(([,n])=>n!==void 0).map(([n,i])=>[n,String(i)]));return this.request(`/api/v1/operations/${encodeURIComponent(e)}/logs?${r}`)}streamOperationLogs(e,t=0){return this.fetchFn(`${this.baseUrl}/api/v1/operations/${encodeURIComponent(e)}/logs/stream`,{headers:{accept:"text/event-stream",authorization:`Bearer ${this.options.token}`,"last-event-id":String(t)}})}cancelOperation(e){return this.request(`/api/v1/operations/${encodeURIComponent(e)}/cancel`,{method:"POST",headers:{"content-type":"application/json"},body:"{}"})}retryOperation(e,t,r=0){return this.request(`/api/v1/operations/${encodeURIComponent(e)}/retry`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({errorClass:t,delayMs:r})})}queueSettings(){return this.request("/api/v1/queue/settings")}updateQueueSettings(e){return this.request("/api/v1/queue/settings",{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify({confirm:"update queue limits",concurrency:e})})}listPreviews(e){return this.request(`/api/v1/previews${e?`?projectId=${encodeURIComponent(e)}`:""}`)}listPreviewDefinitions(e){return this.request(`/api/v1/preview-definitions?projectId=${encodeURIComponent(e)}`)}createPreviewDefinition(e){return this.request("/api/v1/preview-definitions",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}createPreview(e){return this.request("/api/v1/previews",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)})}destroyPreview(e,t){return this.request(`/api/v1/previews/${encodeURIComponent(e)}/destroy`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({confirm:t})})}extendPreview(e,t){return this.request(`/api/v1/previews/${encodeURIComponent(e)}/extend`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({hours:t})})}rebuildPreview(e){return this.request(`/api/v1/previews/${encodeURIComponent(e)}/rebuild`,{method:"POST",headers:{"content-type":"application/json"},body:"{}"})}usage(e={}){let t=new URLSearchParams;for(let[r,n]of Object.entries(e))if(n)t.set(r,String(n));return this.request(`/api/v1/usage${t.size>0?`?${t}`:""}`)}usageRollups(e){let t=new URLSearchParams({from:e.from,to:e.to});if(e.projectId)t.set("projectId",e.projectId);if(e.environmentId)t.set("environmentId",e.environmentId);if(e.limit)t.set("limit",String(e.limit));for(let r of e.meters??[])t.append("meter",r);return this.request(`/api/v1/usage/rollups?${t}`)}allowance(e="deploy",t={}){let r=new URLSearchParams({operation:e});if(t.projectId)r.set("projectId",t.projectId);if(t.environmentId)r.set("environmentId",t.environmentId);return this.request(`/api/v1/spend/allowance?${r}`)}budgets(e={}){let t=new URLSearchParams;for(let[r,n]of Object.entries(e))if(n)t.set(r,String(n));return this.request(`/api/v1/spend/budgets${t.size>0?`?${t}`:""}`)}anomalies(e={}){let t=new URLSearchParams;if(e.projectId)t.set("projectId",e.projectId);if(e.since)t.set("since",e.since);if(e.unacknowledged)t.set("unacknowledged","true");return this.request(`/api/v1/spend/anomalies${t.size>0?`?${t}`:""}`)}}import{createHash as pS}from"node:crypto";import{lstatSync as wb,readdirSync as Ib,readFileSync as Cb}from"node:fs";import{relative as Eb,resolve as pl,sep as Tb}from"node:path";function Ab(e){return new Map(e.map((t)=>[t.path.replace(/^\.\//,"").replace(/\\/g,"/"),t]))}function dn(e,t,r){return{path:e,reason:t,weight:r}}function gl(e){try{return e?.content?JSON.parse(e.content):{}}catch{return{}}}function Vn(e,t,r,n,i,o){return{framework:e,strategy:t,confidence:r,evidence:n,defaults:{build:i,runtime:{target:t==="static"?"serverless":t==="serverless"?"serverless":t==="dockerfile"||t==="prebuilt_image"?"container":"server",architecture:"x86_64",port:t==="static"||t==="serverless"?void 0:3000,healthCheck:t==="static"?void 0:{protocol:"http",path:"/health"}}},description:o}}function qn(e){let t=Ab(e.slice(0,1e4)),r=[];if(t.has("Dockerfile"))r.push(Vn("dockerfile","dockerfile",0.99,[dn("Dockerfile","A root Dockerfile defines an explicit reproducible build.",1)],{kind:"dockerfile",context:".",dockerfile:"Dockerfile"},"Build the repository with its Dockerfile and run the resulting OCI image."));let n=gl(t.get("composer.json"));if(t.has("artisan")&&n.require?.["laravel/framework"])r.push(Vn("laravel","server",0.98,[dn("artisan","Laravel Artisan entrypoint is present.",0.45),dn("composer.json","Requires laravel/framework.",0.55)],{kind:"server",runtime:"php",runtimeVersion:"8.3",installCommand:"composer install --no-dev --prefer-dist --optimize-autoloader",buildCommand:"php artisan config:cache && php artisan route:cache && php artisan view:cache",startCommand:"php artisan serve --host=0.0.0.0 --port=$PORT"},"Install a production Composer tree, warm Laravel caches, and run the HTTP application."));else if(t.has("composer.json"))r.push(Vn("php","server",0.82,[dn("composer.json","Composer metadata identifies a PHP application.",0.8)],{kind:"server",runtime:"php",runtimeVersion:"8.3",installCommand:"composer install --no-dev --prefer-dist --optimize-autoloader",startCommand:"php -S 0.0.0.0:$PORT -t public"},"Install Composer dependencies and run the configured PHP document root."));let i=gl(t.get("package.json"));if(t.has("package.json")){let o=t.has("bun.lock")||t.has("bun.lockb")||i.engines?.bun,s=o?"bun":"node",c=o?"bun":"node",u=[dn("package.json",`Defines ${i.scripts?.build?"build and ":""}${i.scripts?.start?"start ":""}scripts.`,0.55)];if(o)u.push(dn(t.has("bun.lock")?"bun.lock":t.has("bun.lockb")?"bun.lockb":"package.json","Pins the Bun package manager/runtime.",0.35));let a=!!(i.dependencies?.vite||i.devDependencies?.vite||i.dependencies?.["@stacksjs/bunpress"]||i.devDependencies?.["@stacksjs/bunpress"])&&!i.scripts?.start,p=a?{kind:"static",installCommand:`${s} install --frozen-lockfile`,buildCommand:`${s} run build`,publishDirectory:"dist"}:{kind:"server",runtime:s,installCommand:`${s} install --frozen-lockfile`,buildCommand:i.scripts?.build?`${s} run build`:void 0,startCommand:i.scripts?.start?`${s} run start`:s==="bun"?"bun run index.ts":"node index.js"};r.push(Vn(c,p.kind,Math.min(0.94,u.reduce((f,y)=>f+y.weight,0)),u,p,a?"Install dependencies, build static assets, and publish the output directory.":`Install dependencies, build if configured, and start the ${s} server.`))}if(!r.length&&(t.has("index.html")||t.has("public/index.html")))r.push(Vn("static","static",0.78,[dn(t.has("index.html")?"index.html":"public/index.html","A static HTML entrypoint is present.",0.78)],{kind:"static",publishDirectory:t.has("index.html")?".":"public"},"Publish the static directory without a runtime process."));if(!r.length)r.push(Vn("unknown","buildpack",0.2,[],{kind:"buildpack",runtime:"node"},"No supported framework signature was conclusive; choose commands and runtime manually."));return r.sort((o,s)=>s.confidence-o.confidence||o.framework.localeCompare(s.framework))}function Pa(e,t={}){let r=pl(e),n=t.maxFiles??1e4,i=t.maxContentBytes??262144,o=[],s=new Set(["Dockerfile","package.json","composer.json","index.html","public/index.html","bun.lock","bun.lockb","artisan"]),c=new Set([".git",".ts-cloud",".cache",".next",".nuxt",".output",".turbo",".bun","node_modules","vendor","pantry","dist","build","coverage"]),u=(a)=>{for(let p of Ib(a).sort()){if(c.has(p))continue;let f=pl(a,p),y=Eb(r,f).split(Tb).join("/");if(!y||y.startsWith("../"))throw Error("Detection path escaped the application root");let b=wb(f);if(b.isSymbolicLink())throw Error(`Detection refuses symbolic links: ${y}`);if(b.isDirectory())u(f);else if(b.isFile()){if(o.length>=n)throw Error("Application file-count limit exceeded");let C={path:y,size:b.size};if(s.has(y)&&b.size<=i)C.content=Cb(f,"utf8");o.push(C)}}};return u(r),o}function Da(e){if(Array.isArray(e))return`[${e.map(Da).join(",")}]`;if(e&&typeof e==="object")return`{${Object.entries(e).filter(([,t])=>t!==void 0).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>`${JSON.stringify(t)}:${Da(r)}`).join(",")}}`;return JSON.stringify(e)??"null"}function Pi(e){return!!e&&!e.startsWith("/")&&!e.split("/").includes("..")&&!/[\0\r\n]/.test(e)}function It(e,t,r,n){return{path:e,code:t,message:r,alternatives:n}}function xa(e){return JSON.parse(JSON.stringify(e))}function kb(e){let t=Object.fromEntries(Object.entries(e.environment??{}).map(([r,n])=>[r,typeof n==="string"?n:`\${${n.secretRef}}`]));if(e.build.kind==="prebuilt_image"||e.build.kind==="dockerfile")return xa({infrastructure:{containers:{[e.slug]:{source:e.source,build:e.build,port:e.runtime.port,cpu:e.runtime.cpu,memory:e.runtime.memoryMb,architecture:e.runtime.architecture,healthCheck:e.runtime.healthCheck,environment:t}}}});if(e.runtime.target==="serverless"||e.build.kind==="serverless")return xa({serverless:{apps:{[e.slug]:{source:e.source,build:e.build,architecture:e.runtime.architecture,environment:t,domain:e.domain}}}});return xa({sites:{[e.slug]:{source:e.source,build:e.build,port:e.runtime.port,architecture:e.runtime.architecture,healthCheck:e.runtime.healthCheck,env:t,domain:e.domain?.hostname}}})}function Ft(e,t=[]){let r=[],n=(C)=>/(?:secret|token|password|passwd|private[_-]?key|api[_-]?key)/i.test(C),i=Object.fromEntries(Object.entries(e.environment??{}).map(([C,T])=>{if(typeof T==="string"&&n(C))return r.push(It(`environment.${C}`,"write_only_secret_required",`${C} must be supplied through the write-only secrets boundary.`)),[C,{secretRef:C}];return[C,T]})),o=e.build;if(e.build.kind==="dockerfile"&&e.build.buildArgs){let C=Object.fromEntries(Object.entries(e.build.buildArgs).map(([T,R])=>{if(n(T))return r.push(It(`build.buildArgs.${T}`,"write_only_build_secret_required",`${T} must be declared in secretNames instead of buildArgs.`)),[T,"[REDACTED]"];return[T,R]}));o={...e.build,buildArgs:C}}if(!/^[a-z0-9][a-z0-9-]{1,62}$/.test(e.slug))r.push(It("slug","invalid_slug","Slug must contain 2-63 lowercase letters, numbers, or hyphens."));if(!Pi((e.source.kind==="git"?e.source.monorepoRoot:e.source.kind==="local"?e.source.root:".")??"."))r.push(It("source","unsafe_source_root","Source root must stay inside the source tree."));if(e.source.kind==="git"&&e.source.sparsePaths?.some((C)=>!Pi(C)))r.push(It("source.sparsePaths","unsafe_sparse_path","Sparse checkout paths must stay inside the repository."));if(e.build.kind==="dockerfile"&&(!Pi(e.build.context)||!Pi(e.build.dockerfile)))r.push(It("build","unsafe_docker_path","Docker context and Dockerfile must stay inside the source tree."));if(e.build.kind==="static"&&!Pi(e.build.publishDirectory))r.push(It("build.publishDirectory","invalid_publish_directory","A safe static publish directory is required."));if(e.build.kind==="server"&&!e.build.startCommand.trim())r.push(It("build.startCommand","missing_start_command","A server start command is required.",["Use static output","Use a Dockerfile"]));if(e.source.kind==="image"&&e.build.kind!=="prebuilt_image")r.push(It("build.kind","image_requires_prebuilt","An OCI image source must use the prebuilt-image strategy.",["Select prebuilt image"]));if(e.build.kind==="prebuilt_image"&&e.runtime.target!=="container")r.push(It("runtime.target","image_requires_container","Prebuilt images require the container runtime target.",["Select container target"]));if(e.runtime.target==="serverless"&&e.build.kind==="dockerfile")r.push(It("runtime.target","unsupported_serverless_dockerfile","Dockerfile builds currently target the container service path.",["Select container target","Select serverless package"]));if(e.runtime.port!==void 0&&(!Number.isInteger(e.runtime.port)||e.runtime.port<1||e.runtime.port>65535))r.push(It("runtime.port","invalid_port","Port must be an integer from 1 to 65535."));let s=e.runtime.healthCheck;if(s&&s.protocol!=="tcp"&&(!s.path||!s.path.startsWith("/")||/[\r\n]/.test(s.path)))r.push(It("runtime.healthCheck.path","invalid_health_path","HTTP health checks require a path beginning with /."));if(e.domain?.hostname&&(!/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/i.test(e.domain.hostname)||e.domain.hostname.includes("..")))r.push(It("domain.hostname","invalid_domain","Domain must be a valid DNS hostname."));let c=Object.values(i).flatMap((C)=>typeof C==="object"?[C.secretRef]:[]),u=[...new Set([...e.requiredSecretNames??[],...e.build.kind==="dockerfile"?e.build.secretNames??[]:[],...c])].sort(),a=new Set(t),p=u.filter((C)=>!a.has(C)),f={apiVersion:"ts-cloud.dev/v1",kind:"Application",metadata:{name:e.name,slug:e.slug,projectId:e.projectId,environmentId:e.environmentId},spec:{source:e.source,build:o,runtime:e.runtime,environment:i,domain:e.domain}},y=[...new Set([e.source.kind==="git"?"git:read":e.source.kind==="image"?"registry:pull":e.source.kind==="artifact"?"artifact:inspect":"filesystem:read",e.runtime.target==="container"?"containers":e.runtime.target==="serverless"?"serverless":"servers",e.domain?"dns":""])].filter(Boolean).sort(),b=[...new Set([e.runtime.target,e.runtime.cpu?`${e.runtime.cpu} vCPU`:"",e.runtime.memoryMb?`${e.runtime.memoryMb} MB memory`:"",(e.runtime.minInstances??0)>0?`${e.runtime.minInstances} minimum instances`:"scales from zero where supported",e.domain?"managed DNS/TLS":""])].filter(Boolean);return{valid:r.length===0&&p.length===0,issues:r,missingSecrets:p,manifest:f,configPatch:kb({...e,build:o,environment:i}),capabilityRequirements:y,costDrivers:b,serializedManifest:`${Da(f)}
2
2
  `}}function ml(e){let t=e.replace(/\\/g,"/");return!!t&&!t.startsWith("/")&&!/^[A-Za-z]:\//.test(t)&&!t.split("/").includes("..")&&!/[\0\r\n]/.test(t)}function fl(e,t,r={}){let n=r.maxBytes??104857600,i=r.maxEntries??1e4,o=r.maxExpandedBytes??524288000;if(e.byteLength>n)throw Error("Archive exceeds the compressed size limit");let s=new DataView(e.buffer,e.byteOffset,e.byteLength),c=[],u=0;if(t.toLowerCase().endsWith(".zip")||e[0]===80&&e[1]===75){let p=0;while(p+30<=e.length){let f=s.getUint32(p,!0);if(f===33639248||f===101010256)break;if(f!==67324752)throw Error("Malformed ZIP local header");let y=s.getUint16(p+6,!0),b=s.getUint32(p+18,!0),C=s.getUint32(p+22,!0),T=s.getUint16(p+26,!0),R=s.getUint16(p+28,!0);if(y&8)throw Error("ZIP data descriptors are not accepted for uploaded artifacts");let x=new TextDecoder().decode(e.subarray(p+30,p+30+T));if(!ml(x))throw Error(`Unsafe archive path: ${x}`);if(c.push(x),u+=C,c.length>i||u>o)throw Error("Archive expansion limit exceeded");p+=30+T+R+b}return{format:"zip",entries:c.length,expandedBytes:u,paths:c}}if(!t.toLowerCase().match(/\.(?:tar|tar\.gz|tgz)$/))throw Error("Only ZIP and uncompressed TAR artifacts are supported");let a=0;while(a+512<=e.length){let p=e.subarray(a,a+512);if(p.every((T)=>T===0))break;let f=(T,R)=>new TextDecoder().decode(p.subarray(T,T+R)).replace(/\0.*$/,""),y=`${f(345,155)}${f(345,155)?"/":""}${f(0,100)}`,b=Number.parseInt(f(124,12).trim()||"0",8),C=String.fromCharCode(p[156]??0);if(!ml(y))throw Error(`Unsafe archive path: ${y}`);if(C==="1"||C==="2")throw Error(`Archive links are not accepted: ${y}`);if(!Number.isFinite(b)||b<0)throw Error("Malformed TAR entry size");if(c.push(y),u+=b,c.length>i||u>o)throw Error("Archive expansion limit exceeded");a+=512+Math.ceil(b/512)*512}return{format:"tar",entries:c.length,expandedBytes:u,paths:c}}var hl=new Set(["source","build","runtime","environment","domain","review"]);function yl(e,t){try{return typeof e==="string"?JSON.parse(e):t}catch{return t}}function bl(e){return/(?:secret|token|password|passwd|private[_-]?key|api[_-]?key)/i.test(e)}function Sl(e){for(let[r,n]of Object.entries(e.environment??{}))if(typeof n==="string"&&bl(r)&&n)throw Error(`${r} must be stored through the write-only secrets boundary`);if(e.build.kind==="dockerfile"){for(let[r,n]of Object.entries(e.build.buildArgs??{}))if(bl(r)&&n)throw Error(`${r} must be declared as a build secret name`)}let t=JSON.stringify(e);if(/https?:\/\/[^/@\s]+:[^/@\s]+@/i.test(t))throw Error("Drafts cannot contain credential-bearing URLs")}function vl(e){return{id:String(e.id),organizationId:String(e.organization_id),projectId:String(e.project_id),schemaVersion:Number(e.schema_version),name:String(e.name),step:String(e.step),input:yl(e.input,{}),suppliedSecretNames:yl(e.supplied_secret_names,[]),status:String(e.status),version:Number(e.version),createdByActorId:e.created_by_actor_id?String(e.created_by_actor_id):void 0,createdAt:String(e.created_at),updatedAt:String(e.updated_at)}}class Oa{controlPlane;nowFn;idFn;constructor(e,t={}){this.controlPlane=e;this.nowFn=t.now??(()=>new Date),this.idFn=t.id??(()=>crypto.randomUUID())}now(){return this.nowFn().toISOString()}create(e){if(Sl(e.draft),e.draft.projectId!==e.projectId)throw Error("Draft project does not match its storage scope");let t=this.controlPlane.getProject(e.projectId);if(!t||t.organizationId!==e.organizationId)throw Error("Draft project was not found");let r=this.idFn(),n=this.now(),i=e.step??"source";if(!hl.has(i))throw Error("Invalid draft step");let o=[...new Set(e.suppliedSecretNames??[])].sort(),s=Ft(e.draft,o).valid?"ready":"draft";return this.controlPlane.database.run("INSERT INTO application_drafts (id, organization_id, project_id, schema_version, name, step, input, supplied_secret_names, status, created_by_actor_id, created_at, updated_at) VALUES (?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?)",[r,e.organizationId,e.projectId,e.name.trim().slice(0,100),i,JSON.stringify(e.draft),JSON.stringify(o),s,e.actorId??null,n,n]),this.controlPlane.appendEvent({organizationId:e.organizationId,projectId:e.projectId,actorId:e.actorId,type:"application.draft.created",payload:{draftId:r,step:i,status:s,requiredSecretNames:e.draft.requiredSecretNames??[]}}),this.get(r)}get(e){let t=this.controlPlane.database.query("SELECT * FROM application_drafts WHERE id=?").get(e);return t?vl(t):void 0}list(e){return this.controlPlane.database.query("SELECT * FROM application_drafts WHERE project_id=? ORDER BY updated_at DESC").all(e).map(vl)}update(e,t,r){let n=this.get(e);if(!n||n.version!==t)throw Error(`Application draft ${e} changed since version ${t}`);if(!hl.has(r.step))throw Error("Invalid draft step");if(r.draft.projectId!==n.projectId)throw Error("Draft project cannot be changed");Sl(r.draft);let i=[...new Set(r.suppliedSecretNames??n.suppliedSecretNames)].sort(),o=Ft(r.draft,i).valid?"ready":"draft",s=this.now();if(this.controlPlane.database.run("UPDATE application_drafts SET name=?, step=?, input=?, supplied_secret_names=?, status=?, version=version+1, updated_at=? WHERE id=? AND version=?",[r.draft.name.slice(0,100),r.step,JSON.stringify(r.draft),JSON.stringify(i),o,s,e,t]).changes!==1)throw Error(`Application draft ${e} changed since version ${t}`);return this.controlPlane.appendEvent({organizationId:n.organizationId,projectId:n.projectId,actorId:r.actorId,type:"application.draft.updated",payload:{draftId:e,fromVersion:t,toVersion:t+1,step:r.step,status:o}}),this.get(e)}markApplied(e,t,r){let n=this.get(e);if(!n||n.version!==t)throw Error(`Application draft ${e} changed since version ${t}`);let i=Ft(n.input,n.suppliedSecretNames);if(!i.valid)throw Error(`Draft is not deployable: ${[...i.issues.map((s)=>s.message),...i.missingSecrets.map((s)=>`Missing secret ${s}`)].join("; ")}`);let o=this.now();return this.controlPlane.database.run("UPDATE application_drafts SET status='applied', step='review', version=version+1, updated_at=? WHERE id=? AND version=?",[o,e,t]),this.controlPlane.appendEvent({organizationId:n.organizationId,projectId:n.projectId,actorId:r,type:"application.draft.applied",payload:$e({draftId:e,manifest:i.manifest})}),this.get(e)}}import{createCipheriv as Rb,createDecipheriv as Pb,createHash as Cl,randomBytes as xb}from"node:crypto";function xi(e){return typeof e==="string"&&e?e:void 0}function _a(e){return Cl("sha256").update(e).digest("hex").slice(0,16)}function wl(e){let t=new URL(e.includes("://")?e:`https://${e}`);if(t.protocol!=="https:"&&!["localhost","127.0.0.1","::1"].includes(t.hostname))throw Error("Registry host must use HTTPS");if(t.username||t.password||t.search||t.hash)throw Error("Registry host cannot contain credentials");return`${t.protocol}//${t.host}`}function Db(e){return{id:String(e.id),organizationId:String(e.organization_id),provider:String(e.provider),name:String(e.name),host:String(e.host),credentialConfigured:!!e.credential_ciphertext,credentialFingerprint:xi(e.credential_fingerprint),credentialExpiresAt:xi(e.credential_expires_at),status:String(e.status),healthMessage:xi(e.health_message),lastTestedAt:xi(e.last_tested_at),version:Number(e.version),createdByActorId:xi(e.created_by_actor_id),createdAt:String(e.created_at),updatedAt:String(e.updated_at)}}class Na{controlPlane;key;nowFn;idFn;constructor(e,t){this.controlPlane=e;if(!t.encryptionKey)throw Error("Registry encryption key is required");this.key=Cl("sha256").update(`ts-cloud:registry:${t.encryptionKey}`).digest(),this.nowFn=t.now??(()=>new Date),this.idFn=t.id??(()=>crypto.randomUUID())}now(){return this.nowFn().toISOString()}encrypt(e){let t=xb(12),r=Rb("aes-256-gcm",this.key,t),n=Buffer.concat([r.update(e,"utf8"),r.final()]);return`v1.${t.toString("base64url")}.${r.getAuthTag().toString("base64url")}.${n.toString("base64url")}`}decrypt(e){let[t,r,n,i]=e.split(".");if(t!=="v1"||!r||!n||!i)throw Error("Registry credential envelope is invalid");let o=Pb("aes-256-gcm",this.key,Buffer.from(r,"base64url"));return o.setAuthTag(Buffer.from(n,"base64url")),Buffer.concat([o.update(Buffer.from(i,"base64url")),o.final()]).toString("utf8")}current(e){let t=Db(e);return t.status!=="disconnected"&&t.credentialExpiresAt&&new Date(t.credentialExpiresAt).getTime()<=this.nowFn().getTime()?{...t,status:"expired",healthMessage:"Registry credential expired; rotate it before pulling images."}:t}create(e){if(!["docker_hub","ghcr","ecr","gcr","generic"].includes(e.provider))throw Error("A supported registry provider is required");let t=e.credential?JSON.stringify(e.credential):void 0,r=this.idFn(),n=this.now();return this.controlPlane.database.run("INSERT INTO registry_connections (id, organization_id, provider, name, host, credential_ciphertext, credential_fingerprint, credential_expires_at, created_by_actor_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",[r,e.organizationId,e.provider,e.name.trim().slice(0,80),wl(e.host),t?this.encrypt(t):null,t?_a(t):null,e.credentialExpiresAt??null,e.actorId??null,n,n]),this.controlPlane.appendEvent({organizationId:e.organizationId,actorId:e.actorId,type:"registry.connection.created",payload:{connectionId:r,provider:e.provider,host:wl(e.host),credentialConfigured:!!t}}),this.get(r)}get(e){let t=this.controlPlane.database.query("SELECT * FROM registry_connections WHERE id=?").get(e);return t?this.current(t):void 0}list(e){return this.controlPlane.database.query("SELECT * FROM registry_connections WHERE organization_id=? ORDER BY name").all(e).map((t)=>this.current(t))}credential(e){let t=this.controlPlane.database.query("SELECT credential_ciphertext FROM registry_connections WHERE id=?").get(e);return t?.credential_ciphertext?JSON.parse(this.decrypt(String(t.credential_ciphertext))):void 0}rotate(e,t,r={}){let n=this.get(e);if(!n||n.status==="disconnected")throw Error("Registry connection was not found");let i=JSON.stringify(t);return this.controlPlane.database.run("UPDATE registry_connections SET credential_ciphertext=?, credential_fingerprint=?, credential_expires_at=?, status='pending', health_message=NULL, version=version+1, updated_at=? WHERE id=?",[this.encrypt(i),_a(i),r.expiresAt??null,this.now(),e]),this.controlPlane.appendEvent({organizationId:n.organizationId,actorId:r.actorId,type:"registry.credential.rotated",payload:{connectionId:e,fingerprint:_a(i),expiresAt:r.expiresAt??null}}),this.get(e)}disconnect(e,t){let r=this.get(e);if(!r)throw Error("Registry connection was not found");return this.controlPlane.database.run("UPDATE registry_connections SET credential_ciphertext=NULL, credential_fingerprint=NULL, status='disconnected', health_message='Disconnected by an administrator', version=version+1, updated_at=? WHERE id=?",[this.now(),e]),this.controlPlane.appendEvent({organizationId:r.organizationId,actorId:t,type:"registry.connection.disconnected",level:"warning",payload:{connectionId:e}}),this.get(e)}async test(e,t={}){let r=this.get(e);if(!r||["disconnected","expired"].includes(r.status))throw Error("Active registry connection was not found");let n=this.credential(e),i=t.fetch??fetch,o=new AbortController,s=setTimeout(()=>o.abort(),t.timeoutMs??15000),c=n?.token?`Bearer ${n.token}`:n?.username||n?.password?`Basic ${Buffer.from(`${n.username??""}:${n.password??""}`).toString("base64")}`:void 0;try{let u=await i(`${r.host}/v2/`,{method:"GET",redirect:"error",signal:o.signal,headers:c?{Authorization:c}:void 0}),a=u.headers.get("www-authenticate")??"";if(u.status===401&&a.startsWith("Bearer ")&&n){let y=Object.fromEntries([...a.slice(7).matchAll(/(\w+)="([^"]+)"/g)].map((x)=>[x[1],x[2]])),b=new URL(y.realm??"");if(b.protocol!=="https:")throw Error("Registry authentication realm must use HTTPS");if(y.service)b.searchParams.set("service",y.service);if(t.image)b.searchParams.set("scope",`repository:${El(t.image).repository}:pull`);let C=await i(b,{signal:o.signal,redirect:"error",headers:c?{Authorization:c}:void 0});if(!C.ok)throw Error(`Registry authentication failed (${C.status})`);let T=await C.json(),R=T.token??T.access_token;if(!R)throw Error("Registry did not return an access token");u=t.image?await Il(i,r.host,t.image,R,o.signal):await i(`${r.host}/v2/`,{signal:o.signal,redirect:"error",headers:{Authorization:`Bearer ${R}`}})}else if(u.ok&&t.image)u=await Il(i,r.host,t.image,n?.token,o.signal,c);let p=u.ok?"healthy":"degraded",f=u.ok?t.image?"Registry image is readable.":"Registry connection is healthy.":`Registry request failed (${u.status})`;return this.controlPlane.database.run("UPDATE registry_connections SET status=?, health_message=?, last_tested_at=?, version=version+1, updated_at=? WHERE id=?",[p,f,this.now(),this.now(),e]),this.get(e)}catch(u){let a=o.signal.aborted?"Registry request timed out.":u instanceof Error?u.message:"Registry test failed";return this.controlPlane.database.run("UPDATE registry_connections SET status='degraded', health_message=?, last_tested_at=?, version=version+1, updated_at=? WHERE id=?",[a.slice(0,1000),this.now(),this.now(),e]),this.get(e)}finally{clearTimeout(s)}}}function El(e){let t=e.replace(/^https?:\/\//,""),r=t.indexOf("/");if(r<1)throw Error("Image must include registry/repository");let n=t.slice(r+1),i=n.lastIndexOf("@"),o=n.lastIndexOf(":"),s=i>=0?i:o>n.lastIndexOf("/")?o:-1,c=s>=0?n.slice(0,s):n,u=s>=0?n.slice(s+1):"latest";if(!/^[A-Za-z0-9_.\/-]+$/.test(c)||!/^[A-Za-z0-9_.:+-]+$/.test(u))throw Error("Image reference is invalid");return{repository:c,reference:u}}async function Il(e,t,r,n,i,o){let s=El(r);return e(`${t}/v2/${s.repository}/manifests/${encodeURIComponent(s.reference)}`,{method:"HEAD",redirect:"error",signal:i,headers:{Accept:"application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json",...n?{Authorization:`Bearer ${n}`}:o?{Authorization:o}:{}}})}import{createHash as Ob}from"node:crypto";import{chmodSync as Tl,existsSync as Al,mkdirSync as _b,renameSync as Nb,unlinkSync as kl,writeFileSync as $b}from"node:fs";import{basename as Mb,join as Lb}from"node:path";function $a(e){return{id:String(e.id),organizationId:String(e.organization_id),projectId:String(e.project_id),filename:String(e.filename),sha256:String(e.sha256),size:Number(e.size),format:String(e.format),entryCount:Number(e.entry_count),expandedBytes:Number(e.expanded_bytes),createdByActorId:e.created_by_actor_id?String(e.created_by_actor_id):void 0,createdAt:String(e.created_at)}}class Ma{controlPlane;root;idFn;nowFn;constructor(e,t){this.controlPlane=e;this.root=at(t.cwd,"artifacts"),this.idFn=t.id??(()=>crypto.randomUUID()),this.nowFn=t.now??(()=>new Date)}create(e){let t=this.controlPlane.getProject(e.projectId);if(!t||t.organizationId!==e.organizationId)throw Error("Artifact project was not found");let r=Mb(e.filename).slice(0,200);if(!r||r!==e.filename)throw Error("Artifact filename must not contain a path");let n=fl(e.bytes,r),i=Ob("sha256").update(e.bytes).digest("hex"),o=this.controlPlane.database.query("SELECT * FROM application_artifacts WHERE project_id=? AND sha256=?").get(e.projectId,i);if(o)return $a(o);let s=this.idFn();_b(this.root,{recursive:!0,mode:448}),Tl(this.root,448);let c=Lb(this.root,`${s}.archive`),u=`${c}.partial`;try{$b(u,e.bytes,{mode:384,flag:"wx"}),Nb(u,c),Tl(c,384),this.controlPlane.database.run("INSERT INTO application_artifacts (id, organization_id, project_id, filename, storage_path, sha256, size, format, entry_count, expanded_bytes, created_by_actor_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",[s,e.organizationId,e.projectId,r,c,i,e.bytes.byteLength,n.format,n.entries,n.expandedBytes,e.actorId??null,this.nowFn().toISOString()])}catch(a){if(Al(u))kl(u);if(Al(c))kl(c);throw a}return this.controlPlane.appendEvent({organizationId:e.organizationId,projectId:e.projectId,actorId:e.actorId,type:"application.artifact.created",payload:{artifactId:s,filename:r,sha256:i,size:e.bytes.byteLength,entries:n.entries}}),this.get(s)}get(e){let t=this.controlPlane.database.query("SELECT * FROM application_artifacts WHERE id=?").get(e);return t?$a(t):void 0}list(e){return this.controlPlane.database.query("SELECT * FROM application_artifacts WHERE project_id=? ORDER BY created_at DESC").all(e).map($a)}}function Eo(e){let t=e.drafts.get(e.draftId);if(!t||t.version!==e.expectedVersion)throw Error(`Application draft ${e.draftId} changed since version ${e.expectedVersion}`);if(t.status==="applied")throw Error("Application draft was already applied");let r=e.controlPlane.getProject(t.projectId),n=r?e.controlPlane.listEnvironments(r.id).find((a)=>a.id===t.input.environmentId):void 0;if(!r||!n||n.projectId!==r.id||r.organizationId!==t.organizationId)throw Error("Draft deployment scope was not found");if(e.confirmEnvironment!==n.slug)throw Error(`Type ${n.slug} to confirm this deployment target`);let i=Ft(t.input,t.suppliedSecretNames);if(!i.valid)throw Error(`Application plan is invalid: ${[...i.issues.map((a)=>a.message),...i.missingSecrets.map((a)=>`Missing secret ${a}`)].join("; ")}`);let o=e.controlPlane.listResources(r.id,n.id).find((a)=>a.kind==="application"&&a.slug===t.input.slug),s=JSON.parse(JSON.stringify({manifest:i.manifest,configPatch:i.configPatch})),c=o?e.controlPlane.updateResource(o.id,o.version,{name:t.input.name,desiredState:s}):e.controlPlane.createResource({projectId:r.id,environmentId:n.id,kind:"application",slug:t.input.slug,name:t.input.name,desiredState:s,metadata:{source:"application-onboarding",draftId:t.id}}),u=new xe(e.controlPlane).enqueue({projectId:r.id,environmentId:n.id,resourceId:c.id,actorId:e.actorId,kind:"application.create",idempotencyKey:`application-draft:${t.id}:v${t.version}`,correlationId:`application-draft:${t.id}`,input:s,lockKey:`resource:${c.id}`,providerKey:c.provider??"default",buildSlot:t.input.build.kind!=="prebuilt_image",maxAttempts:3,retryClasses:["network","provider_throttled","provider_unavailable"],resumePolicy:"fail",cancellationMode:"provider_non_cancellable",retentionDays:90}).operation;return e.drafts.markApplied(t.id,t.version,e.actorId),e.controlPlane.appendEvent({organizationId:t.organizationId,projectId:r.id,resourceId:c.id,operationId:u.id,actorId:e.actorId,type:"application.create.queued",payload:{draftId:t.id,environment:n.slug,strategy:t.input.build.kind,costDrivers:i.costDrivers}}),{plan:i,resource:c,operation:u}}function Di(e){let t=["static","compute","serverless_zip","serverless_image"].includes(e.kind),r=["container","compose"].includes(e.kind)&&(e.replicas??1)>1,n=e.kind==="container"||e.kind==="serverless_zip"||e.kind==="serverless_image",i=(o,s)=>s&&(o==="atomic"?e.kind==="static"||e.hasHealthGate:e.hasHealthGate);return[{strategy:"atomic",supported:i("atomic",t),explanation:t?e.hasHealthGate||e.kind==="static"?"Activate an immutable version with an atomic pointer/alias switch.":"Atomic activation needs a readiness gate for this runtime.":"This driver has no atomic pointer or alias primitive.",capacityMultiplier:1,costImpact:"none",rollback:"Switch the pointer/alias back to the previous immutable release."},{strategy:"rolling",supported:i("rolling",r),explanation:r?e.hasHealthGate?"Replace healthy replicas within the configured capacity envelope.":"Rolling activation requires a health/readiness gate.":"Rolling activation requires a capable container/Compose driver and at least two replicas.",capacityMultiplier:1.5,costImpact:"temporary",rollback:"Stop replacement and restore the prior task/service definition."},{strategy:"blue_green",supported:i("blue_green",n),explanation:n?e.hasHealthGate?"Provision a complete green target and switch traffic after health passes.":"Blue-green requires a health gate before traffic can switch.":"The provider does not expose two traffic-addressable revisions.",capacityMultiplier:2,costImpact:"temporary",rollback:"Return all traffic to the preserved blue target."},{strategy:"canary",supported:i("canary",n),explanation:n?e.hasHealthGate?"Shift bounded traffic steps while evaluating health.":"Canary requires a health gate and measurable traffic target.":"The provider does not expose weighted traffic primitives.",capacityMultiplier:1.25,costImpact:"temporary",rollback:"Set canary weight to zero and preserve its diagnostics."}]}function To(e,t){let r=Di(e).find((n)=>n.strategy===t);if(!r.supported)throw Error(`${t} is unavailable: ${r.explanation}`);return r}import{createHash as Bb}from"node:crypto";var jb=/^sha256:[a-f0-9]{64}$/,Fb=/^[a-f0-9]{40,64}$/i;function nr(e){return typeof e==="string"&&e?e:void 0}function un(e,t){try{return JSON.parse(String(e))}catch{return t}}function Rl(e){return{id:String(e.id),organizationId:String(e.organization_id),digest:String(e.digest),kind:String(e.kind),uri:String(e.uri),size:Number(e.size),mediaType:String(e.media_type),provenance:un(e.provenance,{}),attestation:un(e.attestation,{}),verifiedAt:String(e.verified_at),createdAt:String(e.created_at)}}function Pl(e){return{id:String(e.id),organizationId:String(e.organization_id),projectId:String(e.project_id),environmentId:String(e.environment_id),resourceId:String(e.resource_id),artifactId:String(e.artifact_id),kind:String(e.kind),sourceSha:nr(e.source_sha),configHash:String(e.config_hash),manifest:un(e.manifest,{}),provenance:un(e.provenance,{}),strategy:String(e.strategy),status:String(e.status),healthGate:e.health_gate==null?void 0:un(e.health_gate,void 0),hooks:un(e.hooks,{migrations:"none",preActivate:[],postActivate:[]}),drainSeconds:Number(e.drain_seconds),graceSeconds:Number(e.grace_seconds),automaticRollback:Number(e.automatic_rollback)===1,rollbackAttempts:Number(e.rollback_attempts),previousReleaseId:nr(e.previous_release_id),promotedFromReleaseId:nr(e.promoted_from_release_id),actorId:nr(e.actor_id),trigger:String(e.trigger),pinned:Number(e.pinned)===1,pinReason:nr(e.pin_reason),activatedAt:nr(e.activated_at),failedAt:nr(e.failed_at),createdAt:String(e.created_at),updatedAt:String(e.updated_at)}}function Ub(e){return{sequence:Number(e.sequence),id:String(e.id),releaseId:String(e.release_id),fromStatus:nr(e.from_status),toStatus:String(e.to_status),trafficPercent:e.traffic_percent==null?void 0:Number(e.traffic_percent),health:un(e.health,{}),message:String(e.message),operationId:nr(e.operation_id),createdAt:String(e.created_at)}}function zb(e,t){let r=[],n=(i,o,s)=>{if(JSON.stringify(o)===JSON.stringify(s))return;if(o&&s&&typeof o==="object"&&typeof s==="object"&&!Array.isArray(o)&&!Array.isArray(s)){for(let c of[...new Set([...Object.keys(o),...Object.keys(s)])].sort())n(i?`${i}.${c}`:c,o[c],s[c]);return}r.push({path:i,...o!==void 0?{before:o}:{},...s!==void 0?{after:s}:{}})};return n("",e,t),r}class La{controlPlane;now;id;constructor(e,t={}){this.controlPlane=e;this.now=t.now??(()=>new Date),this.id=t.id??(()=>crypto.randomUUID())}registerArtifact(e){if(!jb.test(e.digest)||!/^(?:s3|https|oci|file):/.test(e.uri)||!Number.isInteger(e.size)||e.size<0)throw Error("Artifact requires a SHA-256 digest, supported immutable URI, and non-negative byte size");let t=this.controlPlane.database.query("SELECT * FROM release_artifacts WHERE organization_id=? AND digest=?").get(e.organizationId,e.digest);if(t)return Rl(t);let r=this.id(),n=this.now().toISOString();return this.controlPlane.database.run("INSERT INTO release_artifacts (id, organization_id, digest, kind, uri, size, media_type, provenance, attestation, verified_at, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",[r,e.organizationId,e.digest,e.kind,e.uri,e.size,e.mediaType??"application/octet-stream",JSON.stringify($e(e.provenance??{})),JSON.stringify($e(e.attestation??{})),n,n]),this.getArtifact(r)}getArtifact(e){let t=this.controlPlane.database.query("SELECT * FROM release_artifacts WHERE id=?").get(e);return t?Rl(t):void 0}create(e){let t=this.getArtifact(e.artifactId),r=this.controlPlane.getResource(e.resourceId),n=this.controlPlane.listEnvironments(e.projectId).find((f)=>f.id===e.environmentId);if(!t||t.organizationId!==e.organizationId||t.kind!==e.kind)throw Error("Release artifact does not match the organization and deployable kind");if(!r||r.projectId!==e.projectId||r.environmentId!==e.environmentId||!n)throw Error("Release target scope does not match the resource");if(e.sourceSha&&!Fb.test(e.sourceSha))throw Error("Release source must be an immutable commit SHA");To({kind:e.kind,provider:r.provider,hasHealthGate:!!e.healthGate,replicas:e.replicas},e.strategy);let i=$e(e.config),o=Bb("sha256").update(JSON.stringify(i)).digest("hex"),s=this.active(e.resourceId),c=this.id(),u=this.now().toISOString(),a=e.approvalRequired?"awaiting_approval":"built",p=e.hooks??{migrations:"none",preActivate:[],postActivate:[]};return this.controlPlane.database.run("INSERT INTO releases (id, organization_id, project_id, environment_id, resource_id, artifact_id, kind, source_sha, config_hash, manifest, provenance, strategy, status, health_gate, hooks, drain_seconds, grace_seconds, automatic_rollback, previous_release_id, promoted_from_release_id, actor_id, trigger, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",[c,e.organizationId,e.projectId,e.environmentId,e.resourceId,e.artifactId,e.kind,e.sourceSha??null,o,JSON.stringify($e(e.manifest)),JSON.stringify($e(e.provenance??{})),e.strategy,a,e.healthGate?JSON.stringify(e.healthGate):null,JSON.stringify(p),Math.max(0,Math.min(3600,Math.floor(e.drainSeconds??30))),Math.max(0,Math.min(3600,Math.floor(e.graceSeconds??30))),e.automaticRollback===!1?0:1,s?.id??null,e.promotedFromReleaseId??null,e.actorId??null,e.trigger??"manual",u,u]),this.recordTransition(c,void 0,a,{message:e.promotedFromReleaseId?"Immutable artifact promoted without rebuild.":"Immutable release record created."}),this.get(c)}get(e){let t=this.controlPlane.database.query("SELECT * FROM releases WHERE id=?").get(e);return t?Pl(t):void 0}list(e={}){return this.controlPlane.database.query("SELECT * FROM releases ORDER BY created_at DESC, rowid DESC").all().map(Pl).filter((t)=>(!e.projectId||t.projectId===e.projectId)&&(!e.environmentId||t.environmentId===e.environmentId)&&(!e.resourceId||t.resourceId===e.resourceId)&&(!e.status||t.status===e.status))}active(e){return this.list({resourceId:e,status:"active"})[0]}transitions(e){return this.controlPlane.database.query("SELECT * FROM release_transitions WHERE release_id=? ORDER BY sequence").all(e).map(Ub)}recordTransition(e,t,r,n){this.controlPlane.database.run("INSERT INTO release_transitions (id, release_id, from_status, to_status, traffic_percent, health, message, operation_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",[this.id(),e,t??null,r,n.trafficPercent??null,JSON.stringify($e(n.health??{})),n.message.slice(0,1000),n.operationId??null,this.now().toISOString()])}progress(e,t){let r=this.get(e);if(!r)throw Error(`Release ${e} was not found`);return this.recordTransition(e,r.status,r.status,t),this.transitions(e).at(-1)}transition(e,t,r){let n=this.get(e);if(!n)throw Error(`Release ${e} was not found`);if(!{built:["awaiting_approval","activating","failed"],awaiting_approval:["built","failed"],activating:["active","failed","rolled_back"],active:["superseded","failed","rolled_back"],failed:["activating","rolled_back"],rolled_back:["activating"],superseded:["activating"]}[n.status].includes(t))throw Error(`Invalid release transition ${n.status} → ${t}`);let o=this.now().toISOString();if(t==="active"){let s=this.active(n.resourceId);if(s&&s.id!==e)this.controlPlane.database.run("UPDATE releases SET status='superseded', updated_at=? WHERE id=?",[o,s.id]),this.recordTransition(s.id,"active","superseded",{message:`Superseded by ${e}.`,operationId:r.operationId})}return this.controlPlane.database.run("UPDATE releases SET status=?, rollback_attempts=rollback_attempts+?, activated_at=CASE WHEN ?='active' THEN ? ELSE activated_at END, failed_at=CASE WHEN ?='failed' THEN ? ELSE failed_at END, updated_at=? WHERE id=?",[t,t==="rolled_back"?1:0,t,o,t,o,o,e]),this.recordTransition(e,n.status,t,r),this.get(e)}approve(e,t){let r=this.get(e);if(!r||r.status!=="awaiting_approval")throw Error("Release is not awaiting approval");let n=this.id(),i=this.now().toISOString();return this.controlPlane.database.run("INSERT INTO release_approvals (id, release_id, environment_id, decision, actor_id, comment, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)",[n,e,r.environmentId,t.decision,t.actorId,t.comment?.slice(0,1000)??null,i]),this.transition(e,t.decision==="approved"?"built":"failed",{message:t.decision==="approved"?"Environment gate approved.":"Environment gate rejected."})}approvals(e){return this.controlPlane.database.query("SELECT * FROM release_approvals WHERE release_id=? ORDER BY created_at").all(e).map((t)=>({id:String(t.id),releaseId:String(t.release_id),environmentId:String(t.environment_id),decision:String(t.decision),actorId:String(t.actor_id),comment:nr(t.comment),createdAt:String(t.created_at)}))}promote(e,t){let r=this.get(e);if(!r||!["active","superseded"].includes(r.status))throw Error("Only a tested active/superseded release can be promoted");return this.create({organizationId:r.organizationId,projectId:r.projectId,environmentId:t.targetEnvironmentId,resourceId:t.targetResourceId,artifactId:r.artifactId,kind:r.kind,sourceSha:r.sourceSha,config:t.config,manifest:r.manifest,provenance:r.provenance,strategy:t.strategy??r.strategy,healthGate:t.healthGate??r.healthGate,hooks:r.hooks,drainSeconds:r.drainSeconds,graceSeconds:r.graceSeconds,automaticRollback:r.automaticRollback,promotedFromReleaseId:r.id,actorId:t.actorId,trigger:"promotion",approvalRequired:t.approvalRequired})}compare(e,t){let r=this.get(e),n=this.get(t);if(!r||!n)throw Error("Both releases are required for comparison");return{artifactChanged:r.artifactId!==n.artifactId,sourceChanged:r.sourceSha!==n.sourceSha,configChanged:r.configHash!==n.configHash,manifestChanges:zb(r.manifest,n.manifest),dataCaveat:n.hooks.migrations==="none"?"No database migration is recorded.":`Database hooks are ${n.hooks.migrations}; release rollback does not roll back data.`}}pin(e,t,r){if(!this.get(e))throw Error("Release was not found");return this.controlPlane.database.run("UPDATE releases SET pinned=?, pin_reason=?, updated_at=? WHERE id=?",[t?1:0,t?r?.slice(0,500)??"operator":null,this.now().toISOString(),e]),this.get(e)}retentionCandidates(e){let t=Math.max(1,Math.min(100,Math.floor(e.keepPerResource??10))),r=new Map;for(let n of this.list()){let i=r.get(n.resourceId)??[];i.push(n),r.set(n.resourceId,i)}return[...r.values()].flatMap((n)=>n.slice(t)).filter((n)=>new Date(n.createdAt)<e.before&&!n.pinned&&!["active","activating","awaiting_approval"].includes(n.status)&&!this.list().some((i)=>i.previousReleaseId===n.id&&["active","activating"].includes(i.status)))}}class ln{controlPlane;releases;queue;constructor(e){this.controlPlane=e;this.releases=new La(e),this.queue=new xe(e)}enqueueActivation(e,t={}){let r=this.releases.get(e.id);if(!r)throw Error("Release was not found");if(!["built","failed","rolled_back","superseded"].includes(r.status))throw Error(`Release ${r.status} cannot be activated`);let n=t.action??"activate",i=this.queue.enqueue({projectId:r.projectId,environmentId:r.environmentId,resourceId:r.resourceId,actorId:t.actorId,kind:`release.${n}`,idempotencyKey:`release:${r.id}:${n}:${r.rollbackAttempts}`,correlationId:`release:${r.id}`,input:{releaseId:r.id,targetReleaseId:t.targetReleaseId??null},lockKey:`resource:${r.resourceId}`,providerKey:this.controlPlane.getResource(r.resourceId)?.provider??"default",maxAttempts:r.automaticRollback?2:1,retryClasses:["network","provider_throttled","provider_unavailable"],resumePolicy:"fail",cancellationMode:"provider_non_cancellable",retentionDays:365}).operation;return this.releases.transition(r.id,"activating",{message:`${n} queued.`,operationId:i.id,trafficPercent:0}),i}completeHealthGate(e,t){let r=this.releases.get(e);if(!r||r.status!=="activating")throw Error("Release is not activating");if(t.healthy)return{release:this.releases.transition(r.id,"active",{message:t.message??"Health gate passed; activation completed.",operationId:t.operationId,trafficPercent:100,health:t.health})};let n=this.releases.transition(r.id,"failed",{message:t.message??"Health gate failed.",operationId:t.operationId,trafficPercent:0,health:t.health}),i=r.previousReleaseId?this.releases.get(r.previousReleaseId):void 0;if(!r.automaticRollback||r.rollbackAttempts>=1||!i)return{release:n};let o=this.queue.enqueue({projectId:r.projectId,environmentId:r.environmentId,resourceId:r.resourceId,kind:"release.rollback",idempotencyKey:`release:${r.id}:automatic-rollback`,correlationId:`release:${r.id}`,input:{releaseId:r.id,targetReleaseId:i.id,automatic:!0},lockKey:`resource:${r.resourceId}`,providerKey:this.controlPlane.getResource(r.resourceId)?.provider??"default",maxAttempts:1,retryClasses:[],resumePolicy:"fail",cancellationMode:"provider_non_cancellable",retentionDays:365}).operation;return{release:n,rollbackOperation:o}}enqueueRollback(e,t={}){let r=this.releases.get(e.id);if(!r||r.status!=="active")throw Error("Only the active release can be rolled back");let n=t.targetReleaseId?this.releases.get(t.targetReleaseId):r.previousReleaseId?this.releases.get(r.previousReleaseId):void 0;if(!n||n.resourceId!==r.resourceId||!["superseded","active"].includes(n.status))throw Error("Rollback requires a preserved prior release for the same resource");return this.queue.enqueue({projectId:r.projectId,environmentId:r.environmentId,resourceId:r.resourceId,actorId:t.actorId,kind:"release.rollback",idempotencyKey:`release:${r.id}:manual-rollback:${n.id}`,correlationId:`release:${r.id}`,input:{releaseId:r.id,targetReleaseId:n.id,automatic:!1},lockKey:`resource:${r.resourceId}`,providerKey:this.controlPlane.getResource(r.resourceId)?.provider??"default",maxAttempts:1,retryClasses:[],resumePolicy:"fail",cancellationMode:"provider_non_cancellable",retentionDays:365}).operation}}function Vb(e){if(e.strategy==="canary")return[5,25,50,100].map((t,r)=>({step:`canary-${t}`,trafficPercent:t,waitSeconds:r===3?e.drainSeconds:e.graceSeconds,requiresHealth:!0}));if(e.strategy==="blue_green")return[{step:"green-health",trafficPercent:0,waitSeconds:e.graceSeconds,requiresHealth:!0},{step:"traffic-switch",trafficPercent:100,waitSeconds:e.drainSeconds,requiresHealth:!0}];if(e.strategy==="rolling")return[{step:"rolling-replacement",trafficPercent:50,waitSeconds:e.graceSeconds,requiresHealth:!0},{step:"rolling-complete",trafficPercent:100,waitSeconds:e.drainSeconds,requiresHealth:!0}];return[{step:"immutable-switch",trafficPercent:100,waitSeconds:e.drainSeconds,requiresHealth:!!e.healthGate}]}async function xl(e,t){if(t.release.artifactId!==t.artifact.id)throw Error("Release activation artifact identity changed");To({kind:t.release.kind,hasHealthGate:!!t.release.healthGate,replicas:Number(t.release.manifest?.replicas)||void 0},t.release.strategy);let r=e.capability(t);if(!r.supported)throw Error(`${t.release.strategy} is unavailable in ${e.name}: ${r.explanation}`);return e.activate({...t,transitions:Vb(t.release)})}async function Dl(e,t){if(!t.previous)throw Error("Rollback requires the preserved previous release");return e.rollback({...t,transitions:[{step:"rollback",trafficPercent:0,waitSeconds:t.release.drainSeconds,requiresHealth:!0}]})}var qb=["release.activate","release.rollback"];function Jb(e){return e&&typeof e==="object"&&!Array.isArray(e)?e:{}}function Ol(e){let t=new ln(e.store),r=async(n)=>{let i=Jb(n.operation.input),o=typeof i.releaseId==="string"?i.releaseId:"",s=t.releases.get(o);if(!s)throw Error(`Queued release ${o||"(missing)"} was not found`);if(s.projectId!==n.operation.projectId||s.environmentId!==n.operation.environmentId||s.resourceId!==n.operation.resourceId)throw Error("Queued release does not match the operation scope");let c=t.releases.getArtifact(s.artifactId);if(!c||c.digest!==t.releases.getArtifact(s.artifactId)?.digest)throw Error("Immutable release artifact is unavailable");let u=typeof i.targetReleaseId==="string"&&i.targetReleaseId?i.targetReleaseId:s.previousReleaseId,a=u?t.releases.get(u):void 0;if(a&&(a.resourceId!==s.resourceId||a.artifactId===s.artifactId&&a.id===s.id))throw Error("Rollback target does not match the release resource");let p=await e.resolveDriver(s);n.checkpoint("verify",`Verifying ${c.digest} and ${s.configHash} for ${p.name}.`),n.log(`Artifact ${c.digest}; source ${s.sourceSha??"not recorded"}; config ${s.configHash}.`,{stream:"system"}),n.throwIfCancellationRequested();let f;try{f=n.operation.kind==="release.rollback"?await Dl(p,{release:s,artifact:c,previous:a}):await xl(p,{release:s,artifact:c,previous:a})}catch(y){if(n.operation.kind==="release.activate"&&t.releases.get(s.id)?.status==="activating")t.completeHealthGate(s.id,{healthy:!1,operationId:n.operation.id,message:y instanceof Error?y.message:String(y)});throw y}for(let y of f.transitions)t.releases.progress(s.id,{message:`${n.operation.kind==="release.rollback"?"Rollback":"Activation"} traffic at ${y.trafficPercent}%.`,trafficPercent:y.trafficPercent,health:y.observed,operationId:n.operation.id}),n.log(`Traffic ${y.trafficPercent}%: ${y.healthy?"healthy":"unhealthy"}.`,{stream:y.healthy?"step":"stderr",step:"health"});if(n.checkpoint("health","Evaluating the provider health result."),!f.activated||!f.healthy){if(n.operation.kind==="release.activate"&&t.releases.get(s.id)?.status==="activating")t.completeHealthGate(s.id,{healthy:!1,operationId:n.operation.id,health:{resourceVersions:f.resourceVersions},message:f.error??"Provider health gate failed."});throw Error(f.error??`${p.name} reported an unhealthy release`)}if(n.operation.kind==="release.activate"&&s.healthGate?.name){if(!e.resolveHealthGate)throw t.completeHealthGate(s.id,{healthy:!1,operationId:n.operation.id,message:`Named health gate ${s.healthGate.name} is not available to this worker.`}),Error(`Named health gate ${s.healthGate.name} is unavailable`);n.checkpoint("named-health-gate",`Waiting on ${s.healthGate.name}.`);let y=await e.resolveHealthGate(s.healthGate.name,s);if(n.log(`${s.healthGate.name}: ${y.healthy?"healthy":"unhealthy"}${y.message?` — ${y.message}`:""}.`,{stream:y.healthy?"step":"stderr",step:"health"}),!y.healthy)throw t.completeHealthGate(s.id,{healthy:!1,operationId:n.operation.id,health:y.evidence,message:y.message??`Named health gate ${s.healthGate.name} failed.`}),Error(y.message??`Named health gate ${s.healthGate.name} failed`)}if(n.operation.kind==="release.rollback"){let y=t.releases.get(s.id);if(y.status!=="rolled_back")t.releases.transition(y.id,"rolled_back",{message:`Traffic restored through ${p.name}.`,operationId:n.operation.id,trafficPercent:0,health:{resourceVersions:f.resourceVersions}});if(a&&a.status!=="active"){let b=a.status==="activating"?a:t.releases.transition(a.id,"activating",{message:"Rollback target restoring traffic.",operationId:n.operation.id,trafficPercent:0});t.releases.transition(b.id,"active",{message:"Previous immutable release restored.",operationId:n.operation.id,trafficPercent:100,health:{resourceVersions:f.resourceVersions}})}}else t.completeHealthGate(s.id,{healthy:!0,operationId:n.operation.id,health:{providerVersion:f.providerVersion??null,resourceVersions:f.resourceVersions},message:"Provider activation and health gates completed."});return n.checkpoint("finalize","Persisted the terminal release state."),{releaseId:s.id,artifactDigest:c.digest,provider:p.name,providerVersion:f.providerVersion??null,resourceVersions:f.resourceVersions,status:t.releases.get(s.id).status}};return Object.fromEntries(qb.map((n)=>[n,r]))}import{createCipheriv as Hb,createDecipheriv as Wb,createHash as Ll,randomBytes as Ba}from"node:crypto";var Bl={github:{repositories:!0,branches:!0,tags:!0,webhooks:!0,pullRequests:!0,tokenRefresh:!0,deployKeys:!0},gitlab:{repositories:!0,branches:!0,tags:!0,webhooks:!0,pullRequests:!0,tokenRefresh:!0,deployKeys:!0},bitbucket:{repositories:!0,branches:!0,tags:!0,webhooks:!0,pullRequests:!0,tokenRefresh:!0,deployKeys:!0},gitea:{repositories:!0,branches:!0,tags:!0,webhooks:!0,pullRequests:!0,tokenRefresh:!1,deployKeys:!0},generic_https:{repositories:!1,branches:!0,tags:!0,webhooks:!1,pullRequests:!1,tokenRefresh:!1,deployKeys:!1},generic_ssh:{repositories:!1,branches:!0,tags:!0,webhooks:!1,pullRequests:!1,tokenRefresh:!1,deployKeys:!0}};function Te(e){return typeof e==="string"&&e?e:void 0}function gn(e,t){try{return typeof e==="string"?JSON.parse(e):t}catch{return t}}function ir(e){return JSON.stringify($e(e))}function pn(e){return Ll("sha256").update(e).digest("hex")}function _l(e){let t=e.trim().replace(/\/$/,""),r=new URL(t.includes("://")?t:`https://${t}`);if(r.username||r.password||r.search||r.hash)throw Error("Source host cannot contain credentials, a query, or a fragment");if(r.protocol!=="https:"&&!(r.protocol==="http:"&&["localhost","127.0.0.1","::1"].includes(r.hostname)))throw Error("Source host must use HTTPS");return`${r.protocol}//${r.host}${r.pathname.replace(/\/$/,"")}`}function Nl(e){let t=e.trim();if(t.length<2||t.length>80)throw Error("Connection name must contain 2-80 characters");return t}function ja(e){let t=e.trim().replace(/^\/+|\/+$/g,"");if(!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(t))throw Error("Repository must use owner/name format");return t}function $l(e){let t=e.trim()||".";if(t.startsWith("/")||t.split("/").includes("..")||/[\0\r\n]/.test(t))throw Error("Monorepo root must stay inside the repository");return t.replace(/^\.\//,"")||"."}function Ao(e){return[...new Set((e??[]).map((t)=>t.trim()).filter(Boolean))].slice(0,100)}function Gb(e){let t=e.trim();if(/\r|\n/.test(t)||/https?:\/\/[^/@\s]+:[^/@\s]+@/i.test(t))throw Error("Clone URL cannot contain credentials or control characters");if(!/^(?:https?:\/\/|ssh:\/\/|git@[A-Za-z0-9.-]+:)/.test(t))throw Error("Clone URL must use HTTPS or SSH");return t}function Kb(e){return{id:String(e.id),organizationId:String(e.organization_id),provider:String(e.provider),name:String(e.name),host:String(e.host),owner:Te(e.owner),authKind:String(e.auth_kind),credentialConfigured:!!e.credential_ciphertext,credentialFingerprint:Te(e.credential_fingerprint),grantedScopes:gn(e.granted_scopes,[]),capabilities:gn(e.capabilities,Bl[String(e.provider)]),status:String(e.status),healthMessage:Te(e.health_message),lastTestedAt:Te(e.last_tested_at),lastSyncedAt:Te(e.last_synced_at),credentialExpiresAt:Te(e.credential_expires_at),version:Number(e.version),createdByActorId:Te(e.created_by_actor_id),createdAt:String(e.created_at),updatedAt:String(e.updated_at)}}function Fa(e){return{id:String(e.id),connectionId:String(e.connection_id),providerRepositoryId:String(e.provider_repository_id),fullName:String(e.full_name),cloneUrl:String(e.clone_url),defaultBranch:String(e.default_branch),visibility:String(e.visibility),archived:Number(e.archived)===1,metadata:gn(e.metadata,{}),syncedAt:String(e.synced_at)}}function Ml(e){return{id:String(e.id),projectId:String(e.project_id),environmentId:Te(e.environment_id),resourceId:Te(e.resource_id),connectionId:String(e.connection_id),repositoryId:Te(e.repository_id),repositoryFullName:String(e.repository_full_name),defaultBranch:String(e.default_branch),branchRule:Te(e.branch_rule),tagRule:Te(e.tag_rule),monorepoRoot:String(e.monorepo_root),includePaths:gn(e.include_paths,[]),excludePaths:gn(e.exclude_paths,[]),submodules:Number(e.submodules)===1,cloneDepth:e.clone_depth==null?void 0:Number(e.clone_depth),deployKeyId:Te(e.deploy_key_id),autoDeploy:Number(e.auto_deploy)===1,pullRequestPreviews:Number(e.pull_request_previews)===1,status:String(e.status),disabledReason:Te(e.disabled_reason),version:Number(e.version),createdByActorId:Te(e.created_by_actor_id),createdAt:String(e.created_at),updatedAt:String(e.updated_at)}}function Ua(e){return{id:String(e.id),connectionId:String(e.connection_id),name:String(e.name),publicKey:String(e.public_key),publicKeyFingerprint:String(e.public_key_fingerprint),host:String(e.host),hostKey:String(e.host_key),hostKeyFingerprint:String(e.host_key_fingerprint),createdByActorId:Te(e.created_by_actor_id),createdAt:String(e.created_at),updatedAt:String(e.updated_at)}}function za(e){return{id:String(e.id),connectionId:String(e.connection_id),repositoryId:Te(e.repository_id),repositoryFullName:String(e.repository_full_name),providerWebhookId:Te(e.provider_webhook_id),events:gn(e.events,[]),status:String(e.status),healthMessage:Te(e.health_message),lastDeliveryAt:Te(e.last_delivery_at),lastReconciledAt:Te(e.last_reconciled_at),createdAt:String(e.created_at),updatedAt:String(e.updated_at)}}function Va(e){return{id:String(e.id),connectionId:String(e.connection_id),webhookId:String(e.webhook_id),providerDeliveryId:String(e.provider_delivery_id),event:String(e.event),action:Te(e.action),commitSha:Te(e.commit_sha),signatureStatus:String(e.signature_status),status:String(e.status),payloadSummary:gn(e.payload_summary,{}),operationId:Te(e.operation_id),error:Te(e.error),receivedAt:String(e.received_at),processedAt:Te(e.processed_at)}}class qa{controlPlane;key;nowFn;idFn;constructor(e,t={}){this.controlPlane=e;this.key=t.encryptionKey?Ll("sha256").update(t.encryptionKey).digest():void 0,this.nowFn=t.now??(()=>new Date),this.idFn=t.id??(()=>crypto.randomUUID())}now(){return this.nowFn().toISOString()}run(e,t){this.controlPlane.database.run(e,t)}encrypt(e){if(!this.key)throw Error("Source credential encryption key is not configured");let t=Ba(12),r=Hb("aes-256-gcm",this.key,t),n=Buffer.concat([r.update(e,"utf8"),r.final()]);return`v1.${t.toString("base64url")}.${r.getAuthTag().toString("base64url")}.${n.toString("base64url")}`}decrypt(e){if(!this.key)throw Error("Source credential encryption key is not configured");let[t,r,n,i]=e.split(".");if(t!=="v1"||!r||!n||!i)throw Error("Encrypted source credential is unavailable");let o=Wb("aes-256-gcm",this.key,Buffer.from(r,"base64url"));return o.setAuthTag(Buffer.from(n,"base64url")),Buffer.concat([o.update(Buffer.from(i,"base64url")),o.final()]).toString("utf8")}createConnection(e){if(!this.controlPlane.getOrganization(e.organizationId))throw Error("Source connection organization was not found");let t=this.now(),r=this.idFn(),n=e.credential?JSON.stringify(e.credential):void 0;return this.run(`INSERT INTO source_connections (id, organization_id, provider, name, host, owner, auth_kind, credential_ciphertext, credential_fingerprint, granted_scopes, capabilities, status, credential_expires_at, created_by_actor_id, created_at, updated_at)
3
3
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?, ?)`,[r,e.organizationId,e.provider,Nl(e.name),_l(e.host),e.owner?.trim()||null,e.authKind??(n?"access_token":"none"),n?this.encrypt(n):null,n?pn(n).slice(0,16):null,ir([...new Set(e.grantedScopes??[])].sort()),ir(Bl[e.provider]),e.credentialExpiresAt??null,e.createdByActorId??null,t,t]),this.controlPlane.appendEvent({organizationId:e.organizationId,actorId:e.createdByActorId,type:"source.connection.created",payload:{connectionId:r,provider:e.provider,host:_l(e.host),owner:e.owner??null,scopes:e.grantedScopes??[]}}),this.getConnection(r)}getConnection(e){let t=this.controlPlane.database.query("SELECT * FROM source_connections WHERE id = ?").get(e);return t?this.currentConnection(t):void 0}listConnections(e){return this.controlPlane.database.query("SELECT * FROM source_connections WHERE organization_id = ? ORDER BY name").all(e).map((t)=>this.currentConnection(t))}currentConnection(e){let t=Kb(e);if(t.status!=="disconnected"&&t.credentialExpiresAt&&new Date(t.credentialExpiresAt).getTime()<=this.nowFn().getTime())return{...t,status:"expired",healthMessage:"Source credential expired; rotate it before deploying."};return t}getCredential(e){let t=this.controlPlane.database.query("SELECT credential_ciphertext FROM source_connections WHERE id = ?").get(e);return t?.credential_ciphertext?JSON.parse(this.decrypt(String(t.credential_ciphertext))):void 0}rotateCredential(e,t,r={}){let n=this.getConnection(e);if(!n||n.status==="disconnected")throw Error("Active source connection was not found");let i=JSON.stringify(t);return this.run("UPDATE source_connections SET credential_ciphertext = ?, credential_fingerprint = ?, credential_expires_at = ?, status = 'pending', health_message = NULL, version = version + 1, updated_at = ? WHERE id = ?",[this.encrypt(i),pn(i).slice(0,16),r.expiresAt??null,this.now(),e]),this.controlPlane.appendEvent({organizationId:n.organizationId,actorId:r.actorId,type:"source.credential.rotated",payload:{connectionId:e,fingerprint:pn(i).slice(0,16),expiresAt:r.expiresAt??null}}),this.getConnection(e)}updateHealth(e,t){let r=this.getConnection(e);if(!r||["disconnected","expired"].includes(r.status))throw Error("Active source connection was not found");let n=this.now(),i=t.grantedScopes?[...new Set(t.grantedScopes.map((o)=>o.trim()).filter(Boolean))].sort():void 0;if(this.run("UPDATE source_connections SET status = ?, health_message = ?, granted_scopes = COALESCE(?, granted_scopes), last_tested_at = COALESCE(?, last_tested_at), last_synced_at = COALESCE(?, last_synced_at), version = version + 1, updated_at = ? WHERE id = ?",[t.status,t.message?.slice(0,1000)??null,i?ir(i):null,t.tested?n:null,t.synced?n:null,n,e]),i&&JSON.stringify(i)!==JSON.stringify([...r.grantedScopes].sort()))this.controlPlane.appendEvent({organizationId:r.organizationId,type:"source.connection.scopes_updated",payload:{connectionId:e,grantedScopes:i}});return this.getConnection(e)}upsertRepository(e){let t=this.getConnection(e.connectionId);if(!t||["disconnected","expired"].includes(t.status))throw Error("Active source connection was not found");let r=this.now(),n=this.controlPlane.database.query("SELECT id FROM source_repositories WHERE connection_id = ? AND provider_repository_id = ?").get(e.connectionId,e.providerRepositoryId),i=n?String(n.id):this.idFn();return this.run(`INSERT INTO source_repositories (id, connection_id, provider_repository_id, full_name, clone_url, default_branch, visibility, archived, metadata, synced_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
4
4
  ON CONFLICT(connection_id, provider_repository_id) DO UPDATE SET full_name=excluded.full_name, clone_url=excluded.clone_url, default_branch=excluded.default_branch, visibility=excluded.visibility, archived=excluded.archived, metadata=excluded.metadata, synced_at=excluded.synced_at`,[i,e.connectionId,e.providerRepositoryId,ja(e.fullName),Gb(e.cloneUrl),e.defaultBranch.trim()||"main",e.visibility,e.archived?1:0,ir(e.metadata),r]),Fa(this.controlPlane.database.query("SELECT * FROM source_repositories WHERE id = ?").get(i))}listRepositories(e,t,r=100){let n=t?.trim().toLowerCase();return(n?this.controlPlane.database.query("SELECT * FROM source_repositories WHERE connection_id = ? AND lower(full_name) LIKE ? ORDER BY full_name LIMIT ?").all(e,`%${n}%`,Math.min(500,r)):this.controlPlane.database.query("SELECT * FROM source_repositories WHERE connection_id = ? ORDER BY full_name LIMIT ?").all(e,Math.min(500,r))).map(Fa)}getRepository(e){let t=this.controlPlane.database.query("SELECT * FROM source_repositories WHERE id = ?").get(e);return t?Fa(t):void 0}createDeployKey(e){let t=this.getConnection(e.connectionId);if(!t||["disconnected","expired"].includes(t.status)||!t.capabilities.deployKeys)throw Error("An active deploy-key capable source connection is required");if(!/^ssh-(?:ed25519|rsa)\s+[A-Za-z0-9+/=]+/.test(e.publicKey.trim()))throw Error("A valid SSH public key is required");if(!/BEGIN (?:OPENSSH|RSA) PRIVATE KEY/.test(e.privateKey))throw Error("A valid SSH private key is required");if(!/^(?:ssh-ed25519|ssh-rsa|ecdsa-sha2-nistp\d+)\s+[A-Za-z0-9+/=]+/.test(e.hostKey.trim()))throw Error("A pinned SSH host key is required");let r=this.idFn(),n=this.now(),i=`sha256:${pn(e.publicKey.trim())}`,o=`sha256:${pn(e.hostKey.trim())}`;return this.run("INSERT INTO source_deploy_keys (id, connection_id, name, public_key, public_key_fingerprint, private_key_ciphertext, host, host_key, host_key_fingerprint, created_by_actor_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",[r,e.connectionId,Nl(e.name),e.publicKey.trim(),i,this.encrypt(e.privateKey),e.host.trim().toLowerCase(),e.hostKey.trim(),o,e.actorId??null,n,n]),this.controlPlane.appendEvent({organizationId:t.organizationId,actorId:e.actorId,type:"source.deploy_key.created",payload:{connectionId:e.connectionId,deployKeyId:r,publicFingerprint:i,hostFingerprint:o}}),Ua(this.controlPlane.database.query("SELECT * FROM source_deploy_keys WHERE id = ?").get(r))}getDeployKey(e){let t=this.controlPlane.database.query("SELECT * FROM source_deploy_keys WHERE id = ?").get(e);return t?Ua(t):void 0}listDeployKeys(e){return this.controlPlane.database.query("SELECT * FROM source_deploy_keys WHERE connection_id = ? ORDER BY name, created_at").all(e).map(Ua)}revokeDeployKey(e,t){let r=this.getDeployKey(e),n=r?this.getConnection(r.connectionId):void 0;if(!r||!n)throw Error("Deploy key was not found");let i=this.listBindings({connectionId:n.id}).filter((s)=>s.deployKeyId===e),o=this.now();return this.controlPlane.database.transaction(()=>{this.run("UPDATE source_bindings SET deploy_key_id=NULL, status='disabled', auto_deploy=0, disabled_reason='Source deploy key was revoked', version=version+1, updated_at=? WHERE deploy_key_id=?",[o,e]),this.run("DELETE FROM source_deploy_keys WHERE id=?",[e])})(),this.controlPlane.appendEvent({organizationId:n.organizationId,actorId:t,type:"source.deploy_key.revoked",level:"warning",payload:{connectionId:n.id,deployKeyId:e,affectedBindingIds:i.map((s)=>s.id)}}),{affectedBindings:i}}getDeployPrivateKey(e){let t=this.controlPlane.database.query("SELECT private_key_ciphertext FROM source_deploy_keys WHERE id = ?").get(e);if(!t)throw Error("Deploy key was not found");return this.decrypt(String(t.private_key_ciphertext))}createBinding(e){let t=this.getConnection(e.connectionId);if(!t||["disconnected","expired"].includes(t.status))throw Error("Active source connection was not found");if(e.repositoryId&&this.getRepository(e.repositoryId)?.connectionId!==t.id)throw Error("Repository does not belong to this connection");if(e.deployKeyId&&this.getDeployKey(e.deployKeyId)?.connectionId!==t.id)throw Error("Deploy key does not belong to this connection");let r=this.idFn(),n=this.now();return this.run(`INSERT INTO source_bindings (id, project_id, environment_id, resource_id, connection_id, repository_id, repository_full_name, default_branch, branch_rule, tag_rule, monorepo_root, include_paths, exclude_paths, submodules, clone_depth, deploy_key_id, auto_deploy, pull_request_previews, status, created_by_actor_id, created_at, updated_at)