@stacksjs/ts-cloud 0.16.44 → 0.16.46

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 (178) hide show
  1. package/dist/aws/index.js +1 -1
  2. package/dist/bin/{chunk-gn03w47w.js → chunk-01w4zbex.js} +1 -1
  3. package/dist/bin/{chunk-nmqjmx30.js → chunk-12x968en.js} +1 -1
  4. package/dist/bin/chunk-1xxmyx0r.js +1 -0
  5. package/dist/bin/{chunk-kdsgbvep.js → chunk-23m8kbwh.js} +1 -1
  6. package/dist/bin/{chunk-bj7ashn6.js → chunk-2gqhzn2r.js} +1 -1
  7. package/dist/bin/chunk-45qr2bd3.js +1 -0
  8. package/dist/bin/chunk-4nn00brw.js +1 -0
  9. package/dist/bin/chunk-5f1n5d80.js +1 -0
  10. package/dist/bin/{chunk-s64d5nw2.js → chunk-5mm6qn6m.js} +1 -1
  11. package/dist/bin/chunk-5xr76cms.js +1 -0
  12. package/dist/bin/{chunk-q40k9jvk.js → chunk-65yvjecx.js} +2 -2
  13. package/dist/bin/{chunk-5amvqed8.js → chunk-6y6g726g.js} +3 -3
  14. package/dist/bin/{chunk-f64jvh96.js → chunk-7qbxq0be.js} +2 -2
  15. package/dist/bin/{chunk-8z4wgqkh.js → chunk-cz2wq0tf.js} +1 -1
  16. package/dist/bin/chunk-d0tmmzy5.js +1 -0
  17. package/dist/bin/{chunk-7p2va3nj.js → chunk-de69nra4.js} +1 -1
  18. package/dist/bin/{chunk-tf8qxy9k.js → chunk-dj03v81n.js} +20 -20
  19. package/dist/bin/chunk-dn1yybdy.js +1 -0
  20. package/dist/bin/chunk-fqghdpvr.js +1 -0
  21. package/dist/bin/{chunk-35x7z78g.js → chunk-frzpqsbj.js} +1 -1
  22. package/dist/bin/chunk-gkdr09xx.js +1 -0
  23. package/dist/bin/{chunk-xxe9ga9r.js → chunk-gyvkravq.js} +3 -3
  24. package/dist/bin/{chunk-jaexpxs8.js → chunk-her9ny96.js} +1 -1
  25. package/dist/bin/{chunk-3hkppc7h.js → chunk-kqxk1pds.js} +1 -1
  26. package/dist/bin/{chunk-hfnpjy9y.js → chunk-n2c6y0k1.js} +1 -1
  27. package/dist/bin/{chunk-3v2pd05g.js → chunk-nhqz2j90.js} +1 -1
  28. package/dist/bin/{chunk-erf79tm2.js → chunk-nmr484r7.js} +21 -21
  29. package/dist/bin/chunk-pr1dg1km.js +1 -0
  30. package/dist/bin/{chunk-dkf2kcsc.js → chunk-psvqpztv.js} +3 -3
  31. package/dist/bin/{chunk-ce1qnjs2.js → chunk-pzj1ac7x.js} +1 -1
  32. package/dist/bin/{chunk-gngsb689.js → chunk-q4dywv80.js} +2 -2
  33. package/dist/bin/{chunk-pf9dbm3b.js → chunk-q8b77zgw.js} +1 -1
  34. package/dist/bin/{chunk-qj6tr7bw.js → chunk-qq1f07te.js} +3 -3
  35. package/dist/bin/chunk-r1kg6yxp.js +1 -0
  36. package/dist/bin/{chunk-mgwmt2et.js → chunk-rdkr74y2.js} +2 -2
  37. package/dist/bin/{chunk-jh1vmant.js → chunk-rgcrsd5m.js} +1 -1
  38. package/dist/bin/{chunk-t80pxn04.js → chunk-rxy9fa64.js} +6 -6
  39. package/dist/bin/chunk-t1mmfpn1.js +1 -0
  40. package/dist/bin/chunk-thph4z3k.js +1 -0
  41. package/dist/bin/chunk-txykq6f1.js +1 -0
  42. package/dist/bin/{chunk-2r9tj1ac.js → chunk-v09tfezh.js} +5 -5
  43. package/dist/bin/{chunk-dryvqne2.js → chunk-v7t6f8aw.js} +2 -2
  44. package/dist/bin/{chunk-knzje4ka.js → chunk-vjg2fymw.js} +1 -1
  45. package/dist/bin/chunk-vt3hjh9q.js +1 -0
  46. package/dist/bin/{chunk-sg8hcdev.js → chunk-w29z877b.js} +1 -1
  47. package/dist/bin/{chunk-7593hjcq.js → chunk-xdk0gmxt.js} +1 -1
  48. package/dist/bin/{chunk-fkzp9sg5.js → chunk-xe527q1k.js} +1 -1
  49. package/dist/bin/{chunk-9eqpnanr.js → chunk-xx9mrcfs.js} +1 -1
  50. package/dist/bin/{chunk-vjfwnj9z.js → chunk-ym9h629y.js} +4 -4
  51. package/dist/bin/{chunk-cqc4e8md.js → chunk-yp3a0j9n.js} +1 -1
  52. package/dist/bin/{chunk-kzpzkdpd.js → chunk-z8k9mcn5.js} +1 -1
  53. package/dist/bin/chunk-z8scg2vy.js +1 -0
  54. package/dist/bin/{chunk-pnj95hwr.js → chunk-znd507q9.js} +1 -1
  55. package/dist/bin/cli.js +98 -98
  56. package/dist/bin/dashboard-server.js +1 -1
  57. package/dist/{chunk-8jsf214f.js → chunk-1dbe68z7.js} +5 -5
  58. package/dist/{chunk-cgcthvq9.js → chunk-2sc0s7pc.js} +5 -5
  59. package/dist/{chunk-a102vqez.js → chunk-46bazfbh.js} +54 -54
  60. package/dist/chunk-4t0vj9a4.js +1 -0
  61. package/dist/{chunk-d02jxsxv.js → chunk-6ea5d30w.js} +1 -1
  62. package/dist/{chunk-0evxayn1.js → chunk-6rcj3km0.js} +2 -2
  63. package/dist/{chunk-e4pxs9g6.js → chunk-79snvzcx.js} +2 -2
  64. package/dist/{chunk-x2pxtfk0.js → chunk-astzz50p.js} +3 -3
  65. package/dist/chunk-bvzmbksp.js +1 -0
  66. package/dist/{chunk-ct72f70x.js → chunk-fh7apa5n.js} +1 -1
  67. package/dist/{chunk-txwnpxzf.js → chunk-j2jex596.js} +24 -24
  68. package/dist/{chunk-c5jyzyqw.js → chunk-j73ntebp.js} +74 -74
  69. package/dist/{chunk-bwtxb2hf.js → chunk-jpdpr7k6.js} +1 -1
  70. package/dist/{chunk-v3w791ze.js → chunk-jzpwfgg1.js} +12 -12
  71. package/dist/chunk-ksqhsz52.js +1 -0
  72. package/dist/{chunk-w78e5n94.js → chunk-nryh43p1.js} +3 -3
  73. package/dist/chunk-p588apka.js +1 -0
  74. package/dist/{chunk-3s8dtcz8.js → chunk-r86dh7h7.js} +1 -1
  75. package/dist/{chunk-7eaech9q.js → chunk-rpng90t9.js} +1 -1
  76. package/dist/chunk-s7q23exe.js +1 -0
  77. package/dist/{chunk-hm6pwhne.js → chunk-tj852ns1.js} +2 -2
  78. package/dist/{chunk-gp9tkznp.js → chunk-v1bzy3tp.js} +1 -1
  79. package/dist/{chunk-97gre81w.js → chunk-vb4t2r4r.js} +1 -1
  80. package/dist/{chunk-9e4afej1.js → chunk-vjph4eh0.js} +1 -1
  81. package/dist/{chunk-vkrgf3sv.js → chunk-wb7h8ag4.js} +19 -19
  82. package/dist/{chunk-j5x0qn7z.js → chunk-wmxw3pe4.js} +2 -2
  83. package/dist/chunk-x7d93rxb.js +1 -0
  84. package/dist/{chunk-fwcf0n8f.js → chunk-xxq1ygp9.js} +1 -1
  85. package/dist/{chunk-varjr6np.js → chunk-xzcpzxtj.js} +1 -1
  86. package/dist/{chunk-9mv07nz0.js → chunk-ynprwawa.js} +2 -2
  87. package/dist/{chunk-96vc5w8w.js → chunk-yxzdwe7j.js} +1 -1
  88. package/dist/{chunk-1w9v8g26.js → chunk-zwkm3pkb.js} +3 -3
  89. package/dist/deploy/index.js +1 -1
  90. package/dist/deploy/server-dns.d.ts +12 -1
  91. package/dist/dns/index.js +1 -1
  92. package/dist/drivers/factory.d.ts +3 -3
  93. package/dist/drivers/index.js +1 -1
  94. package/dist/fly/client.d.ts +153 -0
  95. package/dist/fly/deploy.d.ts +72 -0
  96. package/dist/fly/index.d.ts +2 -0
  97. package/dist/index.d.ts +2 -1
  98. package/dist/index.js +26 -26
  99. package/dist/operations/index.js +1 -1
  100. package/dist/protection/index.js +1 -1
  101. package/dist/spend/index.js +1 -1
  102. package/dist/ui/404.html +1 -1
  103. package/dist/ui/accept-invitation.html +1 -1
  104. package/dist/ui/access-denied.html +1 -1
  105. package/dist/ui/account/automation.html +5 -5
  106. package/dist/ui/account/security.html +2 -2
  107. package/dist/ui/applications/compose.html +6 -6
  108. package/dist/ui/applications/new.html +2 -2
  109. package/dist/ui/data/backups.html +6 -6
  110. package/dist/ui/data/services.html +6 -6
  111. package/dist/ui/data/volumes.html +5 -5
  112. package/dist/ui/index.html +6 -6
  113. package/dist/ui/infrastructure/topology.html +5 -5
  114. package/dist/ui/integrations.html +2 -2
  115. package/dist/ui/operations/alerts.html +6 -6
  116. package/dist/ui/operations/configuration.html +5 -5
  117. package/dist/ui/operations/jobs.html +6 -6
  118. package/dist/ui/operations/maintenance.html +5 -5
  119. package/dist/ui/operations/observability.html +5 -5
  120. package/dist/ui/operations/previews.html +6 -6
  121. package/dist/ui/operations/queue.html +6 -6
  122. package/dist/ui/operations/regions.html +5 -5
  123. package/dist/ui/operations/releases.html +6 -6
  124. package/dist/ui/operations/spend.html +6 -6
  125. package/dist/ui/operations/workloads.html +6 -6
  126. package/dist/ui/security.html +2 -2
  127. package/dist/ui/server/actions.html +5 -5
  128. package/dist/ui/server/activity.html +2 -2
  129. package/dist/ui/server/capacity.html +6 -6
  130. package/dist/ui/server/database.html +6 -6
  131. package/dist/ui/server/deployments.html +6 -6
  132. package/dist/ui/server/diagnostics.html +2 -2
  133. package/dist/ui/server/firewall.html +6 -6
  134. package/dist/ui/server/fleet.html +6 -6
  135. package/dist/ui/server/logs.html +6 -6
  136. package/dist/ui/server/metrics.html +6 -6
  137. package/dist/ui/server/security.html +2 -2
  138. package/dist/ui/server/services.html +2 -2
  139. package/dist/ui/server/sites.html +6 -6
  140. package/dist/ui/server/ssh-keys.html +6 -6
  141. package/dist/ui/server/team.html +6 -6
  142. package/dist/ui/server/terminal.html +2 -2
  143. package/dist/ui/serverless/alarms.html +6 -6
  144. package/dist/ui/serverless/assets.html +2 -2
  145. package/dist/ui/serverless/cost.html +2 -2
  146. package/dist/ui/serverless/data.html +6 -6
  147. package/dist/ui/serverless/deployments.html +2 -2
  148. package/dist/ui/serverless/firewall.html +1 -1
  149. package/dist/ui/serverless/functions.html +5 -5
  150. package/dist/ui/serverless/logs.html +6 -6
  151. package/dist/ui/serverless/metrics.html +2 -2
  152. package/dist/ui/serverless/queues.html +6 -6
  153. package/dist/ui/serverless/secrets.html +6 -6
  154. package/dist/ui/serverless/traces.html +6 -6
  155. package/dist/ui/serverless.html +5 -5
  156. package/package.json +3 -3
  157. package/dist/bin/chunk-26rf5dy2.js +0 -1
  158. package/dist/bin/chunk-5khcwxhn.js +0 -1
  159. package/dist/bin/chunk-5nc1g18b.js +0 -1
  160. package/dist/bin/chunk-71ep1g7k.js +0 -1
  161. package/dist/bin/chunk-7fh7b8r1.js +0 -1
  162. package/dist/bin/chunk-7nsjehvx.js +0 -1
  163. package/dist/bin/chunk-bb08ms5q.js +0 -1
  164. package/dist/bin/chunk-g3a9a8bx.js +0 -1
  165. package/dist/bin/chunk-jv9qp13h.js +0 -1
  166. package/dist/bin/chunk-k9ate69x.js +0 -1
  167. package/dist/bin/chunk-mh3rgn0e.js +0 -1
  168. package/dist/bin/chunk-nfb3ew1v.js +0 -1
  169. package/dist/bin/chunk-qgqgdkg0.js +0 -1
  170. package/dist/bin/chunk-s77kny9g.js +0 -1
  171. package/dist/bin/chunk-vn05n5z6.js +0 -1
  172. package/dist/bin/chunk-yxpzm8ce.js +0 -1
  173. package/dist/chunk-2ns1w1fe.js +0 -1
  174. package/dist/chunk-c9zjh5bx.js +0 -1
  175. package/dist/chunk-e9z4gzmg.js +0 -1
  176. package/dist/chunk-g8zgpxgz.js +0 -1
  177. package/dist/chunk-mqwdq7w1.js +0 -1
  178. package/dist/chunk-qjghqymj.js +0 -1
