@alchemy.run/modal 0.0.0 → 2.0.0-beta.87
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,386 @@
|
|
|
1
|
+
import * as classes from "@distilled.cloud/modal/classes";
|
|
2
|
+
import * as functions from "@distilled.cloud/modal/functions";
|
|
3
|
+
import * as secret from "@distilled.cloud/modal/secret";
|
|
4
|
+
import { isResolved } from "alchemy/Diff";
|
|
5
|
+
import type { Input } from "alchemy/Input";
|
|
6
|
+
import { Stack } from "alchemy/Stack";
|
|
7
|
+
import { sha256Object } from "alchemy/Util/sha256";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import * as Redacted from "effect/Redacted";
|
|
10
|
+
import type { ContainerProps, Ref } from "../Container.ts";
|
|
11
|
+
import { gpuConfig } from "../ImageBuild.ts";
|
|
12
|
+
import { envNameOf, objectName, seconds } from "../internal.ts";
|
|
13
|
+
import {
|
|
14
|
+
boundCapabilities,
|
|
15
|
+
boundMounts,
|
|
16
|
+
type ModalMounts,
|
|
17
|
+
type Mount,
|
|
18
|
+
type MountBindings,
|
|
19
|
+
modalMountsOf,
|
|
20
|
+
} from "../Mounts.ts";
|
|
21
|
+
import type { Providers } from "../Providers.ts";
|
|
22
|
+
import { putSecret } from "../Secret.ts";
|
|
23
|
+
import { resolveDeclarations } from "./Declared.ts";
|
|
24
|
+
import {
|
|
25
|
+
buildRuntimeImage,
|
|
26
|
+
classTag,
|
|
27
|
+
clientMount,
|
|
28
|
+
DEFAULT_PORT,
|
|
29
|
+
entrypointMount,
|
|
30
|
+
ModalDeployUnexpectedResponse,
|
|
31
|
+
planRuntimeImage,
|
|
32
|
+
publishFunction,
|
|
33
|
+
} from "./Deploy.ts";
|
|
34
|
+
import { ENTRYPOINT_MODULE } from "./Entrypoint.ts";
|
|
35
|
+
import { RPC_CALLERS_ENV, type RpcCallerBinding, rpcCallersOf } from "./RpcAuth.ts";
|
|
36
|
+
|
|
37
|
+
/** What a runtime's bindings attach: mounts, and the keys of RPC callers. */
|
|
38
|
+
type RuntimeBindings = ReadonlyArray<{
|
|
39
|
+
readonly data: { readonly mounts?: MountBindings[number]["data"]["mounts"] } & RpcCallerBinding;
|
|
40
|
+
}>;
|
|
41
|
+
|
|
42
|
+
/** Attributes shared by `Modal.Function` and `Modal.Server`. */
|
|
43
|
+
export interface RuntimeAttributes {
|
|
44
|
+
/** Function ID (`fu-…`). */
|
|
45
|
+
functionId: string;
|
|
46
|
+
/** Name of the function in its App. */
|
|
47
|
+
name: string;
|
|
48
|
+
/** The App the function is deployed in. */
|
|
49
|
+
appId: string;
|
|
50
|
+
/** Name of that App. */
|
|
51
|
+
appName: string | undefined;
|
|
52
|
+
/** Environment of that App. */
|
|
53
|
+
environment: string | undefined;
|
|
54
|
+
/** The image with the bundled program (`im-…`). */
|
|
55
|
+
imageId: string;
|
|
56
|
+
/** The Modal Secret holding the program's env (bindings' credentials included). */
|
|
57
|
+
secretId: string;
|
|
58
|
+
/** Name of that secret. */
|
|
59
|
+
secretName: string;
|
|
60
|
+
/** Port the program listens on. */
|
|
61
|
+
port: number;
|
|
62
|
+
/** The command the entrypoint starts. */
|
|
63
|
+
command: string[];
|
|
64
|
+
/** Content hash of the bundle, image and function definition. */
|
|
65
|
+
hash: string;
|
|
66
|
+
/** The class (`cs-…`) of a function with RPC methods, which Modal runs as a class. */
|
|
67
|
+
classId?: string | undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** What differs between a Function and a Server. */
|
|
71
|
+
export interface RuntimeKind<P extends ContainerProps> {
|
|
72
|
+
/** Resource type, e.g. `Modal.Function`. */
|
|
73
|
+
readonly type: string;
|
|
74
|
+
/** The Function fields specific to the kind (webhook, schedule, HTTP config, ...). */
|
|
75
|
+
readonly definition: (
|
|
76
|
+
props: P,
|
|
77
|
+
context: { readonly port: number; readonly startupTimeout: number },
|
|
78
|
+
) => functions.Function;
|
|
79
|
+
/** The program's bootstrap for an Effect-native `main`. */
|
|
80
|
+
readonly bootstrap: (handler: string) => (importPath: string) => string;
|
|
81
|
+
/** Env the kind adds to the program's (e.g. turning RPC on). */
|
|
82
|
+
readonly env?: (props: P) => Record<string, string>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const resolveRef = <T>(value: Ref<T>) =>
|
|
86
|
+
Effect.isEffect(value) ? (value as Effect.Effect<T, never, Providers>) : Effect.succeed(value);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* `{ app: Coder, mounts: [{ volume: Data, path: "/data" }] }` at module scope
|
|
90
|
+
* holds Effects: yield them so the resources are registered and the props
|
|
91
|
+
* hold their resolved attributes at reconcile.
|
|
92
|
+
*/
|
|
93
|
+
export const resolveContainerProps = <P extends ContainerProps>(_id: string, props: P) =>
|
|
94
|
+
Effect.gen(function* () {
|
|
95
|
+
if (globalThis.__ALCHEMY_RUNTIME__) return props;
|
|
96
|
+
const app = yield* resolveRef(props.app);
|
|
97
|
+
const proxy = props.proxy === undefined ? undefined : yield* resolveRef(props.proxy);
|
|
98
|
+
const secrets =
|
|
99
|
+
props.secrets === undefined ? undefined : yield* Effect.forEach(props.secrets, resolveRef);
|
|
100
|
+
const mounts =
|
|
101
|
+
props.mounts === undefined
|
|
102
|
+
? undefined
|
|
103
|
+
: ((yield* resolveDeclarations(props.mounts)) as Mount[]);
|
|
104
|
+
return { ...props, app, proxy, secrets, mounts };
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
/** The resolved app of the props. */
|
|
108
|
+
export const appOf = (value: unknown) => {
|
|
109
|
+
const rec = (value ?? {}) as { appId?: string; name?: string; environment?: unknown };
|
|
110
|
+
return { appId: rec.appId ?? "", appName: rec.name, environment: envNameOf(rec.environment) };
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const idOf = (value: unknown, key: string): string | undefined =>
|
|
114
|
+
typeof value === "object" && value !== null && key in value
|
|
115
|
+
? ((value as Record<string, unknown>)[key] as string | undefined)
|
|
116
|
+
: undefined;
|
|
117
|
+
|
|
118
|
+
/** An env value as a string, as the program's `process.env` sees it. */
|
|
119
|
+
const envText = (value: unknown): string =>
|
|
120
|
+
typeof value === "string"
|
|
121
|
+
? value
|
|
122
|
+
: Redacted.isRedacted(value)
|
|
123
|
+
? String(Redacted.value(value))
|
|
124
|
+
: JSON.stringify(value ?? null);
|
|
125
|
+
|
|
126
|
+
/** The function definition fields every kind shares. */
|
|
127
|
+
const baseDefinition = (
|
|
128
|
+
props: ContainerProps,
|
|
129
|
+
context: {
|
|
130
|
+
readonly tag: string;
|
|
131
|
+
readonly appName: string | undefined;
|
|
132
|
+
readonly timeout: number;
|
|
133
|
+
readonly startupTimeout: number;
|
|
134
|
+
},
|
|
135
|
+
mounts: ModalMounts,
|
|
136
|
+
): functions.Function => ({
|
|
137
|
+
moduleName: ENTRYPOINT_MODULE,
|
|
138
|
+
functionName: context.tag,
|
|
139
|
+
definitionType: "DEFINITION_TYPE_FILE",
|
|
140
|
+
functionType: "FUNCTION_TYPE_FUNCTION",
|
|
141
|
+
resources: {
|
|
142
|
+
milliCpu: props.cpu !== undefined ? Math.round(props.cpu * 1000) : undefined,
|
|
143
|
+
milliCpuMax: props.cpuLimit !== undefined ? Math.round(props.cpuLimit * 1000) : undefined,
|
|
144
|
+
memoryMb: props.memory,
|
|
145
|
+
memoryMbMax: props.memoryLimit,
|
|
146
|
+
gpuConfig: gpuConfig(props.gpu),
|
|
147
|
+
ephemeralDiskMb: props.ephemeralDisk,
|
|
148
|
+
},
|
|
149
|
+
volumeMounts: mounts.volumeMounts,
|
|
150
|
+
cloudBucketMounts: mounts.cloudBucketMounts,
|
|
151
|
+
proxyId: idOf(props.proxy, "proxyId"),
|
|
152
|
+
timeoutSecs: Math.round(context.timeout),
|
|
153
|
+
startupTimeoutSecs: Math.round(context.startupTimeout),
|
|
154
|
+
cloudProviderStr: props.cloud ?? "",
|
|
155
|
+
appName: context.appName ?? "",
|
|
156
|
+
schedulerPlacement:
|
|
157
|
+
props.regions !== undefined || props.nonpreemptible
|
|
158
|
+
? { regions: props.regions, nonpreemptible: props.nonpreemptible }
|
|
159
|
+
: undefined,
|
|
160
|
+
autoscalerSettings: {
|
|
161
|
+
minContainers: props.minContainers,
|
|
162
|
+
maxContainers: props.maxContainers,
|
|
163
|
+
bufferContainers: props.bufferContainers,
|
|
164
|
+
scaleupWindow: props.scaleupWindow,
|
|
165
|
+
scaledownWindow: props.scaledownWindow,
|
|
166
|
+
},
|
|
167
|
+
// Older mirrors of `autoscalerSettings` the Python client still sets.
|
|
168
|
+
warmPoolSize: props.minContainers ?? 0,
|
|
169
|
+
concurrencyLimit: props.maxContainers ?? 0,
|
|
170
|
+
ExperimentalBufferContainers: props.bufferContainers ?? 0,
|
|
171
|
+
taskIdleTimeoutSecs: props.scaledownWindow ?? 0,
|
|
172
|
+
checkpointingEnabled: props.enableMemorySnapshot,
|
|
173
|
+
blockNetwork: props.blockNetwork,
|
|
174
|
+
untrusted: props.restrictModalAccess,
|
|
175
|
+
i6pnEnabled: props.i6pn,
|
|
176
|
+
methodDefinitionsSet: true,
|
|
177
|
+
mountClientDependencies: true,
|
|
178
|
+
ExperimentalConcurrentCancellations: true,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The lifecycle shared by every Modal runtime: a function in an App whose
|
|
183
|
+
* container runs the bundled program behind Alchemy's Python entrypoint.
|
|
184
|
+
*
|
|
185
|
+
* 1. the program's env goes into a Modal Secret (values never enter the image);
|
|
186
|
+
* 2. the image is the base plus standalone Python plus the bundle, built by
|
|
187
|
+
* Modal and reused while its inputs don't change;
|
|
188
|
+
* 3. `FunctionCreate` writes the definition (with the entrypoint and client
|
|
189
|
+
* mounts), updating the existing function in place;
|
|
190
|
+
* 4. the App is republished with the function, keeping its other functions.
|
|
191
|
+
*/
|
|
192
|
+
export const runtimeLifecycle = <P extends ContainerProps>(kind: RuntimeKind<P>) =>
|
|
193
|
+
Effect.gen(function* () {
|
|
194
|
+
const stack = yield* Stack;
|
|
195
|
+
|
|
196
|
+
const prepare = Effect.fn(function* (id: string, news: P, bindings: RuntimeBindings) {
|
|
197
|
+
const port = news.port ?? DEFAULT_PORT;
|
|
198
|
+
const tag = news.name ?? id;
|
|
199
|
+
const app = appOf(news.app);
|
|
200
|
+
const timeout = seconds(news.timeout) ?? 300;
|
|
201
|
+
const startupTimeout = seconds(news.startupTimeout) ?? timeout;
|
|
202
|
+
const image = yield* planRuntimeImage(
|
|
203
|
+
kind.type,
|
|
204
|
+
news,
|
|
205
|
+
port,
|
|
206
|
+
news.isExternal ? undefined : kind.bootstrap(news.handler ?? "default"),
|
|
207
|
+
boundCapabilities(bindings),
|
|
208
|
+
);
|
|
209
|
+
// The props' mounts, then the ones `Modal.MountVolume` / `MountBucket` bound.
|
|
210
|
+
const mounts = yield* modalMountsOf([...(news.mounts ?? []), ...boundMounts(bindings)]);
|
|
211
|
+
const base = baseDefinition(
|
|
212
|
+
news,
|
|
213
|
+
{ tag, appName: app.appName, timeout, startupTimeout },
|
|
214
|
+
mounts,
|
|
215
|
+
);
|
|
216
|
+
const specific = kind.definition(news, { port, startupTimeout });
|
|
217
|
+
const definition: functions.Function = {
|
|
218
|
+
...base,
|
|
219
|
+
...specific,
|
|
220
|
+
// A class's service function is named `<tag>.*`, as Modal's clients name it.
|
|
221
|
+
functionName: specific.isClass ? classTag(tag) : tag,
|
|
222
|
+
autoscalerSettings: { ...base.autoscalerSettings, ...specific.autoscalerSettings },
|
|
223
|
+
};
|
|
224
|
+
const secretIds = (news.secrets ?? []).flatMap((s) => idOf(s, "secretId") ?? []);
|
|
225
|
+
const env = {
|
|
226
|
+
...Object.fromEntries(
|
|
227
|
+
Object.entries(news.env ?? {}).map(([key, value]) => [key, envText(value)]),
|
|
228
|
+
),
|
|
229
|
+
...kind.env?.(news),
|
|
230
|
+
// The callers whose keys open the program's RPC methods.
|
|
231
|
+
...(rpcCallersOf(bindings).length > 0
|
|
232
|
+
? { [RPC_CALLERS_ENV]: JSON.stringify(rpcCallersOf(bindings)) }
|
|
233
|
+
: {}),
|
|
234
|
+
};
|
|
235
|
+
const hash = yield* sha256Object({ image: image.hash, definition, secretIds, env });
|
|
236
|
+
return { port, tag, app, image, definition, secretIds, env, hash };
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
stables: ["appId", "name", "secretName"] as Array<keyof RuntimeAttributes>,
|
|
241
|
+
diff: Effect.fn(function* ({
|
|
242
|
+
id,
|
|
243
|
+
news,
|
|
244
|
+
output,
|
|
245
|
+
newBindings,
|
|
246
|
+
}: {
|
|
247
|
+
id: string;
|
|
248
|
+
news: Input<P>;
|
|
249
|
+
output: RuntimeAttributes | undefined;
|
|
250
|
+
newBindings: Input<RuntimeBindings>;
|
|
251
|
+
}) {
|
|
252
|
+
if (!isResolved(news) || !isResolved(newBindings) || output === undefined) {
|
|
253
|
+
return undefined;
|
|
254
|
+
}
|
|
255
|
+
if (appOf(news.app).appId !== output.appId) return { action: "replace" } as const;
|
|
256
|
+
if ((news.name ?? id) !== output.name) return { action: "replace" } as const;
|
|
257
|
+
const { hash } = yield* prepare(id, news, newBindings);
|
|
258
|
+
return hash !== output.hash ? ({ action: "update" } as const) : undefined;
|
|
259
|
+
}),
|
|
260
|
+
read: Effect.fn(function* ({ output }: { output: RuntimeAttributes | undefined }) {
|
|
261
|
+
if (output === undefined) return undefined;
|
|
262
|
+
const found = yield* functions
|
|
263
|
+
.functionGetById({ functionId: output.functionId })
|
|
264
|
+
.pipe(Effect.catchTag("FunctionNotFound", () => Effect.undefined));
|
|
265
|
+
return found?.function ? output : undefined;
|
|
266
|
+
}),
|
|
267
|
+
reconcile: Effect.fn(function* ({
|
|
268
|
+
id,
|
|
269
|
+
news,
|
|
270
|
+
output,
|
|
271
|
+
session,
|
|
272
|
+
bindings,
|
|
273
|
+
}: {
|
|
274
|
+
id: string;
|
|
275
|
+
news: P;
|
|
276
|
+
output: RuntimeAttributes | undefined;
|
|
277
|
+
session: { note: (message: string) => Effect.Effect<void> };
|
|
278
|
+
bindings: RuntimeBindings;
|
|
279
|
+
}) {
|
|
280
|
+
const {
|
|
281
|
+
port,
|
|
282
|
+
tag,
|
|
283
|
+
app,
|
|
284
|
+
image,
|
|
285
|
+
definition,
|
|
286
|
+
secretIds,
|
|
287
|
+
env: programEnv,
|
|
288
|
+
hash,
|
|
289
|
+
} = yield* prepare(id, news, bindings);
|
|
290
|
+
for (const warning of image.warnings) yield* session.note(warning);
|
|
291
|
+
const secretName = output?.secretName ?? `${yield* objectName(id, undefined, 58)}-env`;
|
|
292
|
+
|
|
293
|
+
// Sync the program's env (bindings' credentials included) into the
|
|
294
|
+
// secret the function attaches; values never enter the image.
|
|
295
|
+
const env: Record<string, string> = {
|
|
296
|
+
ALCHEMY_STACK_NAME: stack.name,
|
|
297
|
+
ALCHEMY_STAGE: stack.stage,
|
|
298
|
+
ALCHEMY_PHASE: "runtime",
|
|
299
|
+
ALCHEMY_COMMAND: JSON.stringify(image.command),
|
|
300
|
+
HOST: "0.0.0.0",
|
|
301
|
+
PORT: String(port),
|
|
302
|
+
};
|
|
303
|
+
Object.assign(env, programEnv);
|
|
304
|
+
const stored = yield* putSecret(
|
|
305
|
+
yield* secret.secretGetOrCreate,
|
|
306
|
+
secretName,
|
|
307
|
+
app.environment,
|
|
308
|
+
env,
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
// Ensure the image: Modal reuses a cached build for the same inputs.
|
|
312
|
+
const imageId =
|
|
313
|
+
output !== undefined && output.hash === hash && output.appId === app.appId
|
|
314
|
+
? output.imageId
|
|
315
|
+
: yield* buildRuntimeImage(app.appId, image, {
|
|
316
|
+
builderVersion: news.builderVersion,
|
|
317
|
+
gpu: news.gpu,
|
|
318
|
+
note: session.note,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
const mountIds = [yield* clientMount, yield* entrypointMount(app.appId)];
|
|
322
|
+
yield* session.note(`Deploying ${kind.type} ${tag}...`);
|
|
323
|
+
const created = yield* functions.createFunction({
|
|
324
|
+
appId: app.appId,
|
|
325
|
+
function: {
|
|
326
|
+
...definition,
|
|
327
|
+
imageId,
|
|
328
|
+
mountIds,
|
|
329
|
+
secretIds: [stored.secretId, ...secretIds],
|
|
330
|
+
},
|
|
331
|
+
existingFunctionId: output?.appId === app.appId ? output.functionId : undefined,
|
|
332
|
+
});
|
|
333
|
+
if (!created.functionId) {
|
|
334
|
+
return yield* new ModalDeployUnexpectedResponse({
|
|
335
|
+
message: `Modal returned no id for function ${tag}`,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
for (const warning of created.serverWarnings ?? []) {
|
|
339
|
+
if (warning.message) yield* session.note(warning.message);
|
|
340
|
+
}
|
|
341
|
+
// A class function needs its class: its containers hydrate it from
|
|
342
|
+
// the App layout, under the function's name.
|
|
343
|
+
const classId = definition.isClass
|
|
344
|
+
? (yield* classes.createClass({
|
|
345
|
+
appId: app.appId,
|
|
346
|
+
existingClassId: output?.appId === app.appId ? output.classId : undefined,
|
|
347
|
+
onlyClassFunction: true,
|
|
348
|
+
})).classId
|
|
349
|
+
: undefined;
|
|
350
|
+
yield* publishFunction(app, tag, {
|
|
351
|
+
functionId: created.functionId,
|
|
352
|
+
definitionId: created.handleMetadata?.definitionId,
|
|
353
|
+
classId,
|
|
354
|
+
});
|
|
355
|
+
return {
|
|
356
|
+
attributes: {
|
|
357
|
+
functionId: created.functionId,
|
|
358
|
+
name: tag,
|
|
359
|
+
appId: app.appId,
|
|
360
|
+
appName: app.appName,
|
|
361
|
+
environment: app.environment,
|
|
362
|
+
imageId,
|
|
363
|
+
secretId: stored.secretId,
|
|
364
|
+
secretName,
|
|
365
|
+
port,
|
|
366
|
+
command: image.command,
|
|
367
|
+
hash,
|
|
368
|
+
classId,
|
|
369
|
+
} satisfies RuntimeAttributes,
|
|
370
|
+
metadata: created.handleMetadata,
|
|
371
|
+
};
|
|
372
|
+
}),
|
|
373
|
+
delete: Effect.fn(function* ({ output }: { output: RuntimeAttributes }) {
|
|
374
|
+
// Republish the App without the function; stopping the App (its own
|
|
375
|
+
// resource) is what removes everything else.
|
|
376
|
+
yield* publishFunction(output, output.name, undefined).pipe(
|
|
377
|
+
Effect.catchTag("AppNotFound", () => Effect.void),
|
|
378
|
+
);
|
|
379
|
+
yield* secret
|
|
380
|
+
.deleteSecret({ secretId: output.secretId })
|
|
381
|
+
.pipe(Effect.catchTag("SecretNotFound", () => Effect.void));
|
|
382
|
+
// The image stays: Modal content-addresses builds, so another
|
|
383
|
+
// function or sandbox may boot from the same image id.
|
|
384
|
+
}),
|
|
385
|
+
};
|
|
386
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createInternalTags, hasTags, type Tags } from "alchemy/Tags";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Modal tag keys and values must match `^[a-zA-Z0-9._-]{1,63}$`, so
|
|
6
|
+
* Alchemy's `alchemy::stack` / `alchemy::stage` / `alchemy::id` ownership
|
|
7
|
+
* tags are stored as `alchemy.stack` / `alchemy.stage` / `alchemy.id`, and
|
|
8
|
+
* their values are rewritten into that character set (`/` in a nested
|
|
9
|
+
* logical ID becomes `__`).
|
|
10
|
+
*/
|
|
11
|
+
export const ALCHEMY_TAG_PREFIX = "alchemy.";
|
|
12
|
+
|
|
13
|
+
const INTERNAL_PREFIX = "alchemy::";
|
|
14
|
+
|
|
15
|
+
const toModalKey = (key: string): string =>
|
|
16
|
+
key.startsWith(INTERNAL_PREFIX)
|
|
17
|
+
? `${ALCHEMY_TAG_PREFIX}${key.slice(INTERNAL_PREFIX.length)}`
|
|
18
|
+
: key;
|
|
19
|
+
|
|
20
|
+
const toModalValue = (value: string): string => {
|
|
21
|
+
const cleaned = value
|
|
22
|
+
.replaceAll("/", "__")
|
|
23
|
+
.replace(/[^A-Za-z0-9._-]/g, "-")
|
|
24
|
+
.slice(0, 63);
|
|
25
|
+
return cleaned.length > 0 ? cleaned : "-";
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** Alchemy's ownership tags in Modal's tag format. */
|
|
29
|
+
export const createModalInternalTags = Effect.fn(function* (id: string) {
|
|
30
|
+
const tags = yield* createInternalTags(id);
|
|
31
|
+
return Object.fromEntries(
|
|
32
|
+
Object.entries(tags).map(([key, value]) => [toModalKey(key), toModalValue(value)]),
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/** Whether observed Modal tags carry this resource's ownership tags. */
|
|
37
|
+
export const hasModalAlchemyTags = Effect.fn(function* (id: string, tags: Tags | undefined) {
|
|
38
|
+
return hasTags(yield* createModalInternalTags(id), tags);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
/** Whether observed Modal tags carry any Alchemy ownership tag. */
|
|
42
|
+
export const isAlchemyTagged = (
|
|
43
|
+
tags: Readonly<Record<string, string | undefined>> | undefined,
|
|
44
|
+
): boolean => Object.keys(tags ?? {}).some((key) => key.startsWith(ALCHEMY_TAG_PREFIX));
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Socket from "effect/socket/Socket";
|
|
3
|
+
import { isWorkerd } from "./Transport.ts";
|
|
4
|
+
|
|
5
|
+
/** The part of `cloudflare:sockets` this module uses. */
|
|
6
|
+
type CloudflareConnect = (
|
|
7
|
+
address: { hostname: string; port: number },
|
|
8
|
+
options: { secureTransport: "on" | "off"; allowHalfOpen: boolean },
|
|
9
|
+
) => {
|
|
10
|
+
readonly readable: ReadableStream<Uint8Array>;
|
|
11
|
+
readonly writable: WritableStream<Uint8Array>;
|
|
12
|
+
close(): Promise<void>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/** The parts of `node:net`, `node:tls` and `node:stream` this module uses. */
|
|
16
|
+
interface NodeDuplex {
|
|
17
|
+
destroy(): void;
|
|
18
|
+
}
|
|
19
|
+
type NodeConnect = (options: { host: string; port: number; servername?: string }) => NodeDuplex;
|
|
20
|
+
interface NodeStreamModule {
|
|
21
|
+
readonly Duplex: {
|
|
22
|
+
toWeb(duplex: NodeDuplex): Socket.InputTransformStream;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Non-literal specifiers keep a bundler from pulling one runtime's sockets
|
|
27
|
+
// into the other's bundle: workerd resolves `cloudflare:sockets` at runtime,
|
|
28
|
+
// and Node resolves its built-ins.
|
|
29
|
+
const CLOUDFLARE_SOCKETS = "cloudflare:sockets";
|
|
30
|
+
const NODE_NET = "node:net";
|
|
31
|
+
const NODE_TLS = "node:tls";
|
|
32
|
+
const NODE_STREAM = "node:stream";
|
|
33
|
+
|
|
34
|
+
const importModule = <A>(specifier: string) =>
|
|
35
|
+
Effect.tryPromise({
|
|
36
|
+
try: () => import(/* @vite-ignore */ specifier) as Promise<A>,
|
|
37
|
+
catch: (cause) =>
|
|
38
|
+
Socket.SocketError.make({ reason: Socket.SocketOpenError.make({ kind: "Unknown", cause }) }),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const openCloudflare = (host: string, port: number, tls: boolean) =>
|
|
42
|
+
Effect.acquireRelease(
|
|
43
|
+
Effect.map(importModule<{ connect: CloudflareConnect }>(CLOUDFLARE_SOCKETS), ({ connect }) =>
|
|
44
|
+
connect(
|
|
45
|
+
{ hostname: host, port },
|
|
46
|
+
{ secureTransport: tls ? "on" : "off", allowHalfOpen: false },
|
|
47
|
+
),
|
|
48
|
+
),
|
|
49
|
+
(socket) => Effect.promise(() => socket.close().catch(() => undefined)),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const openNode = (host: string, port: number, tls: boolean) =>
|
|
53
|
+
Effect.acquireRelease(
|
|
54
|
+
Effect.gen(function* () {
|
|
55
|
+
const { Duplex } = yield* importModule<NodeStreamModule>(NODE_STREAM);
|
|
56
|
+
const { connect } = yield* importModule<{ connect: NodeConnect }>(tls ? NODE_TLS : NODE_NET);
|
|
57
|
+
const duplex = connect(tls ? { host, port, servername: host } : { host, port });
|
|
58
|
+
return { duplex, stream: Duplex.toWeb(duplex) };
|
|
59
|
+
}),
|
|
60
|
+
({ duplex }) => Effect.sync(() => duplex.destroy()),
|
|
61
|
+
).pipe(Effect.map(({ stream }) => stream));
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A TCP socket to `host:port`, over TLS when `tls` is set: `cloudflare:sockets`
|
|
65
|
+
* on workerd, Node's `net`/`tls` elsewhere. It connects when run.
|
|
66
|
+
*/
|
|
67
|
+
export const connectTcp = (options: {
|
|
68
|
+
readonly host: string;
|
|
69
|
+
readonly port: number;
|
|
70
|
+
readonly tls: boolean;
|
|
71
|
+
}): Effect.Effect<Socket.Socket> =>
|
|
72
|
+
Socket.fromTransformStream(
|
|
73
|
+
Effect.suspend(() =>
|
|
74
|
+
isWorkerd()
|
|
75
|
+
? openCloudflare(options.host, options.port, options.tls)
|
|
76
|
+
: openNode(options.host, options.port, options.tls),
|
|
77
|
+
),
|
|
78
|
+
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { GrpcTransport } from "@distilled.cloud/core/grpc-transport";
|
|
2
|
+
import * as CloudflareSockets from "@distilled.cloud/core/grpc-transport-cloudflare-sockets";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
|
|
5
|
+
import type * as HttpClient from "effect/http/HttpClient";
|
|
6
|
+
import * as Layer from "effect/Layer";
|
|
7
|
+
|
|
8
|
+
/** Whether this code runs on workerd (a Cloudflare Worker or Durable Object). */
|
|
9
|
+
export const isWorkerd = (): boolean =>
|
|
10
|
+
typeof navigator !== "undefined" && navigator.userAgent === "Cloudflare-Workers";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* How Modal calls leave this runtime: `fetch`, and on a Cloudflare Worker,
|
|
14
|
+
* distilled's `cloudflare:sockets` gRPC transport. `api.modal.com` is behind
|
|
15
|
+
* Cloudflare, and a Worker's `fetch` to it takes Cloudflare's internal
|
|
16
|
+
* Worker-to-site path, which resets Modal's gRPC upstream (`grpc-status: 14`).
|
|
17
|
+
* Other HTTP calls (blob uploads, the user's own endpoints) keep `fetch`.
|
|
18
|
+
*/
|
|
19
|
+
export const ModalTransport: Layer.Layer<HttpClient.HttpClient> = Layer.mergeAll(
|
|
20
|
+
FetchHttpClient.layer,
|
|
21
|
+
Layer.effect(
|
|
22
|
+
GrpcTransport,
|
|
23
|
+
Effect.sync(() => (isWorkerd() ? CloudflareSockets.client : undefined)),
|
|
24
|
+
),
|
|
25
|
+
);
|
package/src/internal.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { Credentials } from "@distilled.cloud/modal/Credentials";
|
|
2
|
+
import * as environment from "@distilled.cloud/modal/environment";
|
|
3
|
+
import type { ListPagination } from "@distilled.cloud/modal/volume";
|
|
4
|
+
import { createPhysicalName } from "alchemy/PhysicalName";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import type * as HttpClient from "effect/http/HttpClient";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Modal object names: letters, digits, `-`, `.` and `_`, shorter than 64
|
|
10
|
+
* characters. Names are unique per environment and kind.
|
|
11
|
+
*/
|
|
12
|
+
export const objectName = (id: string, name: string | undefined, maxLength = 63) =>
|
|
13
|
+
name !== undefined ? Effect.succeed(name) : createPhysicalName({ id, maxLength, delimiter: "-" });
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The environment a Modal call targets. Modal resolves an empty name to the
|
|
17
|
+
* workspace's default environment, so `undefined` stays `""` on the wire and
|
|
18
|
+
* `undefined` in attributes.
|
|
19
|
+
*/
|
|
20
|
+
export const environmentOf = (name: string | undefined): string => name ?? "";
|
|
21
|
+
|
|
22
|
+
/** Seconds of a duration given as seconds or a `"10 minutes"`-style string. */
|
|
23
|
+
export const seconds = (value: number | string | undefined): number | undefined => {
|
|
24
|
+
if (value === undefined) return undefined;
|
|
25
|
+
if (typeof value === "number") return value;
|
|
26
|
+
const match =
|
|
27
|
+
/^\s*(\d+(?:\.\d+)?)\s*(ms|millis|milliseconds?|s|secs?|seconds?|m|mins?|minutes?|h|hours?)\s*$/i.exec(
|
|
28
|
+
value,
|
|
29
|
+
);
|
|
30
|
+
if (!match) return Number(value);
|
|
31
|
+
const n = Number(match[1]);
|
|
32
|
+
const unit = match[2]!.toLowerCase();
|
|
33
|
+
if (unit.startsWith("ms") || unit.startsWith("milli")) return n / 1000;
|
|
34
|
+
if (unit.startsWith("h")) return n * 3600;
|
|
35
|
+
if (unit.startsWith("m")) return n * 60;
|
|
36
|
+
return n;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Every item of a Modal `List*` call. Modal pages by creation time: each
|
|
41
|
+
* request takes `maxObjects` and `createdBefore`, the creation time of the
|
|
42
|
+
* oldest item seen so far.
|
|
43
|
+
*/
|
|
44
|
+
export const listAll: <T, E, R>(
|
|
45
|
+
fetch: (pagination: ListPagination) => Effect.Effect<ReadonlyArray<T>, E, R>,
|
|
46
|
+
createdAt: (item: T) => number | undefined,
|
|
47
|
+
pageSize?: number,
|
|
48
|
+
) => Effect.Effect<T[], E, R> = Effect.fn(function* <T, E, R>(
|
|
49
|
+
fetch: (pagination: ListPagination) => Effect.Effect<ReadonlyArray<T>, E, R>,
|
|
50
|
+
createdAt: (item: T) => number | undefined,
|
|
51
|
+
pageSize = 100,
|
|
52
|
+
) {
|
|
53
|
+
const all: T[] = [];
|
|
54
|
+
let before: number | undefined;
|
|
55
|
+
for (let page = 0; page < 1_000; page++) {
|
|
56
|
+
const items = yield* fetch({ maxObjects: pageSize, createdBefore: before });
|
|
57
|
+
all.push(...items);
|
|
58
|
+
if (items.length < pageSize) break;
|
|
59
|
+
const oldest = Math.min(...items.map((item) => createdAt(item) ?? Number.POSITIVE_INFINITY));
|
|
60
|
+
if (!Number.isFinite(oldest) || oldest === before) break;
|
|
61
|
+
before = oldest;
|
|
62
|
+
}
|
|
63
|
+
return all;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The names of every environment in the workspace, for `list` operations that
|
|
68
|
+
* enumerate objects across environments.
|
|
69
|
+
*/
|
|
70
|
+
export const environmentNames = Effect.gen(function* () {
|
|
71
|
+
const response = yield* environment.listEnvironment({});
|
|
72
|
+
return (response.items ?? []).flatMap((item) => (item.name ? [item.name] : []));
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
/** A wire `map<string, string>` without the absent entries proto3 JSON allows. */
|
|
76
|
+
export const stringMap = (
|
|
77
|
+
map: Readonly<Record<string, string | undefined>> | undefined,
|
|
78
|
+
): Record<string, string> =>
|
|
79
|
+
Object.fromEntries(
|
|
80
|
+
Object.entries(map ?? {}).flatMap(([key, value]) =>
|
|
81
|
+
value === undefined ? [] : [[key, value]],
|
|
82
|
+
),
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Whether a name was generated by `createPhysicalName` (it ends in a
|
|
87
|
+
* 16-character base32 instance suffix, or an 8-character truncation hash plus
|
|
88
|
+
* the suffix). Modal objects other than Apps and Sandboxes have no tags, so
|
|
89
|
+
* `list` (and `alchemy nuke`) only enumerate objects with generated names
|
|
90
|
+
* and the objects runtimes created (`isRuntimeName`); objects given an
|
|
91
|
+
* explicit `name` are never swept.
|
|
92
|
+
*/
|
|
93
|
+
export const isGeneratedName = (name: string | undefined): boolean =>
|
|
94
|
+
name !== undefined && /-[a-z2-7]{16}$/.test(name);
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* An environment by name, or a `Modal.Environment` (yielded: `yield* Agents`)
|
|
98
|
+
* whose name is used.
|
|
99
|
+
*/
|
|
100
|
+
export type EnvironmentInput = string | { readonly name: string };
|
|
101
|
+
|
|
102
|
+
/** The environment name an {@link EnvironmentInput} resolved to. */
|
|
103
|
+
export const envNameOf = (value: unknown): string | undefined =>
|
|
104
|
+
typeof value === "string"
|
|
105
|
+
? value || undefined
|
|
106
|
+
: typeof value === "object" &&
|
|
107
|
+
value !== null &&
|
|
108
|
+
"name" in value &&
|
|
109
|
+
typeof value.name === "string"
|
|
110
|
+
? value.name
|
|
111
|
+
: undefined;
|
|
112
|
+
|
|
113
|
+
/** A protobuf `Timestamp` (RFC 3339) for a `Date`, ISO string or epoch milliseconds. */
|
|
114
|
+
export const timestampOf = (value: Date | string | number): string =>
|
|
115
|
+
typeof value === "string" ? value : new Date(value).toISOString();
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Resolve a record of distilled operations into their call functions, like
|
|
119
|
+
* `Effect.all` over Effects. An operation is yieldable (`yield* op` captures
|
|
120
|
+
* the current context and returns a requirement-free call) but isn't an
|
|
121
|
+
* Effect value at runtime, so `Effect.all({ put: queue.putQueue })` fails.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* const QueueOps = operations({ put: queue.putQueue, get: queue.getQueue });
|
|
126
|
+
* const { put } = yield* ModalBindings.withCredentials(QueueOps);
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export const operations = <
|
|
130
|
+
const Ops extends Record<string, Effect.Effect<unknown, never, OperationServices>>,
|
|
131
|
+
>(
|
|
132
|
+
ops: Ops,
|
|
133
|
+
): Effect.Effect<{ readonly [K in keyof Ops]: Effect.Success<Ops[K]> }, never, OperationServices> =>
|
|
134
|
+
Effect.gen(function* () {
|
|
135
|
+
const resolved: Record<string, unknown> = {};
|
|
136
|
+
for (const [key, op] of Object.entries(ops)) resolved[key] = yield* op;
|
|
137
|
+
return resolved as { readonly [K in keyof Ops]: Effect.Success<Ops[K]> };
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
/** The error of a resolved distilled call (or any function returning an Effect). */
|
|
141
|
+
export type ErrorOf<F> = F extends (...args: any[]) => Effect.Effect<any, infer E, any> ? E : never;
|
|
142
|
+
|
|
143
|
+
/** The error of operation `K` of a resolved operations record. */
|
|
144
|
+
export type OpError<Ops, K extends keyof Ops> = ErrorOf<Ops[K]>;
|
|
145
|
+
|
|
146
|
+
/** What a distilled Modal operation needs to run: credentials and an HTTP client. */
|
|
147
|
+
export type OperationServices = Credentials | HttpClient.HttpClient;
|