@alchemy.run/modal 0.0.0 → 2.0.0-beta.86

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (499) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +96 -4
  3. package/lib/App.d.ts +143 -0
  4. package/lib/App.d.ts.map +1 -0
  5. package/lib/App.js +198 -0
  6. package/lib/App.js.map +1 -0
  7. package/lib/AuthProvider.d.ts +53 -0
  8. package/lib/AuthProvider.d.ts.map +1 -0
  9. package/lib/AuthProvider.js +209 -0
  10. package/lib/AuthProvider.js.map +1 -0
  11. package/lib/BindingHttp.d.ts +56 -0
  12. package/lib/BindingHttp.d.ts.map +1 -0
  13. package/lib/BindingHttp.js +50 -0
  14. package/lib/BindingHttp.js.map +1 -0
  15. package/lib/Blob.d.ts +34 -0
  16. package/lib/Blob.d.ts.map +1 -0
  17. package/lib/Blob.js +59 -0
  18. package/lib/Blob.js.map +1 -0
  19. package/lib/Bootstrap.d.ts +8 -0
  20. package/lib/Bootstrap.d.ts.map +1 -0
  21. package/lib/Bootstrap.js +26 -0
  22. package/lib/Bootstrap.js.map +1 -0
  23. package/lib/Cbor.d.ts +10 -0
  24. package/lib/Cbor.d.ts.map +1 -0
  25. package/lib/Cbor.js +177 -0
  26. package/lib/Cbor.js.map +1 -0
  27. package/lib/ConnectSandbox.d.ts +64 -0
  28. package/lib/ConnectSandbox.d.ts.map +1 -0
  29. package/lib/ConnectSandbox.js +33 -0
  30. package/lib/ConnectSandbox.js.map +1 -0
  31. package/lib/Container.d.ts +124 -0
  32. package/lib/Container.d.ts.map +1 -0
  33. package/lib/Container.js +1 -0
  34. package/lib/Container.js.map +1 -0
  35. package/lib/Contributions.d.ts +7 -0
  36. package/lib/Contributions.d.ts.map +1 -0
  37. package/lib/Contributions.js +6 -0
  38. package/lib/Contributions.js.map +1 -0
  39. package/lib/CreateDict.d.ts +83 -0
  40. package/lib/CreateDict.d.ts.map +1 -0
  41. package/lib/CreateDict.js +3 -0
  42. package/lib/CreateDict.js.map +1 -0
  43. package/lib/CreateDictHttp.d.ts +33 -0
  44. package/lib/CreateDictHttp.d.ts.map +1 -0
  45. package/lib/CreateDictHttp.js +67 -0
  46. package/lib/CreateDictHttp.js.map +1 -0
  47. package/lib/CreateImage.d.ts +98 -0
  48. package/lib/CreateImage.d.ts.map +1 -0
  49. package/lib/CreateImage.js +3 -0
  50. package/lib/CreateImage.js.map +1 -0
  51. package/lib/CreateImageHttp.d.ts +51 -0
  52. package/lib/CreateImageHttp.d.ts.map +1 -0
  53. package/lib/CreateImageHttp.js +87 -0
  54. package/lib/CreateImageHttp.js.map +1 -0
  55. package/lib/CreateQueue.d.ts +83 -0
  56. package/lib/CreateQueue.d.ts.map +1 -0
  57. package/lib/CreateQueue.js +3 -0
  58. package/lib/CreateQueue.js.map +1 -0
  59. package/lib/CreateQueueHttp.d.ts +31 -0
  60. package/lib/CreateQueueHttp.d.ts.map +1 -0
  61. package/lib/CreateQueueHttp.js +67 -0
  62. package/lib/CreateQueueHttp.js.map +1 -0
  63. package/lib/CreateSandbox.d.ts +186 -0
  64. package/lib/CreateSandbox.d.ts.map +1 -0
  65. package/lib/CreateSandbox.js +3 -0
  66. package/lib/CreateSandbox.js.map +1 -0
  67. package/lib/CreateSandboxHttp.d.ts +17 -0
  68. package/lib/CreateSandboxHttp.d.ts.map +1 -0
  69. package/lib/CreateSandboxHttp.js +117 -0
  70. package/lib/CreateSandboxHttp.js.map +1 -0
  71. package/lib/CreateSecret.d.ts +95 -0
  72. package/lib/CreateSecret.d.ts.map +1 -0
  73. package/lib/CreateSecret.js +3 -0
  74. package/lib/CreateSecret.js.map +1 -0
  75. package/lib/CreateSecretHttp.d.ts +24 -0
  76. package/lib/CreateSecretHttp.d.ts.map +1 -0
  77. package/lib/CreateSecretHttp.js +60 -0
  78. package/lib/CreateSecretHttp.js.map +1 -0
  79. package/lib/CreateVolume.d.ts +84 -0
  80. package/lib/CreateVolume.d.ts.map +1 -0
  81. package/lib/CreateVolume.js +3 -0
  82. package/lib/CreateVolume.js.map +1 -0
  83. package/lib/CreateVolumeHttp.d.ts +32 -0
  84. package/lib/CreateVolumeHttp.d.ts.map +1 -0
  85. package/lib/CreateVolumeHttp.js +69 -0
  86. package/lib/CreateVolumeHttp.js.map +1 -0
  87. package/lib/Credentials.d.ts +32 -0
  88. package/lib/Credentials.d.ts.map +1 -0
  89. package/lib/Credentials.js +44 -0
  90. package/lib/Credentials.js.map +1 -0
  91. package/lib/Dict.d.ts +147 -0
  92. package/lib/Dict.d.ts.map +1 -0
  93. package/lib/Dict.js +175 -0
  94. package/lib/Dict.js.map +1 -0
  95. package/lib/DictClient.d.ts +55 -0
  96. package/lib/DictClient.d.ts.map +1 -0
  97. package/lib/DictClient.js +89 -0
  98. package/lib/DictClient.js.map +1 -0
  99. package/lib/Domain.d.ts +86 -0
  100. package/lib/Domain.d.ts.map +1 -0
  101. package/lib/Domain.js +113 -0
  102. package/lib/Domain.js.map +1 -0
  103. package/lib/Endpoint.d.ts +181 -0
  104. package/lib/Endpoint.d.ts.map +1 -0
  105. package/lib/Endpoint.js +283 -0
  106. package/lib/Endpoint.js.map +1 -0
  107. package/lib/Environment.d.ts +174 -0
  108. package/lib/Environment.d.ts.map +1 -0
  109. package/lib/Environment.js +244 -0
  110. package/lib/Environment.js.map +1 -0
  111. package/lib/FetchServer.d.ts +105 -0
  112. package/lib/FetchServer.d.ts.map +1 -0
  113. package/lib/FetchServer.js +3 -0
  114. package/lib/FetchServer.js.map +1 -0
  115. package/lib/FetchServerHttp.d.ts +21 -0
  116. package/lib/FetchServerHttp.d.ts.map +1 -0
  117. package/lib/FetchServerHttp.js +84 -0
  118. package/lib/FetchServerHttp.js.map +1 -0
  119. package/lib/Function.d.ts +236 -0
  120. package/lib/Function.d.ts.map +1 -0
  121. package/lib/Function.js +273 -0
  122. package/lib/Function.js.map +1 -0
  123. package/lib/FunctionAutoscaler.d.ts +99 -0
  124. package/lib/FunctionAutoscaler.d.ts.map +1 -0
  125. package/lib/FunctionAutoscaler.js +136 -0
  126. package/lib/FunctionAutoscaler.js.map +1 -0
  127. package/lib/FunctionTarget.d.ts +61 -0
  128. package/lib/FunctionTarget.d.ts.map +1 -0
  129. package/lib/FunctionTarget.js +136 -0
  130. package/lib/FunctionTarget.js.map +1 -0
  131. package/lib/Image.d.ts +159 -0
  132. package/lib/Image.d.ts.map +1 -0
  133. package/lib/Image.js +203 -0
  134. package/lib/Image.js.map +1 -0
  135. package/lib/ImageBuild.d.ts +108 -0
  136. package/lib/ImageBuild.d.ts.map +1 -0
  137. package/lib/ImageBuild.js +157 -0
  138. package/lib/ImageBuild.js.map +1 -0
  139. package/lib/ImagePlan.d.ts +31 -0
  140. package/lib/ImagePlan.d.ts.map +1 -0
  141. package/lib/ImagePlan.js +70 -0
  142. package/lib/ImagePlan.js.map +1 -0
  143. package/lib/InferEnv.d.ts +26 -0
  144. package/lib/InferEnv.d.ts.map +1 -0
  145. package/lib/InferEnv.js +1 -0
  146. package/lib/InferEnv.js.map +1 -0
  147. package/lib/InvokeFunction.d.ts +186 -0
  148. package/lib/InvokeFunction.d.ts.map +1 -0
  149. package/lib/InvokeFunction.js +10 -0
  150. package/lib/InvokeFunction.js.map +1 -0
  151. package/lib/InvokeFunctionHttp.d.ts +39 -0
  152. package/lib/InvokeFunctionHttp.d.ts.map +1 -0
  153. package/lib/InvokeFunctionHttp.js +210 -0
  154. package/lib/InvokeFunctionHttp.js.map +1 -0
  155. package/lib/ModalBindings.d.ts +138 -0
  156. package/lib/ModalBindings.d.ts.map +1 -0
  157. package/lib/ModalBindings.js +223 -0
  158. package/lib/ModalBindings.js.map +1 -0
  159. package/lib/MountBucket.d.ts +80 -0
  160. package/lib/MountBucket.d.ts.map +1 -0
  161. package/lib/MountBucket.js +44 -0
  162. package/lib/MountBucket.js.map +1 -0
  163. package/lib/MountVolume.d.ts +77 -0
  164. package/lib/MountVolume.d.ts.map +1 -0
  165. package/lib/MountVolume.js +41 -0
  166. package/lib/MountVolume.js.map +1 -0
  167. package/lib/Mounts.d.ts +134 -0
  168. package/lib/Mounts.d.ts.map +1 -0
  169. package/lib/Mounts.js +87 -0
  170. package/lib/Mounts.js.map +1 -0
  171. package/lib/PersistentSandbox.d.ts +75 -0
  172. package/lib/PersistentSandbox.d.ts.map +1 -0
  173. package/lib/PersistentSandbox.js +93 -0
  174. package/lib/PersistentSandbox.js.map +1 -0
  175. package/lib/Providers.d.ts +23 -0
  176. package/lib/Providers.d.ts.map +1 -0
  177. package/lib/Providers.js +64 -0
  178. package/lib/Providers.js.map +1 -0
  179. package/lib/Proxy.d.ts +94 -0
  180. package/lib/Proxy.d.ts.map +1 -0
  181. package/lib/Proxy.js +132 -0
  182. package/lib/Proxy.js.map +1 -0
  183. package/lib/Queue.d.ts +147 -0
  184. package/lib/Queue.d.ts.map +1 -0
  185. package/lib/Queue.js +177 -0
  186. package/lib/Queue.js.map +1 -0
  187. package/lib/QueueClient.d.ts +64 -0
  188. package/lib/QueueClient.d.ts.map +1 -0
  189. package/lib/QueueClient.js +73 -0
  190. package/lib/QueueClient.js.map +1 -0
  191. package/lib/ReadApp.d.ts +85 -0
  192. package/lib/ReadApp.d.ts.map +1 -0
  193. package/lib/ReadApp.js +3 -0
  194. package/lib/ReadApp.js.map +1 -0
  195. package/lib/ReadAppHttp.d.ts +26 -0
  196. package/lib/ReadAppHttp.d.ts.map +1 -0
  197. package/lib/ReadAppHttp.js +106 -0
  198. package/lib/ReadAppHttp.js.map +1 -0
  199. package/lib/ReadDict.d.ts +39 -0
  200. package/lib/ReadDict.d.ts.map +1 -0
  201. package/lib/ReadDict.js +3 -0
  202. package/lib/ReadDict.js.map +1 -0
  203. package/lib/ReadDictHttp.d.ts +12 -0
  204. package/lib/ReadDictHttp.d.ts.map +1 -0
  205. package/lib/ReadDictHttp.js +23 -0
  206. package/lib/ReadDictHttp.js.map +1 -0
  207. package/lib/ReadFunction.d.ts +73 -0
  208. package/lib/ReadFunction.d.ts.map +1 -0
  209. package/lib/ReadFunction.js +3 -0
  210. package/lib/ReadFunction.js.map +1 -0
  211. package/lib/ReadFunctionHttp.d.ts +25 -0
  212. package/lib/ReadFunctionHttp.d.ts.map +1 -0
  213. package/lib/ReadFunctionHttp.js +63 -0
  214. package/lib/ReadFunctionHttp.js.map +1 -0
  215. package/lib/ReadQueue.d.ts +39 -0
  216. package/lib/ReadQueue.d.ts.map +1 -0
  217. package/lib/ReadQueue.js +3 -0
  218. package/lib/ReadQueue.js.map +1 -0
  219. package/lib/ReadQueueHttp.d.ts +12 -0
  220. package/lib/ReadQueueHttp.d.ts.map +1 -0
  221. package/lib/ReadQueueHttp.js +23 -0
  222. package/lib/ReadQueueHttp.js.map +1 -0
  223. package/lib/ReadServer.d.ts +72 -0
  224. package/lib/ReadServer.d.ts.map +1 -0
  225. package/lib/ReadServer.js +3 -0
  226. package/lib/ReadServer.js.map +1 -0
  227. package/lib/ReadServerHttp.d.ts +25 -0
  228. package/lib/ReadServerHttp.d.ts.map +1 -0
  229. package/lib/ReadServerHttp.js +70 -0
  230. package/lib/ReadServerHttp.js.map +1 -0
  231. package/lib/ReadVolume.d.ts +40 -0
  232. package/lib/ReadVolume.d.ts.map +1 -0
  233. package/lib/ReadVolume.js +3 -0
  234. package/lib/ReadVolume.js.map +1 -0
  235. package/lib/ReadVolumeHttp.d.ts +12 -0
  236. package/lib/ReadVolumeHttp.d.ts.map +1 -0
  237. package/lib/ReadVolumeHttp.js +24 -0
  238. package/lib/ReadVolumeHttp.js.map +1 -0
  239. package/lib/ReadWriteDict.d.ts +39 -0
  240. package/lib/ReadWriteDict.d.ts.map +1 -0
  241. package/lib/ReadWriteDict.js +3 -0
  242. package/lib/ReadWriteDict.js.map +1 -0
  243. package/lib/ReadWriteDictHttp.d.ts +12 -0
  244. package/lib/ReadWriteDictHttp.d.ts.map +1 -0
  245. package/lib/ReadWriteDictHttp.js +23 -0
  246. package/lib/ReadWriteDictHttp.js.map +1 -0
  247. package/lib/ReadWriteQueue.d.ts +40 -0
  248. package/lib/ReadWriteQueue.d.ts.map +1 -0
  249. package/lib/ReadWriteQueue.js +3 -0
  250. package/lib/ReadWriteQueue.js.map +1 -0
  251. package/lib/ReadWriteQueueHttp.d.ts +12 -0
  252. package/lib/ReadWriteQueueHttp.d.ts.map +1 -0
  253. package/lib/ReadWriteQueueHttp.js +23 -0
  254. package/lib/ReadWriteQueueHttp.js.map +1 -0
  255. package/lib/ReadWriteVolume.d.ts +40 -0
  256. package/lib/ReadWriteVolume.d.ts.map +1 -0
  257. package/lib/ReadWriteVolume.js +3 -0
  258. package/lib/ReadWriteVolume.js.map +1 -0
  259. package/lib/ReadWriteVolumeHttp.d.ts +12 -0
  260. package/lib/ReadWriteVolumeHttp.d.ts.map +1 -0
  261. package/lib/ReadWriteVolumeHttp.js +24 -0
  262. package/lib/ReadWriteVolumeHttp.js.map +1 -0
  263. package/lib/RuntimeOwner.d.ts +39 -0
  264. package/lib/RuntimeOwner.d.ts.map +1 -0
  265. package/lib/RuntimeOwner.js +85 -0
  266. package/lib/RuntimeOwner.js.map +1 -0
  267. package/lib/Sandbox.d.ts +215 -0
  268. package/lib/Sandbox.d.ts.map +1 -0
  269. package/lib/Sandbox.js +278 -0
  270. package/lib/Sandbox.js.map +1 -0
  271. package/lib/SandboxClient.d.ts +748 -0
  272. package/lib/SandboxClient.d.ts.map +1 -0
  273. package/lib/SandboxClient.js +710 -0
  274. package/lib/SandboxClient.js.map +1 -0
  275. package/lib/ScaleFunction.d.ts +54 -0
  276. package/lib/ScaleFunction.d.ts.map +1 -0
  277. package/lib/ScaleFunction.js +3 -0
  278. package/lib/ScaleFunction.js.map +1 -0
  279. package/lib/ScaleFunctionHttp.d.ts +21 -0
  280. package/lib/ScaleFunctionHttp.d.ts.map +1 -0
  281. package/lib/ScaleFunctionHttp.js +39 -0
  282. package/lib/ScaleFunctionHttp.js.map +1 -0
  283. package/lib/Secret.d.ts +170 -0
  284. package/lib/Secret.d.ts.map +1 -0
  285. package/lib/Secret.js +196 -0
  286. package/lib/Secret.js.map +1 -0
  287. package/lib/Server.d.ts +155 -0
  288. package/lib/Server.d.ts.map +1 -0
  289. package/lib/Server.js +184 -0
  290. package/lib/Server.js.map +1 -0
  291. package/lib/ServiceUser.d.ts +139 -0
  292. package/lib/ServiceUser.d.ts.map +1 -0
  293. package/lib/ServiceUser.js +210 -0
  294. package/lib/ServiceUser.js.map +1 -0
  295. package/lib/Volume.d.ts +170 -0
  296. package/lib/Volume.d.ts.map +1 -0
  297. package/lib/Volume.js +220 -0
  298. package/lib/Volume.js.map +1 -0
  299. package/lib/VolumeClient.d.ts +53 -0
  300. package/lib/VolumeClient.d.ts.map +1 -0
  301. package/lib/VolumeClient.js +22 -0
  302. package/lib/VolumeClient.js.map +1 -0
  303. package/lib/VolumeFiles.d.ts +94 -0
  304. package/lib/VolumeFiles.d.ts.map +1 -0
  305. package/lib/VolumeFiles.js +139 -0
  306. package/lib/VolumeFiles.js.map +1 -0
  307. package/lib/WebhookToken.d.ts +102 -0
  308. package/lib/WebhookToken.d.ts.map +1 -0
  309. package/lib/WebhookToken.js +176 -0
  310. package/lib/WebhookToken.js.map +1 -0
  311. package/lib/WorkspaceSettings.d.ts +70 -0
  312. package/lib/WorkspaceSettings.d.ts.map +1 -0
  313. package/lib/WorkspaceSettings.js +105 -0
  314. package/lib/WorkspaceSettings.js.map +1 -0
  315. package/lib/WriteApp.d.ts +38 -0
  316. package/lib/WriteApp.d.ts.map +1 -0
  317. package/lib/WriteApp.js +3 -0
  318. package/lib/WriteApp.js.map +1 -0
  319. package/lib/WriteAppHttp.d.ts +23 -0
  320. package/lib/WriteAppHttp.d.ts.map +1 -0
  321. package/lib/WriteAppHttp.js +36 -0
  322. package/lib/WriteAppHttp.js.map +1 -0
  323. package/lib/WriteDict.d.ts +39 -0
  324. package/lib/WriteDict.d.ts.map +1 -0
  325. package/lib/WriteDict.js +3 -0
  326. package/lib/WriteDict.js.map +1 -0
  327. package/lib/WriteDictHttp.d.ts +12 -0
  328. package/lib/WriteDictHttp.d.ts.map +1 -0
  329. package/lib/WriteDictHttp.js +23 -0
  330. package/lib/WriteDictHttp.js.map +1 -0
  331. package/lib/WriteQueue.d.ts +39 -0
  332. package/lib/WriteQueue.d.ts.map +1 -0
  333. package/lib/WriteQueue.js +3 -0
  334. package/lib/WriteQueue.js.map +1 -0
  335. package/lib/WriteQueueHttp.d.ts +12 -0
  336. package/lib/WriteQueueHttp.d.ts.map +1 -0
  337. package/lib/WriteQueueHttp.js +23 -0
  338. package/lib/WriteQueueHttp.js.map +1 -0
  339. package/lib/WriteSecret.d.ts +53 -0
  340. package/lib/WriteSecret.d.ts.map +1 -0
  341. package/lib/WriteSecret.js +3 -0
  342. package/lib/WriteSecret.js.map +1 -0
  343. package/lib/WriteSecretHttp.d.ts +23 -0
  344. package/lib/WriteSecretHttp.d.ts.map +1 -0
  345. package/lib/WriteSecretHttp.js +44 -0
  346. package/lib/WriteSecretHttp.js.map +1 -0
  347. package/lib/WriteVolume.d.ts +39 -0
  348. package/lib/WriteVolume.d.ts.map +1 -0
  349. package/lib/WriteVolume.js +3 -0
  350. package/lib/WriteVolume.js.map +1 -0
  351. package/lib/WriteVolumeHttp.d.ts +12 -0
  352. package/lib/WriteVolumeHttp.d.ts.map +1 -0
  353. package/lib/WriteVolumeHttp.js +24 -0
  354. package/lib/WriteVolumeHttp.js.map +1 -0
  355. package/lib/index.d.ts +80 -0
  356. package/lib/index.d.ts.map +1 -0
  357. package/lib/index.js +78 -0
  358. package/lib/index.js.map +1 -0
  359. package/lib/internal/Declared.d.ts +29 -0
  360. package/lib/internal/Declared.d.ts.map +1 -0
  361. package/lib/internal/Declared.js +31 -0
  362. package/lib/internal/Declared.js.map +1 -0
  363. package/lib/internal/Deploy.d.ts +110 -0
  364. package/lib/internal/Deploy.d.ts.map +1 -0
  365. package/lib/internal/Deploy.js +230 -0
  366. package/lib/internal/Deploy.js.map +1 -0
  367. package/lib/internal/Entrypoint.d.ts +52 -0
  368. package/lib/internal/Entrypoint.d.ts.map +1 -0
  369. package/lib/internal/Entrypoint.js +193 -0
  370. package/lib/internal/Entrypoint.js.map +1 -0
  371. package/lib/internal/Rpc.d.ts +17 -0
  372. package/lib/internal/Rpc.d.ts.map +1 -0
  373. package/lib/internal/Rpc.js +49 -0
  374. package/lib/internal/Rpc.js.map +1 -0
  375. package/lib/internal/RpcAuth.d.ts +41 -0
  376. package/lib/internal/RpcAuth.d.ts.map +1 -0
  377. package/lib/internal/RpcAuth.js +100 -0
  378. package/lib/internal/RpcAuth.js.map +1 -0
  379. package/lib/internal/Runtime.d.ts +127 -0
  380. package/lib/internal/Runtime.d.ts.map +1 -0
  381. package/lib/internal/Runtime.js +246 -0
  382. package/lib/internal/Runtime.js.map +1 -0
  383. package/lib/internal/Tags.d.ts +19 -0
  384. package/lib/internal/Tags.d.ts.map +1 -0
  385. package/lib/internal/Tags.js +33 -0
  386. package/lib/internal/Tags.js.map +1 -0
  387. package/lib/internal/TcpConnect.d.ts +12 -0
  388. package/lib/internal/TcpConnect.d.ts.map +1 -0
  389. package/lib/internal/TcpConnect.js +46 -0
  390. package/lib/internal/TcpConnect.js.map +1 -0
  391. package/lib/internal/Transport.d.ts +13 -0
  392. package/lib/internal/Transport.d.ts.map +1 -0
  393. package/lib/internal/Transport.js +16 -0
  394. package/lib/internal/Transport.js.map +1 -0
  395. package/lib/internal.d.ts +71 -0
  396. package/lib/internal.d.ts.map +1 -0
  397. package/lib/internal.js +102 -0
  398. package/lib/internal.js.map +1 -0
  399. package/package.json +59 -3
  400. package/src/App.ts +262 -0
  401. package/src/AuthProvider.ts +295 -0
  402. package/src/BindingHttp.ts +112 -0
  403. package/src/Blob.ts +71 -0
  404. package/src/Bootstrap.ts +52 -0
  405. package/src/Cbor.ts +158 -0
  406. package/src/ConnectSandbox.ts +99 -0
  407. package/src/Container.ts +121 -0
  408. package/src/Contributions.ts +9 -0
  409. package/src/CreateDict.ts +95 -0
  410. package/src/CreateDictHttp.ts +91 -0
  411. package/src/CreateImage.ts +105 -0
  412. package/src/CreateImageHttp.ts +117 -0
  413. package/src/CreateQueue.ts +95 -0
  414. package/src/CreateQueueHttp.ts +91 -0
  415. package/src/CreateSandbox.ts +228 -0
  416. package/src/CreateSandboxHttp.ts +160 -0
  417. package/src/CreateSecret.ts +109 -0
  418. package/src/CreateSecretHttp.ts +83 -0
  419. package/src/CreateVolume.ts +96 -0
  420. package/src/CreateVolumeHttp.ts +93 -0
  421. package/src/Credentials.ts +74 -0
  422. package/src/Dict.ts +245 -0
  423. package/src/DictClient.ts +166 -0
  424. package/src/Domain.ts +147 -0
  425. package/src/Endpoint.ts +406 -0
  426. package/src/Environment.ts +373 -0
  427. package/src/FetchServer.ts +126 -0
  428. package/src/FetchServerHttp.ts +110 -0
  429. package/src/Function.ts +397 -0
  430. package/src/FunctionAutoscaler.ts +210 -0
  431. package/src/FunctionTarget.ts +181 -0
  432. package/src/Image.ts +281 -0
  433. package/src/ImageBuild.ts +228 -0
  434. package/src/ImagePlan.ts +78 -0
  435. package/src/InferEnv.ts +27 -0
  436. package/src/InvokeFunction.ts +211 -0
  437. package/src/InvokeFunctionHttp.ts +316 -0
  438. package/src/ModalBindings.ts +310 -0
  439. package/src/MountBucket.ts +137 -0
  440. package/src/MountVolume.ts +113 -0
  441. package/src/Mounts.ts +230 -0
  442. package/src/PersistentSandbox.ts +142 -0
  443. package/src/Providers.ts +100 -0
  444. package/src/Proxy.ts +191 -0
  445. package/src/Queue.ts +258 -0
  446. package/src/QueueClient.ts +149 -0
  447. package/src/ReadApp.ts +101 -0
  448. package/src/ReadAppHttp.ts +142 -0
  449. package/src/ReadDict.ts +44 -0
  450. package/src/ReadDictHttp.ts +27 -0
  451. package/src/ReadFunction.ts +107 -0
  452. package/src/ReadFunctionHttp.ts +91 -0
  453. package/src/ReadQueue.ts +44 -0
  454. package/src/ReadQueueHttp.ts +27 -0
  455. package/src/ReadServer.ts +98 -0
  456. package/src/ReadServerHttp.ts +101 -0
  457. package/src/ReadVolume.ts +45 -0
  458. package/src/ReadVolumeHttp.ts +28 -0
  459. package/src/ReadWriteDict.ts +44 -0
  460. package/src/ReadWriteDictHttp.ts +27 -0
  461. package/src/ReadWriteQueue.ts +45 -0
  462. package/src/ReadWriteQueueHttp.ts +27 -0
  463. package/src/ReadWriteVolume.ts +45 -0
  464. package/src/ReadWriteVolumeHttp.ts +28 -0
  465. package/src/RuntimeOwner.ts +111 -0
  466. package/src/Sandbox.ts +402 -0
  467. package/src/SandboxClient.ts +1404 -0
  468. package/src/ScaleFunction.ts +70 -0
  469. package/src/ScaleFunctionHttp.ts +54 -0
  470. package/src/Secret.ts +296 -0
  471. package/src/Server.ts +285 -0
  472. package/src/ServiceUser.ts +295 -0
  473. package/src/Volume.ts +320 -0
  474. package/src/VolumeClient.ts +108 -0
  475. package/src/VolumeFiles.ts +194 -0
  476. package/src/WebhookToken.ts +230 -0
  477. package/src/WorkspaceSettings.ts +160 -0
  478. package/src/WriteApp.ts +43 -0
  479. package/src/WriteAppHttp.ts +51 -0
  480. package/src/WriteDict.ts +44 -0
  481. package/src/WriteDictHttp.ts +27 -0
  482. package/src/WriteQueue.ts +44 -0
  483. package/src/WriteQueueHttp.ts +27 -0
  484. package/src/WriteSecret.ts +60 -0
  485. package/src/WriteSecretHttp.ts +56 -0
  486. package/src/WriteVolume.ts +44 -0
  487. package/src/WriteVolumeHttp.ts +28 -0
  488. package/src/globals.d.ts +2 -0
  489. package/src/index.ts +85 -0
  490. package/src/internal/Declared.ts +80 -0
  491. package/src/internal/Deploy.ts +309 -0
  492. package/src/internal/Entrypoint.ts +198 -0
  493. package/src/internal/Rpc.ts +59 -0
  494. package/src/internal/RpcAuth.ts +132 -0
  495. package/src/internal/Runtime.ts +386 -0
  496. package/src/internal/Tags.ts +44 -0
  497. package/src/internal/TcpConnect.ts +78 -0
  498. package/src/internal/Transport.ts +25 -0
  499. package/src/internal.ts +147 -0