@@ -1,3 +1,3 @@
1
- import{xe,pa,Ni,Ha,En,Fi,qa,Ga,ec,tc}from"./chunk-tf8qxy9k.js";import{n,r,a,e,d,i,Ce}from"./chunk-5amvqed8.js";import{p}from"./chunk-dkf2kcsc.js";import{v}from"./chunk-7593hjcq.js";import{I}from"./chunk-7p2va3nj.js";import{g}from"./chunk-nmqjmx30.js";import{existsSync as de,readdirSync as me,readFileSync as pe,statSync as fe}from"node:fs";import{join as ne,relative as Se}from"node:path";class K{client;region;constructor(t="us-east-1",s){this.region=t,this.client=new p(void 0,{profile:s})}async createSecret(t){let s={Name:t.Name};if(t.Description)s.Description=t.Description;if(t.KmsKeyId)s.KmsKeyId=t.KmsKeyId;if(t.SecretBinary)s.SecretBinary=t.SecretBinary;if(t.SecretString)s.SecretString=t.SecretString;if(t.Tags&&t.Tags.length>0)s.Tags=t.Tags;if(t.AddReplicaRegions&&t.AddReplicaRegions.length>0)s.AddReplicaRegions=t.AddReplicaRegions;if(t.ForceOverwriteReplicaSecret!==void 0)s.ForceOverwriteReplicaSecret=t.ForceOverwriteReplicaSecret;s.ClientRequestToken=t.ClientRequestToken||crypto.randomUUID();let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.CreateSecret","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name,VersionId:o.VersionId,ReplicationStatus:o.ReplicationStatus}}async updateSecret(t){let s={SecretId:t.SecretId,ClientRequestToken:t.ClientRequestToken||crypto.randomUUID()};if(t.Description)s.Description=t.Description;if(t.KmsKeyId)s.KmsKeyId=t.KmsKeyId;if(t.SecretBinary)s.SecretBinary=t.SecretBinary;if(t.SecretString)s.SecretString=t.SecretString;let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.UpdateSecret","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name,VersionId:o.VersionId}}async putSecretValue(t){let s={SecretId:t.SecretId};if(t.SecretBinary)s.SecretBinary=t.SecretBinary;if(t.SecretString)s.SecretString=t.SecretString;if(t.VersionStages&&t.VersionStages.length>0)s.VersionStages=t.VersionStages;s.ClientRequestToken=t.ClientRequestToken||crypto.randomUUID();let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.PutSecretValue","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name,VersionId:o.VersionId,VersionStages:o.VersionStages}}async getSecretValue(t){let s={SecretId:t.SecretId};if(t.VersionId)s.VersionId=t.VersionId;if(t.VersionStage)s.VersionStage=t.VersionStage;let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.GetSecretValue","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name,VersionId:o.VersionId,SecretBinary:o.SecretBinary,SecretString:o.SecretString,VersionStages:o.VersionStages,CreatedDate:o.CreatedDate}}async describeSecret(t){let s={SecretId:t},o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.DescribeSecret","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return this.parseSecret(o)}async listSecrets(t){let s={};if(t?.MaxResults)s.MaxResults=t.MaxResults;if(t?.NextToken)s.NextToken=t.NextToken;if(t?.Filters&&t.Filters.length>0)s.Filters=t.Filters;if(t?.SortOrder)s.SortOrder=t.SortOrder;let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.ListSecrets","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{SecretList:o.SecretList?.map((c)=>this.parseSecret(c)),NextToken:o.NextToken}}async deleteSecret(t){let s={SecretId:t.SecretId};if(t.RecoveryWindowInDays!==void 0)s.RecoveryWindowInDays=t.RecoveryWindowInDays;if(t.ForceDeleteWithoutRecovery!==void 0)s.ForceDeleteWithoutRecovery=t.ForceDeleteWithoutRecovery;let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.DeleteSecret","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name,DeletionDate:o.DeletionDate}}async restoreSecret(t){let s={SecretId:t},o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.RestoreSecret","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name}}async rotateSecret(t){let s={SecretId:t.SecretId};if(t.ClientRequestToken)s.ClientRequestToken=t.ClientRequestToken;if(t.RotationLambdaARN)s.RotationLambdaARN=t.RotationLambdaARN;if(t.RotationRules)s.RotationRules=t.RotationRules;if(t.RotateImmediately!==void 0)s.RotateImmediately=t.RotateImmediately;let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.RotateSecret","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name,VersionId:o.VersionId}}async cancelRotateSecret(t){let s={SecretId:t},o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.CancelRotateSecret","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name}}async getResourcePolicy(t){let s={SecretId:t},o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.GetResourcePolicy","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name,ResourcePolicy:o.ResourcePolicy}}async putResourcePolicy(t){let s={SecretId:t.SecretId,ResourcePolicy:t.ResourcePolicy};if(t.BlockPublicPolicy!==void 0)s.BlockPublicPolicy=t.BlockPublicPolicy;let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.PutResourcePolicy","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name}}async deleteResourcePolicy(t){let s={SecretId:t},o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.DeleteResourcePolicy","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name}}async tagResource(t){let s={SecretId:t.SecretId,Tags:t.Tags};await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.TagResource","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)})}async untagResource(t){let s={SecretId:t.SecretId,TagKeys:t.TagKeys};await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.UntagResource","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)})}async setString(t,s,o){try{let c=await this.putSecretValue({SecretId:t,SecretString:s});return{ARN:c.ARN,VersionId:c.VersionId}}catch(c){if(c.code==="ResourceNotFoundException"){let l=await this.createSecret({Name:t,SecretString:s,Description:o?.description,KmsKeyId:o?.kmsKeyId,Tags:o?.tags});return{ARN:l.ARN,VersionId:l.VersionId}}throw c}}async setJson(t,s,o){return this.setString(t,JSON.stringify(s),o)}async getString(t){return(await this.getSecretValue({SecretId:t})).SecretString}async getJson(t){let s=await this.getString(t);if(s)return JSON.parse(s);return}async listAll(){let t=[],s;do{let o=await this.listSecrets({NextToken:s});if(o.SecretList)t.push(...o.SecretList);s=o.NextToken}while(s);return t}parseSecret(t){return{ARN:t.ARN,Name:t.Name,Description:t.Description,KmsKeyId:t.KmsKeyId,RotationEnabled:t.RotationEnabled,RotationLambdaARN:t.RotationLambdaARN,RotationRules:t.RotationRules,LastRotatedDate:t.LastRotatedDate,LastChangedDate:t.LastChangedDate,LastAccessedDate:t.LastAccessedDate,DeletedDate:t.DeletedDate,NextRotationDate:t.NextRotationDate,Tags:t.Tags,SecretVersionsToStages:t.SecretVersionsToStages,CreatedDate:t.CreatedDate,PrimaryRegion:t.PrimaryRegion}}}function k(t,s){let o=t.environments?.[s]?.app;if(!o)throw Error(`No serverless app configured for environment '${s}'. Add environments.${s}.app to your config.`);let c=t.project.slug,l=t.environments?.[s]?.region||t.project.region||"us-east-1";return{app:o,slug:c,region:l,stackName:xe(t,s),artifactBucket:pa(c,s),assetsBucket:Ni(c,s)}}function bt(t,s){let o=k(t,s),c=`${o.slug}-${s}`;return{region:o.region,slug:o.slug,stackName:o.stackName,functions:{http:`${c}-http`,queue:`${c}-queue`,cli:`${c}-cli`}}}async function q(t,s=8){let o;for(let c=0;c<s;c++)try{return await t()}catch(l){let u=l?.code||l?.name||"",m=String(l?.message||"");if(u==="ResourceConflictException"||/update is in progress|currently in the following state/i.test(m)){o=l,await new Promise((f)=>setTimeout(f,3000));continue}throw l}throw o}var ie=(t,s)=>`releases/${t}/${s}/current.json`;async function G(t,s,o,c){try{return await t.getObjectJson(s,ie(o,c))}catch{return null}}async function ae(t,s,o,c,l){await t.putObjectJson(s,ie(o,c),l)}async function ye(t,s){if(!t.secrets)return{};let o=new K(s),c={},l=Array.isArray(t.secrets)?t.secrets.map((u)=>({envName:u.split("/").pop().toUpperCase().replace(/[^A-Z0-9_]/g,"_"),secretId:u})):Object.entries(t.secrets).map(([u,m])=>({envName:u,secretId:m}));if(Array.isArray(t.secrets)){let u=new Map;for(let{envName:f,secretId:S}of l)u.set(f,[...u.get(f)??[],S]);let m=[...u.entries()].filter(([,f])=>f.length>1);if(m.length){let f=m.map(([S,R])=>`${S} ← ${R.join(", ")}`).join("; ");throw Error(`serverless secrets: multiple secrets map to the same env var (${f}). Use the map form { ENV_NAME: 'secret/id' } to disambiguate.`)}}for(let{envName:u,secretId:m}of l){let S=(await o.getSecretValue({SecretId:m})).SecretString??"";try{let R=JSON.parse(S);if(R&&typeof R==="object"&&!Array.isArray(R)){for(let[w,N]of Object.entries(R))c[w]=String(N);continue}}catch{}c[u]=S}return c}function Kd(t,s){let o={},c=s.DbProxyEndpoint||s.DbEndpoint;if(c)o.DB_CONNECTION="mysql",o.DB_HOST=c,o.DB_PORT="3306",o.DB_DATABASE=o.DB_DATABASE||"app";if(s.CacheEndpoint){if(o.REDIS_HOST=s.CacheEndpoint,o.REDIS_PORT="6379",t.cache?.driver==="elasticache")o.CACHE_STORE="redis",o.CACHE_DRIVER="redis",o.SESSION_DRIVER="redis"}return o}function _s(t,s,o,c,l,u,m,f={}){let S=t.kind==="php"?ec({cacheDriver:t.cache?.driver==="elasticache"?"redis":"dynamodb"}):{};return{TSCLOUD_LAMBDA_MODE:c,TSCLOUD_ENV:o,MAINTENANCE_MODE:"0",...t.octane?{TSCLOUD_OCTANE:"1"}:{},...t.serveAssets?{TSCLOUD_SERVE_ASSETS:"1"}:{},...t.redirectRobotsTxt===!1?{TSCLOUD_REDIRECT_ROBOTS_TXT:"0"}:{},...S,...f,...s.app.cache?.driver!=="elasticache"?{TSCLOUD_CACHE_TABLE:`${s.slug}-${o}-cache`}:{},...m?{TSCLOUD_QUEUE:m,SQS_QUEUE:m}:{},...u?{ASSET_URL:u}:{},...t.env??{},...l}}function Re(t){let s=t.slice(t.lastIndexOf(".")).toLowerCase();return{".html":"text/html",".css":"text/css",".js":"text/javascript",".mjs":"text/javascript",".json":"application/json",".svg":"image/svg+xml",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".ico":"image/x-icon",".woff":"font/woff",".woff2":"font/woff2",".txt":"text/plain",".map":"application/json"}[s]??"application/octet-stream"}function*oe(t){for(let s of me(t)){let o=ne(t,s);if(fe(o).isDirectory())yield*oe(o);else yield o}}async function he(t,s,o,c,l=!1){let u=0;for(let m of oe(o)){let f=Se(o,m).replace(/\\/g,"/");if(!l&&f.split("/").some((S)=>S.startsWith(".")))continue;await t.putObject({bucket:s,key:`${c}/${f}`,body:pe(m),contentType:Re(m),cacheControl:"public, max-age=31536000, immutable"}),u++}return u}function Io(t,s){let o=Object.entries(s).reduce((c,[l,u])=>c+Buffer.byteLength(`${l}=${u}`,"utf8"),0);if(o>4096){let c=Object.entries(s).map(([l,u])=>[l,Buffer.byteLength(`${l}=${u}`,"utf8")]).sort((l,u)=>u[1]-l[1]).slice(0,5).map(([l,u])=>`${l} (${u}B)`).join(", ");throw Error(`${t}: environment variables total ${o}B, over AWS's 4096B limit (${o-4096}B too large). Largest: ${c}. Move large/secret values out of env — store them in Secrets Manager and fetch at cold start.`)}}async function U(t,s,o,c){Io(s,o),await q(()=>t.updateFunctionConfiguration({FunctionName:s,Environment:{Variables:o}})),await t.waitForFunctionActive(s,120);let l=c.kind==="image"?{FunctionName:s,ImageUri:c.imageUri}:{FunctionName:s,S3Bucket:c.bucket,S3Key:c.key};await q(()=>t.updateFunctionCode(l)),await t.waitForFunctionActive(s,120)}async function X(t,s){let c=(await q(()=>t.publishVersion({FunctionName:s}))).Version;if(!c)throw Error(`publishVersion returned no version for ${s}`);return await q(()=>t.updateAlias({FunctionName:s,Name:"live",FunctionVersion:c})),c}async function sm(t,s,o={}){let c=k(t,s),{app:l,slug:u,region:m,stackName:f,artifactBucket:S}=c,R=o.projectRoot??process.cwd();i(`Deploying serverless app: ${u} (${s})`),e(`Stack: ${f}`),e(`Region: ${m}`);let w=new g(m),N=new v(m),T=new I(m),P=l.kind==="php",O=l.packaging==="image",h=Fi(l),V,E,b,Y,M=[];if(O&&h.usesLayer&&h.layerKind!=="php")throw Error(`packaging: 'image' currently supports PHP and managed Node runtimes. ${h.kind}${h.kind==="node"?` ${h.version}`:""} uses a custom runtime — `+"use the default zip packaging (the runtime layer is built + attached automatically).");if(O){d("Building + pushing container image");let{buildAndPushServerlessImage:y}=await import("./chunk-71ep1g7k.js"),C=await y({app:l,projectRoot:R,region:m,repository:`${u}-${s}`,skipBuild:o.skipBuild,onStep:(D)=>e(` ${D}`)});V=C.handlers,E={kind:"image",imageUri:C.imageUri},b=C.tag,M.push({ParameterKey:"ImageUri",ParameterValue:C.imageUri})}else{d("Packaging application");let y=P?tc({projectRoot:R,app:l,skipBuild:o.skipBuild,onStep:(x)=>e(` ${x}`)}):await En({projectRoot:R,app:l,skipBuild:o.skipBuild,onStep:(x)=>e(` ${x}`)}),C=Ha(u,s,y.sha256);if(e(`Artifact: ${y.sha256.slice(0,12)} (${(y.zip.length/1024).toFixed(0)} KB)`),V=y.handlers,b=y.sha256,h.usesLayer){let x=h.layerEnvVar?process.env[h.layerEnvVar]:void 0;if(Y=l.layers??(x?[x]:void 0),!Y?.length){let ge=h.layerKind==="php"?"serverless:build-php-layer":h.layerKind==="bun"?"serverless:build-bun-layer":"serverless:build-node-layer";throw Error(`${h.kind} apps on provided.al2023 need a runtime layer. Set environments.<env>.app.layers or the ${h.layerEnvVar} env var (build one with \`cloud ${ge}\`)${h.kind==="node"?", or use a managed Node version (18/20/22)":""}.`)}}if(!await w.bucketExists(S))d(`Creating artifact bucket ${S}`),await w.createBucket(S);if(await w.headObject(S,C).then((x)=>x!==null).catch(()=>!1))e("Artifact already uploaded — reusing");else d("Uploading artifact"),await w.putObject({bucket:S,key:C,body:y.zip,contentType:"application/zip"});E={kind:"zip",bucket:S,key:C},M.push({ParameterKey:"ArtifactBucket",ParameterValue:S},{ParameterKey:"ArtifactKey",ParameterValue:C})}let A=Ga({config:t,environment:s,app:l,handlers:V,runtimeLayers:Y}),te=JSON.stringify(A.template),re=["CAPABILITY_NAMED_IAM"],L=await N.describeStacks({stackName:f}).then((y)=>y.Stacks[0]?.StackStatus).catch(()=>{return}),J=L!==void 0;if(L&&/DELETE_IN_PROGRESS/.test(L))d("Waiting for an in-progress stack delete to finish"),await N.waitForStack(f,"stack-delete-complete").catch(()=>{}),J=!1;else if(L&&/(?:ROLLBACK_COMPLETE|REVIEW_IN_PROGRESS|CREATE_FAILED)/.test(L))d(`Deleting unusable stack (status ${L}) before recreate`),await N.deleteStack(f),await N.waitForStack(f,"stack-delete-complete").catch(()=>{}),J=!1;d(J?"Updating infrastructure stack":"Creating infrastructure stack");try{if(J)await N.updateStack({stackName:f,templateBody:te,parameters:M,capabilities:re}),await N.waitForStack(f,"stack-update-complete");else await N.createStack({stackName:f,templateBody:te,parameters:M,capabilities:re,onFailure:"DELETE"}),await N.waitForStack(f,"stack-create-complete")}catch(y){if(/No updates are to be performed/i.test(String(y?.message)))e("No infrastructure changes");else throw y}let Q=await N.getStackOutputs(f).catch(()=>({})),B;if(l.assets){let y=ne(R,l.assets);if(de(y)){let C=l.assetDomain||Q.AssetsCdnDomain;B=C?`https://${C}/${b}`:void 0,d(`Syncing assets from ${l.assets}`);let D=await he(w,c.assetsBucket,y,b,l.dotFilesAsAssets);e(`Uploaded ${D} asset(s)${B?` → ${B}`:""}`)}else a(`Assets directory not found: ${y}`)}d("Resolving secrets");let _=await ye(l,m);if(l.database?.connection==="aurora-serverless")try{let C=await new K(m).getSecretValue({SecretId:`${u}/${s}/db`}),D=JSON.parse(C.SecretString??"{}");if(D.username)_.DB_USERNAME=D.username;if(D.password)_.DB_PASSWORD=D.password}catch(y){a(`Could not resolve Aurora DB credentials: ${y.message}`)}if(!await w.bucketExists(S))await w.createBucket(S);let H=await G(w,S,u,s),F={},Z=A.queueNames[0],ee=Kd(l,Q);if(d("Activating functions"),F.http=_s(l,c,s,"http",_,B,Z,ee),await U(T,A.functionNames.http,F.http,E),F.cli=_s(l,c,s,"cli",_,B,Z,ee),await U(T,A.functionNames.cli,F.cli,E),A.queueNames.length)F.queue=_s(l,c,s,"queue",_,B,Z,ee),await U(T,A.functionNames.queue,F.queue,E);let se=(l.provisionedConcurrency??0)>0,W={};if(se){if(d("Publishing versions + flipping live alias"),W.http=await X(T,A.functionNames.http),W.cli=await X(T,A.functionNames.cli),A.queueNames.length)W.queue=await X(T,A.functionNames.queue)}if(await ae(w,S,u,s,{sha:b,code:E,previousSha:H?.sha,previousCode:H?.code,previousFunctionEnv:H?.functionEnv,...se?{functionVersions:W,previousFunctionVersions:H?.functionVersions}:{},functionEnv:F,functionNames:{http:A.functionNames.http,cli:A.functionNames.cli,...A.queueNames.length?{queue:A.functionNames.queue}:{}},assetUrl:B,timestamp:new Date().toISOString()}),!o.skipDeployHooks&&l.deploy?.length){d("Running deploy hooks");for(let y of l.deploy){e(` deploy: ${y}`);let C=await T.invoke({FunctionName:A.functionNames.cli,InvocationType:"RequestResponse",Payload:{command:y},LogType:"Tail"});if(C.FunctionError)throw r("Deploy hook failed — the new build is ALREADY LIVE."),e(` Roll back with: cloud serverless:rollback --env ${s}`),Error(`Deploy hook failed (${y}): ${C.Payload}`)}}let j=Q.HttpApiEndpoint;if(!o.skipHealthCheck&&j){d("Health check");try{let y=await fetch(j,{method:"GET"});e(`GET ${j} → ${y.status}`)}catch(y){a(`Health check could not reach ${j}: ${y.message}`)}}Ce(["Serverless deploy complete","",j?`URL: ${j}`:"",`Stack: ${f}`,`Build: ${b.slice(0,12)}`].filter(Boolean).join(`
2
- `),"green")}async function Os(t,s){let o=k(t,s),c=new g(o.region),l=new I(o.region),u=await G(c,o.artifactBucket,o.slug,s);if(!u)throw Error("No previous release to redeploy. Run a full deploy first.");i(`Redeploying ${o.slug} (${s}) — build ${u.sha.slice(0,12)}`);for(let[m,f]of Object.entries(u.functionNames)){if(!f)continue;d(`Re-activating ${m} (${f})`),await U(l,f,u.functionEnv[m]??{},u.code);let S=u.functionVersions?.[m];if(S)await q(()=>l.updateAlias({FunctionName:f,Name:"live",FunctionVersion:S}))}n("Redeploy complete")}async function $s(t,s){let o=k(t,s),c=new g(o.region),l=new I(o.region),u=await G(c,o.artifactBucket,o.slug,s);if(!u?.previousCode)throw Error("No previous release to roll back to.");i(`Rolling back ${o.slug} (${s}) → ${u.previousSha?.slice(0,12)}`);for(let[m,f]of Object.entries(u.functionNames)){if(!f)continue;d(`Restoring ${m} (${f})`);let S=u.previousFunctionEnv?.[m]??u.functionEnv[m]??{};await U(l,f,S,u.previousCode);let R=u.previousFunctionVersions?.[m];if(R)await q(()=>l.updateAlias({FunctionName:f,Name:"live",FunctionVersion:R}))}await ae(c,o.artifactBucket,o.slug,s,{...u,sha:u.previousSha??u.sha,code:u.previousCode,functionEnv:u.previousFunctionEnv??u.functionEnv,functionVersions:u.previousFunctionVersions??u.functionVersions,previousSha:void 0,previousCode:void 0,previousFunctionEnv:void 0,previousFunctionVersions:void 0,timestamp:new Date().toISOString()}),n("Rollback complete")}async function Po(t,s,o,c){let l=k(t,s),u=new I(l.region),m=`${l.slug}-${s}-http`,S={...(await u.getFunction(m)).Configuration?.Environment?.Variables??{}};if(S.MAINTENANCE_MODE=o?"1":"0",o&&c)S.MAINTENANCE_BYPASS_SECRET=c;if(!o)delete S.MAINTENANCE_BYPASS_SECRET;if(await q(()=>u.updateFunctionConfiguration({FunctionName:m,Environment:{Variables:S}})),(l.app.provisionedConcurrency??0)>0)await u.waitForFunctionActive(m,120),await X(u,m);n(o?"Application is now in maintenance mode (503)":"Application is live")}async function Ft(t,s,o){let c=k(t,s),l=new I(c.region),u=`${c.slug}-${s}-cli`,m=await l.invoke({FunctionName:u,InvocationType:"RequestResponse",Payload:{command:o},LogType:"Tail"});if(m.FunctionError)throw Error(`Command failed: ${m.Payload}`);return m.Payload??""}var ce=(t,s)=>`commands/${t}/${s}/history.json`,ve=50;async function le(t,s,o,c){try{return(await t.getObjectJson(s,ce(o,c)))?.records??[]}catch{return[]}}async function am(t,s,o){let c=k(t,s),l=new I(c.region),u=`${c.slug}-${s}-cli`,m=await l.invoke({FunctionName:u,InvocationType:"RequestResponse",Payload:{command:o},LogType:"Tail"}),f=m.Payload??"",S=m.FunctionError?"error":"ok",R=-1;try{let w=new g(c.region);if(await w.bucketExists(c.artifactBucket)){let N=await le(w,c.artifactBucket,c.slug,s);R=N.reduce((T,P)=>Math.max(T,P.id),0)+1,N.push({id:R,command:o,timestamp:new Date().toISOString(),status:S,output:f.slice(0,4000)}),await w.putObjectJson(c.artifactBucket,ce(c.slug,s),{records:N.slice(-ve)})}}catch{}if(m.FunctionError)throw Error(`Command failed${R>0?` (#${R})`:""}: ${f}`);return{id:R,status:S,output:f}}async function Jd(t,s){let o=k(t,s),c=new g(o.region);return le(c,o.artifactBucket,o.slug,s)}async function cm(t,s,o){let c=await Jd(t,s);if(!c.length)return;return o==null?c[c.length-1]:c.find((l)=>l.id===o)}async function lm(t,s,o){let c=Buffer.from(o,"utf-8").toString("base64");return Ft(t,s,`tscloud:db-query --sql-base64=${c}`)}async function z(t,s){let o=k(t,s),c=new v(o.region),l=new I(o.region),u=new g(o.region),m="not deployed",f={};try{let{Stacks:O}=await c.describeStacks({stackName:o.stackName});m=O?.[0]?.StackStatus??m;for(let h of O?.[0]?.Outputs??[])if(h.OutputKey)f[h.OutputKey]=h.OutputValue??""}catch{}let S=qa(o.app,o.slug,s),R=o.app.provisionedConcurrency??0,w=["http","cli",...S.length?["queue"]:[]],N=`${o.slug}-${s}`,T=[];for(let O of w){let h=`${N}-${O}`,V={mode:O,name:h,version:"$LATEST"};if(R>0){let E=await l.getAlias(h,"live").catch(()=>null);if(E?.FunctionVersion){V.version=E.FunctionVersion;let b=await l.getProvisionedConcurrencyConfig(h,"live").catch(()=>null);if(b)V.provisioned={status:b.Status??"-",allocated:b.AllocatedProvisionedConcurrentExecutions??0,requested:b.RequestedProvisionedConcurrentExecutions??0}}}T.push(V)}let P=await G(u,o.artifactBucket,o.slug,s).catch(()=>null);return{slug:o.slug,environment:s,region:o.region,stackStatus:m,endpoint:f.HttpApiEndpoint,assetUrl:P?.assetUrl,scheduler:o.app.scheduler??"on",queues:S,provisionedConcurrency:R,functions:T,lastRelease:P?{sha:P.sha,timestamp:P.timestamp}:void 0}}function ue(t,s){return`${t}-${s}-db`}async function Ls(t,s,o,c){let l=k(t,s);if(l.app.database?.connection!=="aurora-serverless")throw Error("No Aurora Serverless v2 database is configured for this environment.");let{RDSClient:u}=await import("./chunk-mh3rgn0e.js"),m=new u(l.region),f=ue(l.slug,s);d(`Scaling ${f} → ${o}–${c} ACUs`),await m.modifyDBCluster({DBClusterIdentifier:f,ServerlessV2ScalingConfiguration:{MinCapacity:o,MaxCapacity:c},ApplyImmediately:!0}),n(`Scaling applied to ${f} (takes effect shortly).`)}async function dm(t,s,o){let c=k(t,s);if(c.app.database?.connection!=="aurora-serverless")throw Error("No Aurora Serverless v2 database is configured for this environment.");if(!o.toTime&&!o.latest)throw Error("Specify a restore point: --to <ISO timestamp> or --latest.");let{RDSClient:l}=await import("./chunk-mh3rgn0e.js"),u=new l(c.region),m=ue(c.slug,s),f=o.latest?"latest":o.toTime.toISOString().replace(/[^0-9]/g,"").slice(0,14),S=o.target??`${m}-restore-${f}`;d(`Restoring ${m} → new cluster ${S} (${o.latest?"latest restorable time":o.toTime.toISOString()})`),await u.restoreDBClusterToPointInTime({DBClusterIdentifier:S,SourceDBClusterIdentifier:m,RestoreToTime:o.toTime,UseLatestRestorableTime:o.latest}),d("Waiting for the restored cluster to be ready");for(let R=0;R<120;R++){let{DBClusters:w}=await u.describeDBClusters({DBClusterIdentifier:S}),N=w?.[0]?.Status;if(N&&N!=="creating")break;await new Promise((T)=>setTimeout(T,15000))}return d("Adding a writer instance"),await u.createDBInstance({DBInstanceIdentifier:`${S}-1`,DBClusterIdentifier:S,Engine:"aurora-mysql",DBInstanceClass:"db.serverless"}),n(`Restore complete → cluster ${S} with writer ${S}-1. Point an env's database at it once available.`),S}
3
- export{K,bt,Kd,_s,Io,sm,Os,$s,Po,Ft,am,Jd,cm,lm,z,Ls,dm};
1
+ import{xe,pa,Ni,Ha,En,Bi,qa,Ga,ec,tc}from"./chunk-dj03v81n.js";import{n,r,a,e,d,i,Ce}from"./chunk-6y6g726g.js";import{p}from"./chunk-psvqpztv.js";import{v}from"./chunk-xdk0gmxt.js";import{I}from"./chunk-de69nra4.js";import{g}from"./chunk-12x968en.js";import{existsSync as de,readdirSync as me,readFileSync as pe,statSync as fe}from"node:fs";import{join as ne,relative as Se}from"node:path";class K{client;region;constructor(t="us-east-1",s){this.region=t,this.client=new p(void 0,{profile:s})}async createSecret(t){let s={Name:t.Name};if(t.Description)s.Description=t.Description;if(t.KmsKeyId)s.KmsKeyId=t.KmsKeyId;if(t.SecretBinary)s.SecretBinary=t.SecretBinary;if(t.SecretString)s.SecretString=t.SecretString;if(t.Tags&&t.Tags.length>0)s.Tags=t.Tags;if(t.AddReplicaRegions&&t.AddReplicaRegions.length>0)s.AddReplicaRegions=t.AddReplicaRegions;if(t.ForceOverwriteReplicaSecret!==void 0)s.ForceOverwriteReplicaSecret=t.ForceOverwriteReplicaSecret;s.ClientRequestToken=t.ClientRequestToken||crypto.randomUUID();let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.CreateSecret","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name,VersionId:o.VersionId,ReplicationStatus:o.ReplicationStatus}}async updateSecret(t){let s={SecretId:t.SecretId,ClientRequestToken:t.ClientRequestToken||crypto.randomUUID()};if(t.Description)s.Description=t.Description;if(t.KmsKeyId)s.KmsKeyId=t.KmsKeyId;if(t.SecretBinary)s.SecretBinary=t.SecretBinary;if(t.SecretString)s.SecretString=t.SecretString;let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.UpdateSecret","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name,VersionId:o.VersionId}}async putSecretValue(t){let s={SecretId:t.SecretId};if(t.SecretBinary)s.SecretBinary=t.SecretBinary;if(t.SecretString)s.SecretString=t.SecretString;if(t.VersionStages&&t.VersionStages.length>0)s.VersionStages=t.VersionStages;s.ClientRequestToken=t.ClientRequestToken||crypto.randomUUID();let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.PutSecretValue","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name,VersionId:o.VersionId,VersionStages:o.VersionStages}}async getSecretValue(t){let s={SecretId:t.SecretId};if(t.VersionId)s.VersionId=t.VersionId;if(t.VersionStage)s.VersionStage=t.VersionStage;let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.GetSecretValue","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name,VersionId:o.VersionId,SecretBinary:o.SecretBinary,SecretString:o.SecretString,VersionStages:o.VersionStages,CreatedDate:o.CreatedDate}}async describeSecret(t){let s={SecretId:t},o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.DescribeSecret","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return this.parseSecret(o)}async listSecrets(t){let s={};if(t?.MaxResults)s.MaxResults=t.MaxResults;if(t?.NextToken)s.NextToken=t.NextToken;if(t?.Filters&&t.Filters.length>0)s.Filters=t.Filters;if(t?.SortOrder)s.SortOrder=t.SortOrder;let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.ListSecrets","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{SecretList:o.SecretList?.map((c)=>this.parseSecret(c)),NextToken:o.NextToken}}async deleteSecret(t){let s={SecretId:t.SecretId};if(t.RecoveryWindowInDays!==void 0)s.RecoveryWindowInDays=t.RecoveryWindowInDays;if(t.ForceDeleteWithoutRecovery!==void 0)s.ForceDeleteWithoutRecovery=t.ForceDeleteWithoutRecovery;let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.DeleteSecret","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name,DeletionDate:o.DeletionDate}}async restoreSecret(t){let s={SecretId:t},o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.RestoreSecret","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name}}async rotateSecret(t){let s={SecretId:t.SecretId};if(t.ClientRequestToken)s.ClientRequestToken=t.ClientRequestToken;if(t.RotationLambdaARN)s.RotationLambdaARN=t.RotationLambdaARN;if(t.RotationRules)s.RotationRules=t.RotationRules;if(t.RotateImmediately!==void 0)s.RotateImmediately=t.RotateImmediately;let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.RotateSecret","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name,VersionId:o.VersionId}}async cancelRotateSecret(t){let s={SecretId:t},o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.CancelRotateSecret","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name}}async getResourcePolicy(t){let s={SecretId:t},o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.GetResourcePolicy","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name,ResourcePolicy:o.ResourcePolicy}}async putResourcePolicy(t){let s={SecretId:t.SecretId,ResourcePolicy:t.ResourcePolicy};if(t.BlockPublicPolicy!==void 0)s.BlockPublicPolicy=t.BlockPublicPolicy;let o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.PutResourcePolicy","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name}}async deleteResourcePolicy(t){let s={SecretId:t},o=await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.DeleteResourcePolicy","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)});return{ARN:o.ARN,Name:o.Name}}async tagResource(t){let s={SecretId:t.SecretId,Tags:t.Tags};await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.TagResource","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)})}async untagResource(t){let s={SecretId:t.SecretId,TagKeys:t.TagKeys};await this.client.request({service:"secretsmanager",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"secretsmanager.UntagResource","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(s)})}async setString(t,s,o){try{let c=await this.putSecretValue({SecretId:t,SecretString:s});return{ARN:c.ARN,VersionId:c.VersionId}}catch(c){if(c.code==="ResourceNotFoundException"){let l=await this.createSecret({Name:t,SecretString:s,Description:o?.description,KmsKeyId:o?.kmsKeyId,Tags:o?.tags});return{ARN:l.ARN,VersionId:l.VersionId}}throw c}}async setJson(t,s,o){return this.setString(t,JSON.stringify(s),o)}async getString(t){return(await this.getSecretValue({SecretId:t})).SecretString}async getJson(t){let s=await this.getString(t);if(s)return JSON.parse(s);return}async listAll(){let t=[],s;do{let o=await this.listSecrets({NextToken:s});if(o.SecretList)t.push(...o.SecretList);s=o.NextToken}while(s);return t}parseSecret(t){return{ARN:t.ARN,Name:t.Name,Description:t.Description,KmsKeyId:t.KmsKeyId,RotationEnabled:t.RotationEnabled,RotationLambdaARN:t.RotationLambdaARN,RotationRules:t.RotationRules,LastRotatedDate:t.LastRotatedDate,LastChangedDate:t.LastChangedDate,LastAccessedDate:t.LastAccessedDate,DeletedDate:t.DeletedDate,NextRotationDate:t.NextRotationDate,Tags:t.Tags,SecretVersionsToStages:t.SecretVersionsToStages,CreatedDate:t.CreatedDate,PrimaryRegion:t.PrimaryRegion}}}function k(t,s){let o=t.environments?.[s]?.app;if(!o)throw Error(`No serverless app configured for environment '${s}'. Add environments.${s}.app to your config.`);let c=t.project.slug,l=t.environments?.[s]?.region||t.project.region||"us-east-1";return{app:o,slug:c,region:l,stackName:xe(t,s),artifactBucket:pa(c,s),assetsBucket:Ni(c,s)}}function bt(t,s){let o=k(t,s),c=`${o.slug}-${s}`;return{region:o.region,slug:o.slug,stackName:o.stackName,functions:{http:`${c}-http`,queue:`${c}-queue`,cli:`${c}-cli`}}}async function q(t,s=8){let o;for(let c=0;c<s;c++)try{return await t()}catch(l){let u=l?.code||l?.name||"",m=String(l?.message||"");if(u==="ResourceConflictException"||/update is in progress|currently in the following state/i.test(m)){o=l,await new Promise((f)=>setTimeout(f,3000));continue}throw l}throw o}var ie=(t,s)=>`releases/${t}/${s}/current.json`;async function G(t,s,o,c){try{return await t.getObjectJson(s,ie(o,c))}catch{return null}}async function ae(t,s,o,c,l){await t.putObjectJson(s,ie(o,c),l)}async function ye(t,s){if(!t.secrets)return{};let o=new K(s),c={},l=Array.isArray(t.secrets)?t.secrets.map((u)=>({envName:u.split("/").pop().toUpperCase().replace(/[^A-Z0-9_]/g,"_"),secretId:u})):Object.entries(t.secrets).map(([u,m])=>({envName:u,secretId:m}));if(Array.isArray(t.secrets)){let u=new Map;for(let{envName:f,secretId:S}of l)u.set(f,[...u.get(f)??[],S]);let m=[...u.entries()].filter(([,f])=>f.length>1);if(m.length){let f=m.map(([S,R])=>`${S} ← ${R.join(", ")}`).join("; ");throw Error(`serverless secrets: multiple secrets map to the same env var (${f}). Use the map form { ENV_NAME: 'secret/id' } to disambiguate.`)}}for(let{envName:u,secretId:m}of l){let S=(await o.getSecretValue({SecretId:m})).SecretString??"";try{let R=JSON.parse(S);if(R&&typeof R==="object"&&!Array.isArray(R)){for(let[w,N]of Object.entries(R))c[w]=String(N);continue}}catch{}c[u]=S}return c}function Kd(t,s){let o={},c=s.DbProxyEndpoint||s.DbEndpoint;if(c)o.DB_CONNECTION="mysql",o.DB_HOST=c,o.DB_PORT="3306",o.DB_DATABASE=o.DB_DATABASE||"app";if(s.CacheEndpoint){if(o.REDIS_HOST=s.CacheEndpoint,o.REDIS_PORT="6379",t.cache?.driver==="elasticache")o.CACHE_STORE="redis",o.CACHE_DRIVER="redis",o.SESSION_DRIVER="redis"}return o}function _s(t,s,o,c,l,u,m,f={}){let S=t.kind==="php"?ec({cacheDriver:t.cache?.driver==="elasticache"?"redis":"dynamodb"}):{};return{TSCLOUD_LAMBDA_MODE:c,TSCLOUD_ENV:o,MAINTENANCE_MODE:"0",...t.octane?{TSCLOUD_OCTANE:"1"}:{},...t.serveAssets?{TSCLOUD_SERVE_ASSETS:"1"}:{},...t.redirectRobotsTxt===!1?{TSCLOUD_REDIRECT_ROBOTS_TXT:"0"}:{},...S,...f,...s.app.cache?.driver!=="elasticache"?{TSCLOUD_CACHE_TABLE:`${s.slug}-${o}-cache`}:{},...m?{TSCLOUD_QUEUE:m,SQS_QUEUE:m}:{},...u?{ASSET_URL:u}:{},...t.env??{},...l}}function Re(t){let s=t.slice(t.lastIndexOf(".")).toLowerCase();return{".html":"text/html",".css":"text/css",".js":"text/javascript",".mjs":"text/javascript",".json":"application/json",".svg":"image/svg+xml",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".ico":"image/x-icon",".woff":"font/woff",".woff2":"font/woff2",".txt":"text/plain",".map":"application/json"}[s]??"application/octet-stream"}function*oe(t){for(let s of me(t)){let o=ne(t,s);if(fe(o).isDirectory())yield*oe(o);else yield o}}async function he(t,s,o,c,l=!1){let u=0;for(let m of oe(o)){let f=Se(o,m).replace(/\\/g,"/");if(!l&&f.split("/").some((S)=>S.startsWith(".")))continue;await t.putObject({bucket:s,key:`${c}/${f}`,body:pe(m),contentType:Re(m),cacheControl:"public, max-age=31536000, immutable"}),u++}return u}function Io(t,s){let o=Object.entries(s).reduce((c,[l,u])=>c+Buffer.byteLength(`${l}=${u}`,"utf8"),0);if(o>4096){let c=Object.entries(s).map(([l,u])=>[l,Buffer.byteLength(`${l}=${u}`,"utf8")]).sort((l,u)=>u[1]-l[1]).slice(0,5).map(([l,u])=>`${l} (${u}B)`).join(", ");throw Error(`${t}: environment variables total ${o}B, over AWS's 4096B limit (${o-4096}B too large). Largest: ${c}. Move large/secret values out of env — store them in Secrets Manager and fetch at cold start.`)}}async function U(t,s,o,c){Io(s,o),await q(()=>t.updateFunctionConfiguration({FunctionName:s,Environment:{Variables:o}})),await t.waitForFunctionActive(s,120);let l=c.kind==="image"?{FunctionName:s,ImageUri:c.imageUri}:{FunctionName:s,S3Bucket:c.bucket,S3Key:c.key};await q(()=>t.updateFunctionCode(l)),await t.waitForFunctionActive(s,120)}async function X(t,s){let c=(await q(()=>t.publishVersion({FunctionName:s}))).Version;if(!c)throw Error(`publishVersion returned no version for ${s}`);return await q(()=>t.updateAlias({FunctionName:s,Name:"live",FunctionVersion:c})),c}async function sm(t,s,o={}){let c=k(t,s),{app:l,slug:u,region:m,stackName:f,artifactBucket:S}=c,R=o.projectRoot??process.cwd();i(`Deploying serverless app: ${u} (${s})`),e(`Stack: ${f}`),e(`Region: ${m}`);let w=new g(m),N=new v(m),T=new I(m),P=l.kind==="php",O=l.packaging==="image",h=Bi(l),V,E,b,Y,M=[];if(O&&h.usesLayer&&h.layerKind!=="php")throw Error(`packaging: 'image' currently supports PHP and managed Node runtimes. ${h.kind}${h.kind==="node"?` ${h.version}`:""} uses a custom runtime — `+"use the default zip packaging (the runtime layer is built + attached automatically).");if(O){d("Building + pushing container image");let{buildAndPushServerlessImage:y}=await import("./chunk-z8scg2vy.js"),C=await y({app:l,projectRoot:R,region:m,repository:`${u}-${s}`,skipBuild:o.skipBuild,onStep:(D)=>e(` ${D}`)});V=C.handlers,E={kind:"image",imageUri:C.imageUri},b=C.tag,M.push({ParameterKey:"ImageUri",ParameterValue:C.imageUri})}else{d("Packaging application");let y=P?tc({projectRoot:R,app:l,skipBuild:o.skipBuild,onStep:(x)=>e(` ${x}`)}):await En({projectRoot:R,app:l,skipBuild:o.skipBuild,onStep:(x)=>e(` ${x}`)}),C=Ha(u,s,y.sha256);if(e(`Artifact: ${y.sha256.slice(0,12)} (${(y.zip.length/1024).toFixed(0)} KB)`),V=y.handlers,b=y.sha256,h.usesLayer){let x=h.layerEnvVar?process.env[h.layerEnvVar]:void 0;if(Y=l.layers??(x?[x]:void 0),!Y?.length){let ge=h.layerKind==="php"?"serverless:build-php-layer":h.layerKind==="bun"?"serverless:build-bun-layer":"serverless:build-node-layer";throw Error(`${h.kind} apps on provided.al2023 need a runtime layer. Set environments.<env>.app.layers or the ${h.layerEnvVar} env var (build one with \`cloud ${ge}\`)${h.kind==="node"?", or use a managed Node version (18/20/22)":""}.`)}}if(!await w.bucketExists(S))d(`Creating artifact bucket ${S}`),await w.createBucket(S);if(await w.headObject(S,C).then((x)=>x!==null).catch(()=>!1))e("Artifact already uploaded — reusing");else d("Uploading artifact"),await w.putObject({bucket:S,key:C,body:y.zip,contentType:"application/zip"});E={kind:"zip",bucket:S,key:C},M.push({ParameterKey:"ArtifactBucket",ParameterValue:S},{ParameterKey:"ArtifactKey",ParameterValue:C})}let A=Ga({config:t,environment:s,app:l,handlers:V,runtimeLayers:Y}),te=JSON.stringify(A.template),re=["CAPABILITY_NAMED_IAM"],L=await N.describeStacks({stackName:f}).then((y)=>y.Stacks[0]?.StackStatus).catch(()=>{return}),J=L!==void 0;if(L&&/DELETE_IN_PROGRESS/.test(L))d("Waiting for an in-progress stack delete to finish"),await N.waitForStack(f,"stack-delete-complete").catch(()=>{}),J=!1;else if(L&&/(?:ROLLBACK_COMPLETE|REVIEW_IN_PROGRESS|CREATE_FAILED)/.test(L))d(`Deleting unusable stack (status ${L}) before recreate`),await N.deleteStack(f),await N.waitForStack(f,"stack-delete-complete").catch(()=>{}),J=!1;d(J?"Updating infrastructure stack":"Creating infrastructure stack");try{if(J)await N.updateStack({stackName:f,templateBody:te,parameters:M,capabilities:re}),await N.waitForStack(f,"stack-update-complete");else await N.createStack({stackName:f,templateBody:te,parameters:M,capabilities:re,onFailure:"DELETE"}),await N.waitForStack(f,"stack-create-complete")}catch(y){if(/No updates are to be performed/i.test(String(y?.message)))e("No infrastructure changes");else throw y}let Q=await N.getStackOutputs(f).catch(()=>({})),B;if(l.assets){let y=ne(R,l.assets);if(de(y)){let C=l.assetDomain||Q.AssetsCdnDomain;B=C?`https://${C}/${b}`:void 0,d(`Syncing assets from ${l.assets}`);let D=await he(w,c.assetsBucket,y,b,l.dotFilesAsAssets);e(`Uploaded ${D} asset(s)${B?` → ${B}`:""}`)}else a(`Assets directory not found: ${y}`)}d("Resolving secrets");let _=await ye(l,m);if(l.database?.connection==="aurora-serverless")try{let C=await new K(m).getSecretValue({SecretId:`${u}/${s}/db`}),D=JSON.parse(C.SecretString??"{}");if(D.username)_.DB_USERNAME=D.username;if(D.password)_.DB_PASSWORD=D.password}catch(y){a(`Could not resolve Aurora DB credentials: ${y.message}`)}if(!await w.bucketExists(S))await w.createBucket(S);let H=await G(w,S,u,s),F={},Z=A.queueNames[0],ee=Kd(l,Q);if(d("Activating functions"),F.http=_s(l,c,s,"http",_,B,Z,ee),await U(T,A.functionNames.http,F.http,E),F.cli=_s(l,c,s,"cli",_,B,Z,ee),await U(T,A.functionNames.cli,F.cli,E),A.queueNames.length)F.queue=_s(l,c,s,"queue",_,B,Z,ee),await U(T,A.functionNames.queue,F.queue,E);let se=(l.provisionedConcurrency??0)>0,W={};if(se){if(d("Publishing versions + flipping live alias"),W.http=await X(T,A.functionNames.http),W.cli=await X(T,A.functionNames.cli),A.queueNames.length)W.queue=await X(T,A.functionNames.queue)}if(await ae(w,S,u,s,{sha:b,code:E,previousSha:H?.sha,previousCode:H?.code,previousFunctionEnv:H?.functionEnv,...se?{functionVersions:W,previousFunctionVersions:H?.functionVersions}:{},functionEnv:F,functionNames:{http:A.functionNames.http,cli:A.functionNames.cli,...A.queueNames.length?{queue:A.functionNames.queue}:{}},assetUrl:B,timestamp:new Date().toISOString()}),!o.skipDeployHooks&&l.deploy?.length){d("Running deploy hooks");for(let y of l.deploy){e(` deploy: ${y}`);let C=await T.invoke({FunctionName:A.functionNames.cli,InvocationType:"RequestResponse",Payload:{command:y},LogType:"Tail"});if(C.FunctionError)throw r("Deploy hook failed — the new build is ALREADY LIVE."),e(` Roll back with: cloud serverless:rollback --env ${s}`),Error(`Deploy hook failed (${y}): ${C.Payload}`)}}let j=Q.HttpApiEndpoint;if(!o.skipHealthCheck&&j){d("Health check");try{let y=await fetch(j,{method:"GET"});e(`GET ${j} → ${y.status}`)}catch(y){a(`Health check could not reach ${j}: ${y.message}`)}}Ce(["Serverless deploy complete","",j?`URL: ${j}`:"",`Stack: ${f}`,`Build: ${b.slice(0,12)}`].filter(Boolean).join(`
2
+ `),"green")}async function Os(t,s){let o=k(t,s),c=new g(o.region),l=new I(o.region),u=await G(c,o.artifactBucket,o.slug,s);if(!u)throw Error("No previous release to redeploy. Run a full deploy first.");i(`Redeploying ${o.slug} (${s}) — build ${u.sha.slice(0,12)}`);for(let[m,f]of Object.entries(u.functionNames)){if(!f)continue;d(`Re-activating ${m} (${f})`),await U(l,f,u.functionEnv[m]??{},u.code);let S=u.functionVersions?.[m];if(S)await q(()=>l.updateAlias({FunctionName:f,Name:"live",FunctionVersion:S}))}n("Redeploy complete")}async function $s(t,s){let o=k(t,s),c=new g(o.region),l=new I(o.region),u=await G(c,o.artifactBucket,o.slug,s);if(!u?.previousCode)throw Error("No previous release to roll back to.");i(`Rolling back ${o.slug} (${s}) → ${u.previousSha?.slice(0,12)}`);for(let[m,f]of Object.entries(u.functionNames)){if(!f)continue;d(`Restoring ${m} (${f})`);let S=u.previousFunctionEnv?.[m]??u.functionEnv[m]??{};await U(l,f,S,u.previousCode);let R=u.previousFunctionVersions?.[m];if(R)await q(()=>l.updateAlias({FunctionName:f,Name:"live",FunctionVersion:R}))}await ae(c,o.artifactBucket,o.slug,s,{...u,sha:u.previousSha??u.sha,code:u.previousCode,functionEnv:u.previousFunctionEnv??u.functionEnv,functionVersions:u.previousFunctionVersions??u.functionVersions,previousSha:void 0,previousCode:void 0,previousFunctionEnv:void 0,previousFunctionVersions:void 0,timestamp:new Date().toISOString()}),n("Rollback complete")}async function Po(t,s,o,c){let l=k(t,s),u=new I(l.region),m=`${l.slug}-${s}-http`,S={...(await u.getFunction(m)).Configuration?.Environment?.Variables??{}};if(S.MAINTENANCE_MODE=o?"1":"0",o&&c)S.MAINTENANCE_BYPASS_SECRET=c;if(!o)delete S.MAINTENANCE_BYPASS_SECRET;if(await q(()=>u.updateFunctionConfiguration({FunctionName:m,Environment:{Variables:S}})),(l.app.provisionedConcurrency??0)>0)await u.waitForFunctionActive(m,120),await X(u,m);n(o?"Application is now in maintenance mode (503)":"Application is live")}async function Bt(t,s,o){let c=k(t,s),l=new I(c.region),u=`${c.slug}-${s}-cli`,m=await l.invoke({FunctionName:u,InvocationType:"RequestResponse",Payload:{command:o},LogType:"Tail"});if(m.FunctionError)throw Error(`Command failed: ${m.Payload}`);return m.Payload??""}var ce=(t,s)=>`commands/${t}/${s}/history.json`,ve=50;async function le(t,s,o,c){try{return(await t.getObjectJson(s,ce(o,c)))?.records??[]}catch{return[]}}async function am(t,s,o){let c=k(t,s),l=new I(c.region),u=`${c.slug}-${s}-cli`,m=await l.invoke({FunctionName:u,InvocationType:"RequestResponse",Payload:{command:o},LogType:"Tail"}),f=m.Payload??"",S=m.FunctionError?"error":"ok",R=-1;try{let w=new g(c.region);if(await w.bucketExists(c.artifactBucket)){let N=await le(w,c.artifactBucket,c.slug,s);R=N.reduce((T,P)=>Math.max(T,P.id),0)+1,N.push({id:R,command:o,timestamp:new Date().toISOString(),status:S,output:f.slice(0,4000)}),await w.putObjectJson(c.artifactBucket,ce(c.slug,s),{records:N.slice(-ve)})}}catch{}if(m.FunctionError)throw Error(`Command failed${R>0?` (#${R})`:""}: ${f}`);return{id:R,status:S,output:f}}async function Jd(t,s){let o=k(t,s),c=new g(o.region);return le(c,o.artifactBucket,o.slug,s)}async function cm(t,s,o){let c=await Jd(t,s);if(!c.length)return;return o==null?c[c.length-1]:c.find((l)=>l.id===o)}async function lm(t,s,o){let c=Buffer.from(o,"utf-8").toString("base64");return Bt(t,s,`tscloud:db-query --sql-base64=${c}`)}async function z(t,s){let o=k(t,s),c=new v(o.region),l=new I(o.region),u=new g(o.region),m="not deployed",f={};try{let{Stacks:O}=await c.describeStacks({stackName:o.stackName});m=O?.[0]?.StackStatus??m;for(let h of O?.[0]?.Outputs??[])if(h.OutputKey)f[h.OutputKey]=h.OutputValue??""}catch{}let S=qa(o.app,o.slug,s),R=o.app.provisionedConcurrency??0,w=["http","cli",...S.length?["queue"]:[]],N=`${o.slug}-${s}`,T=[];for(let O of w){let h=`${N}-${O}`,V={mode:O,name:h,version:"$LATEST"};if(R>0){let E=await l.getAlias(h,"live").catch(()=>null);if(E?.FunctionVersion){V.version=E.FunctionVersion;let b=await l.getProvisionedConcurrencyConfig(h,"live").catch(()=>null);if(b)V.provisioned={status:b.Status??"-",allocated:b.AllocatedProvisionedConcurrentExecutions??0,requested:b.RequestedProvisionedConcurrentExecutions??0}}}T.push(V)}let P=await G(u,o.artifactBucket,o.slug,s).catch(()=>null);return{slug:o.slug,environment:s,region:o.region,stackStatus:m,endpoint:f.HttpApiEndpoint,assetUrl:P?.assetUrl,scheduler:o.app.scheduler??"on",queues:S,provisionedConcurrency:R,functions:T,lastRelease:P?{sha:P.sha,timestamp:P.timestamp}:void 0}}function ue(t,s){return`${t}-${s}-db`}async function Ls(t,s,o,c){let l=k(t,s);if(l.app.database?.connection!=="aurora-serverless")throw Error("No Aurora Serverless v2 database is configured for this environment.");let{RDSClient:u}=await import("./chunk-5f1n5d80.js"),m=new u(l.region),f=ue(l.slug,s);d(`Scaling ${f} → ${o}–${c} ACUs`),await m.modifyDBCluster({DBClusterIdentifier:f,ServerlessV2ScalingConfiguration:{MinCapacity:o,MaxCapacity:c},ApplyImmediately:!0}),n(`Scaling applied to ${f} (takes effect shortly).`)}async function dm(t,s,o){let c=k(t,s);if(c.app.database?.connection!=="aurora-serverless")throw Error("No Aurora Serverless v2 database is configured for this environment.");if(!o.toTime&&!o.latest)throw Error("Specify a restore point: --to <ISO timestamp> or --latest.");let{RDSClient:l}=await import("./chunk-5f1n5d80.js"),u=new l(c.region),m=ue(c.slug,s),f=o.latest?"latest":o.toTime.toISOString().replace(/[^0-9]/g,"").slice(0,14),S=o.target??`${m}-restore-${f}`;d(`Restoring ${m} → new cluster ${S} (${o.latest?"latest restorable time":o.toTime.toISOString()})`),await u.restoreDBClusterToPointInTime({DBClusterIdentifier:S,SourceDBClusterIdentifier:m,RestoreToTime:o.toTime,UseLatestRestorableTime:o.latest}),d("Waiting for the restored cluster to be ready");for(let R=0;R<120;R++){let{DBClusters:w}=await u.describeDBClusters({DBClusterIdentifier:S}),N=w?.[0]?.Status;if(N&&N!=="creating")break;await new Promise((T)=>setTimeout(T,15000))}return d("Adding a writer instance"),await u.createDBInstance({DBInstanceIdentifier:`${S}-1`,DBClusterIdentifier:S,Engine:"aurora-mysql",DBInstanceClass:"db.serverless"}),n(`Restore complete → cluster ${S} with writer ${S}-1. Point an env's database at it once available.`),S}
3
+ export{K,bt,Kd,_s,Io,sm,Os,$s,Po,Bt,am,Jd,cm,lm,z,Ls,dm};
@@ -0,0 +1 @@
1
+ import{js,um,pm,Vs}from"./chunk-her9ny96.js";import"./chunk-yp3a0j9n.js";import"./chunk-dj03v81n.js";import"./chunk-4yym8xm4.js";export{um as getComputeDeployHistory,Vs as restoreDatabaseBackup,js as rollbackComputeSite,pm as runComputeRecipe};
@@ -1,7 +1,7 @@
1
- import{F}from"./chunk-tf8qxy9k.js";import"./chunk-4yym8xm4.js";function g(t){let e=t.sites||{};for(let r of Object.values(e))if(r?.repository?.url)return{provider:r.repository.provider,branch:r.repository.branch};return}function m(t){let e=t.trim().toLowerCase();if(e.includes("github.com"))return"github";if(e.includes("gitlab.com"))return"gitlab";if(e.includes("bitbucket.org"))return"bitbucket";return}function a(t){return`'${t.replace(/'/g,"'\\''")}'`}function E(t){return t.replace(/'/g,"''")}function _(t,e){let r=["bunx --bun @stacksjs/ts-cloud deploy","--env",a(t)];if(e.site)r.push("--site",a(e.site));if(e.skipDnsVerification)r.push("--skip-dns-verification");return r.push("--yes"),r.join(" ")}function v(t,e="production",r={}){let l=g(t),n=r.provider||l?.provider;if(n!=="github"&&n!=="gitlab"&&n!=="bitbucket")return null;let i=t.environments?.[e],s=i?.deployBranch||l?.branch||"main",u=_(e,r),c=JSON.stringify(s),d=JSON.stringify(e),p=i?.domain?i.domain.startsWith("http")?i.domain:`https://${i.domain}`:void 0,f=p?`
1
+ import{B}from"./chunk-dj03v81n.js";import"./chunk-4yym8xm4.js";function g(t){let e=t.sites||{};for(let r of Object.values(e))if(r?.repository?.url)return{provider:r.repository.provider,branch:r.repository.branch};return}function m(t){let e=t.trim().toLowerCase();if(e.includes("github.com"))return"github";if(e.includes("gitlab.com"))return"gitlab";if(e.includes("bitbucket.org"))return"bitbucket";return}function a(t){return`'${t.replace(/'/g,"'\\''")}'`}function E(t){return t.replace(/'/g,"''")}function _(t,e){let r=["bunx --bun @stacksjs/ts-cloud deploy","--env",a(t)];if(e.site)r.push("--site",a(e.site));if(e.skipDnsVerification)r.push("--skip-dns-verification");return r.push("--yes"),r.join(" ")}function v(t,e="production",r={}){let l=g(t),n=r.provider||l?.provider;if(n!=="github"&&n!=="gitlab"&&n!=="bitbucket")return null;let i=t.environments?.[e],s=i?.deployBranch||l?.branch||"main",u=_(e,r),c=JSON.stringify(s),d=JSON.stringify(e),p=i?.domain?i.domain.startsWith("http")?i.domain:`https://${i.domain}`:void 0,f=p?`
2
2
  url: ${JSON.stringify(p)}`:"",h=r.setup==="pantry"?` - name: Setup Pantry
3
3
  uses: home-lang/pantry/packages/action@main`:` - uses: oven-sh/setup-bun@v2
4
- - run: bun install --frozen-lockfile`,y=F(t)==="hetzner"&&!!t.infrastructure?.compute,o=r.sshPrivateKeySecret||"SSH_PRIVATE_KEY";if(!/^[A-Z_][A-Z0-9_]*$/i.test(o))throw Error(`Invalid SSH private key secret name '${o}'`);let b=y?`
4
+ - run: bun install --frozen-lockfile`,y=B(t)==="hetzner"&&!!t.infrastructure?.compute,o=r.sshPrivateKeySecret||"SSH_PRIVATE_KEY";if(!/^[A-Z_][A-Z0-9_]*$/i.test(o))throw Error(`Invalid SSH private key secret name '${o}'`);let b=y?`
5
5
  - name: Configure deployment SSH key
6
6
  env:
7
7
  SSH_PRIVATE_KEY: \${{ secrets.${o} }}
@@ -1 +1 @@
1
- import{Dr,Jc,Xc,Tt,kr,Nr,xr,_r,gn}from"./chunk-8z4wgqkh.js";import"./chunk-pnj95hwr.js";import"./chunk-tf8qxy9k.js";import"./chunk-4yym8xm4.js";export{kr as AwsSecretsManagerConfigurationBackend,Nr as AwsSsmConfigurationBackend,_r as ConfigurationService,Dr as ConfigurationStore,xr as ExternalConfigurationBackend,Tt as LocalEncryptedConfigurationBackend,Jc as parseDotenv,Xc as serializeDotenv,gn as synchronizeConfiguredConfiguration};
1
+ import{Dr,Jc,Xc,Tt,kr,Nr,xr,_r,gn}from"./chunk-cz2wq0tf.js";import"./chunk-znd507q9.js";import"./chunk-dj03v81n.js";import"./chunk-4yym8xm4.js";export{kr as AwsSecretsManagerConfigurationBackend,Nr as AwsSsmConfigurationBackend,_r as ConfigurationService,Dr as ConfigurationStore,xr as ExternalConfigurationBackend,Tt as LocalEncryptedConfigurationBackend,Jc as parseDotenv,Xc as serializeDotenv,gn as synchronizeConfiguredConfiguration};
@@ -1,4 +1,4 @@
1
- import{oa,A,w,ki,aa,ca,F,T,Wt,Lr}from"./chunk-tf8qxy9k.js";import{p}from"./chunk-dkf2kcsc.js";import{v}from"./chunk-7593hjcq.js";import{Q}from"./chunk-pf9dbm3b.js";import{g}from"./chunk-nmqjmx30.js";import{J,Ei,zs,P,U,Ir,St,et,No,wi,_o,vt,Hc,Oo,$o,Ii,Lo,Vt,Ws,qc,Pi,Ks,Js,Xs,Qs,Mo,pn,Ys,mn,Zs,Uo}from"./chunk-cqc4e8md.js";import{Ao,Ro,xs}from"./chunk-xxe9ga9r.js";import{ps}from"./chunk-n7yefy09.js";import{rn}from"./chunk-5nmt4s5w.js";import{Cr}from"./chunk-k343q24p.js";import{existsSync as Wl,readFileSync as Gl}from"node:fs";import{readFileSync as es}from"node:fs";import{join as Oa}from"node:path";function rs(e={}){let t=new Set;for(let r of Object.values(e)){if(!r.domain)continue;if(r.redirect||r.deploy==="server"||r.start)t.add(r.domain)}return t}function tr(e){return e.replace(/\.$/,"").toLowerCase()}function Ho(e,t,r){let n=tr(e.name),s=tr(t),i=tr(r),o=i===s?"@":i.endsWith(`.${s}`)?i.slice(0,-(s.length+1)):i;return n===i||n===o||o==="@"&&(n===""||n===s)}async function ko(e,t,r,n,s="A"){let i=await e.listRecords(t);if(!i.success)return[`could not list ${s} records: ${i.message||"unknown provider error"}`];let o=i.records.filter((l)=>l.type===s&&Ho(l,t,r)),a=o.findIndex((l)=>l.content===n);if(o.length<=1||a===-1)return[];let c=[];for(let[l,u]of o.entries()){if(l===a)continue;let d=await e.deleteRecord(t,u);if(!d.success)c.push(`could not remove stale ${u.name} ${s} ${u.content}: ${d.message||"unknown provider error"}`)}return c}function ue(e){if(!e)return;let t=e.trim();if(!t)return;if(!t.includes("/"))return t;let[r]=t.split("/");if(!r.endsWith("::"))return r||void 0;return`${r}1`}var Fo=new Set(["mail","smtp","imap","mx"]);function Wi(e){return!Fo.has(e.split(".")[0]??"")}async function ai(e){let{provider:t,zone:r,fqdn:n,ipv4:s,ipv6:i,ttl:o=600}=e,a={published:[],warnings:[]},c=[{type:"A",content:s}];if(i&&Wi(n))c.push({type:"AAAA",content:i});for(let{type:l,content:u}of c){let d=await t.upsertRecord(r,{name:n,type:l,content:u,ttl:o});if(d?.success===!1){a.warnings.push(`${n} → ${u} failed: ${d.error||d.message||"unknown error"}`);continue}if(a.warnings.push(...(await ko(t,r,n,u,l)).map((m)=>`${n} cleanup: ${m}`)),await Ki(t,r,n,u,l))a.published.push({fqdn:n,type:l,content:u});else a.warnings.push(`${n} → ${u} reported success at ${t.name} but no matching ${l} record exists — create it manually: ${l} ${n} → ${u}`)}return a}async function Ki(e,t,r,n,s){try{if(typeof e?.listRecords!=="function")return!0;let i=await e.listRecords(t);if(!i?.success||!Array.isArray(i.records))return!0;return i.records.some((o)=>{let a=typeof o?.name==="string"?o.name.replace(/\.$/,""):"";return o?.type===s&&a===r&&o?.content===n})}catch{return!0}}function rr(e){return(Buffer.isBuffer(e)?e.toString("utf8"):typeof e==="string"?e:"").replace(/(^|\n)(\s*(?:export\s+)?[A-Za-z_][A-Za-z0-9_]*=).*$/gm,"$1$2[redacted]").replace(/encrypted:[A-Za-z0-9+/=]+/g,"encrypted:[redacted]").trim().slice(-8000)}function st(e,t){let r=rr(e?.stderr),n=/line (\d+): syntax error/.exec(r);if(!n)return"";let s=Number(n[1]),i=t.split(`
1
+ import{oa,A,w,ki,aa,ca,B,T,Wt,Lr}from"./chunk-dj03v81n.js";import{p}from"./chunk-psvqpztv.js";import{v}from"./chunk-xdk0gmxt.js";import{Q}from"./chunk-q8b77zgw.js";import{g}from"./chunk-12x968en.js";import{J,Ei,zs,P,U,Ir,St,et,No,wi,_o,vt,Hc,Oo,$o,Ii,Lo,Vt,Ws,qc,Pi,Ks,Js,Xs,Qs,Mo,pn,Ys,mn,Zs,Uo}from"./chunk-yp3a0j9n.js";import{Ao,Ro,xs}from"./chunk-gyvkravq.js";import{ps}from"./chunk-n7yefy09.js";import{rn}from"./chunk-5nmt4s5w.js";import{Cr}from"./chunk-k343q24p.js";import{existsSync as Wl,readFileSync as Gl}from"node:fs";import{readFileSync as es}from"node:fs";import{join as Oa}from"node:path";function rs(e={}){let t=new Set;for(let r of Object.values(e)){if(!r.domain)continue;if(r.redirect||r.deploy==="server"||r.start)t.add(r.domain)}return t}function tr(e){return e.replace(/\.$/,"").toLowerCase()}function Ho(e,t,r){let n=tr(e.name),s=tr(t),i=tr(r),o=i===s?"@":i.endsWith(`.${s}`)?i.slice(0,-(s.length+1)):i;return n===i||n===o||o==="@"&&(n===""||n===s)}async function ko(e,t,r,n,s="A"){let i=await e.listRecords(t);if(!i.success)return[`could not list ${s} records: ${i.message||"unknown provider error"}`];let o=i.records.filter((l)=>l.type===s&&Ho(l,t,r)),a=o.findIndex((l)=>l.content===n);if(o.length<=1||a===-1)return[];let c=[];for(let[l,u]of o.entries()){if(l===a)continue;let d=await e.deleteRecord(t,u);if(!d.success)c.push(`could not remove stale ${u.name} ${s} ${u.content}: ${d.message||"unknown provider error"}`)}return c}function ue(e){if(!e)return;let t=e.trim();if(!t)return;if(!t.includes("/"))return t;let[r]=t.split("/");if(!r.endsWith("::"))return r||void 0;return`${r}1`}var Fo=new Set(["mail","smtp","imap","mx"]);function Wi(e){return!Fo.has(e.split(".")[0]??"")}async function ai(e){let{provider:t,zone:r,fqdn:n,ipv4:s,ipv6:i,ttl:o=600}=e,a={published:[],warnings:[]},c=[{type:"A",content:s}];if(i&&Wi(n))c.push({type:"AAAA",content:i});for(let{type:l,content:u}of c){let d=await t.upsertRecord(r,{name:n,type:l,content:u,ttl:o});if(d?.success===!1){a.warnings.push(`${n} → ${u} failed: ${d.error||d.message||"unknown error"}`);continue}if(a.warnings.push(...(await ko(t,r,n,u,l)).map((m)=>`${n} cleanup: ${m}`)),await Ki(t,r,n,u,l))a.published.push({fqdn:n,type:l,content:u});else a.warnings.push(`${n} → ${u} reported success at ${t.name} but no matching ${l} record exists — create it manually: ${l} ${n} → ${u}`)}return a}async function Ki(e,t,r,n,s){try{if(typeof e?.listRecords!=="function")return!0;let i=await e.listRecords(t);if(!i?.success||!Array.isArray(i.records))return!0;return i.records.some((o)=>{let a=typeof o?.name==="string"?o.name.replace(/\.$/,""):"";return o?.type===s&&a===r&&o?.content===n})}catch{return!0}}function rr(e){return(Buffer.isBuffer(e)?e.toString("utf8"):typeof e==="string"?e:"").replace(/(^|\n)(\s*(?:export\s+)?[A-Za-z_][A-Za-z0-9_]*=).*$/gm,"$1$2[redacted]").replace(/encrypted:[A-Za-z0-9+/=]+/g,"encrypted:[redacted]").trim().slice(-8000)}function st(e,t){let r=rr(e?.stderr),n=/line (\d+): syntax error/.exec(r);if(!n)return"";let s=Number(n[1]),i=t.split(`
2
2
  `),o=Math.max(1,s-12),a=Math.min(i.length,s+2),c=i.slice(o-1,a).map((l,u)=>{let d=o+u,m=/^[A-Z_][A-Z0-9_]*=/.test(l)?l.replace(/=.*/,"=<redacted>"):l;return` ${String(d).padStart(4)}${d===s?" >":" "} ${m}`});return["",`The deploy script bash rejected is ${i.length} lines; it stopped at ${s}.`,...c].join(`
3
3
  `)}function Pe(e){let t=e,r=typeof t?.status==="number"?` (exit ${t.status})`:"",n=t?.signal?` (signal ${t.signal})`:"",s=[rr(t?.stderr),rr(t?.stdout)].filter(Boolean).join(`
4
4
  `);return`Remote SSH command failed${r}${n}${s?`
@@ -12,7 +12,7 @@ EOF`,`DISK_PCT=$(df -P / | awk 'NR==2{gsub("%","",$5); print $5}')`,"UPTIME_SEC=
12
12
  `)}
13
13
  `}return`${["server {"," listen 80;"," listen [::]:80;",` server_name ${r};`,...n,"}"].join(`
14
14
  `)}
15
- `}function sr(e){let t=`/etc/nginx/sites-available/${e.siteName}`,r=`/etc/nginx/sites-enabled/${e.siteName}`,n=sa(e),s=[...ir()];if(e.auth){let i=Dn(e.siteName),o=(l)=>l.split("'").join("'\\''"),a=o(e.auth.password),c=o(e.auth.username);s.push(`TS_CLOUD_HTPASS=$(openssl passwd -apr1 '${a}')`,`printf '%s:%s\\n' '${c}' "$TS_CLOUD_HTPASS" > ${i}`,`chmod 640 ${i}`,`chown root:www-data ${i} 2>/dev/null || true`)}return s.push(`cat > ${t} <<'TS_CLOUD_NGINX_EOF'`,n.replace(/\n$/,""),"TS_CLOUD_NGINX_EOF",`ln -sf ${t} ${r}`,"rm -f /etc/nginx/sites-enabled/default",`${ye} -t`,"systemctl reload ts-cloud-nginx 2>/dev/null || systemctl restart ts-cloud-nginx 2>/dev/null || true"),s}var ye="/usr/local/bin/ts-cloud-nginx";function ir(e="/opt/pantry"){return[...Ir(),...et(["nginx.org"]),"mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled /var/log/nginx /var/lib/nginx/body /var/lib/nginx/proxy /var/lib/nginx/fastcgi /var/lib/nginx/uwsgi /var/lib/nginx/scgi","getent passwd www-data >/dev/null || useradd --system --no-create-home --shell /usr/sbin/nologin www-data","chown -R www-data:www-data /var/lib/nginx /var/log/nginx",`cat > ${ye} <<'TS_CLOUD_NGINXBIN_EOF'`,"#!/bin/sh",`eval "$(cd ${e} && pantry env 2>/dev/null)"`,"NGINX_BIN=/opt/pantry/pantry/.bin/nginx",'if [ ! -x "$NGINX_BIN" ]; then NGINX_BIN=$(command -v nginx || true); fi','if [ -z "$NGINX_BIN" ]; then echo "nginx binary not found; run pantry install nginx.org or install nginx explicitly" >&2; exit 1; fi','exec "$NGINX_BIN" -c /etc/nginx/nginx.conf "$@"',"TS_CLOUD_NGINXBIN_EOF",`chmod +x ${ye}`,"cat > /etc/nginx/fastcgi_params <<'TS_CLOUD_FCGI_EOF'","fastcgi_param QUERY_STRING $query_string;","fastcgi_param REQUEST_METHOD $request_method;","fastcgi_param CONTENT_TYPE $content_type;","fastcgi_param CONTENT_LENGTH $content_length;","fastcgi_param SCRIPT_NAME $fastcgi_script_name;","fastcgi_param REQUEST_URI $request_uri;","fastcgi_param DOCUMENT_URI $document_uri;","fastcgi_param DOCUMENT_ROOT $document_root;","fastcgi_param SERVER_PROTOCOL $server_protocol;","fastcgi_param REQUEST_SCHEME $scheme;","fastcgi_param HTTPS $https if_not_empty;","fastcgi_param GATEWAY_INTERFACE CGI/1.1;","fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;","fastcgi_param REMOTE_ADDR $remote_addr;","fastcgi_param REMOTE_PORT $remote_port;","fastcgi_param SERVER_ADDR $server_addr;","fastcgi_param SERVER_PORT $server_port;","fastcgi_param SERVER_NAME $server_name;","fastcgi_param REDIRECT_STATUS 200;","TS_CLOUD_FCGI_EOF","cat > /etc/nginx/nginx.conf <<'TS_CLOUD_NGINXCONF_EOF'","user www-data;","worker_processes auto;","pid /run/nginx.pid;","error_log /var/log/nginx/error.log;","events { worker_connections 1024; }","http {"," default_type application/octet-stream;"," types {"," text/html html htm;"," text/css css;"," application/javascript js;"," application/json json;"," image/svg+xml svg;"," image/png png;"," image/jpeg jpg jpeg;"," image/gif gif;"," image/x-icon ico;"," image/webp webp;"," font/woff2 woff2;"," font/woff woff;"," text/plain txt;"," }"," access_log /var/log/nginx/access.log;"," sendfile on;"," tcp_nopush on;"," keepalive_timeout 65;"," server_tokens off;"," client_max_body_size 100m;"," client_body_temp_path /var/lib/nginx/body;"," proxy_temp_path /var/lib/nginx/proxy;"," fastcgi_temp_path /var/lib/nginx/fastcgi;"," uwsgi_temp_path /var/lib/nginx/uwsgi;"," scgi_temp_path /var/lib/nginx/scgi;"," server {"," listen 80 default_server;"," listen [::]:80 default_server;"," server_name _;"," return 444;"," }"," include /etc/nginx/sites-enabled/*;","}","TS_CLOUD_NGINXCONF_EOF","cat > /etc/systemd/system/ts-cloud-nginx.service <<'TS_CLOUD_NGINXUNIT_EOF'","[Unit]","Description=ts-cloud nginx (pantry)","After=network.target","","[Service]","Type=simple",`ExecStartPre=${ye} -t`,`ExecStart=${ye} -g 'daemon off;'`,`ExecReload=${ye} -s reload`,"Restart=always","RestartSec=3","","[Install]","WantedBy=multi-user.target","TS_CLOUD_NGINXUNIT_EOF","systemctl daemon-reload","systemctl enable ts-cloud-nginx","ls /etc/nginx/sites-enabled/* >/dev/null 2>&1 && systemctl restart ts-cloud-nginx || true"]}function it(e,t){return!e.events||e.events.includes(t)}async function or(e,t,r,n={}){if(!e)return[];let s=n.fetchImpl??globalThis.fetch,i=[],o=[],a=(c,l)=>s(c,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(l)}).catch(()=>{return});if(e.slack?.webhookUrl&&it(e,t))i.push("slack"),o.push(a(e.slack.webhookUrl,{text:r}));if(e.discord?.webhookUrl&&it(e,t))i.push("discord"),o.push(a(e.discord.webhookUrl,{content:r}));if(e.telegram?.botToken&&e.telegram.chatId&&it(e,t)){i.push("telegram");let c=`https://api.telegram.org/bot${e.telegram.botToken}/sendMessage`;o.push(a(c,{chat_id:e.telegram.chatId,text:r}))}if(e.webhook?.url&&it(e,t))if(i.push("webhook"),(e.webhook.method||"POST")==="GET"){let c=e.webhook.url.includes("?")?"&":"?",l=`${e.webhook.url}${c}event=${encodeURIComponent(t)}&message=${encodeURIComponent(r)}`;o.push(s(l,{method:"GET"}).catch(()=>{return}))}else o.push(a(e.webhook.url,{event:t,message:r}));if(e.email?.to&&it(e,t))i.push("email"),o.push(ia(e.email,t,r));return await Promise.all(o),i}async function ia(e,t,r){try{let n=await import("./chunk-vn05n5z6.js");await n.email.send({to:e.to,from:e.from,subject:`[ts-cloud] ${t}`,text:r})}catch{}}function In(e,t){return t??e}function He(e){if(!e)return[];let t=[];if(e.slack?.webhookUrl)t.push(`curl -fsS -X POST -H 'Content-Type: application/json' -d "{\\"text\\":\\"$MSG\\"}" ${e.slack.webhookUrl} || true`);if(e.discord?.webhookUrl)t.push(`curl -fsS -X POST -H 'Content-Type: application/json' -d "{\\"content\\":\\"$MSG\\"}" ${e.discord.webhookUrl} || true`);if(e.telegram?.botToken&&e.telegram.chatId)t.push(`curl -fsS -X POST "https://api.telegram.org/bot${e.telegram.botToken}/sendMessage" --data-urlencode "chat_id=${e.telegram.chatId}" --data-urlencode "text=$MSG" || true`);if(e.webhook?.url)t.push(`curl -fsS -X POST -H 'Content-Type: application/json' -d "{\\"message\\":\\"$MSG\\"}" ${e.webhook.url} || true`);if(t.length===0)return[];return["cat > /usr/local/bin/ts-cloud-notify <<'TS_CLOUD_NOTIFY_EOF'","#!/bin/bash",'MSG="$1"',...t,"TS_CLOUD_NOTIFY_EOF","chmod +x /usr/local/bin/ts-cloud-notify"]}import{isIP as Ln}from"node:net";var Xr="/etc/rpx/certs",Hn="/etc/rpx/local-ca",la="rpx-root-ca.crt";function Pt(e=Hn){return`${e.replace(/\/+$/,"")}/${la}`}function Sr(e){if(e.toLowerCase().endsWith(".local"))return!1;return e.split(".").length===2}function Xi(e,t={}){let r=new Set;for(let n of Object.values(e)){if(!n?.domain)continue;if(P(n)==="bucket")continue;r.add(n.domain)}if(t.autoWww!==!1)for(let n of[...r]){if(!Sr(n))continue;r.add(`www.${n}`)}return[...r]}var ua=["1.1.1.1","8.8.8.8"],pa="/var/www/acme-challenge";function da(e){if(typeof e==="string")return{to:e};let t={to:e.to};if(e.status!=null)t.status=e.status;if(e.preservePath!=null)t.preservePath=e.preservePath;return t}function ma(e){let t=e.auth;if(!t||t.enabled===!1||!t.password)return;return{username:t.username||"admin",password:t.password,...t.realm?{realm:t.realm}:{}}}function yr(e){if(!e)return;return e.tls??"local-ca"}function Qi(e,t){if(e.cloud?.provider!=="ssh")return;let r=e.ssh?.lan;if(!r)return;return{...r.hostname?{hostname:r.hostname}:{},tls:r.tls??"local-ca",...t?{ip:t}:{}}}function Nn(e){if(e.cloud?.provider!=="ssh")return;return e.ssh?.profile}function fa(e){let t=e.trim().toLowerCase();if(!t||t.startsWith("*")||t.includes(":")||t.includes("/")||t.includes(" "))return!1;if(Ln(t)!==0)return!1;return t.endsWith(".local")||!t.includes(".")}function ga(e,t,r){let n=[],s=new Set,i=(l,u)=>{let d=l.trim().toLowerCase();if(!d||s.has(d))return;s.add(d),n.push({host:d,source:u})},o=t.hostname?.trim(),a=(o||`${r}.local`).toLowerCase();if(i(a,o?"ssh.lan.hostname":`the ssh provider's default LAN name for project "${r}"`),Object.entries(e).some(([l,u])=>!!u&&Lr(l)))i(`dashboard.${a}`,"the management dashboard site");for(let[l,u]of Object.entries(e)){if(!u?.domain)continue;if(P(u)==="bucket")continue;if(!fa(u.domain))continue;i(u.domain,`site "${l}"`)}return n}function ha(e,t){if(!t?.length)return;return t.find((r)=>{let n=(r.startsWith(".")?r.slice(1):r).toLowerCase();return e===n||e.endsWith(`.${n}`)})}function Sa(e,t){if(!t?.enabled)return;for(let{host:r,source:n}of e){let s=ha(r,t.allowedSuffixes);if(!s)continue;throw Error(`The LAN certificate authority and public on-demand TLS both claim "${r}". LAN source: ${n}. Public source: the on-demand TLS allowed suffix "${s}", which is derived from the site domains. A host is either LAN-only, with a certificate signed by the box's own CA, or public, with one issued by Let's Encrypt, never both. Give the LAN a name no site domain covers (ssh.lan.hostname), or take that domain out of the sites this deploy routes.`)}}function Un(e){if(!e||e==="/")return;let t=`/${e}`.replace(/\/+/g,"/").replace(/\/+$/,"");if(!t.startsWith("/"))t=`/${t}`;return t===""||t==="/"?void 0:t}function ar(e,t){let n=(t?`${e}${t}`:e).replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,128);return n.length>0?n:"rpx"}function ya(e,t){if(!t||t.length===0)return`localhost:${e}`;return t.map((r)=>`${r.privateIp??r.publicIp}:${e}`)}function kn(e,t,r){let n=(t.wwwRoot??"/var/www").replace(/\/+$/,""),s=t.slug??"app",i=t.proxy.certsDir??Xr,o=t.proxy.loadBalancer,a=[],c=new Set;for(let[m,f]of Object.entries(e)){if(!f||!f.domain)continue;let h=P(f);if(h==="bucket")continue;let x=Un(f.path),_=ar(f.domain,x),y=ma(f);if(h==="redirect"){a.push({to:f.domain,path:x,redirect:da(f.redirect),id:_,...y?{auth:y}:{}}),c.add(f.domain);continue}if(h==="proxy"){let S=zs(f);if(S.length===0)continue;let R=S.length===1?S[0]:S;a.push({to:f.domain,path:x,from:R,id:_,...y?{auth:y}:{},...Array.isArray(R)&&o?{loadBalancer:o}:{}}),c.add(f.domain);continue}if(h==="server-app"){if(typeof f.port!=="number")continue;let S=ya(f.port,r);a.push({to:f.domain,path:x,from:S,id:_,...y?{auth:y}:{},...Array.isArray(S)&&o?{loadBalancer:o}:{}})}else a.push({to:f.domain,path:x,static:{dir:`${n}/${s}-${m}/current`,spa:f.spa??!1,pathRewriteStyle:f.pathRewriteStyle??"directory"},cleanUrls:f.pathRewriteStyle!=="flat",...y?{auth:y}:{},id:_});c.add(f.domain)}if(t.proxy.autoWww!==!1)for(let m of[...c]){if(!Sr(m))continue;let f=`www.${m}`;if(c.has(f))continue;a.push({to:f,redirect:{to:`https://${m}`},id:ar(f)}),c.add(f)}a.sort((m,f)=>{if(m.to!==f.to)return m.to.localeCompare(f.to);return(f.path?.length??0)-(m.path?.length??0)});let l={proxies:a,productionCerts:{certsDir:i,certsDirServerNames:[...c]},https:!0,hostsManagement:!1,cleanup:{hosts:!1,certs:!1}};if(t.proxy.onDemandTls&&c.size>0)l.onDemandTls={enabled:!0,allowedSuffixes:[...c],email:t.proxy.onDemandTlsEmail,certsDir:i,staging:t.proxy.onDemandTlsStaging??!1};if(t.proxy.onDemandTls)l.acmeChallengeWebroot=t.proxy.acmeWebroot??pa;let u=t.proxy.cdn,d=u?.frontedHosts?.length?u.frontedHosts:[...c];if(u?.secret&&d.length>0)l.originGuard={header:u.secretHeader??"X-Origin-Verify",value:u.secret,hosts:d};if(t.lan)if(yr(t.lan)==="off")l.https=!1;else{let m=ga(e,t.lan,s);Sa(m,l.onDemandTls);let f=t.lan.ip&&Ln(t.lan.ip)!==0?[t.lan.ip]:[];l.localCa={dir:Hn,hosts:m.map((h)=>h.host),...f.length>0?{ips:f}:{},installTrust:!0}}return l}function ve(e,t){return kn(e,t)}function Me(e,t,r){return kn(e,r,t)}function Z(e){return e?.webServer==="rpx"||e?.proxy?.engine==="rpx"}var ot="/etc/rpx",G="/opt/rpx-gateway",cr="/etc/rpx/gateway.ts",Ue="/etc/rpx/gateway",ce="rpx-gateway.service",Y="/etc/rpx/sites.d";function va(e=Y,t=Xr){return`// Generated by ts-cloud — rpx gateway assembler.
15
+ `}function sr(e){let t=`/etc/nginx/sites-available/${e.siteName}`,r=`/etc/nginx/sites-enabled/${e.siteName}`,n=sa(e),s=[...ir()];if(e.auth){let i=Dn(e.siteName),o=(l)=>l.split("'").join("'\\''"),a=o(e.auth.password),c=o(e.auth.username);s.push(`TS_CLOUD_HTPASS=$(openssl passwd -apr1 '${a}')`,`printf '%s:%s\\n' '${c}' "$TS_CLOUD_HTPASS" > ${i}`,`chmod 640 ${i}`,`chown root:www-data ${i} 2>/dev/null || true`)}return s.push(`cat > ${t} <<'TS_CLOUD_NGINX_EOF'`,n.replace(/\n$/,""),"TS_CLOUD_NGINX_EOF",`ln -sf ${t} ${r}`,"rm -f /etc/nginx/sites-enabled/default",`${ye} -t`,"systemctl reload ts-cloud-nginx 2>/dev/null || systemctl restart ts-cloud-nginx 2>/dev/null || true"),s}var ye="/usr/local/bin/ts-cloud-nginx";function ir(e="/opt/pantry"){return[...Ir(),...et(["nginx.org"]),"mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled /var/log/nginx /var/lib/nginx/body /var/lib/nginx/proxy /var/lib/nginx/fastcgi /var/lib/nginx/uwsgi /var/lib/nginx/scgi","getent passwd www-data >/dev/null || useradd --system --no-create-home --shell /usr/sbin/nologin www-data","chown -R www-data:www-data /var/lib/nginx /var/log/nginx",`cat > ${ye} <<'TS_CLOUD_NGINXBIN_EOF'`,"#!/bin/sh",`eval "$(cd ${e} && pantry env 2>/dev/null)"`,"NGINX_BIN=/opt/pantry/pantry/.bin/nginx",'if [ ! -x "$NGINX_BIN" ]; then NGINX_BIN=$(command -v nginx || true); fi','if [ -z "$NGINX_BIN" ]; then echo "nginx binary not found; run pantry install nginx.org or install nginx explicitly" >&2; exit 1; fi','exec "$NGINX_BIN" -c /etc/nginx/nginx.conf "$@"',"TS_CLOUD_NGINXBIN_EOF",`chmod +x ${ye}`,"cat > /etc/nginx/fastcgi_params <<'TS_CLOUD_FCGI_EOF'","fastcgi_param QUERY_STRING $query_string;","fastcgi_param REQUEST_METHOD $request_method;","fastcgi_param CONTENT_TYPE $content_type;","fastcgi_param CONTENT_LENGTH $content_length;","fastcgi_param SCRIPT_NAME $fastcgi_script_name;","fastcgi_param REQUEST_URI $request_uri;","fastcgi_param DOCUMENT_URI $document_uri;","fastcgi_param DOCUMENT_ROOT $document_root;","fastcgi_param SERVER_PROTOCOL $server_protocol;","fastcgi_param REQUEST_SCHEME $scheme;","fastcgi_param HTTPS $https if_not_empty;","fastcgi_param GATEWAY_INTERFACE CGI/1.1;","fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;","fastcgi_param REMOTE_ADDR $remote_addr;","fastcgi_param REMOTE_PORT $remote_port;","fastcgi_param SERVER_ADDR $server_addr;","fastcgi_param SERVER_PORT $server_port;","fastcgi_param SERVER_NAME $server_name;","fastcgi_param REDIRECT_STATUS 200;","TS_CLOUD_FCGI_EOF","cat > /etc/nginx/nginx.conf <<'TS_CLOUD_NGINXCONF_EOF'","user www-data;","worker_processes auto;","pid /run/nginx.pid;","error_log /var/log/nginx/error.log;","events { worker_connections 1024; }","http {"," default_type application/octet-stream;"," types {"," text/html html htm;"," text/css css;"," application/javascript js;"," application/json json;"," image/svg+xml svg;"," image/png png;"," image/jpeg jpg jpeg;"," image/gif gif;"," image/x-icon ico;"," image/webp webp;"," font/woff2 woff2;"," font/woff woff;"," text/plain txt;"," }"," access_log /var/log/nginx/access.log;"," sendfile on;"," tcp_nopush on;"," keepalive_timeout 65;"," server_tokens off;"," client_max_body_size 100m;"," client_body_temp_path /var/lib/nginx/body;"," proxy_temp_path /var/lib/nginx/proxy;"," fastcgi_temp_path /var/lib/nginx/fastcgi;"," uwsgi_temp_path /var/lib/nginx/uwsgi;"," scgi_temp_path /var/lib/nginx/scgi;"," server {"," listen 80 default_server;"," listen [::]:80 default_server;"," server_name _;"," return 444;"," }"," include /etc/nginx/sites-enabled/*;","}","TS_CLOUD_NGINXCONF_EOF","cat > /etc/systemd/system/ts-cloud-nginx.service <<'TS_CLOUD_NGINXUNIT_EOF'","[Unit]","Description=ts-cloud nginx (pantry)","After=network.target","","[Service]","Type=simple",`ExecStartPre=${ye} -t`,`ExecStart=${ye} -g 'daemon off;'`,`ExecReload=${ye} -s reload`,"Restart=always","RestartSec=3","","[Install]","WantedBy=multi-user.target","TS_CLOUD_NGINXUNIT_EOF","systemctl daemon-reload","systemctl enable ts-cloud-nginx","ls /etc/nginx/sites-enabled/* >/dev/null 2>&1 && systemctl restart ts-cloud-nginx || true"]}function it(e,t){return!e.events||e.events.includes(t)}async function or(e,t,r,n={}){if(!e)return[];let s=n.fetchImpl??globalThis.fetch,i=[],o=[],a=(c,l)=>s(c,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(l)}).catch(()=>{return});if(e.slack?.webhookUrl&&it(e,t))i.push("slack"),o.push(a(e.slack.webhookUrl,{text:r}));if(e.discord?.webhookUrl&&it(e,t))i.push("discord"),o.push(a(e.discord.webhookUrl,{content:r}));if(e.telegram?.botToken&&e.telegram.chatId&&it(e,t)){i.push("telegram");let c=`https://api.telegram.org/bot${e.telegram.botToken}/sendMessage`;o.push(a(c,{chat_id:e.telegram.chatId,text:r}))}if(e.webhook?.url&&it(e,t))if(i.push("webhook"),(e.webhook.method||"POST")==="GET"){let c=e.webhook.url.includes("?")?"&":"?",l=`${e.webhook.url}${c}event=${encodeURIComponent(t)}&message=${encodeURIComponent(r)}`;o.push(s(l,{method:"GET"}).catch(()=>{return}))}else o.push(a(e.webhook.url,{event:t,message:r}));if(e.email?.to&&it(e,t))i.push("email"),o.push(ia(e.email,t,r));return await Promise.all(o),i}async function ia(e,t,r){try{let n=await import("./chunk-pr1dg1km.js");await n.email.send({to:e.to,from:e.from,subject:`[ts-cloud] ${t}`,text:r})}catch{}}function In(e,t){return t??e}function He(e){if(!e)return[];let t=[];if(e.slack?.webhookUrl)t.push(`curl -fsS -X POST -H 'Content-Type: application/json' -d "{\\"text\\":\\"$MSG\\"}" ${e.slack.webhookUrl} || true`);if(e.discord?.webhookUrl)t.push(`curl -fsS -X POST -H 'Content-Type: application/json' -d "{\\"content\\":\\"$MSG\\"}" ${e.discord.webhookUrl} || true`);if(e.telegram?.botToken&&e.telegram.chatId)t.push(`curl -fsS -X POST "https://api.telegram.org/bot${e.telegram.botToken}/sendMessage" --data-urlencode "chat_id=${e.telegram.chatId}" --data-urlencode "text=$MSG" || true`);if(e.webhook?.url)t.push(`curl -fsS -X POST -H 'Content-Type: application/json' -d "{\\"message\\":\\"$MSG\\"}" ${e.webhook.url} || true`);if(t.length===0)return[];return["cat > /usr/local/bin/ts-cloud-notify <<'TS_CLOUD_NOTIFY_EOF'","#!/bin/bash",'MSG="$1"',...t,"TS_CLOUD_NOTIFY_EOF","chmod +x /usr/local/bin/ts-cloud-notify"]}import{isIP as Ln}from"node:net";var Xr="/etc/rpx/certs",Hn="/etc/rpx/local-ca",la="rpx-root-ca.crt";function Pt(e=Hn){return`${e.replace(/\/+$/,"")}/${la}`}function Sr(e){if(e.toLowerCase().endsWith(".local"))return!1;return e.split(".").length===2}function Xi(e,t={}){let r=new Set;for(let n of Object.values(e)){if(!n?.domain)continue;if(P(n)==="bucket")continue;r.add(n.domain)}if(t.autoWww!==!1)for(let n of[...r]){if(!Sr(n))continue;r.add(`www.${n}`)}return[...r]}var ua=["1.1.1.1","8.8.8.8"],pa="/var/www/acme-challenge";function da(e){if(typeof e==="string")return{to:e};let t={to:e.to};if(e.status!=null)t.status=e.status;if(e.preservePath!=null)t.preservePath=e.preservePath;return t}function ma(e){let t=e.auth;if(!t||t.enabled===!1||!t.password)return;return{username:t.username||"admin",password:t.password,...t.realm?{realm:t.realm}:{}}}function yr(e){if(!e)return;return e.tls??"local-ca"}function Qi(e,t){if(e.cloud?.provider!=="ssh")return;let r=e.ssh?.lan;if(!r)return;return{...r.hostname?{hostname:r.hostname}:{},tls:r.tls??"local-ca",...t?{ip:t}:{}}}function Nn(e){if(e.cloud?.provider!=="ssh")return;return e.ssh?.profile}function fa(e){let t=e.trim().toLowerCase();if(!t||t.startsWith("*")||t.includes(":")||t.includes("/")||t.includes(" "))return!1;if(Ln(t)!==0)return!1;return t.endsWith(".local")||!t.includes(".")}function ga(e,t,r){let n=[],s=new Set,i=(l,u)=>{let d=l.trim().toLowerCase();if(!d||s.has(d))return;s.add(d),n.push({host:d,source:u})},o=t.hostname?.trim(),a=(o||`${r}.local`).toLowerCase();if(i(a,o?"ssh.lan.hostname":`the ssh provider's default LAN name for project "${r}"`),Object.entries(e).some(([l,u])=>!!u&&Lr(l)))i(`dashboard.${a}`,"the management dashboard site");for(let[l,u]of Object.entries(e)){if(!u?.domain)continue;if(P(u)==="bucket")continue;if(!fa(u.domain))continue;i(u.domain,`site "${l}"`)}return n}function ha(e,t){if(!t?.length)return;return t.find((r)=>{let n=(r.startsWith(".")?r.slice(1):r).toLowerCase();return e===n||e.endsWith(`.${n}`)})}function Sa(e,t){if(!t?.enabled)return;for(let{host:r,source:n}of e){let s=ha(r,t.allowedSuffixes);if(!s)continue;throw Error(`The LAN certificate authority and public on-demand TLS both claim "${r}". LAN source: ${n}. Public source: the on-demand TLS allowed suffix "${s}", which is derived from the site domains. A host is either LAN-only, with a certificate signed by the box's own CA, or public, with one issued by Let's Encrypt, never both. Give the LAN a name no site domain covers (ssh.lan.hostname), or take that domain out of the sites this deploy routes.`)}}function Un(e){if(!e||e==="/")return;let t=`/${e}`.replace(/\/+/g,"/").replace(/\/+$/,"");if(!t.startsWith("/"))t=`/${t}`;return t===""||t==="/"?void 0:t}function ar(e,t){let n=(t?`${e}${t}`:e).replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,128);return n.length>0?n:"rpx"}function ya(e,t){if(!t||t.length===0)return`localhost:${e}`;return t.map((r)=>`${r.privateIp??r.publicIp}:${e}`)}function kn(e,t,r){let n=(t.wwwRoot??"/var/www").replace(/\/+$/,""),s=t.slug??"app",i=t.proxy.certsDir??Xr,o=t.proxy.loadBalancer,a=[],c=new Set;for(let[m,f]of Object.entries(e)){if(!f||!f.domain)continue;let h=P(f);if(h==="bucket")continue;let x=Un(f.path),_=ar(f.domain,x),y=ma(f);if(h==="redirect"){a.push({to:f.domain,path:x,redirect:da(f.redirect),id:_,...y?{auth:y}:{}}),c.add(f.domain);continue}if(h==="proxy"){let S=zs(f);if(S.length===0)continue;let R=S.length===1?S[0]:S;a.push({to:f.domain,path:x,from:R,id:_,...y?{auth:y}:{},...Array.isArray(R)&&o?{loadBalancer:o}:{}}),c.add(f.domain);continue}if(h==="server-app"){if(typeof f.port!=="number")continue;let S=ya(f.port,r);a.push({to:f.domain,path:x,from:S,id:_,...y?{auth:y}:{},...Array.isArray(S)&&o?{loadBalancer:o}:{}})}else a.push({to:f.domain,path:x,static:{dir:`${n}/${s}-${m}/current`,spa:f.spa??!1,pathRewriteStyle:f.pathRewriteStyle??"directory"},cleanUrls:f.pathRewriteStyle!=="flat",...y?{auth:y}:{},id:_});c.add(f.domain)}if(t.proxy.autoWww!==!1)for(let m of[...c]){if(!Sr(m))continue;let f=`www.${m}`;if(c.has(f))continue;a.push({to:f,redirect:{to:`https://${m}`},id:ar(f)}),c.add(f)}a.sort((m,f)=>{if(m.to!==f.to)return m.to.localeCompare(f.to);return(f.path?.length??0)-(m.path?.length??0)});let l={proxies:a,productionCerts:{certsDir:i,certsDirServerNames:[...c]},https:!0,hostsManagement:!1,cleanup:{hosts:!1,certs:!1}};if(t.proxy.onDemandTls&&c.size>0)l.onDemandTls={enabled:!0,allowedSuffixes:[...c],email:t.proxy.onDemandTlsEmail,certsDir:i,staging:t.proxy.onDemandTlsStaging??!1};if(t.proxy.onDemandTls)l.acmeChallengeWebroot=t.proxy.acmeWebroot??pa;let u=t.proxy.cdn,d=u?.frontedHosts?.length?u.frontedHosts:[...c];if(u?.secret&&d.length>0)l.originGuard={header:u.secretHeader??"X-Origin-Verify",value:u.secret,hosts:d};if(t.lan)if(yr(t.lan)==="off")l.https=!1;else{let m=ga(e,t.lan,s);Sa(m,l.onDemandTls);let f=t.lan.ip&&Ln(t.lan.ip)!==0?[t.lan.ip]:[];l.localCa={dir:Hn,hosts:m.map((h)=>h.host),...f.length>0?{ips:f}:{},installTrust:!0}}return l}function ve(e,t){return kn(e,t)}function Me(e,t,r){return kn(e,r,t)}function Z(e){return e?.webServer==="rpx"||e?.proxy?.engine==="rpx"}var ot="/etc/rpx",G="/opt/rpx-gateway",cr="/etc/rpx/gateway.ts",Ue="/etc/rpx/gateway",ce="rpx-gateway.service",Y="/etc/rpx/sites.d";function va(e=Y,t=Xr){return`// Generated by ts-cloud — rpx gateway assembler.
16
16
  // Merges every app's fragment in ${e} so independent deploys compose
17
17
  // without clobbering each other. Each deploy writes only its own <slug>.json.
18
18
  import { startProxies } from '@stacksjs/rpx'
@@ -141,7 +141,7 @@ writeFileSync(candidatePath, JSON.stringify(candidate, null, 2) + '\\n', { mode:
141
141
  `);return[`mkdir -p ${n}`,...e.tenant?[]:[`(cd ${G} && ${i} add @stacksjs/tlsx@${o}) || true`],...ke(f,_,"TS_CLOUD_RENEW_EOF"),`chmod +x ${f}`,...ke(`/etc/systemd/system/${h}`,y,"TS_CLOUD_RENEW_SVC_EOF"),...ke(`/etc/systemd/system/${x}`,S,"TS_CLOUD_RENEW_TIMER_EOF"),"systemctl daemon-reload",`systemctl enable --now ${x} || true`,`${f} || true`]}function le(e){let{config:t,proxy:r}=e,n=e.bunBin??"/usr/local/bin/bun",s=r.version??"latest",i=t.productionCerts.certsDir,o=(e.slug||"app").replace(/[^a-z0-9._-]+/gi,"-"),a=JSON.stringify({slug:o,...t},null,2),c=va(Y,i);if(e.tenant)return["set -euo pipefail",`if [ ! -x ${Ue} ] || [ ! -d ${G}/node_modules/@stacksjs/rpx ]; then`,` echo "ts-cloud: no rpx gateway on this box. This project attaches to another project's compute, so it does not provision one - deploy the owning project first." >&2`," exit 1","fi",`mkdir -p ${Y} ${i}`,`__tsc_rpx_before="$(sha256sum ${Y}/${o}.json 2>/dev/null | cut -d' ' -f1 || true)"`,...hr(`${Y}/${o}.json`,a,"TS_CLOUD_RPX_FRAGMENT_EOF",{bunBin:n,preserveManagementDashboardRoutes:e.preserveManagementDashboardRoutes}),`__tsc_rpx_after="$(sha256sum ${Y}/${o}.json 2>/dev/null | cut -d' ' -f1 || true)"`,`if [ "$__tsc_rpx_before" != "$__tsc_rpx_after" ] || ! systemctl is-active --quiet ${ce}; then`,` systemctl restart ${ce}`,"else",` echo "[ts-cloud] rpx routes for ${o} unchanged - leaving the shared gateway alone"`,"fi",...An(e)];let u=[`Environment=RPX_UPSTREAM_TIMEOUT=${r.upstreamTimeout??60}`];if(typeof r.maxUpstreamConns==="number")u.push(`Environment=RPX_MAX_UPSTREAM_CONNS=${r.maxUpstreamConns}`);let d=e.profile==="raspberry-pi"?_a:wa,m=r.memoryHigh??d.high,f=r.memoryMax??d.max;return["set -euo pipefail",`mkdir -p ${ot} ${Y} ${i} ${G}`,`rm -rf ${G}.next ${G}.prev`,`mkdir -p ${G}.next`,`(cd ${G}.next && ${n} add @stacksjs/rpx@${s})`,`mv ${G} ${G}.prev`,`mv ${G}.next ${G}`,`rm -rf ${G}.prev`,`ln -sfn ${G}/node_modules ${ot}/node_modules`,...hr(`${Y}/${o}.json`,a,"TS_CLOUD_RPX_FRAGMENT_EOF",{bunBin:n,preserveManagementDashboardRoutes:e.preserveManagementDashboardRoutes}),...ke(cr,c,"TS_CLOUD_RPX_EOF"),`${n} build --production --compile --outfile ${Ue}.next ${cr}`,`chmod 0755 ${Ue}.next`,`mv -f ${Ue}.next ${Ue}`,...ke(`/etc/systemd/system/${ce}`,["[Unit]","Description=rpx reverse-proxy gateway (managed by ts-cloud)","After=network.target network-online.target","Wants=network-online.target","","[Service]","Type=simple",`ExecStart=${Ue}`,`WorkingDirectory=${G}`,"Environment=BUN_INSTALL=/root/.bun","Environment=APP_ENV=production","Environment=NODE_ENV=production",...u,"MemoryAccounting=true",`MemoryHigh=${m}`,`MemoryMax=${f}`,"CPUWeight=500","IOWeight=500","OOMPolicy=stop","Restart=always","RestartSec=5","StartLimitIntervalSec=120","StartLimitBurst=5",`ExecStartPre=/bin/sh -c 'for p in 80 443; do fuser -k -n tcp "$p" 2>/dev/null || true; done; sleep 1'`,"LimitNOFILE=1048576","AmbientCapabilities=CAP_NET_BIND_SERVICE","","[Install]","WantedBy=multi-user.target"].join(`
142
142
  `),"TS_CLOUD_RPX_UNIT_EOF"),"systemctl daemon-reload","systemctl disable --now bun-gateway.service 2>/dev/null || true","systemctl disable --now bun-gateway-renew.timer bun-gateway-renew.service 2>/dev/null || true","rm -f /etc/systemd/system/bun-gateway-renew.timer /etc/systemd/system/bun-gateway-renew.service","systemctl disable --now ts-cloud-nginx.service 2>/dev/null || true",`for timer in /etc/systemd/system/rpx-cert-renew-*.timer; do [ -e "$timer" ] || continue; unit="$(basename "$timer")"; tenant="\${unit#rpx-cert-renew-}"; tenant="\${tenant%.timer}"; [ -f "${Y}/$tenant.json" ] && continue; systemctl disable --now "$unit" 2>/dev/null || true; rm -f "$timer" "/etc/systemd/system/rpx-cert-renew-$tenant.service" "${ot}/renew-certs-$tenant.sh"; done`,`systemctl enable ${ce}`,`systemctl restart ${ce}`,...An(e)]}function Ge(e){let t=(e.slug||"app").replace(/[^a-z0-9._-]+/gi,"-"),r=JSON.stringify({slug:t,...e.config},null,2);return["set -euo pipefail",`mkdir -p ${Y}`,...hr(`${Y}/${t}.json`,r,"TS_CLOUD_RPX_FRAGMENT_EOF",{bunBin:"/usr/local/bin/bun",preserveManagementDashboardRoutes:e.preserveManagementDashboardRoutes}),`systemctl restart ${ce}`]}function Fe(e=[],t={}){let r=t.path??"/root/.ssh/authorized_keys",s=[`mkdir -p ${r.replace(/\/[^/]*$/,"")}`,`touch ${r}`,`sed -i '/^${Mn("# >>> ts-cloud managed keys >>>")}$/,/^${Mn("# <<< ts-cloud managed keys <<<")}$/d' ${r}`];if(e.length>0){let i=["# >>> ts-cloud managed keys >>>",...e.map((o)=>`${o.publicKey.trim()} ${o.name}`),"# <<< ts-cloud managed keys <<<"].join(`
143
143
  `);s.push(`cat >> ${r} <<'TS_CLOUD_KEYS_EOF'`,i,"TS_CLOUD_KEYS_EOF",`chmod 600 ${r}`)}return s}function Mn(e){return e.replace(/[.*[\]\\/^$]/g,"\\$&")}function zn(e,t){let r=`/tmp/ts-cloud-${e}.sh`,n=`TS_CLOUD_${e.toUpperCase().replace(/[^A-Z0-9]/g,"_")}_EOF`;return[`cat > ${r} <<'${n}'`,t.replace(/\n$/,""),n,`chmod +x ${r}`,`bash ${r} || echo "ts-cloud: ${e} setup did not complete; continuing" >&2`,`rm -f ${r}`]}function Bn(e,t){if(e===!1)return;if(e===!0||e===void 0)return t;return e}var Fn={enabled:!0,ports:[80,443]},Ta={mode:"detection",paranoiaLevel:1};function $a(e=!0,t=[]){let r=Bn(e,Fn);if(!r||r.enabled===!1)return[];let n=[...new Set([...r.ports??Fn.ports??[],...t])].sort((s,i)=>s-i);return zn("ddos",Cr({...r,ports:n}))}function Ca(e=!0){let t=Bn(e,Ta);if(!t||t.mode==="off")return[];return zn("waf",ps(t))}function Kn(e={},t=[]){return[...$a(e.ddos,t),...Ca(e.waf)]}function Pa(e){return`${e}-sftp`}function jn(e){return e.port??2222}function xa(e){let t=e.sftp.storage;if(t?.type==="efs"&&t.path)return t.path;return`/var/sftp/${e.slug}`}function ze(e){return`'${e.split("'").join("'\\''")}'`}function Wn(e,t){if(t==="aws")return;let r=e.storage;if(r?.type==="s3"||!r&&!!e.bucket)throw Error(`sftp: bucket-backed storage is only available on the aws provider (Transfer Family). On ${t}, use storage: { type: 'efs' } to serve a directory on the server.`);for(let[s,i]of Object.entries(e.users))if(!i.sshPublicKeys?.length)throw Error(`sftp: user ${s} needs at least one SSH public key`)}function Gn(e){let{slug:t,sftp:r}=e,n=Pa(t),s=xa(e),i=jn(r),o="/etc/ts-sftp/host_key",a="/etc/ts-sftp/users",c=r.serviceUser??"ts-sftp",l=[`# --- ts-sftp (${n}) ---`,`id -u ${c} >/dev/null 2>&1 || useradd --system --home-dir ${ze(s)} --shell /usr/sbin/nologin ${c}`,'export HOME="${HOME:-/root}"','command -v bun >/dev/null 2>&1 || (curl -fsSL https://bun.sh/install | bash && ln -sf "$HOME/.bun/bin/bun" /usr/local/bin/bun)',`mkdir -p ${ze(s)} /etc/ts-sftp /etc/ts-sftp/users /opt/ts-sftp`,"chmod 750 /etc/ts-sftp",`cd /opt/ts-sftp && [ -f package.json ] || printf '{"name":"ts-sftp-host","private":true}' > package.json`,`cd /opt/ts-sftp && bun add ts-sftp@${r.version??"latest"} --no-save --production || bun add ts-sftp@${r.version??"latest"}`,`[ -f /etc/ts-sftp/host_key ] || bun /opt/ts-sftp/node_modules/ts-sftp/dist/bin/cli.js keygen --out /etc/ts-sftp/host_key --comment ${ze(`${t}-sftp`)}`,"chmod 600 /etc/ts-sftp/host_key",'BUN_REAL=$(readlink -f "$(command -v bun)")','case "$BUN_REAL" in /root/*) install -m 0755 "$BUN_REAL" /usr/local/bin/ts-sftp-bun; BUN_BIN=/usr/local/bin/ts-sftp-bun;; *) BUN_BIN="$BUN_REAL";; esac'],u=[];for(let[d,m]of Object.entries(r.users)){let f=(m.homeDirectory??d).replace(/^\/+|\/+$/g,"");if(!f||f.split("/").includes(".."))throw Error(`sftp: invalid homeDirectory for user ${d}`);let h=`/etc/ts-sftp/users/${d}.pub`;l.push(`mkdir -p ${ze(`${s}/${f}`)}`,`printf '%s\\n' ${ze(m.sshPublicKeys.join(`
144
- `))} > ${h}`,`chmod 644 ${h}`),u.push(`--user ${d}:${h}`)}return l.push(`chown -R ${c}:${c} ${ze(s)} /etc/ts-sftp`,`cat > /etc/systemd/system/${n}.service <<TSCLOUD_SFTP_UNIT`,"[Unit]","Description=ts-sftp file transfer server","After=network.target","","[Service]",`User=${c}`,`ExecStart=$BUN_BIN /opt/ts-sftp/node_modules/ts-sftp/dist/bin/cli.js serve --root ${s} --host-key /etc/ts-sftp/host_key --port ${i}${r.readOnly?" --read-only":""} ${u.join(" ")}`,"Restart=always","RestartSec=2","NoNewPrivileges=true","PrivateTmp=true","ProtectSystem=strict",`ReadWritePaths=${s}`,"","[Install]","WantedBy=multi-user.target","TSCLOUD_SFTP_UNIT","systemctl daemon-reload",`systemctl enable ${n}.service`,`systemctl restart ${n}.service`),l}function Vn(e){return e?[jn(e)]:[]}var Ra=[80,443];function xt(e={}){if(e.enabled===!1)return[];let t=[...new Set([...Ra,...e.allowedPorts||[]])].filter((n)=>Number.isInteger(n)&&n>=1&&n<=65535).sort((n,s)=>n-s),r=["export DEBIAN_FRONTEND=noninteractive","command -v ufw >/dev/null 2>&1 || apt-get install -y ufw","ufw --force reset","ufw default deny incoming","ufw default allow outgoing","ufw allow OpenSSH"];for(let n of t)r.push(`ufw allow ${n}/tcp`);return r.push("ufw --force enable"),r}function Re(e,t){let r=e.infrastructure?.compute??{},n=r.runtime==="php"||!!r.php,s=ki(e,{environment:t?.environment}),i=n||!!r.managedServices,o=i?Ir():[],a=!Z(r),c=n?[...o,...Ct({versions:r.php?.versions,default:r.php?.default,extensions:r.php?.extensions,installNginx:a,optimizeForProduction:r.php?.optimizeForProduction,ini:r.php?.ini}),...a?ir():[]]:void 0,l=[],u=w(e);if(!n&&i)l.push(...o);if(l.push(...He(e.notifications)),r.managedServices)l.push(..._o(r.managedServices,{mail:s.enabled?s:void 0}),...vt(u,r.managedServices));let d=e.infrastructure?.sftp;if(d)Wn(d,F(e)),l.push(...Gn({slug:e.project.slug,sftp:d}));let m=r.firewall??(n?{enabled:!0}:{enabled:!1}),f=aa(s);if(l.push(...xt(f.length>0?{...m,allowedPorts:[...m.allowedPorts??[],...f]}:m)),l.push(...Kn({ddos:r.ddos,waf:r.waf},[...r.firewall?.allowedPorts??[],...f])),l.push(...he(r.autoUpdates??n)),l.push(...Ji(r.appUpdates)),l.push(...Se(r.monitoring??n)),l.push(...Fe(r.sshKeys,{path:t?.authorizedKeysPath})),r.backups?.enabled)l.push(...Oo({database:u,backups:r.backups}));return{runtime:r.runtime||"bun",runtimeVersion:r.runtimeVersion||"latest",phpBox:n,phpProvision:c,servicesProvision:l.length>0?l:void 0}}function ee(e={}){let{runtime:t="bun",runtimeVersion:r="latest",systemPackages:n=[],database:s,phpProvision:i,servicesProvision:o,caddyfile:a,rpxProvision:c,baked:l=!1,swapGb:u=2}=e,d=new Set(n);if(s==="sqlite")d.add("sqlite3");else if(s==="mysql")d.add("mysql-client");else if(s==="postgres")d.add("postgresql-client");let m=`#!/bin/bash
144
+ `))} > ${h}`,`chmod 644 ${h}`),u.push(`--user ${d}:${h}`)}return l.push(`chown -R ${c}:${c} ${ze(s)} /etc/ts-sftp`,`cat > /etc/systemd/system/${n}.service <<TSCLOUD_SFTP_UNIT`,"[Unit]","Description=ts-sftp file transfer server","After=network.target","","[Service]",`User=${c}`,`ExecStart=$BUN_BIN /opt/ts-sftp/node_modules/ts-sftp/dist/bin/cli.js serve --root ${s} --host-key /etc/ts-sftp/host_key --port ${i}${r.readOnly?" --read-only":""} ${u.join(" ")}`,"Restart=always","RestartSec=2","NoNewPrivileges=true","PrivateTmp=true","ProtectSystem=strict",`ReadWritePaths=${s}`,"","[Install]","WantedBy=multi-user.target","TSCLOUD_SFTP_UNIT","systemctl daemon-reload",`systemctl enable ${n}.service`,`systemctl restart ${n}.service`),l}function Vn(e){return e?[jn(e)]:[]}var Ra=[80,443];function xt(e={}){if(e.enabled===!1)return[];let t=[...new Set([...Ra,...e.allowedPorts||[]])].filter((n)=>Number.isInteger(n)&&n>=1&&n<=65535).sort((n,s)=>n-s),r=["export DEBIAN_FRONTEND=noninteractive","command -v ufw >/dev/null 2>&1 || apt-get install -y ufw","ufw --force reset","ufw default deny incoming","ufw default allow outgoing","ufw allow OpenSSH"];for(let n of t)r.push(`ufw allow ${n}/tcp`);return r.push("ufw --force enable"),r}function Re(e,t){let r=e.infrastructure?.compute??{},n=r.runtime==="php"||!!r.php,s=ki(e,{environment:t?.environment}),i=n||!!r.managedServices,o=i?Ir():[],a=!Z(r),c=n?[...o,...Ct({versions:r.php?.versions,default:r.php?.default,extensions:r.php?.extensions,installNginx:a,optimizeForProduction:r.php?.optimizeForProduction,ini:r.php?.ini}),...a?ir():[]]:void 0,l=[],u=w(e);if(!n&&i)l.push(...o);if(l.push(...He(e.notifications)),r.managedServices)l.push(..._o(r.managedServices,{mail:s.enabled?s:void 0}),...vt(u,r.managedServices));let d=e.infrastructure?.sftp;if(d)Wn(d,B(e)),l.push(...Gn({slug:e.project.slug,sftp:d}));let m=r.firewall??(n?{enabled:!0}:{enabled:!1}),f=aa(s);if(l.push(...xt(f.length>0?{...m,allowedPorts:[...m.allowedPorts??[],...f]}:m)),l.push(...Kn({ddos:r.ddos,waf:r.waf},[...r.firewall?.allowedPorts??[],...f])),l.push(...he(r.autoUpdates??n)),l.push(...Ji(r.appUpdates)),l.push(...Se(r.monitoring??n)),l.push(...Fe(r.sshKeys,{path:t?.authorizedKeysPath})),r.backups?.enabled)l.push(...Oo({database:u,backups:r.backups}));return{runtime:r.runtime||"bun",runtimeVersion:r.runtimeVersion||"latest",phpBox:n,phpProvision:c,servicesProvision:l.length>0?l:void 0}}function ee(e={}){let{runtime:t="bun",runtimeVersion:r="latest",systemPackages:n=[],database:s,phpProvision:i,servicesProvision:o,caddyfile:a,rpxProvision:c,baked:l=!1,swapGb:u=2}=e,d=new Set(n);if(s==="sqlite")d.add("sqlite3");else if(s==="mysql")d.add("mysql-client");else if(s==="postgres")d.add("postgresql-client");let m=`#!/bin/bash
145
145
  set -euo pipefail
146
146
  `,f=Math.floor(u);if(f>0)m+=`
147
147
  # Swap: a swapless box under memory pressure lets the kernel OOM killer choose
@@ -274,7 +274,7 @@ ${s}
274
274
  runcmd:
275
275
  - [ bash, ${r} ]
276
276
  ${Es(t.after)}`}function Yr(e){let t=new Set([80,443,...e.sitePorts,...e.allowedPorts??[]]);if(e.allowSsh)t.add(22);return[...t].map((r)=>({direction:"in",protocol:"tcp",port:String(r),source_ips:["0.0.0.0/0","::/0"],description:`ts-cloud port ${r}`}))}var Br={micro:"cpx11",small:"cx23",medium:"cx33",large:"cx43",xlarge:"cx53","2xlarge":"ccx33"};function dt(e){if(!e)return Br.micro;if(e in Br)return Br[e];return e}var K="ts-cloud";function j(e,t,r="app"){return{[`${K}/project`]:e,[`${K}/environment`]:t,[`${K}/role`]:r,[`${K}/managed-by`]:"ts-cloud"}}function te(e,t,r,n="app"){return Kr(e,t,r)&&e[`${K}/role`]===n}function Kr(e,t,r){if(!e)return!1;return e[`${K}/project`]===t&&e[`${K}/environment`]===r}import{mkdir as Xa,readFile as Ya,rename as Ja,writeFile as Qa}from"node:fs/promises";import{join as Os}from"node:path";var jr="storage/cloud/state";function be(e=process.cwd()){return oa()?A(e,"state"):Os(e,jr)}function Xe(e,t=process.cwd()){return Os(be(t),`${e}.json`)}async function Ie(e,t=process.cwd()){try{let r=await Ya(Xe(e,t),"utf8");return JSON.parse(r)}catch{return null}}async function Ye(e,t,r=process.cwd()){let n=Xe(e,r);await Xa(be(r),{recursive:!0});let s=`${n}.${process.pid}.tmp`;await Qa(s,`${JSON.stringify(t,null,2)}
277
- `,"utf8"),await Ja(s,n)}async function pe(e){return Ie(e)}async function ut(e,t){await Ye(e,t)}var Wr=268435456;function yt(e){return`'${e.replace(/'/g,`'"'"'`)}'`}async function sc(e){let t=ec("sha256");for await(let r of tc(e))t.update(r);return t.digest("hex")}class _e{name="hetzner";usesCloudFormation=!1;client;sshPrivateKeyPath;sshPublicKeyPath;sshUser;location;stateOnly;waitForBoot;bootWait;constructor(e={}){let t=ft(e.apiToken);if(this.stateOnly=!e.client&&!t&&e.allowStateOnly===!0,!e.client&&!t&&!this.stateOnly)throw Error("Hetzner API token required. Set hetzner.apiToken in cloud.config.ts or HCLOUD_TOKEN / HETZNER_API_TOKEN.");this.client=e.client??new We({apiToken:t??""});let r=lr(void 0,{sshPrivateKeyPath:e.sshPrivateKeyPath,sshPublicKeyPath:e.sshPublicKeyPath,sshUser:e.sshUser,location:e.location});this.sshPrivateKeyPath=r.sshPrivateKeyPath,this.sshPublicKeyPath=r.sshPublicKeyPath,this.sshUser=r.sshUser,this.location=r.location,this.waitForBoot=e.waitForBoot??!0,this.bootWait={sshIntervalMs:e.bootWait?.sshIntervalMs??5000,sshTimeoutMs:e.bootWait?.sshTimeoutMs??300000,cloudInitIntervalMs:e.bootWait?.cloudInitIntervalMs??5000,cloudInitTimeoutMs:e.bootWait?.cloudInitTimeoutMs??600000}}async provisionComputeInfrastructure(e){let{config:t,environment:r}=e,n=t.project.slug,s=t.infrastructure?.compute;if(!s)throw Error("infrastructure.compute is required to provision Hetzner compute");if(t.cloud?.attachTo)return this.attachToComputeInfrastructure(t.cloud.attachTo,t,r);let i=T(t,r),o=`${n}-${r}-app`,a=s.runtime==="php"||!!s.php,c=Cs(s);if(c.dedicatedServices||c.appServers>1)return a?this.provisionFleet(e,c):this.provisionBunFleet(e,c);let l=j(n,r,"app"),u=t.sites||{},d=Z(s)?[]:this.collectUpstreamPorts(u),m=`${n}-${r}-app-fw`,{firewall:f}=await this.ensureFirewall(m,l,Yr({allowSsh:s.allowSsh!==!1,sitePorts:d,allowedPorts:[...s.firewall?.allowedPorts??[],...Vn(t.infrastructure?.sftp)]})),h=await this.ensureSshKey(n,r,l),x=await pe(i);if(x?.serverId){let k=await this.tryGetServer(x.serverId);if(k&&k.status!=="off")return this.outputsFromState(x,k)}let _=await this.findExistingServer(n,r,o);if(_&&_.status!=="off"){let k={provider:"hetzner",stackName:i,serverId:_.id,serverName:_.name,publicIp:_.public_net.ipv4?.ip,publicIpv6:ue(_.public_net.ipv6?.ip),deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser};return await ut(i,k),this.outputsFromState(k,_)}let y=s.proxy?.engine==="rpx"?le({proxy:s.proxy,config:ve(u,{proxy:s.proxy,slug:t.project.slug}),slug:t.project.slug,bunBin:s.runtime==="node"||s.runtime==="deno"?void 0:"/usr/local/bin/bun"}):void 0,S=Re(t,{environment:r}),R=s.bakedImage===!0,b=ee({runtime:S.runtime,runtimeVersion:S.runtimeVersion,systemPackages:s.systemPackages,database:t.infrastructure?.database,phpProvision:S.phpProvision,servicesProvision:S.servicesProvision,rpxProvision:y,baked:R,swapGb:s.swapGb}),I=de(b),C=dt(s.size),E=gt(t),{server:H,action:re}=await this.client.createServer({name:o,serverType:C,image:E,location:t.hetzner?.location||this.location,userData:I,labels:l,sshKeys:h?[h]:void 0,firewalls:[{firewall:f.id}]});await this.client.waitForAction(re.id);let W=await this.client.waitForServerRunning(H.id),N={provider:"hetzner",stackName:i,serverId:W.id,serverName:W.name,firewallId:f.id,publicIp:W.public_net.ipv4?.ip,publicIpv6:ue(W.public_net.ipv6?.ip),deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser};await ut(i,N);let q=W.public_net.ipv4?.ip;if(q&&this.waitForBoot)await this.waitForSshReady(q),await this.waitForCloudInit(q);return this.outputsFromState(N,W)}async provisionFleet(e,t){let{config:r,environment:n}=e,s=r.project.slug,i=r.infrastructure.compute,o=T(r,n),a=dt(i.size),c=gt(r),l=r.hetzner?.location||this.location,u=i.bakedImage===!0,d=await pe(o);if(d?.loadBalancerId){let M=(await this.client.listLoadBalancers().catch(()=>[])).find((D)=>D.id===d.loadBalancerId);if(M)return{appPublicIp:M.public_net?.ipv4?.ip,loadBalancerIp:M.public_net?.ipv4?.ip,servicesPrivateIp:d.servicesPrivateIp,deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser}}let m=await this.ensureSshKey(s,n,j(s,n,"app")),f=`${s}-${n}-net`,x=(await this.client.listNetworks().catch(()=>[])).find((L)=>L.name===f)??await this.client.createNetwork({name:f,labels:j(s,n,"app")}),{firewall:_}=await this.ensureFirewall(`${s}-${n}-app-fw`,j(s,n,"app"),Yr({allowSsh:!0,sitePorts:[],allowedPorts:i.firewall?.allowedPorts})),{firewall:y}=await this.ensureFirewall(`${s}-${n}-services-fw`,j(s,n,"services"),[{direction:"in",protocol:"tcp",port:"22",source_ips:["0.0.0.0/0","::/0"]},{direction:"in",protocol:"tcp",port:"3306",source_ips:[x.ip_range]},{direction:"in",protocol:"tcp",port:"5432",source_ips:[x.ip_range]},{direction:"in",protocol:"tcp",port:"6379",source_ips:[x.ip_range]},{direction:"in",protocol:"tcp",port:"7700",source_ips:[x.ip_range]}]),S=Fr(r),R=de(ee({runtime:"php",servicesProvision:S,baked:u,swapGb:i.swapGb})),b=await this.client.listServers().catch(()=>[]),I=[],C=b.find((L)=>te(L.labels,s,n,"services"));if(!C){let{server:L,action:M}=await this.client.createServer({name:`${s}-${n}-services`,serverType:dt(typeof i.servicesServer==="object"?i.servicesServer.size:i.size),image:c,location:l,userData:R,labels:j(s,n,"services"),sshKeys:m?[m]:void 0,firewalls:[{firewall:y.id}],networks:[x.id]});await this.client.waitForAction(M.id),C=await this.client.waitForServerRunning(L.id),I.push(L.id)}let E=C.id,H=C.private_net?.[0]?.ip??(await this.client.getServer(E)).private_net?.[0]?.ip,re=[...he(!0),...Se(!0),...Fe(i.sshKeys),...He(r.notifications)],W=Ct({versions:i.php?.versions,default:i.php?.default,extensions:i.php?.extensions,installNginx:!Z(i),optimizeForProduction:i.php?.optimizeForProduction,ini:i.php?.ini}),N=de(ee({runtime:"php",phpProvision:W,servicesProvision:re,baked:u,swapGb:i.swapGb})),q=b.filter((L)=>te(L.labels,s,n,"app")),k=q.map((L)=>L.id);if(q.length>t.appServers)for(let L of q.slice(t.appServers))await this.client.deleteServer(L.id).catch(()=>{}),k.splice(k.indexOf(L.id),1);for(let L=q.length;L<t.appServers;L++){let{server:M,action:D}=await this.client.createServer({name:`${s}-${n}-app-${L+1}`,serverType:a,image:c,location:l,userData:N,labels:j(s,n,"app"),sshKeys:m?[m]:void 0,firewalls:[{firewall:_.id}],networks:[x.id]});await this.client.waitForAction(D.id),k.push(M.id),I.push(M.id)}if(this.waitForBoot&&I.length>0)await Promise.all(I.map(async(L)=>{let D=(await this.client.waitForServerRunning(L)).public_net.ipv4?.ip;if(D)await this.waitForSshReady(D),await this.waitForCloudInit(D)}));let X=`${s}-${n}-lb`,oe,fe;if(t.loadBalancer){let M=(await this.client.listLoadBalancers().catch(()=>[])).find((D)=>D.name===X)??await this.client.createLoadBalancer({name:X,location:l,network:x.id,labels:j(s,n,"lb"),labelSelector:`ts-cloud/project=${s},ts-cloud/environment=${n},ts-cloud/role=app`,services:[{listenPort:80,destinationPort:80},{listenPort:443,destinationPort:443}]});fe=M.id,oe=M.public_net?.ipv4?.ip}let $e=oe??(await this.client.getServer(k[0]).catch(()=>{return}))?.public_net.ipv4?.ip,ne={provider:"hetzner",stackName:o,networkId:x.id,loadBalancerId:fe,servicesServerId:E,servicesPrivateIp:H,publicIp:$e,deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser};return await ut(o,ne),{appPublicIp:$e,loadBalancerIp:oe,servicesPrivateIp:H,deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser}}async provisionBunFleet(e,t){let{config:r,environment:n}=e,s=r.project.slug,i=r.infrastructure.compute,o=T(r,n),a=dt(i.size),c=gt(r),l=r.hetzner?.location||this.location,u=i.bakedImage===!0,d=r.sites||{},m=await pe(o);if(m?.lbServerId){let D=await this.tryGetServer(m.lbServerId);if(D&&D.status!=="off"){let z=i.proxy?.engine==="rpx"?i.proxy:{engine:"rpx"},B=await this.findComputeTargets({slug:s,environment:n,role:"app",stackName:o});return this.refreshLbRoutes(D,d,B.map((ae)=>({privateIp:ae.privateIp,publicIp:ae.publicIp})),z,s),{appPublicIp:D.public_net.ipv4?.ip??m.publicIp,loadBalancerIp:D.public_net.ipv4?.ip??m.publicIp,servicesPrivateIp:m.servicesPrivateIp,deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser}}}let f=await this.ensureSshKey(s,n,j(s,n,"app")),h=`${s}-${n}-net`,_=(await this.client.listNetworks().catch(()=>[])).find((D)=>D.name===h)??await this.client.createNetwork({name:h,labels:j(s,n,"app")}),y=this.collectUpstreamPorts(d),{firewall:S}=await this.ensureFirewall(`${s}-${n}-app-fw`,j(s,n,"app"),[{direction:"in",protocol:"tcp",port:"22",source_ips:["0.0.0.0/0","::/0"]},...y.map((D)=>({direction:"in",protocol:"tcp",port:String(D),source_ips:[_.ip_range],description:`ts-cloud port ${D} (private, LB-only)`}))]),{firewall:R}=await this.ensureFirewall(`${s}-${n}-lb-fw`,j(s,n,"lb"),Yr({allowSsh:!0,sitePorts:[],allowedPorts:i.firewall?.allowedPorts})),b=await this.client.listServers().catch(()=>[]),I=[],C,E,H=!!i.servicesServer||!!i.managedServices,re=t.dedicatedServices&&H;if(re){let{firewall:D}=await this.ensureFirewall(`${s}-${n}-services-fw`,j(s,n,"services"),[{direction:"in",protocol:"tcp",port:"22",source_ips:["0.0.0.0/0","::/0"]},{direction:"in",protocol:"tcp",port:"3306",source_ips:[_.ip_range]},{direction:"in",protocol:"tcp",port:"5432",source_ips:[_.ip_range]},{direction:"in",protocol:"tcp",port:"6379",source_ips:[_.ip_range]},{direction:"in",protocol:"tcp",port:"7700",source_ips:[_.ip_range]}]),z=Fr(r),B=de(ee({runtime:"bun",servicesProvision:z,baked:u,swapGb:i.swapGb})),ae=b.find((ge)=>te(ge.labels,s,n,"services"));if(!ae){let{server:ge,action:rt}=await this.client.createServer({name:`${s}-${n}-services`,serverType:dt(typeof i.servicesServer==="object"?i.servicesServer.size:i.size),image:c,location:l,userData:B,labels:j(s,n,"services"),sshKeys:f?[f]:void 0,firewalls:[{firewall:D.id}],networks:[_.id]});await this.client.waitForAction(rt.id),ae=await this.client.waitForServerRunning(ge.id),I.push(ge.id)}C=ae.id,E=ae.private_net?.[0]?.ip??(await this.client.getServer(C)).private_net?.[0]?.ip}let W=re?{...i,managedServices:void 0,backups:void 0}:i,N=Re({...r,infrastructure:{...r.infrastructure,compute:W}},{environment:n}),q=de(ee({runtime:N.runtime,runtimeVersion:N.runtimeVersion,systemPackages:i.systemPackages,database:r.infrastructure?.database,servicesProvision:N.servicesProvision,baked:u,swapGb:i.swapGb})),k=b.filter((D)=>te(D.labels,s,n,"app")),X=k.map((D)=>D.id);if(k.length>t.appServers)for(let D of k.slice(t.appServers))await this.client.deleteServer(D.id).catch(()=>{}),X.splice(X.indexOf(D.id),1);for(let D=k.length;D<t.appServers;D++){let{server:z,action:B}=await this.client.createServer({name:`${s}-${n}-app-${D+1}`,serverType:a,image:c,location:l,userData:q,labels:j(s,n,"app"),sshKeys:f?[f]:void 0,firewalls:[{firewall:S.id}],networks:[_.id]});await this.client.waitForAction(B.id),X.push(z.id),I.push(z.id)}if(this.waitForBoot&&I.length>0)await Promise.all(I.map(async(D)=>{let B=(await this.client.waitForServerRunning(D)).public_net.ipv4?.ip;if(B)await this.waitForSshReady(B),await this.waitForCloudInit(B)}));let oe=[];for(let D of X){let z=b.find((B)=>B.id===D);if(!z||!z.private_net?.[0]?.ip)z=await this.client.getServer(D).catch(()=>z);oe.push({privateIp:z?.private_net?.[0]?.ip,publicIp:z?.public_net.ipv4?.ip})}let fe=`${s}-${n}-lb`,$e,ne;if(t.loadBalancer){let D=b.find((B)=>te(B.labels,s,n,"lb")),z=i.proxy?.engine==="rpx"?i.proxy:{engine:"rpx"};if(!D){let B=le({proxy:z,config:Me(d,oe,{proxy:z,slug:s}),slug:s,bunBin:N.runtime==="node"||N.runtime==="deno"?void 0:"/usr/local/bin/bun"}),ae=de(ee({runtime:"bun",rpxProvision:B,baked:!1})),ge=typeof i.loadBalancer==="object"?i.loadBalancer.size:void 0,{server:rt,action:er}=await this.client.createServer({name:fe,serverType:dt(ge??"micro"),image:c,location:l,userData:ae,labels:j(s,n,"lb"),sshKeys:f?[f]:void 0,firewalls:[{firewall:R.id}],networks:[_.id]});if(await this.client.waitForAction(er.id),D=await this.client.waitForServerRunning(rt.id),this.waitForBoot){let nt=D.public_net.ipv4?.ip;if(nt)await this.waitForSshReady(nt),await this.waitForCloudInit(nt)}}else this.refreshLbRoutes(D,d,oe,z,s);$e=D.id,ne=D.public_net.ipv4?.ip}let L=ne??(await this.client.getServer(X[0]).catch(()=>{return}))?.public_net.ipv4?.ip,M={provider:"hetzner",stackName:o,networkId:_.id,lbServerId:$e,appServerIds:X,servicesServerId:C,servicesPrivateIp:E,publicIp:L,deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser};return await ut(o,M),{appPublicIp:L,loadBalancerIp:ne,servicesPrivateIp:E,deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser}}async destroyCompute(e){let{config:t,environment:r}=e,n=t.project.slug,s=T(t,r),i=await pe(s),o=[],a=`${n}-${r}-lb`,l=(await this.client.listLoadBalancers().catch(()=>[])).find((S)=>S.name===a);if(l)try{await this.client.deleteLoadBalancer(l.id),o.push(`load balancer ${a}`)}catch{}let u=await this.client.listServers().catch(()=>[]),d=new Set;for(let S of u)if(te(S.labels,n,r,"app")||te(S.labels,n,r,"services")||te(S.labels,n,r,"lb"))d.add(S.id);if(i?.serverId)d.add(i.serverId);if(i?.servicesServerId)d.add(i.servicesServerId);if(i?.lbServerId)d.add(i.lbServerId);for(let S of i?.appServerIds??[])d.add(S);for(let S of d)try{await this.client.deleteServer(S),o.push(`server ${S}`)}catch{}let m=await this.client.listFirewalls().catch(()=>[]);for(let S of[`${n}-${r}-app-fw`,`${n}-${r}-services-fw`,`${n}-${r}-lb-fw`]){let R=m.find((b)=>b.name===S);if(!R)continue;for(let b=0;b<12;b++)try{await this.client.deleteFirewall(R.id),o.push(`firewall ${S}`);break}catch{await this.sleep(3000)}}let f=`${n}-${r}-net`,x=(await this.client.listNetworks().catch(()=>[])).find((S)=>S.name===f);if(x)for(let S=0;S<12;S++)try{await this.client.deleteNetwork(x.id),o.push(`network ${f}`);break}catch{await this.sleep(3000)}let y=(await this.client.listSshKeys().catch(()=>[])).filter((S)=>Kr(S.labels,n,r));for(let S of y)try{await this.client.deleteSshKey(S.id),o.push(`ssh key ${S.name}`)}catch{}return await ut(s,{provider:"hetzner",stackName:s}).catch(()=>{}),{destroyed:o}}async getComputeOutputs(e){let t=T(e.config,e.environment),r=await pe(t);if(r?.serverId){let i=await this.tryGetServer(r.serverId);if(i)return this.outputsFromState(r,i)}if(r?.lbServerId){let o=(await this.tryGetServer(r.lbServerId))?.public_net.ipv4?.ip??r.publicIp;return{...this.outputsFromState(r),appPublicIp:o,loadBalancerIp:o}}if(r?.loadBalancerId||r?.servicesPrivateIp)return this.outputsFromState(r);let s=(await this.findComputeTargets({slug:e.config.project.slug,environment:e.environment,role:"app",stackName:T(e.config,e.environment)}))[0];return{deployStoragePath:"/var/ts-cloud/staging",appInstanceId:s?.id,appPublicIp:s?.publicIp,sshUser:this.sshUser}}async uploadRelease(e){let t=e.targets?.length?e.targets:await this.findComputeTargets({slug:e.config.project.slug,environment:e.environment,role:"app",stackName:T(e.config,e.environment)});if(t.length===0)throw Error("No Hetzner compute targets found for release upload");let s=`/var/ts-cloud/staging/${`${e.remoteKey.replace(/^releases\//,"").replace(/\.tar\.gz$/,"").replace(/\//g,"-")}-${Is()}.tar.gz`}`,i=await sc(e.localPath),o=Be,a=`${o}/${i}.tar.gz`;for(let c of t){if(!c.publicIp)throw Error(`Target ${c.id} has no public IP for SCP upload`);let l=["set -euo pipefail",`mkdir -p ${yt(o)} /var/ts-cloud/staging`,`test -s ${yt(a)}`,`cp -- ${yt(a)} ${yt(s)}`].join(`
277
+ `,"utf8"),await Ja(s,n)}async function pe(e){return Ie(e)}async function ut(e,t){await Ye(e,t)}var Wr=268435456;function yt(e){return`'${e.replace(/'/g,`'"'"'`)}'`}async function sc(e){let t=ec("sha256");for await(let r of tc(e))t.update(r);return t.digest("hex")}class _e{name="hetzner";usesCloudFormation=!1;client;sshPrivateKeyPath;sshPublicKeyPath;sshUser;location;stateOnly;waitForBoot;bootWait;constructor(e={}){let t=ft(e.apiToken);if(this.stateOnly=!e.client&&!t&&e.allowStateOnly===!0,!e.client&&!t&&!this.stateOnly)throw Error("Hetzner API token required. Set hetzner.apiToken in cloud.config.ts or HCLOUD_TOKEN / HETZNER_API_TOKEN.");this.client=e.client??new We({apiToken:t??""});let r=lr(void 0,{sshPrivateKeyPath:e.sshPrivateKeyPath,sshPublicKeyPath:e.sshPublicKeyPath,sshUser:e.sshUser,location:e.location});this.sshPrivateKeyPath=r.sshPrivateKeyPath,this.sshPublicKeyPath=r.sshPublicKeyPath,this.sshUser=r.sshUser,this.location=r.location,this.waitForBoot=e.waitForBoot??!0,this.bootWait={sshIntervalMs:e.bootWait?.sshIntervalMs??5000,sshTimeoutMs:e.bootWait?.sshTimeoutMs??300000,cloudInitIntervalMs:e.bootWait?.cloudInitIntervalMs??5000,cloudInitTimeoutMs:e.bootWait?.cloudInitTimeoutMs??600000}}async provisionComputeInfrastructure(e){let{config:t,environment:r}=e,n=t.project.slug,s=t.infrastructure?.compute;if(!s)throw Error("infrastructure.compute is required to provision Hetzner compute");if(t.cloud?.attachTo)return this.attachToComputeInfrastructure(t.cloud.attachTo,t,r);let i=T(t,r),o=`${n}-${r}-app`,a=s.runtime==="php"||!!s.php,c=Cs(s);if(c.dedicatedServices||c.appServers>1)return a?this.provisionFleet(e,c):this.provisionBunFleet(e,c);let l=j(n,r,"app"),u=t.sites||{},d=Z(s)?[]:this.collectUpstreamPorts(u),m=`${n}-${r}-app-fw`,{firewall:f}=await this.ensureFirewall(m,l,Yr({allowSsh:s.allowSsh!==!1,sitePorts:d,allowedPorts:[...s.firewall?.allowedPorts??[],...Vn(t.infrastructure?.sftp)]})),h=await this.ensureSshKey(n,r,l),x=await pe(i);if(x?.serverId){let k=await this.tryGetServer(x.serverId);if(k&&k.status!=="off")return this.outputsFromState(x,k)}let _=await this.findExistingServer(n,r,o);if(_&&_.status!=="off"){let k={provider:"hetzner",stackName:i,serverId:_.id,serverName:_.name,publicIp:_.public_net.ipv4?.ip,publicIpv6:ue(_.public_net.ipv6?.ip),deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser};return await ut(i,k),this.outputsFromState(k,_)}let y=s.proxy?.engine==="rpx"?le({proxy:s.proxy,config:ve(u,{proxy:s.proxy,slug:t.project.slug}),slug:t.project.slug,bunBin:s.runtime==="node"||s.runtime==="deno"?void 0:"/usr/local/bin/bun"}):void 0,S=Re(t,{environment:r}),R=s.bakedImage===!0,b=ee({runtime:S.runtime,runtimeVersion:S.runtimeVersion,systemPackages:s.systemPackages,database:t.infrastructure?.database,phpProvision:S.phpProvision,servicesProvision:S.servicesProvision,rpxProvision:y,baked:R,swapGb:s.swapGb}),I=de(b),C=dt(s.size),E=gt(t),{server:H,action:re}=await this.client.createServer({name:o,serverType:C,image:E,location:t.hetzner?.location||this.location,userData:I,labels:l,sshKeys:h?[h]:void 0,firewalls:[{firewall:f.id}]});await this.client.waitForAction(re.id);let W=await this.client.waitForServerRunning(H.id),N={provider:"hetzner",stackName:i,serverId:W.id,serverName:W.name,firewallId:f.id,publicIp:W.public_net.ipv4?.ip,publicIpv6:ue(W.public_net.ipv6?.ip),deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser};await ut(i,N);let q=W.public_net.ipv4?.ip;if(q&&this.waitForBoot)await this.waitForSshReady(q),await this.waitForCloudInit(q);return this.outputsFromState(N,W)}async provisionFleet(e,t){let{config:r,environment:n}=e,s=r.project.slug,i=r.infrastructure.compute,o=T(r,n),a=dt(i.size),c=gt(r),l=r.hetzner?.location||this.location,u=i.bakedImage===!0,d=await pe(o);if(d?.loadBalancerId){let M=(await this.client.listLoadBalancers().catch(()=>[])).find((D)=>D.id===d.loadBalancerId);if(M)return{appPublicIp:M.public_net?.ipv4?.ip,loadBalancerIp:M.public_net?.ipv4?.ip,servicesPrivateIp:d.servicesPrivateIp,deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser}}let m=await this.ensureSshKey(s,n,j(s,n,"app")),f=`${s}-${n}-net`,x=(await this.client.listNetworks().catch(()=>[])).find((L)=>L.name===f)??await this.client.createNetwork({name:f,labels:j(s,n,"app")}),{firewall:_}=await this.ensureFirewall(`${s}-${n}-app-fw`,j(s,n,"app"),Yr({allowSsh:!0,sitePorts:[],allowedPorts:i.firewall?.allowedPorts})),{firewall:y}=await this.ensureFirewall(`${s}-${n}-services-fw`,j(s,n,"services"),[{direction:"in",protocol:"tcp",port:"22",source_ips:["0.0.0.0/0","::/0"]},{direction:"in",protocol:"tcp",port:"3306",source_ips:[x.ip_range]},{direction:"in",protocol:"tcp",port:"5432",source_ips:[x.ip_range]},{direction:"in",protocol:"tcp",port:"6379",source_ips:[x.ip_range]},{direction:"in",protocol:"tcp",port:"7700",source_ips:[x.ip_range]}]),S=Fr(r),R=de(ee({runtime:"php",servicesProvision:S,baked:u,swapGb:i.swapGb})),b=await this.client.listServers().catch(()=>[]),I=[],C=b.find((L)=>te(L.labels,s,n,"services"));if(!C){let{server:L,action:M}=await this.client.createServer({name:`${s}-${n}-services`,serverType:dt(typeof i.servicesServer==="object"?i.servicesServer.size:i.size),image:c,location:l,userData:R,labels:j(s,n,"services"),sshKeys:m?[m]:void 0,firewalls:[{firewall:y.id}],networks:[x.id]});await this.client.waitForAction(M.id),C=await this.client.waitForServerRunning(L.id),I.push(L.id)}let E=C.id,H=C.private_net?.[0]?.ip??(await this.client.getServer(E)).private_net?.[0]?.ip,re=[...he(!0),...Se(!0),...Fe(i.sshKeys),...He(r.notifications)],W=Ct({versions:i.php?.versions,default:i.php?.default,extensions:i.php?.extensions,installNginx:!Z(i),optimizeForProduction:i.php?.optimizeForProduction,ini:i.php?.ini}),N=de(ee({runtime:"php",phpProvision:W,servicesProvision:re,baked:u,swapGb:i.swapGb})),q=b.filter((L)=>te(L.labels,s,n,"app")),k=q.map((L)=>L.id);if(q.length>t.appServers)for(let L of q.slice(t.appServers))await this.client.deleteServer(L.id).catch(()=>{}),k.splice(k.indexOf(L.id),1);for(let L=q.length;L<t.appServers;L++){let{server:M,action:D}=await this.client.createServer({name:`${s}-${n}-app-${L+1}`,serverType:a,image:c,location:l,userData:N,labels:j(s,n,"app"),sshKeys:m?[m]:void 0,firewalls:[{firewall:_.id}],networks:[x.id]});await this.client.waitForAction(D.id),k.push(M.id),I.push(M.id)}if(this.waitForBoot&&I.length>0)await Promise.all(I.map(async(L)=>{let D=(await this.client.waitForServerRunning(L)).public_net.ipv4?.ip;if(D)await this.waitForSshReady(D),await this.waitForCloudInit(D)}));let X=`${s}-${n}-lb`,oe,fe;if(t.loadBalancer){let M=(await this.client.listLoadBalancers().catch(()=>[])).find((D)=>D.name===X)??await this.client.createLoadBalancer({name:X,location:l,network:x.id,labels:j(s,n,"lb"),labelSelector:`ts-cloud/project=${s},ts-cloud/environment=${n},ts-cloud/role=app`,services:[{listenPort:80,destinationPort:80},{listenPort:443,destinationPort:443}]});fe=M.id,oe=M.public_net?.ipv4?.ip}let $e=oe??(await this.client.getServer(k[0]).catch(()=>{return}))?.public_net.ipv4?.ip,ne={provider:"hetzner",stackName:o,networkId:x.id,loadBalancerId:fe,servicesServerId:E,servicesPrivateIp:H,publicIp:$e,deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser};return await ut(o,ne),{appPublicIp:$e,loadBalancerIp:oe,servicesPrivateIp:H,deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser}}async provisionBunFleet(e,t){let{config:r,environment:n}=e,s=r.project.slug,i=r.infrastructure.compute,o=T(r,n),a=dt(i.size),c=gt(r),l=r.hetzner?.location||this.location,u=i.bakedImage===!0,d=r.sites||{},m=await pe(o);if(m?.lbServerId){let D=await this.tryGetServer(m.lbServerId);if(D&&D.status!=="off"){let F=i.proxy?.engine==="rpx"?i.proxy:{engine:"rpx"},z=await this.findComputeTargets({slug:s,environment:n,role:"app",stackName:o});return this.refreshLbRoutes(D,d,z.map((ae)=>({privateIp:ae.privateIp,publicIp:ae.publicIp})),F,s),{appPublicIp:D.public_net.ipv4?.ip??m.publicIp,loadBalancerIp:D.public_net.ipv4?.ip??m.publicIp,servicesPrivateIp:m.servicesPrivateIp,deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser}}}let f=await this.ensureSshKey(s,n,j(s,n,"app")),h=`${s}-${n}-net`,_=(await this.client.listNetworks().catch(()=>[])).find((D)=>D.name===h)??await this.client.createNetwork({name:h,labels:j(s,n,"app")}),y=this.collectUpstreamPorts(d),{firewall:S}=await this.ensureFirewall(`${s}-${n}-app-fw`,j(s,n,"app"),[{direction:"in",protocol:"tcp",port:"22",source_ips:["0.0.0.0/0","::/0"]},...y.map((D)=>({direction:"in",protocol:"tcp",port:String(D),source_ips:[_.ip_range],description:`ts-cloud port ${D} (private, LB-only)`}))]),{firewall:R}=await this.ensureFirewall(`${s}-${n}-lb-fw`,j(s,n,"lb"),Yr({allowSsh:!0,sitePorts:[],allowedPorts:i.firewall?.allowedPorts})),b=await this.client.listServers().catch(()=>[]),I=[],C,E,H=!!i.servicesServer||!!i.managedServices,re=t.dedicatedServices&&H;if(re){let{firewall:D}=await this.ensureFirewall(`${s}-${n}-services-fw`,j(s,n,"services"),[{direction:"in",protocol:"tcp",port:"22",source_ips:["0.0.0.0/0","::/0"]},{direction:"in",protocol:"tcp",port:"3306",source_ips:[_.ip_range]},{direction:"in",protocol:"tcp",port:"5432",source_ips:[_.ip_range]},{direction:"in",protocol:"tcp",port:"6379",source_ips:[_.ip_range]},{direction:"in",protocol:"tcp",port:"7700",source_ips:[_.ip_range]}]),F=Fr(r),z=de(ee({runtime:"bun",servicesProvision:F,baked:u,swapGb:i.swapGb})),ae=b.find((ge)=>te(ge.labels,s,n,"services"));if(!ae){let{server:ge,action:rt}=await this.client.createServer({name:`${s}-${n}-services`,serverType:dt(typeof i.servicesServer==="object"?i.servicesServer.size:i.size),image:c,location:l,userData:z,labels:j(s,n,"services"),sshKeys:f?[f]:void 0,firewalls:[{firewall:D.id}],networks:[_.id]});await this.client.waitForAction(rt.id),ae=await this.client.waitForServerRunning(ge.id),I.push(ge.id)}C=ae.id,E=ae.private_net?.[0]?.ip??(await this.client.getServer(C)).private_net?.[0]?.ip}let W=re?{...i,managedServices:void 0,backups:void 0}:i,N=Re({...r,infrastructure:{...r.infrastructure,compute:W}},{environment:n}),q=de(ee({runtime:N.runtime,runtimeVersion:N.runtimeVersion,systemPackages:i.systemPackages,database:r.infrastructure?.database,servicesProvision:N.servicesProvision,baked:u,swapGb:i.swapGb})),k=b.filter((D)=>te(D.labels,s,n,"app")),X=k.map((D)=>D.id);if(k.length>t.appServers)for(let D of k.slice(t.appServers))await this.client.deleteServer(D.id).catch(()=>{}),X.splice(X.indexOf(D.id),1);for(let D=k.length;D<t.appServers;D++){let{server:F,action:z}=await this.client.createServer({name:`${s}-${n}-app-${D+1}`,serverType:a,image:c,location:l,userData:q,labels:j(s,n,"app"),sshKeys:f?[f]:void 0,firewalls:[{firewall:S.id}],networks:[_.id]});await this.client.waitForAction(z.id),X.push(F.id),I.push(F.id)}if(this.waitForBoot&&I.length>0)await Promise.all(I.map(async(D)=>{let z=(await this.client.waitForServerRunning(D)).public_net.ipv4?.ip;if(z)await this.waitForSshReady(z),await this.waitForCloudInit(z)}));let oe=[];for(let D of X){let F=b.find((z)=>z.id===D);if(!F||!F.private_net?.[0]?.ip)F=await this.client.getServer(D).catch(()=>F);oe.push({privateIp:F?.private_net?.[0]?.ip,publicIp:F?.public_net.ipv4?.ip})}let fe=`${s}-${n}-lb`,$e,ne;if(t.loadBalancer){let D=b.find((z)=>te(z.labels,s,n,"lb")),F=i.proxy?.engine==="rpx"?i.proxy:{engine:"rpx"};if(!D){let z=le({proxy:F,config:Me(d,oe,{proxy:F,slug:s}),slug:s,bunBin:N.runtime==="node"||N.runtime==="deno"?void 0:"/usr/local/bin/bun"}),ae=de(ee({runtime:"bun",rpxProvision:z,baked:!1})),ge=typeof i.loadBalancer==="object"?i.loadBalancer.size:void 0,{server:rt,action:er}=await this.client.createServer({name:fe,serverType:dt(ge??"micro"),image:c,location:l,userData:ae,labels:j(s,n,"lb"),sshKeys:f?[f]:void 0,firewalls:[{firewall:R.id}],networks:[_.id]});if(await this.client.waitForAction(er.id),D=await this.client.waitForServerRunning(rt.id),this.waitForBoot){let nt=D.public_net.ipv4?.ip;if(nt)await this.waitForSshReady(nt),await this.waitForCloudInit(nt)}}else this.refreshLbRoutes(D,d,oe,F,s);$e=D.id,ne=D.public_net.ipv4?.ip}let L=ne??(await this.client.getServer(X[0]).catch(()=>{return}))?.public_net.ipv4?.ip,M={provider:"hetzner",stackName:o,networkId:_.id,lbServerId:$e,appServerIds:X,servicesServerId:C,servicesPrivateIp:E,publicIp:L,deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser};return await ut(o,M),{appPublicIp:L,loadBalancerIp:ne,servicesPrivateIp:E,deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser}}async destroyCompute(e){let{config:t,environment:r}=e,n=t.project.slug,s=T(t,r),i=await pe(s),o=[],a=`${n}-${r}-lb`,l=(await this.client.listLoadBalancers().catch(()=>[])).find((S)=>S.name===a);if(l)try{await this.client.deleteLoadBalancer(l.id),o.push(`load balancer ${a}`)}catch{}let u=await this.client.listServers().catch(()=>[]),d=new Set;for(let S of u)if(te(S.labels,n,r,"app")||te(S.labels,n,r,"services")||te(S.labels,n,r,"lb"))d.add(S.id);if(i?.serverId)d.add(i.serverId);if(i?.servicesServerId)d.add(i.servicesServerId);if(i?.lbServerId)d.add(i.lbServerId);for(let S of i?.appServerIds??[])d.add(S);for(let S of d)try{await this.client.deleteServer(S),o.push(`server ${S}`)}catch{}let m=await this.client.listFirewalls().catch(()=>[]);for(let S of[`${n}-${r}-app-fw`,`${n}-${r}-services-fw`,`${n}-${r}-lb-fw`]){let R=m.find((b)=>b.name===S);if(!R)continue;for(let b=0;b<12;b++)try{await this.client.deleteFirewall(R.id),o.push(`firewall ${S}`);break}catch{await this.sleep(3000)}}let f=`${n}-${r}-net`,x=(await this.client.listNetworks().catch(()=>[])).find((S)=>S.name===f);if(x)for(let S=0;S<12;S++)try{await this.client.deleteNetwork(x.id),o.push(`network ${f}`);break}catch{await this.sleep(3000)}let y=(await this.client.listSshKeys().catch(()=>[])).filter((S)=>Kr(S.labels,n,r));for(let S of y)try{await this.client.deleteSshKey(S.id),o.push(`ssh key ${S.name}`)}catch{}return await ut(s,{provider:"hetzner",stackName:s}).catch(()=>{}),{destroyed:o}}async getComputeOutputs(e){let t=T(e.config,e.environment),r=await pe(t);if(r?.serverId){let i=await this.tryGetServer(r.serverId);if(i)return this.outputsFromState(r,i)}if(r?.lbServerId){let o=(await this.tryGetServer(r.lbServerId))?.public_net.ipv4?.ip??r.publicIp;return{...this.outputsFromState(r),appPublicIp:o,loadBalancerIp:o}}if(r?.loadBalancerId||r?.servicesPrivateIp)return this.outputsFromState(r);let s=(await this.findComputeTargets({slug:e.config.project.slug,environment:e.environment,role:"app",stackName:T(e.config,e.environment)}))[0];return{deployStoragePath:"/var/ts-cloud/staging",appInstanceId:s?.id,appPublicIp:s?.publicIp,sshUser:this.sshUser}}async uploadRelease(e){let t=e.targets?.length?e.targets:await this.findComputeTargets({slug:e.config.project.slug,environment:e.environment,role:"app",stackName:T(e.config,e.environment)});if(t.length===0)throw Error("No Hetzner compute targets found for release upload");let s=`/var/ts-cloud/staging/${`${e.remoteKey.replace(/^releases\//,"").replace(/\.tar\.gz$/,"").replace(/\//g,"-")}-${Is()}.tar.gz`}`,i=await sc(e.localPath),o=Be,a=`${o}/${i}.tar.gz`;for(let c of t){if(!c.publicIp)throw Error(`Target ${c.id} has no public IP for SCP upload`);let l=["set -euo pipefail",`mkdir -p ${yt(o)} /var/ts-cloud/staging`,`test -s ${yt(a)}`,`cp -- ${yt(a)} ${yt(s)}`].join(`
278
278
  `);try{this.sshExec(c.publicIp,l);continue}catch{}let u=`${o}/.${i}-${Is()}.tmp`;try{this.scpToHost(c.publicIp,e.localPath,u),this.sshExec(c.publicIp,Lt(u,a,s))}catch(d){try{this.sshExec(c.publicIp,Ht(u))}catch{}throw d}}return{artifactRef:s}}async findComputeTargets(e){let t=e.role||"app",r=(i)=>({id:String(i.id),name:i.name,publicIp:i.public_net.ipv4?.ip,publicIpv6:ue(i.public_net.ipv6?.ip),privateIp:i.private_net?.[0]?.ip,status:i.status});if(t==="app"||t==="lb"||t==="services"){let i=await pe(e.stackName??`${e.slug}-${e.environment}`),o=[...new Set(t==="app"?[i?.serverId,...i?.appServerIds??[]]:t==="lb"?[i?.lbServerId]:[i?.servicesServerId])].filter((l)=>typeof l==="number");if(this.stateOnly){if(o.length>0&&i?.publicIp)return o.map((l)=>({id:String(l),name:l===i.serverId?i.serverName:void 0,publicIp:i.publicIp,publicIpv6:i.publicIpv6,status:"running"}));if(t!=="app")return[];throw Error(`Hetzner API token required because '${e.stackName??`${e.slug}-${e.environment}`}' has no complete local compute state pin.`)}let a=await this.client.listServers();if(o.length>0){let l=[];for(let u of o){let d=a.find((m)=>m.id===u)??await this.tryGetServer(u);if(u===i?.serverId&&i.serverName&&d?.name!==i.serverName)continue;if(d&&d.status!=="off")l.push(d)}if(l.length>0)return l.map(r)}let c=a.filter((l)=>te(l.labels,e.slug,e.environment,t));if(c.length>0)return c.map(r);if(t==="app"){let l=a.filter((u)=>u.status!=="off"&&u.labels?.[`${K}/managed-by`]==="ts-cloud"&&u.labels?.[`${K}/environment`]===e.environment&&u.labels?.[`${K}/role`]==="app");if(l.length===1)return l.map(r)}return[]}if(this.stateOnly)throw Error(`Hetzner API token required to discover '${t}' compute targets.`);let s=(await this.client.listServers()).filter((i)=>te(i.labels,e.slug,e.environment,t));if(s.length>0)return s.map(r);return[]}async attachToComputeInfrastructure(e,t,r){let n=await this.client.listServers(),s=(m)=>n.filter((f)=>f.status!=="off"&&te(f.labels,e,r,m)),i=s("app");if(i.length===0)throw Error(`Cannot attach '${t.project.slug}' to '${e}': no running Hetzner app server is labeled for ${e}/${r}. Deploy the owner project first.`);let o=s("lb")[0],a=s("services")[0],c=i[0],l=o??c,u=T(t,r),d={provider:"hetzner",stackName:u,serverId:c.id,serverName:c.name,appServerIds:i.map((m)=>m.id),lbServerId:o?.id,servicesServerId:a?.id,servicesPrivateIp:a?.private_net?.[0]?.ip,publicIp:l.public_net.ipv4?.ip,deployStoragePath:"/var/ts-cloud/staging",sshUser:this.sshUser};return await ut(u,d),this.outputsFromState(d)}async listReachableResources(){return(await this.client.listServers()).map((t)=>({name:t.name,labels:t.labels}))}async runRemoteDeploy(e){if(e.targets.length===0)return{success:!1,instanceCount:0,perInstance:[],error:"No targets provided"};let t=e.commands.join(`
