@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
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Url } from "@alchemy.run/redis";
|
|
2
|
+
import * as Binding from "alchemy/Binding";
|
|
3
|
+
import type { Resource } from "alchemy/Resource";
|
|
4
|
+
import * as Config from "effect/Config";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Redacted from "effect/Redacted";
|
|
7
|
+
import type { ServiceBinding } from "./MountVolume.ts";
|
|
8
|
+
import { REDIS_URL_ENV, RedisUrlMissing, type Redis } from "./Redis.ts";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Shared scaffolding for Upstash Redis bindings.
|
|
12
|
+
*
|
|
13
|
+
* Each `{Op}Http.ts` is a thin `Layer.effect` over {@link makeRedisBinding}.
|
|
14
|
+
* Deploy-time registers the Redis add-on on the host so Service reconcile
|
|
15
|
+
* can attach it. The URL travels as `yield* redis.url` (RuntimeContext
|
|
16
|
+
* set/get). `REDIS_URL` written as an App secret is a fallback, not the
|
|
17
|
+
* primary path.
|
|
18
|
+
*
|
|
19
|
+
* The RESP client lives in `@alchemy.run/redis`. This file only wires the
|
|
20
|
+
* Fly host binding.
|
|
21
|
+
*
|
|
22
|
+
* NOT exported from `index.ts`.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const isFlyHost = (value: unknown): value is Resource<string, any, any, ServiceBinding> =>
|
|
26
|
+
typeof value === "object" &&
|
|
27
|
+
value !== null &&
|
|
28
|
+
((value as { Type?: string }).Type === "Fly.Service" ||
|
|
29
|
+
(value as { Type?: string }).Type === "Fly.Machine");
|
|
30
|
+
|
|
31
|
+
const asPlain = (value: unknown): string | undefined => {
|
|
32
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
33
|
+
if (Redacted.isRedacted(value)) return value.pipe(Redacted.value, asPlain);
|
|
34
|
+
return undefined;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const redisUrlFromEnv = Config.Redacted(REDIS_URL_ENV).pipe(
|
|
38
|
+
Effect.map((value) => Redacted.value(value)),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
export const makeRedisBinding = <Client>(options: { makeClient: (url: Url) => Client }) =>
|
|
42
|
+
Effect.succeed(
|
|
43
|
+
Effect.fn(function* (redis: Redis) {
|
|
44
|
+
if (!globalThis.__ALCHEMY_RUNTIME__) {
|
|
45
|
+
const host = yield* Binding.Host;
|
|
46
|
+
if (isFlyHost(host)) {
|
|
47
|
+
yield* host.bind`${redis}`({
|
|
48
|
+
redis: { name: redis.name, id: redis.redisId },
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const urlFromResource = yield* redis.url;
|
|
54
|
+
const missing = new RedisUrlMissing({
|
|
55
|
+
name: asPlain(redis.name) ?? redis.LogicalId,
|
|
56
|
+
});
|
|
57
|
+
const url: Url = Effect.gen(function* () {
|
|
58
|
+
const fromOutput = asPlain(yield* urlFromResource);
|
|
59
|
+
if (fromOutput !== undefined) return fromOutput;
|
|
60
|
+
return yield* redisUrlFromEnv.pipe(Effect.mapError(() => missing));
|
|
61
|
+
});
|
|
62
|
+
return options.makeClient(url);
|
|
63
|
+
}),
|
|
64
|
+
);
|
package/src/RedisHttp.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Redis command-client builders. NOT exported from `index.ts`.
|
|
3
|
+
*
|
|
4
|
+
* The RESP client lives in `@alchemy.run/redis`. These aliases keep the
|
|
5
|
+
* `{Op}Http.ts` layers stable.
|
|
6
|
+
*/
|
|
7
|
+
export {
|
|
8
|
+
makeRead as makeReadRedisClient,
|
|
9
|
+
makeReadWrite as makeReadWriteRedisClient,
|
|
10
|
+
makeWrite as makeWriteRedisClient,
|
|
11
|
+
} from "@alchemy.run/redis";
|
package/src/Region.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Fly.io region code. The literal codes are the regions Fly listed in
|
|
3
|
+
* September 2026 and give editor completion; any other string is accepted
|
|
4
|
+
* so new regions work without an Alchemy release. `bom` is deprecated by
|
|
5
|
+
* Fly. See [Regions](/fly/compute/regions).
|
|
6
|
+
*/
|
|
7
|
+
export type Region =
|
|
8
|
+
| "ams"
|
|
9
|
+
| "arn"
|
|
10
|
+
| "bom"
|
|
11
|
+
| "cdg"
|
|
12
|
+
| "dfw"
|
|
13
|
+
| "ewr"
|
|
14
|
+
| "fra"
|
|
15
|
+
| "gru"
|
|
16
|
+
| "iad"
|
|
17
|
+
| "jnb"
|
|
18
|
+
| "lax"
|
|
19
|
+
| "lhr"
|
|
20
|
+
| "nrt"
|
|
21
|
+
| "ord"
|
|
22
|
+
| "sin"
|
|
23
|
+
| "sjc"
|
|
24
|
+
| "syd"
|
|
25
|
+
| "yyz"
|
|
26
|
+
| (string & {});
|
|
27
|
+
|
|
28
|
+
/** Normalize a `Region | Region[]` prop into a de-duplicated list. */
|
|
29
|
+
export const regionList = (
|
|
30
|
+
region: Region | readonly Region[] | undefined,
|
|
31
|
+
fallback: string,
|
|
32
|
+
): string[] => {
|
|
33
|
+
const list = region === undefined ? [fallback] : [region].flat();
|
|
34
|
+
const unique = [...new Set(list)];
|
|
35
|
+
return unique.length === 0 ? [fallback] : unique;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Region of replica `index` when `count` replicas run in each of
|
|
40
|
+
* `regions`. Replicas are spread round-robin, so growing `count` keeps
|
|
41
|
+
* every existing replica in its region.
|
|
42
|
+
*/
|
|
43
|
+
export const regionOfReplica = (regions: readonly string[], index: number) =>
|
|
44
|
+
regions[index % regions.length]!;
|
package/src/Secret.ts
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import type { AppSecret } from "@distilled.cloud/fly-io/machines";
|
|
3
|
+
import * as machines from "@distilled.cloud/fly-io/machines";
|
|
4
|
+
import { Unowned } from "alchemy/AdoptPolicy";
|
|
5
|
+
import { isResolved } from "alchemy/Diff";
|
|
6
|
+
import * as Provider from "alchemy/Provider";
|
|
7
|
+
import { Resource } from "alchemy/Resource";
|
|
8
|
+
import * as Data from "effect/Data";
|
|
9
|
+
import * as Effect from "effect/Effect";
|
|
10
|
+
import * as Redacted from "effect/Redacted";
|
|
11
|
+
import * as Schedule from "effect/Schedule";
|
|
12
|
+
import { App, listOwnedApps } from "./App.ts";
|
|
13
|
+
import { createFlyVolumeName, matchesAlchemyPhysicalName } from "./Metadata.ts";
|
|
14
|
+
import type { Providers } from "./Providers.ts";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A resource-valued prop: the resource itself, or an Effect that produces
|
|
18
|
+
* it (so `yield* App(...)` and `App(...)` both type-check).
|
|
19
|
+
*/
|
|
20
|
+
type Ref<T> = T | Effect.Effect<T, never, Providers>;
|
|
21
|
+
|
|
22
|
+
export interface SecretProps {
|
|
23
|
+
/**
|
|
24
|
+
* Parent Fly App. Changing it replaces the secret.
|
|
25
|
+
*/
|
|
26
|
+
app: Ref<App>;
|
|
27
|
+
/**
|
|
28
|
+
* Secret name. Used as the Machine env-var name when set by the user
|
|
29
|
+
* (case-sensitive, stored as-is). If omitted, a unique name is generated
|
|
30
|
+
* from the stack, stage and logical ID (the ownership stamp). Changing
|
|
31
|
+
* it replaces the secret.
|
|
32
|
+
*/
|
|
33
|
+
name?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Secret value. Wrap with `Redacted.make(...)` so it is never logged.
|
|
36
|
+
* Updated in place via `updateSecrets`. Never persisted in attributes.
|
|
37
|
+
*/
|
|
38
|
+
value: Redacted.Redacted<string> | string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type Secret = Resource<
|
|
42
|
+
"Fly.Secret",
|
|
43
|
+
SecretProps,
|
|
44
|
+
{
|
|
45
|
+
/** Parent Fly App name. */
|
|
46
|
+
appName: string;
|
|
47
|
+
/** Secret name (unique per App). */
|
|
48
|
+
name: string;
|
|
49
|
+
/** Fly digest of the current value. Not the plaintext. */
|
|
50
|
+
digest: string | undefined;
|
|
51
|
+
/** RFC3339 creation timestamp. */
|
|
52
|
+
createdAt: string | undefined;
|
|
53
|
+
/** RFC3339 last-update timestamp. */
|
|
54
|
+
updatedAt: string | undefined;
|
|
55
|
+
},
|
|
56
|
+
never,
|
|
57
|
+
Providers
|
|
58
|
+
>;
|
|
59
|
+
|
|
60
|
+
const resolveSecretProps = (
|
|
61
|
+
props: SecretProps | Effect.Effect<SecretProps, never, Providers>,
|
|
62
|
+
): Effect.Effect<SecretProps, never, Providers> =>
|
|
63
|
+
Effect.gen(function* () {
|
|
64
|
+
const resolved = Effect.isEffect(props) ? yield* props : props;
|
|
65
|
+
if (globalThis.__ALCHEMY_RUNTIME__) return resolved;
|
|
66
|
+
const app = Effect.isEffect(resolved.app)
|
|
67
|
+
? yield* resolved.app as Effect.Effect<App, never, Providers>
|
|
68
|
+
: resolved.app;
|
|
69
|
+
return { ...resolved, app };
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const SecretResource = Resource<Secret>("Fly.Secret");
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* A Fly.Secret is an App vault entry. Fly injects it as an environment
|
|
76
|
+
* variable on every Machine. Use it when the value is shared and
|
|
77
|
+
* managed in one place by Fly.
|
|
78
|
+
*
|
|
79
|
+
* For a secret only this {@link Service} reads from `.env` at deploy
|
|
80
|
+
* time, yield `Config.Redacted` instead. Do not pass `env: { ... }` on
|
|
81
|
+
* a Service.
|
|
82
|
+
*
|
|
83
|
+
* @see https://fly.io/docs/apps/secrets/
|
|
84
|
+
*
|
|
85
|
+
* ### Config.Redacted on a Service
|
|
86
|
+
* Most secrets in a Service come from your `.env`. Yield
|
|
87
|
+
* `Config.Redacted` in init. Alchemy binds the value onto the Machine.
|
|
88
|
+
*
|
|
89
|
+
* **Example:** Bind from .env
|
|
90
|
+
* ```typescript
|
|
91
|
+
* import * as Config from "effect/Config";
|
|
92
|
+
* import * as Redacted from "effect/Redacted";
|
|
93
|
+
*
|
|
94
|
+
* export default class Api extends Fly.Service<Api>()(
|
|
95
|
+
* "Api",
|
|
96
|
+
* { app: Site, main: import.meta.url, port: 3000 },
|
|
97
|
+
* Effect.gen(function* () {
|
|
98
|
+
* const apiKey = yield* Config.Redacted("API_KEY");
|
|
99
|
+
*
|
|
100
|
+
* return {
|
|
101
|
+
* fetch: Effect.gen(function* () {
|
|
102
|
+
* const token = Redacted.value(apiKey);
|
|
103
|
+
* return HttpServerResponse.text("ok");
|
|
104
|
+
* }),
|
|
105
|
+
* };
|
|
106
|
+
* }),
|
|
107
|
+
* ) {}
|
|
108
|
+
* ```
|
|
109
|
+
*
|
|
110
|
+
* ### Create a Secret
|
|
111
|
+
* Wrap the value with `Redacted.make` so it is never logged. The
|
|
112
|
+
* plaintext is never stored in attributes. Omit `name` and Alchemy
|
|
113
|
+
* generates an ownership-stamped name.
|
|
114
|
+
*
|
|
115
|
+
* **Example:** Generated name
|
|
116
|
+
* ```typescript
|
|
117
|
+
* const dbUrl = yield* Fly.Secret("DatabaseUrl", {
|
|
118
|
+
* app: Site,
|
|
119
|
+
* value: Redacted.make("postgres://…"),
|
|
120
|
+
* });
|
|
121
|
+
* ```
|
|
122
|
+
*
|
|
123
|
+
* :::caution[Changing `app` replaces the Secret]
|
|
124
|
+
* The value is created on the new App. The old name is deleted.
|
|
125
|
+
* :::
|
|
126
|
+
*
|
|
127
|
+
* ### Env-var name
|
|
128
|
+
* `name` is the env-var Machines see. It is stored as-is
|
|
129
|
+
* (case-sensitive).
|
|
130
|
+
*
|
|
131
|
+
* **Example:** Explicit name
|
|
132
|
+
* ```typescript
|
|
133
|
+
* export const ApiToken = Fly.Secret("ApiToken", {
|
|
134
|
+
* app: Site,
|
|
135
|
+
* name: "API_TOKEN",
|
|
136
|
+
* value: Redacted.make("sk_live_…"),
|
|
137
|
+
* });
|
|
138
|
+
* ```
|
|
139
|
+
*
|
|
140
|
+
* :::caution[Changing `name` replaces the Secret]
|
|
141
|
+
* Fly cannot rename a secret. Alchemy creates the new name, then
|
|
142
|
+
* deletes the old one.
|
|
143
|
+
* :::
|
|
144
|
+
*
|
|
145
|
+
* ### Rotate the value
|
|
146
|
+
* Updating `value` is in place via `updateSecrets`.
|
|
147
|
+
*
|
|
148
|
+
* **Example:** New value
|
|
149
|
+
* ```typescript
|
|
150
|
+
* export const ApiToken = Fly.Secret("ApiToken", {
|
|
151
|
+
* app: Site,
|
|
152
|
+
* name: "API_TOKEN",
|
|
153
|
+
* value: Redacted.make("sk_live_rotated"),
|
|
154
|
+
* });
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* ### Get a secret at runtime
|
|
158
|
+
* {@link GetSecret} is bound to one Secret. Provide
|
|
159
|
+
* {@link GetSecretHttp}. Fly only returns plaintext from a Machine in
|
|
160
|
+
* the same App. From a deploy-time Action you get metadata (name,
|
|
161
|
+
* digest, timestamps).
|
|
162
|
+
*
|
|
163
|
+
* **Example:** GetSecret
|
|
164
|
+
* ```typescript
|
|
165
|
+
* const get = yield* Fly.GetSecret(ApiToken);
|
|
166
|
+
* const got = yield* get();
|
|
167
|
+
* ```
|
|
168
|
+
*
|
|
169
|
+
* ### List secrets
|
|
170
|
+
* {@link ListSecrets} is bound to an {@link App}. From an Action, the
|
|
171
|
+
* org token can list any App in the org. From a Machine, deploy tokens
|
|
172
|
+
* are per-App. Mixing Apps on one Machine shares one `FLY_API_TOKEN`
|
|
173
|
+
* and is not supported.
|
|
174
|
+
*
|
|
175
|
+
* **Example:** ListSecrets
|
|
176
|
+
* ```typescript
|
|
177
|
+
* const list = yield* Fly.ListSecrets(Site);
|
|
178
|
+
* const { secrets } = yield* list();
|
|
179
|
+
* ```
|
|
180
|
+
*
|
|
181
|
+
* ### Write secrets
|
|
182
|
+
* {@link WriteSecret} creates, updates, and deletes by name. Provide
|
|
183
|
+
* {@link WriteSecretHttp} on the Action or Service Effect.
|
|
184
|
+
*
|
|
185
|
+
* **Example:** Rotate from an Action
|
|
186
|
+
* ```typescript
|
|
187
|
+
* const Seed = Alchemy.Action(
|
|
188
|
+
* "Seed",
|
|
189
|
+
* Effect.gen(function* () {
|
|
190
|
+
* const secrets = yield* Fly.WriteSecret(ApiToken);
|
|
191
|
+
*
|
|
192
|
+
* return Effect.fn(function* () {
|
|
193
|
+
* yield* secrets.update("API_TOKEN", Redacted.make("sk_live_rotated"));
|
|
194
|
+
* });
|
|
195
|
+
* }).pipe(Effect.provide(Fly.WriteSecretHttp)),
|
|
196
|
+
* );
|
|
197
|
+
* ```
|
|
198
|
+
*
|
|
199
|
+
* @resource
|
|
200
|
+
* @product Secret
|
|
201
|
+
*/
|
|
202
|
+
export const Secret: typeof SecretResource = Object.assign(
|
|
203
|
+
(id: string, props: SecretProps | Effect.Effect<SecretProps, never, Providers>) =>
|
|
204
|
+
SecretResource(id, resolveSecretProps(props)),
|
|
205
|
+
SecretResource,
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
export class SecretNotCreated extends Data.TaggedError("Fly.SecretNotCreated")<{
|
|
209
|
+
appName: string;
|
|
210
|
+
name: string;
|
|
211
|
+
}> {}
|
|
212
|
+
|
|
213
|
+
export class SecretAppRequired extends Data.TaggedError("Fly.SecretAppRequired")<{
|
|
214
|
+
message: string;
|
|
215
|
+
}> {}
|
|
216
|
+
|
|
217
|
+
const appNameOf = (value: unknown): string | undefined => {
|
|
218
|
+
if (value === null || typeof value !== "object") return undefined;
|
|
219
|
+
const rec = value as { appName?: unknown };
|
|
220
|
+
return typeof rec.appName === "string" && rec.appName.length > 0 ? rec.appName : undefined;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const unwrapSecret = (value: Redacted.Redacted<string> | string): string =>
|
|
224
|
+
Redacted.isRedacted(value) ? Redacted.value(value) : value;
|
|
225
|
+
|
|
226
|
+
const resolveName = (id: string, name: string | undefined, existing?: string) =>
|
|
227
|
+
Effect.gen(function* () {
|
|
228
|
+
// User env-var names are stored as-is (case-sensitive). Generated
|
|
229
|
+
// names use the Volume underscore shape — Fly rejects hyphens.
|
|
230
|
+
if (name !== undefined) return name;
|
|
231
|
+
if (existing !== undefined) return existing;
|
|
232
|
+
return yield* createFlyVolumeName(id);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
const toAttrs = (
|
|
236
|
+
appName: string,
|
|
237
|
+
secret: AppSecret,
|
|
238
|
+
fallbackName: string,
|
|
239
|
+
): Secret["Attributes"] => ({
|
|
240
|
+
appName,
|
|
241
|
+
name: secret.name ?? fallbackName,
|
|
242
|
+
digest: secret.digest,
|
|
243
|
+
createdAt: secret.created_at,
|
|
244
|
+
updatedAt: secret.updated_at,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const getByName = (appName: string, secretName: string) =>
|
|
248
|
+
machines
|
|
249
|
+
.getSecret({
|
|
250
|
+
app_name: appName,
|
|
251
|
+
secret_name: secretName,
|
|
252
|
+
show_secrets: false,
|
|
253
|
+
})
|
|
254
|
+
.pipe(Effect.catchTag("NotFound", () => Effect.succeed(undefined)));
|
|
255
|
+
|
|
256
|
+
const listSecrets = (appName: string) =>
|
|
257
|
+
machines
|
|
258
|
+
.listSecrets({
|
|
259
|
+
app_name: appName,
|
|
260
|
+
show_secrets: false,
|
|
261
|
+
})
|
|
262
|
+
.pipe(
|
|
263
|
+
Effect.map((res) => res.secrets ?? []),
|
|
264
|
+
Effect.catchTag(["NotFound", "Forbidden"], () => Effect.succeed([])),
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
const flyDigestCandidates = (plain: string) =>
|
|
268
|
+
Effect.sync(() => {
|
|
269
|
+
const utf8 = Buffer.from(plain, "utf8");
|
|
270
|
+
return [
|
|
271
|
+
createHash("md5").update(utf8).digest("hex"),
|
|
272
|
+
createHash("sha256").update(utf8).digest("hex"),
|
|
273
|
+
];
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
export const SecretProvider = () =>
|
|
277
|
+
Provider.succeed(Secret, {
|
|
278
|
+
stables: ["appName", "name", "createdAt"],
|
|
279
|
+
nuke: { dependsOn: ["Fly.App"] },
|
|
280
|
+
|
|
281
|
+
diff: Effect.fn(function* ({ news, output }) {
|
|
282
|
+
if (news === undefined || !isResolved(news)) return undefined;
|
|
283
|
+
if (output === undefined) return undefined;
|
|
284
|
+
const desiredName = news.name !== undefined ? news.name : output.name;
|
|
285
|
+
const nameChanged = desiredName !== output.name;
|
|
286
|
+
const nextApp = appNameOf(news.app);
|
|
287
|
+
const appChanged = nextApp !== undefined && nextApp !== output.appName;
|
|
288
|
+
if (nameChanged || appChanged) {
|
|
289
|
+
return {
|
|
290
|
+
action: "replace" as const,
|
|
291
|
+
// Same (app, name) cannot exist twice — delete the old secret first.
|
|
292
|
+
deleteFirst: nameChanged && !appChanged,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
return undefined;
|
|
296
|
+
}),
|
|
297
|
+
|
|
298
|
+
read: Effect.fn(function* ({ id, olds, output }) {
|
|
299
|
+
const appName = output?.appName ?? (olds !== undefined ? appNameOf(olds.app) : undefined);
|
|
300
|
+
if (appName === undefined) return undefined;
|
|
301
|
+
const name = yield* resolveName(id, olds?.name, output?.name);
|
|
302
|
+
const found = yield* getByName(appName, name);
|
|
303
|
+
if (found === undefined) return undefined;
|
|
304
|
+
const attrs = toAttrs(appName, found, name);
|
|
305
|
+
if (output !== undefined) return attrs;
|
|
306
|
+
return matchesAlchemyPhysicalName(name) ? attrs : Unowned(attrs);
|
|
307
|
+
}),
|
|
308
|
+
|
|
309
|
+
list: Effect.fn(function* () {
|
|
310
|
+
const apps = yield* listOwnedApps();
|
|
311
|
+
const rows = yield* Effect.forEach(
|
|
312
|
+
apps,
|
|
313
|
+
(app) =>
|
|
314
|
+
listSecrets(app.appName).pipe(
|
|
315
|
+
Effect.map((secrets) =>
|
|
316
|
+
secrets.flatMap((secret) => {
|
|
317
|
+
const name = secret.name;
|
|
318
|
+
if (!matchesAlchemyPhysicalName(name)) return [];
|
|
319
|
+
return [toAttrs(app.appName, secret, name ?? "")];
|
|
320
|
+
}),
|
|
321
|
+
),
|
|
322
|
+
),
|
|
323
|
+
{ concurrency: 8 },
|
|
324
|
+
);
|
|
325
|
+
return rows.flat();
|
|
326
|
+
}),
|
|
327
|
+
|
|
328
|
+
reconcile: Effect.fn(function* ({ id, news, olds, output }) {
|
|
329
|
+
const props = news ?? ({} as SecretProps);
|
|
330
|
+
const appName = appNameOf(props.app) ?? output?.appName;
|
|
331
|
+
if (appName === undefined) {
|
|
332
|
+
return yield* new SecretAppRequired({
|
|
333
|
+
message: "Secret requires a resolved Fly.App",
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
const name = yield* resolveName(id, props.name, output?.name);
|
|
337
|
+
const desiredPlain = unwrapSecret(props.value);
|
|
338
|
+
|
|
339
|
+
// Observe by cached identity, then the desired (app, name).
|
|
340
|
+
let current =
|
|
341
|
+
output !== undefined ? yield* getByName(output.appName, output.name) : undefined;
|
|
342
|
+
if (
|
|
343
|
+
current === undefined &&
|
|
344
|
+
(output === undefined || output.appName !== appName || output.name !== name)
|
|
345
|
+
) {
|
|
346
|
+
current = yield* getByName(appName, name);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
let createdThisRun = false;
|
|
350
|
+
if (current === undefined) {
|
|
351
|
+
yield* machines
|
|
352
|
+
.createSecret({
|
|
353
|
+
app_name: appName,
|
|
354
|
+
secret_name: name,
|
|
355
|
+
value: desiredPlain,
|
|
356
|
+
})
|
|
357
|
+
.pipe(Effect.catchTag("Conflict", () => Effect.void));
|
|
358
|
+
current = yield* getByName(appName, name);
|
|
359
|
+
createdThisRun = true;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (current === undefined) {
|
|
363
|
+
return yield* new SecretNotCreated({ appName, name });
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Sync — skip if we just created. Prefer observed digest vs hash of
|
|
367
|
+
// desired; otherwise re-put when olds is absent (adoption) or the
|
|
368
|
+
// previous value differs. Never log the plaintext.
|
|
369
|
+
if (!createdThisRun) {
|
|
370
|
+
const candidates = yield* flyDigestCandidates(desiredPlain);
|
|
371
|
+
const digestMatches = current.digest !== undefined && candidates.includes(current.digest);
|
|
372
|
+
const previousPlain = olds?.value !== undefined ? unwrapSecret(olds.value) : undefined;
|
|
373
|
+
const valueChanged = previousPlain === undefined || previousPlain !== desiredPlain;
|
|
374
|
+
if (!digestMatches && valueChanged) {
|
|
375
|
+
yield* machines
|
|
376
|
+
.updateSecrets({
|
|
377
|
+
app_name: appName,
|
|
378
|
+
values: { [name]: desiredPlain },
|
|
379
|
+
})
|
|
380
|
+
.pipe(Effect.catchTag("Conflict", () => Effect.void));
|
|
381
|
+
current = (yield* getByName(appName, name)) ?? current;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return toAttrs(appName, current, name);
|
|
386
|
+
}),
|
|
387
|
+
|
|
388
|
+
delete: Effect.fn(function* ({ output }) {
|
|
389
|
+
if (output.appName.length === 0 || output.name.length === 0) return;
|
|
390
|
+
yield* machines
|
|
391
|
+
.deleteSecret({
|
|
392
|
+
app_name: output.appName,
|
|
393
|
+
secret_name: output.name,
|
|
394
|
+
})
|
|
395
|
+
.pipe(Effect.catchTag("NotFound", () => Effect.void));
|
|
396
|
+
yield* getByName(output.appName, output.name).pipe(
|
|
397
|
+
Effect.map((secret) => secret === undefined),
|
|
398
|
+
Effect.repeat({
|
|
399
|
+
schedule: Schedule.spaced("1 second"),
|
|
400
|
+
until: (gone) => gone,
|
|
401
|
+
times: 8,
|
|
402
|
+
}),
|
|
403
|
+
);
|
|
404
|
+
}),
|
|
405
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Credentials,
|
|
3
|
+
CredentialsFromEnv,
|
|
4
|
+
credentials,
|
|
5
|
+
MachineIdentity,
|
|
6
|
+
} from "@distilled.cloud/fly-io";
|
|
7
|
+
import * as NodeHttpClient from "@effect/platform-node/NodeHttpClient";
|
|
8
|
+
import type { RuntimeContext } from "alchemy/RuntimeContext";
|
|
9
|
+
import type * as Context from "effect/Context";
|
|
10
|
+
import * as Effect from "effect/Effect";
|
|
11
|
+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
|
|
12
|
+
import * as HttpClient from "effect/http/HttpClient";
|
|
13
|
+
import * as Layer from "effect/Layer";
|
|
14
|
+
import * as Redacted from "effect/Redacted";
|
|
15
|
+
import type { App } from "./App.ts";
|
|
16
|
+
import { bindFlyApiToken } from "./Credentials.ts";
|
|
17
|
+
import type { Secret } from "./Secret.ts";
|
|
18
|
+
import type { SecretKey } from "./SecretKey.ts";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Shared scaffolding for the HTTP-backed Fly Secret bindings.
|
|
22
|
+
*
|
|
23
|
+
* Fly has no native Worker-style binding. This layer captures the ambient
|
|
24
|
+
* org token during stack-eval (so Actions work in-process) and `yield*`s
|
|
25
|
+
* it plus `appName` / secret `name` so RuntimeContext.set runs. Platform
|
|
26
|
+
* copies those Outputs into host env; runtime `yield*` gets them back.
|
|
27
|
+
* {@link CredentialsFromEnv} still reads `FLY_API_TOKEN` after that copy.
|
|
28
|
+
*
|
|
29
|
+
* NOT exported from `index.ts`.
|
|
30
|
+
*/
|
|
31
|
+
export type AppNamed = Secret | SecretKey;
|
|
32
|
+
|
|
33
|
+
export const makeHttpSecretBinding = <Target extends AppNamed, Client>(options: {
|
|
34
|
+
makeClient: (
|
|
35
|
+
auth: SecretAuth,
|
|
36
|
+
appName: Effect.Effect<string>,
|
|
37
|
+
secretName: Effect.Effect<string>,
|
|
38
|
+
) => Client;
|
|
39
|
+
/**
|
|
40
|
+
* PetSem encrypt/sign/decrypt/verify only work from a Machine over
|
|
41
|
+
* `/.fly/api` (implicit machine identity). Org tokens return Forbidden.
|
|
42
|
+
*/
|
|
43
|
+
kms?: boolean;
|
|
44
|
+
}) =>
|
|
45
|
+
Effect.gen(function* () {
|
|
46
|
+
const context = yield* Effect.context<Credentials | HttpClient.HttpClient>();
|
|
47
|
+
const auth = options.kms === true ? makeKmsAuth(context) : makeSecretAuth(context);
|
|
48
|
+
|
|
49
|
+
return Effect.fn(function* (resource: Target) {
|
|
50
|
+
yield* bindFlyApiToken().pipe(Effect.provideContext(context));
|
|
51
|
+
const appName = yield* resource.appName;
|
|
52
|
+
const secretName = yield* resource.name;
|
|
53
|
+
return options.makeClient(auth, appName, secretName);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Same scaffolding as {@link makeHttpSecretBinding}, but the target is
|
|
59
|
+
* an {@link App} (no secret name). Used by ListSecrets.
|
|
60
|
+
*/
|
|
61
|
+
export const makeHttpAppBinding = <Client>(options: {
|
|
62
|
+
makeClient: (auth: SecretAuth, appName: Effect.Effect<string>) => Client;
|
|
63
|
+
}) =>
|
|
64
|
+
Effect.gen(function* () {
|
|
65
|
+
const context = yield* Effect.context<Credentials | HttpClient.HttpClient>();
|
|
66
|
+
|
|
67
|
+
return Effect.fn(function* (app: App) {
|
|
68
|
+
yield* bindFlyApiToken().pipe(Effect.provideContext(context));
|
|
69
|
+
const appName = yield* app.appName;
|
|
70
|
+
return options.makeClient(makeSecretAuth(context), appName);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Injectable auth for the Secret HTTP client builders. Supplies an
|
|
76
|
+
* `authorize` that provides `Credentials` + `HttpClient` to a raw SDK op.
|
|
77
|
+
*/
|
|
78
|
+
export interface SecretAuth {
|
|
79
|
+
authorize: <A, E>(
|
|
80
|
+
eff: Effect.Effect<A, E, Credentials | HttpClient.HttpClient>,
|
|
81
|
+
) => Effect.Effect<A, E, RuntimeContext>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Build auth that uses ambient stack creds, or env creds inside a host. */
|
|
85
|
+
export const makeSecretAuth = (
|
|
86
|
+
ambient: Context.Context<Credentials | HttpClient.HttpClient>,
|
|
87
|
+
): SecretAuth => ({
|
|
88
|
+
authorize: <A, E>(
|
|
89
|
+
eff: Effect.Effect<A, E, Credentials | HttpClient.HttpClient>,
|
|
90
|
+
): Effect.Effect<A, E, RuntimeContext> => {
|
|
91
|
+
if (globalThis.__ALCHEMY_RUNTIME__) {
|
|
92
|
+
// oxlint-disable-next-line effecttsgo/unsafe-effect-type-assertion -- TimeoutError/env-credential errors surface untyped; widening SecretAuth would ripple through every Fly secret client
|
|
93
|
+
return eff.pipe(
|
|
94
|
+
Effect.provide(Layer.mergeAll(CredentialsFromEnv, FetchHttpClient.layer)),
|
|
95
|
+
Effect.timeout("8 seconds"),
|
|
96
|
+
) as Effect.Effect<A, E, RuntimeContext>;
|
|
97
|
+
}
|
|
98
|
+
return eff.pipe(Effect.provideContext(ambient)) as Effect.Effect<A, E, RuntimeContext>;
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const FLY_MACHINE_API_SOCKET = "/.fly/api";
|
|
103
|
+
|
|
104
|
+
/** Distilled machines client over `/.fly/api`. URL host is unused. */
|
|
105
|
+
const flyMachineApiHttp: Layer.Layer<HttpClient.HttpClient> =
|
|
106
|
+
NodeHttpClient.layerNodeHttpNoAgent.pipe(
|
|
107
|
+
Layer.provide(
|
|
108
|
+
NodeHttpClient.layerAgentOptions({
|
|
109
|
+
socketPath: FLY_MACHINE_API_SOCKET,
|
|
110
|
+
keepAlive: false,
|
|
111
|
+
}),
|
|
112
|
+
),
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* PetSem encrypt/sign/decrypt/verify from a Machine. Org API tokens are
|
|
117
|
+
* Forbidden; the machine identity is the unix socket at `/.fly/api`.
|
|
118
|
+
* {@link MachineIdentity} is the protocol signal (no Authorization,
|
|
119
|
+
* Connection: close). GetSecret in the same process keeps Bearer.
|
|
120
|
+
*/
|
|
121
|
+
export const makeKmsAuth = (
|
|
122
|
+
ambient: Context.Context<Credentials | HttpClient.HttpClient>,
|
|
123
|
+
): SecretAuth => ({
|
|
124
|
+
authorize: <A, E>(
|
|
125
|
+
eff: Effect.Effect<A, E, Credentials | HttpClient.HttpClient>,
|
|
126
|
+
): Effect.Effect<A, E, RuntimeContext> => {
|
|
127
|
+
if (globalThis.__ALCHEMY_RUNTIME__) {
|
|
128
|
+
// oxlint-disable-next-line effecttsgo/unsafe-effect-type-assertion -- TimeoutError surfaces untyped; widening SecretAuth would ripple through every Fly secret client
|
|
129
|
+
return Effect.scoped(
|
|
130
|
+
eff.pipe(
|
|
131
|
+
Effect.provide(
|
|
132
|
+
Layer.mergeAll(
|
|
133
|
+
Layer.succeed(MachineIdentity, true),
|
|
134
|
+
flyMachineApiHttp,
|
|
135
|
+
credentials({
|
|
136
|
+
apiKey: Redacted.make("unused"),
|
|
137
|
+
apiBaseUrl: "http://localhost/v1",
|
|
138
|
+
}),
|
|
139
|
+
),
|
|
140
|
+
),
|
|
141
|
+
),
|
|
142
|
+
).pipe(Effect.timeout("8 seconds")) as Effect.Effect<A, E, RuntimeContext>;
|
|
143
|
+
}
|
|
144
|
+
return eff.pipe(Effect.provideContext(ambient)) as Effect.Effect<A, E, RuntimeContext>;
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
export const unwrapSecretValue = (value: Redacted.Redacted<string> | string): string =>
|
|
149
|
+
Redacted.isRedacted(value) ? Redacted.value(value) : value;
|