@alchemy.run/modal 0.0.0 → 2.0.0-beta.86
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 +96 -4
- package/lib/App.d.ts +143 -0
- package/lib/App.d.ts.map +1 -0
- package/lib/App.js +198 -0
- package/lib/App.js.map +1 -0
- package/lib/AuthProvider.d.ts +53 -0
- package/lib/AuthProvider.d.ts.map +1 -0
- package/lib/AuthProvider.js +209 -0
- package/lib/AuthProvider.js.map +1 -0
- package/lib/BindingHttp.d.ts +56 -0
- package/lib/BindingHttp.d.ts.map +1 -0
- package/lib/BindingHttp.js +50 -0
- package/lib/BindingHttp.js.map +1 -0
- package/lib/Blob.d.ts +34 -0
- package/lib/Blob.d.ts.map +1 -0
- package/lib/Blob.js +59 -0
- package/lib/Blob.js.map +1 -0
- package/lib/Bootstrap.d.ts +8 -0
- package/lib/Bootstrap.d.ts.map +1 -0
- package/lib/Bootstrap.js +26 -0
- package/lib/Bootstrap.js.map +1 -0
- package/lib/Cbor.d.ts +10 -0
- package/lib/Cbor.d.ts.map +1 -0
- package/lib/Cbor.js +177 -0
- package/lib/Cbor.js.map +1 -0
- package/lib/ConnectSandbox.d.ts +64 -0
- package/lib/ConnectSandbox.d.ts.map +1 -0
- package/lib/ConnectSandbox.js +33 -0
- package/lib/ConnectSandbox.js.map +1 -0
- package/lib/Container.d.ts +124 -0
- package/lib/Container.d.ts.map +1 -0
- package/lib/Container.js +1 -0
- package/lib/Container.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/CreateDict.d.ts +83 -0
- package/lib/CreateDict.d.ts.map +1 -0
- package/lib/CreateDict.js +3 -0
- package/lib/CreateDict.js.map +1 -0
- package/lib/CreateDictHttp.d.ts +33 -0
- package/lib/CreateDictHttp.d.ts.map +1 -0
- package/lib/CreateDictHttp.js +67 -0
- package/lib/CreateDictHttp.js.map +1 -0
- package/lib/CreateImage.d.ts +98 -0
- package/lib/CreateImage.d.ts.map +1 -0
- package/lib/CreateImage.js +3 -0
- package/lib/CreateImage.js.map +1 -0
- package/lib/CreateImageHttp.d.ts +51 -0
- package/lib/CreateImageHttp.d.ts.map +1 -0
- package/lib/CreateImageHttp.js +87 -0
- package/lib/CreateImageHttp.js.map +1 -0
- package/lib/CreateQueue.d.ts +83 -0
- package/lib/CreateQueue.d.ts.map +1 -0
- package/lib/CreateQueue.js +3 -0
- package/lib/CreateQueue.js.map +1 -0
- package/lib/CreateQueueHttp.d.ts +31 -0
- package/lib/CreateQueueHttp.d.ts.map +1 -0
- package/lib/CreateQueueHttp.js +67 -0
- package/lib/CreateQueueHttp.js.map +1 -0
- package/lib/CreateSandbox.d.ts +186 -0
- package/lib/CreateSandbox.d.ts.map +1 -0
- package/lib/CreateSandbox.js +3 -0
- package/lib/CreateSandbox.js.map +1 -0
- package/lib/CreateSandboxHttp.d.ts +17 -0
- package/lib/CreateSandboxHttp.d.ts.map +1 -0
- package/lib/CreateSandboxHttp.js +117 -0
- package/lib/CreateSandboxHttp.js.map +1 -0
- package/lib/CreateSecret.d.ts +95 -0
- package/lib/CreateSecret.d.ts.map +1 -0
- package/lib/CreateSecret.js +3 -0
- package/lib/CreateSecret.js.map +1 -0
- package/lib/CreateSecretHttp.d.ts +24 -0
- package/lib/CreateSecretHttp.d.ts.map +1 -0
- package/lib/CreateSecretHttp.js +60 -0
- package/lib/CreateSecretHttp.js.map +1 -0
- package/lib/CreateVolume.d.ts +84 -0
- package/lib/CreateVolume.d.ts.map +1 -0
- package/lib/CreateVolume.js +3 -0
- package/lib/CreateVolume.js.map +1 -0
- package/lib/CreateVolumeHttp.d.ts +32 -0
- package/lib/CreateVolumeHttp.d.ts.map +1 -0
- package/lib/CreateVolumeHttp.js +69 -0
- package/lib/CreateVolumeHttp.js.map +1 -0
- package/lib/Credentials.d.ts +32 -0
- package/lib/Credentials.d.ts.map +1 -0
- package/lib/Credentials.js +44 -0
- package/lib/Credentials.js.map +1 -0
- package/lib/Dict.d.ts +147 -0
- package/lib/Dict.d.ts.map +1 -0
- package/lib/Dict.js +175 -0
- package/lib/Dict.js.map +1 -0
- package/lib/DictClient.d.ts +55 -0
- package/lib/DictClient.d.ts.map +1 -0
- package/lib/DictClient.js +89 -0
- package/lib/DictClient.js.map +1 -0
- package/lib/Domain.d.ts +86 -0
- package/lib/Domain.d.ts.map +1 -0
- package/lib/Domain.js +113 -0
- package/lib/Domain.js.map +1 -0
- package/lib/Endpoint.d.ts +181 -0
- package/lib/Endpoint.d.ts.map +1 -0
- package/lib/Endpoint.js +283 -0
- package/lib/Endpoint.js.map +1 -0
- package/lib/Environment.d.ts +174 -0
- package/lib/Environment.d.ts.map +1 -0
- package/lib/Environment.js +244 -0
- package/lib/Environment.js.map +1 -0
- package/lib/FetchServer.d.ts +105 -0
- package/lib/FetchServer.d.ts.map +1 -0
- package/lib/FetchServer.js +3 -0
- package/lib/FetchServer.js.map +1 -0
- package/lib/FetchServerHttp.d.ts +21 -0
- package/lib/FetchServerHttp.d.ts.map +1 -0
- package/lib/FetchServerHttp.js +84 -0
- package/lib/FetchServerHttp.js.map +1 -0
- package/lib/Function.d.ts +236 -0
- package/lib/Function.d.ts.map +1 -0
- package/lib/Function.js +273 -0
- package/lib/Function.js.map +1 -0
- package/lib/FunctionAutoscaler.d.ts +99 -0
- package/lib/FunctionAutoscaler.d.ts.map +1 -0
- package/lib/FunctionAutoscaler.js +136 -0
- package/lib/FunctionAutoscaler.js.map +1 -0
- package/lib/FunctionTarget.d.ts +61 -0
- package/lib/FunctionTarget.d.ts.map +1 -0
- package/lib/FunctionTarget.js +136 -0
- package/lib/FunctionTarget.js.map +1 -0
- package/lib/Image.d.ts +159 -0
- package/lib/Image.d.ts.map +1 -0
- package/lib/Image.js +203 -0
- package/lib/Image.js.map +1 -0
- package/lib/ImageBuild.d.ts +108 -0
- package/lib/ImageBuild.d.ts.map +1 -0
- package/lib/ImageBuild.js +157 -0
- package/lib/ImageBuild.js.map +1 -0
- package/lib/ImagePlan.d.ts +31 -0
- package/lib/ImagePlan.d.ts.map +1 -0
- package/lib/ImagePlan.js +70 -0
- package/lib/ImagePlan.js.map +1 -0
- package/lib/InferEnv.d.ts +26 -0
- package/lib/InferEnv.d.ts.map +1 -0
- package/lib/InferEnv.js +1 -0
- package/lib/InferEnv.js.map +1 -0
- package/lib/InvokeFunction.d.ts +186 -0
- package/lib/InvokeFunction.d.ts.map +1 -0
- package/lib/InvokeFunction.js +10 -0
- package/lib/InvokeFunction.js.map +1 -0
- package/lib/InvokeFunctionHttp.d.ts +39 -0
- package/lib/InvokeFunctionHttp.d.ts.map +1 -0
- package/lib/InvokeFunctionHttp.js +210 -0
- package/lib/InvokeFunctionHttp.js.map +1 -0
- package/lib/ModalBindings.d.ts +138 -0
- package/lib/ModalBindings.d.ts.map +1 -0
- package/lib/ModalBindings.js +223 -0
- package/lib/ModalBindings.js.map +1 -0
- package/lib/MountBucket.d.ts +80 -0
- package/lib/MountBucket.d.ts.map +1 -0
- package/lib/MountBucket.js +44 -0
- package/lib/MountBucket.js.map +1 -0
- package/lib/MountVolume.d.ts +77 -0
- package/lib/MountVolume.d.ts.map +1 -0
- package/lib/MountVolume.js +41 -0
- package/lib/MountVolume.js.map +1 -0
- package/lib/Mounts.d.ts +134 -0
- package/lib/Mounts.d.ts.map +1 -0
- package/lib/Mounts.js +87 -0
- package/lib/Mounts.js.map +1 -0
- package/lib/PersistentSandbox.d.ts +75 -0
- package/lib/PersistentSandbox.d.ts.map +1 -0
- package/lib/PersistentSandbox.js +93 -0
- package/lib/PersistentSandbox.js.map +1 -0
- package/lib/Providers.d.ts +23 -0
- package/lib/Providers.d.ts.map +1 -0
- package/lib/Providers.js +64 -0
- package/lib/Providers.js.map +1 -0
- package/lib/Proxy.d.ts +94 -0
- package/lib/Proxy.d.ts.map +1 -0
- package/lib/Proxy.js +132 -0
- package/lib/Proxy.js.map +1 -0
- package/lib/Queue.d.ts +147 -0
- package/lib/Queue.d.ts.map +1 -0
- package/lib/Queue.js +177 -0
- package/lib/Queue.js.map +1 -0
- package/lib/QueueClient.d.ts +64 -0
- package/lib/QueueClient.d.ts.map +1 -0
- package/lib/QueueClient.js +73 -0
- package/lib/QueueClient.js.map +1 -0
- package/lib/ReadApp.d.ts +85 -0
- package/lib/ReadApp.d.ts.map +1 -0
- package/lib/ReadApp.js +3 -0
- package/lib/ReadApp.js.map +1 -0
- package/lib/ReadAppHttp.d.ts +26 -0
- package/lib/ReadAppHttp.d.ts.map +1 -0
- package/lib/ReadAppHttp.js +106 -0
- package/lib/ReadAppHttp.js.map +1 -0
- package/lib/ReadDict.d.ts +39 -0
- package/lib/ReadDict.d.ts.map +1 -0
- package/lib/ReadDict.js +3 -0
- package/lib/ReadDict.js.map +1 -0
- package/lib/ReadDictHttp.d.ts +12 -0
- package/lib/ReadDictHttp.d.ts.map +1 -0
- package/lib/ReadDictHttp.js +23 -0
- package/lib/ReadDictHttp.js.map +1 -0
- package/lib/ReadFunction.d.ts +73 -0
- package/lib/ReadFunction.d.ts.map +1 -0
- package/lib/ReadFunction.js +3 -0
- package/lib/ReadFunction.js.map +1 -0
- package/lib/ReadFunctionHttp.d.ts +25 -0
- package/lib/ReadFunctionHttp.d.ts.map +1 -0
- package/lib/ReadFunctionHttp.js +63 -0
- package/lib/ReadFunctionHttp.js.map +1 -0
- package/lib/ReadQueue.d.ts +39 -0
- package/lib/ReadQueue.d.ts.map +1 -0
- package/lib/ReadQueue.js +3 -0
- package/lib/ReadQueue.js.map +1 -0
- package/lib/ReadQueueHttp.d.ts +12 -0
- package/lib/ReadQueueHttp.d.ts.map +1 -0
- package/lib/ReadQueueHttp.js +23 -0
- package/lib/ReadQueueHttp.js.map +1 -0
- package/lib/ReadServer.d.ts +72 -0
- package/lib/ReadServer.d.ts.map +1 -0
- package/lib/ReadServer.js +3 -0
- package/lib/ReadServer.js.map +1 -0
- package/lib/ReadServerHttp.d.ts +25 -0
- package/lib/ReadServerHttp.d.ts.map +1 -0
- package/lib/ReadServerHttp.js +70 -0
- package/lib/ReadServerHttp.js.map +1 -0
- package/lib/ReadVolume.d.ts +40 -0
- package/lib/ReadVolume.d.ts.map +1 -0
- package/lib/ReadVolume.js +3 -0
- package/lib/ReadVolume.js.map +1 -0
- package/lib/ReadVolumeHttp.d.ts +12 -0
- package/lib/ReadVolumeHttp.d.ts.map +1 -0
- package/lib/ReadVolumeHttp.js +24 -0
- package/lib/ReadVolumeHttp.js.map +1 -0
- package/lib/ReadWriteDict.d.ts +39 -0
- package/lib/ReadWriteDict.d.ts.map +1 -0
- package/lib/ReadWriteDict.js +3 -0
- package/lib/ReadWriteDict.js.map +1 -0
- package/lib/ReadWriteDictHttp.d.ts +12 -0
- package/lib/ReadWriteDictHttp.d.ts.map +1 -0
- package/lib/ReadWriteDictHttp.js +23 -0
- package/lib/ReadWriteDictHttp.js.map +1 -0
- package/lib/ReadWriteQueue.d.ts +40 -0
- package/lib/ReadWriteQueue.d.ts.map +1 -0
- package/lib/ReadWriteQueue.js +3 -0
- package/lib/ReadWriteQueue.js.map +1 -0
- package/lib/ReadWriteQueueHttp.d.ts +12 -0
- package/lib/ReadWriteQueueHttp.d.ts.map +1 -0
- package/lib/ReadWriteQueueHttp.js +23 -0
- package/lib/ReadWriteQueueHttp.js.map +1 -0
- package/lib/ReadWriteVolume.d.ts +40 -0
- package/lib/ReadWriteVolume.d.ts.map +1 -0
- package/lib/ReadWriteVolume.js +3 -0
- package/lib/ReadWriteVolume.js.map +1 -0
- package/lib/ReadWriteVolumeHttp.d.ts +12 -0
- package/lib/ReadWriteVolumeHttp.d.ts.map +1 -0
- package/lib/ReadWriteVolumeHttp.js +24 -0
- package/lib/ReadWriteVolumeHttp.js.map +1 -0
- package/lib/RuntimeOwner.d.ts +39 -0
- package/lib/RuntimeOwner.d.ts.map +1 -0
- package/lib/RuntimeOwner.js +85 -0
- package/lib/RuntimeOwner.js.map +1 -0
- package/lib/Sandbox.d.ts +215 -0
- package/lib/Sandbox.d.ts.map +1 -0
- package/lib/Sandbox.js +278 -0
- package/lib/Sandbox.js.map +1 -0
- package/lib/SandboxClient.d.ts +748 -0
- package/lib/SandboxClient.d.ts.map +1 -0
- package/lib/SandboxClient.js +710 -0
- package/lib/SandboxClient.js.map +1 -0
- package/lib/ScaleFunction.d.ts +54 -0
- package/lib/ScaleFunction.d.ts.map +1 -0
- package/lib/ScaleFunction.js +3 -0
- package/lib/ScaleFunction.js.map +1 -0
- package/lib/ScaleFunctionHttp.d.ts +21 -0
- package/lib/ScaleFunctionHttp.d.ts.map +1 -0
- package/lib/ScaleFunctionHttp.js +39 -0
- package/lib/ScaleFunctionHttp.js.map +1 -0
- package/lib/Secret.d.ts +170 -0
- package/lib/Secret.d.ts.map +1 -0
- package/lib/Secret.js +196 -0
- package/lib/Secret.js.map +1 -0
- package/lib/Server.d.ts +155 -0
- package/lib/Server.d.ts.map +1 -0
- package/lib/Server.js +184 -0
- package/lib/Server.js.map +1 -0
- package/lib/ServiceUser.d.ts +139 -0
- package/lib/ServiceUser.d.ts.map +1 -0
- package/lib/ServiceUser.js +210 -0
- package/lib/ServiceUser.js.map +1 -0
- package/lib/Volume.d.ts +170 -0
- package/lib/Volume.d.ts.map +1 -0
- package/lib/Volume.js +220 -0
- package/lib/Volume.js.map +1 -0
- package/lib/VolumeClient.d.ts +53 -0
- package/lib/VolumeClient.d.ts.map +1 -0
- package/lib/VolumeClient.js +22 -0
- package/lib/VolumeClient.js.map +1 -0
- package/lib/VolumeFiles.d.ts +94 -0
- package/lib/VolumeFiles.d.ts.map +1 -0
- package/lib/VolumeFiles.js +139 -0
- package/lib/VolumeFiles.js.map +1 -0
- package/lib/WebhookToken.d.ts +102 -0
- package/lib/WebhookToken.d.ts.map +1 -0
- package/lib/WebhookToken.js +176 -0
- package/lib/WebhookToken.js.map +1 -0
- package/lib/WorkspaceSettings.d.ts +70 -0
- package/lib/WorkspaceSettings.d.ts.map +1 -0
- package/lib/WorkspaceSettings.js +105 -0
- package/lib/WorkspaceSettings.js.map +1 -0
- package/lib/WriteApp.d.ts +38 -0
- package/lib/WriteApp.d.ts.map +1 -0
- package/lib/WriteApp.js +3 -0
- package/lib/WriteApp.js.map +1 -0
- package/lib/WriteAppHttp.d.ts +23 -0
- package/lib/WriteAppHttp.d.ts.map +1 -0
- package/lib/WriteAppHttp.js +36 -0
- package/lib/WriteAppHttp.js.map +1 -0
- package/lib/WriteDict.d.ts +39 -0
- package/lib/WriteDict.d.ts.map +1 -0
- package/lib/WriteDict.js +3 -0
- package/lib/WriteDict.js.map +1 -0
- package/lib/WriteDictHttp.d.ts +12 -0
- package/lib/WriteDictHttp.d.ts.map +1 -0
- package/lib/WriteDictHttp.js +23 -0
- package/lib/WriteDictHttp.js.map +1 -0
- package/lib/WriteQueue.d.ts +39 -0
- package/lib/WriteQueue.d.ts.map +1 -0
- package/lib/WriteQueue.js +3 -0
- package/lib/WriteQueue.js.map +1 -0
- package/lib/WriteQueueHttp.d.ts +12 -0
- package/lib/WriteQueueHttp.d.ts.map +1 -0
- package/lib/WriteQueueHttp.js +23 -0
- package/lib/WriteQueueHttp.js.map +1 -0
- package/lib/WriteSecret.d.ts +53 -0
- package/lib/WriteSecret.d.ts.map +1 -0
- package/lib/WriteSecret.js +3 -0
- package/lib/WriteSecret.js.map +1 -0
- package/lib/WriteSecretHttp.d.ts +23 -0
- package/lib/WriteSecretHttp.d.ts.map +1 -0
- package/lib/WriteSecretHttp.js +44 -0
- package/lib/WriteSecretHttp.js.map +1 -0
- package/lib/WriteVolume.d.ts +39 -0
- package/lib/WriteVolume.d.ts.map +1 -0
- package/lib/WriteVolume.js +3 -0
- package/lib/WriteVolume.js.map +1 -0
- package/lib/WriteVolumeHttp.d.ts +12 -0
- package/lib/WriteVolumeHttp.d.ts.map +1 -0
- package/lib/WriteVolumeHttp.js +24 -0
- package/lib/WriteVolumeHttp.js.map +1 -0
- package/lib/index.d.ts +80 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +78 -0
- package/lib/index.js.map +1 -0
- package/lib/internal/Declared.d.ts +29 -0
- package/lib/internal/Declared.d.ts.map +1 -0
- package/lib/internal/Declared.js +31 -0
- package/lib/internal/Declared.js.map +1 -0
- package/lib/internal/Deploy.d.ts +110 -0
- package/lib/internal/Deploy.d.ts.map +1 -0
- package/lib/internal/Deploy.js +230 -0
- package/lib/internal/Deploy.js.map +1 -0
- package/lib/internal/Entrypoint.d.ts +52 -0
- package/lib/internal/Entrypoint.d.ts.map +1 -0
- package/lib/internal/Entrypoint.js +193 -0
- package/lib/internal/Entrypoint.js.map +1 -0
- package/lib/internal/Rpc.d.ts +17 -0
- package/lib/internal/Rpc.d.ts.map +1 -0
- package/lib/internal/Rpc.js +49 -0
- package/lib/internal/Rpc.js.map +1 -0
- package/lib/internal/RpcAuth.d.ts +41 -0
- package/lib/internal/RpcAuth.d.ts.map +1 -0
- package/lib/internal/RpcAuth.js +100 -0
- package/lib/internal/RpcAuth.js.map +1 -0
- package/lib/internal/Runtime.d.ts +127 -0
- package/lib/internal/Runtime.d.ts.map +1 -0
- package/lib/internal/Runtime.js +246 -0
- package/lib/internal/Runtime.js.map +1 -0
- package/lib/internal/Tags.d.ts +19 -0
- package/lib/internal/Tags.d.ts.map +1 -0
- package/lib/internal/Tags.js +33 -0
- package/lib/internal/Tags.js.map +1 -0
- package/lib/internal/TcpConnect.d.ts +12 -0
- package/lib/internal/TcpConnect.d.ts.map +1 -0
- package/lib/internal/TcpConnect.js +46 -0
- package/lib/internal/TcpConnect.js.map +1 -0
- package/lib/internal/Transport.d.ts +13 -0
- package/lib/internal/Transport.d.ts.map +1 -0
- package/lib/internal/Transport.js +16 -0
- package/lib/internal/Transport.js.map +1 -0
- package/lib/internal.d.ts +71 -0
- package/lib/internal.d.ts.map +1 -0
- package/lib/internal.js +102 -0
- package/lib/internal.js.map +1 -0
- package/package.json +59 -3
- package/src/App.ts +262 -0
- package/src/AuthProvider.ts +295 -0
- package/src/BindingHttp.ts +112 -0
- package/src/Blob.ts +71 -0
- package/src/Bootstrap.ts +52 -0
- package/src/Cbor.ts +158 -0
- package/src/ConnectSandbox.ts +99 -0
- package/src/Container.ts +121 -0
- package/src/Contributions.ts +9 -0
- package/src/CreateDict.ts +95 -0
- package/src/CreateDictHttp.ts +91 -0
- package/src/CreateImage.ts +105 -0
- package/src/CreateImageHttp.ts +117 -0
- package/src/CreateQueue.ts +95 -0
- package/src/CreateQueueHttp.ts +91 -0
- package/src/CreateSandbox.ts +228 -0
- package/src/CreateSandboxHttp.ts +160 -0
- package/src/CreateSecret.ts +109 -0
- package/src/CreateSecretHttp.ts +83 -0
- package/src/CreateVolume.ts +96 -0
- package/src/CreateVolumeHttp.ts +93 -0
- package/src/Credentials.ts +74 -0
- package/src/Dict.ts +245 -0
- package/src/DictClient.ts +166 -0
- package/src/Domain.ts +147 -0
- package/src/Endpoint.ts +406 -0
- package/src/Environment.ts +373 -0
- package/src/FetchServer.ts +126 -0
- package/src/FetchServerHttp.ts +110 -0
- package/src/Function.ts +397 -0
- package/src/FunctionAutoscaler.ts +210 -0
- package/src/FunctionTarget.ts +181 -0
- package/src/Image.ts +281 -0
- package/src/ImageBuild.ts +228 -0
- package/src/ImagePlan.ts +78 -0
- package/src/InferEnv.ts +27 -0
- package/src/InvokeFunction.ts +211 -0
- package/src/InvokeFunctionHttp.ts +316 -0
- package/src/ModalBindings.ts +310 -0
- package/src/MountBucket.ts +137 -0
- package/src/MountVolume.ts +113 -0
- package/src/Mounts.ts +230 -0
- package/src/PersistentSandbox.ts +142 -0
- package/src/Providers.ts +100 -0
- package/src/Proxy.ts +191 -0
- package/src/Queue.ts +258 -0
- package/src/QueueClient.ts +149 -0
- package/src/ReadApp.ts +101 -0
- package/src/ReadAppHttp.ts +142 -0
- package/src/ReadDict.ts +44 -0
- package/src/ReadDictHttp.ts +27 -0
- package/src/ReadFunction.ts +107 -0
- package/src/ReadFunctionHttp.ts +91 -0
- package/src/ReadQueue.ts +44 -0
- package/src/ReadQueueHttp.ts +27 -0
- package/src/ReadServer.ts +98 -0
- package/src/ReadServerHttp.ts +101 -0
- package/src/ReadVolume.ts +45 -0
- package/src/ReadVolumeHttp.ts +28 -0
- package/src/ReadWriteDict.ts +44 -0
- package/src/ReadWriteDictHttp.ts +27 -0
- package/src/ReadWriteQueue.ts +45 -0
- package/src/ReadWriteQueueHttp.ts +27 -0
- package/src/ReadWriteVolume.ts +45 -0
- package/src/ReadWriteVolumeHttp.ts +28 -0
- package/src/RuntimeOwner.ts +111 -0
- package/src/Sandbox.ts +402 -0
- package/src/SandboxClient.ts +1404 -0
- package/src/ScaleFunction.ts +70 -0
- package/src/ScaleFunctionHttp.ts +54 -0
- package/src/Secret.ts +296 -0
- package/src/Server.ts +285 -0
- package/src/ServiceUser.ts +295 -0
- package/src/Volume.ts +320 -0
- package/src/VolumeClient.ts +108 -0
- package/src/VolumeFiles.ts +194 -0
- package/src/WebhookToken.ts +230 -0
- package/src/WorkspaceSettings.ts +160 -0
- package/src/WriteApp.ts +43 -0
- package/src/WriteAppHttp.ts +51 -0
- package/src/WriteDict.ts +44 -0
- package/src/WriteDictHttp.ts +27 -0
- package/src/WriteQueue.ts +44 -0
- package/src/WriteQueueHttp.ts +27 -0
- package/src/WriteSecret.ts +60 -0
- package/src/WriteSecretHttp.ts +56 -0
- package/src/WriteVolume.ts +44 -0
- package/src/WriteVolumeHttp.ts +28 -0
- package/src/globals.d.ts +2 -0
- package/src/index.ts +85 -0
- package/src/internal/Declared.ts +80 -0
- package/src/internal/Deploy.ts +309 -0
- package/src/internal/Entrypoint.ts +198 -0
- package/src/internal/Rpc.ts +59 -0
- package/src/internal/RpcAuth.ts +132 -0
- package/src/internal/Runtime.ts +386 -0
- package/src/internal/Tags.ts +44 -0
- package/src/internal/TcpConnect.ts +78 -0
- package/src/internal/Transport.ts +25 -0
- package/src/internal.ts +147 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import * as environment from "@distilled.cloud/modal/environment";
|
|
2
|
+
import * as service from "@distilled.cloud/modal/service";
|
|
3
|
+
import * as workspace from "@distilled.cloud/modal/workspace";
|
|
4
|
+
import { isResolved } from "alchemy/Diff";
|
|
5
|
+
import * as Provider from "alchemy/Provider";
|
|
6
|
+
import { Resource, type ResourceBinding } from "alchemy/Resource";
|
|
7
|
+
import * as Data from "effect/Data";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import * as Redacted from "effect/Redacted";
|
|
10
|
+
import { type EnvironmentInput, envNameOf, objectName, seconds } from "./internal.ts";
|
|
11
|
+
import { declarable } from "./internal/Declared.ts";
|
|
12
|
+
import type { Providers } from "./Providers.ts";
|
|
13
|
+
import type { DurationInput } from "./SandboxClient.ts";
|
|
14
|
+
|
|
15
|
+
export class ServiceUserUnexpectedResponse extends Data.TaggedError(
|
|
16
|
+
"Modal.ServiceUserUnexpectedResponse",
|
|
17
|
+
)<{
|
|
18
|
+
readonly message: string;
|
|
19
|
+
}> {}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The workspace's plan has no service users (Modal's Starter plan). Bindings
|
|
23
|
+
* create one per host; `Modal.DeployerHostIdentity` binds the deployer's token
|
|
24
|
+
* instead.
|
|
25
|
+
*/
|
|
26
|
+
export class ServiceUsersNotAvailable extends Data.TaggedError("Modal.ServiceUsersNotAvailable")<{
|
|
27
|
+
readonly message: string;
|
|
28
|
+
}> {}
|
|
29
|
+
|
|
30
|
+
/** A role a scoped service user holds in one environment. */
|
|
31
|
+
export interface ServiceUserEnvironmentRole {
|
|
32
|
+
/** The environment: its name, or a yielded `Modal.Environment`. */
|
|
33
|
+
readonly environment: EnvironmentInput;
|
|
34
|
+
/** `viewer` reads; `contributor` also deploys and writes. */
|
|
35
|
+
readonly role: "viewer" | "contributor";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ServiceUserProps {
|
|
39
|
+
/**
|
|
40
|
+
* Service user name, unique in the workspace. If omitted, one is generated
|
|
41
|
+
* from the stack, stage and logical ID. Changing it replaces the user (and
|
|
42
|
+
* its token).
|
|
43
|
+
*/
|
|
44
|
+
name?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Limit the user to the environments in {@link environments}. An unscoped
|
|
47
|
+
* user acts on the whole workspace. Changing it replaces the user.
|
|
48
|
+
* @default true
|
|
49
|
+
*/
|
|
50
|
+
scoped?: boolean;
|
|
51
|
+
/** Roles of a scoped user, per environment. */
|
|
52
|
+
environments?: ServiceUserEnvironmentRole[];
|
|
53
|
+
/**
|
|
54
|
+
* Expire the user's token after this long. Changing it replaces the user.
|
|
55
|
+
* @default never
|
|
56
|
+
*/
|
|
57
|
+
expiresIn?: DurationInput;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface ServiceUserAttributes {
|
|
61
|
+
/** Service user ID. */
|
|
62
|
+
serviceUserId: string;
|
|
63
|
+
/** Service user name. */
|
|
64
|
+
name: string;
|
|
65
|
+
/** Token ID (`ak-…`), the `MODAL_TOKEN_ID` of the user. */
|
|
66
|
+
tokenId: string;
|
|
67
|
+
/** Token secret (`as-…`), the `MODAL_TOKEN_SECRET` of the user. */
|
|
68
|
+
tokenSecret: Redacted.Redacted<string>;
|
|
69
|
+
/** Whether the user is limited to its environments. */
|
|
70
|
+
scoped: boolean;
|
|
71
|
+
/** Roles by environment name. */
|
|
72
|
+
environments: Record<string, "viewer" | "contributor">;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* A Modal service user: a workspace identity with its own token pair, for
|
|
77
|
+
* CI or another system. A scoped user acts only in the environments you
|
|
78
|
+
* grant it, as a `viewer` or a `contributor`.
|
|
79
|
+
*
|
|
80
|
+
* Modal shows a service user's token secret once, at creation, so a user
|
|
81
|
+
* deleted outside Alchemy is recreated with a new token on the next deploy,
|
|
82
|
+
* and an existing user can't be adopted.
|
|
83
|
+
*
|
|
84
|
+
* ### Creating a service user
|
|
85
|
+
* **Example:** CI that deploys to one environment
|
|
86
|
+
* ```typescript
|
|
87
|
+
* import * as Modal from "@alchemy.run/modal";
|
|
88
|
+
*
|
|
89
|
+
* export const Staging = Modal.Environment("Staging", {});
|
|
90
|
+
* export const Deployer = Modal.ServiceUser("Deployer", {
|
|
91
|
+
* environments: [{ environment: Staging, role: "contributor" }],
|
|
92
|
+
* });
|
|
93
|
+
* ```
|
|
94
|
+
*
|
|
95
|
+
* **Example:** A short-lived, read-only token
|
|
96
|
+
* ```typescript
|
|
97
|
+
* export const Auditor = Modal.ServiceUser("Auditor", {
|
|
98
|
+
* environments: [{ environment: "main", role: "viewer" }],
|
|
99
|
+
* expiresIn: "7 days",
|
|
100
|
+
* });
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* ### Using its token
|
|
104
|
+
* **Example:** Hand the pair to a Worker that calls Modal as the user
|
|
105
|
+
* ```typescript
|
|
106
|
+
* import * as Cloudflare from "@alchemy.run/cloudflare";
|
|
107
|
+
* import * as Effect from "effect/Effect";
|
|
108
|
+
*
|
|
109
|
+
* export const Ci = Effect.gen(function* () {
|
|
110
|
+
* const deployer = yield* Deployer;
|
|
111
|
+
* return yield* Cloudflare.Worker("Ci", {
|
|
112
|
+
* main: "./src/ci.ts",
|
|
113
|
+
* env: { MODAL_TOKEN_ID: deployer.tokenId, MODAL_TOKEN_SECRET: deployer.tokenSecret },
|
|
114
|
+
* });
|
|
115
|
+
* });
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* @resource
|
|
119
|
+
* @product Workspace
|
|
120
|
+
*/
|
|
121
|
+
export const ServiceUser = declarable<ServiceUser>()(Resource<ServiceUser>("Modal.ServiceUser"));
|
|
122
|
+
|
|
123
|
+
export type ServiceUser = Resource<
|
|
124
|
+
"Modal.ServiceUser",
|
|
125
|
+
ServiceUserProps,
|
|
126
|
+
ServiceUserAttributes,
|
|
127
|
+
{
|
|
128
|
+
/**
|
|
129
|
+
* Roles contributed by bindings: a binding on a host grants the host's
|
|
130
|
+
* service user the role it needs on the bound resource's environment
|
|
131
|
+
* (`undefined` is the workspace default). The strongest role per
|
|
132
|
+
* environment wins.
|
|
133
|
+
*/
|
|
134
|
+
environments?: Array<{
|
|
135
|
+
readonly environment: string | undefined;
|
|
136
|
+
readonly role: "viewer" | "contributor";
|
|
137
|
+
}>;
|
|
138
|
+
},
|
|
139
|
+
Providers
|
|
140
|
+
>;
|
|
141
|
+
|
|
142
|
+
const ROLE = {
|
|
143
|
+
viewer: "ENVIRONMENT_ROLE_VIEWER",
|
|
144
|
+
contributor: "ENVIRONMENT_ROLE_CONTRIBUTOR",
|
|
145
|
+
} as const;
|
|
146
|
+
|
|
147
|
+
const roleOf = (role: string | undefined) =>
|
|
148
|
+
role === ROLE.viewer ? "viewer" : role === ROLE.contributor ? "contributor" : undefined;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The roles the props and the bindings ask for, by environment name; the
|
|
152
|
+
* workspace default environment resolves to its name. A `contributor` grant
|
|
153
|
+
* beats a `viewer` grant for the same environment.
|
|
154
|
+
*/
|
|
155
|
+
const desiredRoles = Effect.fn(function* (
|
|
156
|
+
props: ServiceUserProps,
|
|
157
|
+
bindings: ReadonlyArray<ResourceBinding<ServiceUser["Binding"]>>,
|
|
158
|
+
) {
|
|
159
|
+
const entries = [
|
|
160
|
+
...(props.environments ?? []).map((entry) => ({
|
|
161
|
+
environment: envNameOf(entry.environment),
|
|
162
|
+
role: entry.role,
|
|
163
|
+
})),
|
|
164
|
+
...bindings
|
|
165
|
+
.filter((binding) => (binding as { action?: string }).action !== "delete")
|
|
166
|
+
.flatMap((binding) => binding.data?.environments ?? []),
|
|
167
|
+
];
|
|
168
|
+
let defaultName: string | undefined;
|
|
169
|
+
const roles: Record<string, "viewer" | "contributor"> = {};
|
|
170
|
+
for (const entry of entries) {
|
|
171
|
+
let name = entry.environment;
|
|
172
|
+
if (!name) {
|
|
173
|
+
defaultName ??= (yield* workspace.workspaceSettings({})).defaultEnvironmentName ?? "main";
|
|
174
|
+
name = defaultName;
|
|
175
|
+
}
|
|
176
|
+
if (roles[name] !== "contributor") roles[name] = entry.role;
|
|
177
|
+
}
|
|
178
|
+
return roles;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
export const ServiceUserProvider = () =>
|
|
182
|
+
Provider.effect(
|
|
183
|
+
ServiceUser,
|
|
184
|
+
Effect.gen(function* () {
|
|
185
|
+
const observe = Effect.fn(function* (name: string) {
|
|
186
|
+
const found = yield* service
|
|
187
|
+
.serviceUserInfo({ serviceUserName: name })
|
|
188
|
+
.pipe(Effect.catchTag("ServiceUserNotFound", () => Effect.undefined));
|
|
189
|
+
return found?.serviceUser?.serviceUserId ? found.serviceUser : undefined;
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
stables: ["serviceUserId", "name", "tokenId", "tokenSecret"],
|
|
194
|
+
diff: Effect.fn(function* ({ id, news, olds, output }) {
|
|
195
|
+
if (!isResolved(news) || output === undefined) return undefined;
|
|
196
|
+
if ((yield* objectName(id, news.name)) !== output.name)
|
|
197
|
+
return { action: "replace" } as const;
|
|
198
|
+
if ((news.scoped ?? true) !== output.scoped) return { action: "replace" } as const;
|
|
199
|
+
if (seconds(news.expiresIn) !== seconds(olds?.expiresIn))
|
|
200
|
+
return { action: "replace" } as const;
|
|
201
|
+
return undefined;
|
|
202
|
+
}),
|
|
203
|
+
read: Effect.fn(function* ({ output }) {
|
|
204
|
+
if (output === undefined) return undefined;
|
|
205
|
+
const found = yield* observe(output.name);
|
|
206
|
+
return found?.serviceUserId === output.serviceUserId ? output : undefined;
|
|
207
|
+
}),
|
|
208
|
+
reconcile: Effect.fn(function* ({ id, news, output, session, bindings }) {
|
|
209
|
+
const name = output?.name ?? (yield* objectName(id, news.name));
|
|
210
|
+
const scoped = news.scoped ?? true;
|
|
211
|
+
// Observe: the user we created still exists (its secret lives only in state).
|
|
212
|
+
let current: Partial<service.ServiceUser> | undefined = yield* observe(name);
|
|
213
|
+
let token =
|
|
214
|
+
current !== undefined &&
|
|
215
|
+
output !== undefined &&
|
|
216
|
+
current.serviceUserId === output.serviceUserId
|
|
217
|
+
? { tokenId: output.tokenId, tokenSecret: output.tokenSecret }
|
|
218
|
+
: undefined;
|
|
219
|
+
// Ensure: a user we can't read the secret of is replaced by a fresh one.
|
|
220
|
+
if (token === undefined) {
|
|
221
|
+
if (current?.serviceUserId) {
|
|
222
|
+
yield* service
|
|
223
|
+
.deleteServiceUser({ serviceUserId: current.serviceUserId })
|
|
224
|
+
.pipe(Effect.catchTag("ServiceUserNotFound", () => Effect.void));
|
|
225
|
+
}
|
|
226
|
+
yield* session.note(`Creating service user ${name}...`);
|
|
227
|
+
const created = yield* service
|
|
228
|
+
.createServiceUser({
|
|
229
|
+
name,
|
|
230
|
+
scoped,
|
|
231
|
+
expiresInSeconds: seconds(news.expiresIn),
|
|
232
|
+
})
|
|
233
|
+
.pipe(
|
|
234
|
+
Effect.catchTag("ServiceUsersNotAvailable", (e) =>
|
|
235
|
+
Effect.fail(
|
|
236
|
+
new ServiceUsersNotAvailable({
|
|
237
|
+
message: `${e.message ?? "Service users are not available on this plan."} Modal bindings give each host its own service user; on this plan, add Modal.DeployerHostIdentity to the Stack's providers to bind your own token into hosts instead.`,
|
|
238
|
+
}),
|
|
239
|
+
),
|
|
240
|
+
),
|
|
241
|
+
);
|
|
242
|
+
if (!created.serviceUserId || !created.tokenId || !created.tokenSecret) {
|
|
243
|
+
return yield* new ServiceUserUnexpectedResponse({
|
|
244
|
+
message: `Modal returned no token for service user ${name}`,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
token = {
|
|
248
|
+
tokenId: created.tokenId,
|
|
249
|
+
tokenSecret: Redacted.isRedacted(created.tokenSecret)
|
|
250
|
+
? created.tokenSecret
|
|
251
|
+
: Redacted.make(created.tokenSecret),
|
|
252
|
+
};
|
|
253
|
+
current = (yield* observe(name)) ?? { serviceUserId: created.serviceUserId, name };
|
|
254
|
+
}
|
|
255
|
+
const user: Partial<service.ServiceUser> = current ?? {};
|
|
256
|
+
const serviceUserId = user.serviceUserId ?? "";
|
|
257
|
+
// Sync roles against the observed ones; a dropped environment loses access.
|
|
258
|
+
const observed: Record<string, string | undefined> = {};
|
|
259
|
+
for (const entry of user.environmentRoles ?? []) {
|
|
260
|
+
if (entry.environmentName) observed[entry.environmentName] = roleOf(entry.role);
|
|
261
|
+
}
|
|
262
|
+
const desired = scoped ? yield* desiredRoles(news, bindings) : {};
|
|
263
|
+
for (const [env, role] of Object.entries(desired)) {
|
|
264
|
+
if (observed[env] === role) continue;
|
|
265
|
+
yield* environment.setEnvironmentRole({
|
|
266
|
+
environmentName: env,
|
|
267
|
+
serviceUserId,
|
|
268
|
+
role: ROLE[role],
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
for (const env of Object.keys(observed)) {
|
|
272
|
+
if (env in desired || observed[env] === undefined) continue;
|
|
273
|
+
yield* environment.setEnvironmentRole({
|
|
274
|
+
environmentName: env,
|
|
275
|
+
serviceUserId,
|
|
276
|
+
role: "ENVIRONMENT_ROLE_NO_ACCESS",
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
serviceUserId,
|
|
281
|
+
name,
|
|
282
|
+
tokenId: token.tokenId,
|
|
283
|
+
tokenSecret: token.tokenSecret,
|
|
284
|
+
scoped,
|
|
285
|
+
environments: desired,
|
|
286
|
+
};
|
|
287
|
+
}),
|
|
288
|
+
delete: Effect.fn(function* ({ output }) {
|
|
289
|
+
yield* service
|
|
290
|
+
.deleteServiceUser({ serviceUserId: output.serviceUserId })
|
|
291
|
+
.pipe(Effect.catchTag("ServiceUserNotFound", () => Effect.void));
|
|
292
|
+
}),
|
|
293
|
+
};
|
|
294
|
+
}),
|
|
295
|
+
);
|
package/src/Volume.ts
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import * as volume from "@distilled.cloud/modal/volume";
|
|
2
|
+
import { Unowned } from "alchemy/AdoptPolicy";
|
|
3
|
+
import { isResolved } from "alchemy/Diff";
|
|
4
|
+
import * as Provider from "alchemy/Provider";
|
|
5
|
+
import { Resource } from "alchemy/Resource";
|
|
6
|
+
import * as Data from "effect/Data";
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import {
|
|
9
|
+
type EnvironmentInput,
|
|
10
|
+
envNameOf,
|
|
11
|
+
environmentNames,
|
|
12
|
+
environmentOf,
|
|
13
|
+
isGeneratedName,
|
|
14
|
+
listAll,
|
|
15
|
+
objectName,
|
|
16
|
+
} from "./internal.ts";
|
|
17
|
+
import { declarable } from "./internal/Declared.ts";
|
|
18
|
+
import type { Providers } from "./Providers.ts";
|
|
19
|
+
import { isRuntimeName } from "./RuntimeOwner.ts";
|
|
20
|
+
|
|
21
|
+
export class VolumeUnexpectedResponse extends Data.TaggedError("Modal.VolumeUnexpectedResponse")<{
|
|
22
|
+
readonly message: string;
|
|
23
|
+
}> {}
|
|
24
|
+
|
|
25
|
+
export interface VolumeProps {
|
|
26
|
+
/**
|
|
27
|
+
* Volume name, unique per environment. If omitted, a unique name is
|
|
28
|
+
* generated from the stack, stage and logical ID. Changing it renames the
|
|
29
|
+
* volume in place.
|
|
30
|
+
*/
|
|
31
|
+
name?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Environment the volume lives in. Changing it replaces the volume.
|
|
34
|
+
* @default the workspace's default environment
|
|
35
|
+
*/
|
|
36
|
+
environment?: EnvironmentInput;
|
|
37
|
+
/**
|
|
38
|
+
* Volume filesystem version. `2` supports many concurrent writers and
|
|
39
|
+
* files of any count; `1` is Modal's original format. Changing it
|
|
40
|
+
* replaces the volume.
|
|
41
|
+
* @default 2
|
|
42
|
+
*/
|
|
43
|
+
version?: 1 | 2;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface VolumeAttributes {
|
|
47
|
+
/** Volume ID (`vo-…`). */
|
|
48
|
+
volumeId: string;
|
|
49
|
+
/** Volume name. */
|
|
50
|
+
name: string;
|
|
51
|
+
/** Environment name, or `undefined` for the workspace default. */
|
|
52
|
+
environment: string | undefined;
|
|
53
|
+
/** Volume filesystem version. */
|
|
54
|
+
version: 1 | 2;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A Modal Volume: a persistent, shared filesystem that Sandboxes and
|
|
59
|
+
* Functions mount at a path. Writes from one container become visible to
|
|
60
|
+
* others after a commit (Sandboxes commit when they exit).
|
|
61
|
+
*
|
|
62
|
+
* Declare a Volume here for data every deployment shares, such as a model
|
|
63
|
+
* cache or a dataset. For one volume per user or session, create them at
|
|
64
|
+
* runtime with {@link CreateVolume}; to read and write files from a Worker or
|
|
65
|
+
* Lambda without a sandbox, bind {@link ReadWriteVolume}.
|
|
66
|
+
*
|
|
67
|
+
* Modal volumes have no tags. Ownership is inferred from Alchemy state: a
|
|
68
|
+
* volume found under an explicit `name` without prior state is reported as
|
|
69
|
+
* unowned.
|
|
70
|
+
*
|
|
71
|
+
* ### Creating a Volume
|
|
72
|
+
* **Example:** A volume with a generated name
|
|
73
|
+
* ```typescript
|
|
74
|
+
* import * as Modal from "@alchemy.run/modal";
|
|
75
|
+
*
|
|
76
|
+
* export const Cache = Modal.Volume("Cache", {});
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* **Example:** A named volume in an environment
|
|
80
|
+
* ```typescript
|
|
81
|
+
* import * as Modal from "@alchemy.run/modal";
|
|
82
|
+
*
|
|
83
|
+
* export const Agents = Modal.Environment("Agents", {});
|
|
84
|
+
* export const Models = Modal.Volume("Models", { name: "model-weights", environment: Agents });
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* ### Mounting in a Sandbox
|
|
88
|
+
* **Example:** Mount at `/cache`
|
|
89
|
+
* ```typescript
|
|
90
|
+
* // construction phase
|
|
91
|
+
* const cache = yield* Modal.MountVolume(Cache, { path: "/cache" });
|
|
92
|
+
* const sandboxes = yield* Modal.CreateSandbox(Coder);
|
|
93
|
+
* // per request
|
|
94
|
+
* const sandbox = yield* sandboxes.create({ mounts: [cache] });
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* ### Reading files from a Worker
|
|
98
|
+
* **Example:** Bind file access
|
|
99
|
+
* ```typescript
|
|
100
|
+
* import * as Cloudflare from "@alchemy.run/cloudflare";
|
|
101
|
+
* import * as Modal from "@alchemy.run/modal";
|
|
102
|
+
* import * as Effect from "effect/Effect";
|
|
103
|
+
* import * as HttpServerResponse from "effect/http/HttpServerResponse";
|
|
104
|
+
* import { Cache } from "./Storage.ts";
|
|
105
|
+
*
|
|
106
|
+
* export default class Api extends Cloudflare.Worker<Api>()(
|
|
107
|
+
* "Api",
|
|
108
|
+
* { main: import.meta.url },
|
|
109
|
+
* Effect.gen(function* () {
|
|
110
|
+
* const cache = yield* Modal.ReadWriteVolume(Cache);
|
|
111
|
+
* return {
|
|
112
|
+
* fetch: Effect.gen(function* () {
|
|
113
|
+
* yield* cache.writeFile("/hello.txt", "hello");
|
|
114
|
+
* const text = yield* cache.readFileString("/hello.txt");
|
|
115
|
+
* return HttpServerResponse.text(text);
|
|
116
|
+
* }).pipe(Effect.orDie),
|
|
117
|
+
* };
|
|
118
|
+
* }).pipe(Effect.provide(Modal.ReadWriteVolumeHttp)),
|
|
119
|
+
* ) {}
|
|
120
|
+
* ```
|
|
121
|
+
*
|
|
122
|
+
* ### Looking up an existing volume
|
|
123
|
+
* `Volume.lookup` references a volume Alchemy doesn't manage (created with
|
|
124
|
+
* the Modal CLI or by another stack). It is read during plan/deploy, never
|
|
125
|
+
* created, updated or deleted.
|
|
126
|
+
*
|
|
127
|
+
* **Example:** Reference an existing volume
|
|
128
|
+
* ```typescript
|
|
129
|
+
* const weights = Modal.Volume.lookup({ name: "model-weights" });
|
|
130
|
+
* ```
|
|
131
|
+
*
|
|
132
|
+
* @resource
|
|
133
|
+
* @product Volume
|
|
134
|
+
*/
|
|
135
|
+
export const Volume = declarable<Volume>()(Resource<Volume>("Modal.Volume"));
|
|
136
|
+
|
|
137
|
+
export type Volume = Resource<
|
|
138
|
+
"Modal.Volume",
|
|
139
|
+
VolumeProps,
|
|
140
|
+
VolumeAttributes,
|
|
141
|
+
never,
|
|
142
|
+
Providers,
|
|
143
|
+
VolumeLookup
|
|
144
|
+
>;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Props for {@link Volume.lookup}: find a volume this stack
|
|
148
|
+
* doesn't manage by its name.
|
|
149
|
+
*/
|
|
150
|
+
export interface VolumeLookup {
|
|
151
|
+
/** Volume name. */
|
|
152
|
+
name: string;
|
|
153
|
+
/**
|
|
154
|
+
* Modal environment the object lives in.
|
|
155
|
+
* @default the workspace's default environment
|
|
156
|
+
*/
|
|
157
|
+
environment?: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const versionToWire = (version: 1 | 2 | undefined): volume.VolumeFsVersion =>
|
|
161
|
+
version === 1 ? "VOLUME_FS_VERSION_V1" : "VOLUME_FS_VERSION_V2";
|
|
162
|
+
|
|
163
|
+
const versionFromWire = (version: string | undefined): 1 | 2 =>
|
|
164
|
+
version === "VOLUME_FS_VERSION_V1" ? 1 : 2;
|
|
165
|
+
|
|
166
|
+
/** A volume by name, or `undefined` when missing. */
|
|
167
|
+
export const lookupVolume = Effect.fn(function* (
|
|
168
|
+
getOrCreate: Effect.Success<typeof volume.volumeGetOrCreate>,
|
|
169
|
+
name: string,
|
|
170
|
+
env: string | undefined,
|
|
171
|
+
) {
|
|
172
|
+
const found = yield* getOrCreate({
|
|
173
|
+
deploymentName: name,
|
|
174
|
+
environmentName: environmentOf(env),
|
|
175
|
+
objectCreationType: "OBJECT_CREATION_TYPE_UNSPECIFIED",
|
|
176
|
+
}).pipe(Effect.catchTag("VolumeNotFound", () => Effect.undefined));
|
|
177
|
+
if (!found?.volumeId) return undefined;
|
|
178
|
+
return {
|
|
179
|
+
volumeId: found.volumeId,
|
|
180
|
+
name,
|
|
181
|
+
environment: env,
|
|
182
|
+
version: versionFromWire(found.version ?? found.metadata?.version),
|
|
183
|
+
} satisfies VolumeAttributes;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get a volume by name, creating it when missing: the one implementation
|
|
188
|
+
* behind {@link Volume}'s reconcile and the runtime {@link CreateVolume}
|
|
189
|
+
* client.
|
|
190
|
+
*/
|
|
191
|
+
export const ensureVolume = Effect.fn(function* (
|
|
192
|
+
getOrCreate: Effect.Success<typeof volume.volumeGetOrCreate>,
|
|
193
|
+
name: string,
|
|
194
|
+
env: string | undefined,
|
|
195
|
+
version?: 1 | 2,
|
|
196
|
+
) {
|
|
197
|
+
const created = yield* getOrCreate({
|
|
198
|
+
deploymentName: name,
|
|
199
|
+
environmentName: environmentOf(env),
|
|
200
|
+
objectCreationType: "OBJECT_CREATION_TYPE_CREATE_IF_MISSING",
|
|
201
|
+
version: versionToWire(version),
|
|
202
|
+
});
|
|
203
|
+
if (!created.volumeId) {
|
|
204
|
+
return yield* new VolumeUnexpectedResponse({
|
|
205
|
+
message: `Modal returned no id for volume ${name}`,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
volumeId: created.volumeId,
|
|
210
|
+
name: created.metadata?.name ?? name,
|
|
211
|
+
environment: env,
|
|
212
|
+
version: versionFromWire(created.version ?? created.metadata?.version),
|
|
213
|
+
} satisfies VolumeAttributes;
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
export const VolumeProvider = () =>
|
|
217
|
+
Provider.effect(
|
|
218
|
+
Volume,
|
|
219
|
+
Effect.gen(function* () {
|
|
220
|
+
const observe = Effect.fn(function* (
|
|
221
|
+
volumeId: string | undefined,
|
|
222
|
+
name: string,
|
|
223
|
+
env: string | undefined,
|
|
224
|
+
) {
|
|
225
|
+
if (volumeId !== undefined) {
|
|
226
|
+
const byId = yield* volume
|
|
227
|
+
.volumeGetById({ volumeId })
|
|
228
|
+
.pipe(Effect.catchTag("VolumeNotFound", () => Effect.undefined));
|
|
229
|
+
if (byId?.volumeId) {
|
|
230
|
+
return {
|
|
231
|
+
volumeId: byId.volumeId,
|
|
232
|
+
name: byId.metadata?.name ?? name,
|
|
233
|
+
environment: env,
|
|
234
|
+
version: versionFromWire(byId.metadata?.version),
|
|
235
|
+
} satisfies VolumeAttributes;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return yield* lookupVolume(yield* volume.volumeGetOrCreate, name, env);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
stables: ["volumeId", "environment", "version"],
|
|
243
|
+
diff: Effect.fn(function* ({ news, olds }) {
|
|
244
|
+
if (!isResolved(news)) return undefined;
|
|
245
|
+
if (envNameOf(news.environment) !== envNameOf(olds?.environment))
|
|
246
|
+
return { action: "replace" } as const;
|
|
247
|
+
if ((news.version ?? 2) !== (olds?.version ?? 2)) return { action: "replace" } as const;
|
|
248
|
+
return undefined;
|
|
249
|
+
}),
|
|
250
|
+
lookup: Effect.fn(function* ({ name, environment }) {
|
|
251
|
+
return yield* observe(undefined, name, environment);
|
|
252
|
+
}),
|
|
253
|
+
|
|
254
|
+
read: Effect.fn(function* ({ id, olds, output }) {
|
|
255
|
+
const name = output?.name ?? (yield* objectName(id, olds?.name));
|
|
256
|
+
const observed = yield* observe(output?.volumeId, name, envNameOf(olds?.environment));
|
|
257
|
+
if (!observed) return undefined;
|
|
258
|
+
return output !== undefined || olds?.name === undefined ? observed : Unowned(observed);
|
|
259
|
+
}),
|
|
260
|
+
reconcile: Effect.fn(function* ({ id, news, output, session }) {
|
|
261
|
+
const name = yield* objectName(id, news.name);
|
|
262
|
+
// Observe: the volume we created (by id) or one with the name.
|
|
263
|
+
let current = yield* observe(
|
|
264
|
+
output?.volumeId,
|
|
265
|
+
output?.name ?? name,
|
|
266
|
+
envNameOf(news.environment),
|
|
267
|
+
);
|
|
268
|
+
// Ensure
|
|
269
|
+
if (!current) {
|
|
270
|
+
yield* session.note(`Creating volume ${name}...`);
|
|
271
|
+
current = yield* ensureVolume(
|
|
272
|
+
yield* volume.volumeGetOrCreate,
|
|
273
|
+
name,
|
|
274
|
+
envNameOf(news.environment),
|
|
275
|
+
news.version,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
// Sync the name: a renamed volume keeps its id and files.
|
|
279
|
+
if (current.name !== name) {
|
|
280
|
+
yield* session.note(`Renaming volume ${current.name} to ${name}...`);
|
|
281
|
+
yield* volume.renameVolume({ volumeId: current.volumeId, name });
|
|
282
|
+
current = { ...current, name };
|
|
283
|
+
}
|
|
284
|
+
return current;
|
|
285
|
+
}),
|
|
286
|
+
delete: Effect.fn(function* ({ output }) {
|
|
287
|
+
yield* volume
|
|
288
|
+
.deleteVolume({
|
|
289
|
+
volumeId: output.volumeId,
|
|
290
|
+
environmentName: environmentOf(output.environment),
|
|
291
|
+
})
|
|
292
|
+
.pipe(Effect.catchTag("VolumeNotFound", () => Effect.void));
|
|
293
|
+
}),
|
|
294
|
+
list: Effect.fn(function* () {
|
|
295
|
+
const envs = yield* environmentNames;
|
|
296
|
+
const all: VolumeAttributes[] = [];
|
|
297
|
+
for (const env of envs) {
|
|
298
|
+
const items = yield* listAll(
|
|
299
|
+
(pagination) =>
|
|
300
|
+
volume
|
|
301
|
+
.listVolume({ environmentName: env, pagination })
|
|
302
|
+
.pipe(Effect.map((response) => response.items ?? [])),
|
|
303
|
+
(item) => item.createdAt,
|
|
304
|
+
);
|
|
305
|
+
for (const item of items) {
|
|
306
|
+
const name = item.label ?? item.metadata?.name;
|
|
307
|
+
if (!item.volumeId || !(isGeneratedName(name) || isRuntimeName(name))) continue;
|
|
308
|
+
all.push({
|
|
309
|
+
volumeId: item.volumeId,
|
|
310
|
+
name: name ?? "",
|
|
311
|
+
environment: env,
|
|
312
|
+
version: versionFromWire(item.metadata?.version),
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return all;
|
|
317
|
+
}),
|
|
318
|
+
};
|
|
319
|
+
}),
|
|
320
|
+
);
|