279
279
  `),r=[];for(let s of e.targets){if(!s.publicIp){r.push({instanceId:s.id,status:"Failed",error:"Missing public IP"});continue}try{let i=this.sshExec(s.publicIp,t);Le(i),r.push({instanceId:s.id,status:"Success",output:i})}catch(i){r.push({instanceId:s.id,status:"Failed",error:i.message})}}let n=r.every((s)=>s.status==="Success");return{success:n,instanceCount:e.targets.length,perInstance:r,error:n?void 0:xe(r,"One or more SSH deploy commands failed")}}async ensureSshKey(e,t,r){if(!rc(this.sshPublicKeyPath))throw Error(`SSH public key not found at ${this.sshPublicKeyPath}. ts-cloud deploys to Hetzner over SSH and needs a public key to authorize on the server. Generate one (\`ssh-keygen -t ed25519\`) or set hetzner.sshPrivateKeyPath / HCLOUD_SSH_PUBLIC_KEY.`);let n=nc(this.sshPublicKeyPath,"utf8").trim(),s=ht(n),o=(await this.client.listSshKeys()).find((c)=>ht(c.public_key)===s);if(o)return o.id;return(await this.client.createSshKey({name:`${e}-${t}-deploy`,publicKey:n,labels:r})).id}async tryGetServer(e){try{return await this.client.getServer(e)}catch{return null}}refreshLbRoutes(e,t,r,n,s){let i=e.public_net.ipv4?.ip;if(!i){console.warn(`[ts-cloud] LB server '${e.name}' has no public IP — skipping the rpx route refresh; its routes may be stale.`);return}let o=r.filter((c)=>c.privateIp||c.publicIp);if(o.length===0){console.warn(`[ts-cloud] No routable app boxes found for LB '${e.name}' — leaving its current rpx routes untouched.`);return}let a=Me(t,o,{proxy:n,slug:s});this.sshExec(i,Ge({config:a,slug:s}).join(`
280
280
  `))}async findExistingServer(e,t,r){let n=await this.client.listServers(),s=n.find((o)=>te(o.labels,e,t,"app")||o.name===r);if(s)return s;let i=n.filter((o)=>o.status!=="off"&&o.labels?.[`${K}/managed-by`]==="ts-cloud"&&o.labels?.[`${K}/environment`]===t&&o.labels?.[`${K}/role`]==="app");if(i.length===1){let o=i[0],a=o.labels?.[`${K}/project`]??"unknown";return console.warn(`[ts-cloud] No server named '${r}' found; adopting existing ts-cloud app server '${o.name}' (project label '${a}') for project '${e}' — `+"updating it in place instead of provisioning a new server."),o}return}async ensureFirewall(e,t,r){let s=(await this.client.listFirewalls()).find((o)=>o.name===e);if(s)return await this.client.setFirewallRules(s.id,r),{firewall:s};let{firewall:i}=await this.client.createFirewall({name:e,labels:t,rules:r});return{firewall:i}}collectUpstreamPorts(e){let t=new Set;for(let r of Object.values(e))if(typeof r.port==="number")t.add(r.port);return[...t].filter((r)=>![80,443].includes(r))}async sleep(e){await new Promise((t)=>setTimeout(t,e))}async waitForSshReady(e){let{sshIntervalMs:t,sshTimeoutMs:r}=this.bootWait,n=Date.now(),s;while(Date.now()-n<r)try{Ds(`ssh ${this.sshBaseArgs(e,["-o","ConnectTimeout=5"]).map(yt).join(" ")} true`,{stdio:"pipe",maxBuffer:Wr});return}catch(i){s=i,await this.sleep(t)}throw Error(`Timed out waiting for SSH on ${e} after ${r}ms: ${s?.message??"unknown error"}`)}async waitForCloudInit(e){let{cloudInitIntervalMs:t,cloudInitTimeoutMs:r}=this.bootWait,n=Date.now();while(Date.now()-n<r){try{let s=this.sshExec(e,"if command -v cloud-init >/dev/null 2>&1; then cloud-init status --long 2>/dev/null || true; else echo 'status: done'; fi");if(/status:\s*error/.test(s))throw Error(`cloud-init reported an error on ${e}:
@@ -373,7 +373,7 @@ ${" ".repeat(t+10)}${r.remediation}`:n}).join(`
373
373
  `);try{await this.exec(a.publicIp,c);continue}catch{}let l=`${cn}/.${i}-${fi()}.tmp`;try{await this.transport.scp(a.publicIp,e.localPath,l),await this.exec(a.publicIp,Lt(l,o,s))}catch(u){throw await this.exec(a.publicIp,Ht(l)).catch(()=>{}),u}}return{artifactRef:s}}async runRemoteDeploy(e){if(e.targets.length===0)return{success:!1,instanceCount:0,perInstance:[],error:"No targets provided"};let t=e.commands.join(`
374
374
  `),r=[];for(let s of e.targets){if(!s.publicIp){r.push({instanceId:s.id,status:"Failed",error:"Missing address"});continue}try{let i=await this.exec(s.publicIp,t);Le(i),r.push({instanceId:s.id,status:"Success",output:i})}catch(i){r.push({instanceId:s.id,status:"Failed",error:i.message})}}let n=r.every((s)=>s.status==="Success");return{success:n,instanceCount:e.targets.length,perInstance:r,error:n?void 0:xe(r,"One or more SSH deploy commands failed")}}targetHost(){return this.host.host}ensureHostKey(e=this.host.host){return this.transport.ensureHostKey(e)}async preflight(e=this.host.host){let t=await this.transport.exec(e,sn,{sudo:!1}),r=on(t);return{facts:r,findings:an(r,{user:this.host.user,profile:this.settings.profile})}}async provisionComputeInfrastructure(e){let{config:t,environment:r}=e;for(let h of hi(t,this.settings))console.warn(`[ts-cloud] ${h}`);let n=this.host,s=T(t,r),i=await Ie(s,this.cwd),o=process.env[gi]==="1"&&i?.provider==="ssh"&&i.host===n.host&&i.bootstrapVersion===we,a=await this.ensureHostKey(n.host);if(a.pinnedNow)console.warn(`[ts-cloud] pinned ${n.host} host key ${a.fingerprint}`);await this.waitForSshReady(n.host);let{facts:c,findings:l}=await this.preflight(n.host);if(gr(l))throw Error(`Preflight failed for ${n.host}; refusing to bootstrap it.
375
375
  ${Zr(l)}`);for(let h of l)console.warn(`[ts-cloud] ${h.severity}: ${h.message}`);if(c.cloudInitPresent&&/running/i.test(c.cloudInitStatus??""))await this.waitForCloudInit(n.host);let u=c.lanIp?.trim()||($c(n.host)!==0?n.host:void 0),d=i?.provider==="ssh"?i.bootstrappedAt:void 0;if(!o){let h=pr({config:t,environment:r,profile:this.settings.profile,facts:c,sudoUser:n.user!=="root"?n.user:void 0,lan:this.settings.lan,lanIp:u});Le(await this.exec(n.host,h)),d=new Date().toISOString()}let m={provider:"ssh",stackName:s,host:n.host,sshUser:n.user,sshPort:n.port,hostKeyFingerprint:a.fingerprint||void 0,publicIp:this.settings.publicIp==="auto"?void 0:this.settings.publicIp,lanIp:c.lanIp||void 0,deployStoragePath:Tt,profile:this.settings.profile,bootstrapVersion:we,bootstrappedAt:d};await Ye(s,m,this.cwd);let f=await this.getComputeOutputs();if(Z(t.infrastructure?.compute)&&yr(this.settings.lan)==="local-ca")f.lanCaCertPath=Pt(),console.warn(`[ts-cloud] LAN certificate authority on ${n.host} at ${f.lanCaCertPath}. Fetch it with: scp ${n.user}@${n.host}:${f.lanCaCertPath} ./rpx-root-ca.crt`);return f}async destroyCompute(e){let t=T(e.config,e.environment),r=this.host;return console.warn(`[ts-cloud] forgot ${r.host} for ${t}; nothing was removed on the host (stop the systemd units and remove /var/ts-cloud and /etc/rpx by hand if you want it clean).`),await Ye(t,{provider:"ssh",stackName:t,host:r.host,sshUser:r.user,sshPort:r.port},this.cwd).catch(()=>{}),{destroyed:[`local state for ${t}`]}}async sleep(e){await new Promise((t)=>setTimeout(t,e))}async waitForSshReady(e){let{sshIntervalMs:t,sshTimeoutMs:r}=this.bootWait,n=Date.now();do{if(await this.transport.probe(e))return;await this.sleep(t)}while(Date.now()-n<r);throw Error(`Timed out waiting for SSH on ${e} after ${r}ms`)}async waitForCloudInit(e){let{cloudInitIntervalMs:t,cloudInitTimeoutMs:r}=this.bootWait,n=Date.now();while(Date.now()-n<r){try{let s=await this.transport.exec(e,"if command -v cloud-init >/dev/null 2>&1; then cloud-init status --long 2>/dev/null || true; else echo 'status: done'; fi",{sudo:!1});if(/status:\s*error/.test(s))throw Error(`cloud-init reported an error on ${e}:
376
- ${s}`);if(/status:\s*(?:done|degraded)/.test(s))return}catch(s){if(s instanceof Error&&/cloud-init reported an error/.test(s.message))throw s}await this.sleep(t)}throw Error(`Timed out waiting for cloud-init to finish on ${e} after ${r}ms`)}}function O(e){if(Gr())return new Mt;switch(e.provider??F(e.config)){case"aws":return new Et({region:e.config.project.region});case"hetzner":return new _e({apiToken:e.config.hetzner?.apiToken,allowStateOnly:Boolean(e.config.cloud?.attachTo),sshPrivateKeyPath:e.config.hetzner?.sshPrivateKeyPath,sshPublicKeyPath:e.config.hetzner?.sshPublicKeyPath,sshUser:e.config.hetzner?.sshUser,location:e.config.hetzner?.location});case"ssh":return new fr({hosts:e.config.ssh?.hosts,hostKey:e.config.ssh?.hostKey,sudo:e.config.ssh?.sudo,profile:e.config.ssh?.profile,publicIp:e.config.ssh?.publicIp,lan:e.config.ssh?.lan});default:throw Error(`Unknown cloud provider: ${e.provider??F(e.config)}`)}}class Si{drivers=new Map;getDriver(e,t){let r=t??F(e),n=`${r}:${e.project.slug}:${e.project.region||"default"}`,s=this.drivers.get(n);if(s)return s;let i=O({config:e,provider:r});return this.drivers.set(n,i),i}}var im=new Si;var yi="/var/lib/ts-cloud/bootstrap.sh",Zi={"raspberry-pi-os":"bootfs",ubuntu:"system-boot"},xc=/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/,Rc=/^[a-z_][a-z0-9_-]{0,31}$/,Ec=/^[A-Z]{2}$/;function se(e){return JSON.stringify(e)}function Oc(e){if(!xc.test(e.hostname))throw Error(`Invalid hostname '${e.hostname}': use lowercase letters, digits and hyphens.`);if(!Rc.test(e.user))throw Error(`Invalid user '${e.user}': use a lowercase Unix user name.`);let t=e.publicKey.trim();if(!t||t.includes(`
376
+ ${s}`);if(/status:\s*(?:done|degraded)/.test(s))return}catch(s){if(s instanceof Error&&/cloud-init reported an error/.test(s.message))throw s}await this.sleep(t)}throw Error(`Timed out waiting for cloud-init to finish on ${e} after ${r}ms`)}}function O(e){if(Gr())return new Mt;switch(e.provider??B(e.config)){case"aws":return new Et({region:e.config.project.region});case"hetzner":return new _e({apiToken:e.config.hetzner?.apiToken,allowStateOnly:Boolean(e.config.cloud?.attachTo),sshPrivateKeyPath:e.config.hetzner?.sshPrivateKeyPath,sshPublicKeyPath:e.config.hetzner?.sshPublicKeyPath,sshUser:e.config.hetzner?.sshUser,location:e.config.hetzner?.location});case"ssh":return new fr({hosts:e.config.ssh?.hosts,hostKey:e.config.ssh?.hostKey,sudo:e.config.ssh?.sudo,profile:e.config.ssh?.profile,publicIp:e.config.ssh?.publicIp,lan:e.config.ssh?.lan});case"fly":throw Error("Fly.io has no box to provision or reach over SSH: deploy it with deployToFly() from @stacksjs/ts-cloud.");default:throw Error(`Unknown cloud provider: ${e.provider??B(e.config)}`)}}class Si{drivers=new Map;getDriver(e,t){let r=t??B(e),n=`${r}:${e.project.slug}:${e.project.region||"default"}`,s=this.drivers.get(n);if(s)return s;let i=O({config:e,provider:r});return this.drivers.set(n,i),i}}var im=new Si;var yi="/var/lib/ts-cloud/bootstrap.sh",Zi={"raspberry-pi-os":"bootfs",ubuntu:"system-boot"},xc=/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/,Rc=/^[a-z_][a-z0-9_-]{0,31}$/,Ec=/^[A-Z]{2}$/;function se(e){return JSON.stringify(e)}function Oc(e){if(!xc.test(e.hostname))throw Error(`Invalid hostname '${e.hostname}': use lowercase letters, digits and hyphens.`);if(!Rc.test(e.user))throw Error(`Invalid user '${e.user}': use a lowercase Unix user name.`);let t=e.publicKey.trim();if(!t||t.includes(`
377
377
  `)||!/^(?:ssh|ecdsa|sk)-/.test(t))throw Error("publicKey must be one public key line, e.g. the contents of ~/.ssh/id_ed25519.pub.");if(e.wifi){if(!e.wifi.ssid)throw Error("wifi.ssid is required.");if(!e.wifi.passphrase)throw Error("wifi.passphrase is required.");if(!Ec.test(e.wifi.country))throw Error(`wifi.country must be a two-letter regulatory domain such as DE or US, not '${e.wifi.country}'.`)}}function Dc(e,t){let r=[`hostname: ${se(e.hostname)}`,"manage_etc_hosts: true","users:",` - name: ${se(e.user)}`," ssh_authorized_keys:",` - ${se(e.publicKey.trim())}`,` sudo: ${se("ALL=(ALL) NOPASSWD:ALL")}`," shell: /bin/bash"," lock_passwd: true","ssh_pwauth: false"];if(e.timezone)r.push(`timezone: ${se(e.timezone)}`);if(e.locale)r.push(`locale: ${se(e.locale)}`);if(r.push("package_update: true"),t==="raspberry-pi-os")r.push("rpi:"," enable_ssh: true"," interfaces:"," spi: false"," i2c: false"," onewire: false"," remote_gpio: false"," serial:"," console: false"," hardware: false");return`${r.join(`
378
378
  `)}
379
379
  `}function Ic(e){let t=["version: 2","ethernets:"," eth0:"," match:",` name: ${se("e*")}`," dhcp4: true"," dhcp6: true"," optional: true"];if(e.wifi)t.push("wifis:"," wlan0:"," dhcp4: true"," dhcp6: true"," optional: true",` regulatory-domain: ${se(e.wifi.country)}`," access-points:",` ${se(e.wifi.ssid)}:`,` password: ${se(e.wifi.passphrase)}`);return`${t.join(`
@@ -526,7 +526,7 @@ console.log(JSON.stringify({
526
526
  `.replace(/%/g,"\\%");c.push(`cat > ${_} <<'TS_CLOUD_CRON_EOF'`,R.replace(/\n$/,""),"TS_CLOUD_CRON_EOF",`chmod 644 ${_}`)}else c.push(`rm -f ${_}`);return c}function Sn(e){return!!(e.queues?.length||e.daemons?.length||e.scheduler)}function yn(e){if(e.ssl?.provider)return e.ssl.provider;return e.domain?"letsencrypt":"none"}var Mi={cloudflare:{pkg:"python3-certbot-dns-cloudflare",plugin:"dns-cloudflare"},route53:{pkg:"python3-certbot-dns-route53",plugin:"dns-route53"},digitalocean:{pkg:"python3-certbot-dns-digitalocean",plugin:"dns-digitalocean"},google:{pkg:"python3-certbot-dns-google",plugin:"dns-google"}};function Fi(e){return`/etc/letsencrypt/ts-cloud-${e}.ini`}function gl(e){let t=e?Mi[e.provider]:void 0,r=["certbot","python3-certbot-nginx",...t?[t.pkg]:[]].join(" ");return["export DEBIAN_FRONTEND=noninteractive",t?`apt-get update -y && apt-get install -y ${r}`:`command -v certbot >/dev/null 2>&1 || { apt-get update -y && apt-get install -y ${r}; }`,"mkdir -p /etc/letsencrypt/renewal-hooks/deploy","cat > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh <<'TS_CLOUD_HOOK_EOF'","#!/bin/sh","systemctl reload ts-cloud-nginx 2>/dev/null || systemctl reload nginx 2>/dev/null || true","TS_CLOUD_HOOK_EOF","chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh","systemctl enable certbot.timer 2>/dev/null || true","systemctl start certbot.timer 2>/dev/null || true"]}function hl(e){if(!e.credentials||Object.keys(e.credentials).length===0)return[];let t=Fi(e.provider),r=Object.entries(e.credentials).map(([n,s])=>`${n} = ${s}`).join(`
527
527
  `);return[`cat > ${t} <<'TS_CLOUD_DNSCREDS_EOF'`,r,"TS_CLOUD_DNSCREDS_EOF",`chmod 600 ${t}`]}function Sl(e){let t=e.dns,r=e.wildcard?[`*.${e.domain}`,e.domain]:[e.domain,...e.aliases||[]].filter(Boolean),n=["certbot","--non-interactive","--agree-tos","--keep-until-expiring"];if(t){let s=Mi[t.provider].plugin;if(n.push(`--${s}`),t.provider!=="route53"&&t.credentials)n.push(`--${s}-credentials ${Fi(t.provider)}`);if(typeof t.propagationSeconds==="number"&&t.provider!=="route53")n.push(`--${s}-propagation-seconds ${t.propagationSeconds}`);n.push("certonly")}else n.push("--nginx",`--nginx-ctl ${ye}`,e.redirect===!1?"--no-redirect":"--redirect");if(e.email)n.push(`-m ${e.email}`);else n.push("--register-unsafely-without-email");for(let s of r)n.push(`-d ${s}`);return[n.join(" ")]}function vn(e){if(yn(e)!=="letsencrypt"||!e.domain)return[];let t=e.ssl,r=t?.dns,n=t?.wildcard===!0;if(n&&!r)return[];return[...gl(r),...r?hl(r):[],...Sl({domain:e.domain,aliases:e.aliases,email:t?.email,wildcard:n,dns:r})]}function zi(e){let{repository:t,releaseDir:r,commit:n}=e,s=t.branch||"main",i=t.url,o=[`rm -rf ${r}`,`mkdir -p ${r}`];if(t.strategy==="tag")if(t.tag)o.push(`git clone -q --depth 1 --branch ${ie(t.tag)} ${ie(i)} ${r}`),o.push(`printf '%s' ${ie(t.tag)} > ${r}/.ts-cloud-tag`);else{let a=t.tagPattern||"v*";o.push(`TS_CLOUD_TAG="$(git ls-remote --tags --refs --sort=-v:refname ${ie(i)} ${ie(`refs/tags/${a}`)} | head -n1 | sed 's#.*refs/tags/##')"`,`test -n "$TS_CLOUD_TAG" || { echo "no tags matching" ${ie(a)} "found in" ${ie(i)} >&2; exit 1; }`,`git clone -q --depth 1 --branch "$TS_CLOUD_TAG" ${ie(i)} ${r}`,`printf '%s' "$TS_CLOUD_TAG" > ${r}/.ts-cloud-tag`)}else if(n)o.push(`git -C ${r} init -q`,`git -C ${r} remote add origin ${ie(i)}`,`git -C ${r} fetch -q --depth 1 origin ${ie(n)}`,`git -C ${r} checkout -q FETCH_HEAD`);else o.push(`git clone -q --depth 1 --branch ${ie(s)} ${ie(i)} ${r}`);return o.push(`git -C ${r} rev-parse HEAD > ${r}/.ts-cloud-sha`),o}function ie(e){return`'${e.split("'").join("'\\''")}'`}var qt="$CREATE_RELEASE",Xt="$ACTIVATE_RELEASE",Gi="$RESTART_QUEUES",Bi="composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev",yl=["php artisan migrate --force","php artisan config:cache","php artisan route:cache","php artisan view:cache","php artisan event:cache","php artisan storage:link"];function vl(e){switch(e){case"laravel":case"statamic":return[qt,Bi,...yl,Xt,Gi];case"php":case"wordpress":return[qt,`${Bi} || true`,Xt];case"static":case"spa":return[qt,Xt]}}function bl(e,t){return e.replace(/^php\s+/,`${t} `).replace(/(\s)php\s+artisan\s+/g,`$1${t} artisan `)}function _l(e,t){let r=Ot(t);return[`cat > ${e} <<'TS_CLOUD_ENV_EOF'`,r,"TS_CLOUD_ENV_EOF",`chmod 600 ${e}`]}function ji(e,t,r){return[`cat > ${e} <<'${r}'`,t.replace(/\n$/,""),r,`chmod 600 ${e}`]}function wl(e,t){if(!t)return[];let r=[];if(t.composerAuth){let n=typeof t.composerAuth==="string"?t.composerAuth:JSON.stringify(t.composerAuth,null,2);r.push(...ji(`${e}/auth.json`,n,"TS_CLOUD_AUTHJSON_EOF"))}if(t.npmrc)r.push(...ji(`${e}/.npmrc`,t.npmrc,"TS_CLOUD_NPMRC_EOF"));return r}function Vi(e){let t=e.healthCheck?.path;if(!t||!e.domain)return[];let n=`http://127.0.0.1${t.startsWith("/")?t:`/${t}`}`;return[`echo "[ts-cloud] health check ${n} (Host: ${e.domain})"`,"TS_CLOUD_HC_OK=0",`for i in $(seq 1 10); do TS_CLOUD_HC=$(curl -s -o /dev/null -w "%{http_code}" -H "Host: ${e.domain}" -m 10 "${n}" 2>/dev/null || echo 000); case "$TS_CLOUD_HC" in 2*|3*) TS_CLOUD_HC_OK=1; break ;; esac; sleep 3; done`,'[ "$TS_CLOUD_HC_OK" = 1 ] && echo "health check passed ($TS_CLOUD_HC)" || { echo "health check FAILED (last=$TS_CLOUD_HC)" >&2; exit 1; }']}function qi(e){let{siteName:t,site:r,releaseId:n,commit:s}=e;if(!r.repository?.url)throw Error(`Site '${t}' is a PHP/git site but has no repository.url to clone`);let i=e.appBase??`/var/www/${t}`,o="php",a=Vt(i,n),c=r.sharedPaths??$o,l=[...c,...Dt(r.env,c)],u=r.keepReleases??Ii,d=r.deployScript?.length?r.deployScript:vl(r.type??"laravel"),m=["set -euo pipefail",'export HOME="${HOME:-/root}"','export COMPOSER_HOME="${COMPOSER_HOME:-/root/.composer}"',"export COMPOSER_ALLOW_SUPERUSER=1",St()];if(m.push(...Uo(i,{releaseId:n,commit:s,branch:r.repository.branch,keepLogs:u})),m.push(...Pi(a,l)),r.env&&Object.keys(r.env).length>0)m.push(..._l(`${a.shared}/.env`,r.env));for(let f of d){let h=f.trim();if(h===qt)m.push(...zi({repository:r.repository,releaseDir:a.release,commit:s})),m.push(...Mo(a,l)),m.push(`cd ${a.release}`),m.push(...wl(a.release,r.credentials)),m.push(`chown -R www-data:www-data ${a.shared}/storage 2>/dev/null || true`,`[ -d ${a.release}/bootstrap/cache ] && chown -R www-data:www-data ${a.release}/bootstrap/cache 2>/dev/null || true`,`chmod -R ug+rwX ${a.shared}/storage 2>/dev/null || true`);else if(h===Xt)m.push(...pn(a)),m.push(...mn(a,u)),m.push(`(cd ${U} && pantry restart php-fpm) 2>/dev/null || true`);else if(h===Gi)m.push(`${o} artisan queue:restart || true`);else m.push(bl(f,o))}return m}var Yt={mysql:{port:3306,binaries:["mysqld","mysql"]},mariadb:{port:3306,binaries:["mariadbd","mariadb","mysqld"]},postgres:{port:5432,binaries:["postgres","psql"]},redis:{port:6379,binaries:["redis-server","redis-cli"]},memcached:{port:11211,binaries:["memcached"]},meilisearch:{port:7700,binaries:["meilisearch"]},vitess:{port:15306,binaries:["vtgate"]}},$t="ts-cloud-service:";function Tl(e){return e===!0||typeof e==="object"&&e!=null}function so(e){if(!e)return[];return Object.keys(Yt).filter((t)=>Tl(e[t]))}function io(e){if(e.length===0)return[];return[St(),"ts_cloud_port_open() {",' if command -v ss >/dev/null 2>&1; then ss -ltn 2>/dev/null | grep -qE "[:.]$1[[:space:]]" && return 0; fi',' if command -v netstat >/dev/null 2>&1; then netstat -ltn 2>/dev/null | grep -qE "[:.]$1[[:space:]]" && return 0; fi'," (exec 3<>/dev/tcp/127.0.0.1/$1) 2>/dev/null && { exec 3<&- 3>&-; return 0; }"," return 1","}","ts_cloud_probe() {",' TS_CLOUD_SVC="$1"; TS_CLOUD_PORT="$2"; shift 2',' for TS_CLOUD_BIN in "$@"; do',` if command -v "$TS_CLOUD_BIN" >/dev/null 2>&1; then echo "${$t}$TS_CLOUD_SVC:present"; return 0; fi`," done",` if ts_cloud_port_open "$TS_CLOUD_PORT"; then echo "${$t}$TS_CLOUD_SVC:present"; return 0; fi`,` echo "${$t}$TS_CLOUD_SVC:missing"`," return 0","}",...e.filter((t)=>Yt[t]).map((t)=>`ts_cloud_probe ${t} ${Yt[t].port} ${Yt[t].binaries.join(" ")}`),"exit 0"]}function oo(e,t){if(!e)return[];let r=new Map;for(let n of e.split(`
528
528
  `)){let s=n.indexOf($t);if(s===-1)continue;let[i,o]=n.slice(s+$t.length).trim().split(":");if(i)r.set(i,o)}return t.filter((n)=>r.get(n)==="missing")}function ao(e,t,r){let n=r?` on ${r}`:"",s=e.map((o)=>`managedServices.${o}`).join(", "),i=e.length===1?`no ${e[0]}`:`none of ${e.join(", ")}`;return`This project declares ${s}, but '${t}' has ${i}${n}. Attach mode does not provision services on the owner's box, so nothing will install ${e.length===1?"it":"them"}. Point the app at an external service, or ask the owner of '${t}' to add ${e.length===1?"it":"them"} to their own config and re-provision.`}var co="/var/lib/pantry/php-fpm/pool.d",$l=9100,Cl=400;function Jt(e){return(e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")||"site").slice(0,28)}function lo(e){return`web_${Jt(e)}`}function Pl(e){let t=Jt(e),r=0;for(let n=0;n<t.length;n++)r=r*31+t.charCodeAt(n)>>>0;return $l+r%Cl}function bn(e){return`127.0.0.1:${Pl(e)}`}function xl(e){let t=Jt(e.siteName),r=lo(e.siteName),n=e.maxChildren??10;return[`; ts-cloud-managed pool for ${e.siteName} (site isolation)`,`[${t}]`,`user = ${r}`,`group = ${r}`,`listen = ${bn(e.siteName)}`,"pm = dynamic",`pm.max_children = ${n}`,"pm.start_servers = 2","pm.min_spare_servers = 1","pm.max_spare_servers = 3","pm.max_requests = 500","catch_workers_output = yes",`php_admin_value[open_basedir] = ${e.appBase}:/tmp`,""].join(`
529
- `)}function uo(e){let t=lo(e.siteName),r=Jt(e.siteName),n=`${co}/${r}.conf`;return[`getent group ${t} >/dev/null || groupadd --system ${t}`,`id -u ${t} >/dev/null 2>&1 || useradd --system --no-create-home --shell /usr/sbin/nologin -g ${t} ${t}`,`usermod -aG ${t} www-data 2>/dev/null || true`,`chown -R ${t}:${t} ${e.appBase} || true`,`chmod -R g+rX ${e.appBase} || true`,`chmod 600 ${e.appBase}/shared/.env 2>/dev/null || true`,`mkdir -p ${co}`,`cat > ${n} <<'TS_CLOUD_FPMPOOL_EOF'`,xl(e).replace(/\n$/,""),"TS_CLOUD_FPMPOOL_EOF",`(cd ${U} && pantry restart php-fpm) 2>/dev/null || true`]}var Zt={info:()=>{},warn:()=>{},error:()=>{},step:()=>{},success:()=>{}};function Dl(e){return`'${e.replace(/'/g,`'"'"'`)}'`}async function mo(e,t,r=Zt){let{config:n,environment:s,siteName:i,site:o,slug:a,sha:c,runtime:l,localTarballPath:u}=t,d=`releases/${i}/${c}.tar.gz`,m=T(n,s),f=await e.getComputeOutputs({config:n,environment:s}),h=await e.findComputeTargets({slug:a,environment:s,role:"app",stackName:m});if(h.length===0)return{success:!1,error:e.name==="aws"?`Stack '${m}' has no EC2 instances tagged Project=${a} Environment=${s} Role=app, and storage/cloud/state/${m}.json pins no live instance. For a shared box, record its instanceId there.`:`No Hetzner servers labeled ts-cloud/project=${a} ts-cloud/environment=${s} ts-cloud/role=app, and storage/cloud/state/${m}.json pins no live server. For a shared box, record its serverId there.`};let x=f.servicesPrivateIp?Ps(f.servicesPrivateIp,w(n)):Hc(w(n)),_=ca(ki(n,{environment:s})),y=n.infrastructure?.compute,S=Or(y?.hostCleanup),R=Z(y)?{HOST:"127.0.0.1"}:{},b=s==="production"?{APP_ENV:"production",NODE_ENV:"production"}:{},I={...x,..._,...R,...o.env||{},...b},C=Pr(I,o.sharedPaths??[]);if(C.warning)r.warn(`Site '${i}': ${C.warning}`);else if(C.path)r.info(`Site '${i}': sharing '${C.path}' so the SQLite database survives deploys.`);if(Ei(o)){let L=o.phpVersion??y?.php?.default??y?.php?.versions?.[0],M=J(a,i),D=Object.keys(x).length>0?{...o,env:I}:o,z=qi({siteName:i,site:D,releaseId:c,appBase:M,defaultPhpVersion:L}),B=!Z(y),ge=yn(o)==="custom"&&o.ssl?.certPath&&o.ssl?.keyPath?{certPath:o.ssl.certPath,keyPath:o.ssl.keyPath}:void 0,rt=o.isolation?uo({siteName:i,appBase:M}):[],er=B?sr({siteName:i,domain:o.domain||i,aliases:o.aliases,type:o.type,appDir:`${M}/current`,webDirectory:o.webDirectory,phpVersion:L,fastcgiPass:o.isolation?bn(i):void 0,redirects:o.redirects,ssl:ge,auth:o.auth&&o.auth.enabled!==!1&&o.auth.password?{username:o.auth.username||"admin",password:o.auth.password,realm:o.auth.realm}:void 0,serverSnippet:nr(o.nginx,y?.nginxTemplates),clientMaxBodySize:o.nginx?.clientMaxBodySize,hsts:o.ssl?.hsts,tlsProtocols:o.ssl?.tlsProtocols,security:o.security}):[],nt=B?vn(o):[],Eo=Sn(o)?hn({slug:a,siteName:i,site:o,phpVersion:L,appBase:M}):[],Do=B?Vi(o):[];r.step(`Deploying PHP site '${i}' to ${h.length} target(s)...`);let Ce=await e.runRemoteDeploy({targets:h,commands:[...Zs(M,a),...z,...rt,...er,...nt,...Eo,...Do,...S],comment:`ts-cloud deploy ${a}/${i}@${c}`,tags:{Project:a,Environment:s,Role:"app"}}),xn=In(n.notifications,o.notifications);if(!Ce.success)return await po(e,h,S,r),await or(xn,"deploy-failed",`❌ Deploy of ${a}/${i}@${c} failed: ${Ce.error||"unknown error"}`),{success:!1,error:Ce.error||"Remote PHP deploy failed",instanceCount:Ce.instanceCount,perInstance:Ce.perInstance};let Io=o.domain?` → https://${o.domain}`:"";return await or(xn,"deploy",`✅ Deployed ${a}/${i}@${c}${Io}`),{success:!0,instanceCount:Ce.instanceCount,perInstance:Ce.perInstance}}if(!u)return{success:!1,error:`Site '${i}' requires a release tarball but none was provided`};let E=await e.uploadRelease({config:n,environment:s,localPath:u,remoteKey:d,targets:h}),H=mt(a,i,c),re=e.name==="aws"?kr(f.deployBucketName,d,n.project.region||"us-east-1",H):Mr(E.artifactRef,H),W=P(o),N=J(a,i),q=W==="server-static"?Ur({siteName:i,slug:a,appDir:N,artifactFetch:re,releaseId:c,preStartCommands:o.preStart}):Nr({siteName:i,slug:a,appDir:N,artifactFetch:re,releaseId:c,execStart:Hr(o.start,l,`${N.replace(/\/+$/,"")}/releases/%i`),envEntries:I,port:o.port,preStartCommands:o.preStart,sharedPaths:o.sharedPaths,zeroDowntime:o.zeroDowntime!==!1,healthCheckPath:o.healthCheck?.path,liveness:o.liveness,memoryHigh:o.memoryHigh,memoryMax:o.memoryMax,cpuWeight:o.cpuWeight,ioWeight:o.ioWeight,tasksMax:o.tasksMax,stopTimeout:o.stopTimeout}),k=W==="server-static"&&!Z(y)&&!!o.domain,X=k?sr({siteName:i,domain:o.domain,aliases:o.aliases,type:o.type==="spa"?"spa":"static",appDir:`${N}/current`,webDirectory:"",redirects:o.redirects,auth:o.auth&&o.auth.enabled!==!1&&o.auth.password?{username:o.auth.username||"admin",password:o.auth.password,realm:o.auth.realm}:void 0,serverSnippet:nr(o.nginx,y?.nginxTemplates),clientMaxBodySize:o.nginx?.clientMaxBodySize,hsts:o.ssl?.hsts,tlsProtocols:o.ssl?.tlsProtocols,security:o.security}):[],oe=k?vn(o):[],fe=Sn(o)?hn({slug:a,siteName:i,site:o,appBase:N}):[],$e=[...Uo(N,{releaseId:c,commit:c,branch:o.repository?.branch??"main"}),...Zs(N,a),...q,...X,...oe,...fe,...S];r.step(`Deploying to ${h.length} target(s)...`);let ne=await e.runRemoteDeploy({targets:h,commands:$e,comment:`ts-cloud deploy ${a}/${i}@${c}`,tags:{Project:a,Environment:s,Role:"app"}});if(!ne.success)return await po(e,h,S,r),{success:!1,error:ne.error||"Remote deploy failed",instanceCount:ne.instanceCount,perInstance:ne.perInstance};return{success:!0,instanceCount:ne.instanceCount,perInstance:ne.perInstance}}async function po(e,t,r,n){if(r.length===0)return;try{let s=await e.runRemoteDeploy({targets:t,commands:r,comment:"ts-cloud host cleanup after failed deploy"});if(!s.success)n.warn(`Host cleanup after the failed deploy did not complete: ${s.error||"unknown error"}`)}catch(s){n.warn(`Host cleanup after the failed deploy did not run: ${s.message}`)}}function Qt(e){return`'${e.replaceAll("'","'\\''")}'`}function Il(e,t,r=!1,n=[]){let s=`${e}-dashboard-`,i=t.map((a)=>`${e}-${a}.service`),o=r?[`export TS_CLOUD_DASHBOARD_KEEP_DOMAINS=${Qt(JSON.stringify(n))}`,"rm -f /run/ts-cloud-dashboard-routes-changed",`/usr/local/bin/bun --bun -e ${Qt(`
529
+ `)}function uo(e){let t=lo(e.siteName),r=Jt(e.siteName),n=`${co}/${r}.conf`;return[`getent group ${t} >/dev/null || groupadd --system ${t}`,`id -u ${t} >/dev/null 2>&1 || useradd --system --no-create-home --shell /usr/sbin/nologin -g ${t} ${t}`,`usermod -aG ${t} www-data 2>/dev/null || true`,`chown -R ${t}:${t} ${e.appBase} || true`,`chmod -R g+rX ${e.appBase} || true`,`chmod 600 ${e.appBase}/shared/.env 2>/dev/null || true`,`mkdir -p ${co}`,`cat > ${n} <<'TS_CLOUD_FPMPOOL_EOF'`,xl(e).replace(/\n$/,""),"TS_CLOUD_FPMPOOL_EOF",`(cd ${U} && pantry restart php-fpm) 2>/dev/null || true`]}var Zt={info:()=>{},warn:()=>{},error:()=>{},step:()=>{},success:()=>{}};function Dl(e){return`'${e.replace(/'/g,`'"'"'`)}'`}async function mo(e,t,r=Zt){let{config:n,environment:s,siteName:i,site:o,slug:a,sha:c,runtime:l,localTarballPath:u}=t,d=`releases/${i}/${c}.tar.gz`,m=T(n,s),f=await e.getComputeOutputs({config:n,environment:s}),h=await e.findComputeTargets({slug:a,environment:s,role:"app",stackName:m});if(h.length===0)return{success:!1,error:e.name==="aws"?`Stack '${m}' has no EC2 instances tagged Project=${a} Environment=${s} Role=app, and storage/cloud/state/${m}.json pins no live instance. For a shared box, record its instanceId there.`:`No Hetzner servers labeled ts-cloud/project=${a} ts-cloud/environment=${s} ts-cloud/role=app, and storage/cloud/state/${m}.json pins no live server. For a shared box, record its serverId there.`};let x=f.servicesPrivateIp?Ps(f.servicesPrivateIp,w(n)):Hc(w(n)),_=ca(ki(n,{environment:s})),y=n.infrastructure?.compute,S=Or(y?.hostCleanup),R=Z(y)?{HOST:"127.0.0.1"}:{},b=s==="production"?{APP_ENV:"production",NODE_ENV:"production"}:{},I={...x,..._,...R,...o.env||{},...b},C=Pr(I,o.sharedPaths??[]);if(C.warning)r.warn(`Site '${i}': ${C.warning}`);else if(C.path)r.info(`Site '${i}': sharing '${C.path}' so the SQLite database survives deploys.`);if(Ei(o)){let L=o.phpVersion??y?.php?.default??y?.php?.versions?.[0],M=J(a,i),D=Object.keys(x).length>0?{...o,env:I}:o,F=qi({siteName:i,site:D,releaseId:c,appBase:M,defaultPhpVersion:L}),z=!Z(y),ge=yn(o)==="custom"&&o.ssl?.certPath&&o.ssl?.keyPath?{certPath:o.ssl.certPath,keyPath:o.ssl.keyPath}:void 0,rt=o.isolation?uo({siteName:i,appBase:M}):[],er=z?sr({siteName:i,domain:o.domain||i,aliases:o.aliases,type:o.type,appDir:`${M}/current`,webDirectory:o.webDirectory,phpVersion:L,fastcgiPass:o.isolation?bn(i):void 0,redirects:o.redirects,ssl:ge,auth:o.auth&&o.auth.enabled!==!1&&o.auth.password?{username:o.auth.username||"admin",password:o.auth.password,realm:o.auth.realm}:void 0,serverSnippet:nr(o.nginx,y?.nginxTemplates),clientMaxBodySize:o.nginx?.clientMaxBodySize,hsts:o.ssl?.hsts,tlsProtocols:o.ssl?.tlsProtocols,security:o.security}):[],nt=z?vn(o):[],Eo=Sn(o)?hn({slug:a,siteName:i,site:o,phpVersion:L,appBase:M}):[],Do=z?Vi(o):[];r.step(`Deploying PHP site '${i}' to ${h.length} target(s)...`);let Ce=await e.runRemoteDeploy({targets:h,commands:[...Zs(M,a),...F,...rt,...er,...nt,...Eo,...Do,...S],comment:`ts-cloud deploy ${a}/${i}@${c}`,tags:{Project:a,Environment:s,Role:"app"}}),xn=In(n.notifications,o.notifications);if(!Ce.success)return await po(e,h,S,r),await or(xn,"deploy-failed",`❌ Deploy of ${a}/${i}@${c} failed: ${Ce.error||"unknown error"}`),{success:!1,error:Ce.error||"Remote PHP deploy failed",instanceCount:Ce.instanceCount,perInstance:Ce.perInstance};let Io=o.domain?` → https://${o.domain}`:"";return await or(xn,"deploy",`✅ Deployed ${a}/${i}@${c}${Io}`),{success:!0,instanceCount:Ce.instanceCount,perInstance:Ce.perInstance}}if(!u)return{success:!1,error:`Site '${i}' requires a release tarball but none was provided`};let E=await e.uploadRelease({config:n,environment:s,localPath:u,remoteKey:d,targets:h}),H=mt(a,i,c),re=e.name==="aws"?kr(f.deployBucketName,d,n.project.region||"us-east-1",H):Mr(E.artifactRef,H),W=P(o),N=J(a,i),q=W==="server-static"?Ur({siteName:i,slug:a,appDir:N,artifactFetch:re,releaseId:c,preStartCommands:o.preStart}):Nr({siteName:i,slug:a,appDir:N,artifactFetch:re,releaseId:c,execStart:Hr(o.start,l,`${N.replace(/\/+$/,"")}/releases/%i`),envEntries:I,port:o.port,preStartCommands:o.preStart,sharedPaths:o.sharedPaths,zeroDowntime:o.zeroDowntime!==!1,healthCheckPath:o.healthCheck?.path,liveness:o.liveness,memoryHigh:o.memoryHigh,memoryMax:o.memoryMax,cpuWeight:o.cpuWeight,ioWeight:o.ioWeight,tasksMax:o.tasksMax,stopTimeout:o.stopTimeout}),k=W==="server-static"&&!Z(y)&&!!o.domain,X=k?sr({siteName:i,domain:o.domain,aliases:o.aliases,type:o.type==="spa"?"spa":"static",appDir:`${N}/current`,webDirectory:"",redirects:o.redirects,auth:o.auth&&o.auth.enabled!==!1&&o.auth.password?{username:o.auth.username||"admin",password:o.auth.password,realm:o.auth.realm}:void 0,serverSnippet:nr(o.nginx,y?.nginxTemplates),clientMaxBodySize:o.nginx?.clientMaxBodySize,hsts:o.ssl?.hsts,tlsProtocols:o.ssl?.tlsProtocols,security:o.security}):[],oe=k?vn(o):[],fe=Sn(o)?hn({slug:a,siteName:i,site:o,appBase:N}):[],$e=[...Uo(N,{releaseId:c,commit:c,branch:o.repository?.branch??"main"}),...Zs(N,a),...q,...X,...oe,...fe,...S];r.step(`Deploying to ${h.length} target(s)...`);let ne=await e.runRemoteDeploy({targets:h,commands:$e,comment:`ts-cloud deploy ${a}/${i}@${c}`,tags:{Project:a,Environment:s,Role:"app"}});if(!ne.success)return await po(e,h,S,r),{success:!1,error:ne.error||"Remote deploy failed",instanceCount:ne.instanceCount,perInstance:ne.perInstance};return{success:!0,instanceCount:ne.instanceCount,perInstance:ne.perInstance}}async function po(e,t,r,n){if(r.length===0)return;try{let s=await e.runRemoteDeploy({targets:t,commands:r,comment:"ts-cloud host cleanup after failed deploy"});if(!s.success)n.warn(`Host cleanup after the failed deploy did not complete: ${s.error||"unknown error"}`)}catch(s){n.warn(`Host cleanup after the failed deploy did not run: ${s.message}`)}}function Qt(e){return`'${e.replaceAll("'","'\\''")}'`}function Il(e,t,r=!1,n=[]){let s=`${e}-dashboard-`,i=t.map((a)=>`${e}-${a}.service`),o=r?[`export TS_CLOUD_DASHBOARD_KEEP_DOMAINS=${Qt(JSON.stringify(n))}`,"rm -f /run/ts-cloud-dashboard-routes-changed",`/usr/local/bin/bun --bun -e ${Qt(`
530
530
  const { chmodSync, existsSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync } = require('node:fs')
