@alchemy.run/modal 0.0.0-stage → 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 +100 -2
- 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 +75 -3
- package/src/App.ts +262 -0
- package/src/AuthProvider.ts +295 -0
- package/src/BindingHttp.ts +112 -0
- package/src/Blob.ts +71 -0
- package/src/Bootstrap.ts +52 -0
- package/src/Cbor.ts +158 -0
- package/src/ConnectSandbox.ts +99 -0
- package/src/Container.ts +121 -0
- package/src/Contributions.ts +9 -0
- package/src/CreateDict.ts +95 -0
- package/src/CreateDictHttp.ts +91 -0
- package/src/CreateImage.ts +105 -0
- package/src/CreateImageHttp.ts +117 -0
- package/src/CreateQueue.ts +95 -0
- package/src/CreateQueueHttp.ts +91 -0
- package/src/CreateSandbox.ts +228 -0
- package/src/CreateSandboxHttp.ts +160 -0
- package/src/CreateSecret.ts +109 -0
- package/src/CreateSecretHttp.ts +83 -0
- package/src/CreateVolume.ts +96 -0
- package/src/CreateVolumeHttp.ts +93 -0
- package/src/Credentials.ts +74 -0
- package/src/Dict.ts +245 -0
- package/src/DictClient.ts +166 -0
- package/src/Domain.ts +147 -0
- package/src/Endpoint.ts +406 -0
- package/src/Environment.ts +373 -0
- package/src/FetchServer.ts +126 -0
- package/src/FetchServerHttp.ts +110 -0
- package/src/Function.ts +397 -0
- package/src/FunctionAutoscaler.ts +210 -0
- package/src/FunctionTarget.ts +181 -0
- package/src/Image.ts +281 -0
- package/src/ImageBuild.ts +228 -0
- package/src/ImagePlan.ts +78 -0
- package/src/InferEnv.ts +27 -0
- package/src/InvokeFunction.ts +211 -0
- package/src/InvokeFunctionHttp.ts +316 -0
- package/src/ModalBindings.ts +310 -0
- package/src/MountBucket.ts +137 -0
- package/src/MountVolume.ts +113 -0
- package/src/Mounts.ts +230 -0
- package/src/PersistentSandbox.ts +142 -0
- package/src/Providers.ts +100 -0
- package/src/Proxy.ts +191 -0
- package/src/Queue.ts +258 -0
- package/src/QueueClient.ts +149 -0
- package/src/ReadApp.ts +101 -0
- package/src/ReadAppHttp.ts +142 -0
- package/src/ReadDict.ts +44 -0
- package/src/ReadDictHttp.ts +27 -0
- package/src/ReadFunction.ts +107 -0
- package/src/ReadFunctionHttp.ts +91 -0
- package/src/ReadQueue.ts +44 -0
- package/src/ReadQueueHttp.ts +27 -0
- package/src/ReadServer.ts +98 -0
- package/src/ReadServerHttp.ts +101 -0
- package/src/ReadVolume.ts +45 -0
- package/src/ReadVolumeHttp.ts +28 -0
- package/src/ReadWriteDict.ts +44 -0
- package/src/ReadWriteDictHttp.ts +27 -0
- package/src/ReadWriteQueue.ts +45 -0
- package/src/ReadWriteQueueHttp.ts +27 -0
- package/src/ReadWriteVolume.ts +45 -0
- package/src/ReadWriteVolumeHttp.ts +28 -0
- package/src/RuntimeOwner.ts +111 -0
- package/src/Sandbox.ts +402 -0
- package/src/SandboxClient.ts +1404 -0
- package/src/ScaleFunction.ts +70 -0
- package/src/ScaleFunctionHttp.ts +54 -0
- package/src/Secret.ts +296 -0
- package/src/Server.ts +285 -0
- package/src/ServiceUser.ts +295 -0
- package/src/Volume.ts +320 -0
- package/src/VolumeClient.ts +108 -0
- package/src/VolumeFiles.ts +194 -0
- package/src/WebhookToken.ts +230 -0
- package/src/WorkspaceSettings.ts +160 -0
- package/src/WriteApp.ts +43 -0
- package/src/WriteAppHttp.ts +51 -0
- package/src/WriteDict.ts +44 -0
- package/src/WriteDictHttp.ts +27 -0
- package/src/WriteQueue.ts +44 -0
- package/src/WriteQueueHttp.ts +27 -0
- package/src/WriteSecret.ts +60 -0
- package/src/WriteSecretHttp.ts +56 -0
- package/src/WriteVolume.ts +44 -0
- package/src/WriteVolumeHttp.ts +28 -0
- package/src/globals.d.ts +2 -0
- package/src/index.ts +85 -0
- package/src/internal/Declared.ts +80 -0
- package/src/internal/Deploy.ts +309 -0
- package/src/internal/Entrypoint.ts +198 -0
- package/src/internal/Rpc.ts +59 -0
- package/src/internal/RpcAuth.ts +132 -0
- package/src/internal/Runtime.ts +386 -0
- package/src/internal/Tags.ts +44 -0
- package/src/internal/TcpConnect.ts +78 -0
- package/src/internal/Transport.ts +25 -0
- package/src/internal.ts +147 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { anonymous, DEFAULT_API_BASE_URL } from "@distilled.cloud/modal/Credentials";
|
|
2
|
+
import * as token from "@distilled.cloud/modal/token";
|
|
3
|
+
import {
|
|
4
|
+
AuthError,
|
|
5
|
+
AuthProviderLayer,
|
|
6
|
+
type ConfigureField,
|
|
7
|
+
type ConfigureMethod,
|
|
8
|
+
type ProviderDetails,
|
|
9
|
+
} from "alchemy/Auth/AuthProvider";
|
|
10
|
+
import { displayRedacted } from "alchemy/Auth/Credentials";
|
|
11
|
+
import { getEnv, getEnvRedacted, mapPromptCancellation } from "alchemy/Auth/Env";
|
|
12
|
+
import {
|
|
13
|
+
collectFieldValues,
|
|
14
|
+
storedSecret,
|
|
15
|
+
storedValueText,
|
|
16
|
+
validateFieldValues,
|
|
17
|
+
type StoredValues,
|
|
18
|
+
} from "alchemy/Auth/StoredAuthProvider";
|
|
19
|
+
import * as Interaction from "alchemy/Interaction";
|
|
20
|
+
import * as Effect from "effect/Effect";
|
|
21
|
+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
|
|
22
|
+
import * as Layer from "effect/Layer";
|
|
23
|
+
import * as Match from "effect/Match";
|
|
24
|
+
import * as Redacted from "effect/Redacted";
|
|
25
|
+
import * as Schema from "effect/Schema";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Canonical name registered in the AuthProviders registry. Use this key to
|
|
29
|
+
* look up the Modal AuthProvider from inside provider Layers.
|
|
30
|
+
*/
|
|
31
|
+
export const MODAL_AUTH_PROVIDER_NAME = "Modal";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A Modal profile entry. Both methods store a workspace token pair, the
|
|
35
|
+
* same kind `modal token new` writes to `~/.modal.toml`:
|
|
36
|
+
*
|
|
37
|
+
* - `browser`: Alchemy runs Modal's browser token flow, you approve it on
|
|
38
|
+
* modal.com, and Modal mints the pair.
|
|
39
|
+
* - `stored`: you paste a pair created at https://modal.com/settings/tokens.
|
|
40
|
+
*
|
|
41
|
+
* Modal tokens don't expire unless created with an expiry, so there is
|
|
42
|
+
* nothing to refresh. A revoked token fails with Modal's auth error; run
|
|
43
|
+
* `alchemy profile edit --reconfigure Modal` to get a new one.
|
|
44
|
+
*/
|
|
45
|
+
export const ModalAuthConfigSchema = Schema.Union([
|
|
46
|
+
Schema.Struct({
|
|
47
|
+
method: Schema.Literal("browser"),
|
|
48
|
+
tokenId: Schema.String,
|
|
49
|
+
tokenSecret: Schema.String,
|
|
50
|
+
workspace: Schema.optional(Schema.String),
|
|
51
|
+
}),
|
|
52
|
+
Schema.Struct({
|
|
53
|
+
method: Schema.Literal("stored"),
|
|
54
|
+
tokenId: Schema.String,
|
|
55
|
+
tokenSecret: Schema.String,
|
|
56
|
+
}),
|
|
57
|
+
]);
|
|
58
|
+
export type ModalAuthConfig = typeof ModalAuthConfigSchema.Type;
|
|
59
|
+
|
|
60
|
+
/** Resolved Modal credentials: a workspace token pair. */
|
|
61
|
+
export interface ModalResolvedCredentials {
|
|
62
|
+
type: "token";
|
|
63
|
+
tokenId: Redacted.Redacted<string>;
|
|
64
|
+
tokenSecret: Redacted.Redacted<string>;
|
|
65
|
+
apiBaseUrl: string;
|
|
66
|
+
source: { type: ModalAuthConfig["method"] | "env"; details?: string };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const trimmed = (value: Redacted.Redacted<string> | undefined) =>
|
|
70
|
+
value === undefined || Redacted.value(value).trim().length === 0
|
|
71
|
+
? undefined
|
|
72
|
+
: Redacted.make(Redacted.value(value).trim());
|
|
73
|
+
|
|
74
|
+
const tokenFields: ReadonlyArray<ConfigureField> = [
|
|
75
|
+
{
|
|
76
|
+
name: "tokenId",
|
|
77
|
+
label: "Modal Token ID",
|
|
78
|
+
description:
|
|
79
|
+
"Create one at https://modal.com/settings/tokens, or run `modal token new` and copy token_id from ~/.modal.toml.",
|
|
80
|
+
validate: (value) => (value.trim().startsWith("ak-") ? undefined : "Expected an ak-… id"),
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "tokenSecret",
|
|
84
|
+
label: "Modal Token Secret",
|
|
85
|
+
secret: true,
|
|
86
|
+
validate: (value) => (value.trim().startsWith("as-") ? undefined : "Expected an as-… secret"),
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
const storedConfig = (values: StoredValues): ModalAuthConfig => ({
|
|
91
|
+
method: "stored",
|
|
92
|
+
tokenId: (storedValueText(values.tokenId) ?? "").trim(),
|
|
93
|
+
tokenSecret: Redacted.value(storedSecret(values.tokenSecret) ?? Redacted.make("")).trim(),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const methods: Array<{
|
|
97
|
+
value: ModalAuthConfig["method"];
|
|
98
|
+
label: string;
|
|
99
|
+
description?: string;
|
|
100
|
+
}> = [
|
|
101
|
+
{
|
|
102
|
+
value: "browser",
|
|
103
|
+
label: "Browser login",
|
|
104
|
+
description: "recommended — approve on modal.com and Modal mints a token",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
value: "stored",
|
|
108
|
+
label: "Token",
|
|
109
|
+
description: "paste a token pair from modal.com/settings/tokens",
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
/** How long one `waitTokenFlow` call long-polls, as `modal token new` does. */
|
|
114
|
+
const WAIT_SECONDS = 40;
|
|
115
|
+
|
|
116
|
+
/** Long-polls before the login gives up: 8 × 40s, about five minutes. */
|
|
117
|
+
const WAIT_ATTEMPTS = 8;
|
|
118
|
+
|
|
119
|
+
const loginFailed = (cause: unknown) =>
|
|
120
|
+
AuthError.make({ message: "Modal: browser login failed.", cause });
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Modal's browser token flow, the one `modal token new` runs: create a flow,
|
|
124
|
+
* open its page for the user to approve, then long-poll until Modal hands
|
|
125
|
+
* back the new token pair. Both calls run without a token.
|
|
126
|
+
*/
|
|
127
|
+
const browserLogin = Effect.gen(function* () {
|
|
128
|
+
const anonymousApi = Layer.mergeAll(anonymous(), FetchHttpClient.layer);
|
|
129
|
+
const flow = yield* token
|
|
130
|
+
.createTokenFlow({ utmSource: "alchemy" })
|
|
131
|
+
.pipe(Effect.provide(anonymousApi), Effect.mapError(loginFailed));
|
|
132
|
+
if (!flow.tokenFlowId || !flow.webUrl) {
|
|
133
|
+
return yield* AuthError.make({ message: "Modal: the token flow returned no login page." });
|
|
134
|
+
}
|
|
135
|
+
const waitOnce = token
|
|
136
|
+
.waitTokenFlow({
|
|
137
|
+
tokenFlowId: flow.tokenFlowId,
|
|
138
|
+
waitSecret: flow.waitSecret,
|
|
139
|
+
timeout: WAIT_SECONDS,
|
|
140
|
+
})
|
|
141
|
+
.pipe(Effect.provide(anonymousApi), Effect.mapError(loginFailed));
|
|
142
|
+
const approved = waitOnce.pipe(
|
|
143
|
+
Effect.repeat({ until: (result) => result.timeout !== true, times: WAIT_ATTEMPTS - 1 }),
|
|
144
|
+
Effect.flatMap((result) =>
|
|
145
|
+
result.timeout === true || !result.tokenId || !result.tokenSecret
|
|
146
|
+
? Effect.fail(
|
|
147
|
+
AuthError.make({
|
|
148
|
+
message: "Modal: the browser login wasn't approved within five minutes.",
|
|
149
|
+
}),
|
|
150
|
+
)
|
|
151
|
+
: Effect.succeed({
|
|
152
|
+
tokenId: result.tokenId,
|
|
153
|
+
tokenSecret: Redacted.isRedacted(result.tokenSecret)
|
|
154
|
+
? Redacted.value(result.tokenSecret)
|
|
155
|
+
: result.tokenSecret,
|
|
156
|
+
workspace: result.workspaceUsername || undefined,
|
|
157
|
+
}),
|
|
158
|
+
),
|
|
159
|
+
);
|
|
160
|
+
const minted = yield* Interaction.browserLogin({
|
|
161
|
+
provider: "Modal",
|
|
162
|
+
url: flow.webUrl,
|
|
163
|
+
code: flow.code || undefined,
|
|
164
|
+
waitFor: approved,
|
|
165
|
+
}).pipe(Effect.mapError((e) => (e._tag === "AuthError" ? e : loginFailed(e))));
|
|
166
|
+
yield* Interaction.success(
|
|
167
|
+
minted.workspace
|
|
168
|
+
? `Modal: logged in to the ${minted.workspace} workspace.`
|
|
169
|
+
: "Modal: logged in.",
|
|
170
|
+
);
|
|
171
|
+
return { method: "browser" as const, ...minted } satisfies ModalAuthConfig;
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const readEnvironment = Effect.gen(function* () {
|
|
175
|
+
const tokenId = trimmed(yield* getEnvRedacted("MODAL_TOKEN_ID"));
|
|
176
|
+
const tokenSecret = trimmed(yield* getEnvRedacted("MODAL_TOKEN_SECRET"));
|
|
177
|
+
if (tokenId === undefined || tokenSecret === undefined) {
|
|
178
|
+
return yield* AuthError.make({
|
|
179
|
+
message: "Modal CI credentials not found. Set MODAL_TOKEN_ID and MODAL_TOKEN_SECRET.",
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
type: "token" as const,
|
|
184
|
+
tokenId,
|
|
185
|
+
tokenSecret,
|
|
186
|
+
apiBaseUrl: (yield* getEnv("MODAL_SERVER_URL")) ?? DEFAULT_API_BASE_URL,
|
|
187
|
+
source: { type: "env" as const, details: "MODAL_TOKEN_ID" },
|
|
188
|
+
} satisfies ModalResolvedCredentials;
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const resolve = (config: ModalAuthConfig): ModalResolvedCredentials => ({
|
|
192
|
+
type: "token",
|
|
193
|
+
tokenId: Redacted.make(config.tokenId),
|
|
194
|
+
tokenSecret: Redacted.make(config.tokenSecret),
|
|
195
|
+
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
196
|
+
source: {
|
|
197
|
+
type: config.method,
|
|
198
|
+
details: config.method === "browser" ? config.workspace : undefined,
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Layer that registers the Modal {@link AuthProvider} into the
|
|
204
|
+
* `AuthProviders` registry when built. Included in `Modal.providers()` so
|
|
205
|
+
* `alchemy profile edit` can configure it.
|
|
206
|
+
*
|
|
207
|
+
* Supported methods:
|
|
208
|
+
* - `browser`: Modal's browser token flow (interactive only).
|
|
209
|
+
* - `stored`: a pasted token pair (`--method stored --set tokenId=… --set tokenSecret=…`).
|
|
210
|
+
*/
|
|
211
|
+
export const ModalAuth = AuthProviderLayer<ModalAuthConfig, ModalResolvedCredentials>()(
|
|
212
|
+
MODAL_AUTH_PROVIDER_NAME,
|
|
213
|
+
Effect.gen(function* () {
|
|
214
|
+
const pasteToken = collectFieldValues(tokenFields).pipe(
|
|
215
|
+
Effect.map(storedConfig),
|
|
216
|
+
Effect.tap(() => Interaction.success("Modal: credentials saved.")),
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const configure = (_profileName: string) =>
|
|
220
|
+
Interaction.select({ message: "Modal authentication method", options: methods }).pipe(
|
|
221
|
+
mapPromptCancellation,
|
|
222
|
+
Effect.flatMap((method) =>
|
|
223
|
+
Match.value(method).pipe(
|
|
224
|
+
Match.when("browser", () => browserLogin),
|
|
225
|
+
Match.when("stored", () => pasteToken),
|
|
226
|
+
Match.exhaustive,
|
|
227
|
+
),
|
|
228
|
+
),
|
|
229
|
+
Effect.mapError((e) =>
|
|
230
|
+
e._tag === "AuthError"
|
|
231
|
+
? e
|
|
232
|
+
: AuthError.make({ message: "failed to configure credentials", cause: e }),
|
|
233
|
+
),
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
/** The browser flow needs a person, so only `stored` takes flags. */
|
|
237
|
+
const configureMethods: ReadonlyArray<ConfigureMethod> = [
|
|
238
|
+
{ method: "stored", fields: tokenFields },
|
|
239
|
+
];
|
|
240
|
+
|
|
241
|
+
const configureWith = (
|
|
242
|
+
_profileName: string,
|
|
243
|
+
input: { readonly method: string; readonly values: Record<string, string> },
|
|
244
|
+
) =>
|
|
245
|
+
input.method === "stored"
|
|
246
|
+
? validateFieldValues(MODAL_AUTH_PROVIDER_NAME, tokenFields, input.values).pipe(
|
|
247
|
+
Effect.map(storedConfig),
|
|
248
|
+
Effect.tap(() => Interaction.success("Modal: credentials saved.")),
|
|
249
|
+
)
|
|
250
|
+
: Effect.fail(
|
|
251
|
+
AuthError.make({
|
|
252
|
+
message: `Modal: unknown method '${input.method}'. Valid methods: stored. (Browser login is interactive-only.)`,
|
|
253
|
+
}),
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
// A token pair doesn't expire on its own, so logging in again only
|
|
257
|
+
// repeats the browser flow; a pasted token stays as it is.
|
|
258
|
+
const login = (_profileName: string, config: ModalAuthConfig) =>
|
|
259
|
+
config.method === "browser" ? browserLogin : Effect.succeed(config);
|
|
260
|
+
|
|
261
|
+
const details = (_profileName: string, config: ModalAuthConfig) =>
|
|
262
|
+
Effect.succeed<ProviderDetails>({
|
|
263
|
+
lines: [
|
|
264
|
+
{ key: "tokenId", value: config.tokenId },
|
|
265
|
+
{ key: "tokenSecret", value: displayRedacted(Redacted.make(config.tokenSecret)) },
|
|
266
|
+
...(config.method === "browser" && config.workspace
|
|
267
|
+
? [{ key: "workspace", value: config.workspace }]
|
|
268
|
+
: []),
|
|
269
|
+
{ key: "source", value: config.method },
|
|
270
|
+
],
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
configSchema: ModalAuthConfigSchema,
|
|
275
|
+
configure,
|
|
276
|
+
configureWith,
|
|
277
|
+
configureMethods,
|
|
278
|
+
login,
|
|
279
|
+
logout: () => Effect.void,
|
|
280
|
+
details,
|
|
281
|
+
read: (_profileName: string, config: ModalAuthConfig) => Effect.succeed(resolve(config)),
|
|
282
|
+
readEnvironment,
|
|
283
|
+
environment: [
|
|
284
|
+
{ name: "MODAL_TOKEN_ID", required: true, description: "Modal token id (`ak-…`)." },
|
|
285
|
+
{
|
|
286
|
+
name: "MODAL_TOKEN_SECRET",
|
|
287
|
+
required: true,
|
|
288
|
+
secret: true,
|
|
289
|
+
description: "Modal token secret (`as-…`).",
|
|
290
|
+
},
|
|
291
|
+
{ name: "MODAL_SERVER_URL", required: false, description: "Modal API URL override." },
|
|
292
|
+
],
|
|
293
|
+
};
|
|
294
|
+
}),
|
|
295
|
+
);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { Input } from "alchemy/Input";
|
|
2
|
+
import type { ResourceLike } from "alchemy/Resource";
|
|
3
|
+
import * as Data from "effect/Data";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import type { OperationServices } from "./internal.ts";
|
|
6
|
+
import * as ModalBindings from "./ModalBindings.ts";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Shared scaffolding for Modal's HTTP bindings. NOT exported from `index.ts`.
|
|
10
|
+
*
|
|
11
|
+
* Every `{Cap}Http.ts` is a thin `Layer.effect(Cap, makeModalBinding({ … }))`:
|
|
12
|
+
* the operations it calls (resolved once with the host's credentials by
|
|
13
|
+
* {@link ModalBindings.withCredentials}), the environment role it grants the
|
|
14
|
+
* host's identity on the bound resource's environment, and the client it
|
|
15
|
+
* builds. `make` runs in the construction phase, so it reads the resource's
|
|
16
|
+
* attribute accessors there (`yield* volume.volumeId`) and yields them per call.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** A role on an environment, granted to the host's identity. */
|
|
20
|
+
export type Role = ModalBindings.EnvironmentGrant["role"];
|
|
21
|
+
|
|
22
|
+
const GrantRole = Effect.gen(function* () {
|
|
23
|
+
const bind = yield* ModalBindings.Bind;
|
|
24
|
+
return Effect.fn(function* (
|
|
25
|
+
label: TemplateStringsArray,
|
|
26
|
+
args: ReadonlyArray<unknown>,
|
|
27
|
+
environment: Input<string | undefined>,
|
|
28
|
+
role: Role,
|
|
29
|
+
) {
|
|
30
|
+
if (!globalThis.__ALCHEMY_RUNTIME__) {
|
|
31
|
+
yield* bind(label, ...args)({ roles: [{ environment, role }] });
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/** A binding over one resource that lives in an environment. */
|
|
37
|
+
export const makeModalBinding = <
|
|
38
|
+
Res extends ResourceLike & { readonly environment: unknown },
|
|
39
|
+
Ops,
|
|
40
|
+
Client,
|
|
41
|
+
>(options: {
|
|
42
|
+
/** Fully qualified binding tag, e.g. `Modal.WriteQueue`. */
|
|
43
|
+
readonly tag: string;
|
|
44
|
+
/** The distilled operations the client calls, e.g. `operations({ put: queue.putQueue })`. */
|
|
45
|
+
readonly operations: Effect.Effect<Ops, never, OperationServices>;
|
|
46
|
+
/** The role the host needs on the resource's environment. */
|
|
47
|
+
readonly role: Role;
|
|
48
|
+
readonly make: (resource: Res, ops: Ops) => Effect.Effect<Client>;
|
|
49
|
+
}) =>
|
|
50
|
+
Effect.gen(function* () {
|
|
51
|
+
const ops = yield* ModalBindings.withCredentials(options.operations);
|
|
52
|
+
const grant = yield* GrantRole;
|
|
53
|
+
return Effect.fn(options.tag)(function* (resource: Res) {
|
|
54
|
+
yield* grant(
|
|
55
|
+
labelOf(options.tag),
|
|
56
|
+
[resource],
|
|
57
|
+
resource.environment as Input<string | undefined>,
|
|
58
|
+
options.role,
|
|
59
|
+
);
|
|
60
|
+
return yield* options.make(resource, ops);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A binding with no bound resource (get-or-create by name at runtime): the
|
|
66
|
+
* role is granted on the environment `environmentOf(args)` names, the
|
|
67
|
+
* workspace default when `undefined`.
|
|
68
|
+
*/
|
|
69
|
+
export const makeOpenModalBinding = <Args extends ReadonlyArray<unknown>, Ops, Client>(options: {
|
|
70
|
+
readonly tag: string;
|
|
71
|
+
readonly operations: Effect.Effect<Ops, never, OperationServices>;
|
|
72
|
+
readonly role: Role;
|
|
73
|
+
/** The environment the client acts in (an `Input`, read at deploy). */
|
|
74
|
+
readonly environmentOf: (...args: Args) => Input<string | undefined>;
|
|
75
|
+
readonly make: (ops: Ops, ...args: Args) => Effect.Effect<Client>;
|
|
76
|
+
}) =>
|
|
77
|
+
Effect.gen(function* () {
|
|
78
|
+
const ops = yield* ModalBindings.withCredentials(options.operations);
|
|
79
|
+
const grant = yield* GrantRole;
|
|
80
|
+
return Effect.fn(options.tag)(function* (...args: Args) {
|
|
81
|
+
yield* grant(openLabelOf(options.tag), [], options.environmentOf(...args), options.role);
|
|
82
|
+
return yield* options.make(ops, ...args);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
/** `<tag>(<resource>)` as a template label. */
|
|
87
|
+
const labelOf = (tag: string): TemplateStringsArray =>
|
|
88
|
+
Object.assign([`${tag}(`, ")"], { raw: [`${tag}(`, ")"] }) as unknown as TemplateStringsArray;
|
|
89
|
+
|
|
90
|
+
/** `<tag>` as a template label. */
|
|
91
|
+
const openLabelOf = (tag: string): TemplateStringsArray =>
|
|
92
|
+
Object.assign([tag], { raw: [tag] }) as unknown as TemplateStringsArray;
|
|
93
|
+
|
|
94
|
+
/** A stored value isn't JSON (e.g. a pickled value written by Modal's Python client). */
|
|
95
|
+
export class ModalValueNotJson extends Data.TaggedError("Modal.ValueNotJson")<{
|
|
96
|
+
readonly message: string;
|
|
97
|
+
}> {}
|
|
98
|
+
|
|
99
|
+
/** JSON-encode a value as base64 bytes, Modal's `bytes` wire form. */
|
|
100
|
+
export const encodeJson = (value: unknown) =>
|
|
101
|
+
Effect.sync(() => Buffer.from(JSON.stringify(value), "utf8").toString("base64"));
|
|
102
|
+
|
|
103
|
+
/** Decode base64 JSON bytes written by {@link encodeJson} or any JSON writer. */
|
|
104
|
+
export const decodeJson = <A = unknown>(data: string) =>
|
|
105
|
+
Effect.try({
|
|
106
|
+
try: () => JSON.parse(Buffer.from(data, "base64").toString("utf8")) as A,
|
|
107
|
+
catch: () =>
|
|
108
|
+
new ModalValueNotJson({
|
|
109
|
+
message:
|
|
110
|
+
"The stored value isn't JSON. Values written by Modal's Python client are pickled; write them as JSON (bytes of json.dumps) to share them.",
|
|
111
|
+
}),
|
|
112
|
+
});
|
package/src/Blob.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import * as blob from "@distilled.cloud/modal/blob";
|
|
3
|
+
import * as Data from "effect/Data";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as HttpClient from "effect/http/HttpClient";
|
|
6
|
+
import * as HttpClientRequest from "effect/http/HttpClientRequest";
|
|
7
|
+
import { operations } from "./internal.ts";
|
|
8
|
+
|
|
9
|
+
/** Inputs and results above this size travel through Modal's blob store. */
|
|
10
|
+
export const MAX_INLINE_BYTES = 2 * 1024 * 1024;
|
|
11
|
+
|
|
12
|
+
/** A payload Modal's blob store couldn't take or give back. */
|
|
13
|
+
export class BlobTransferFailed extends Data.TaggedError("Modal.BlobTransferFailed")<{
|
|
14
|
+
readonly message: string;
|
|
15
|
+
}> {}
|
|
16
|
+
|
|
17
|
+
/** The blob operations, resolved by `ModalBindings.withCredentials`. */
|
|
18
|
+
export const BlobOps = operations({
|
|
19
|
+
create: blob.createBlob,
|
|
20
|
+
get: blob.getBlob,
|
|
21
|
+
http: HttpClient.HttpClient,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export type BlobOps = Effect.Success<typeof BlobOps>;
|
|
25
|
+
|
|
26
|
+
/** Upload bytes to Modal's blob store and return the blob id. */
|
|
27
|
+
export const uploadBlob = Effect.fn(function* (ops: BlobOps, bytes: Uint8Array) {
|
|
28
|
+
const digests = yield* Effect.sync(() => ({
|
|
29
|
+
md5: createHash("md5").update(bytes).digest("base64"),
|
|
30
|
+
sha256: createHash("sha256").update(bytes).digest("base64"),
|
|
31
|
+
}));
|
|
32
|
+
const created = yield* ops.create({
|
|
33
|
+
contentMd5: digests.md5,
|
|
34
|
+
contentSha256Base64: digests.sha256,
|
|
35
|
+
contentLength: String(bytes.length),
|
|
36
|
+
});
|
|
37
|
+
if (!created.blobId || !created.uploadUrl) {
|
|
38
|
+
return yield* new BlobTransferFailed({
|
|
39
|
+
message: `Modal asked for a multipart upload of ${bytes.length} bytes, which this client doesn't send`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// A presigned upload URL Modal handed back: used as-is.
|
|
43
|
+
const response = yield* ops.http.execute(
|
|
44
|
+
HttpClientRequest.put(created.uploadUrl).pipe(
|
|
45
|
+
HttpClientRequest.bodyUint8Array(bytes, "application/octet-stream"),
|
|
46
|
+
HttpClientRequest.setHeader("content-md5", digests.md5),
|
|
47
|
+
),
|
|
48
|
+
);
|
|
49
|
+
if (response.status >= 300) {
|
|
50
|
+
return yield* new BlobTransferFailed({
|
|
51
|
+
message: `Uploading a blob failed with HTTP ${response.status}`,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return created.blobId;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/** Download a blob's bytes. */
|
|
58
|
+
export const downloadBlob = Effect.fn(function* (ops: BlobOps, blobId: string) {
|
|
59
|
+
const { downloadUrl } = yield* ops.get({ blobId });
|
|
60
|
+
if (!downloadUrl) {
|
|
61
|
+
return yield* new BlobTransferFailed({ message: `Modal returned no URL for blob ${blobId}` });
|
|
62
|
+
}
|
|
63
|
+
// A presigned download URL Modal handed back: used as-is.
|
|
64
|
+
const response = yield* ops.http.get(downloadUrl);
|
|
65
|
+
if (response.status >= 300) {
|
|
66
|
+
return yield* new BlobTransferFailed({
|
|
67
|
+
message: `Downloading blob ${blobId} failed with HTTP ${response.status}`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return new Uint8Array(yield* response.arrayBuffer);
|
|
71
|
+
});
|
package/src/Bootstrap.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process bootstrap for `Modal.Sandbox` (a Node process in a Modal sandbox serving the
|
|
3
|
+
* bundled program). The generated entry imports this module and the user's
|
|
4
|
+
* `main`, nothing else — see `alchemy/Runtime/Bootstrap` for why.
|
|
5
|
+
*/
|
|
6
|
+
import { NodeServices } from "@effect/platform-node";
|
|
7
|
+
import { NodeHttpServer } from "alchemy/Http";
|
|
8
|
+
import { reifyBoundConfigProvider } from "alchemy/Runtime";
|
|
9
|
+
import {
|
|
10
|
+
entrypointLayer,
|
|
11
|
+
resolveProgram,
|
|
12
|
+
runProcess,
|
|
13
|
+
stackFromEnv,
|
|
14
|
+
} from "alchemy/Runtime/Bootstrap";
|
|
15
|
+
import * as ConfigProvider from "effect/ConfigProvider";
|
|
16
|
+
import * as Effect from "effect/Effect";
|
|
17
|
+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
|
|
18
|
+
import * as Layer from "effect/Layer";
|
|
19
|
+
import * as Logger from "effect/Logger";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the bundled program (the runners registered via `host.run` /
|
|
23
|
+
* serve) and run it with a Node HTTP server bound to `PORT`, so the
|
|
24
|
+
* returned `{ fetch }` handler is actually served and `host.run` loops
|
|
25
|
+
* stay alive.
|
|
26
|
+
*/
|
|
27
|
+
export const bootstrap = (entrypoint: unknown): Promise<void> => {
|
|
28
|
+
const platform = Layer.mergeAll(
|
|
29
|
+
NodeServices.layer,
|
|
30
|
+
FetchHttpClient.layer,
|
|
31
|
+
Logger.layer([Logger.consolePretty()]),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const program = resolveProgram("program").pipe(
|
|
35
|
+
Effect.provide(
|
|
36
|
+
entrypointLayer(entrypoint).pipe(
|
|
37
|
+
Layer.provideMerge(stackFromEnv),
|
|
38
|
+
Layer.provideMerge(NodeHttpServer({ hostname: "0.0.0.0" })),
|
|
39
|
+
Layer.provideMerge(platform),
|
|
40
|
+
Layer.provideMerge(
|
|
41
|
+
Layer.succeed(
|
|
42
|
+
ConfigProvider.ConfigProvider,
|
|
43
|
+
reifyBoundConfigProvider(ConfigProvider.fromEnv(), process.env),
|
|
44
|
+
),
|
|
45
|
+
),
|
|
46
|
+
),
|
|
47
|
+
),
|
|
48
|
+
Effect.scoped,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return runProcess("Modal sandbox", program);
|
|
52
|
+
};
|
package/src/Cbor.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal CBOR (RFC 8949) codec for Modal function arguments and results
|
|
3
|
+
* (`DATA_FORMAT_CBOR`): null, booleans, numbers, strings, byte strings,
|
|
4
|
+
* arrays and string-keyed maps. NOT exported from `index.ts`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const enc = new TextEncoder();
|
|
8
|
+
const dec = new TextDecoder();
|
|
9
|
+
|
|
10
|
+
class Out {
|
|
11
|
+
bytes: number[] = [];
|
|
12
|
+
head(major: number, n: number) {
|
|
13
|
+
if (n < 24) this.bytes.push((major << 5) | n);
|
|
14
|
+
else if (n < 0x100) this.bytes.push((major << 5) | 24, n);
|
|
15
|
+
else if (n < 0x10000) this.bytes.push((major << 5) | 25, n >> 8, n & 0xff);
|
|
16
|
+
else if (n < 0x100000000)
|
|
17
|
+
this.bytes.push(
|
|
18
|
+
(major << 5) | 26,
|
|
19
|
+
(n >>> 24) & 0xff,
|
|
20
|
+
(n >>> 16) & 0xff,
|
|
21
|
+
(n >>> 8) & 0xff,
|
|
22
|
+
n & 0xff,
|
|
23
|
+
);
|
|
24
|
+
else {
|
|
25
|
+
const big = BigInt(n);
|
|
26
|
+
this.bytes.push((major << 5) | 27);
|
|
27
|
+
for (let i = 7; i >= 0; i--) this.bytes.push(Number((big >> BigInt(i * 8)) & 0xffn));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const write = (out: Out, value: unknown): void => {
|
|
33
|
+
if (value === null || value === undefined) {
|
|
34
|
+
out.bytes.push(0xf6);
|
|
35
|
+
} else if (value === false) {
|
|
36
|
+
out.bytes.push(0xf4);
|
|
37
|
+
} else if (value === true) {
|
|
38
|
+
out.bytes.push(0xf5);
|
|
39
|
+
} else if (typeof value === "number") {
|
|
40
|
+
if (Number.isSafeInteger(value)) {
|
|
41
|
+
if (value >= 0) out.head(0, value);
|
|
42
|
+
else out.head(1, -1 - value);
|
|
43
|
+
} else {
|
|
44
|
+
const view = new DataView(new ArrayBuffer(8));
|
|
45
|
+
view.setFloat64(0, value);
|
|
46
|
+
out.bytes.push(0xfb, ...new Uint8Array(view.buffer));
|
|
47
|
+
}
|
|
48
|
+
} else if (typeof value === "bigint") {
|
|
49
|
+
write(out, Number(value));
|
|
50
|
+
} else if (typeof value === "string") {
|
|
51
|
+
const bytes = enc.encode(value);
|
|
52
|
+
out.head(3, bytes.length);
|
|
53
|
+
out.bytes.push(...bytes);
|
|
54
|
+
} else if (value instanceof Uint8Array) {
|
|
55
|
+
out.head(2, value.length);
|
|
56
|
+
out.bytes.push(...value);
|
|
57
|
+
} else if (Array.isArray(value)) {
|
|
58
|
+
out.head(4, value.length);
|
|
59
|
+
for (const item of value) write(out, item);
|
|
60
|
+
} else if (typeof value === "object") {
|
|
61
|
+
const entries = Object.entries(value).filter(([, v]) => v !== undefined);
|
|
62
|
+
out.head(5, entries.length);
|
|
63
|
+
for (const [key, item] of entries) {
|
|
64
|
+
write(out, key);
|
|
65
|
+
write(out, item);
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
throw new TypeError(`Can't encode ${typeof value} as CBOR`);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/** Encode a value as CBOR. Throws a `TypeError` for functions and symbols. */
|
|
73
|
+
export const encodeCbor = (value: unknown): Uint8Array => {
|
|
74
|
+
const out = new Out();
|
|
75
|
+
write(out, value);
|
|
76
|
+
return new Uint8Array(out.bytes);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** Decode CBOR. Throws a `RangeError` on malformed input. */
|
|
80
|
+
export const decodeCbor = (bytes: Uint8Array): unknown => {
|
|
81
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
82
|
+
let pos = 0;
|
|
83
|
+
const need = (n: number) => {
|
|
84
|
+
if (pos + n > bytes.length) throw new RangeError("Truncated CBOR");
|
|
85
|
+
};
|
|
86
|
+
const length = (info: number): number => {
|
|
87
|
+
if (info < 24) return info;
|
|
88
|
+
if (info === 24) return (need(1), bytes[pos++]!);
|
|
89
|
+
if (info === 25) return (need(2), (pos += 2), view.getUint16(pos - 2));
|
|
90
|
+
if (info === 26) return (need(4), (pos += 4), view.getUint32(pos - 4));
|
|
91
|
+
if (info === 27) return (need(8), (pos += 8), Number(view.getBigUint64(pos - 8)));
|
|
92
|
+
throw new RangeError(`Unsupported CBOR length ${info}`);
|
|
93
|
+
};
|
|
94
|
+
const read = (): unknown => {
|
|
95
|
+
need(1);
|
|
96
|
+
const first = bytes[pos++]!;
|
|
97
|
+
const major = first >> 5;
|
|
98
|
+
const info = first & 31;
|
|
99
|
+
switch (major) {
|
|
100
|
+
case 0:
|
|
101
|
+
return length(info);
|
|
102
|
+
case 1:
|
|
103
|
+
return -1 - length(info);
|
|
104
|
+
case 2: {
|
|
105
|
+
const n = length(info);
|
|
106
|
+
need(n);
|
|
107
|
+
pos += n;
|
|
108
|
+
return bytes.slice(pos - n, pos);
|
|
109
|
+
}
|
|
110
|
+
case 3: {
|
|
111
|
+
const n = length(info);
|
|
112
|
+
need(n);
|
|
113
|
+
pos += n;
|
|
114
|
+
return dec.decode(bytes.subarray(pos - n, pos));
|
|
115
|
+
}
|
|
116
|
+
case 4: {
|
|
117
|
+
const n = length(info);
|
|
118
|
+
return Array.from({ length: n }, read);
|
|
119
|
+
}
|
|
120
|
+
case 5: {
|
|
121
|
+
const n = length(info);
|
|
122
|
+
const out: Record<string, unknown> = {};
|
|
123
|
+
for (let i = 0; i < n; i++) {
|
|
124
|
+
const key = read();
|
|
125
|
+
out[String(key)] = read();
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
case 6:
|
|
130
|
+
length(info);
|
|
131
|
+
return read();
|
|
132
|
+
case 7:
|
|
133
|
+
if (info === 20) return false;
|
|
134
|
+
if (info === 21) return true;
|
|
135
|
+
if (info === 22 || info === 23) return null;
|
|
136
|
+
if (info === 25) {
|
|
137
|
+
need(2);
|
|
138
|
+
pos += 2;
|
|
139
|
+
return view.getFloat16 ? view.getFloat16(pos - 2) : halfToFloat(view.getUint16(pos - 2));
|
|
140
|
+
}
|
|
141
|
+
if (info === 26) return (need(4), (pos += 4), view.getFloat32(pos - 4));
|
|
142
|
+
if (info === 27) return (need(8), (pos += 8), view.getFloat64(pos - 8));
|
|
143
|
+
throw new RangeError(`Unsupported CBOR simple value ${info}`);
|
|
144
|
+
default:
|
|
145
|
+
throw new RangeError(`Unsupported CBOR major type ${major}`);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
return read();
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const halfToFloat = (h: number): number => {
|
|
152
|
+
const sign = h & 0x8000 ? -1 : 1;
|
|
153
|
+
const exp = (h >> 10) & 0x1f;
|
|
154
|
+
const frac = h & 0x3ff;
|
|
155
|
+
if (exp === 0) return sign * 2 ** -14 * (frac / 1024);
|
|
156
|
+
if (exp === 31) return frac ? Number.NaN : sign * Number.POSITIVE_INFINITY;
|
|
157
|
+
return sign * 2 ** (exp - 15) * (1 + frac / 1024);
|
|
158
|
+
};
|