@@ -0,0 +1,309 @@
1
+ import * as BuildPlan from "@alchemy.run/docker/BuildPlan";
2
+ import * as app from "@distilled.cloud/modal/app";
3
+ import * as mount from "@distilled.cloud/modal/mount";
4
+ import * as workspace from "@distilled.cloud/modal/workspace";
5
+ import type * as Bundle from "alchemy/Bundle/Bundle";
6
+ import { type MountCapability, mountInstallLines } from "alchemy/Mount";
7
+ import { sha256, sha256Object } from "alchemy/Util/sha256";
8
+ import * as Data from "effect/Data";
9
+ import * as Effect from "effect/Effect";
10
+ import * as Semaphore from "effect/Semaphore";
11
+ import { buildImage, ImageBuildOps, type ImageBuildSpec } from "../ImageBuild.ts";
12
+ import { checkCommands, lowerPlan, MODAL_CAPABILITIES } from "../ImagePlan.ts";
13
+ import { checkModalMounts } from "../Mounts.ts";
14
+ import { ENTRYPOINT_PATH, ENTRYPOINT_SOURCE } from "./Entrypoint.ts";
15
+
16
+ /*
17
+ * Deploy-time plumbing shared by `Modal.Function` and `Modal.Server`: the
18
+ * Python layer and mounts every Modal Function container needs, and
19
+ * publishing a function into its App.
20
+ */
21
+
22
+ /** Modal returned a response without a field the deploy needs. */
23
+ export class ModalDeployUnexpectedResponse extends Data.TaggedError(
24
+ "Modal.DeployUnexpectedResponse",
25
+ )<{
26
+ readonly message: string;
27
+ }> {}
28
+
29
+ /**
30
+ * The Modal Python client whose container entrypoint runs the function.
31
+ * Modal publishes it as the global mount `modal-client-mount-<version>`.
32
+ */
33
+ export const MODAL_CLIENT_VERSION = "1.6.3";
34
+
35
+ /** Standalone Python builds Modal publishes as global mounts, by image builder version. */
36
+ const PYTHON_STANDALONE: Record<
37
+ "2026.10" | "2025.06",
38
+ Record<string, readonly [string, string]>
39
+ > = {
40
+ "2026.10": {
41
+ "3.11": ["20261021", "3.11.17"],
42
+ "3.12": ["20261021", "3.12.15"],
43
+ "3.13": ["20261021", "3.13.17"],
44
+ "3.14": ["20261021", "3.14.9"],
45
+ },
46
+ "2025.06": {
47
+ "3.11": ["20230826", "3.11.5"],
48
+ "3.12": ["20240107", "3.12.1"],
49
+ "3.13": ["20241008", "3.13.0"],
50
+ "3.14": ["20251205", "3.14.2"],
51
+ },
52
+ };
53
+
54
+ /** The Python version added to a base image without one. */
55
+ export const DEFAULT_PYTHON = "3.12";
56
+
57
+ /** Port the program listens on when the resource doesn't set one. */
58
+ export const DEFAULT_PORT = 8000;
59
+
60
+ const lookupMount = (deploymentName: string) =>
61
+ Effect.gen(function* () {
62
+ const { mountId } = yield* mount.mountGetOrCreate({
63
+ deploymentName,
64
+ namespace: "DEPLOYMENT_NAMESPACE_GLOBAL",
65
+ objectCreationType: "OBJECT_CREATION_TYPE_UNSPECIFIED",
66
+ });
67
+ if (!mountId) {
68
+ return yield* new ModalDeployUnexpectedResponse({
69
+ message: `Modal returned no id for mount ${deploymentName}`,
70
+ });
71
+ }
72
+ return mountId;
73
+ });
74
+
75
+ /** The mount holding Modal's Python client, which runs the container entrypoint. */
76
+ export const clientMount = /*@__PURE__*/ lookupMount(`modal-client-mount-${MODAL_CLIENT_VERSION}`);
77
+
78
+ /**
79
+ * The image builder version builds use: the resource's pin, else the
80
+ * workspace's setting.
81
+ */
82
+ const builderVersionOf = Effect.fn(function* (pinned: string | undefined) {
83
+ if (pinned !== undefined) return pinned;
84
+ const settings = yield* workspace.workspaceSettings({});
85
+ return settings.imageBuilderVersion ?? "2025.06";
86
+ });
87
+
88
+ /**
89
+ * Upload {@link ENTRYPOINT_SOURCE} as a mount owned by the app. Modal stores
90
+ * files by content hash, so redeploys reuse it.
91
+ */
92
+ export const entrypointMount = Effect.fn(function* (appId: string) {
93
+ const bytes = new TextEncoder().encode(ENTRYPOINT_SOURCE);
94
+ const hash = yield* sha256(bytes);
95
+ const data = yield* Effect.sync(() => Buffer.from(bytes).toString("base64"));
96
+ for (let attempt = 0; attempt < 10; attempt++) {
97
+ const { exists } = yield* mount.mountPutFile({ sha256Hex: hash, data });
98
+ if (exists) break;
99
+ }
100
+ const { mountId } = yield* mount.mountGetOrCreate({
101
+ objectCreationType: "OBJECT_CREATION_TYPE_ANONYMOUS_OWNED_BY_APP",
102
+ appId,
103
+ files: [
104
+ { filename: ENTRYPOINT_PATH, sha256Hex: hash, size: String(bytes.length), mode: 0o100644 },
105
+ ],
106
+ });
107
+ if (!mountId) {
108
+ return yield* new ModalDeployUnexpectedResponse({
109
+ message: "Modal returned no id for the entrypoint mount",
110
+ });
111
+ }
112
+ return mountId;
113
+ });
114
+
115
+ /** What a runtime's image is built from. */
116
+ export interface RuntimeImageInput {
117
+ readonly main: string;
118
+ readonly image?: string;
119
+ readonly commands?: ReadonlyArray<string>;
120
+ readonly handler?: string;
121
+ readonly build?: Bundle.BundleConfig;
122
+ readonly isExternal?: boolean;
123
+ readonly python?: string | false;
124
+ readonly builderVersion?: string;
125
+ readonly gpu?: string;
126
+ }
127
+
128
+ /**
129
+ * Plan a runtime's image with the shared BuildPlan: the bundled program on
130
+ * its base image, without the image's `ENTRYPOINT`/`CMD` (Modal starts the
131
+ * container with its own Python entrypoint, which then runs the program).
132
+ */
133
+ export const planRuntimeImage = Effect.fn(function* (
134
+ target: string,
135
+ input: RuntimeImageInput,
136
+ port: number,
137
+ bootstrap: ((importPath: string) => string) | undefined,
138
+ capabilities: ReadonlyArray<MountCapability> = [],
139
+ ) {
140
+ yield* checkModalMounts(target, capabilities);
141
+ const plan = yield* BuildPlan.plan(
142
+ { main: input.main, image: input.image, handler: input.handler, build: input.build },
143
+ {
144
+ capabilities: { ...MODAL_CAPABILITIES, target },
145
+ port,
146
+ bootstrap,
147
+ install: mountInstallLines(capabilities),
148
+ },
149
+ );
150
+ const extra = yield* checkCommands(input.commands ?? []);
151
+ const lowered = yield* lowerPlan(plan, extra);
152
+ const command = [...(BuildPlan.startCommand(plan) ?? [])];
153
+ // Lines after `FROM`, minus the start instruction, run on top of the Python layer.
154
+ const commands = lowered.commands
155
+ .slice(1)
156
+ .filter((line) => !/^\s*(ENTRYPOINT|CMD)\b/i.test(line));
157
+ const python = input.python === undefined ? DEFAULT_PYTHON : input.python;
158
+ const hash = yield* sha256Object({
159
+ plan: plan.hash,
160
+ extra,
161
+ python,
162
+ builder: input.builderVersion,
163
+ gpu: input.gpu,
164
+ });
165
+ return {
166
+ plan,
167
+ command,
168
+ hash,
169
+ warnings: plan.warnings,
170
+ spec: { ...lowered, commands: [...commands, "ENTRYPOINT []"] } satisfies ImageBuildSpec,
171
+ python,
172
+ };
173
+ });
174
+
175
+ /**
176
+ * Build a runtime's image: the base, a layer adding standalone Python
177
+ * (unless `python: false`), then the program.
178
+ */
179
+ export const buildRuntimeImage = Effect.fn(function* (
180
+ appId: string,
181
+ planned: {
182
+ readonly plan: BuildPlan.BuildPlan;
183
+ readonly spec: ImageBuildSpec;
184
+ readonly python: string | false;
185
+ },
186
+ options: {
187
+ readonly builderVersion?: string;
188
+ readonly gpu?: string;
189
+ readonly note?: (message: string) => Effect.Effect<void>;
190
+ },
191
+ ) {
192
+ const builderVersion = yield* builderVersionOf(options.builderVersion);
193
+ const buildOps = yield* ImageBuildOps;
194
+ let baseImageId: string | undefined;
195
+ if (planned.python !== false) {
196
+ const table =
197
+ builderVersion >= "2026.10" || builderVersion === "PREVIEW"
198
+ ? PYTHON_STANDALONE["2026.10"]
199
+ : PYTHON_STANDALONE["2025.06"];
200
+ const release = table[planned.python];
201
+ if (release === undefined) {
202
+ return yield* new ModalDeployUnexpectedResponse({
203
+ message: `Python ${planned.python} isn't available for image builder ${builderVersion}; use one of ${Object.keys(table).join(", ")}`,
204
+ });
205
+ }
206
+ const contextMountId = yield* lookupMount(
207
+ `python-build-standalone.${release[0]}.${release[1]}-gnu`,
208
+ );
209
+ baseImageId = yield* buildImage(
210
+ buildOps,
211
+ appId,
212
+ {
213
+ dockerTag: planned.plan.base,
214
+ commands: [
215
+ "COPY /python/. /usr/local",
216
+ "ENV TERMINFO_DIRS=/etc/terminfo:/lib/terminfo:/usr/share/terminfo:/usr/lib/terminfo",
217
+ ],
218
+ contextMountId,
219
+ registry: undefined,
220
+ builderVersion,
221
+ },
222
+ { note: options.note },
223
+ );
224
+ }
225
+ return yield* buildImage(
226
+ buildOps,
227
+ appId,
228
+ {
229
+ ...planned.spec,
230
+ ...(baseImageId !== undefined ? { baseImageId } : { dockerTag: planned.plan.base }),
231
+ gpu: options.gpu,
232
+ builderVersion,
233
+ },
234
+ { note: options.note },
235
+ );
236
+ });
237
+
238
+ /** Serializes publishes per App: each publish replaces the App's whole function map. */
239
+ const appLocks = /*@__PURE__*/ new Map<string, Semaphore.Semaphore>();
240
+ const lockFor = (appId: string) => {
241
+ let lock = appLocks.get(appId);
242
+ if (lock === undefined) {
243
+ lock = Semaphore.makeUnsafe(1);
244
+ appLocks.set(appId, lock);
245
+ }
246
+ return lock;
247
+ };
248
+
249
+ /**
250
+ * Publish the App with `tag` pointing at `created` (or removed, when
251
+ * `created` is `undefined`), keeping every other function and class the
252
+ * App serves. Modal deploys an App as a whole, so a publish carries the full
253
+ * map read back from the App's current layout.
254
+ *
255
+ * A class function (one with a `classId`) is published as Modal's clients
256
+ * publish `@app.cls`: its service function under `<tag>.*` and its class
257
+ * under `<tag>`, where its containers look the class up.
258
+ */
259
+ /** The service function name of the class `tag`, as Modal's clients name it. */
260
+ export const classTag = (tag: string) => `${tag}.*`;
261
+
262
+ export const publishFunction = (
263
+ target: { readonly appId: string; readonly appName: string | undefined },
264
+ tag: string,
265
+ created:
266
+ | {
267
+ readonly functionId: string;
268
+ readonly definitionId: string | undefined;
269
+ readonly classId?: string | undefined;
270
+ }
271
+ | undefined,
272
+ ) =>
273
+ Semaphore.withPermits(
274
+ lockFor(target.appId),
275
+ 1,
276
+ )(
277
+ Effect.gen(function* () {
278
+ const { appLayout } = yield* app.appGetLayout({ appId: target.appId });
279
+ const functionIds: Record<string, string> = {};
280
+ for (const [key, value] of Object.entries(appLayout?.functionIds ?? {})) {
281
+ if (value !== undefined && key !== tag && key !== classTag(tag)) functionIds[key] = value;
282
+ }
283
+ if (created !== undefined) {
284
+ functionIds[created.classId ? classTag(tag) : tag] = created.functionId;
285
+ }
286
+ const classIds: Record<string, string> = {};
287
+ for (const [key, value] of Object.entries(appLayout?.classIds ?? {})) {
288
+ if (value !== undefined && key !== tag) classIds[key] = value;
289
+ }
290
+ if (created?.classId) classIds[tag] = created.classId;
291
+ const published = new Set(Object.values(functionIds));
292
+ const definitionIds: Record<string, string> = {};
293
+ for (const object of appLayout?.objects ?? []) {
294
+ const definitionId = object.functionHandleMetadata?.definitionId;
295
+ if (object.objectId && definitionId && published.has(object.objectId)) {
296
+ definitionIds[object.objectId] = definitionId;
297
+ }
298
+ }
299
+ if (created?.definitionId) definitionIds[created.functionId] = created.definitionId;
300
+ return yield* app.publishApp({
301
+ appId: target.appId,
302
+ name: target.appName,
303
+ appState: "APP_STATE_DEPLOYED",
304
+ functionIds,
305
+ classIds,
306
+ definitionIds,
307
+ });
308
+ }),
309
+ );
@@ -0,0 +1,198 @@
1
+ /**
2
+ * The Python module a `Modal.Function` or `Modal.Server` container imports.
3
+ *
4
+ * Modal boots every Function container with its Python entrypoint, which
5
+ * imports the function's module and looks the function up by name. This
6
+ * module is that function: it starts the bundled program (`ALCHEMY_COMMAND`,
7
+ * from the function's env secret) once per container and hands it work as
8
+ * HTTP on `PORT`:
9
+ *
10
+ * - `Rpc` (a class function) has one method per RPC method of the program
11
+ * (`ALCHEMY_MODAL_RPC_METHODS`, from the function's env secret); a call is
12
+ * sent to the program's `POST /__rpc__/<method>` with the positional
13
+ * arguments as a JSON array and the caller's key (the
14
+ * `__alchemy_rpc_key__` keyword argument) as `x-alchemy-rpc-key`, and
15
+ * returns the JSON result (a value, or an
16
+ * error envelope the caller fails with). A streamed result is read to its
17
+ * end and returned as its list of values;
18
+ * - `call` and `schedule` forward each input as `POST /__modal/call` or
19
+ * `POST /__modal/schedule` with a JSON body `{ "args": [...], "kwargs":
20
+ * {...} }`, and return the JSON response (an async program's calls, and
21
+ * schedule ticks);
22
+ * - `web` (a `WEBHOOK_TYPE_WEB_SERVER` function) starts the program, then
23
+ * Modal proxies its `*.modal.run` URL to `PORT`;
24
+ * - `Server` (`is_server`) starts the program in `@modal.enter`, then Modal
25
+ * routes the Server's URL to `PORT`.
26
+ *
27
+ * It's uploaded as a Mount (`/root/alchemy_modal.py`); the module name and
28
+ * its function names are part of every function definition, so changing
29
+ * them redeploys every function.
30
+ */
31
+ export const ENTRYPOINT_MODULE = "alchemy_modal";
32
+
33
+ export const ENTRYPOINT_PATH = `/root/${ENTRYPOINT_MODULE}.py`;
34
+
35
+ /** Function implementations in {@link ENTRYPOINT_SOURCE}. */
36
+ export const ENTRYPOINT = {
37
+ call: "call",
38
+ schedule: "schedule",
39
+ web: "web",
40
+ server: "Server",
41
+ rpc: "Rpc",
42
+ } as const;
43
+
44
+ /** Env var listing a program's RPC methods (a JSON array), set at plan. */
45
+ export const RPC_METHODS_ENV = "ALCHEMY_MODAL_RPC_METHODS";
46
+
47
+ /**
48
+ * Env var that turns on the program's `/__rpc__/*` routes. Set only where
49
+ * Modal authenticates every request: a class function's calls and a Server
50
+ * behind proxy auth. A public web endpoint never serves RPC.
51
+ */
52
+ export const RPC_SERVE_ENV = "ALCHEMY_MODAL_SERVE_RPC";
53
+
54
+ /** A streamed RPC result, as {@link ENTRYPOINT_SOURCE} returns it: its values in order. */
55
+ export const RPC_STREAM_KEY = "~alchemy/modal/rpc-stream";
56
+
57
+ export const ENTRYPOINT_SOURCE = `"""Alchemy's entrypoint for a JavaScript program on Modal (generated, do not edit)."""
58
+
59
+ import base64
60
+ import http.client
61
+ import json
62
+ import os
63
+ import socket
64
+ import subprocess
65
+ import threading
66
+ import time
67
+
68
+ import modal
69
+
70
+ _PORT = int(os.environ.get("PORT", "8000"))
71
+ _lock = threading.Lock()
72
+ _process = None
73
+
74
+
75
+ def _start():
76
+ global _process
77
+ with _lock:
78
+ if _process is None or _process.poll() is not None:
79
+ _process = subprocess.Popen(json.loads(os.environ["ALCHEMY_COMMAND"]))
80
+ return _process
81
+
82
+
83
+ def _wait_until_listening(process, timeout=600.0):
84
+ deadline = time.monotonic() + timeout
85
+ while time.monotonic() < deadline:
86
+ if process.poll() is not None:
87
+ raise RuntimeError(f"The program exited with code {process.returncode} before listening on port {_PORT}")
88
+ try:
89
+ socket.create_connection(("127.0.0.1", _PORT), timeout=1).close()
90
+ return
91
+ except OSError:
92
+ time.sleep(0.05)
93
+ raise TimeoutError(f"The program didn't listen on port {_PORT} within {timeout:.0f} seconds")
94
+
95
+
96
+ def _json_default(value):
97
+ if isinstance(value, (bytes, bytearray, memoryview)):
98
+ return base64.b64encode(bytes(value)).decode("ascii")
99
+ raise TypeError(f"{type(value).__name__} isn't JSON-serializable")
100
+
101
+
102
+ def _forward(path, args, kwargs):
103
+ _wait_until_listening(_start())
104
+ body = json.dumps({"args": list(args), "kwargs": kwargs}, default=_json_default).encode()
105
+ headers = {"content-type": "application/json"}
106
+ try:
107
+ call_id = modal.current_function_call_id()
108
+ if call_id:
109
+ headers["x-modal-function-call-id"] = call_id
110
+ except Exception:
111
+ pass
112
+ connection = http.client.HTTPConnection("127.0.0.1", _PORT)
113
+ try:
114
+ connection.request("POST", path, body=body, headers=headers)
115
+ response = connection.getresponse()
116
+ data = response.read()
117
+ finally:
118
+ connection.close()
119
+ if response.status >= 400:
120
+ raise RuntimeError(f"{path} returned {response.status}: {data.decode(errors='replace')}")
121
+ if not data:
122
+ return None
123
+ if response.getheader("content-type", "").startswith("application/json"):
124
+ return json.loads(data)
125
+ return data.decode(errors="replace")
126
+
127
+
128
+ def _post(path, body, headers=None):
129
+ _wait_until_listening(_start())
130
+ connection = http.client.HTTPConnection("127.0.0.1", _PORT)
131
+ try:
132
+ connection.request("POST", path, body=body, headers={"content-type": "application/json", **(headers or {})})
133
+ response = connection.getresponse()
134
+ return response, response.read()
135
+ finally:
136
+ connection.close()
137
+
138
+
139
+ def _rpc(name):
140
+ def method(self, *args, **kwargs):
141
+ key = kwargs.pop("__alchemy_rpc_key__", None)
142
+ if kwargs:
143
+ raise TypeError(f"RPC method {name} takes positional arguments only")
144
+ body = json.dumps(list(args), default=_json_default).encode()
145
+ headers = {"x-alchemy-rpc-key": key} if isinstance(key, str) else {}
146
+ response, data = _post("/__rpc__/" + name, body, headers)
147
+ if response.status >= 400:
148
+ raise RuntimeError(f"RPC method {name} returned {response.status}: {data.decode(errors='replace')}")
149
+ if response.getheader("x-alchemy-rpc-stream") is not None:
150
+ lines = [json.loads(line) for line in data.decode().splitlines() if line]
151
+ return {"~alchemy/modal/rpc-stream": lines}
152
+ return json.loads(data) if data else None
153
+
154
+ method.__name__ = name
155
+ return modal.method()(method)
156
+
157
+
158
+ def _rpc_enter(self):
159
+ _wait_until_listening(_start())
160
+
161
+
162
+ Rpc = type(
163
+ "Rpc",
164
+ (),
165
+ {
166
+ "start": modal.enter()(_rpc_enter),
167
+ **{name: _rpc(name) for name in json.loads(os.environ.get("ALCHEMY_MODAL_RPC_METHODS") or "[]")},
168
+ },
169
+ )
170
+
171
+
172
+ def call(*args, **kwargs):
173
+ return _forward("/__modal/call", args, kwargs)
174
+
175
+
176
+ def schedule(*args, **kwargs):
177
+ return _forward("/__modal/schedule", args, kwargs)
178
+
179
+
180
+ def web():
181
+ _start()
182
+
183
+
184
+ class Server:
185
+ @modal.enter()
186
+ def start(self):
187
+ _wait_until_listening(_start())
188
+
189
+ @modal.exit()
190
+ def stop(self):
191
+ process = _process
192
+ if process is not None and process.poll() is None:
193
+ process.terminate()
194
+ try:
195
+ process.wait(timeout=20)
196
+ except subprocess.TimeoutExpired:
197
+ process.kill()
198
+ `;
@@ -0,0 +1,59 @@
1
+ import { safeHttpEffect } from "alchemy/Http";
2
+ import * as Output from "alchemy/Output";
3
+ import { serveRpc } from "alchemy/Rpc";
4
+ import { createHostRuntimeContext, type HostRuntimeContext } from "alchemy/Runtime/Process";
5
+ import * as Effect from "effect/Effect";
6
+ import { RPC_METHODS_ENV, RPC_SERVE_ENV } from "./Entrypoint.ts";
7
+ import { parseCallers, requireRpcCaller, RPC_CALLERS_ENV } from "./RpcAuth.ts";
8
+
9
+ /** The RPC methods of a program's shape: every function besides `fetch` and `run`. */
10
+ export const rpcMethodsOf = (shape: Record<string, unknown> | undefined) =>
11
+ Object.fromEntries(
12
+ Object.entries(shape ?? {}).filter(
13
+ ([key, value]) => key !== "fetch" && key !== "run" && typeof value === "function",
14
+ ),
15
+ );
16
+
17
+ /** The method names a function's env records (see {@link RPC_METHODS_ENV}). */
18
+ export const rpcMethodNamesOf = (env: Record<string, unknown> | undefined): string[] => {
19
+ const value = env?.[RPC_METHODS_ENV];
20
+ if (typeof value !== "string") return [];
21
+ try {
22
+ const names = JSON.parse(value) as unknown;
23
+ return Array.isArray(names) ? names.filter((name) => typeof name === "string") : [];
24
+ } catch {
25
+ return [];
26
+ }
27
+ };
28
+
29
+ /**
30
+ * The runtime context of a `Modal.Function` or `Modal.Server` program. At
31
+ * plan it records the program's RPC methods in its env, so the Function's
32
+ * definition can name them. At run time it serves them on `/__rpc__/*`
33
+ * next to `fetch`, but only where the provider set {@link RPC_SERVE_ENV}
34
+ * (where Modal authenticates every request), and only to callers holding a
35
+ * key a binding issued (see `RpcAuth.ts`).
36
+ */
37
+ export const createModalRuntimeContext =
38
+ (type: string) =>
39
+ (id: string): HostRuntimeContext => {
40
+ const base = createHostRuntimeContext(type)(id);
41
+ const serveBase = base.serve;
42
+ const serve: HostRuntimeContext["serve"] = (handler, options) =>
43
+ Effect.gen(function* () {
44
+ const methods = rpcMethodsOf(options?.shape);
45
+ const names = Object.keys(methods).sort();
46
+ if (!globalThis.__ALCHEMY_RUNTIME__ && names.length > 0) {
47
+ yield* base.set(RPC_METHODS_ENV, Output.literal(names));
48
+ }
49
+ const enabled = (yield* base.get<unknown>(RPC_SERVE_ENV)) !== undefined;
50
+ if (!enabled || names.length === 0) return yield* serveBase(handler, options);
51
+ const fallback = safeHttpEffect(handler);
52
+ const callers = parseCallers(yield* base.get<unknown>(RPC_CALLERS_ENV));
53
+ return yield* serveBase(
54
+ requireRpcCaller(callers, serveRpc(methods, fallback), fallback),
55
+ options,
56
+ );
57
+ }) as Effect.Effect<void, never, any>;
58
+ return Object.assign(base, { serve });
59
+ };
@@ -0,0 +1,132 @@
1
+ import * as Binding from "alchemy/Binding";
2
+ import * as Output from "alchemy/Output";
3
+ import { Random } from "alchemy/Random";
4
+ import { rpcMethodOf } from "alchemy/Rpc";
5
+ import { CurrentRuntimeContext, sanitizeKey } from "alchemy/RuntimeContext";
6
+ import * as Effect from "effect/Effect";
7
+ import { HttpServerRequest } from "effect/http/HttpServerRequest";
8
+ import * as HttpServerResponse from "effect/http/HttpServerResponse";
9
+ import * as Redacted from "effect/Redacted";
10
+
11
+ /*
12
+ * Who may call a program's RPC methods. Every binding that calls them
13
+ * (`Modal.InvokeFunction`, `Modal.FetchServer`) gets its own random key: the
14
+ * caller keeps the key, the target keeps only its SHA-256, and each RPC call
15
+ * carries the key. A key opens one target for one caller, so a target can't
16
+ * replay what it receives to another target. Modal's own access control
17
+ * (proxy auth, environment roles) still applies underneath.
18
+ */
19
+
20
+ /** Env var on the target: the SHA-256 (hex) of each caller's key, as a JSON array. */
21
+ export const RPC_CALLERS_ENV = "ALCHEMY_MODAL_RPC_CALLERS";
22
+
23
+ /** Request header a Server's RPC call carries its key in. */
24
+ export const RPC_KEY_HEADER = "x-alchemy-rpc-key";
25
+
26
+ /** Keyword argument a Function's RPC call carries its key in (the entrypoint forwards it as {@link RPC_KEY_HEADER}). */
27
+ export const RPC_KEY_KWARG = "__alchemy_rpc_key__";
28
+
29
+ /** Env var on the caller holding its key for the target `targetId`. */
30
+ export const rpcKeyEnv = (targetId: string) => sanitizeKey(`ALCHEMY_MODAL_RPC_KEY_${targetId}`);
31
+
32
+ /** What a caller's binding attaches to the target. */
33
+ export interface RpcCallerBinding {
34
+ /** SHA-256 (hex) of the caller's key. */
35
+ readonly rpcCallers?: ReadonlyArray<string>;
36
+ }
37
+
38
+ /** SHA-256 of `text`, as hex. */
39
+ export const sha256Hex = (text: string) =>
40
+ Effect.promise(() => crypto.subtle.digest("SHA-256", new TextEncoder().encode(text))).pipe(
41
+ Effect.map((digest) =>
42
+ Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join(""),
43
+ ),
44
+ );
45
+
46
+ /** The caller key hashes a target's bindings recorded, sorted. */
47
+ export const rpcCallersOf = (
48
+ bindings: ReadonlyArray<{ readonly data: RpcCallerBinding }>,
49
+ ): string[] => [...new Set(bindings.flatMap((binding) => binding.data.rpcCallers ?? []))].sort();
50
+
51
+ type BindTarget = {
52
+ readonly LogicalId: string;
53
+ bind: (...args: unknown[]) => (data: RpcCallerBinding) => Effect.Effect<void>;
54
+ };
55
+
56
+ /**
57
+ * The caller half of an RPC binding. Yield it while the binding's layer is
58
+ * built; call the result with the target in the binding. At deploy it mints
59
+ * the key for this caller and target (`<host><target>RpcKey`), binds it into
60
+ * the host's env, and records its hash on the target. At run time it reads
61
+ * the key back, or `undefined` when the caller has none (a script).
62
+ */
63
+ export const RpcCaller = Effect.gen(function* () {
64
+ // Deploy only: a host bundle never creates the key.
65
+ const Key = globalThis.__ALCHEMY_RUNTIME__ ? undefined : yield* Random;
66
+ return Effect.fn(function* (target: { readonly LogicalId: string }) {
67
+ // The host binding the target, read per call: one binding layer serves
68
+ // every host in the Stack, so a host captured when the layer was built
69
+ // would be the first one to build it.
70
+ const runtime = yield* CurrentRuntimeContext;
71
+ const env = rpcKeyEnv(target.LogicalId);
72
+ if (!globalThis.__ALCHEMY_RUNTIME__ && Key !== undefined && runtime !== undefined) {
73
+ const host = yield* Binding.Host;
74
+ if (host !== undefined) {
75
+ const key = yield* Key(`${host.LogicalId}${target.LogicalId}RpcKey`, {});
76
+ yield* runtime.set(env, key.text);
77
+ yield* (target as unknown as BindTarget).bind`Modal.RpcCaller(${host})`({
78
+ rpcCallers: [
79
+ key.text.pipe(
80
+ Output.mapEffect((text) => text.pipe(Redacted.value, sha256Hex)),
81
+ ) as unknown as string,
82
+ ],
83
+ });
84
+ }
85
+ }
86
+ return Effect.gen(function* () {
87
+ if (runtime === undefined) return undefined;
88
+ const value = yield* runtime.get<string | Redacted.Redacted<string>>(env);
89
+ return value === undefined
90
+ ? undefined
91
+ : Redacted.isRedacted(value)
92
+ ? Redacted.value(value)
93
+ : value;
94
+ });
95
+ });
96
+ });
97
+
98
+ /**
99
+ * Serve `rpc` only to callers whose key's hash is in {@link RPC_CALLERS_ENV};
100
+ * every other request goes to `fallback`. A call without a key, or with one
101
+ * no binding issued, gets `401`.
102
+ */
103
+ export const requireRpcCaller = <A, E, R, A2, E2, R2>(
104
+ callers: ReadonlySet<string>,
105
+ rpc: Effect.Effect<A, E, R>,
106
+ fallback: Effect.Effect<A2, E2, R2>,
107
+ ) =>
108
+ Effect.gen(function* () {
109
+ const request = yield* HttpServerRequest;
110
+ if (rpcMethodOf(request) === undefined) return yield* fallback;
111
+ const key = request.headers[RPC_KEY_HEADER];
112
+ if (typeof key !== "string" || key.length === 0 || !callers.has(yield* sha256Hex(key))) {
113
+ return HttpServerResponse.text("Unauthorized: this caller has no binding to the program", {
114
+ status: 401,
115
+ });
116
+ }
117
+ return yield* rpc;
118
+ });
119
+
120
+ /** The caller key hashes in a target's env (see {@link RPC_CALLERS_ENV}). */
121
+ export const parseCallers = (value: unknown): ReadonlySet<string> => {
122
+ const list = typeof value === "string" ? safeParse(value) : value;
123
+ return new Set(Array.isArray(list) ? list.filter((item) => typeof item === "string") : []);
124
+ };
125
+
126
+ const safeParse = (text: string): unknown => {
127
+ try {
128
+ return JSON.parse(text);
129
+ } catch {
130
+ return undefined;
131
+ }
132
+ };