531
531
  const { basename, join } = require('node:path')
532
532
  const root = '/etc/rpx/sites.d'
@@ -0,0 +1 @@
1
+ import{si}from"./chunk-nhqz2j90.js";import"./chunk-xx9mrcfs.js";import"./chunk-qq1f07te.js";import"./chunk-de69nra4.js";import"./chunk-6y6g726g.js";import"./chunk-xdk0gmxt.js";import"./chunk-12x968en.js";import"./chunk-psvqpztv.js";import"./chunk-dj03v81n.js";import"./chunk-4yym8xm4.js";export{si as resolveDashboardData};
@@ -0,0 +1 @@
1
+ import{Td,Ed,wd,Ad,Up,Fp}from"./chunk-v09tfezh.js";import"./chunk-q4dywv80.js";import"./chunk-01w4zbex.js";import"./chunk-xdk0gmxt.js";import"./chunk-7qbxq0be.js";import"./chunk-12x968en.js";import"./chunk-psvqpztv.js";export{Up as deleteStaticSite,Ed as deployStaticSite,Fp as deployStaticSiteFull,Td as generateStaticSiteTemplate,Ad as invalidateCache,wd as uploadStaticFiles};
@@ -0,0 +1 @@
1
+ import{v}from"./chunk-xdk0gmxt.js";import"./chunk-psvqpztv.js";export{v as CloudFormationClient};