@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,45 @@
|
|
|
1
|
+
import * as Binding from "alchemy/Binding";
|
|
2
|
+
import type * as Effect from "effect/Effect";
|
|
3
|
+
import type { Queue } from "./Queue.ts";
|
|
4
|
+
import type { ReadWriteQueueClient } from "./QueueClient.ts";
|
|
5
|
+
|
|
6
|
+
export type { ReadWriteQueueClient } from "./QueueClient.ts";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Put and take items on a Modal Queue from any runtime. Items are JSON.
|
|
10
|
+
*
|
|
11
|
+
* ### Bind a queue
|
|
12
|
+
* **Example:** From a Cloudflare Worker
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import * as Cloudflare from "@alchemy.run/cloudflare";
|
|
15
|
+
* import * as Modal from "@alchemy.run/modal";
|
|
16
|
+
* import * as Effect from "effect/Effect";
|
|
17
|
+
* import * as HttpServerResponse from "effect/http/HttpServerResponse";
|
|
18
|
+
* import { Jobs } from "./Storage.ts";
|
|
19
|
+
*
|
|
20
|
+
* export default class Api extends Cloudflare.Worker<Api>()(
|
|
21
|
+
* "Api",
|
|
22
|
+
* { main: import.meta.url },
|
|
23
|
+
* Effect.gen(function* () {
|
|
24
|
+
* const queue = yield* Modal.ReadWriteQueue(Jobs);
|
|
25
|
+
* return {
|
|
26
|
+
* fetch: Effect.gen(function* () {
|
|
27
|
+
* yield* queue.put({ n: 1 });
|
|
28
|
+
* const items = yield* queue.take({ count: 10 });
|
|
29
|
+
* return yield* HttpServerResponse.json(items);
|
|
30
|
+
* }).pipe(Effect.orDie),
|
|
31
|
+
* };
|
|
32
|
+
* }).pipe(Effect.provide(Modal.ReadWriteQueueHttp)),
|
|
33
|
+
* ) {}
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @binding
|
|
37
|
+
* @product Queue
|
|
38
|
+
*/
|
|
39
|
+
export interface ReadWriteQueue extends Binding.Service<
|
|
40
|
+
ReadWriteQueue,
|
|
41
|
+
"Modal.ReadWriteQueue",
|
|
42
|
+
(queue: Queue) => Effect.Effect<ReadWriteQueueClient>
|
|
43
|
+
> {}
|
|
44
|
+
|
|
45
|
+
export const ReadWriteQueue = Binding.Service<ReadWriteQueue>("Modal.ReadWriteQueue");
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import { makeModalBinding } from "./BindingHttp.ts";
|
|
4
|
+
import type { Queue } from "./Queue.ts";
|
|
5
|
+
import { QueueOps, makeReadWriteQueueClient } from "./QueueClient.ts";
|
|
6
|
+
import { ReadWriteQueue } from "./ReadWriteQueue.ts";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* {@link ReadWriteQueue} over Modal's API. Grants the host's identity (a scoped
|
|
10
|
+
* `Modal.ServiceUser`) the `contributor` role on the queue's environment.
|
|
11
|
+
*
|
|
12
|
+
* @layer
|
|
13
|
+
* @product Queue
|
|
14
|
+
* @provides Modal.ReadWriteQueue
|
|
15
|
+
*/
|
|
16
|
+
export const ReadWriteQueueHttp = Layer.effect(
|
|
17
|
+
ReadWriteQueue,
|
|
18
|
+
makeModalBinding({
|
|
19
|
+
tag: "Modal.ReadWriteQueue",
|
|
20
|
+
operations: QueueOps,
|
|
21
|
+
role: "contributor",
|
|
22
|
+
make: Effect.fn(function* (queue: Queue, ops) {
|
|
23
|
+
const queueId = yield* queue.queueId;
|
|
24
|
+
return makeReadWriteQueueClient(queueId, ops);
|
|
25
|
+
}),
|
|
26
|
+
}),
|
|
27
|
+
);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as Binding from "alchemy/Binding";
|
|
2
|
+
import type * as Effect from "effect/Effect";
|
|
3
|
+
import type { Volume } from "./Volume.ts";
|
|
4
|
+
import type { ReadWriteVolumeClient } from "./VolumeClient.ts";
|
|
5
|
+
|
|
6
|
+
export type { ReadWriteVolumeClient } from "./VolumeClient.ts";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Read and write a Modal Volume's files from any runtime without mounting it.
|
|
10
|
+
*
|
|
11
|
+
* ### Bind a volume
|
|
12
|
+
* **Example:** ReadWrite from a Cloudflare Worker
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import * as Cloudflare from "@alchemy.run/cloudflare";
|
|
15
|
+
* import * as Modal from "@alchemy.run/modal";
|
|
16
|
+
* import * as Effect from "effect/Effect";
|
|
17
|
+
* import * as HttpServerResponse from "effect/http/HttpServerResponse";
|
|
18
|
+
* import { Workspace } from "./Storage.ts";
|
|
19
|
+
*
|
|
20
|
+
* export default class Api extends Cloudflare.Worker<Api>()(
|
|
21
|
+
* "Api",
|
|
22
|
+
* { main: import.meta.url },
|
|
23
|
+
* Effect.gen(function* () {
|
|
24
|
+
* const volume = yield* Modal.ReadWriteVolume(Workspace);
|
|
25
|
+
* return {
|
|
26
|
+
* fetch: Effect.gen(function* () {
|
|
27
|
+
* yield* volume.writeFile("/hello.txt", "hello");
|
|
28
|
+
* const text = yield* volume.readFileString("/hello.txt");
|
|
29
|
+
* return HttpServerResponse.text(text);
|
|
30
|
+
* }).pipe(Effect.orDie),
|
|
31
|
+
* };
|
|
32
|
+
* }).pipe(Effect.provide(Modal.ReadWriteVolumeHttp)),
|
|
33
|
+
* ) {}
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @binding
|
|
37
|
+
* @product Volume
|
|
38
|
+
*/
|
|
39
|
+
export interface ReadWriteVolume extends Binding.Service<
|
|
40
|
+
ReadWriteVolume,
|
|
41
|
+
"Modal.ReadWriteVolume",
|
|
42
|
+
(volume: Volume) => Effect.Effect<ReadWriteVolumeClient>
|
|
43
|
+
> {}
|
|
44
|
+
|
|
45
|
+
export const ReadWriteVolume = Binding.Service<ReadWriteVolume>("Modal.ReadWriteVolume");
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import { makeModalBinding } from "./BindingHttp.ts";
|
|
4
|
+
import { ReadWriteVolume } from "./ReadWriteVolume.ts";
|
|
5
|
+
import type { Volume } from "./Volume.ts";
|
|
6
|
+
import { makeReadWriteVolumeClient } from "./VolumeClient.ts";
|
|
7
|
+
import { VolumeFileOps } from "./VolumeFiles.ts";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* {@link ReadWriteVolume} over Modal's volume API. Grants the host's identity (a scoped
|
|
11
|
+
* `Modal.ServiceUser`) the `contributor` role on the volume's environment.
|
|
12
|
+
*
|
|
13
|
+
* @layer
|
|
14
|
+
* @product Volume
|
|
15
|
+
* @provides Modal.ReadWriteVolume
|
|
16
|
+
*/
|
|
17
|
+
export const ReadWriteVolumeHttp = Layer.effect(
|
|
18
|
+
ReadWriteVolume,
|
|
19
|
+
makeModalBinding({
|
|
20
|
+
tag: "Modal.ReadWriteVolume",
|
|
21
|
+
operations: VolumeFileOps,
|
|
22
|
+
role: "contributor",
|
|
23
|
+
make: Effect.fn(function* (volume: Volume, ops) {
|
|
24
|
+
const volumeId = yield* volume.volumeId;
|
|
25
|
+
return makeReadWriteVolumeClient(volumeId, ops);
|
|
26
|
+
}),
|
|
27
|
+
}),
|
|
28
|
+
);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import * as Binding from "alchemy/Binding";
|
|
2
|
+
import * as Output from "alchemy/Output";
|
|
3
|
+
import { CurrentRuntimeContext } from "alchemy/RuntimeContext";
|
|
4
|
+
import { Stack } from "alchemy/Stack";
|
|
5
|
+
import { Stage } from "alchemy/Stage";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Option from "effect/Option";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Who created an object at run time: the stack, stage and binding host
|
|
11
|
+
* (`alchemy::stack`, `alchemy::stage`, `alchemy::id`).
|
|
12
|
+
*
|
|
13
|
+
* The `Create*` bindings mark every object they create with it, so the
|
|
14
|
+
* objects can be found and cleaned up. Destroying the stack does not delete
|
|
15
|
+
* them. Modal tags sandboxes, so a sandbox carries the three keys as tags;
|
|
16
|
+
* volumes, dicts, queues and secrets have only a name, so theirs starts with
|
|
17
|
+
* {@link runtimePrefix}, and `alchemy nuke` deletes them by it.
|
|
18
|
+
*/
|
|
19
|
+
export interface RuntimeOwner {
|
|
20
|
+
readonly stack: string;
|
|
21
|
+
readonly stage: string;
|
|
22
|
+
readonly id: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The owner as Modal sandbox tags. */
|
|
26
|
+
export const runtimeTags = (owner: RuntimeOwner): Record<string, string> => ({
|
|
27
|
+
"alchemy::stack": owner.stack,
|
|
28
|
+
"alchemy::stage": owner.stage,
|
|
29
|
+
"alchemy::id": owner.id,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const toLabel = (value: string) =>
|
|
33
|
+
value
|
|
34
|
+
.toLowerCase()
|
|
35
|
+
.replace(/[^a-z0-9-]+/g, "-")
|
|
36
|
+
.replace(/-+/g, "-")
|
|
37
|
+
.replace(/^-|-$/g, "");
|
|
38
|
+
|
|
39
|
+
/** FNV-1a, 32 bits: a short, stable digest of the full identity. */
|
|
40
|
+
const digest = (value: string) => {
|
|
41
|
+
let hash = 0x811c9dc5;
|
|
42
|
+
for (let i = 0; i < value.length; i++) {
|
|
43
|
+
hash ^= value.charCodeAt(i);
|
|
44
|
+
hash = Math.imul(hash, 0x01000193);
|
|
45
|
+
}
|
|
46
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The name prefix of objects that carry only a name:
|
|
51
|
+
* `alc-<stack-stage-id>-<digest>`, the identity readable and cut to 16
|
|
52
|
+
* characters, then a digest of all three. An object `name` becomes
|
|
53
|
+
* `<prefix>-<name>`. The `alc-` marker is how `alchemy nuke` recognizes them.
|
|
54
|
+
*/
|
|
55
|
+
export const runtimePrefix = (owner: RuntimeOwner): string => {
|
|
56
|
+
const readable =
|
|
57
|
+
toLabel(`${owner.stack}-${owner.stage}-${owner.id}`).slice(0, 16).replace(/-+$/, "") || "x";
|
|
58
|
+
return `alc-${readable}-${digest(`${owner.stack}/${owner.stage}/${owner.id}`)}`;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** Whether `name` carries some owner's {@link runtimePrefix}. */
|
|
62
|
+
export const isRuntimeName = (name: string | undefined): boolean =>
|
|
63
|
+
name !== undefined && /^alc-[a-z0-9](?:[a-z0-9-]*[a-z0-9])?-[0-9a-f]{8}-./.test(name);
|
|
64
|
+
|
|
65
|
+
/** The name an object gets: `<prefix>-<name>` with an owner, `name` without one. */
|
|
66
|
+
export const ownedName = (owner: RuntimeOwner | undefined, name: string) =>
|
|
67
|
+
owner === undefined ? name : `${runtimePrefix(owner)}-${name}`;
|
|
68
|
+
|
|
69
|
+
/** An owned object's name without the prefix, or `undefined` when another owner's. */
|
|
70
|
+
export const unownedName = (owner: RuntimeOwner | undefined, name: string) => {
|
|
71
|
+
if (owner === undefined) return name;
|
|
72
|
+
const prefix = `${runtimePrefix(owner)}-`;
|
|
73
|
+
return name.startsWith(prefix) ? name.slice(prefix.length) : undefined;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const ENV = {
|
|
77
|
+
stack: "ALCHEMY_MODAL_OWNER_STACK",
|
|
78
|
+
stage: "ALCHEMY_MODAL_OWNER_STAGE",
|
|
79
|
+
id: "ALCHEMY_MODAL_OWNER_ID",
|
|
80
|
+
} as const;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The owner a `Create*` binding marks its objects with. Yield it while the
|
|
84
|
+
* binding's layer is built and call the result inside the binding: at deploy
|
|
85
|
+
* it binds the host's identity into the host's env; at run time its Effect
|
|
86
|
+
* reads it back. Without a host (a script or an Action) there is no owner.
|
|
87
|
+
*/
|
|
88
|
+
export const RuntimeOwnerOf = Effect.fn(function* () {
|
|
89
|
+
const runtime = yield* CurrentRuntimeContext;
|
|
90
|
+
if (runtime === undefined) return Effect.succeed<RuntimeOwner | undefined>(undefined);
|
|
91
|
+
if (!globalThis.__ALCHEMY_RUNTIME__) {
|
|
92
|
+
const host = yield* Binding.Host;
|
|
93
|
+
// The deploying stack and stage, read without requiring them, so the
|
|
94
|
+
// binding's `make` needs nothing a script or an Action lacks.
|
|
95
|
+
const stack = Option.getOrUndefined(yield* Effect.serviceOption(Stack));
|
|
96
|
+
const stage = Option.getOrUndefined(yield* Effect.serviceOption(Stage));
|
|
97
|
+
if (host !== undefined && stack !== undefined && stage !== undefined) {
|
|
98
|
+
yield* runtime.set(ENV.stack, Output.literal(stack.name));
|
|
99
|
+
yield* runtime.set(ENV.stage, Output.literal(stage));
|
|
100
|
+
yield* runtime.set(ENV.id, Output.literal(host.LogicalId));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return Effect.gen(function* () {
|
|
104
|
+
const stack = yield* runtime.get<string>(ENV.stack);
|
|
105
|
+
const stage = yield* runtime.get<string>(ENV.stage);
|
|
106
|
+
const id = yield* runtime.get<string>(ENV.id);
|
|
107
|
+
return stack === undefined || stage === undefined || id === undefined
|
|
108
|
+
? undefined
|
|
109
|
+
: ({ stack, stage, id } satisfies RuntimeOwner);
|
|
110
|
+
});
|
|
111
|
+
});
|
package/src/Sandbox.ts
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import * as BuildPlan from "@alchemy.run/docker/BuildPlan";
|
|
2
|
+
import * as image from "@distilled.cloud/modal/image";
|
|
3
|
+
import * as secret from "@distilled.cloud/modal/secret";
|
|
4
|
+
import type * as Bundle from "alchemy/Bundle/Bundle";
|
|
5
|
+
import { deepEqual, isResolved } from "alchemy/Diff";
|
|
6
|
+
import { mountInstallLines } from "alchemy/Mount";
|
|
7
|
+
import { Platform, type Main, type PlatformProps } from "alchemy/Platform";
|
|
8
|
+
import * as Provider from "alchemy/Provider";
|
|
9
|
+
import type { Resource } from "alchemy/Resource";
|
|
10
|
+
import {
|
|
11
|
+
createHostRuntimeContext,
|
|
12
|
+
type HostRuntimeContext,
|
|
13
|
+
type ServerHost,
|
|
14
|
+
} from "alchemy/Runtime/Process";
|
|
15
|
+
import { Stack } from "alchemy/Stack";
|
|
16
|
+
import { sha256Object } from "alchemy/Util/sha256";
|
|
17
|
+
import * as Effect from "effect/Effect";
|
|
18
|
+
import * as Redacted from "effect/Redacted";
|
|
19
|
+
import type { App } from "./App.ts";
|
|
20
|
+
import { buildImage, ImageBuildOps } from "./ImageBuild.ts";
|
|
21
|
+
import { checkCommands, lowerPlan, MODAL_CAPABILITIES } from "./ImagePlan.ts";
|
|
22
|
+
import { envNameOf, objectName } from "./internal.ts";
|
|
23
|
+
import { resolveDeclarations } from "./internal/Declared.ts";
|
|
24
|
+
import {
|
|
25
|
+
boundCapabilities,
|
|
26
|
+
boundMounts,
|
|
27
|
+
checkModalMounts,
|
|
28
|
+
type ModalMounts,
|
|
29
|
+
type Mount,
|
|
30
|
+
type MountBindings,
|
|
31
|
+
type MountContract,
|
|
32
|
+
modalMountsOf,
|
|
33
|
+
} from "./Mounts.ts";
|
|
34
|
+
import type { Providers } from "./Providers.ts";
|
|
35
|
+
import { putSecret } from "./Secret.ts";
|
|
36
|
+
|
|
37
|
+
const DEFAULT_PORT = 8080;
|
|
38
|
+
|
|
39
|
+
type Ref<T> = T | Effect.Effect<T, never, Providers>;
|
|
40
|
+
|
|
41
|
+
export interface SandboxProps extends PlatformProps {
|
|
42
|
+
/**
|
|
43
|
+
* Module entrypoint, bundled and baked into the sandbox image. Typically
|
|
44
|
+
* `import.meta.url`. A change to the bundle builds a new image.
|
|
45
|
+
*/
|
|
46
|
+
main: string;
|
|
47
|
+
/** The App sandboxes run in. */
|
|
48
|
+
app: Ref<App>;
|
|
49
|
+
/**
|
|
50
|
+
* Base image the bundle runs on. It needs Bun on the `PATH`.
|
|
51
|
+
* @default "oven/bun:1"
|
|
52
|
+
*/
|
|
53
|
+
image?: string;
|
|
54
|
+
/** Dockerfile lines run on the base before the bundle is added. */
|
|
55
|
+
commands?: string[];
|
|
56
|
+
/**
|
|
57
|
+
* Port the program's `fetch` handler listens on, opened as an encrypted
|
|
58
|
+
* tunnel on every sandbox.
|
|
59
|
+
* @default 8080
|
|
60
|
+
*/
|
|
61
|
+
port?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Named export to load from `main`.
|
|
64
|
+
* @default "default"
|
|
65
|
+
*/
|
|
66
|
+
handler?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Extra environment variables for the program. Stored, with the
|
|
69
|
+
* bindings' env, in a Modal Secret attached to every sandbox.
|
|
70
|
+
*/
|
|
71
|
+
env?: Record<string, any>;
|
|
72
|
+
/** Bundler options. */
|
|
73
|
+
build?: Bundle.BundleConfig;
|
|
74
|
+
/**
|
|
75
|
+
* Volumes and cloud buckets mounted into every sandbox:
|
|
76
|
+
* `[{ volume: Data, path: "/data" }]`. `Modal.MountVolume` and
|
|
77
|
+
* `Modal.MountBucket` in the program add more.
|
|
78
|
+
*/
|
|
79
|
+
mounts?: Mount[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface SandboxAttributes {
|
|
83
|
+
/** The image with the bundled program (`im-…`). */
|
|
84
|
+
imageId: string;
|
|
85
|
+
/** The App sandboxes run in. */
|
|
86
|
+
appId: string;
|
|
87
|
+
/** Name of the App. */
|
|
88
|
+
appName: string | undefined;
|
|
89
|
+
/** Environment of the App. */
|
|
90
|
+
environment: string | undefined;
|
|
91
|
+
/** The Modal Secret holding the program's env (bindings' credentials included). */
|
|
92
|
+
secretId: string;
|
|
93
|
+
/** Name of that secret. */
|
|
94
|
+
secretName: string;
|
|
95
|
+
/** Port the program listens on. */
|
|
96
|
+
port: number;
|
|
97
|
+
/** The command each sandbox runs. */
|
|
98
|
+
command: string[];
|
|
99
|
+
/** Modal's volume and bucket mounts every sandbox gets (props and bindings). */
|
|
100
|
+
mounts: ModalMounts;
|
|
101
|
+
/** Content hash of the bundle and image inputs. */
|
|
102
|
+
hash: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type Sandbox = Resource<
|
|
106
|
+
"Modal.Sandbox",
|
|
107
|
+
SandboxProps,
|
|
108
|
+
SandboxAttributes,
|
|
109
|
+
{ env?: Record<string, any>; mounts?: MountContract },
|
|
110
|
+
Providers
|
|
111
|
+
>;
|
|
112
|
+
|
|
113
|
+
export type SandboxServices = ServerHost;
|
|
114
|
+
export type SandboxShape = Main<SandboxServices>;
|
|
115
|
+
export type SandboxRuntimeContext = HostRuntimeContext;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* An Effect program that runs in Modal sandboxes. Alchemy bundles `main`,
|
|
119
|
+
* builds it into an image on Modal's builder (no Docker), and stores its
|
|
120
|
+
* env, including the credentials its bindings need, in a Modal Secret.
|
|
121
|
+
* `Modal.CreateSandbox(Agent)` then boots sandboxes that run the program and
|
|
122
|
+
* serve its `fetch` handler; `sandbox.getTcpPort()` reaches it.
|
|
123
|
+
*
|
|
124
|
+
* Each sandbox is an isolated instance: give one to every user, session or
|
|
125
|
+
* task, and terminate it when the work is done. A sandbox lives at most 24
|
|
126
|
+
* hours. Bindings work inside it like on any host, so the program can read a
|
|
127
|
+
* Modal Volume or a Cloudflare R2 bucket.
|
|
128
|
+
*
|
|
129
|
+
* ### Effect program
|
|
130
|
+
* **Example:** An agent that answers over HTTP
|
|
131
|
+
* ```typescript
|
|
132
|
+
* // src/Agent.ts
|
|
133
|
+
* import * as Modal from "@alchemy.run/modal";
|
|
134
|
+
* import * as Effect from "effect/Effect";
|
|
135
|
+
* import * as HttpServerResponse from "effect/http/HttpServerResponse";
|
|
136
|
+
* import { Coder } from "./Coder.ts";
|
|
137
|
+
*
|
|
138
|
+
* export default class Agent extends Modal.Sandbox<Agent>()(
|
|
139
|
+
* "Agent",
|
|
140
|
+
* { main: import.meta.url, app: Coder },
|
|
141
|
+
* Effect.gen(function* () {
|
|
142
|
+
* return {
|
|
143
|
+
* fetch: Effect.succeed(HttpServerResponse.text("ready")),
|
|
144
|
+
* };
|
|
145
|
+
* }),
|
|
146
|
+
* ) {}
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* ### Async program
|
|
150
|
+
* **Example:** A plain HTTP server, no Effect (it runs on Bun)
|
|
151
|
+
* ```typescript
|
|
152
|
+
* // src/Agent.ts
|
|
153
|
+
* import * as Modal from "@alchemy.run/modal";
|
|
154
|
+
* import { Coder } from "./Coder.ts";
|
|
155
|
+
*
|
|
156
|
+
* export const Agent = Modal.Sandbox("Agent", {
|
|
157
|
+
* main: "./src/agent-server.ts",
|
|
158
|
+
* app: Coder,
|
|
159
|
+
* env: { GREETING: "hello" },
|
|
160
|
+
* });
|
|
161
|
+
* export type AgentEnv = Modal.InferEnv<typeof Agent>;
|
|
162
|
+
* ```
|
|
163
|
+
*
|
|
164
|
+
* ```typescript
|
|
165
|
+
* // src/agent-server.ts
|
|
166
|
+
* import { createServer } from "node:http";
|
|
167
|
+
* import type { AgentEnv } from "./Agent.ts";
|
|
168
|
+
*
|
|
169
|
+
* const env = process.env as unknown as AgentEnv;
|
|
170
|
+
*
|
|
171
|
+
* createServer((_request, response) => response.end(env.GREETING)).listen(Number(process.env.PORT));
|
|
172
|
+
* ```
|
|
173
|
+
*
|
|
174
|
+
* ### Starting instances
|
|
175
|
+
* **Example:** One sandbox per request from a Worker
|
|
176
|
+
* ```typescript
|
|
177
|
+
* // src/Api.ts
|
|
178
|
+
* import * as Cloudflare from "@alchemy.run/cloudflare";
|
|
179
|
+
* import * as Modal from "@alchemy.run/modal";
|
|
180
|
+
* import * as Effect from "effect/Effect";
|
|
181
|
+
* import * as HttpClientRequest from "effect/http/HttpClientRequest";
|
|
182
|
+
* import * as HttpServerResponse from "effect/http/HttpServerResponse";
|
|
183
|
+
* import Agent from "./Agent.ts";
|
|
184
|
+
*
|
|
185
|
+
* export default class Api extends Cloudflare.Worker<Api>()(
|
|
186
|
+
* "Api",
|
|
187
|
+
* { main: import.meta.url, compatibility: { flags: ["nodejs_compat"] } },
|
|
188
|
+
* Effect.gen(function* () {
|
|
189
|
+
* const agents = yield* Modal.CreateSandbox(Agent);
|
|
190
|
+
* return {
|
|
191
|
+
* fetch: Effect.gen(function* () {
|
|
192
|
+
* const agent = yield* agents.create({ timeout: "10 minutes" });
|
|
193
|
+
* const port = yield* agent.getTcpPort();
|
|
194
|
+
* const response = yield* port.fetch(HttpClientRequest.get("/"));
|
|
195
|
+
* const text = yield* response.text;
|
|
196
|
+
* yield* agent.terminate();
|
|
197
|
+
* return HttpServerResponse.text(text);
|
|
198
|
+
* }).pipe(Effect.orDie),
|
|
199
|
+
* };
|
|
200
|
+
* }).pipe(Effect.provide(Modal.CreateSandboxHttp)),
|
|
201
|
+
* ) {}
|
|
202
|
+
* ```
|
|
203
|
+
*
|
|
204
|
+
* ### Bindings inside the sandbox
|
|
205
|
+
* **Example:** Read a Modal Volume from the program
|
|
206
|
+
* ```typescript
|
|
207
|
+
* // src/Agent.ts
|
|
208
|
+
* import * as Modal from "@alchemy.run/modal";
|
|
209
|
+
* import * as Effect from "effect/Effect";
|
|
210
|
+
* import * as HttpServerResponse from "effect/http/HttpServerResponse";
|
|
211
|
+
* import { Coder, Models } from "./Coder.ts";
|
|
212
|
+
*
|
|
213
|
+
* export default class Agent extends Modal.Sandbox<Agent>()(
|
|
214
|
+
* "Agent",
|
|
215
|
+
* { main: import.meta.url, app: Coder },
|
|
216
|
+
* Effect.gen(function* () {
|
|
217
|
+
* const models = yield* Modal.ReadVolume(Models);
|
|
218
|
+
* return {
|
|
219
|
+
* fetch: Effect.gen(function* () {
|
|
220
|
+
* const files = yield* models.listFiles("/");
|
|
221
|
+
* return yield* HttpServerResponse.json(files);
|
|
222
|
+
* }).pipe(Effect.orDie),
|
|
223
|
+
* };
|
|
224
|
+
* }).pipe(Effect.provide(Modal.ReadVolumeHttp)),
|
|
225
|
+
* ) {}
|
|
226
|
+
* ```
|
|
227
|
+
*
|
|
228
|
+
* ### Configuration
|
|
229
|
+
* **Example:** A GPU-ready base and a custom port
|
|
230
|
+
* ```typescript
|
|
231
|
+
* export default class Trainer extends Modal.Sandbox<Trainer>()(
|
|
232
|
+
* "Trainer",
|
|
233
|
+
* {
|
|
234
|
+
* main: import.meta.url,
|
|
235
|
+
* app: Coder,
|
|
236
|
+
* image: "nvidia/cuda:12.4.1-runtime-ubuntu22.04",
|
|
237
|
+
* commands: ["RUN apt-get update && apt-get install -y curl unzip && curl -fsSL https://bun.sh/install | bash && ln -s /root/.bun/bin/bun /usr/local/bin/bun"],
|
|
238
|
+
* port: 3000,
|
|
239
|
+
* },
|
|
240
|
+
* program,
|
|
241
|
+
* ) {}
|
|
242
|
+
* ```
|
|
243
|
+
*
|
|
244
|
+
* @resource
|
|
245
|
+
* @product Sandbox
|
|
246
|
+
*/
|
|
247
|
+
export const Sandbox: Platform<Sandbox, SandboxServices, SandboxShape, SandboxRuntimeContext> =
|
|
248
|
+
Platform("Modal.Sandbox", {
|
|
249
|
+
createRuntimeContext: createHostRuntimeContext("Modal.Sandbox"),
|
|
250
|
+
// `{ app: Coder }` at module scope is an Effect: yield it so the App is
|
|
251
|
+
// registered and `news.app` is resolved attributes at reconcile.
|
|
252
|
+
transformProps: (_id, props) =>
|
|
253
|
+
Effect.gen(function* () {
|
|
254
|
+
if (globalThis.__ALCHEMY_RUNTIME__) return props;
|
|
255
|
+
const app = Effect.isEffect(props.app)
|
|
256
|
+
? yield* props.app as Effect.Effect<App, never, Providers>
|
|
257
|
+
: props.app;
|
|
258
|
+
const mounts =
|
|
259
|
+
props.mounts === undefined
|
|
260
|
+
? undefined
|
|
261
|
+
: ((yield* resolveDeclarations(props.mounts)) as Mount[]);
|
|
262
|
+
return { ...props, app, mounts };
|
|
263
|
+
}),
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const bootstrapEntry =
|
|
267
|
+
(handler: string) =>
|
|
268
|
+
(importPath: string): string =>
|
|
269
|
+
`
|
|
270
|
+
import { bootstrap } from "@alchemy.run/modal/Bootstrap";
|
|
271
|
+
import { ${handler} as entrypoint } from ${JSON.stringify(importPath)};
|
|
272
|
+
|
|
273
|
+
await bootstrap(entrypoint);
|
|
274
|
+
`;
|
|
275
|
+
|
|
276
|
+
const appOf = (value: unknown) => {
|
|
277
|
+
const rec = (value ?? {}) as { appId?: string; name?: string; environment?: unknown };
|
|
278
|
+
return { appId: rec.appId ?? "", appName: rec.name, environment: envNameOf(rec.environment) };
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
/** An env value as a string, as the program's `process.env` sees it. */
|
|
282
|
+
const envText = (value: unknown): string =>
|
|
283
|
+
typeof value === "string"
|
|
284
|
+
? value
|
|
285
|
+
: Redacted.isRedacted(value)
|
|
286
|
+
? String(Redacted.value(value))
|
|
287
|
+
: JSON.stringify(value ?? null);
|
|
288
|
+
|
|
289
|
+
export const SandboxProvider = () =>
|
|
290
|
+
Provider.effect(
|
|
291
|
+
Sandbox,
|
|
292
|
+
Effect.gen(function* () {
|
|
293
|
+
const stack = yield* Stack;
|
|
294
|
+
|
|
295
|
+
/** Plan the program's image (bundle + bootstrap) with the shared BuildPlan. */
|
|
296
|
+
const prepare = Effect.fn(function* (
|
|
297
|
+
news: SandboxProps,
|
|
298
|
+
port: number,
|
|
299
|
+
bindings: MountBindings,
|
|
300
|
+
) {
|
|
301
|
+
// Other providers' mount clients go into the image; Modal can't
|
|
302
|
+
// provide FUSE or NFS, so those that need it fail here.
|
|
303
|
+
const capabilities = boundCapabilities(bindings);
|
|
304
|
+
yield* checkModalMounts("Modal.Sandbox", capabilities);
|
|
305
|
+
const plan = yield* BuildPlan.plan(
|
|
306
|
+
{
|
|
307
|
+
main: news.main,
|
|
308
|
+
image: news.image,
|
|
309
|
+
handler: news.handler,
|
|
310
|
+
build: news.build,
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
capabilities: { ...MODAL_CAPABILITIES, target: "Modal.Sandbox" },
|
|
314
|
+
port,
|
|
315
|
+
bootstrap: news.isExternal ? undefined : bootstrapEntry(news.handler ?? "default"),
|
|
316
|
+
install: mountInstallLines(capabilities),
|
|
317
|
+
},
|
|
318
|
+
);
|
|
319
|
+
const extra = yield* checkCommands(news.commands ?? []);
|
|
320
|
+
const spec = yield* lowerPlan(plan, extra);
|
|
321
|
+
const hash = yield* sha256Object({ plan: plan.hash, extra });
|
|
322
|
+
const command = BuildPlan.startCommand(plan) ?? [];
|
|
323
|
+
return { spec, hash, command: [...command] };
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
stables: ["appId", "secretName"],
|
|
328
|
+
diff: Effect.fn(function* ({ news, output, newBindings }) {
|
|
329
|
+
if (!isResolved(news) || !isResolved(newBindings) || output === undefined) {
|
|
330
|
+
return undefined;
|
|
331
|
+
}
|
|
332
|
+
if (appOf(news.app).appId !== output.appId) return { action: "replace" } as const;
|
|
333
|
+
const { hash } = yield* prepare(news, news.port ?? DEFAULT_PORT, newBindings);
|
|
334
|
+
if (hash !== output.hash) return { action: "update" } as const;
|
|
335
|
+
const mounts = yield* modalMountsOf([
|
|
336
|
+
...(news.mounts ?? []),
|
|
337
|
+
...boundMounts(newBindings),
|
|
338
|
+
]);
|
|
339
|
+
return deepEqual(mounts, output.mounts) ? undefined : ({ action: "update" } as const);
|
|
340
|
+
}),
|
|
341
|
+
read: Effect.fn(function* ({ output }) {
|
|
342
|
+
if (output === undefined) return undefined;
|
|
343
|
+
const found = yield* image
|
|
344
|
+
.imageFromId({ imageId: output.imageId })
|
|
345
|
+
.pipe(Effect.catchTag("ImageNotFound", () => Effect.undefined));
|
|
346
|
+
return found?.imageId ? output : undefined;
|
|
347
|
+
}),
|
|
348
|
+
reconcile: Effect.fn(function* ({ id, news, output, session, bindings }) {
|
|
349
|
+
const app = appOf(news.app);
|
|
350
|
+
// The props' mounts, then the ones `Modal.MountVolume` / `MountBucket` bound.
|
|
351
|
+
const mounts = yield* modalMountsOf([...(news.mounts ?? []), ...boundMounts(bindings)]);
|
|
352
|
+
const port = news.port ?? DEFAULT_PORT;
|
|
353
|
+
const secretName = output?.secretName ?? `${yield* objectName(id, undefined, 58)}-env`;
|
|
354
|
+
|
|
355
|
+
// Sync the program's env (bindings' credentials included) into a
|
|
356
|
+
// secret every sandbox attaches; values never enter the image.
|
|
357
|
+
const env: Record<string, string> = {
|
|
358
|
+
ALCHEMY_STACK_NAME: stack.name,
|
|
359
|
+
ALCHEMY_STAGE: stack.stage,
|
|
360
|
+
ALCHEMY_PHASE: "runtime",
|
|
361
|
+
HOST: "0.0.0.0",
|
|
362
|
+
PORT: String(port),
|
|
363
|
+
};
|
|
364
|
+
for (const [key, value] of Object.entries(news.env ?? {})) env[key] = envText(value);
|
|
365
|
+
const stored = yield* putSecret(
|
|
366
|
+
yield* secret.secretGetOrCreate,
|
|
367
|
+
secretName,
|
|
368
|
+
app.environment,
|
|
369
|
+
env,
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
// Ensure the image: Modal reuses a cached build for the same inputs.
|
|
373
|
+
const { spec, hash, command } = yield* prepare(news, port, bindings);
|
|
374
|
+
const imageId =
|
|
375
|
+
output !== undefined && output.hash === hash && output.appId === app.appId
|
|
376
|
+
? output.imageId
|
|
377
|
+
: yield* buildImage(yield* ImageBuildOps, app.appId, spec, { note: session.note });
|
|
378
|
+
|
|
379
|
+
return {
|
|
380
|
+
imageId,
|
|
381
|
+
appId: app.appId,
|
|
382
|
+
appName: app.appName,
|
|
383
|
+
environment: app.environment,
|
|
384
|
+
secretId: stored.secretId,
|
|
385
|
+
secretName,
|
|
386
|
+
port,
|
|
387
|
+
command,
|
|
388
|
+
mounts,
|
|
389
|
+
hash,
|
|
390
|
+
};
|
|
391
|
+
}),
|
|
392
|
+
delete: Effect.fn(function* ({ output }) {
|
|
393
|
+
yield* secret
|
|
394
|
+
.deleteSecret({ secretId: output.secretId })
|
|
395
|
+
.pipe(Effect.catchTag("SecretNotFound", () => Effect.void));
|
|
396
|
+
yield* image
|
|
397
|
+
.deleteImage({ imageId: output.imageId })
|
|
398
|
+
.pipe(Effect.catchTag("ImageNotFound", () => Effect.void));
|
|
399
|
+
}),
|
|
400
|
+
};
|
|
401
|
+
}),
|
|
402
|
+
);
|