@alchemy.run/fly 0.0.0 → 2.0.0-beta.84

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 (499) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +57 -4
  3. package/lib/App.d.ts +230 -0
  4. package/lib/App.d.ts.map +1 -0
  5. package/lib/App.js +340 -0
  6. package/lib/App.js.map +1 -0
  7. package/lib/AuthProvider.d.ts +22 -0
  8. package/lib/AuthProvider.d.ts.map +1 -0
  9. package/lib/AuthProvider.js +48 -0
  10. package/lib/AuthProvider.js.map +1 -0
  11. package/lib/BindService.d.ts +159 -0
  12. package/lib/BindService.d.ts.map +1 -0
  13. package/lib/BindService.js +222 -0
  14. package/lib/BindService.js.map +1 -0
  15. package/lib/Bootstrap.d.ts +8 -0
  16. package/lib/Bootstrap.d.ts.map +1 -0
  17. package/lib/Bootstrap.js +41 -0
  18. package/lib/Bootstrap.js.map +1 -0
  19. package/lib/Bucket.d.ts +278 -0
  20. package/lib/Bucket.d.ts.map +1 -0
  21. package/lib/Bucket.js +658 -0
  22. package/lib/Bucket.js.map +1 -0
  23. package/lib/BucketBinding.d.ts +33 -0
  24. package/lib/BucketBinding.d.ts.map +1 -0
  25. package/lib/BucketBinding.js +70 -0
  26. package/lib/BucketBinding.js.map +1 -0
  27. package/lib/Catalog.d.ts +28 -0
  28. package/lib/Catalog.d.ts.map +1 -0
  29. package/lib/Catalog.js +31 -0
  30. package/lib/Catalog.js.map +1 -0
  31. package/lib/Certificate.d.ts +189 -0
  32. package/lib/Certificate.d.ts.map +1 -0
  33. package/lib/Certificate.js +380 -0
  34. package/lib/Certificate.js.map +1 -0
  35. package/lib/Checkpoint.d.ts +55 -0
  36. package/lib/Checkpoint.d.ts.map +1 -0
  37. package/lib/Checkpoint.js +3 -0
  38. package/lib/Checkpoint.js.map +1 -0
  39. package/lib/CheckpointHttp.d.ts +22 -0
  40. package/lib/CheckpointHttp.d.ts.map +1 -0
  41. package/lib/CheckpointHttp.js +53 -0
  42. package/lib/CheckpointHttp.js.map +1 -0
  43. package/lib/ConnectPostgres.d.ts +58 -0
  44. package/lib/ConnectPostgres.d.ts.map +1 -0
  45. package/lib/ConnectPostgres.js +13 -0
  46. package/lib/ConnectPostgres.js.map +1 -0
  47. package/lib/ConnectPostgresHttp.d.ts +26 -0
  48. package/lib/ConnectPostgresHttp.d.ts.map +1 -0
  49. package/lib/ConnectPostgresHttp.js +57 -0
  50. package/lib/ConnectPostgresHttp.js.map +1 -0
  51. package/lib/Contributions.d.ts +7 -0
  52. package/lib/Contributions.d.ts.map +1 -0
  53. package/lib/Contributions.js +6 -0
  54. package/lib/Contributions.js.map +1 -0
  55. package/lib/Credentials.d.ts +33 -0
  56. package/lib/Credentials.d.ts.map +1 -0
  57. package/lib/Credentials.js +59 -0
  58. package/lib/Credentials.js.map +1 -0
  59. package/lib/Decrypt.d.ts +52 -0
  60. package/lib/Decrypt.d.ts.map +1 -0
  61. package/lib/Decrypt.js +3 -0
  62. package/lib/Decrypt.js.map +1 -0
  63. package/lib/DecryptHttp.d.ts +22 -0
  64. package/lib/DecryptHttp.d.ts.map +1 -0
  65. package/lib/DecryptHttp.js +43 -0
  66. package/lib/DecryptHttp.js.map +1 -0
  67. package/lib/DeleteObject.d.ts +29 -0
  68. package/lib/DeleteObject.d.ts.map +1 -0
  69. package/lib/DeleteObject.js +3 -0
  70. package/lib/DeleteObject.js.map +1 -0
  71. package/lib/DeleteObjectHttp.d.ts +12 -0
  72. package/lib/DeleteObjectHttp.d.ts.map +1 -0
  73. package/lib/DeleteObjectHttp.js +17 -0
  74. package/lib/DeleteObjectHttp.js.map +1 -0
  75. package/lib/Deployment.d.ts +51 -0
  76. package/lib/Deployment.d.ts.map +1 -0
  77. package/lib/Deployment.js +62 -0
  78. package/lib/Deployment.js.map +1 -0
  79. package/lib/DeploymentImages.d.ts +18 -0
  80. package/lib/DeploymentImages.d.ts.map +1 -0
  81. package/lib/DeploymentImages.js +61 -0
  82. package/lib/DeploymentImages.js.map +1 -0
  83. package/lib/DeploymentState.d.ts +17 -0
  84. package/lib/DeploymentState.d.ts.map +1 -0
  85. package/lib/DeploymentState.js +87 -0
  86. package/lib/DeploymentState.js.map +1 -0
  87. package/lib/Encrypt.d.ts +50 -0
  88. package/lib/Encrypt.d.ts.map +1 -0
  89. package/lib/Encrypt.js +3 -0
  90. package/lib/Encrypt.js.map +1 -0
  91. package/lib/EncryptHttp.d.ts +22 -0
  92. package/lib/EncryptHttp.d.ts.map +1 -0
  93. package/lib/EncryptHttp.js +39 -0
  94. package/lib/EncryptHttp.js.map +1 -0
  95. package/lib/Environment.d.ts +45 -0
  96. package/lib/Environment.d.ts.map +1 -0
  97. package/lib/Environment.js +43 -0
  98. package/lib/Environment.js.map +1 -0
  99. package/lib/Errors.d.ts +18 -0
  100. package/lib/Errors.d.ts.map +1 -0
  101. package/lib/Errors.js +13 -0
  102. package/lib/Errors.js.map +1 -0
  103. package/lib/Exec.d.ts +41 -0
  104. package/lib/Exec.d.ts.map +1 -0
  105. package/lib/Exec.js +3 -0
  106. package/lib/Exec.js.map +1 -0
  107. package/lib/ExecHttp.d.ts +22 -0
  108. package/lib/ExecHttp.d.ts.map +1 -0
  109. package/lib/ExecHttp.js +39 -0
  110. package/lib/ExecHttp.js.map +1 -0
  111. package/lib/GetObject.d.ts +34 -0
  112. package/lib/GetObject.d.ts.map +1 -0
  113. package/lib/GetObject.js +3 -0
  114. package/lib/GetObject.js.map +1 -0
  115. package/lib/GetObjectHttp.d.ts +12 -0
  116. package/lib/GetObjectHttp.d.ts.map +1 -0
  117. package/lib/GetObjectHttp.js +17 -0
  118. package/lib/GetObjectHttp.js.map +1 -0
  119. package/lib/GetSecret.d.ts +44 -0
  120. package/lib/GetSecret.d.ts.map +1 -0
  121. package/lib/GetSecret.js +3 -0
  122. package/lib/GetSecret.js.map +1 -0
  123. package/lib/GetSecretHttp.d.ts +22 -0
  124. package/lib/GetSecretHttp.d.ts.map +1 -0
  125. package/lib/GetSecretHttp.js +50 -0
  126. package/lib/GetSecretHttp.js.map +1 -0
  127. package/lib/HeadObject.d.ts +29 -0
  128. package/lib/HeadObject.d.ts.map +1 -0
  129. package/lib/HeadObject.js +3 -0
  130. package/lib/HeadObject.js.map +1 -0
  131. package/lib/HeadObjectHttp.d.ts +12 -0
  132. package/lib/HeadObjectHttp.d.ts.map +1 -0
  133. package/lib/HeadObjectHttp.js +17 -0
  134. package/lib/HeadObjectHttp.js.map +1 -0
  135. package/lib/IpAssignment.d.ts +258 -0
  136. package/lib/IpAssignment.d.ts.map +1 -0
  137. package/lib/IpAssignment.js +376 -0
  138. package/lib/IpAssignment.js.map +1 -0
  139. package/lib/ListObjectsV2.d.ts +29 -0
  140. package/lib/ListObjectsV2.d.ts.map +1 -0
  141. package/lib/ListObjectsV2.js +3 -0
  142. package/lib/ListObjectsV2.js.map +1 -0
  143. package/lib/ListObjectsV2Http.d.ts +12 -0
  144. package/lib/ListObjectsV2Http.d.ts.map +1 -0
  145. package/lib/ListObjectsV2Http.js +17 -0
  146. package/lib/ListObjectsV2Http.js.map +1 -0
  147. package/lib/ListSecrets.d.ts +53 -0
  148. package/lib/ListSecrets.d.ts.map +1 -0
  149. package/lib/ListSecrets.js +3 -0
  150. package/lib/ListSecrets.js.map +1 -0
  151. package/lib/ListSecretsHttp.d.ts +22 -0
  152. package/lib/ListSecretsHttp.d.ts.map +1 -0
  153. package/lib/ListSecretsHttp.js +35 -0
  154. package/lib/ListSecretsHttp.js.map +1 -0
  155. package/lib/Machine.d.ts +802 -0
  156. package/lib/Machine.d.ts.map +1 -0
  157. package/lib/Machine.js +773 -0
  158. package/lib/Machine.js.map +1 -0
  159. package/lib/MachineContainers.d.ts +44 -0
  160. package/lib/MachineContainers.d.ts.map +1 -0
  161. package/lib/MachineContainers.js +115 -0
  162. package/lib/MachineContainers.js.map +1 -0
  163. package/lib/Metadata.d.ts +109 -0
  164. package/lib/Metadata.d.ts.map +1 -0
  165. package/lib/Metadata.js +159 -0
  166. package/lib/Metadata.js.map +1 -0
  167. package/lib/MountVolume.d.ts +295 -0
  168. package/lib/MountVolume.d.ts.map +1 -0
  169. package/lib/MountVolume.js +20 -0
  170. package/lib/MountVolume.js.map +1 -0
  171. package/lib/Postgres.d.ts +298 -0
  172. package/lib/Postgres.d.ts.map +1 -0
  173. package/lib/Postgres.js +508 -0
  174. package/lib/Postgres.js.map +1 -0
  175. package/lib/PostgresMigrations.d.ts +35 -0
  176. package/lib/PostgresMigrations.d.ts.map +1 -0
  177. package/lib/PostgresMigrations.js +110 -0
  178. package/lib/PostgresMigrations.js.map +1 -0
  179. package/lib/Providers.d.ts +38 -0
  180. package/lib/Providers.d.ts.map +1 -0
  181. package/lib/Providers.js +95 -0
  182. package/lib/Providers.js.map +1 -0
  183. package/lib/PutObject.d.ts +35 -0
  184. package/lib/PutObject.d.ts.map +1 -0
  185. package/lib/PutObject.js +3 -0
  186. package/lib/PutObject.js.map +1 -0
  187. package/lib/PutObjectHttp.d.ts +12 -0
  188. package/lib/PutObjectHttp.d.ts.map +1 -0
  189. package/lib/PutObjectHttp.js +17 -0
  190. package/lib/PutObjectHttp.js.map +1 -0
  191. package/lib/ReadRedis.d.ts +26 -0
  192. package/lib/ReadRedis.d.ts.map +1 -0
  193. package/lib/ReadRedis.js +3 -0
  194. package/lib/ReadRedis.js.map +1 -0
  195. package/lib/ReadRedisHttp.d.ts +11 -0
  196. package/lib/ReadRedisHttp.d.ts.map +1 -0
  197. package/lib/ReadRedisHttp.js +15 -0
  198. package/lib/ReadRedisHttp.js.map +1 -0
  199. package/lib/ReadWriteRedis.d.ts +29 -0
  200. package/lib/ReadWriteRedis.d.ts.map +1 -0
  201. package/lib/ReadWriteRedis.js +3 -0
  202. package/lib/ReadWriteRedis.js.map +1 -0
  203. package/lib/ReadWriteRedisHttp.d.ts +11 -0
  204. package/lib/ReadWriteRedisHttp.d.ts.map +1 -0
  205. package/lib/ReadWriteRedisHttp.js +15 -0
  206. package/lib/ReadWriteRedisHttp.js.map +1 -0
  207. package/lib/Redis.d.ts +252 -0
  208. package/lib/Redis.d.ts.map +1 -0
  209. package/lib/Redis.js +608 -0
  210. package/lib/Redis.js.map +1 -0
  211. package/lib/RedisBinding.d.ts +7 -0
  212. package/lib/RedisBinding.d.ts.map +1 -0
  213. package/lib/RedisBinding.js +53 -0
  214. package/lib/RedisBinding.js.map +1 -0
  215. package/lib/RedisHttp.d.ts +8 -0
  216. package/lib/RedisHttp.d.ts.map +1 -0
  217. package/lib/RedisHttp.js +8 -0
  218. package/lib/RedisHttp.js.map +1 -0
  219. package/lib/Region.d.ts +16 -0
  220. package/lib/Region.d.ts.map +1 -0
  221. package/lib/Region.js +13 -0
  222. package/lib/Region.js.map +1 -0
  223. package/lib/Secret.d.ts +190 -0
  224. package/lib/Secret.d.ts.map +1 -0
  225. package/lib/Secret.js +310 -0
  226. package/lib/Secret.js.map +1 -0
  227. package/lib/SecretHttp.d.ts +54 -0
  228. package/lib/SecretHttp.d.ts.map +1 -0
  229. package/lib/SecretHttp.js +66 -0
  230. package/lib/SecretHttp.js.map +1 -0
  231. package/lib/SecretKey.d.ts +164 -0
  232. package/lib/SecretKey.d.ts.map +1 -0
  233. package/lib/SecretKey.js +277 -0
  234. package/lib/SecretKey.js.map +1 -0
  235. package/lib/SecretKeyHttp.d.ts +14 -0
  236. package/lib/SecretKeyHttp.d.ts.map +1 -0
  237. package/lib/SecretKeyHttp.js +5 -0
  238. package/lib/SecretKeyHttp.js.map +1 -0
  239. package/lib/Service.d.ts +1022 -0
  240. package/lib/Service.d.ts.map +1 -0
  241. package/lib/Service.js +1397 -0
  242. package/lib/Service.js.map +1 -0
  243. package/lib/Sign.d.ts +36 -0
  244. package/lib/Sign.d.ts.map +1 -0
  245. package/lib/Sign.js +3 -0
  246. package/lib/Sign.js.map +1 -0
  247. package/lib/SignHttp.d.ts +22 -0
  248. package/lib/SignHttp.d.ts.map +1 -0
  249. package/lib/SignHttp.js +36 -0
  250. package/lib/SignHttp.js.map +1 -0
  251. package/lib/Sprite.d.ts +283 -0
  252. package/lib/Sprite.d.ts.map +1 -0
  253. package/lib/Sprite.js +469 -0
  254. package/lib/Sprite.js.map +1 -0
  255. package/lib/SpriteHttp.d.ts +25 -0
  256. package/lib/SpriteHttp.d.ts.map +1 -0
  257. package/lib/SpriteHttp.js +33 -0
  258. package/lib/SpriteHttp.js.map +1 -0
  259. package/lib/Verify.d.ts +37 -0
  260. package/lib/Verify.d.ts.map +1 -0
  261. package/lib/Verify.js +3 -0
  262. package/lib/Verify.js.map +1 -0
  263. package/lib/VerifyHttp.d.ts +22 -0
  264. package/lib/VerifyHttp.d.ts.map +1 -0
  265. package/lib/VerifyHttp.js +38 -0
  266. package/lib/VerifyHttp.js.map +1 -0
  267. package/lib/Volume.d.ts +53 -0
  268. package/lib/Volume.d.ts.map +1 -0
  269. package/lib/Volume.js +188 -0
  270. package/lib/Volume.js.map +1 -0
  271. package/lib/VolumeSnapshot.d.ts +135 -0
  272. package/lib/VolumeSnapshot.d.ts.map +1 -0
  273. package/lib/VolumeSnapshot.js +213 -0
  274. package/lib/VolumeSnapshot.js.map +1 -0
  275. package/lib/Website/AssetDeployment.d.ts +43 -0
  276. package/lib/Website/AssetDeployment.d.ts.map +1 -0
  277. package/lib/Website/AssetDeployment.js +162 -0
  278. package/lib/Website/AssetDeployment.js.map +1 -0
  279. package/lib/Website/Astro.d.ts +87 -0
  280. package/lib/Website/Astro.d.ts.map +1 -0
  281. package/lib/Website/Astro.js +55 -0
  282. package/lib/Website/Astro.js.map +1 -0
  283. package/lib/Website/Foldkit.d.ts +52 -0
  284. package/lib/Website/Foldkit.d.ts.map +1 -0
  285. package/lib/Website/Foldkit.js +45 -0
  286. package/lib/Website/Foldkit.js.map +1 -0
  287. package/lib/Website/FrameworkSite.d.ts +175 -0
  288. package/lib/Website/FrameworkSite.d.ts.map +1 -0
  289. package/lib/Website/FrameworkSite.js +162 -0
  290. package/lib/Website/FrameworkSite.js.map +1 -0
  291. package/lib/Website/Nextjs.d.ts +54 -0
  292. package/lib/Website/Nextjs.d.ts.map +1 -0
  293. package/lib/Website/Nextjs.js +46 -0
  294. package/lib/Website/Nextjs.js.map +1 -0
  295. package/lib/Website/Nuxt.d.ts +51 -0
  296. package/lib/Website/Nuxt.d.ts.map +1 -0
  297. package/lib/Website/Nuxt.js +36 -0
  298. package/lib/Website/Nuxt.js.map +1 -0
  299. package/lib/Website/Octane.d.ts +49 -0
  300. package/lib/Website/Octane.d.ts.map +1 -0
  301. package/lib/Website/Octane.js +39 -0
  302. package/lib/Website/Octane.js.map +1 -0
  303. package/lib/Website/ReactRouter.d.ts +80 -0
  304. package/lib/Website/ReactRouter.d.ts.map +1 -0
  305. package/lib/Website/ReactRouter.js +70 -0
  306. package/lib/Website/ReactRouter.js.map +1 -0
  307. package/lib/Website/SolidStart.d.ts +93 -0
  308. package/lib/Website/SolidStart.d.ts.map +1 -0
  309. package/lib/Website/SolidStart.js +71 -0
  310. package/lib/Website/SolidStart.js.map +1 -0
  311. package/lib/Website/StaticSite.d.ts +155 -0
  312. package/lib/Website/StaticSite.d.ts.map +1 -0
  313. package/lib/Website/StaticSite.js +168 -0
  314. package/lib/Website/StaticSite.js.map +1 -0
  315. package/lib/Website/SvelteKit.d.ts +52 -0
  316. package/lib/Website/SvelteKit.d.ts.map +1 -0
  317. package/lib/Website/SvelteKit.js +37 -0
  318. package/lib/Website/SvelteKit.js.map +1 -0
  319. package/lib/Website/TanStackStart.d.ts +76 -0
  320. package/lib/Website/TanStackStart.d.ts.map +1 -0
  321. package/lib/Website/TanStackStart.js +66 -0
  322. package/lib/Website/TanStackStart.js.map +1 -0
  323. package/lib/Website/Vinext.d.ts +73 -0
  324. package/lib/Website/Vinext.d.ts.map +1 -0
  325. package/lib/Website/Vinext.js +67 -0
  326. package/lib/Website/Vinext.js.map +1 -0
  327. package/lib/Website/Vite.d.ts +103 -0
  328. package/lib/Website/Vite.d.ts.map +1 -0
  329. package/lib/Website/Vite.js +91 -0
  330. package/lib/Website/Vite.js.map +1 -0
  331. package/lib/Website/Vocs.d.ts +52 -0
  332. package/lib/Website/Vocs.d.ts.map +1 -0
  333. package/lib/Website/Vocs.js +38 -0
  334. package/lib/Website/Vocs.js.map +1 -0
  335. package/lib/Website/Waku.d.ts +64 -0
  336. package/lib/Website/Waku.d.ts.map +1 -0
  337. package/lib/Website/Waku.js +39 -0
  338. package/lib/Website/Waku.js.map +1 -0
  339. package/lib/Website/index.d.ts +17 -0
  340. package/lib/Website/index.d.ts.map +1 -0
  341. package/lib/Website/index.js +17 -0
  342. package/lib/Website/index.js.map +1 -0
  343. package/lib/WriteCertificates.d.ts +63 -0
  344. package/lib/WriteCertificates.d.ts.map +1 -0
  345. package/lib/WriteCertificates.js +3 -0
  346. package/lib/WriteCertificates.js.map +1 -0
  347. package/lib/WriteCertificatesHttp.d.ts +25 -0
  348. package/lib/WriteCertificatesHttp.d.ts.map +1 -0
  349. package/lib/WriteCertificatesHttp.js +67 -0
  350. package/lib/WriteCertificatesHttp.js.map +1 -0
  351. package/lib/WriteRedis.d.ts +26 -0
  352. package/lib/WriteRedis.d.ts.map +1 -0
  353. package/lib/WriteRedis.js +3 -0
  354. package/lib/WriteRedis.js.map +1 -0
  355. package/lib/WriteRedisHttp.d.ts +11 -0
  356. package/lib/WriteRedisHttp.d.ts.map +1 -0
  357. package/lib/WriteRedisHttp.js +15 -0
  358. package/lib/WriteRedisHttp.js.map +1 -0
  359. package/lib/WriteSecret.d.ts +68 -0
  360. package/lib/WriteSecret.d.ts.map +1 -0
  361. package/lib/WriteSecret.js +4 -0
  362. package/lib/WriteSecret.js.map +1 -0
  363. package/lib/WriteSecretHttp.d.ts +26 -0
  364. package/lib/WriteSecretHttp.d.ts.map +1 -0
  365. package/lib/WriteSecretHttp.js +54 -0
  366. package/lib/WriteSecretHttp.js.map +1 -0
  367. package/lib/bluegreen.d.ts +36 -0
  368. package/lib/bluegreen.d.ts.map +1 -0
  369. package/lib/bluegreen.js +458 -0
  370. package/lib/bluegreen.js.map +1 -0
  371. package/lib/hosted.d.ts +172 -0
  372. package/lib/hosted.d.ts.map +1 -0
  373. package/lib/hosted.js +458 -0
  374. package/lib/hosted.js.map +1 -0
  375. package/lib/index.d.ts +65 -0
  376. package/lib/index.d.ts.map +1 -0
  377. package/lib/index.js +63 -0
  378. package/lib/index.js.map +1 -0
  379. package/lib/internal/RemoteBuilder.d.ts +28 -0
  380. package/lib/internal/RemoteBuilder.d.ts.map +1 -0
  381. package/lib/internal/RemoteBuilder.js +145 -0
  382. package/lib/internal/RemoteBuilder.js.map +1 -0
  383. package/lib/leases.d.ts +53 -0
  384. package/lib/leases.d.ts.map +1 -0
  385. package/lib/leases.js +279 -0
  386. package/lib/leases.js.map +1 -0
  387. package/lib/ports.d.ts +51 -0
  388. package/lib/ports.d.ts.map +1 -0
  389. package/lib/ports.js +123 -0
  390. package/lib/ports.js.map +1 -0
  391. package/lib/replicas.d.ts +270 -0
  392. package/lib/replicas.d.ts.map +1 -0
  393. package/lib/replicas.js +969 -0
  394. package/lib/replicas.js.map +1 -0
  395. package/lib/rpc.d.ts +35 -0
  396. package/lib/rpc.d.ts.map +1 -0
  397. package/lib/rpc.js +113 -0
  398. package/lib/rpc.js.map +1 -0
  399. package/package.json +83 -3
  400. package/src/App.ts +516 -0
  401. package/src/AuthProvider.ts +68 -0
  402. package/src/BindService.ts +304 -0
  403. package/src/Bootstrap.ts +66 -0
  404. package/src/Bucket.ts +916 -0
  405. package/src/BucketBinding.ts +128 -0
  406. package/src/Catalog.ts +43 -0
  407. package/src/Certificate.ts +548 -0
  408. package/src/Checkpoint.ts +69 -0
  409. package/src/CheckpointHttp.ts +66 -0
  410. package/src/ConnectPostgres.ts +68 -0
  411. package/src/ConnectPostgresHttp.ts +77 -0
  412. package/src/Contributions.ts +9 -0
  413. package/src/Credentials.ts +102 -0
  414. package/src/Decrypt.ts +62 -0
  415. package/src/DecryptHttp.ts +52 -0
  416. package/src/DeleteObject.ts +43 -0
  417. package/src/DeleteObjectHttp.ts +20 -0
  418. package/src/Deployment.ts +121 -0
  419. package/src/DeploymentImages.ts +84 -0
  420. package/src/DeploymentState.ts +104 -0
  421. package/src/Encrypt.ts +60 -0
  422. package/src/EncryptHttp.ts +48 -0
  423. package/src/Environment.ts +81 -0
  424. package/src/Errors.ts +14 -0
  425. package/src/Exec.ts +50 -0
  426. package/src/ExecHttp.ts +47 -0
  427. package/src/GetObject.ts +48 -0
  428. package/src/GetObjectHttp.ts +20 -0
  429. package/src/GetSecret.ts +48 -0
  430. package/src/GetSecretHttp.ts +60 -0
  431. package/src/HeadObject.ts +43 -0
  432. package/src/HeadObjectHttp.ts +20 -0
  433. package/src/IpAssignment.ts +522 -0
  434. package/src/ListObjectsV2.ts +43 -0
  435. package/src/ListObjectsV2Http.ts +20 -0
  436. package/src/ListSecrets.ts +57 -0
  437. package/src/ListSecretsHttp.ts +43 -0
  438. package/src/Machine.ts +1268 -0
  439. package/src/MachineContainers.ts +152 -0
  440. package/src/Metadata.ts +195 -0
  441. package/src/MountVolume.ts +315 -0
  442. package/src/Postgres.ts +777 -0
  443. package/src/PostgresMigrations.ts +153 -0
  444. package/src/Providers.ts +155 -0
  445. package/src/PutObject.ts +49 -0
  446. package/src/PutObjectHttp.ts +20 -0
  447. package/src/ReadRedis.ts +31 -0
  448. package/src/ReadRedisHttp.ts +18 -0
  449. package/src/ReadWriteRedis.ts +33 -0
  450. package/src/ReadWriteRedisHttp.ts +18 -0
  451. package/src/Redis.ts +820 -0
  452. package/src/RedisBinding.ts +64 -0
  453. package/src/RedisHttp.ts +11 -0
  454. package/src/Region.ts +44 -0
  455. package/src/Secret.ts +405 -0
  456. package/src/SecretHttp.ts +149 -0
  457. package/src/SecretKey.ts +389 -0
  458. package/src/SecretKeyHttp.ts +23 -0
  459. package/src/Service.ts +1934 -0
  460. package/src/Sign.ts +46 -0
  461. package/src/SignHttp.ts +44 -0
  462. package/src/Sprite.ts +671 -0
  463. package/src/SpriteHttp.ts +50 -0
  464. package/src/Verify.ts +47 -0
  465. package/src/VerifyHttp.ts +46 -0
  466. package/src/Volume.ts +264 -0
  467. package/src/VolumeSnapshot.ts +321 -0
  468. package/src/Website/AssetDeployment.ts +294 -0
  469. package/src/Website/Astro.ts +87 -0
  470. package/src/Website/Foldkit.ts +54 -0
  471. package/src/Website/FrameworkSite.ts +363 -0
  472. package/src/Website/Nextjs.ts +50 -0
  473. package/src/Website/Nuxt.ts +48 -0
  474. package/src/Website/Octane.ts +44 -0
  475. package/src/Website/ReactRouter.ts +76 -0
  476. package/src/Website/SolidStart.ts +90 -0
  477. package/src/Website/StaticSite.ts +275 -0
  478. package/src/Website/SvelteKit.ts +49 -0
  479. package/src/Website/TanStackStart.ts +72 -0
  480. package/src/Website/Vinext.ts +79 -0
  481. package/src/Website/Vite.ts +112 -0
  482. package/src/Website/Vocs.ts +50 -0
  483. package/src/Website/Waku.ts +65 -0
  484. package/src/Website/index.ts +16 -0
  485. package/src/WriteCertificates.ts +90 -0
  486. package/src/WriteCertificatesHttp.ts +93 -0
  487. package/src/WriteRedis.ts +31 -0
  488. package/src/WriteRedisHttp.ts +18 -0
  489. package/src/WriteSecret.ts +86 -0
  490. package/src/WriteSecretHttp.ts +71 -0
  491. package/src/bluegreen.ts +708 -0
  492. package/src/globals.d.ts +2 -0
  493. package/src/hosted.ts +668 -0
  494. package/src/index.ts +64 -0
  495. package/src/internal/RemoteBuilder.ts +224 -0
  496. package/src/leases.ts +437 -0
  497. package/src/ports.ts +177 -0
  498. package/src/replicas.ts +1480 -0
  499. package/src/rpc.ts +136 -0
