@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.
- package/LICENSE +201 -0
- package/README.md +57 -4
- package/lib/App.d.ts +230 -0
- package/lib/App.d.ts.map +1 -0
- package/lib/App.js +340 -0
- package/lib/App.js.map +1 -0
- package/lib/AuthProvider.d.ts +22 -0
- package/lib/AuthProvider.d.ts.map +1 -0
- package/lib/AuthProvider.js +48 -0
- package/lib/AuthProvider.js.map +1 -0
- package/lib/BindService.d.ts +159 -0
- package/lib/BindService.d.ts.map +1 -0
- package/lib/BindService.js +222 -0
- package/lib/BindService.js.map +1 -0
- package/lib/Bootstrap.d.ts +8 -0
- package/lib/Bootstrap.d.ts.map +1 -0
- package/lib/Bootstrap.js +41 -0
- package/lib/Bootstrap.js.map +1 -0
- package/lib/Bucket.d.ts +278 -0
- package/lib/Bucket.d.ts.map +1 -0
- package/lib/Bucket.js +658 -0
- package/lib/Bucket.js.map +1 -0
- package/lib/BucketBinding.d.ts +33 -0
- package/lib/BucketBinding.d.ts.map +1 -0
- package/lib/BucketBinding.js +70 -0
- package/lib/BucketBinding.js.map +1 -0
- package/lib/Catalog.d.ts +28 -0
- package/lib/Catalog.d.ts.map +1 -0
- package/lib/Catalog.js +31 -0
- package/lib/Catalog.js.map +1 -0
- package/lib/Certificate.d.ts +189 -0
- package/lib/Certificate.d.ts.map +1 -0
- package/lib/Certificate.js +380 -0
- package/lib/Certificate.js.map +1 -0
- package/lib/Checkpoint.d.ts +55 -0
- package/lib/Checkpoint.d.ts.map +1 -0
- package/lib/Checkpoint.js +3 -0
- package/lib/Checkpoint.js.map +1 -0
- package/lib/CheckpointHttp.d.ts +22 -0
- package/lib/CheckpointHttp.d.ts.map +1 -0
- package/lib/CheckpointHttp.js +53 -0
- package/lib/CheckpointHttp.js.map +1 -0
- package/lib/ConnectPostgres.d.ts +58 -0
- package/lib/ConnectPostgres.d.ts.map +1 -0
- package/lib/ConnectPostgres.js +13 -0
- package/lib/ConnectPostgres.js.map +1 -0
- package/lib/ConnectPostgresHttp.d.ts +26 -0
- package/lib/ConnectPostgresHttp.d.ts.map +1 -0
- package/lib/ConnectPostgresHttp.js +57 -0
- package/lib/ConnectPostgresHttp.js.map +1 -0
- package/lib/Contributions.d.ts +7 -0
- package/lib/Contributions.d.ts.map +1 -0
- package/lib/Contributions.js +6 -0
- package/lib/Contributions.js.map +1 -0
- package/lib/Credentials.d.ts +33 -0
- package/lib/Credentials.d.ts.map +1 -0
- package/lib/Credentials.js +59 -0
- package/lib/Credentials.js.map +1 -0
- package/lib/Decrypt.d.ts +52 -0
- package/lib/Decrypt.d.ts.map +1 -0
- package/lib/Decrypt.js +3 -0
- package/lib/Decrypt.js.map +1 -0
- package/lib/DecryptHttp.d.ts +22 -0
- package/lib/DecryptHttp.d.ts.map +1 -0
- package/lib/DecryptHttp.js +43 -0
- package/lib/DecryptHttp.js.map +1 -0
- package/lib/DeleteObject.d.ts +29 -0
- package/lib/DeleteObject.d.ts.map +1 -0
- package/lib/DeleteObject.js +3 -0
- package/lib/DeleteObject.js.map +1 -0
- package/lib/DeleteObjectHttp.d.ts +12 -0
- package/lib/DeleteObjectHttp.d.ts.map +1 -0
- package/lib/DeleteObjectHttp.js +17 -0
- package/lib/DeleteObjectHttp.js.map +1 -0
- package/lib/Deployment.d.ts +51 -0
- package/lib/Deployment.d.ts.map +1 -0
- package/lib/Deployment.js +62 -0
- package/lib/Deployment.js.map +1 -0
- package/lib/DeploymentImages.d.ts +18 -0
- package/lib/DeploymentImages.d.ts.map +1 -0
- package/lib/DeploymentImages.js +61 -0
- package/lib/DeploymentImages.js.map +1 -0
- package/lib/DeploymentState.d.ts +17 -0
- package/lib/DeploymentState.d.ts.map +1 -0
- package/lib/DeploymentState.js +87 -0
- package/lib/DeploymentState.js.map +1 -0
- package/lib/Encrypt.d.ts +50 -0
- package/lib/Encrypt.d.ts.map +1 -0
- package/lib/Encrypt.js +3 -0
- package/lib/Encrypt.js.map +1 -0
- package/lib/EncryptHttp.d.ts +22 -0
- package/lib/EncryptHttp.d.ts.map +1 -0
- package/lib/EncryptHttp.js +39 -0
- package/lib/EncryptHttp.js.map +1 -0
- package/lib/Environment.d.ts +45 -0
- package/lib/Environment.d.ts.map +1 -0
- package/lib/Environment.js +43 -0
- package/lib/Environment.js.map +1 -0
- package/lib/Errors.d.ts +18 -0
- package/lib/Errors.d.ts.map +1 -0
- package/lib/Errors.js +13 -0
- package/lib/Errors.js.map +1 -0
- package/lib/Exec.d.ts +41 -0
- package/lib/Exec.d.ts.map +1 -0
- package/lib/Exec.js +3 -0
- package/lib/Exec.js.map +1 -0
- package/lib/ExecHttp.d.ts +22 -0
- package/lib/ExecHttp.d.ts.map +1 -0
- package/lib/ExecHttp.js +39 -0
- package/lib/ExecHttp.js.map +1 -0
- package/lib/GetObject.d.ts +34 -0
- package/lib/GetObject.d.ts.map +1 -0
- package/lib/GetObject.js +3 -0
- package/lib/GetObject.js.map +1 -0
- package/lib/GetObjectHttp.d.ts +12 -0
- package/lib/GetObjectHttp.d.ts.map +1 -0
- package/lib/GetObjectHttp.js +17 -0
- package/lib/GetObjectHttp.js.map +1 -0
- package/lib/GetSecret.d.ts +44 -0
- package/lib/GetSecret.d.ts.map +1 -0
- package/lib/GetSecret.js +3 -0
- package/lib/GetSecret.js.map +1 -0
- package/lib/GetSecretHttp.d.ts +22 -0
- package/lib/GetSecretHttp.d.ts.map +1 -0
- package/lib/GetSecretHttp.js +50 -0
- package/lib/GetSecretHttp.js.map +1 -0
- package/lib/HeadObject.d.ts +29 -0
- package/lib/HeadObject.d.ts.map +1 -0
- package/lib/HeadObject.js +3 -0
- package/lib/HeadObject.js.map +1 -0
- package/lib/HeadObjectHttp.d.ts +12 -0
- package/lib/HeadObjectHttp.d.ts.map +1 -0
- package/lib/HeadObjectHttp.js +17 -0
- package/lib/HeadObjectHttp.js.map +1 -0
- package/lib/IpAssignment.d.ts +258 -0
- package/lib/IpAssignment.d.ts.map +1 -0
- package/lib/IpAssignment.js +376 -0
- package/lib/IpAssignment.js.map +1 -0
- package/lib/ListObjectsV2.d.ts +29 -0
- package/lib/ListObjectsV2.d.ts.map +1 -0
- package/lib/ListObjectsV2.js +3 -0
- package/lib/ListObjectsV2.js.map +1 -0
- package/lib/ListObjectsV2Http.d.ts +12 -0
- package/lib/ListObjectsV2Http.d.ts.map +1 -0
- package/lib/ListObjectsV2Http.js +17 -0
- package/lib/ListObjectsV2Http.js.map +1 -0
- package/lib/ListSecrets.d.ts +53 -0
- package/lib/ListSecrets.d.ts.map +1 -0
- package/lib/ListSecrets.js +3 -0
- package/lib/ListSecrets.js.map +1 -0
- package/lib/ListSecretsHttp.d.ts +22 -0
- package/lib/ListSecretsHttp.d.ts.map +1 -0
- package/lib/ListSecretsHttp.js +35 -0
- package/lib/ListSecretsHttp.js.map +1 -0
- package/lib/Machine.d.ts +802 -0
- package/lib/Machine.d.ts.map +1 -0
- package/lib/Machine.js +773 -0
- package/lib/Machine.js.map +1 -0
- package/lib/MachineContainers.d.ts +44 -0
- package/lib/MachineContainers.d.ts.map +1 -0
- package/lib/MachineContainers.js +115 -0
- package/lib/MachineContainers.js.map +1 -0
- package/lib/Metadata.d.ts +109 -0
- package/lib/Metadata.d.ts.map +1 -0
- package/lib/Metadata.js +159 -0
- package/lib/Metadata.js.map +1 -0
- package/lib/MountVolume.d.ts +295 -0
- package/lib/MountVolume.d.ts.map +1 -0
- package/lib/MountVolume.js +20 -0
- package/lib/MountVolume.js.map +1 -0
- package/lib/Postgres.d.ts +298 -0
- package/lib/Postgres.d.ts.map +1 -0
- package/lib/Postgres.js +508 -0
- package/lib/Postgres.js.map +1 -0
- package/lib/PostgresMigrations.d.ts +35 -0
- package/lib/PostgresMigrations.d.ts.map +1 -0
- package/lib/PostgresMigrations.js +110 -0
- package/lib/PostgresMigrations.js.map +1 -0
- package/lib/Providers.d.ts +38 -0
- package/lib/Providers.d.ts.map +1 -0
- package/lib/Providers.js +95 -0
- package/lib/Providers.js.map +1 -0
- package/lib/PutObject.d.ts +35 -0
- package/lib/PutObject.d.ts.map +1 -0
- package/lib/PutObject.js +3 -0
- package/lib/PutObject.js.map +1 -0
- package/lib/PutObjectHttp.d.ts +12 -0
- package/lib/PutObjectHttp.d.ts.map +1 -0
- package/lib/PutObjectHttp.js +17 -0
- package/lib/PutObjectHttp.js.map +1 -0
- package/lib/ReadRedis.d.ts +26 -0
- package/lib/ReadRedis.d.ts.map +1 -0
- package/lib/ReadRedis.js +3 -0
- package/lib/ReadRedis.js.map +1 -0
- package/lib/ReadRedisHttp.d.ts +11 -0
- package/lib/ReadRedisHttp.d.ts.map +1 -0
- package/lib/ReadRedisHttp.js +15 -0
- package/lib/ReadRedisHttp.js.map +1 -0
- package/lib/ReadWriteRedis.d.ts +29 -0
- package/lib/ReadWriteRedis.d.ts.map +1 -0
- package/lib/ReadWriteRedis.js +3 -0
- package/lib/ReadWriteRedis.js.map +1 -0
- package/lib/ReadWriteRedisHttp.d.ts +11 -0
- package/lib/ReadWriteRedisHttp.d.ts.map +1 -0
- package/lib/ReadWriteRedisHttp.js +15 -0
- package/lib/ReadWriteRedisHttp.js.map +1 -0
- package/lib/Redis.d.ts +252 -0
- package/lib/Redis.d.ts.map +1 -0
- package/lib/Redis.js +608 -0
- package/lib/Redis.js.map +1 -0
- package/lib/RedisBinding.d.ts +7 -0
- package/lib/RedisBinding.d.ts.map +1 -0
- package/lib/RedisBinding.js +53 -0
- package/lib/RedisBinding.js.map +1 -0
- package/lib/RedisHttp.d.ts +8 -0
- package/lib/RedisHttp.d.ts.map +1 -0
- package/lib/RedisHttp.js +8 -0
- package/lib/RedisHttp.js.map +1 -0
- package/lib/Region.d.ts +16 -0
- package/lib/Region.d.ts.map +1 -0
- package/lib/Region.js +13 -0
- package/lib/Region.js.map +1 -0
- package/lib/Secret.d.ts +190 -0
- package/lib/Secret.d.ts.map +1 -0
- package/lib/Secret.js +310 -0
- package/lib/Secret.js.map +1 -0
- package/lib/SecretHttp.d.ts +54 -0
- package/lib/SecretHttp.d.ts.map +1 -0
- package/lib/SecretHttp.js +66 -0
- package/lib/SecretHttp.js.map +1 -0
- package/lib/SecretKey.d.ts +164 -0
- package/lib/SecretKey.d.ts.map +1 -0
- package/lib/SecretKey.js +277 -0
- package/lib/SecretKey.js.map +1 -0
- package/lib/SecretKeyHttp.d.ts +14 -0
- package/lib/SecretKeyHttp.d.ts.map +1 -0
- package/lib/SecretKeyHttp.js +5 -0
- package/lib/SecretKeyHttp.js.map +1 -0
- package/lib/Service.d.ts +1022 -0
- package/lib/Service.d.ts.map +1 -0
- package/lib/Service.js +1397 -0
- package/lib/Service.js.map +1 -0
- package/lib/Sign.d.ts +36 -0
- package/lib/Sign.d.ts.map +1 -0
- package/lib/Sign.js +3 -0
- package/lib/Sign.js.map +1 -0
- package/lib/SignHttp.d.ts +22 -0
- package/lib/SignHttp.d.ts.map +1 -0
- package/lib/SignHttp.js +36 -0
- package/lib/SignHttp.js.map +1 -0
- package/lib/Sprite.d.ts +283 -0
- package/lib/Sprite.d.ts.map +1 -0
- package/lib/Sprite.js +469 -0
- package/lib/Sprite.js.map +1 -0
- package/lib/SpriteHttp.d.ts +25 -0
- package/lib/SpriteHttp.d.ts.map +1 -0
- package/lib/SpriteHttp.js +33 -0
- package/lib/SpriteHttp.js.map +1 -0
- package/lib/Verify.d.ts +37 -0
- package/lib/Verify.d.ts.map +1 -0
- package/lib/Verify.js +3 -0
- package/lib/Verify.js.map +1 -0
- package/lib/VerifyHttp.d.ts +22 -0
- package/lib/VerifyHttp.d.ts.map +1 -0
- package/lib/VerifyHttp.js +38 -0
- package/lib/VerifyHttp.js.map +1 -0
- package/lib/Volume.d.ts +53 -0
- package/lib/Volume.d.ts.map +1 -0
- package/lib/Volume.js +188 -0
- package/lib/Volume.js.map +1 -0
- package/lib/VolumeSnapshot.d.ts +135 -0
- package/lib/VolumeSnapshot.d.ts.map +1 -0
- package/lib/VolumeSnapshot.js +213 -0
- package/lib/VolumeSnapshot.js.map +1 -0
- package/lib/Website/AssetDeployment.d.ts +43 -0
- package/lib/Website/AssetDeployment.d.ts.map +1 -0
- package/lib/Website/AssetDeployment.js +162 -0
- package/lib/Website/AssetDeployment.js.map +1 -0
- package/lib/Website/Astro.d.ts +87 -0
- package/lib/Website/Astro.d.ts.map +1 -0
- package/lib/Website/Astro.js +55 -0
- package/lib/Website/Astro.js.map +1 -0
- package/lib/Website/Foldkit.d.ts +52 -0
- package/lib/Website/Foldkit.d.ts.map +1 -0
- package/lib/Website/Foldkit.js +45 -0
- package/lib/Website/Foldkit.js.map +1 -0
- package/lib/Website/FrameworkSite.d.ts +175 -0
- package/lib/Website/FrameworkSite.d.ts.map +1 -0
- package/lib/Website/FrameworkSite.js +162 -0
- package/lib/Website/FrameworkSite.js.map +1 -0
- package/lib/Website/Nextjs.d.ts +54 -0
- package/lib/Website/Nextjs.d.ts.map +1 -0
- package/lib/Website/Nextjs.js +46 -0
- package/lib/Website/Nextjs.js.map +1 -0
- package/lib/Website/Nuxt.d.ts +51 -0
- package/lib/Website/Nuxt.d.ts.map +1 -0
- package/lib/Website/Nuxt.js +36 -0
- package/lib/Website/Nuxt.js.map +1 -0
- package/lib/Website/Octane.d.ts +49 -0
- package/lib/Website/Octane.d.ts.map +1 -0
- package/lib/Website/Octane.js +39 -0
- package/lib/Website/Octane.js.map +1 -0
- package/lib/Website/ReactRouter.d.ts +80 -0
- package/lib/Website/ReactRouter.d.ts.map +1 -0
- package/lib/Website/ReactRouter.js +70 -0
- package/lib/Website/ReactRouter.js.map +1 -0
- package/lib/Website/SolidStart.d.ts +93 -0
- package/lib/Website/SolidStart.d.ts.map +1 -0
- package/lib/Website/SolidStart.js +71 -0
- package/lib/Website/SolidStart.js.map +1 -0
- package/lib/Website/StaticSite.d.ts +155 -0
- package/lib/Website/StaticSite.d.ts.map +1 -0
- package/lib/Website/StaticSite.js +168 -0
- package/lib/Website/StaticSite.js.map +1 -0
- package/lib/Website/SvelteKit.d.ts +52 -0
- package/lib/Website/SvelteKit.d.ts.map +1 -0
- package/lib/Website/SvelteKit.js +37 -0
- package/lib/Website/SvelteKit.js.map +1 -0
- package/lib/Website/TanStackStart.d.ts +76 -0
- package/lib/Website/TanStackStart.d.ts.map +1 -0
- package/lib/Website/TanStackStart.js +66 -0
- package/lib/Website/TanStackStart.js.map +1 -0
- package/lib/Website/Vinext.d.ts +73 -0
- package/lib/Website/Vinext.d.ts.map +1 -0
- package/lib/Website/Vinext.js +67 -0
- package/lib/Website/Vinext.js.map +1 -0
- package/lib/Website/Vite.d.ts +103 -0
- package/lib/Website/Vite.d.ts.map +1 -0
- package/lib/Website/Vite.js +91 -0
- package/lib/Website/Vite.js.map +1 -0
- package/lib/Website/Vocs.d.ts +52 -0
- package/lib/Website/Vocs.d.ts.map +1 -0
- package/lib/Website/Vocs.js +38 -0
- package/lib/Website/Vocs.js.map +1 -0
- package/lib/Website/Waku.d.ts +64 -0
- package/lib/Website/Waku.d.ts.map +1 -0
- package/lib/Website/Waku.js +39 -0
- package/lib/Website/Waku.js.map +1 -0
- package/lib/Website/index.d.ts +17 -0
- package/lib/Website/index.d.ts.map +1 -0
- package/lib/Website/index.js +17 -0
- package/lib/Website/index.js.map +1 -0
- package/lib/WriteCertificates.d.ts +63 -0
- package/lib/WriteCertificates.d.ts.map +1 -0
- package/lib/WriteCertificates.js +3 -0
- package/lib/WriteCertificates.js.map +1 -0
- package/lib/WriteCertificatesHttp.d.ts +25 -0
- package/lib/WriteCertificatesHttp.d.ts.map +1 -0
- package/lib/WriteCertificatesHttp.js +67 -0
- package/lib/WriteCertificatesHttp.js.map +1 -0
- package/lib/WriteRedis.d.ts +26 -0
- package/lib/WriteRedis.d.ts.map +1 -0
- package/lib/WriteRedis.js +3 -0
- package/lib/WriteRedis.js.map +1 -0
- package/lib/WriteRedisHttp.d.ts +11 -0
- package/lib/WriteRedisHttp.d.ts.map +1 -0
- package/lib/WriteRedisHttp.js +15 -0
- package/lib/WriteRedisHttp.js.map +1 -0
- package/lib/WriteSecret.d.ts +68 -0
- package/lib/WriteSecret.d.ts.map +1 -0
- package/lib/WriteSecret.js +4 -0
- package/lib/WriteSecret.js.map +1 -0
- package/lib/WriteSecretHttp.d.ts +26 -0
- package/lib/WriteSecretHttp.d.ts.map +1 -0
- package/lib/WriteSecretHttp.js +54 -0
- package/lib/WriteSecretHttp.js.map +1 -0
- package/lib/bluegreen.d.ts +36 -0
- package/lib/bluegreen.d.ts.map +1 -0
- package/lib/bluegreen.js +458 -0
- package/lib/bluegreen.js.map +1 -0
- package/lib/hosted.d.ts +172 -0
- package/lib/hosted.d.ts.map +1 -0
- package/lib/hosted.js +458 -0
- package/lib/hosted.js.map +1 -0
- package/lib/index.d.ts +65 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +63 -0
- package/lib/index.js.map +1 -0
- package/lib/internal/RemoteBuilder.d.ts +28 -0
- package/lib/internal/RemoteBuilder.d.ts.map +1 -0
- package/lib/internal/RemoteBuilder.js +145 -0
- package/lib/internal/RemoteBuilder.js.map +1 -0
- package/lib/leases.d.ts +53 -0
- package/lib/leases.d.ts.map +1 -0
- package/lib/leases.js +279 -0
- package/lib/leases.js.map +1 -0
- package/lib/ports.d.ts +51 -0
- package/lib/ports.d.ts.map +1 -0
- package/lib/ports.js +123 -0
- package/lib/ports.js.map +1 -0
- package/lib/replicas.d.ts +270 -0
- package/lib/replicas.d.ts.map +1 -0
- package/lib/replicas.js +969 -0
- package/lib/replicas.js.map +1 -0
- package/lib/rpc.d.ts +35 -0
- package/lib/rpc.d.ts.map +1 -0
- package/lib/rpc.js +113 -0
- package/lib/rpc.js.map +1 -0
- package/package.json +83 -3
- package/src/App.ts +516 -0
- package/src/AuthProvider.ts +68 -0
- package/src/BindService.ts +304 -0
- package/src/Bootstrap.ts +66 -0
- package/src/Bucket.ts +916 -0
- package/src/BucketBinding.ts +128 -0
- package/src/Catalog.ts +43 -0
- package/src/Certificate.ts +548 -0
- package/src/Checkpoint.ts +69 -0
- package/src/CheckpointHttp.ts +66 -0
- package/src/ConnectPostgres.ts +68 -0
- package/src/ConnectPostgresHttp.ts +77 -0
- package/src/Contributions.ts +9 -0
- package/src/Credentials.ts +102 -0
- package/src/Decrypt.ts +62 -0
- package/src/DecryptHttp.ts +52 -0
- package/src/DeleteObject.ts +43 -0
- package/src/DeleteObjectHttp.ts +20 -0
- package/src/Deployment.ts +121 -0
- package/src/DeploymentImages.ts +84 -0
- package/src/DeploymentState.ts +104 -0
- package/src/Encrypt.ts +60 -0
- package/src/EncryptHttp.ts +48 -0
- package/src/Environment.ts +81 -0
- package/src/Errors.ts +14 -0
- package/src/Exec.ts +50 -0
- package/src/ExecHttp.ts +47 -0
- package/src/GetObject.ts +48 -0
- package/src/GetObjectHttp.ts +20 -0
- package/src/GetSecret.ts +48 -0
- package/src/GetSecretHttp.ts +60 -0
- package/src/HeadObject.ts +43 -0
- package/src/HeadObjectHttp.ts +20 -0
- package/src/IpAssignment.ts +522 -0
- package/src/ListObjectsV2.ts +43 -0
- package/src/ListObjectsV2Http.ts +20 -0
- package/src/ListSecrets.ts +57 -0
- package/src/ListSecretsHttp.ts +43 -0
- package/src/Machine.ts +1268 -0
- package/src/MachineContainers.ts +152 -0
- package/src/Metadata.ts +195 -0
- package/src/MountVolume.ts +315 -0
- package/src/Postgres.ts +777 -0
- package/src/PostgresMigrations.ts +153 -0
- package/src/Providers.ts +155 -0
- package/src/PutObject.ts +49 -0
- package/src/PutObjectHttp.ts +20 -0
- package/src/ReadRedis.ts +31 -0
- package/src/ReadRedisHttp.ts +18 -0
- package/src/ReadWriteRedis.ts +33 -0
- package/src/ReadWriteRedisHttp.ts +18 -0
- package/src/Redis.ts +820 -0
- package/src/RedisBinding.ts +64 -0
- package/src/RedisHttp.ts +11 -0
- package/src/Region.ts +44 -0
- package/src/Secret.ts +405 -0
- package/src/SecretHttp.ts +149 -0
- package/src/SecretKey.ts +389 -0
- package/src/SecretKeyHttp.ts +23 -0
- package/src/Service.ts +1934 -0
- package/src/Sign.ts +46 -0
- package/src/SignHttp.ts +44 -0
- package/src/Sprite.ts +671 -0
- package/src/SpriteHttp.ts +50 -0
- package/src/Verify.ts +47 -0
- package/src/VerifyHttp.ts +46 -0
- package/src/Volume.ts +264 -0
- package/src/VolumeSnapshot.ts +321 -0
- package/src/Website/AssetDeployment.ts +294 -0
- package/src/Website/Astro.ts +87 -0
- package/src/Website/Foldkit.ts +54 -0
- package/src/Website/FrameworkSite.ts +363 -0
- package/src/Website/Nextjs.ts +50 -0
- package/src/Website/Nuxt.ts +48 -0
- package/src/Website/Octane.ts +44 -0
- package/src/Website/ReactRouter.ts +76 -0
- package/src/Website/SolidStart.ts +90 -0
- package/src/Website/StaticSite.ts +275 -0
- package/src/Website/SvelteKit.ts +49 -0
- package/src/Website/TanStackStart.ts +72 -0
- package/src/Website/Vinext.ts +79 -0
- package/src/Website/Vite.ts +112 -0
- package/src/Website/Vocs.ts +50 -0
- package/src/Website/Waku.ts +65 -0
- package/src/Website/index.ts +16 -0
- package/src/WriteCertificates.ts +90 -0
- package/src/WriteCertificatesHttp.ts +93 -0
- package/src/WriteRedis.ts +31 -0
- package/src/WriteRedisHttp.ts +18 -0
- package/src/WriteSecret.ts +86 -0
- package/src/WriteSecretHttp.ts +71 -0
- package/src/bluegreen.ts +708 -0
- package/src/globals.d.ts +2 -0
- package/src/hosted.ts +668 -0
- package/src/index.ts +64 -0
- package/src/internal/RemoteBuilder.ts +224 -0
- package/src/leases.ts +437 -0
- package/src/ports.ts +177 -0
- package/src/replicas.ts +1480 -0
- package/src/rpc.ts +136 -0
package/src/Bucket.ts
ADDED
|
@@ -0,0 +1,916 @@
|
|
|
1
|
+
import type { AddOnsResponseEdgesItemNode } from "@distilled.cloud/fly-io/addons";
|
|
2
|
+
import * as addons from "@distilled.cloud/fly-io/addons";
|
|
3
|
+
import * as machines from "@distilled.cloud/fly-io/machines";
|
|
4
|
+
import { Unowned } from "alchemy/AdoptPolicy";
|
|
5
|
+
import { deepEqual, isResolved } from "alchemy/Diff";
|
|
6
|
+
import * as Provider from "alchemy/Provider";
|
|
7
|
+
import { Resource } from "alchemy/Resource";
|
|
8
|
+
import { createInternalTags } from "alchemy/Tags";
|
|
9
|
+
import * as Data from "effect/Data";
|
|
10
|
+
import * as Duration from "effect/Duration";
|
|
11
|
+
import * as Effect from "effect/Effect";
|
|
12
|
+
import * as Redacted from "effect/Redacted";
|
|
13
|
+
import * as Result from "effect/Result";
|
|
14
|
+
import * as Schedule from "effect/Schedule";
|
|
15
|
+
import { resolveOrgSlug } from "./Environment.ts";
|
|
16
|
+
import { TigrisCredentialsMissing } from "./Errors.ts";
|
|
17
|
+
import { createFlyAppName, matchesAlchemyPhysicalName } from "./Metadata.ts";
|
|
18
|
+
import type { Providers } from "./Providers.ts";
|
|
19
|
+
|
|
20
|
+
/** Env-var names Tigris HTTP bindings write onto an App (same as `fly storage create`). */
|
|
21
|
+
export const BUCKET_SECRET_NAMES = [
|
|
22
|
+
"AWS_ACCESS_KEY_ID",
|
|
23
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
24
|
+
"AWS_ENDPOINT_URL_S3",
|
|
25
|
+
"AWS_REGION",
|
|
26
|
+
"BUCKET_NAME",
|
|
27
|
+
] as const;
|
|
28
|
+
|
|
29
|
+
export type BucketSecretName = (typeof BUCKET_SECRET_NAMES)[number];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Shadow bucket used to migrate from another S3-compatible store.
|
|
33
|
+
* Every field is required when set.
|
|
34
|
+
*/
|
|
35
|
+
export interface BucketShadow {
|
|
36
|
+
/** Shadow-bucket access key id. */
|
|
37
|
+
accessKey: string;
|
|
38
|
+
/** Shadow-bucket secret. Wrap with `Redacted.make`. */
|
|
39
|
+
secretKey: Redacted.Redacted<string> | string;
|
|
40
|
+
/** Shadow-bucket region. */
|
|
41
|
+
region: string;
|
|
42
|
+
/** Shadow-bucket name. */
|
|
43
|
+
name: string;
|
|
44
|
+
/** Shadow-bucket S3 endpoint URL. */
|
|
45
|
+
endpoint: string;
|
|
46
|
+
/**
|
|
47
|
+
* Dual-write new objects back to the shadow bucket.
|
|
48
|
+
*
|
|
49
|
+
* @default false
|
|
50
|
+
*/
|
|
51
|
+
writeThrough?: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface BucketProps {
|
|
55
|
+
/**
|
|
56
|
+
* Tigris bucket / add-on name. Globally unique, DNS-compatible
|
|
57
|
+
* (lowercase letters, digits, hyphens), must start with a letter,
|
|
58
|
+
* max 63 characters. If omitted, a unique name is generated from
|
|
59
|
+
* the stack, stage and logical ID. Changing it replaces the Bucket.
|
|
60
|
+
*/
|
|
61
|
+
name?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Organization slug. Defaults to the current token's org
|
|
64
|
+
* (`getCurrentToken`). Changing it replaces the Bucket.
|
|
65
|
+
*/
|
|
66
|
+
orgSlug?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Serve objects without credentials. Default is private.
|
|
69
|
+
*
|
|
70
|
+
* @default false
|
|
71
|
+
*/
|
|
72
|
+
public?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Custom domain for public website hosting. Maps to Tigris
|
|
75
|
+
* `options.website.domain_name`. Empty string when omitted
|
|
76
|
+
* (the Tigris API requires the field).
|
|
77
|
+
*/
|
|
78
|
+
domainName?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Tigris accelerate. Default is off.
|
|
81
|
+
*
|
|
82
|
+
* @default false
|
|
83
|
+
*/
|
|
84
|
+
accelerate?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Shadow bucket for zero-downtime migration from another
|
|
87
|
+
* S3-compatible store. Every nested field is required when set.
|
|
88
|
+
*/
|
|
89
|
+
shadowBucket?: BucketShadow;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export type Bucket = Resource<
|
|
93
|
+
"Fly.Bucket",
|
|
94
|
+
BucketProps,
|
|
95
|
+
{
|
|
96
|
+
/** Fly GraphQL add-on id. Identity of the Bucket. */
|
|
97
|
+
addOnId: string;
|
|
98
|
+
/** Physical Tigris add-on / bucket name. */
|
|
99
|
+
name: string;
|
|
100
|
+
/** Observed provisioning status (`ready`, …). */
|
|
101
|
+
status: string | undefined;
|
|
102
|
+
/** Organization slug. */
|
|
103
|
+
orgSlug: string | undefined;
|
|
104
|
+
/** Whether the bucket is public. */
|
|
105
|
+
public: boolean;
|
|
106
|
+
/** Custom website domain, if set. */
|
|
107
|
+
domainName: string | undefined;
|
|
108
|
+
/** Tigris accelerate. */
|
|
109
|
+
accelerate: boolean;
|
|
110
|
+
/** Tigris SSO dashboard URL, if the API returned one. */
|
|
111
|
+
ssoLink: string | undefined;
|
|
112
|
+
/** RFC3339 creation timestamp. */
|
|
113
|
+
createdAt: string | undefined;
|
|
114
|
+
/**
|
|
115
|
+
* `AWS_ACCESS_KEY_ID` from `createAddOn.environment`. Never logged.
|
|
116
|
+
*/
|
|
117
|
+
accessKeyId: Redacted.Redacted<string> | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* `AWS_SECRET_ACCESS_KEY` from `createAddOn.environment`. Never logged.
|
|
120
|
+
*/
|
|
121
|
+
secretAccessKey: Redacted.Redacted<string> | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* `AWS_ENDPOINT_URL_S3` from `createAddOn.environment`. Never logged.
|
|
124
|
+
*/
|
|
125
|
+
endpoint: Redacted.Redacted<string> | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* `AWS_REGION` from `createAddOn.environment`. Never logged.
|
|
128
|
+
*/
|
|
129
|
+
region: Redacted.Redacted<string> | undefined;
|
|
130
|
+
/**
|
|
131
|
+
* `BUCKET_NAME` from `createAddOn.environment`. Never logged.
|
|
132
|
+
*/
|
|
133
|
+
bucketName: Redacted.Redacted<string> | undefined;
|
|
134
|
+
},
|
|
135
|
+
never,
|
|
136
|
+
Providers
|
|
137
|
+
>;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* A Fly.Bucket is Tigris S3-compatible object storage, billed through
|
|
141
|
+
* Fly. It is an org-scoped GraphQL add-on (`AddOnType` `tigris`).
|
|
142
|
+
*
|
|
143
|
+
* @see https://fly.io/docs/tigris/
|
|
144
|
+
*
|
|
145
|
+
* ### Create a Bucket
|
|
146
|
+
* Alchemy generates a unique name unless you pass one.
|
|
147
|
+
*
|
|
148
|
+
* **Example:** Generated name
|
|
149
|
+
* ```typescript
|
|
150
|
+
* const data = yield* Fly.Bucket("Data");
|
|
151
|
+
* ```
|
|
152
|
+
*
|
|
153
|
+
* ### A stable name
|
|
154
|
+
* Pass `name` when you need a stable Tigris bucket name.
|
|
155
|
+
*
|
|
156
|
+
* **Example:** Explicit name
|
|
157
|
+
* ```typescript
|
|
158
|
+
* const data = yield* Fly.Bucket("Data", {
|
|
159
|
+
* name: "my-bucket",
|
|
160
|
+
* });
|
|
161
|
+
* ```
|
|
162
|
+
*
|
|
163
|
+
* :::caution[Changing `name` replaces the Bucket]
|
|
164
|
+
* Tigris cannot rename a bucket. Alchemy creates the new name, then
|
|
165
|
+
* deletes the old one.
|
|
166
|
+
* :::
|
|
167
|
+
*
|
|
168
|
+
* ### Organization
|
|
169
|
+
* Org defaults to the current token. Pass `orgSlug` to pin it.
|
|
170
|
+
*
|
|
171
|
+
* **Example:** Pin an org
|
|
172
|
+
* ```typescript
|
|
173
|
+
* const data = yield* Fly.Bucket("Data", {
|
|
174
|
+
* orgSlug: "my-org",
|
|
175
|
+
* });
|
|
176
|
+
* ```
|
|
177
|
+
*
|
|
178
|
+
* :::caution[Changing `orgSlug` replaces the Bucket]
|
|
179
|
+
* The bucket is created in the new org. The old bucket is deleted.
|
|
180
|
+
* :::
|
|
181
|
+
*
|
|
182
|
+
* ### Public access
|
|
183
|
+
* Buckets are private by default. `public: true` serves objects
|
|
184
|
+
* without credentials.
|
|
185
|
+
*
|
|
186
|
+
* **Example:** Public bucket
|
|
187
|
+
* ```typescript
|
|
188
|
+
* const assets = yield* Fly.Bucket("Assets", {
|
|
189
|
+
* public: true,
|
|
190
|
+
* });
|
|
191
|
+
* ```
|
|
192
|
+
*
|
|
193
|
+
* ### Custom domain
|
|
194
|
+
* `domainName` sets `website.domain_name` on the add-on. Point a
|
|
195
|
+
* CNAME at `{name}.t3.tigrisbucket.io`.
|
|
196
|
+
*
|
|
197
|
+
* **Example:** Custom domain
|
|
198
|
+
* ```typescript
|
|
199
|
+
* const assets = yield* Fly.Bucket("Assets", {
|
|
200
|
+
* public: true,
|
|
201
|
+
* domainName: "assets.example.com",
|
|
202
|
+
* });
|
|
203
|
+
* ```
|
|
204
|
+
*
|
|
205
|
+
* ### Put and get objects
|
|
206
|
+
* Tigris speaks the S3 API. Bind {@link PutObject} / {@link GetObject}
|
|
207
|
+
* (and the other S3 object ops) in Service init. Alchemy injects the
|
|
208
|
+
* Tigris endpoint and credentials; you never read `AWS_*` yourself.
|
|
209
|
+
*
|
|
210
|
+
* **Example:** Put an object from a Service
|
|
211
|
+
* ```typescript
|
|
212
|
+
* import * as HttpServerResponse from "effect/http/HttpServerResponse";
|
|
213
|
+
*
|
|
214
|
+
* export default class Api extends Fly.Service<Api>()(
|
|
215
|
+
* "Api",
|
|
216
|
+
* { app: Site, main: import.meta.url, port: 3000 },
|
|
217
|
+
* Effect.gen(function* () {
|
|
218
|
+
* const putObject = yield* Fly.PutObject(Data);
|
|
219
|
+
* const getObject = yield* Fly.GetObject(Data);
|
|
220
|
+
*
|
|
221
|
+
* return {
|
|
222
|
+
* fetch: Effect.gen(function* () {
|
|
223
|
+
* yield* putObject({
|
|
224
|
+
* Key: "hello.txt",
|
|
225
|
+
* Body: "hello",
|
|
226
|
+
* ContentType: "text/plain",
|
|
227
|
+
* });
|
|
228
|
+
* const obj = yield* getObject({ Key: "hello.txt" });
|
|
229
|
+
* return HttpServerResponse.json({ ok: obj.ETag !== undefined });
|
|
230
|
+
* }),
|
|
231
|
+
* };
|
|
232
|
+
* }).pipe(Effect.provide([Fly.PutObjectHttp, Fly.GetObjectHttp])),
|
|
233
|
+
* ) {}
|
|
234
|
+
* ```
|
|
235
|
+
*
|
|
236
|
+
* ### Fly.Secret
|
|
237
|
+
* You can also set each key yourself with {@link Secret}.
|
|
238
|
+
*
|
|
239
|
+
* **Example:** Manual secrets
|
|
240
|
+
* ```typescript
|
|
241
|
+
* yield* Fly.Secret("BucketName", {
|
|
242
|
+
* app: Site,
|
|
243
|
+
* name: "BUCKET_NAME",
|
|
244
|
+
* value: Data.bucketName,
|
|
245
|
+
* });
|
|
246
|
+
* ```
|
|
247
|
+
*
|
|
248
|
+
* @resource
|
|
249
|
+
* @product Bucket
|
|
250
|
+
*/
|
|
251
|
+
export const Bucket = Resource<Bucket>("Fly.Bucket");
|
|
252
|
+
|
|
253
|
+
export class BucketNotCreated extends Data.TaggedError("Fly.BucketNotCreated")<{
|
|
254
|
+
name: string;
|
|
255
|
+
}> {}
|
|
256
|
+
|
|
257
|
+
export class BucketOrgMissing extends Data.TaggedError("Fly.BucketOrgMissing")<{
|
|
258
|
+
orgSlug: string;
|
|
259
|
+
}> {}
|
|
260
|
+
|
|
261
|
+
export class BucketProvisionFailed extends Data.TaggedError("Fly.BucketProvisionFailed")<{
|
|
262
|
+
name: string;
|
|
263
|
+
status: string | undefined;
|
|
264
|
+
errorMessage: string | undefined;
|
|
265
|
+
}> {}
|
|
266
|
+
|
|
267
|
+
class BucketPending extends Data.TaggedError("Fly.BucketPending")<{
|
|
268
|
+
name: string;
|
|
269
|
+
status: string;
|
|
270
|
+
}> {}
|
|
271
|
+
|
|
272
|
+
const TIGRIS = "tigris" as const;
|
|
273
|
+
|
|
274
|
+
const backoff = Schedule.min([
|
|
275
|
+
Schedule.exponential(Duration.millis(500), 1.5),
|
|
276
|
+
Schedule.spaced(Duration.seconds(5)),
|
|
277
|
+
]);
|
|
278
|
+
|
|
279
|
+
const sanitizeFlyBucketName = (name: string): string => {
|
|
280
|
+
const lowered = name
|
|
281
|
+
.toLowerCase()
|
|
282
|
+
.replace(/[^a-z0-9-]+/g, "-")
|
|
283
|
+
.replace(/-+/g, "-")
|
|
284
|
+
.replace(/^-+|-+$/g, "");
|
|
285
|
+
const clipped = lowered.length > 63 ? lowered.slice(0, 63).replace(/-+$/g, "") : lowered;
|
|
286
|
+
const raw = clipped.length === 0 ? "f" : clipped;
|
|
287
|
+
return /^[a-z]/.test(raw) ? raw : `f${raw}`.slice(0, 63);
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const resolveBucketName = (id: string, name: string | undefined, existing?: string) =>
|
|
291
|
+
Effect.gen(function* () {
|
|
292
|
+
if (name !== undefined) return sanitizeFlyBucketName(name);
|
|
293
|
+
if (existing !== undefined) return existing;
|
|
294
|
+
return yield* createFlyAppName(id);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const asRecord = (value: unknown): Record<string, unknown> => {
|
|
298
|
+
if (Redacted.isRedacted(value)) {
|
|
299
|
+
return value.pipe(Redacted.value, asRecord);
|
|
300
|
+
}
|
|
301
|
+
if (typeof value === "string") {
|
|
302
|
+
try {
|
|
303
|
+
return asRecord(JSON.parse(value));
|
|
304
|
+
} catch {
|
|
305
|
+
return {};
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
309
|
+
return value as Record<string, unknown>;
|
|
310
|
+
}
|
|
311
|
+
return {};
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const asString = (value: unknown): string | undefined => {
|
|
315
|
+
if (typeof value === "string") {
|
|
316
|
+
return value.length > 0 ? value : undefined;
|
|
317
|
+
}
|
|
318
|
+
if (Redacted.isRedacted(value)) {
|
|
319
|
+
return value.pipe(Redacted.value, asString);
|
|
320
|
+
}
|
|
321
|
+
return undefined;
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
const redact = (value: string | undefined): Redacted.Redacted<string> | undefined =>
|
|
325
|
+
value !== undefined && value.length > 0 ? Redacted.make(value) : undefined;
|
|
326
|
+
|
|
327
|
+
const keepSecret = (
|
|
328
|
+
next: Redacted.Redacted<string> | undefined,
|
|
329
|
+
previous: Redacted.Redacted<string> | undefined,
|
|
330
|
+
): Redacted.Redacted<string> | undefined => next ?? previous;
|
|
331
|
+
|
|
332
|
+
const envString = (env: Record<string, unknown>, key: BucketSecretName): string | undefined =>
|
|
333
|
+
asString(env[key]);
|
|
334
|
+
|
|
335
|
+
type ObservedAddOn = Pick<
|
|
336
|
+
AddOnsResponseEdgesItemNode,
|
|
337
|
+
| "id"
|
|
338
|
+
| "name"
|
|
339
|
+
| "status"
|
|
340
|
+
| "errorMessage"
|
|
341
|
+
| "environment"
|
|
342
|
+
| "options"
|
|
343
|
+
| "metadata"
|
|
344
|
+
| "ssoLink"
|
|
345
|
+
| "createdAt"
|
|
346
|
+
> & {
|
|
347
|
+
organization?: { slug?: string | null; rawSlug?: string } | null;
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const unwrapSecret = (value: Redacted.Redacted<string> | string): string =>
|
|
351
|
+
Redacted.isRedacted(value) ? Redacted.value(value) : value;
|
|
352
|
+
|
|
353
|
+
const desiredOptions = (props: BucketProps): Record<string, unknown> => {
|
|
354
|
+
const options: Record<string, unknown> = {
|
|
355
|
+
public: props.public ?? false,
|
|
356
|
+
accelerate: props.accelerate ?? false,
|
|
357
|
+
website: { domain_name: props.domainName ?? "" },
|
|
358
|
+
};
|
|
359
|
+
if (props.shadowBucket !== undefined) {
|
|
360
|
+
options.shadow_bucket = {
|
|
361
|
+
access_key: props.shadowBucket.accessKey,
|
|
362
|
+
secret_key: unwrapSecret(props.shadowBucket.secretKey),
|
|
363
|
+
region: props.shadowBucket.region,
|
|
364
|
+
name: props.shadowBucket.name,
|
|
365
|
+
endpoint: props.shadowBucket.endpoint,
|
|
366
|
+
write_through: props.shadowBucket.writeThrough ?? false,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
return options;
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const websiteDomain = (options: Record<string, unknown>): string | undefined => {
|
|
373
|
+
const website = asRecord(options.website);
|
|
374
|
+
return asString(website.domain_name);
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const toAttrs = (
|
|
378
|
+
addOn: ObservedAddOn,
|
|
379
|
+
previous?: Bucket["Attributes"],
|
|
380
|
+
fallback?: { name?: string; orgSlug?: string },
|
|
381
|
+
): Bucket["Attributes"] => {
|
|
382
|
+
const options = asRecord(addOn.options);
|
|
383
|
+
const env = asRecord(addOn.environment);
|
|
384
|
+
const name = addOn.name ?? fallback?.name ?? previous?.name ?? "";
|
|
385
|
+
const orgSlug =
|
|
386
|
+
asString(addOn.organization?.slug) ??
|
|
387
|
+
asString(addOn.organization?.rawSlug) ??
|
|
388
|
+
fallback?.orgSlug ??
|
|
389
|
+
previous?.orgSlug;
|
|
390
|
+
return {
|
|
391
|
+
addOnId: addOn.id,
|
|
392
|
+
name,
|
|
393
|
+
status: addOn.status ?? undefined,
|
|
394
|
+
orgSlug,
|
|
395
|
+
public: typeof options.public === "boolean" ? options.public : (previous?.public ?? false),
|
|
396
|
+
domainName: websiteDomain(options),
|
|
397
|
+
accelerate: options.accelerate === true,
|
|
398
|
+
ssoLink: addOn.ssoLink ?? undefined,
|
|
399
|
+
createdAt: addOn.createdAt,
|
|
400
|
+
accessKeyId: keepSecret(redact(envString(env, "AWS_ACCESS_KEY_ID")), previous?.accessKeyId),
|
|
401
|
+
secretAccessKey: keepSecret(
|
|
402
|
+
redact(envString(env, "AWS_SECRET_ACCESS_KEY")),
|
|
403
|
+
previous?.secretAccessKey,
|
|
404
|
+
),
|
|
405
|
+
endpoint: keepSecret(redact(envString(env, "AWS_ENDPOINT_URL_S3")), previous?.endpoint),
|
|
406
|
+
region: keepSecret(redact(envString(env, "AWS_REGION")), previous?.region),
|
|
407
|
+
bucketName: keepSecret(redact(envString(env, "BUCKET_NAME") ?? name), previous?.bucketName),
|
|
408
|
+
};
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
const stringRecordOf = (value: unknown): Record<string, string> =>
|
|
412
|
+
Object.fromEntries(
|
|
413
|
+
Object.entries(asRecord(value)).flatMap(([key, item]) => {
|
|
414
|
+
const text = asString(item);
|
|
415
|
+
return text !== undefined ? [[key, text]] : [];
|
|
416
|
+
}),
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
const hasAlchemyMetadata = (metadata: unknown): boolean => {
|
|
420
|
+
const tags = stringRecordOf(metadata);
|
|
421
|
+
const stack = tags["alchemy::stack"] ?? tags["alchemy.stack"];
|
|
422
|
+
return stack !== undefined && stack.length > 0;
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const isOwnedAddOn = (addOn: ObservedAddOn): boolean =>
|
|
426
|
+
matchesAlchemyPhysicalName(addOn.name ?? undefined) || hasAlchemyMetadata(addOn.metadata);
|
|
427
|
+
|
|
428
|
+
export const listTigrisAddOns = Effect.fn(function* () {
|
|
429
|
+
const rows: AddOnsResponseEdgesItemNode[] = [];
|
|
430
|
+
let after: string | undefined;
|
|
431
|
+
for (let i = 0; i < 8; i++) {
|
|
432
|
+
const page = yield* addons.addOns({
|
|
433
|
+
type: TIGRIS,
|
|
434
|
+
first: 50,
|
|
435
|
+
after,
|
|
436
|
+
});
|
|
437
|
+
for (const edge of page.edges ?? []) {
|
|
438
|
+
if (edge?.node != null) rows.push(edge.node);
|
|
439
|
+
}
|
|
440
|
+
if (!page.pageInfo.hasNextPage) break;
|
|
441
|
+
after = page.pageInfo.endCursor ?? undefined;
|
|
442
|
+
if (after === undefined || after.length === 0) break;
|
|
443
|
+
}
|
|
444
|
+
return rows;
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
const findById = (addOnId: string) =>
|
|
448
|
+
listTigrisAddOns().pipe(Effect.map((addOns) => addOns.find((addOn) => addOn.id === addOnId)));
|
|
449
|
+
|
|
450
|
+
const findByName = (name: string) =>
|
|
451
|
+
listTigrisAddOns().pipe(Effect.map((addOns) => addOns.find((addOn) => addOn.name === name)));
|
|
452
|
+
|
|
453
|
+
const failedStatus = (status: string | undefined) => status === "error" || status === "failed";
|
|
454
|
+
|
|
455
|
+
const pendingStatus = (status: string | undefined) =>
|
|
456
|
+
status === "creating" ||
|
|
457
|
+
status === "provisioning" ||
|
|
458
|
+
status === "pending" ||
|
|
459
|
+
status === "pending_create";
|
|
460
|
+
|
|
461
|
+
const waitUntilReady = (name: string, addOnId: string) =>
|
|
462
|
+
findById(addOnId).pipe(
|
|
463
|
+
Effect.flatMap(
|
|
464
|
+
(
|
|
465
|
+
addOn,
|
|
466
|
+
): Effect.Effect<AddOnsResponseEdgesItemNode, BucketPending | BucketProvisionFailed> => {
|
|
467
|
+
if (addOn === undefined) {
|
|
468
|
+
return Effect.fail(new BucketPending({ name, status: "missing" }));
|
|
469
|
+
}
|
|
470
|
+
const status = addOn.status ?? undefined;
|
|
471
|
+
if (failedStatus(status)) {
|
|
472
|
+
return Effect.fail(
|
|
473
|
+
new BucketProvisionFailed({
|
|
474
|
+
name,
|
|
475
|
+
status,
|
|
476
|
+
errorMessage: addOn.errorMessage ?? undefined,
|
|
477
|
+
}),
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
if (pendingStatus(status)) {
|
|
481
|
+
return Effect.fail(new BucketPending({ name, status: status ?? "creating" }));
|
|
482
|
+
}
|
|
483
|
+
return Effect.succeed(addOn);
|
|
484
|
+
},
|
|
485
|
+
),
|
|
486
|
+
Effect.retry({
|
|
487
|
+
while: (e) => e._tag === "Fly.BucketPending",
|
|
488
|
+
times: 8,
|
|
489
|
+
schedule: backoff,
|
|
490
|
+
}),
|
|
491
|
+
Effect.catchTag("Fly.BucketPending", () => findById(addOnId)),
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
const waitUntilGone = (addOnId: string, name: string) =>
|
|
495
|
+
Effect.gen(function* () {
|
|
496
|
+
// Best-effort: Tigris often answers "The specified bucket does not
|
|
497
|
+
// exist" (untyped UnknownFlyIoError) for a delete that already
|
|
498
|
+
// succeeded. Never fail the wait on that — only on the add-on still
|
|
499
|
+
// showing up in list.
|
|
500
|
+
yield* Effect.result(
|
|
501
|
+
addons.deleteAddOn({
|
|
502
|
+
input: addOnId.length > 0 ? { addOnId } : { name, provider: TIGRIS },
|
|
503
|
+
}),
|
|
504
|
+
);
|
|
505
|
+
const still =
|
|
506
|
+
(yield* findById(addOnId)) ?? (name.length > 0 ? yield* findByName(name) : undefined);
|
|
507
|
+
if (still !== undefined) {
|
|
508
|
+
return yield* new BucketPending({
|
|
509
|
+
name,
|
|
510
|
+
status: still.status ?? "deleting",
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
}).pipe(
|
|
514
|
+
Effect.retry({
|
|
515
|
+
while: (e) => e._tag === "Fly.BucketPending",
|
|
516
|
+
schedule: Schedule.spaced("2 seconds"),
|
|
517
|
+
times: 20,
|
|
518
|
+
}),
|
|
519
|
+
);
|
|
520
|
+
|
|
521
|
+
const resolveOrganization = (orgSlug: string) =>
|
|
522
|
+
addons
|
|
523
|
+
.organization({ slug: orgSlug })
|
|
524
|
+
.pipe(Effect.catchTag("FlyIoParseError", () => Effect.succeed(undefined)));
|
|
525
|
+
|
|
526
|
+
const ensureTos = (orgSlug: string, organizationId: string) =>
|
|
527
|
+
Effect.gen(function* () {
|
|
528
|
+
const agreed = yield* addons.agreedToProviderTos({
|
|
529
|
+
slug: orgSlug,
|
|
530
|
+
providerName: TIGRIS,
|
|
531
|
+
});
|
|
532
|
+
if (agreed === true) return;
|
|
533
|
+
const tos = yield* Effect.result(
|
|
534
|
+
addons.createExtensionTosAgreement({
|
|
535
|
+
input: {
|
|
536
|
+
addOnProviderName: TIGRIS,
|
|
537
|
+
organizationId,
|
|
538
|
+
},
|
|
539
|
+
}),
|
|
540
|
+
);
|
|
541
|
+
if (Result.isFailure(tos)) return;
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
const optionsEqual = (
|
|
545
|
+
observed: Record<string, unknown>,
|
|
546
|
+
desired: Record<string, unknown>,
|
|
547
|
+
): boolean => {
|
|
548
|
+
const observedPublic = observed.public === true;
|
|
549
|
+
const desiredPublic = desired.public === true;
|
|
550
|
+
const observedAccelerate = observed.accelerate === true;
|
|
551
|
+
const desiredAccelerate = desired.accelerate === true;
|
|
552
|
+
const observedDomain = websiteDomain(observed) ?? "";
|
|
553
|
+
const desiredDomain = websiteDomain(desired) ?? "";
|
|
554
|
+
if (
|
|
555
|
+
observedPublic !== desiredPublic ||
|
|
556
|
+
observedAccelerate !== desiredAccelerate ||
|
|
557
|
+
observedDomain !== desiredDomain
|
|
558
|
+
) {
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
561
|
+
if (desired.shadow_bucket !== undefined) {
|
|
562
|
+
return deepEqual(asRecord(observed.shadow_bucket), desired.shadow_bucket);
|
|
563
|
+
}
|
|
564
|
+
return true;
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
export const BucketProvider = () =>
|
|
568
|
+
Provider.succeed(Bucket, {
|
|
569
|
+
stables: ["addOnId", "name", "orgSlug"],
|
|
570
|
+
|
|
571
|
+
diff: Effect.fn(function* ({ news, output }) {
|
|
572
|
+
if (news === undefined || !isResolved(news)) return undefined;
|
|
573
|
+
if (output === undefined) return undefined;
|
|
574
|
+
const desiredName = news.name !== undefined ? sanitizeFlyBucketName(news.name) : output.name;
|
|
575
|
+
const nameChanged = desiredName !== output.name;
|
|
576
|
+
const orgChanged = news.orgSlug !== undefined && news.orgSlug !== output.orgSlug;
|
|
577
|
+
if (nameChanged || orgChanged) {
|
|
578
|
+
return { action: "replace" as const };
|
|
579
|
+
}
|
|
580
|
+
return undefined;
|
|
581
|
+
}),
|
|
582
|
+
|
|
583
|
+
read: Effect.fn(function* ({ id, olds, output }) {
|
|
584
|
+
const name = yield* resolveBucketName(id, olds?.name, output?.name);
|
|
585
|
+
const found =
|
|
586
|
+
(output?.addOnId !== undefined && output.addOnId.length > 0
|
|
587
|
+
? yield* findById(output.addOnId)
|
|
588
|
+
: undefined) ?? (yield* findByName(name));
|
|
589
|
+
if (found === undefined) return undefined;
|
|
590
|
+
const attrs = toAttrs(found, output, {
|
|
591
|
+
name,
|
|
592
|
+
orgSlug: output?.orgSlug ?? olds?.orgSlug,
|
|
593
|
+
});
|
|
594
|
+
if (output !== undefined) return attrs;
|
|
595
|
+
return isOwnedAddOn(found) ? attrs : Unowned(attrs);
|
|
596
|
+
}),
|
|
597
|
+
|
|
598
|
+
list: Effect.fn(function* () {
|
|
599
|
+
const addOns = yield* listTigrisAddOns();
|
|
600
|
+
return addOns.flatMap((addOn) => (isOwnedAddOn(addOn) ? [toAttrs(addOn)] : []));
|
|
601
|
+
}),
|
|
602
|
+
|
|
603
|
+
reconcile: Effect.fn(function* ({ id, news, output }) {
|
|
604
|
+
const props = news ?? ({} as BucketProps);
|
|
605
|
+
const name = yield* resolveBucketName(id, props.name, output?.name);
|
|
606
|
+
const orgSlug = props.orgSlug ?? output?.orgSlug ?? (yield* resolveOrgSlug());
|
|
607
|
+
let previous = output;
|
|
608
|
+
|
|
609
|
+
// Observe by cached id, then the desired name.
|
|
610
|
+
let current: ObservedAddOn | undefined =
|
|
611
|
+
output?.addOnId !== undefined && output.addOnId.length > 0
|
|
612
|
+
? yield* findById(output.addOnId)
|
|
613
|
+
: undefined;
|
|
614
|
+
if (current === undefined) {
|
|
615
|
+
current = yield* findByName(name);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const org = yield* resolveOrganization(orgSlug);
|
|
619
|
+
if (org === undefined) {
|
|
620
|
+
return yield* new BucketOrgMissing({ orgSlug });
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (current === undefined) {
|
|
624
|
+
yield* ensureTos(orgSlug, org.id);
|
|
625
|
+
const created = yield* Effect.result(
|
|
626
|
+
addons.createAddOn({
|
|
627
|
+
input: {
|
|
628
|
+
type: TIGRIS,
|
|
629
|
+
name,
|
|
630
|
+
organizationId: org.id,
|
|
631
|
+
options: desiredOptions(props),
|
|
632
|
+
},
|
|
633
|
+
}),
|
|
634
|
+
);
|
|
635
|
+
if (Result.isSuccess(created)) {
|
|
636
|
+
current = created.success.addOn;
|
|
637
|
+
} else {
|
|
638
|
+
current = yield* findByName(name);
|
|
639
|
+
if (current === undefined) {
|
|
640
|
+
return yield* created.failure;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
const createdSecrets = current;
|
|
644
|
+
if (current.id.length > 0) {
|
|
645
|
+
current = (yield* waitUntilReady(name, current.id)) ?? current;
|
|
646
|
+
}
|
|
647
|
+
// List/get omit `environment` after create. Keep the create
|
|
648
|
+
// payload as the secrets source for keepSecret below.
|
|
649
|
+
previous = toAttrs(createdSecrets, previous, { name, orgSlug });
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if (current === undefined || current.id.length === 0) {
|
|
653
|
+
return yield* new BucketNotCreated({ name });
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const status = current.status ?? undefined;
|
|
657
|
+
if (failedStatus(status)) {
|
|
658
|
+
return yield* new BucketProvisionFailed({
|
|
659
|
+
name,
|
|
660
|
+
status,
|
|
661
|
+
errorMessage: current.errorMessage ?? undefined,
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const observedOptions = asRecord(current.options);
|
|
666
|
+
const desired = desiredOptions(props);
|
|
667
|
+
const internal = yield* createInternalTags(id);
|
|
668
|
+
const observedMeta = stringRecordOf(current.metadata);
|
|
669
|
+
const nextMeta = { ...observedMeta, ...internal };
|
|
670
|
+
const optionsChanged = !optionsEqual(observedOptions, desired);
|
|
671
|
+
const metaChanged = !deepEqual(observedMeta, nextMeta);
|
|
672
|
+
if (optionsChanged || metaChanged) {
|
|
673
|
+
const nextOptions = {
|
|
674
|
+
...observedOptions,
|
|
675
|
+
...desired,
|
|
676
|
+
};
|
|
677
|
+
const updated = yield* Effect.result(
|
|
678
|
+
addons.updateAddOn({
|
|
679
|
+
input: {
|
|
680
|
+
addOnId: current.id,
|
|
681
|
+
options: optionsChanged ? nextOptions : undefined,
|
|
682
|
+
metadata: metaChanged ? nextMeta : undefined,
|
|
683
|
+
},
|
|
684
|
+
}),
|
|
685
|
+
);
|
|
686
|
+
if (Result.isSuccess(updated)) {
|
|
687
|
+
current = {
|
|
688
|
+
...current,
|
|
689
|
+
...updated.success.addOn,
|
|
690
|
+
};
|
|
691
|
+
} else {
|
|
692
|
+
current = (yield* findById(current.id)) ?? current;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const latest = (yield* findById(current.id)) ?? current;
|
|
697
|
+
// GraphQL list/get omit `environment` after create. Keep credentials
|
|
698
|
+
// from the create payload (or last persisted attrs) via keepSecret.
|
|
699
|
+
const attrs = toAttrs(latest, toAttrs(current, previous, { name, orgSlug }), {
|
|
700
|
+
name,
|
|
701
|
+
orgSlug,
|
|
702
|
+
});
|
|
703
|
+
const observedPublic = asRecord(latest.options).public;
|
|
704
|
+
return {
|
|
705
|
+
...attrs,
|
|
706
|
+
public:
|
|
707
|
+
typeof observedPublic === "boolean" ? observedPublic : (props.public ?? attrs.public),
|
|
708
|
+
domainName: websiteDomain(asRecord(latest.options)) ?? props.domainName ?? attrs.domainName,
|
|
709
|
+
accelerate:
|
|
710
|
+
typeof asRecord(latest.options).accelerate === "boolean"
|
|
711
|
+
? (asRecord(latest.options).accelerate as boolean)
|
|
712
|
+
: (props.accelerate ?? attrs.accelerate),
|
|
713
|
+
};
|
|
714
|
+
}),
|
|
715
|
+
|
|
716
|
+
delete: Effect.fn(function* ({ output }) {
|
|
717
|
+
const addOnId = output.addOnId;
|
|
718
|
+
const name = output.name;
|
|
719
|
+
if (addOnId.length === 0 && name.length === 0) return;
|
|
720
|
+
// Always wait for the add-on to leave the list. A failed
|
|
721
|
+
// deleteAddOn plus a single list miss is not gone — Tigris
|
|
722
|
+
// list lags, and a bucket still attached to an App is a
|
|
723
|
+
// transient error. waitUntilGone retries delete and only
|
|
724
|
+
// succeeds once list (id and name) is empty.
|
|
725
|
+
yield* waitUntilGone(addOnId, name);
|
|
726
|
+
}),
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
class BucketSecretVersionMissing extends Data.TaggedError("Fly.BucketSecretVersionMissing")<{
|
|
730
|
+
appName: string;
|
|
731
|
+
}> {}
|
|
732
|
+
|
|
733
|
+
const bucketSecretVersion = (
|
|
734
|
+
appName: string,
|
|
735
|
+
response: machines.AppSecretsUpdateResp | machines.SetAppSecretResponse,
|
|
736
|
+
) => {
|
|
737
|
+
const version = response.version ?? response.Version;
|
|
738
|
+
return version !== undefined && Number.isSafeInteger(version) && version >= 0
|
|
739
|
+
? Effect.succeed(version)
|
|
740
|
+
: Effect.fail(new BucketSecretVersionMissing({ appName }));
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
const requireBucketSecrets = Effect.fn(function* (appName: string, values: Record<string, string>) {
|
|
744
|
+
const required = [
|
|
745
|
+
"BUCKET_NAME",
|
|
746
|
+
"AWS_ACCESS_KEY_ID",
|
|
747
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
748
|
+
"AWS_ENDPOINT_URL_S3",
|
|
749
|
+
];
|
|
750
|
+
if (required.some((key) => !values[key]?.trim())) {
|
|
751
|
+
return yield* new TigrisCredentialsMissing({
|
|
752
|
+
name: values.BUCKET_NAME || appName,
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
const putSecretValues = (appName: string, values: Record<string, string>) =>
|
|
758
|
+
Effect.gen(function* () {
|
|
759
|
+
yield* requireBucketSecrets(appName, values);
|
|
760
|
+
return yield* machines
|
|
761
|
+
.updateSecrets({
|
|
762
|
+
app_name: appName,
|
|
763
|
+
values,
|
|
764
|
+
})
|
|
765
|
+
.pipe(
|
|
766
|
+
Effect.flatMap((response) => bucketSecretVersion(appName, response)),
|
|
767
|
+
Effect.catchTag("NotFound", () =>
|
|
768
|
+
Effect.gen(function* () {
|
|
769
|
+
const versions: number[] = [];
|
|
770
|
+
for (const [secretName, value] of Object.entries(values)) {
|
|
771
|
+
const version = yield* machines
|
|
772
|
+
.createSecret({
|
|
773
|
+
app_name: appName,
|
|
774
|
+
secret_name: secretName,
|
|
775
|
+
value,
|
|
776
|
+
})
|
|
777
|
+
.pipe(
|
|
778
|
+
Effect.flatMap((response) => bucketSecretVersion(appName, response)),
|
|
779
|
+
Effect.catchTag("Conflict", () =>
|
|
780
|
+
machines
|
|
781
|
+
.updateSecrets({
|
|
782
|
+
app_name: appName,
|
|
783
|
+
values: { [secretName]: value },
|
|
784
|
+
})
|
|
785
|
+
.pipe(Effect.flatMap((response) => bucketSecretVersion(appName, response))),
|
|
786
|
+
),
|
|
787
|
+
);
|
|
788
|
+
versions.push(version);
|
|
789
|
+
}
|
|
790
|
+
return Math.max(...versions);
|
|
791
|
+
}),
|
|
792
|
+
),
|
|
793
|
+
);
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
const envValuesOf = (addOn: ObservedAddOn): Record<string, string> => {
|
|
797
|
+
const env = asRecord(addOn.environment);
|
|
798
|
+
const values: Record<string, string> = {};
|
|
799
|
+
for (const key of BUCKET_SECRET_NAMES) {
|
|
800
|
+
const value = envString(env, key);
|
|
801
|
+
if (value !== undefined) values[key] = value;
|
|
802
|
+
}
|
|
803
|
+
if (values.BUCKET_NAME === undefined && addOn.name != null) {
|
|
804
|
+
values.BUCKET_NAME = addOn.name;
|
|
805
|
+
}
|
|
806
|
+
if (values.AWS_ENDPOINT_URL === undefined && values.AWS_ENDPOINT_URL_S3 !== undefined) {
|
|
807
|
+
values.AWS_ENDPOINT_URL = values.AWS_ENDPOINT_URL_S3;
|
|
808
|
+
}
|
|
809
|
+
return values;
|
|
810
|
+
};
|
|
811
|
+
|
|
812
|
+
const findAttachedBucket = (input: { id?: string; name: string }) =>
|
|
813
|
+
Effect.gen(function* () {
|
|
814
|
+
const listed =
|
|
815
|
+
input.id !== undefined && input.id.length > 0
|
|
816
|
+
? ((yield* findById(input.id)) ??
|
|
817
|
+
(input.name.length > 0 ? yield* findByName(input.name) : undefined))
|
|
818
|
+
: input.name.length > 0
|
|
819
|
+
? yield* findByName(input.name)
|
|
820
|
+
: undefined;
|
|
821
|
+
if (listed === undefined) {
|
|
822
|
+
return yield* new BucketPending({
|
|
823
|
+
name: input.name || input.id || "",
|
|
824
|
+
status: "missing",
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
const detail = yield* addons
|
|
828
|
+
.addOn({
|
|
829
|
+
id: listed.id,
|
|
830
|
+
name: listed.name ?? input.name,
|
|
831
|
+
provider: TIGRIS,
|
|
832
|
+
})
|
|
833
|
+
.pipe(Effect.catchTag("FlyIoParseError", () => Effect.succeed(undefined)));
|
|
834
|
+
return detail ?? listed;
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
const secretsFromBoundEnv = (env: Record<string, string>): Record<string, string> => {
|
|
838
|
+
const values: Record<string, string> = {};
|
|
839
|
+
for (const key of BUCKET_SECRET_NAMES) {
|
|
840
|
+
const value = env[key];
|
|
841
|
+
if (value !== undefined && value.length > 0) values[key] = value;
|
|
842
|
+
}
|
|
843
|
+
if (values.AWS_ENDPOINT_URL === undefined && values.AWS_ENDPOINT_URL_S3 !== undefined) {
|
|
844
|
+
values.AWS_ENDPOINT_URL = values.AWS_ENDPOINT_URL_S3;
|
|
845
|
+
}
|
|
846
|
+
return values;
|
|
847
|
+
};
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Write Tigris `AWS_*` / `BUCKET_NAME` secrets onto an App. Called from
|
|
851
|
+
* {@link Service} reconcile so the secrets exist before Machines boot.
|
|
852
|
+
*
|
|
853
|
+
* Looks up the add-on by id/name (retrying while Tigris is still
|
|
854
|
+
* provisioning) and also copies any already-evaluated `AWS_*` /
|
|
855
|
+
* `BUCKET_NAME` values from binding env — GraphQL `environment` is often
|
|
856
|
+
* empty after create. A missing add-on may use bound values only when the
|
|
857
|
+
* bucket name, credential pair, and S3 endpoint are complete. Partial values
|
|
858
|
+
* are never written. Region is optional, as in the Tigris bindings.
|
|
859
|
+
* Without an attachment, only an explicit `BUCKET_NAME` requests this mode;
|
|
860
|
+
* ordinary AWS region or credential settings do not imply a Bucket.
|
|
861
|
+
* Returns the highest accepted secret-version floor, or `undefined` when
|
|
862
|
+
* no Bucket was requested; this is not a vault snapshot.
|
|
863
|
+
*/
|
|
864
|
+
export const attachBucketSecrets = Effect.fn(function* (
|
|
865
|
+
appName: string,
|
|
866
|
+
attached: readonly {
|
|
867
|
+
name: string;
|
|
868
|
+
id?: string;
|
|
869
|
+
/** Credentials from the bound Bucket's attributes. */
|
|
870
|
+
values?: Record<string, string>;
|
|
871
|
+
}[],
|
|
872
|
+
boundEnv: Record<string, string> = {},
|
|
873
|
+
) {
|
|
874
|
+
if (appName.length === 0) return undefined;
|
|
875
|
+
if (attached.length === 0 && boundEnv.BUCKET_NAME === undefined) {
|
|
876
|
+
return undefined;
|
|
877
|
+
}
|
|
878
|
+
const fromEnv = secretsFromBoundEnv(boundEnv);
|
|
879
|
+
if (attached.length === 0) {
|
|
880
|
+
return yield* putSecretValues(appName, fromEnv);
|
|
881
|
+
}
|
|
882
|
+
const versions: number[] = [];
|
|
883
|
+
for (const item of attached) {
|
|
884
|
+
const name = item.name;
|
|
885
|
+
const id = item.id;
|
|
886
|
+
const fallbackValues: Record<string, string> = {
|
|
887
|
+
...fromEnv,
|
|
888
|
+
...item.values,
|
|
889
|
+
};
|
|
890
|
+
if (
|
|
891
|
+
fallbackValues.AWS_ENDPOINT_URL === undefined &&
|
|
892
|
+
fallbackValues.AWS_ENDPOINT_URL_S3 !== undefined
|
|
893
|
+
) {
|
|
894
|
+
fallbackValues.AWS_ENDPOINT_URL = fallbackValues.AWS_ENDPOINT_URL_S3;
|
|
895
|
+
}
|
|
896
|
+
if (fallbackValues.BUCKET_NAME === undefined && name.length > 0) {
|
|
897
|
+
fallbackValues.BUCKET_NAME = name;
|
|
898
|
+
}
|
|
899
|
+
const row = yield* findAttachedBucket({ id, name }).pipe(
|
|
900
|
+
Effect.retry({
|
|
901
|
+
while: (error) => error._tag === "Fly.BucketPending",
|
|
902
|
+
times: 8,
|
|
903
|
+
schedule: backoff,
|
|
904
|
+
}),
|
|
905
|
+
Effect.catchTag("Fly.BucketPending", () =>
|
|
906
|
+
requireBucketSecrets(appName, fallbackValues).pipe(Effect.as(undefined)),
|
|
907
|
+
),
|
|
908
|
+
);
|
|
909
|
+
const values = {
|
|
910
|
+
...fallbackValues,
|
|
911
|
+
...(row === undefined ? {} : envValuesOf(row)),
|
|
912
|
+
};
|
|
913
|
+
versions.push(yield* putSecretValues(appName, values));
|
|
914
|
+
}
|
|
915
|
+
return versions.length > 0 ? Math.max(...versions) : undefined;
|
|
916
|
+
});
|