@@ -0,0 +1,969 @@
1
+ import * as machines from "@distilled.cloud/fly-io/machines";
2
+ import * as Retry from "@distilled.cloud/fly-io/Retry";
3
+ import { deepEqual } from "alchemy/Diff";
4
+ import * as Data from "effect/Data";
5
+ import * as Effect from "effect/Effect";
6
+ import * as Result from "effect/Result";
7
+ import * as Schedule from "effect/Schedule";
8
+ import { listOwnedApps } from "./App.js";
9
+ import { reconcileBlueGreen, setRouting } from "./bluegreen.js";
10
+ import { validateDeployment } from "./Deployment.js";
11
+ import { classifyDeploymentState, validProtocol2Generation } from "./DeploymentState.js";
12
+ import { usingMachineLeases } from "./leases.js";
13
+ import { alchemyMetadataKeys, createMachineMetadata, isAlchemyOwnedMetadata, sanitizeFlyAppName, } from "./Metadata.js";
14
+ import { regionOfReplica } from "./Region.js";
15
+ import { deleteVolume, ensureVolumeGroup, getVolumeById, volumeGroupName } from "./Volume.js";
16
+ const WAIT_TIMEOUT_SECONDS = 8;
17
+ const waitBackoff = Schedule.exponential("500 millis");
18
+ const SERVICE_CHECK_NAME_PREFIX = "servicecheck-";
19
+ export class ReplicaNotCreated extends Data.TaggedError("Fly.ReplicaNotCreated") {
20
+ }
21
+ export class ReplicaChecksNotPassing extends Data.TaggedError("Fly.ReplicaChecksNotPassing") {
22
+ get message() {
23
+ const checks = this.checks.map((check) => `${check.name ?? "unnamed"}: ${check.status ?? "unknown"}${check.output ? ` (${check.output})` : ""}`);
24
+ return `Service checks did not pass for ${this.appName}/${this.machineId}: ${checks.join("; ") || "no service check results"}`;
25
+ }
26
+ }
27
+ const compactRecord = (record) => Object.fromEntries(Object.entries(record ?? {}).flatMap(([key, value]) => value === undefined ? [] : [[key, value]]));
28
+ export const gone = (machine) => machine === undefined || machine.state === "destroyed";
29
+ export const getMachineById = (appName, machineId) => machines.getMachine({ app_name: appName, machine_id: machineId }).pipe(Effect.map((machine) => (gone(machine) ? undefined : machine)), Effect.catchTag("NotFound", () => Effect.succeed(undefined)));
30
+ /**
31
+ * Reads a Machine we hold a lease on. Fly's Machines API is not
32
+ * read-after-write consistent: a freshly created Machine can accept a lease
33
+ * (201) and then answer `GET /machines/{id}` with 404 for several seconds.
34
+ * Holding the lease blocks other writers, so a 404 here is lag rather than
35
+ * deletion; retry it (bounded) before surfacing `NotFound`.
36
+ */
37
+ const getLeasedMachine = (appName, machineId) => machines.getMachine({ app_name: appName, machine_id: machineId }).pipe(Effect.retry({
38
+ times: 10,
39
+ schedule: Schedule.spaced("1 second"),
40
+ while: (error) => error._tag === "NotFound",
41
+ }));
42
+ const getLeasedMachineById = (appName, machineId) => getLeasedMachine(appName, machineId).pipe(Effect.map((machine) => (gone(machine) ? undefined : machine)), Effect.catchTag("NotFound", () => Effect.succeed(undefined)));
43
+ export const listMachinesByApp = (appName) => machines.listMachines({ app_name: appName }).pipe(
44
+ // A just-created App can answer 500 "failed to get app: sql: no rows in
45
+ // result set" until the Machines API sees it; safe to replay a read.
46
+ Effect.retry({
47
+ while: (error) => error._tag === "InternalServerError",
48
+ schedule: Schedule.spaced("2 seconds"),
49
+ times: 8,
50
+ }), Effect.map((machines) => machines.filter((machine) => !gone(machine))), Effect.catchTag("NotFound", () => Effect.succeed([])));
51
+ export const resolveCount = (count) => Math.max(1, Math.floor(count ?? 1));
52
+ export const replicaMachineName = (base, index, count) => {
53
+ if (count <= 1 && index === 0)
54
+ return base;
55
+ const suffix = `-${index}`;
56
+ const room = 30 - suffix.length;
57
+ const clipped = base.slice(0, Math.max(1, room)).replace(/-+$/g, "");
58
+ return sanitizeFlyAppName(`${clipped}${suffix}`);
59
+ };
60
+ export const replicaIndexOf = (machine) => {
61
+ const raw = compactRecord(machine.config?.metadata)[alchemyMetadataKeys.replica];
62
+ const parsed = raw === undefined ? 0 : Number(raw);
63
+ return Number.isFinite(parsed) ? parsed : 0;
64
+ };
65
+ export const alchemyIdOf = (machine) => {
66
+ const id = compactRecord(machine.config?.metadata)[alchemyMetadataKeys.id];
67
+ return id !== undefined && id.length > 0 ? id : undefined;
68
+ };
69
+ export const isOwnedType = (machine, type) => {
70
+ const metadata = compactRecord(machine.config?.metadata);
71
+ return isAlchemyOwnedMetadata(metadata) && metadata[alchemyMetadataKeys.type] === type;
72
+ };
73
+ export const toImageRef = (ref) => {
74
+ if (ref === undefined)
75
+ return undefined;
76
+ const imageRef = {
77
+ registry: ref.registry,
78
+ repository: ref.repository,
79
+ tag: ref.tag,
80
+ digest: ref.digest,
81
+ };
82
+ return imageRef.registry === undefined &&
83
+ imageRef.repository === undefined &&
84
+ imageRef.tag === undefined &&
85
+ imageRef.digest === undefined
86
+ ? undefined
87
+ : imageRef;
88
+ };
89
+ export const toGuestAttrs = (guest) => {
90
+ if (guest === undefined)
91
+ return undefined;
92
+ return {
93
+ cpuKind: guest.cpu_kind,
94
+ cpus: guest.cpus,
95
+ memoryMb: guest.memory_mb,
96
+ gpuKind: guest.gpu_kind,
97
+ gpus: guest.gpus,
98
+ };
99
+ };
100
+ export const toFlyServiceCheck = (check) => ({
101
+ type: check.type,
102
+ port: check.port,
103
+ interval: check.interval,
104
+ timeout: check.timeout,
105
+ grace_period: check.gracePeriod,
106
+ method: check.method,
107
+ path: check.path,
108
+ protocol: check.protocol,
109
+ headers: check.headers?.map((header) => ({ name: header.name, values: header.values })),
110
+ tls_server_name: check.tlsServerName,
111
+ tls_skip_verify: check.tlsSkipVerify,
112
+ });
113
+ export const toFlyService = (service) => ({
114
+ protocol: service.protocol,
115
+ internal_port: service.internalPort,
116
+ autostart: service.autostart,
117
+ autostop: typeof service.autostop === "boolean" ? (service.autostop ? "stop" : "off") : service.autostop,
118
+ min_machines_running: service.minMachinesRunning,
119
+ ports: service.ports?.map((port) => ({
120
+ port: port.port,
121
+ handlers: port.handlers,
122
+ force_https: port.forceHttps,
123
+ start_port: port.startPort,
124
+ end_port: port.endPort,
125
+ })),
126
+ checks: service.checks?.map((check) => ({
127
+ ...toFlyServiceCheck(check),
128
+ // Fly clamps service-check intervals; named Machine checks retain longer intervals.
129
+ interval: (durationNanoseconds(check.interval) ?? 0n) > 60000000000n ? "60s" : check.interval,
130
+ })),
131
+ });
132
+ export const autostopMode = (value) => value === true ? "stop" : value === false || value === undefined ? "off" : value;
133
+ const normalizedCheckDuration = (value) => {
134
+ const nanos = durationNanoseconds(value);
135
+ return nanos === undefined ? value : `${nanos}ns`;
136
+ };
137
+ const normalizedCheck = (check) => check === undefined
138
+ ? undefined
139
+ : {
140
+ ...check,
141
+ grace_period: normalizedCheckDuration(check.grace_period),
142
+ interval: normalizedCheckDuration(check.interval),
143
+ timeout: normalizedCheckDuration(check.timeout),
144
+ };
145
+ export const sameChecks = (observed, desired) => {
146
+ const normalize = (checks) => Object.fromEntries(Object.entries(checks ?? {}).map(([name, check]) => [name, normalizedCheck(check)]));
147
+ return deepEqual(normalize(observed), normalize(desired), { stripNullish: true });
148
+ };
149
+ export const normalizedServices = (services) => (services ?? []).map((service) => ({
150
+ ...service,
151
+ autostop: autostopMode(service.autostop),
152
+ checks: service.checks?.map(normalizedCheck),
153
+ }));
154
+ export const sameServices = (observed, desired) => deepEqual(normalizedServices(observed), normalizedServices(desired), { stripNullish: true });
155
+ export const hasPublishedService = (services) => (services ?? []).some((service) => (service.ports ?? []).some((port) => port.port !== undefined || port.start_port !== undefined));
156
+ export const waitStarted = (appName, machineId) => machines
157
+ .waitMachine({
158
+ app_name: appName,
159
+ machine_id: machineId,
160
+ state: "started",
161
+ timeout: WAIT_TIMEOUT_SECONDS,
162
+ })
163
+ .pipe(Retry.none, Effect.retry({
164
+ times: 6,
165
+ schedule: waitBackoff,
166
+ while: (e) => e._tag === "GatewayTimeout" || e._tag === "MachineWaitTimeout",
167
+ }), Effect.timeout("50 seconds"));
168
+ export const configuredCheckNames = (config) => [
169
+ ...Object.keys(config?.checks ?? {}),
170
+ ...(config?.services ?? []).flatMap((service) => (service.checks ?? []).map((check, checkIndex) => `${SERVICE_CHECK_NAME_PREFIX}${String(checkIndex).padStart(2, "0")}-${check.type ?? "tcp"}-${service.internal_port}`)),
171
+ ];
172
+ export const checksPassing = (machine, config) => {
173
+ const expected = configuredCheckNames(config);
174
+ const checks = machine.checks ?? [];
175
+ // Fly may also report compatibility mirrors; they do not replace service reports.
176
+ return (machine.state === "started" &&
177
+ new Set(expected).size === expected.length &&
178
+ checks.every((check) => check.name !== undefined &&
179
+ check.status === "passing" &&
180
+ (expected.includes(check.name) ||
181
+ expected.some((name) => name.startsWith(SERVICE_CHECK_NAME_PREFIX) &&
182
+ check.name === name.replace(SERVICE_CHECK_NAME_PREFIX, "bg_deployments_compat-")))) &&
183
+ new Set(checks.map((check) => check.name)).size === checks.length &&
184
+ expected.every((name) => {
185
+ const reports = checks.filter((check) => check.name === name);
186
+ return reports.length === 1 && reports[0]?.status === "passing";
187
+ }));
188
+ };
189
+ const TRANSIENT_GET_TAGS = [
190
+ "TooManyRequests",
191
+ "InternalServerError",
192
+ "BadGateway",
193
+ "ServiceUnavailable",
194
+ "GatewayTimeout",
195
+ ];
196
+ /**
197
+ * After the Machine is started, wait until Fly reports every service
198
+ * check as passing. No configured checks is a no-op. Empty live
199
+ * `servicecheck-*` results keep polling — they are not success.
200
+ * `warning` / `unknown` during grace keep polling.
201
+ */
202
+ export const waitHealthy = Effect.fn(function* (appName, machine, healthTimeoutMs = 60_000, config = machine.config) {
203
+ const machineId = machine.id;
204
+ const named = Object.keys(config?.checks ?? {});
205
+ const expected = (config?.services ?? []).flatMap((service) => service.checks ?? []).length;
206
+ if (machineId === undefined || (expected === 0 && named.length === 0))
207
+ return machine;
208
+ let observed = machine;
209
+ const notPassing = () => new ReplicaChecksNotPassing({
210
+ appName,
211
+ machineId,
212
+ checks: (observed.checks ?? []).map((check) => ({
213
+ name: check.name,
214
+ status: check.status,
215
+ output: undefined,
216
+ })),
217
+ });
218
+ const passing = yield* getMachineById(appName, machineId).pipe(Retry.none, Effect.map((current) => {
219
+ if (current === undefined)
220
+ return false;
221
+ observed = current;
222
+ return (current.instance_id !== undefined &&
223
+ current.instance_id === machine.instance_id &&
224
+ checksPassing(current, config));
225
+ }), Effect.catchTag(TRANSIENT_GET_TAGS, () => Effect.succeed(false)), Effect.catchReason("HttpClientError", "TransportError", () => Effect.succeed(false)), Effect.repeat({
226
+ schedule: Schedule.spaced(healthTimeoutMs / 10),
227
+ until: (passing) => passing,
228
+ times: 10,
229
+ }), Effect.timeoutOrElse({ duration: healthTimeoutMs, orElse: () => Effect.fail(notPassing()) }));
230
+ if (!passing)
231
+ return yield* notPassing();
232
+ return observed;
233
+ });
234
+ export const waitDestroyed = (appName, machineId) => machines
235
+ .waitMachine({
236
+ app_name: appName,
237
+ machine_id: machineId,
238
+ state: "destroyed",
239
+ timeout: WAIT_TIMEOUT_SECONDS,
240
+ })
241
+ .pipe(Retry.none, Effect.as(undefined), Effect.catchTag("NotFound", () => Effect.void), Effect.retry({
242
+ times: 6,
243
+ schedule: waitBackoff,
244
+ while: (e) => e._tag === "GatewayTimeout" || e._tag === "MachineWaitTimeout",
245
+ }), Effect.timeout("50 seconds"));
246
+ export const ensureStarted = (appName, machine, skipLaunch, healthTimeoutMs = 60_000, expectedConfig, existingLeases) => usingMachineLeases(appName, existingLeases, (leases) => ensureLeasedStarted(appName, machine, skipLaunch, healthTimeoutMs, expectedConfig, leases));
247
+ const ensureLeasedStarted = Effect.fn(function* (appName, machine, skipLaunch, healthTimeoutMs, expectedConfig, leases) {
248
+ const machineId = machine.id;
249
+ if (machineId === undefined || skipLaunch)
250
+ return machine;
251
+ yield* leases.acquire([machineId]);
252
+ const started = yield* Effect.gen(function* () {
253
+ // Create/update responses can lag Fly's automatic launch.
254
+ const current = yield* getLeasedMachine(appName, machineId);
255
+ if (!sameOwnership(current, machine)) {
256
+ return yield* new ReplicaOwnershipChanged({ appName, machineId });
257
+ }
258
+ yield* Effect.logDebug("Fly machine startup", { appName, machineId, state: current.state });
259
+ if (current.state === "stopped" ||
260
+ current.state === "suspended" ||
261
+ current.state === "failed") {
262
+ yield* leases.mutate(machineId, (lease_nonce) => machines.startMachine({ app_name: appName, machine_id: machineId, lease_nonce }));
263
+ }
264
+ // Re-observe state between waits instead of retrying the wait in the SDK.
265
+ yield* machines
266
+ .waitMachine({
267
+ app_name: appName,
268
+ machine_id: machineId,
269
+ state: "started",
270
+ instance_id: current.instance_id,
271
+ timeout: WAIT_TIMEOUT_SECONDS,
272
+ })
273
+ .pipe(Retry.none);
274
+ return yield* getLeasedMachine(appName, machineId);
275
+ }).pipe(Effect.retry({
276
+ times: 6,
277
+ schedule: waitBackoff,
278
+ while: (error) => error._tag === "MachineStartFromCreatedState" ||
279
+ error._tag === "MachineReplacing" ||
280
+ error._tag === "MachineWaitTimeout" ||
281
+ error._tag === "Conflict" ||
282
+ error._tag === "GatewayTimeout",
283
+ }), Effect.timeout("180 seconds"));
284
+ return yield* waitHealthy(appName, started, healthTimeoutMs, expectedConfig ?? machine.config);
285
+ });
286
+ export const deleteMachine = (appName, machineId, existingLeases) => usingMachineLeases(appName, existingLeases, (leases) => Effect.gen(function* () {
287
+ if (appName.length === 0 || machineId.length === 0)
288
+ return;
289
+ yield* leases.acquire([machineId]);
290
+ yield* leases.remove(machineId, (lease_nonce) => machines
291
+ .deleteMachine({ app_name: appName, machine_id: machineId, force: true, lease_nonce })
292
+ .pipe(Effect.timeout("30 seconds")));
293
+ yield* waitDestroyed(appName, machineId);
294
+ }).pipe(Effect.catchTag("NotFound", () => leases.forget(machineId))));
295
+ const durationNanoseconds = (value) => {
296
+ if (value === undefined || value === "")
297
+ return undefined;
298
+ const negative = value.startsWith("-");
299
+ const duration = /^[+-]/.test(value) ? value.slice(1) : value;
300
+ if (duration === "0")
301
+ return 0n;
302
+ const limit = negative ? 1n << 63n : (1n << 63n) - 1n;
303
+ const units = {
304
+ ns: 1n,
305
+ us: 1000n,
306
+ µs: 1000n,
307
+ μs: 1000n,
308
+ ms: 1000000n,
309
+ s: 1000000000n,
310
+ m: 60000000000n,
311
+ h: 3600000000000n,
312
+ };
313
+ let total = 0n;
314
+ let consumed = 0;
315
+ for (const part of duration.matchAll(/(\d+(?:\.\d*)?|\.\d+)(ns|us|µs|μs|ms|s|m|h)/gy)) {
316
+ const [integer = "", fraction = ""] = part[1].split(".");
317
+ const whole = integer.replace(/^0+/, "") || "0";
318
+ if (whole.length > 19)
319
+ return undefined;
320
+ const unit = units[part[2]];
321
+ let fractionalNanos = 0n;
322
+ // Truncate each component to nanoseconds without unbounded BigInt operands.
323
+ for (let index = fraction.length - 1; index >= 0; index--) {
324
+ fractionalNanos = (BigInt(fraction[index]) * unit + fractionalNanos) / 10n;
325
+ }
326
+ total += BigInt(whole) * unit + fractionalNanos;
327
+ if (total > limit)
328
+ return undefined;
329
+ consumed += part[0].length;
330
+ }
331
+ if (consumed === 0 || consumed !== duration.length)
332
+ return undefined;
333
+ return negative ? -total : total;
334
+ };
335
+ const stopTimeoutMillis = (timeout) => {
336
+ if (timeout === "")
337
+ return 0;
338
+ // Shutdown policies retain their unsigned millisecond-or-larger syntax.
339
+ if (timeout === undefined || !/^(?:\d+(?:\.\d+)?(?:ms|s|m|h))+$/.test(timeout)) {
340
+ return undefined;
341
+ }
342
+ const nanos = durationNanoseconds(timeout);
343
+ return nanos === undefined ? undefined : Number(nanos) / 1_000_000;
344
+ };
345
+ export const sameStopConfig = (observed, desired) => observed?.signal === desired?.signal &&
346
+ stopTimeoutMillis(observed?.timeout) === stopTimeoutMillis(desired?.timeout);
347
+ export class ShutdownPolicyMismatch extends Data.TaggedError("Fly.ShutdownPolicyMismatch") {
348
+ }
349
+ export const predecessorShutdown = Effect.fn(function* (machine) {
350
+ const persisted = machine.config?.stop_config;
351
+ const injected = machine.config?.env?.ALCHEMY_FLY_SHUTDOWN_TIMEOUT_MS;
352
+ const timeoutMs = stopTimeoutMillis(persisted?.timeout);
353
+ if (injected !== undefined) {
354
+ const managedTimeout = Number(injected);
355
+ const signal = persisted?.signal ?? "SIGTERM";
356
+ if (!/^\d+$/.test(injected) ||
357
+ !Number.isSafeInteger(managedTimeout) ||
358
+ managedTimeout <= 0 ||
359
+ managedTimeout > 300_000 ||
360
+ (signal !== "SIGTERM" && signal !== "SIGINT") ||
361
+ (persisted?.timeout !== undefined && timeoutMs !== managedTimeout)) {
362
+ return yield* new ShutdownPolicyMismatch({
363
+ machineId: machine.id ?? "",
364
+ message: "The predecessor's managed shutdown environment and stop_config disagree; reconcile its shutdown policy before migration.",
365
+ });
366
+ }
367
+ return {
368
+ signal,
369
+ timeout: persisted?.timeout ?? `${managedTimeout}ms`,
370
+ timeoutMs: managedTimeout,
371
+ };
372
+ }
373
+ if (persisted?.timeout !== undefined && (!timeoutMs || timeoutMs > 300_000)) {
374
+ return yield* new ShutdownPolicyMismatch({
375
+ machineId: machine.id ?? "",
376
+ message: "The predecessor has an invalid stop_config timeout; refusing to shorten its shutdown window.",
377
+ });
378
+ }
379
+ // Unspecified raw-image overrides must remain unspecified for Fly's defaults.
380
+ return {
381
+ signal: persisted?.signal,
382
+ timeout: persisted?.timeout,
383
+ timeoutMs: timeoutMs ?? 300_000,
384
+ };
385
+ });
386
+ export class ReplicaOwnershipChanged extends Data.TaggedError("Fly.ReplicaOwnershipChanged") {
387
+ }
388
+ export class ReplicaRetirementIncomplete extends Data.TaggedError("Fly.ReplicaRetirementIncomplete") {
389
+ }
390
+ const retirementStep = (appName, machineId, stage) => (effect) => effect.pipe(Effect.mapError((error) => error._tag === "NotFound"
391
+ ? error
392
+ : new ReplicaRetirementIncomplete({
393
+ appName,
394
+ residuals: [{ machineId, stage: `${stage}: ${error._tag}` }],
395
+ })));
396
+ const sameOwnership = (observed, snapshot) => observed.id === snapshot.id &&
397
+ [
398
+ alchemyMetadataKeys.stack,
399
+ alchemyMetadataKeys.stage,
400
+ alchemyMetadataKeys.id,
401
+ alchemyMetadataKeys.type,
402
+ alchemyMetadataKeys.instance,
403
+ alchemyMetadataKeys.fqn,
404
+ alchemyMetadataKeys.generation,
405
+ ].every((key) => observed.config?.metadata?.[key] === snapshot.config?.metadata?.[key]);
406
+ export const leaseSnapshot = Effect.fn(function* (appName, snapshot, leases) {
407
+ const ordered = [...snapshot].sort((a, b) => (a.id ?? "").localeCompare(b.id ?? ""));
408
+ const leased = new Set();
409
+ yield* Effect.forEach(ordered, (machine) => Effect.gen(function* () {
410
+ if (!machine.id || machine.host_status === "unreachable")
411
+ return;
412
+ const id = machine.id;
413
+ yield* leases.acquire([id]).pipe(Effect.tap(() => Effect.sync(() => leased.add(id))),
414
+ // A just-created Machine can 404 on the lease API while it already
415
+ // lists; a Machine that is really gone is dropped by the GET below.
416
+ Effect.retry({
417
+ while: (error) => error._tag === "NotFound",
418
+ schedule: Schedule.spaced("1 second"),
419
+ times: 5,
420
+ }), Effect.catchTag("NotFound", () => Effect.void));
421
+ }), { concurrency: 1 }).pipe(Effect.timeout("30 seconds"));
422
+ const observed = [];
423
+ for (const machine of ordered) {
424
+ if (!machine.id)
425
+ continue;
426
+ if (machine.host_status === "unreachable") {
427
+ observed.push(machine);
428
+ continue;
429
+ }
430
+ const current = yield* leased.has(machine.id)
431
+ ? getLeasedMachineById(appName, machine.id)
432
+ : getMachineById(appName, machine.id);
433
+ if (!current) {
434
+ yield* leases.forget(machine.id);
435
+ continue;
436
+ }
437
+ if (!sameOwnership(current, machine))
438
+ return yield* new ReplicaOwnershipChanged({ appName, machineId: machine.id });
439
+ yield* leases.checkTarget(machine.id);
440
+ observed.push(current);
441
+ }
442
+ yield* leases.check;
443
+ return observed;
444
+ });
445
+ export const retireMachines = (appName, snapshot, replacementReady = false, existingLeases) => usingMachineLeases(appName, existingLeases, (leases) => retireLeasedMachines(appName, snapshot, replacementReady, leases));
446
+ const retireLeasedMachines = Effect.fn(function* (appName, snapshot, replacementReady, leases) {
447
+ const targets = yield* leaseSnapshot(appName, snapshot, leases);
448
+ const results = yield* Effect.forEach(targets, (machine) => Effect.gen(function* () {
449
+ yield* leases.check;
450
+ const result = yield* Effect.gen(function* () {
451
+ if (!machine.id)
452
+ return;
453
+ if (replacementReady && machine.host_status === "unreachable") {
454
+ const metadata = machine.config?.metadata;
455
+ if (!machine.config ||
456
+ machine.incomplete_config ||
457
+ (machine.config.mounts?.length ?? 0) > 0 ||
458
+ !metadata?.[alchemyMetadataKeys.instance] ||
459
+ !metadata[alchemyMetadataKeys.fqn]) {
460
+ return yield* new ReplicaOwnershipChanged({ appName, machineId: machine.id });
461
+ }
462
+ yield* Effect.logWarning("Force-retiring owned stateless Fly Machine on unreachable host; work was not proven drained", { appName, machineId: machine.id });
463
+ const machineId = machine.id;
464
+ const heldNonce = yield* leases.nonceIfHeld(machineId);
465
+ yield* Effect.gen(function* () {
466
+ const remove = (lease_nonce) => machines
467
+ .deleteMachine({
468
+ app_name: appName,
469
+ machine_id: machineId,
470
+ force: true,
471
+ lease_nonce,
472
+ })
473
+ .pipe(Retry.none, Effect.timeout("30 seconds"));
474
+ if (heldNonce !== undefined)
475
+ yield* leases.remove(machineId, remove);
476
+ else
477
+ yield* leases.guard(remove());
478
+ }).pipe(Effect.catchTag("NotFound", () => Effect.void), retirementStep(appName, machineId, "unreachable force-delete"));
479
+ yield* leases.forget(machine.id);
480
+ return yield* waitDestroyed(appName, machine.id);
481
+ }
482
+ yield* retireMachine(appName, machine.id, machine, leases);
483
+ }).pipe(Effect.result);
484
+ yield* leases.check;
485
+ return Result.isFailure(result)
486
+ ? result.failure._tag === "Fly.ReplicaRetirementIncomplete"
487
+ ? result.failure.residuals
488
+ : [{ machineId: machine.id ?? "", stage: result.failure._tag }]
489
+ : [];
490
+ }), { concurrency: 4 });
491
+ const residuals = results.flat();
492
+ if (residuals.length)
493
+ return yield* new ReplicaRetirementIncomplete({ appName, residuals });
494
+ });
495
+ export const retireMachine = (appName, machineId, snapshot, existingLeases) => usingMachineLeases(appName, existingLeases, (leases) => retireLeasedMachine(appName, machineId, snapshot, leases).pipe(Effect.tap(() => leases.forget(machineId))));
496
+ const retireLeasedMachine = Effect.fn(function* (appName, machineId, snapshot, leases) {
497
+ yield* leases.acquire([machineId]);
498
+ const current = yield* getMachineById(appName, machineId);
499
+ if (current === undefined)
500
+ return;
501
+ if (snapshot && !sameOwnership(current, snapshot)) {
502
+ return yield* new ReplicaOwnershipChanged({ appName, machineId });
503
+ }
504
+ const stop = yield* predecessorShutdown(current);
505
+ if (current.cordoned === true &&
506
+ current.config?.metadata?.[alchemyMetadataKeys.phase] === "candidate")
507
+ return yield* deleteMachine(appName, machineId, leases);
508
+ if (current.state !== "stopped" && current.state !== "created") {
509
+ yield* leases
510
+ .mutate(machineId, (lease_nonce) => machines
511
+ .cordonMachine({ app_name: appName, machine_id: machineId, lease_nonce })
512
+ .pipe(Effect.timeout("30 seconds")), { idempotent: true })
513
+ .pipe(retirementStep(appName, machineId, "cordon"));
514
+ if (hasPublishedService(current.config?.services))
515
+ yield* Effect.sleep("10 seconds");
516
+ yield* leases
517
+ .mutate(machineId, (lease_nonce) => machines
518
+ .stopMachine({
519
+ app_name: appName,
520
+ machine_id: machineId,
521
+ lease_nonce,
522
+ signal: stop.signal,
523
+ timeout: stop.timeout,
524
+ })
525
+ .pipe(Effect.timeout(stop.timeoutMs + 15_000)), { timeoutMs: stop.timeoutMs + 120_000 })
526
+ .pipe(retirementStep(appName, machineId, "stop"));
527
+ yield* machines
528
+ .waitMachine({
529
+ app_name: appName,
530
+ machine_id: machineId,
531
+ state: "stopped",
532
+ instance_id: current.instance_id,
533
+ timeout: WAIT_TIMEOUT_SECONDS,
534
+ })
535
+ .pipe(Retry.none, Effect.retry({
536
+ times: 10,
537
+ schedule: Schedule.spaced(Math.max(500, stop.timeoutMs / 10)),
538
+ while: (error) => error._tag === "MachineWaitTimeout" || error._tag === "GatewayTimeout",
539
+ }), Effect.timeout(stop.timeoutMs + 15_000), retirementStep(appName, machineId, "wait-stopped"));
540
+ }
541
+ yield* leases
542
+ .remove(machineId, (lease_nonce) => machines
543
+ .deleteMachine({ app_name: appName, machine_id: machineId, force: false, lease_nonce })
544
+ .pipe(Effect.timeout("30 seconds")))
545
+ .pipe(Effect.catchTag("NotFound", () => Effect.void), retirementStep(appName, machineId, "delete"));
546
+ yield* waitDestroyed(appName, machineId).pipe(retirementStep(appName, machineId, "verify-absent"));
547
+ }, Effect.catchTag("NotFound", () => Effect.void));
548
+ const mountedDisksOf = (machine, volumesById) => (machine.config?.mounts ?? []).flatMap((mount) => {
549
+ const volumeId = mount.volume;
550
+ const path = mount.path;
551
+ if (volumeId === undefined || path === undefined)
552
+ return [];
553
+ const volume = volumesById.get(volumeId);
554
+ return [{ path, volumeId, sizeGb: volume?.size_gb ?? 0, name: volume?.name ?? "" }];
555
+ });
556
+ export const toReplica = (machine, volumesById) => ({
557
+ machineId: machine.id ?? "",
558
+ name: machine.name ?? "",
559
+ region: machine.region ?? "",
560
+ state: machine.state ?? "",
561
+ instanceId: machine.instance_id,
562
+ privateIp: machine.private_ip,
563
+ imageRef: toImageRef(machine.image_ref),
564
+ guest: toGuestAttrs(machine.config?.guest),
565
+ mounts: mountedDisksOf(machine, volumesById),
566
+ });
567
+ export const toReplicaSet = (replicas, appName, baseName, services) => {
568
+ const primary = replicas[0];
569
+ return {
570
+ appName,
571
+ machineId: primary?.machineId ?? "",
572
+ machineIds: replicas.map((replica) => replica.machineId),
573
+ name: primary?.name ?? baseName,
574
+ baseName,
575
+ region: primary?.region ?? "",
576
+ regions: [
577
+ ...new Set(replicas.map((replica) => replica.region).filter((region) => region.length > 0)),
578
+ ],
579
+ state: primary?.state ?? "",
580
+ instanceId: primary?.instanceId,
581
+ privateIp: primary?.privateIp,
582
+ imageRef: primary?.imageRef,
583
+ guest: primary?.guest,
584
+ services,
585
+ count: replicas.length,
586
+ mounts: primary?.mounts ?? [],
587
+ replicas,
588
+ };
589
+ };
590
+ export const ownedReplicas = (listed, input) => listed.filter((machine) => {
591
+ const metadata = machine.config?.metadata ?? {};
592
+ if (!Object.entries(input.metadata).every(([key, value]) => metadata[key] === value))
593
+ return false;
594
+ if (metadata[alchemyMetadataKeys.instance] !== undefined) {
595
+ return (metadata[alchemyMetadataKeys.instance] === input.resourceInstanceId &&
596
+ metadata[alchemyMetadataKeys.fqn] === input.fqn);
597
+ }
598
+ return ((machine.id !== undefined && input.machineIds?.includes(machine.id)) ||
599
+ (input.baseName !== undefined &&
600
+ (machine.name === input.baseName ||
601
+ machine.name === replicaMachineName(input.baseName, replicaIndexOf(machine), 2))));
602
+ });
603
+ export const listReplicas = Effect.fn(function* (input) {
604
+ const machines = yield* listMachinesByApp(input.appName);
605
+ return machines
606
+ .filter((machine) => isOwnedType(machine, input.type) && alchemyIdOf(machine) === input.id)
607
+ .sort((left, right) => replicaIndexOf(left) - replicaIndexOf(right));
608
+ });
609
+ export const listReplicaSets = Effect.fn(function* (type) {
610
+ const apps = yield* listOwnedApps();
611
+ const groups = yield* Effect.forEach(apps, (app) => listMachinesByApp(app.appName).pipe(Effect.map((machines) => {
612
+ const owned = machines.filter((machine) => isOwnedType(machine, type));
613
+ const byId = new Map();
614
+ for (const machine of owned) {
615
+ const id = alchemyIdOf(machine);
616
+ if (id === undefined)
617
+ continue;
618
+ const metadata = machine.config?.metadata;
619
+ const key = JSON.stringify([
620
+ metadata?.[alchemyMetadataKeys.stack],
621
+ metadata?.[alchemyMetadataKeys.stage],
622
+ metadata?.[alchemyMetadataKeys.fqn] ?? id,
623
+ metadata?.[alchemyMetadataKeys.instance],
624
+ metadata?.[alchemyMetadataKeys.generation],
625
+ ]);
626
+ const group = byId.get(key) ?? [];
627
+ group.push(machine);
628
+ byId.set(key, group);
629
+ }
630
+ return [...byId.values()].map((group) => {
631
+ const sorted = [...group].sort((left, right) => replicaIndexOf(left) - replicaIndexOf(right));
632
+ const replicas = sorted.map((machine) => toReplica(machine, new Map()));
633
+ return toReplicaSet(replicas, app.appName, replicas[0]?.name ?? "", sorted[0]?.config?.services);
634
+ });
635
+ })), { concurrency: 8 });
636
+ return groups.flat();
637
+ });
638
+ const pickVolume = (group, used, preferId) => {
639
+ if (preferId !== undefined && !used.has(preferId)) {
640
+ const preferred = group.find((volume) => volume.id === preferId);
641
+ if (preferred !== undefined)
642
+ return preferred;
643
+ }
644
+ return group.find((volume) => volume.id !== undefined && !used.has(volume.id));
645
+ };
646
+ export const reconcileReplicas = (input) => usingMachineLeases(input.appName, undefined, (leases) => reconcileLeasedReplicas(input, leases));
647
+ const reconcileLeasedReplicas = Effect.fn(function* (input, leases) {
648
+ const ownership = yield* createMachineMetadata(input.id, input.type);
649
+ const alchemy = {
650
+ ...ownership,
651
+ [alchemyMetadataKeys.instance]: input.resourceInstanceId,
652
+ [alchemyMetadataKeys.fqn]: input.fqn,
653
+ [alchemyMetadataKeys.baseName]: input.baseName,
654
+ };
655
+ const buildConfig = (replica) => ({
656
+ ...input.buildConfig(replica),
657
+ checks: input.checks === undefined
658
+ ? undefined
659
+ : Object.fromEntries(Object.entries(input.checks).map(([name, check]) => [name, toFlyServiceCheck(check)])),
660
+ stop_config: input.policy.shutdown === undefined
661
+ ? undefined
662
+ : { signal: input.policy.shutdown.signal, timeout: input.policy.shutdown.timeout },
663
+ });
664
+ const config = buildConfig({ index: 0, mounts: [], metadata: alchemy });
665
+ yield* validateDeployment(input.policy, config, input.disks.length > 0, input.skipLaunch);
666
+ if (input.policy.bluegreen)
667
+ return yield* reconcileBlueGreen({ ...input, buildConfig }, ownership, alchemy, leases);
668
+ const desiredNames = new Set(Array.from({ length: input.count }, (_, index) => replicaMachineName(input.baseName, index, input.count)));
669
+ const listed = yield* listMachinesByApp(input.appName);
670
+ const owned = yield* leaseSnapshot(input.appName, ownedReplicas(listed, { ...input, metadata: ownership, machineIds: input.outputMachineIds }), leases);
671
+ const byIndex = new Map();
672
+ const preferIds = new Set((input.outputMachineIds ?? []).filter((id) => id.length > 0));
673
+ for (const machine of owned) {
674
+ const id = machine.id;
675
+ if (id !== undefined && preferIds.has(id)) {
676
+ byIndex.set(replicaIndexOf(machine), machine);
677
+ }
678
+ }
679
+ for (const machine of owned) {
680
+ const name = machine.name;
681
+ if (name === undefined ||
682
+ (!desiredNames.has(name) &&
683
+ machine.config?.metadata?.[alchemyMetadataKeys.instance] !== input.resourceInstanceId))
684
+ continue;
685
+ const index = replicaIndexOf(machine);
686
+ if (!byIndex.has(index))
687
+ byIndex.set(index, machine);
688
+ }
689
+ const scaleDown = [...byIndex].filter(([index]) => index >= input.count);
690
+ yield* retireMachines(input.appName, scaleDown.map(([, machine]) => machine), false, leases);
691
+ for (const [index] of scaleDown)
692
+ byIndex.delete(index);
693
+ // Volumes are regional: one group per disk and region, sized to the
694
+ // replicas that run there.
695
+ const replicasIn = (region) => Array.from({ length: input.count }, (_, index) => index).filter((index) => regionOfReplica(input.regions, index) === region).length;
696
+ const groups = [];
697
+ for (const [diskIndex, disk] of input.disks.entries()) {
698
+ const name = yield* volumeGroupName(input.id, disk);
699
+ const preferIds = (input.preferVolumeIds ?? [])
700
+ .map((replica) => replica[diskIndex])
701
+ .filter((id) => id !== undefined && id.length > 0);
702
+ const byRegion = new Map();
703
+ for (const region of input.regions) {
704
+ byRegion.set(region, yield* ensureVolumeGroup({
705
+ appName: input.appName,
706
+ name,
707
+ region,
708
+ count: replicasIn(region),
709
+ disk,
710
+ preferIds,
711
+ }));
712
+ }
713
+ groups.push({ disk, name, byRegion });
714
+ }
715
+ const usedVolumeIds = new Set();
716
+ const live = [];
717
+ for (let index = 0; index < input.count; index++) {
718
+ const name = replicaMachineName(input.baseName, index, input.count);
719
+ const region = regionOfReplica(input.regions, index);
720
+ const metadata = {
721
+ ...alchemy,
722
+ [alchemyMetadataKeys.replica]: String(index),
723
+ ...(input.minSecretsVersion === undefined
724
+ ? {}
725
+ : { [alchemyMetadataKeys.secretsVersion]: String(input.minSecretsVersion) }),
726
+ };
727
+ const prefer = input.preferVolumeIds?.[index] ?? [];
728
+ const mounts = [];
729
+ for (const [diskIndex, group] of groups.entries()) {
730
+ const volume = pickVolume(group.byRegion.get(region)?.volumes ?? [], usedVolumeIds, prefer[diskIndex]);
731
+ const volumeId = volume?.id;
732
+ if (volumeId === undefined) {
733
+ return yield* new ReplicaNotCreated({ name, appName: input.appName });
734
+ }
735
+ usedVolumeIds.add(volumeId);
736
+ mounts.push({ volume: volumeId, path: group.disk.path });
737
+ }
738
+ const config = buildConfig({ index, mounts, metadata });
739
+ let current = byIndex.get(index);
740
+ // Fly cannot move a Machine between regions. Machine names are unique
741
+ // per App, so retire the old replica before creating it in its region.
742
+ if (current !== undefined && current.region !== region) {
743
+ yield* retireMachines(input.appName, [current], false, leases);
744
+ byIndex.delete(index);
745
+ current = undefined;
746
+ }
747
+ if (current === undefined) {
748
+ const created = yield* machines
749
+ .createMachine({
750
+ app_name: input.appName,
751
+ name,
752
+ region,
753
+ config,
754
+ skip_launch: input.skipLaunch === true ? true : undefined,
755
+ min_secrets_version: input.minSecretsVersion,
756
+ })
757
+ .pipe(Effect.catchTag("Conflict", () => Effect.succeed(undefined)));
758
+ current =
759
+ created ??
760
+ (yield* listMachinesByApp(input.appName).pipe(Effect.map((machines) => ownedReplicas(machines, {
761
+ ...input,
762
+ metadata: ownership,
763
+ machineIds: input.outputMachineIds,
764
+ }).find((machine) => machine.name === name))));
765
+ if (current === undefined || current.id === undefined) {
766
+ return yield* new ReplicaNotCreated({ name, appName: input.appName });
767
+ }
768
+ const observed = (yield* leaseSnapshot(input.appName, [current], leases))[0];
769
+ if (!observed || input.configDrifted(observed, { mounts, metadata })) {
770
+ return yield* new ReplicaNotCreated({ name, appName: input.appName });
771
+ }
772
+ current = observed;
773
+ }
774
+ else if (!sameChecks(current.config?.checks, config.checks) ||
775
+ !sameStopConfig(current.config?.stop_config, config.stop_config) ||
776
+ input.configDrifted(current, { mounts, metadata })) {
777
+ const machineId = current.id;
778
+ const currentVersion = current.instance_id;
779
+ const previous = current;
780
+ const expected = { ...previous, config };
781
+ yield* leases.mutate(machineId, (lease_nonce) => machines
782
+ .updateMachine({
783
+ app_name: input.appName,
784
+ machine_id: machineId,
785
+ current_version: currentVersion,
786
+ config,
787
+ skip_launch: input.skipLaunch === true ? true : undefined,
788
+ min_secrets_version: input.minSecretsVersion,
789
+ lease_nonce,
790
+ })
791
+ .pipe(Effect.timeout("30 seconds")));
792
+ // Rolling opt-out removes generation metadata; observe that transition before startup.
793
+ current = yield* leases.guard(Effect.gen(function* () {
794
+ const observed = yield* machines
795
+ .getMachine({ app_name: input.appName, machine_id: machineId })
796
+ .pipe(Retry.none);
797
+ if (!sameOwnership(observed, expected) && !sameOwnership(observed, previous))
798
+ return yield* new ReplicaOwnershipChanged({ appName: input.appName, machineId });
799
+ if (!sameOwnership(observed, expected) ||
800
+ input.configDrifted(observed, { mounts, metadata }) ||
801
+ !sameChecks(observed.config?.checks, config.checks) ||
802
+ !sameStopConfig(observed.config?.stop_config, config.stop_config))
803
+ return yield* new ReplicaNotCreated({ appName: input.appName, name });
804
+ return observed;
805
+ }).pipe(Effect.retry({
806
+ times: 8,
807
+ schedule: waitBackoff,
808
+ while: (error) => error._tag === "Fly.ReplicaNotCreated" ||
809
+ TRANSIENT_GET_TAGS.some((tag) => tag === error._tag),
810
+ }), Effect.timeout("60 seconds")));
811
+ }
812
+ current = yield* ensureStarted(input.appName, current, input.skipLaunch === true, input.policy.healthTimeoutMs, config, leases);
813
+ if (current.cordoned === true && !input.skipLaunch) {
814
+ yield* setRouting(input.appName, current.id, false, leases);
815
+ if (hasPublishedService(config.services))
816
+ yield* Effect.sleep("10 seconds");
817
+ current = yield* waitHealthy(input.appName, current, input.policy.healthTimeoutMs, config);
818
+ }
819
+ live.push(current);
820
+ }
821
+ const liveIds = new Set(live.map((machine) => machine.id));
822
+ yield* retireMachines(input.appName, owned.filter((machine) => !liveIds.has(machine.id)), false, leases);
823
+ for (const group of groups) {
824
+ for (const extra of [...group.byRegion.values()].flatMap((placed) => placed.extras)) {
825
+ const volumeId = extra.id;
826
+ if (volumeId === undefined || usedVolumeIds.has(volumeId))
827
+ continue;
828
+ yield* deleteVolume(input.appName, volumeId);
829
+ }
830
+ }
831
+ const volumesById = new Map();
832
+ for (const group of groups) {
833
+ for (const volume of [...group.byRegion.values()].flatMap((placed) => placed.volumes)) {
834
+ if (volume.id !== undefined)
835
+ volumesById.set(volume.id, volume);
836
+ }
837
+ }
838
+ const fresh = yield* Effect.forEach(live, (machine) => machine.id === undefined
839
+ ? Effect.succeed(machine)
840
+ : getMachineById(input.appName, machine.id).pipe(Effect.map((next) => next ?? machine)), { concurrency: 4 });
841
+ const replicas = fresh.map((machine) => toReplica(machine, volumesById));
842
+ return toReplicaSet(replicas, input.appName, input.baseName, fresh[0]?.config?.services);
843
+ });
844
+ export const deleteReplicaSet = Effect.fn(function* (input) {
845
+ return yield* usingMachineLeases(input.appName, undefined, (leases) => Effect.gen(function* () {
846
+ const owned = yield* leaseSnapshot(input.appName, ownedReplicas(yield* listMachinesByApp(input.appName), {
847
+ ...input,
848
+ metadata: yield* createMachineMetadata(input.id, input.type),
849
+ }), leases);
850
+ if (input.force) {
851
+ yield* Effect.forEach(owned, (machine) => Effect.gen(function* () {
852
+ if (!machine.id)
853
+ return;
854
+ const current = yield* getMachineById(input.appName, machine.id);
855
+ if (!current)
856
+ return;
857
+ if (!sameOwnership(current, machine))
858
+ return yield* new ReplicaOwnershipChanged({
859
+ appName: input.appName,
860
+ machineId: machine.id,
861
+ });
862
+ yield* deleteMachine(input.appName, machine.id, leases);
863
+ }), { concurrency: 4 });
864
+ }
865
+ else
866
+ yield* retireMachines(input.appName, owned, false, leases);
867
+ yield* Effect.forEach([...new Set(input.volumeIds)].filter((id) => id.length > 0), (volumeId) => deleteVolume(input.appName, volumeId), { concurrency: 4 });
868
+ }));
869
+ });
870
+ export const volumeIdsOf = (set) => {
871
+ const ids = new Set();
872
+ for (const mount of set.mounts ?? [])
873
+ ids.add(mount.volumeId);
874
+ for (const replica of set.replicas ?? []) {
875
+ for (const mount of replica.mounts)
876
+ ids.add(mount.volumeId);
877
+ }
878
+ return [...ids];
879
+ };
880
+ export const groupGenerations = (machines) => {
881
+ const groups = new Map();
882
+ for (const machine of machines) {
883
+ const generation = machine.config?.metadata?.[alchemyMetadataKeys.generation];
884
+ const group = groups.get(generation) ?? [];
885
+ group.push(machine);
886
+ groups.set(generation, group);
887
+ }
888
+ return groups;
889
+ };
890
+ export const observeReplicaSet = Effect.fn(function* (input) {
891
+ if (input.appName === undefined)
892
+ return undefined;
893
+ const owned = ownedReplicas(yield* listMachinesByApp(input.appName), {
894
+ ...input,
895
+ metadata: yield* createMachineMetadata(input.id, input.type),
896
+ });
897
+ const groups = groupGenerations(owned);
898
+ const committed = [...groups.entries()]
899
+ .filter(([generation, group]) => {
900
+ if (generation === undefined)
901
+ return false;
902
+ const count = Number(group[0]?.config?.metadata?.[alchemyMetadataKeys.count]);
903
+ if (group.some((machine) => classifyDeploymentState(machine).protocol === "invalid"))
904
+ return false;
905
+ const protocol2 = group.some((machine) => machine.config?.metadata?.[alchemyMetadataKeys.protocol] === "2");
906
+ if (protocol2 && !validProtocol2Generation(group))
907
+ return false;
908
+ return (Number.isSafeInteger(count) &&
909
+ count > 0 &&
910
+ group.length === count &&
911
+ new Set(group.map(replicaIndexOf)).size === count &&
912
+ group.every((machine) => {
913
+ const metadata = machine.config?.metadata;
914
+ const index = replicaIndexOf(machine);
915
+ return (index >= 0 &&
916
+ index < count &&
917
+ Number(metadata?.[alchemyMetadataKeys.count]) === count &&
918
+ metadata?.[alchemyMetadataKeys.phase] === "active" &&
919
+ machine.cordoned === false &&
920
+ (metadata[alchemyMetadataKeys.protocol] === undefined ||
921
+ ((metadata[alchemyMetadataKeys.protocol] === "1" ||
922
+ metadata[alchemyMetadataKeys.protocol] === "2") &&
923
+ metadata[alchemyMetadataKeys.restored] === "true" &&
924
+ (metadata[alchemyMetadataKeys.role] === "idle" ||
925
+ (machine.instance_id !== undefined &&
926
+ metadata[alchemyMetadataKeys.checkedInstance] === machine.instance_id)))));
927
+ }));
928
+ })
929
+ .sort(([, a], [, b]) => Number(b[0]?.config?.metadata?.[alchemyMetadataKeys.sequence] ?? 0) -
930
+ Number(a[0]?.config?.metadata?.[alchemyMetadataKeys.sequence] ?? 0));
931
+ const legacy = (groups.get(undefined) ?? []).filter((machine) => {
932
+ const { protocol } = classifyDeploymentState(machine);
933
+ return protocol === "legacy" || protocol === "1";
934
+ });
935
+ const listed = (committed[0]?.[1] ?? legacy).sort((a, b) => replicaIndexOf(a) - replicaIndexOf(b));
936
+ const rolloutPending = owned.some((machine) => !listed.includes(machine));
937
+ if (listed.length > 0) {
938
+ const volumesById = new Map();
939
+ for (const machine of listed) {
940
+ for (const mount of machine.config?.mounts ?? []) {
941
+ const volumeId = mount.volume;
942
+ if (volumeId === undefined || volumesById.has(volumeId))
943
+ continue;
944
+ const volume = yield* getVolumeById(input.appName, volumeId);
945
+ if (volume !== undefined)
946
+ volumesById.set(volumeId, volume);
947
+ }
948
+ }
949
+ const replicas = listed.map((machine) => toReplica(machine, volumesById));
950
+ return {
951
+ ...toReplicaSet(replicas, input.appName, listed[0]?.config?.metadata?.[alchemyMetadataKeys.baseName] ??
952
+ input.baseName ??
953
+ replicas[0]?.name ??
954
+ "", listed[0]?.config?.services),
955
+ rolloutPending,
956
+ };
957
+ }
958
+ if (owned.length)
959
+ return {
960
+ ...toReplicaSet([], input.appName, input.baseName ?? ""),
961
+ region: owned[0]?.region ?? "",
962
+ regions: [
963
+ ...new Set(owned.map((machine) => machine.region ?? "").filter((region) => region.length > 0)),
964
+ ],
965
+ rolloutPending: true,
966
+ };
967
+ return undefined;
968
+ });
969
+ //# sourceMappingURL=replicas.js.map