alchemy-effect 0.2.0 → 0.4.0

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 (494) hide show
  1. package/bin/alchemy-effect.js +55354 -8
  2. package/bin/alchemy-effect.js.map +1 -1
  3. package/bin/alchemy-effect.ts +266 -10
  4. package/lib/$.d.ts +5 -0
  5. package/lib/$.d.ts.map +1 -0
  6. package/lib/$.js +9 -0
  7. package/lib/$.js.map +1 -0
  8. package/lib/app.d.ts +4 -17
  9. package/lib/app.d.ts.map +1 -1
  10. package/lib/app.js +0 -20
  11. package/lib/app.js.map +1 -1
  12. package/lib/apply.d.ts +15 -75
  13. package/lib/apply.d.ts.map +1 -1
  14. package/lib/apply.js +439 -154
  15. package/lib/apply.js.map +1 -1
  16. package/lib/assert-never.d.ts +12 -0
  17. package/lib/assert-never.d.ts.map +1 -0
  18. package/lib/assert-never.js +11 -0
  19. package/lib/assert-never.js.map +1 -0
  20. package/lib/aws/account.d.ts +10 -1
  21. package/lib/aws/account.d.ts.map +1 -1
  22. package/lib/aws/account.js +18 -3
  23. package/lib/aws/account.js.map +1 -1
  24. package/lib/aws/client.d.ts.map +1 -1
  25. package/lib/aws/client.js +0 -1
  26. package/lib/aws/client.js.map +1 -1
  27. package/lib/aws/config.d.ts +15 -0
  28. package/lib/aws/config.d.ts.map +1 -0
  29. package/lib/aws/config.js +1 -0
  30. package/lib/aws/config.js.map +1 -0
  31. package/lib/aws/credentials.d.ts +10 -0
  32. package/lib/aws/credentials.d.ts.map +1 -1
  33. package/lib/aws/credentials.js +73 -47
  34. package/lib/aws/credentials.js.map +1 -1
  35. package/lib/aws/dynamodb/client.d.ts +1 -1
  36. package/lib/aws/dynamodb/client.d.ts.map +1 -1
  37. package/lib/aws/dynamodb/index.d.ts +2 -1
  38. package/lib/aws/dynamodb/index.d.ts.map +1 -1
  39. package/lib/aws/dynamodb/index.js +1 -2
  40. package/lib/aws/dynamodb/index.js.map +1 -1
  41. package/lib/aws/dynamodb/secondary-index.d.ts +5 -4
  42. package/lib/aws/dynamodb/secondary-index.d.ts.map +1 -1
  43. package/lib/aws/dynamodb/table.d.ts +23 -20
  44. package/lib/aws/dynamodb/table.d.ts.map +1 -1
  45. package/lib/aws/dynamodb/table.get-item.d.ts +8 -6
  46. package/lib/aws/dynamodb/table.get-item.d.ts.map +1 -1
  47. package/lib/aws/dynamodb/table.get-item.js +4 -2
  48. package/lib/aws/dynamodb/table.get-item.js.map +1 -1
  49. package/lib/aws/dynamodb/table.js.map +1 -1
  50. package/lib/aws/dynamodb/table.provider.d.ts +3 -4
  51. package/lib/aws/dynamodb/table.provider.d.ts.map +1 -1
  52. package/lib/aws/dynamodb/table.provider.js +18 -29
  53. package/lib/aws/dynamodb/table.provider.js.map +1 -1
  54. package/lib/aws/ec2/client.d.ts +1 -1
  55. package/lib/aws/ec2/client.d.ts.map +1 -1
  56. package/lib/aws/ec2/index.d.ts +11 -0
  57. package/lib/aws/ec2/index.d.ts.map +1 -1
  58. package/lib/aws/ec2/index.js +11 -0
  59. package/lib/aws/ec2/index.js.map +1 -1
  60. package/lib/aws/ec2/internet-gateway.d.ts +65 -0
  61. package/lib/aws/ec2/internet-gateway.d.ts.map +1 -0
  62. package/lib/aws/ec2/internet-gateway.js +4 -0
  63. package/lib/aws/ec2/internet-gateway.js.map +1 -0
  64. package/lib/aws/ec2/internet-gateway.provider.d.ts +6 -0
  65. package/lib/aws/ec2/internet-gateway.provider.d.ts.map +1 -0
  66. package/lib/aws/ec2/internet-gateway.provider.js +193 -0
  67. package/lib/aws/ec2/internet-gateway.provider.js.map +1 -0
  68. package/lib/aws/ec2/route-table-association.d.ts +63 -0
  69. package/lib/aws/ec2/route-table-association.d.ts.map +1 -0
  70. package/lib/aws/ec2/route-table-association.js +4 -0
  71. package/lib/aws/ec2/route-table-association.js.map +1 -0
  72. package/lib/aws/ec2/route-table-association.provider.d.ts +4 -0
  73. package/lib/aws/ec2/route-table-association.provider.d.ts.map +1 -0
  74. package/lib/aws/ec2/route-table-association.provider.js +121 -0
  75. package/lib/aws/ec2/route-table-association.provider.js.map +1 -0
  76. package/lib/aws/ec2/route-table.d.ts +159 -0
  77. package/lib/aws/ec2/route-table.d.ts.map +1 -0
  78. package/lib/aws/ec2/route-table.js +4 -0
  79. package/lib/aws/ec2/route-table.js.map +1 -0
  80. package/lib/aws/ec2/route-table.provider.d.ts +6 -0
  81. package/lib/aws/ec2/route-table.provider.d.ts.map +1 -0
  82. package/lib/aws/ec2/route-table.provider.js +213 -0
  83. package/lib/aws/ec2/route-table.provider.js.map +1 -0
  84. package/lib/aws/ec2/route.d.ts +155 -0
  85. package/lib/aws/ec2/route.d.ts.map +1 -0
  86. package/lib/aws/ec2/route.js +3 -0
  87. package/lib/aws/ec2/route.js.map +1 -0
  88. package/lib/aws/ec2/route.provider.d.ts +4 -0
  89. package/lib/aws/ec2/route.provider.d.ts.map +1 -0
  90. package/lib/aws/ec2/route.provider.js +166 -0
  91. package/lib/aws/ec2/route.provider.js.map +1 -0
  92. package/lib/aws/ec2/subnet.d.ts +175 -0
  93. package/lib/aws/ec2/subnet.d.ts.map +1 -0
  94. package/lib/aws/ec2/subnet.js +4 -0
  95. package/lib/aws/ec2/subnet.js.map +1 -0
  96. package/lib/aws/ec2/subnet.provider.d.ts +4 -0
  97. package/lib/aws/ec2/subnet.provider.d.ts.map +1 -0
  98. package/lib/aws/ec2/subnet.provider.js +250 -0
  99. package/lib/aws/ec2/subnet.provider.js.map +1 -0
  100. package/lib/aws/ec2/vpc.d.ts +13 -8
  101. package/lib/aws/ec2/vpc.d.ts.map +1 -1
  102. package/lib/aws/ec2/vpc.js +1 -0
  103. package/lib/aws/ec2/vpc.js.map +1 -1
  104. package/lib/aws/ec2/vpc.provider.d.ts +1 -2
  105. package/lib/aws/ec2/vpc.provider.d.ts.map +1 -1
  106. package/lib/aws/ec2/vpc.provider.js +45 -37
  107. package/lib/aws/ec2/vpc.provider.js.map +1 -1
  108. package/lib/aws/index.d.ts +15 -19
  109. package/lib/aws/index.d.ts.map +1 -1
  110. package/lib/aws/index.js +8 -10
  111. package/lib/aws/index.js.map +1 -1
  112. package/lib/aws/lambda/consume.d.ts +10 -11
  113. package/lib/aws/lambda/consume.d.ts.map +1 -1
  114. package/lib/aws/lambda/consume.js +3 -3
  115. package/lib/aws/lambda/consume.js.map +1 -1
  116. package/lib/aws/lambda/function.d.ts +7 -7
  117. package/lib/aws/lambda/function.d.ts.map +1 -1
  118. package/lib/aws/lambda/function.handler.d.ts +1 -1
  119. package/lib/aws/lambda/function.handler.d.ts.map +1 -1
  120. package/lib/aws/lambda/function.handler.js.map +1 -1
  121. package/lib/aws/lambda/function.invoke.d.ts +6 -4
  122. package/lib/aws/lambda/function.invoke.d.ts.map +1 -1
  123. package/lib/aws/lambda/function.invoke.js +3 -1
  124. package/lib/aws/lambda/function.invoke.js.map +1 -1
  125. package/lib/aws/lambda/function.js +1 -1
  126. package/lib/aws/lambda/function.js.map +1 -1
  127. package/lib/aws/lambda/function.provider.d.ts +3 -2
  128. package/lib/aws/lambda/function.provider.d.ts.map +1 -1
  129. package/lib/aws/lambda/function.provider.js +28 -25
  130. package/lib/aws/lambda/function.provider.js.map +1 -1
  131. package/lib/aws/lambda/index.d.ts +1 -0
  132. package/lib/aws/lambda/index.d.ts.map +1 -1
  133. package/lib/aws/lambda/index.js +1 -0
  134. package/lib/aws/lambda/index.js.map +1 -1
  135. package/lib/aws/lambda/serve.d.ts +2 -4
  136. package/lib/aws/lambda/serve.d.ts.map +1 -1
  137. package/lib/aws/profile.d.ts +2 -2
  138. package/lib/aws/profile.d.ts.map +1 -1
  139. package/lib/aws/profile.js +1 -1
  140. package/lib/aws/profile.js.map +1 -1
  141. package/lib/aws/region.d.ts +14 -2
  142. package/lib/aws/region.d.ts.map +1 -1
  143. package/lib/aws/region.js +26 -1
  144. package/lib/aws/region.js.map +1 -1
  145. package/lib/aws/sqs/client.d.ts +1 -1
  146. package/lib/aws/sqs/client.d.ts.map +1 -1
  147. package/lib/aws/sqs/index.d.ts +1 -0
  148. package/lib/aws/sqs/index.d.ts.map +1 -1
  149. package/lib/aws/sqs/index.js +1 -0
  150. package/lib/aws/sqs/index.js.map +1 -1
  151. package/lib/aws/sqs/queue.consume.d.ts +1 -1
  152. package/lib/aws/sqs/queue.consume.d.ts.map +1 -1
  153. package/lib/aws/sqs/queue.consume.js +0 -1
  154. package/lib/aws/sqs/queue.consume.js.map +1 -1
  155. package/lib/aws/sqs/queue.d.ts +6 -4
  156. package/lib/aws/sqs/queue.d.ts.map +1 -1
  157. package/lib/aws/sqs/queue.event-source.d.ts +8 -6
  158. package/lib/aws/sqs/queue.event-source.d.ts.map +1 -1
  159. package/lib/aws/sqs/queue.event-source.js +26 -44
  160. package/lib/aws/sqs/queue.event-source.js.map +1 -1
  161. package/lib/aws/sqs/queue.js +1 -1
  162. package/lib/aws/sqs/queue.js.map +1 -1
  163. package/lib/aws/sqs/queue.provider.d.ts +2 -2
  164. package/lib/aws/sqs/queue.provider.d.ts.map +1 -1
  165. package/lib/aws/sqs/queue.provider.js +2 -1
  166. package/lib/aws/sqs/queue.provider.js.map +1 -1
  167. package/lib/aws/sqs/queue.send-message.d.ts +7 -5
  168. package/lib/aws/sqs/queue.send-message.d.ts.map +1 -1
  169. package/lib/aws/sqs/queue.send-message.js +4 -2
  170. package/lib/aws/sqs/queue.send-message.js.map +1 -1
  171. package/lib/binding.d.ts +12 -12
  172. package/lib/binding.d.ts.map +1 -1
  173. package/lib/binding.js.map +1 -1
  174. package/lib/cli/components/ApprovePlan.d.ts +2 -2
  175. package/lib/cli/components/ApprovePlan.d.ts.map +1 -1
  176. package/lib/cli/components/ApprovePlan.js.map +1 -1
  177. package/lib/cli/components/Plan.d.ts +2 -2
  178. package/lib/cli/components/Plan.d.ts.map +1 -1
  179. package/lib/cli/components/Plan.js.map +1 -1
  180. package/lib/cli/components/PlanProgress.d.ts +8 -4
  181. package/lib/cli/components/PlanProgress.d.ts.map +1 -1
  182. package/lib/cli/components/PlanProgress.js +11 -1
  183. package/lib/cli/components/PlanProgress.js.map +1 -1
  184. package/lib/cli/index.d.ts +384 -264
  185. package/lib/cli/index.d.ts.map +1 -1
  186. package/lib/cli/index.js +57 -65
  187. package/lib/cli/index.js.map +1 -1
  188. package/lib/cli/ink-service.d.ts +4 -0
  189. package/lib/cli/ink-service.d.ts.map +1 -0
  190. package/lib/cli/ink-service.js +43 -0
  191. package/lib/cli/ink-service.js.map +1 -0
  192. package/lib/cli/service.d.ts +21 -0
  193. package/lib/cli/service.d.ts.map +1 -0
  194. package/lib/cli/service.js +5 -0
  195. package/lib/cli/service.js.map +1 -0
  196. package/lib/cloudflare/account.d.ts +10 -0
  197. package/lib/cloudflare/account.d.ts.map +1 -0
  198. package/lib/cloudflare/account.js +24 -0
  199. package/lib/cloudflare/account.js.map +1 -0
  200. package/lib/cloudflare/api.d.ts +7 -7
  201. package/lib/cloudflare/api.d.ts.map +1 -1
  202. package/lib/cloudflare/api.js +18 -17
  203. package/lib/cloudflare/api.js.map +1 -1
  204. package/lib/cloudflare/config.d.ts +9 -0
  205. package/lib/cloudflare/config.d.ts.map +1 -0
  206. package/lib/cloudflare/config.js +1 -0
  207. package/lib/cloudflare/config.js.map +1 -0
  208. package/lib/cloudflare/index.d.ts +3 -1
  209. package/lib/cloudflare/index.d.ts.map +1 -1
  210. package/lib/cloudflare/index.js +3 -0
  211. package/lib/cloudflare/index.js.map +1 -1
  212. package/lib/cloudflare/kv/namespace.binding.d.ts +5 -3
  213. package/lib/cloudflare/kv/namespace.binding.d.ts.map +1 -1
  214. package/lib/cloudflare/kv/namespace.binding.js +1 -1
  215. package/lib/cloudflare/kv/namespace.binding.js.map +1 -1
  216. package/lib/cloudflare/kv/namespace.client.d.ts +1 -1
  217. package/lib/cloudflare/kv/namespace.d.ts +3 -2
  218. package/lib/cloudflare/kv/namespace.d.ts.map +1 -1
  219. package/lib/cloudflare/kv/namespace.js.map +1 -1
  220. package/lib/cloudflare/kv/namespace.provider.d.ts +3 -2
  221. package/lib/cloudflare/kv/namespace.provider.d.ts.map +1 -1
  222. package/lib/cloudflare/kv/namespace.provider.js +9 -7
  223. package/lib/cloudflare/kv/namespace.provider.js.map +1 -1
  224. package/lib/cloudflare/live.d.ts +5 -5
  225. package/lib/cloudflare/live.d.ts.map +1 -1
  226. package/lib/cloudflare/live.js +5 -8
  227. package/lib/cloudflare/live.js.map +1 -1
  228. package/lib/cloudflare/r2/bucket.binding.d.ts +5 -3
  229. package/lib/cloudflare/r2/bucket.binding.d.ts.map +1 -1
  230. package/lib/cloudflare/r2/bucket.binding.js +1 -1
  231. package/lib/cloudflare/r2/bucket.binding.js.map +1 -1
  232. package/lib/cloudflare/r2/bucket.d.ts +3 -2
  233. package/lib/cloudflare/r2/bucket.d.ts.map +1 -1
  234. package/lib/cloudflare/r2/bucket.js.map +1 -1
  235. package/lib/cloudflare/r2/bucket.provider.d.ts +3 -2
  236. package/lib/cloudflare/r2/bucket.provider.d.ts.map +1 -1
  237. package/lib/cloudflare/r2/bucket.provider.js +14 -8
  238. package/lib/cloudflare/r2/bucket.provider.js.map +1 -1
  239. package/lib/cloudflare/worker/assets.fetch.d.ts +3 -2
  240. package/lib/cloudflare/worker/assets.fetch.d.ts.map +1 -1
  241. package/lib/cloudflare/worker/assets.fetch.js +2 -1
  242. package/lib/cloudflare/worker/assets.fetch.js.map +1 -1
  243. package/lib/cloudflare/worker/assets.provider.d.ts +1 -1
  244. package/lib/cloudflare/worker/assets.provider.d.ts.map +1 -1
  245. package/lib/cloudflare/worker/index.d.ts +0 -1
  246. package/lib/cloudflare/worker/index.d.ts.map +1 -1
  247. package/lib/cloudflare/worker/worker.d.ts +5 -6
  248. package/lib/cloudflare/worker/worker.d.ts.map +1 -1
  249. package/lib/cloudflare/worker/worker.handler.d.ts +1 -1
  250. package/lib/cloudflare/worker/worker.handler.d.ts.map +1 -1
  251. package/lib/cloudflare/worker/worker.handler.js.map +1 -1
  252. package/lib/cloudflare/worker/worker.js.map +1 -1
  253. package/lib/cloudflare/worker/worker.provider.d.ts +3 -2
  254. package/lib/cloudflare/worker/worker.provider.d.ts.map +1 -1
  255. package/lib/cloudflare/worker/worker.provider.js +12 -7
  256. package/lib/cloudflare/worker/worker.provider.js.map +1 -1
  257. package/lib/cloudflare/worker/worker.serve.d.ts +7 -7
  258. package/lib/cloudflare/worker/worker.serve.d.ts.map +1 -1
  259. package/lib/cloudflare/worker/worker.serve.js.map +1 -1
  260. package/lib/data.d.ts +3 -0
  261. package/lib/data.d.ts.map +1 -0
  262. package/lib/data.js +8 -0
  263. package/lib/data.js.map +1 -0
  264. package/lib/destroy.d.ts +1 -1
  265. package/lib/destroy.d.ts.map +1 -1
  266. package/lib/destroy.js +1 -4
  267. package/lib/destroy.js.map +1 -1
  268. package/lib/diff.d.ts +18 -0
  269. package/lib/diff.d.ts.map +1 -0
  270. package/lib/diff.js +22 -0
  271. package/lib/diff.js.map +1 -0
  272. package/lib/env.d.ts +5 -0
  273. package/lib/env.d.ts.map +1 -1
  274. package/lib/env.js +15 -29
  275. package/lib/env.js.map +1 -1
  276. package/lib/event.d.ts +1 -1
  277. package/lib/event.d.ts.map +1 -1
  278. package/lib/exports.d.ts +9 -0
  279. package/lib/exports.d.ts.map +1 -0
  280. package/lib/exports.js +13 -0
  281. package/lib/exports.js.map +1 -0
  282. package/lib/index.d.ts +10 -2
  283. package/lib/index.d.ts.map +1 -1
  284. package/lib/index.js +10 -5
  285. package/lib/index.js.map +1 -1
  286. package/lib/input.d.ts +32 -0
  287. package/lib/input.d.ts.map +1 -0
  288. package/lib/input.js +1 -0
  289. package/lib/input.js.map +1 -0
  290. package/lib/instance-id.d.ts +8 -0
  291. package/lib/instance-id.d.ts.map +1 -0
  292. package/lib/instance-id.js +12 -0
  293. package/lib/instance-id.js.map +1 -0
  294. package/lib/output.d.ts +145 -0
  295. package/lib/output.d.ts.map +1 -0
  296. package/lib/output.js +283 -0
  297. package/lib/output.js.map +1 -0
  298. package/lib/physical-name.d.ts +14 -1
  299. package/lib/physical-name.d.ts.map +1 -1
  300. package/lib/physical-name.js +41 -2
  301. package/lib/physical-name.js.map +1 -1
  302. package/lib/plan.d.ts +84 -58
  303. package/lib/plan.d.ts.map +1 -1
  304. package/lib/plan.js +504 -166
  305. package/lib/plan.js.map +1 -1
  306. package/lib/policy.d.ts +3 -4
  307. package/lib/policy.d.ts.map +1 -1
  308. package/lib/policy.js +0 -1
  309. package/lib/policy.js.map +1 -1
  310. package/lib/provider.d.ts +39 -24
  311. package/lib/provider.d.ts.map +1 -1
  312. package/lib/provider.js +9 -0
  313. package/lib/provider.js.map +1 -1
  314. package/lib/ref.d.ts +14 -0
  315. package/lib/ref.d.ts.map +1 -0
  316. package/lib/ref.js +21 -0
  317. package/lib/ref.js.map +1 -0
  318. package/lib/resource.d.ts +13 -8
  319. package/lib/resource.d.ts.map +1 -1
  320. package/lib/resource.js.map +1 -1
  321. package/lib/runtime.d.ts +7 -6
  322. package/lib/runtime.d.ts.map +1 -1
  323. package/lib/runtime.js.map +1 -1
  324. package/lib/service.d.ts +9 -6
  325. package/lib/service.d.ts.map +1 -1
  326. package/lib/service.js.map +1 -1
  327. package/lib/stack.d.ts +60 -0
  328. package/lib/stack.d.ts.map +1 -0
  329. package/lib/stack.js +11 -0
  330. package/lib/stack.js.map +1 -0
  331. package/lib/stage.d.ts +39 -0
  332. package/lib/stage.d.ts.map +1 -0
  333. package/lib/stage.js +32 -0
  334. package/lib/stage.js.map +1 -0
  335. package/lib/state.d.ts +135 -17
  336. package/lib/state.d.ts.map +1 -1
  337. package/lib/state.js +34 -30
  338. package/lib/state.js.map +1 -1
  339. package/lib/tags.d.ts +15 -0
  340. package/lib/tags.d.ts.map +1 -1
  341. package/lib/tags.js +27 -0
  342. package/lib/tags.js.map +1 -1
  343. package/lib/test.d.ts +25 -4
  344. package/lib/test.d.ts.map +1 -1
  345. package/lib/test.js +54 -14
  346. package/lib/test.js.map +1 -1
  347. package/lib/todo.d.ts +3 -0
  348. package/lib/todo.d.ts.map +1 -0
  349. package/lib/todo.js +3 -0
  350. package/lib/todo.js.map +1 -0
  351. package/lib/tsconfig.test.tsbuildinfo +1 -1
  352. package/lib/type.d.ts +3 -0
  353. package/lib/type.d.ts.map +1 -1
  354. package/lib/unknown.d.ts +4 -0
  355. package/lib/unknown.d.ts.map +1 -0
  356. package/lib/unknown.js +4 -0
  357. package/lib/unknown.js.map +1 -0
  358. package/lib/user.d.ts +3 -0
  359. package/lib/user.d.ts.map +1 -0
  360. package/lib/user.js +3 -0
  361. package/lib/user.js.map +1 -0
  362. package/lib/util.d.ts +6 -0
  363. package/lib/util.d.ts.map +1 -0
  364. package/lib/util.js +9 -0
  365. package/lib/util.js.map +1 -0
  366. package/package.json +18 -12
  367. package/src/$.ts +17 -0
  368. package/src/app.ts +3 -32
  369. package/src/apply.ts +824 -452
  370. package/src/assert-never.ts +18 -0
  371. package/src/aws/account.ts +23 -3
  372. package/src/aws/client.ts +0 -1
  373. package/src/aws/config.ts +16 -0
  374. package/src/aws/credentials.ts +212 -177
  375. package/src/aws/dynamodb/index.ts +3 -3
  376. package/src/aws/dynamodb/table.get-item.ts +5 -9
  377. package/src/aws/dynamodb/table.provider.ts +36 -39
  378. package/src/aws/dynamodb/table.ts +29 -84
  379. package/src/aws/ec2/index.ts +12 -0
  380. package/src/aws/ec2/internet-gateway.provider.ts +316 -0
  381. package/src/aws/ec2/internet-gateway.ts +79 -0
  382. package/src/aws/ec2/route-table-association.provider.ts +214 -0
  383. package/src/aws/ec2/route-table-association.ts +82 -0
  384. package/src/aws/ec2/route-table.provider.ts +306 -0
  385. package/src/aws/ec2/route-table.ts +175 -0
  386. package/src/aws/ec2/route.provider.ts +213 -0
  387. package/src/aws/ec2/route.ts +192 -0
  388. package/src/aws/ec2/subnet.provider.ts +358 -0
  389. package/src/aws/ec2/subnet.ts +213 -0
  390. package/src/aws/ec2/vpc.provider.ts +58 -50
  391. package/src/aws/ec2/vpc.ts +21 -8
  392. package/src/aws/index.ts +49 -40
  393. package/src/aws/lambda/consume.ts +8 -7
  394. package/src/aws/lambda/function.handler.ts +1 -1
  395. package/src/aws/lambda/function.invoke.ts +6 -2
  396. package/src/aws/lambda/function.provider.ts +41 -32
  397. package/src/aws/lambda/function.ts +7 -4
  398. package/src/aws/lambda/index.ts +2 -0
  399. package/src/aws/profile.ts +1 -4
  400. package/src/aws/region.ts +42 -3
  401. package/src/aws/sqs/index.ts +2 -0
  402. package/src/aws/sqs/queue.consume.ts +1 -1
  403. package/src/aws/sqs/queue.event-source.ts +29 -55
  404. package/src/aws/sqs/queue.provider.ts +10 -2
  405. package/src/aws/sqs/queue.send-message.ts +5 -8
  406. package/src/aws/sqs/queue.ts +9 -4
  407. package/src/binding.ts +19 -19
  408. package/src/cli/components/ApprovePlan.tsx +2 -2
  409. package/src/cli/components/Plan.tsx +3 -2
  410. package/src/cli/components/PlanProgress.tsx +32 -14
  411. package/src/cli/index.ts +2 -6
  412. package/src/cli/ink-service.tsx +61 -0
  413. package/src/cli/service.ts +23 -0
  414. package/src/cloudflare/account.ts +37 -0
  415. package/src/cloudflare/api.ts +33 -29
  416. package/src/cloudflare/config.ts +7 -0
  417. package/src/cloudflare/index.ts +3 -1
  418. package/src/cloudflare/kv/namespace.binding.ts +3 -1
  419. package/src/cloudflare/kv/namespace.provider.ts +10 -8
  420. package/src/cloudflare/kv/namespace.ts +3 -2
  421. package/src/cloudflare/live.ts +11 -17
  422. package/src/cloudflare/r2/bucket.binding.ts +3 -1
  423. package/src/cloudflare/r2/bucket.provider.ts +16 -9
  424. package/src/cloudflare/r2/bucket.ts +8 -2
  425. package/src/cloudflare/worker/assets.fetch.ts +3 -1
  426. package/src/cloudflare/worker/assets.provider.ts +1 -1
  427. package/src/cloudflare/worker/index.ts +0 -2
  428. package/src/cloudflare/worker/worker.handler.ts +1 -1
  429. package/src/cloudflare/worker/worker.provider.ts +21 -14
  430. package/src/cloudflare/worker/worker.serve.ts +5 -2
  431. package/src/cloudflare/worker/worker.ts +4 -3
  432. package/src/data.ts +18 -0
  433. package/src/destroy.ts +1 -5
  434. package/src/diff.ts +48 -0
  435. package/src/env.ts +20 -32
  436. package/src/event.ts +6 -0
  437. package/src/exports.ts +21 -0
  438. package/src/index.ts +10 -5
  439. package/src/input.ts +81 -0
  440. package/src/instance-id.ts +16 -0
  441. package/src/output.ts +542 -0
  442. package/src/physical-name.ts +57 -2
  443. package/src/plan.ts +757 -278
  444. package/src/policy.ts +3 -5
  445. package/src/provider.ts +70 -31
  446. package/src/ref.ts +48 -0
  447. package/src/resource.ts +70 -10
  448. package/src/runtime.ts +15 -8
  449. package/src/service.ts +11 -7
  450. package/src/stack.ts +116 -0
  451. package/src/stage.ts +85 -0
  452. package/src/state.ts +269 -76
  453. package/src/tags.ts +31 -0
  454. package/src/test.ts +118 -17
  455. package/src/todo.ts +4 -0
  456. package/src/type.ts +4 -0
  457. package/src/unknown.ts +6 -0
  458. package/src/user.ts +4 -0
  459. package/src/util.ts +21 -0
  460. package/lib/approve.d.ts +0 -15
  461. package/lib/approve.d.ts.map +0 -1
  462. package/lib/approve.js +0 -7
  463. package/lib/approve.js.map +0 -1
  464. package/lib/cli/approve.d.ts +0 -4
  465. package/lib/cli/approve.d.ts.map +0 -1
  466. package/lib/cli/approve.js +0 -18
  467. package/lib/cli/approve.js.map +0 -1
  468. package/lib/cli/clack.d.ts +0 -14
  469. package/lib/cli/clack.d.ts.map +0 -1
  470. package/lib/cli/clack.js +0 -12
  471. package/lib/cli/clack.js.map +0 -1
  472. package/lib/cli/main.d.ts +0 -2
  473. package/lib/cli/main.d.ts.map +0 -1
  474. package/lib/cli/main.js +0 -1
  475. package/lib/cli/main.js.map +0 -1
  476. package/lib/cli/plan.d.ts +0 -13
  477. package/lib/cli/plan.d.ts.map +0 -1
  478. package/lib/cli/plan.js +0 -1
  479. package/lib/cli/plan.js.map +0 -1
  480. package/lib/cli/progress.d.ts +0 -7
  481. package/lib/cli/progress.d.ts.map +0 -1
  482. package/lib/cli/progress.js +0 -30
  483. package/lib/cli/progress.js.map +0 -1
  484. package/lib/cli/spinner.d.ts +0 -2
  485. package/lib/cli/spinner.d.ts.map +0 -1
  486. package/lib/cli/spinner.js +0 -13
  487. package/lib/cli/spinner.js.map +0 -1
  488. package/src/approve.ts +0 -13
  489. package/src/cli/approve.tsx +0 -30
  490. package/src/cli/clack.ts +0 -22
  491. package/src/cli/main.ts +0 -0
  492. package/src/cli/plan.ts +0 -16
  493. package/src/cli/progress.tsx +0 -46
  494. package/src/cli/spinner.ts +0 -14
package/src/apply.ts CHANGED
@@ -1,476 +1,848 @@
1
1
  import * as Context from "effect/Context";
2
2
  import * as Effect from "effect/Effect";
3
- import * as Option from "effect/Option";
4
3
  import type { Simplify } from "effect/Types";
5
- import { PlanReviewer, type PlanRejected } from "./approve.ts";
4
+ import { App } from "./app.ts";
6
5
  import type { AnyBinding, BindingService } from "./binding.ts";
7
- import type { ApplyEvent, ApplyStatus } from "./event.ts";
8
6
  import {
7
+ type PlanStatusSession,
8
+ CLI,
9
+ type ScopedPlanStatusSession,
10
+ } from "./cli/service.ts";
11
+ import type { ApplyStatus } from "./event.ts";
12
+ import { generateInstanceId } from "./instance-id.ts";
13
+ import * as Output from "./output.ts";
14
+ import {
15
+ type Apply,
9
16
  plan,
10
17
  type BindNode,
11
- type Create,
12
- type CRUD,
13
18
  type Delete,
14
- type Plan,
15
- type Update,
19
+ type DerivePlan,
20
+ type IPlan,
21
+ type Providers,
16
22
  } from "./plan.ts";
17
- import type { Resource } from "./resource.ts";
18
- import type { Service } from "./service.ts";
19
- import { State } from "./state.ts";
20
-
21
- export interface PlanStatusSession {
22
- emit: (event: ApplyEvent) => Effect.Effect<void>;
23
- done: () => Effect.Effect<void>;
24
- }
25
-
26
- export interface ScopedPlanStatusSession extends PlanStatusSession {
27
- note: (note: string) => Effect.Effect<void>;
28
- }
29
-
30
- export class PlanStatusReporter extends Context.Tag("PlanStatusReporter")<
31
- PlanStatusReporter,
23
+ import type { Instance } from "./policy.ts";
24
+ import type { AnyResource, Resource } from "./resource.ts";
25
+ import type { AnyService } from "./service.ts";
26
+ import {
27
+ type CreatedResourceState,
28
+ type CreatingResourceState,
29
+ type DeletingResourceState,
30
+ type ReplacedResourceState,
31
+ type ReplacingResourceState,
32
+ type ResourceState,
33
+ type UpdatedResourceState,
34
+ type UpdatingReourceState,
35
+ State,
36
+ StateStoreError,
37
+ } from "./state.ts";
38
+ import { asEffect } from "./util.ts";
39
+ import { getProviderByType } from "./provider.ts";
40
+
41
+ export type ApplyEffect<
42
+ P extends IPlan,
43
+ Err = never,
44
+ Req = never,
45
+ > = Effect.Effect<
32
46
  {
33
- start(plan: Plan): Effect.Effect<PlanStatusSession, never>;
34
- }
35
- >() {}
36
-
37
- export const apply: typeof applyPlan &
38
- typeof applyResources &
39
- typeof applyResourcesPhase = (...args: any[]): any =>
40
- Effect.isEffect(args[0])
41
- ? applyPlan(args[0] as any)
42
- : args.length === 1 && "phase" in args[0]
43
- ? applyResourcesPhase(args[0])
44
- : applyResources(...args);
45
-
46
- export const applyResourcesPhase = <
47
- const Phase extends "update" | "destroy",
48
- const Resources extends (Service | Resource)[],
49
- >(props: {
50
- resources: Resources;
51
- phase: Phase;
52
- }) => applyPlan(plan(props));
53
-
54
- export const applyResources = <const Resources extends (Service | Resource)[]>(
47
+ [k in keyof AppliedPlan<P>]: AppliedPlan<P>[k];
48
+ },
49
+ Err,
50
+ Req
51
+ >;
52
+
53
+ export type AppliedPlan<P extends IPlan> = {
54
+ [id in keyof P["resources"]]: P["resources"][id] extends
55
+ | Delete<Resource>
56
+ | undefined
57
+ | never
58
+ ? never
59
+ : Simplify<P["resources"][id]["resource"]["attr"]>;
60
+ };
61
+
62
+ export const apply = <
63
+ const Resources extends (AnyService | AnyResource)[] = never,
64
+ >(
55
65
  ...resources: Resources
56
- ) =>
57
- applyPlan(
58
- plan({
59
- phase: "update",
60
- resources,
61
- }),
62
- );
63
-
64
- export const applyPlan = <P extends Plan, Err, Req>(
65
- plan: Effect.Effect<P, Err, Req>,
66
- ) =>
67
- plan.pipe(
68
- Effect.flatMap((plan) =>
69
- Effect.gen(function* () {
70
- const state = yield* State;
71
- const outputs = {} as Record<string, Effect.Effect<any, any>>;
72
- const reviewer = yield* Effect.serviceOption(PlanReviewer);
73
-
74
- if (Option.isSome(reviewer)) {
75
- yield* reviewer.value.approve(plan);
76
- }
77
-
78
- const events = yield* Effect.serviceOption(PlanStatusReporter);
79
-
80
- const session = Option.isSome(events)
81
- ? yield* events.value.start(plan)
82
- : ({
83
- emit: () => Effect.void,
84
- done: () => Effect.void,
85
- } satisfies PlanStatusSession);
86
- const { emit, done } = session;
87
-
88
- const constOrEffect = <T, Err = never, Req = never>(
89
- effect: T | Effect.Effect<T>,
90
- ): Effect.Effect<T, Err, Req> =>
91
- Effect.isEffect(effect) ? effect : Effect.succeed(effect);
92
-
93
- const resolveBindingUpstream = Effect.fn(function* ({
94
- node,
95
- resource,
96
- }: {
97
- node: BindNode;
98
- resource: Resource;
99
- }) {
100
- const binding = node.binding as AnyBinding & {
101
- // smuggled property (because it interacts poorly with inference)
102
- Tag: Context.Tag<never, BindingService>;
103
- };
104
- const provider = yield* binding.Tag;
105
-
106
- const resourceId: string = node.binding.capability.resource.id;
107
- const upstreamNode = plan.resources[resourceId];
108
- const upstreamAttr = resource
109
- ? yield* apply(upstreamNode)
110
- : yield* Effect.dieMessage(`Resource ${resourceId} not found`);
111
-
112
- return {
113
- resourceId,
114
- upstreamAttr,
115
- upstreamNode,
116
- provider,
117
- };
66
+ ): ApplyEffect<
67
+ DerivePlan<Instance<Resources[number]>>,
68
+ never,
69
+ State | Providers<Instance<Resources[number]>>
70
+ // TODO(sam): don't cast to any
71
+ > =>
72
+ plan(...resources).pipe(
73
+ Effect.flatMap((p) => applyPlan(p as any as IPlan)),
74
+ ) as any;
75
+
76
+ export const applyPlan = <P extends IPlan>(plan: P) =>
77
+ Effect.gen(function* () {
78
+ const cli = yield* CLI;
79
+ const session = yield* cli.startApplySession(plan);
80
+
81
+ // 1. expand the graph (create new resources, update existing and create replacements)
82
+ const resources = yield* expandAndPivot(plan, session);
83
+ // TODO(sam): support roll back to previous state if errors occur during expansion
84
+ // -> RISK: some UPDATEs may not be reverisble (i.e. trigger replacements)
85
+ // TODO(sam): should pivot be done separately? E.g shift traffic?
86
+
87
+ // 2. delete orphans and replaced resources
88
+ yield* collectGarbage(plan, session);
89
+
90
+ yield* session.done();
91
+
92
+ if (Object.keys(plan.resources).length === 0) {
93
+ // all resources are deleted, return undefined
94
+ return undefined;
95
+ }
96
+ return resources as {
97
+ [k in keyof AppliedPlan<P>]: AppliedPlan<P>[k];
98
+ };
99
+ });
100
+
101
+ const expandAndPivot = Effect.fnUntraced(function* (
102
+ plan: IPlan,
103
+ session: PlanStatusSession,
104
+ ) {
105
+ const state = yield* State;
106
+ const app = yield* App;
107
+
108
+ const outputs = {} as Record<string, Effect.Effect<any, any, State>>;
109
+ const resolveUpstream = Effect.fn(function* (resourceId: string) {
110
+ const upstreamNode = plan.resources[resourceId];
111
+ const upstreamAttr = upstreamNode
112
+ ? yield* apply(upstreamNode)
113
+ : yield* Effect.dieMessage(`Resource ${resourceId} not found`);
114
+ return {
115
+ resourceId,
116
+ upstreamAttr,
117
+ upstreamNode,
118
+ };
119
+ });
120
+
121
+ const resolveBindingUpstream = Effect.fn(function* ({
122
+ node,
123
+ }: {
124
+ node: BindNode;
125
+ resource: Resource;
126
+ }) {
127
+ const binding = node.binding as AnyBinding & {
128
+ // smuggled property (because it interacts poorly with inference)
129
+ Tag: Context.Tag<never, BindingService>;
130
+ };
131
+ const provider = yield* binding.Tag;
132
+ const resourceId: string = node.binding.capability.resource.id;
133
+ const { upstreamAttr, upstreamNode } = yield* resolveUpstream(resourceId);
134
+
135
+ return {
136
+ resourceId,
137
+ upstreamAttr,
138
+ upstreamNode,
139
+ provider,
140
+ };
141
+ });
142
+
143
+ const attachBindings = ({
144
+ resource,
145
+ bindings,
146
+ target,
147
+ }: {
148
+ resource: Resource;
149
+ bindings: BindNode[];
150
+ target: {
151
+ id: string;
152
+ props: any;
153
+ attr: any;
154
+ };
155
+ }) =>
156
+ Effect.all(
157
+ bindings.map(
158
+ Effect.fn(function* (node) {
159
+ const { resourceId, upstreamAttr, upstreamNode, provider } =
160
+ yield* resolveBindingUpstream({ node, resource });
161
+
162
+ const input = {
163
+ source: {
164
+ id: resourceId,
165
+ attr: upstreamAttr,
166
+ props: upstreamNode.resource.props,
167
+ },
168
+ props: node.binding.props,
169
+ attr: node.attr,
170
+ target,
171
+ } as const;
172
+ if (node.action === "attach") {
173
+ return yield* asEffect(provider.attach(input));
174
+ } else if (node.action === "reattach") {
175
+ // reattach is optional, we fall back to attach if it's not available
176
+ return yield* asEffect(
177
+ (provider.reattach ? provider.reattach : provider.attach)(input),
178
+ );
179
+ } else if (node.action === "detach" && provider.detach) {
180
+ return yield* asEffect(
181
+ provider.detach({
182
+ ...input,
183
+ target,
184
+ }),
185
+ );
186
+ }
187
+ return node.attr;
188
+ }),
189
+ ),
190
+ );
191
+
192
+ const postAttachBindings = ({
193
+ bindings,
194
+ bindingOutputs,
195
+ resource,
196
+ target,
197
+ }: {
198
+ bindings: BindNode[];
199
+ bindingOutputs: any[];
200
+ resource: Resource;
201
+ target: {
202
+ id: string;
203
+ props: any;
204
+ attr: any;
205
+ };
206
+ }) =>
207
+ Effect.all(
208
+ bindings.map(
209
+ Effect.fn(function* (node, i) {
210
+ const { resourceId, upstreamAttr, upstreamNode, provider } =
211
+ yield* resolveBindingUpstream({ node, resource });
212
+
213
+ const oldBindingOutput = bindingOutputs[i];
214
+
215
+ if (
216
+ provider.postattach &&
217
+ (node.action === "attach" || node.action === "reattach")
218
+ ) {
219
+ const bindingOutput = yield* asEffect(
220
+ provider.postattach({
221
+ source: {
222
+ id: resourceId,
223
+ attr: upstreamAttr,
224
+ props: upstreamNode.resource.props,
225
+ },
226
+ props: node.binding.props,
227
+ attr: oldBindingOutput,
228
+ target,
229
+ } as const),
230
+ );
231
+ return {
232
+ ...oldBindingOutput,
233
+ ...bindingOutput,
234
+ };
235
+ }
236
+ return oldBindingOutput;
237
+ }),
238
+ ),
239
+ );
240
+
241
+ const apply: (node: Apply) => Effect.Effect<any, never, never> = (node) =>
242
+ Effect.gen(function* () {
243
+ const commit = <State extends ResourceState>(value: State) =>
244
+ state.set({
245
+ stack: app.name,
246
+ stage: app.stage,
247
+ resourceId: node.resource.id,
248
+ value,
118
249
  });
119
250
 
120
- const attachBindings = ({
121
- resource,
122
- bindings,
123
- target,
124
- }: {
125
- resource: Resource;
126
- bindings: BindNode[];
127
- target: {
128
- id: string;
129
- props: any;
130
- attr: any;
131
- };
132
- }) =>
133
- Effect.all(
134
- bindings.map(
135
- Effect.fn(function* (node) {
136
- const { resourceId, upstreamAttr, upstreamNode, provider } =
137
- yield* resolveBindingUpstream({ node, resource });
138
-
139
- const input = {
140
- source: {
141
- id: resourceId,
142
- attr: upstreamAttr,
143
- props: upstreamNode.resource.props,
144
- },
145
- props: node.binding.props,
146
- attr: node.attr,
147
- target,
148
- } as const;
149
- if (node.action === "attach") {
150
- return yield* constOrEffect(provider.attach(input));
151
- } else if (node.action === "reattach") {
152
- // reattach is optional, we fall back to attach if it's not available
153
- return yield* constOrEffect(
154
- (provider.reattach ? provider.reattach : provider.attach)(
155
- input,
156
- ),
157
- );
158
- } else if (node.action === "detach" && provider.detach) {
159
- return yield* constOrEffect(
160
- provider.detach({
161
- ...input,
162
- target,
163
- }),
164
- );
165
- }
166
- return node.attr;
167
- }),
251
+ const id = node.resource.id;
252
+ const resource = node.resource;
253
+
254
+ const scopedSession = {
255
+ ...session,
256
+ note: (note: string) =>
257
+ session.emit({
258
+ id,
259
+ kind: "annotate",
260
+ message: note,
261
+ }),
262
+ } satisfies ScopedPlanStatusSession;
263
+
264
+ return yield* (outputs[id] ??= yield* Effect.cached(
265
+ Effect.gen(function* () {
266
+ const report = (status: ApplyStatus) =>
267
+ session.emit({
268
+ kind: "status-change",
269
+ id,
270
+ type: node.resource.type,
271
+ status,
272
+ });
273
+
274
+ if (node.action === "noop") {
275
+ return node.state.attr;
276
+ }
277
+
278
+ // resolve upstream dependencies before committing any changes to state
279
+ const upstream = Object.fromEntries(
280
+ yield* Effect.all(
281
+ Object.entries(Output.resolveUpstream(node.props)).map(([id]) =>
282
+ resolveUpstream(id).pipe(
283
+ Effect.map(({ upstreamAttr }) => [id, upstreamAttr]),
284
+ ),
285
+ ),
168
286
  ),
169
287
  );
170
288
 
171
- const postAttachBindings = ({
172
- bindings,
173
- bindingOutputs,
174
- resource,
175
- target,
176
- }: {
177
- bindings: BindNode[];
178
- bindingOutputs: any[];
179
- resource: Resource;
180
- target: {
181
- id: string;
182
- props: any;
183
- attr: any;
184
- };
185
- }) =>
186
- Effect.all(
187
- bindings.map(
188
- Effect.fn(function* (node, i) {
189
- const { resourceId, upstreamAttr, upstreamNode, provider } =
190
- yield* resolveBindingUpstream({ node, resource });
191
-
192
- const oldBindingOutput = bindingOutputs[i];
193
-
194
- if (
195
- provider.postattach &&
196
- (node.action === "attach" || node.action === "reattach")
197
- ) {
198
- const bindingOutput = yield* constOrEffect(
199
- provider.postattach({
200
- source: {
201
- id: resourceId,
202
- attr: upstreamAttr,
203
- props: upstreamNode.resource.props,
204
- },
205
- props: node.binding.props,
206
- attr: oldBindingOutput,
207
- target,
208
- } as const),
209
- );
210
- return {
211
- ...oldBindingOutput,
212
- ...bindingOutput,
213
- };
214
- }
215
- return oldBindingOutput;
216
- }),
217
- ),
218
- );
289
+ const instanceId = yield* Effect.gen(function* () {
290
+ if (node.action === "create" && !node.state?.instanceId) {
291
+ const instanceId = yield* generateInstanceId();
292
+ yield* commit<CreatingResourceState>({
293
+ status: "creating",
294
+ instanceId,
295
+ logicalId: id,
296
+ downstream: node.downstream,
297
+ props: node.props,
298
+ providerVersion: node.provider.version ?? 0,
299
+ resourceType: node.resource.type,
300
+ bindings: node.bindings,
301
+ });
302
+ return instanceId;
303
+ } else if (node.action === "replace") {
304
+ if (
305
+ node.state.status === "replaced" ||
306
+ node.state.status === "replacing"
307
+ ) {
308
+ // replace has already begun and we have the new instanceId, do not re-create it
309
+ return node.state.instanceId;
310
+ }
311
+ const instanceId = yield* generateInstanceId();
312
+ yield* commit<ReplacingResourceState>({
313
+ status: "replacing",
314
+ instanceId,
315
+ logicalId: id,
316
+ downstream: node.downstream,
317
+ props: node.props,
318
+ providerVersion: node.provider.version ?? 0,
319
+ resourceType: node.resource.type,
320
+ bindings: node.bindings,
321
+ old: node.state,
322
+ deleteFirst: node.deleteFirst,
323
+ });
324
+ return instanceId;
325
+ } else if (node.state?.instanceId) {
326
+ // we're in a create, update or delete state with a stable instanceId, use it
327
+ return node.state.instanceId;
328
+ }
329
+ // this should never happen
330
+ return yield* Effect.dieMessage(
331
+ `Instance ID not found for resource '${id}' and action is '${node.action}'`,
332
+ );
333
+ });
334
+
335
+ if (node.action === "create") {
336
+ const news = (yield* Output.evaluate(
337
+ node.props,
338
+ upstream,
339
+ )) as Record<string, any>;
340
+
341
+ const checkpoint = (attr: any) =>
342
+ commit<CreatingResourceState>({
343
+ status: "creating",
344
+ logicalId: id,
345
+ instanceId,
346
+ resourceType: node.resource.type,
347
+ props: news,
348
+ attr,
349
+ providerVersion: node.provider.version ?? 0,
350
+ bindings: node.bindings,
351
+ downstream: node.downstream,
352
+ });
353
+
354
+ if (!node.state) {
355
+ yield* checkpoint(undefined);
356
+ }
219
357
 
220
- const apply: (node: CRUD) => Effect.Effect<any, never, never> = (
221
- node,
222
- ) =>
223
- Effect.gen(function* () {
224
- const checkpoint = <Out, Err>(
225
- effect: Effect.Effect<Out, Err, never>,
226
- ) => effect.pipe(Effect.flatMap((output) => saveState({ output })));
227
-
228
- const saveState = <Output>({
229
- output,
230
- bindings = node.bindings,
231
- }: {
232
- output: Output;
233
- bindings?: BindNode[];
234
- }) =>
235
- state
236
- .set(node.resource.id, {
237
- id: node.resource.id,
238
- type: node.resource.type,
239
- status: node.action === "create" ? "created" : "updated",
240
- props: node.resource.props,
241
- output,
242
- bindings,
243
- })
244
- .pipe(Effect.map(() => output));
245
-
246
- const id = node.resource.id;
247
- const resource = node.resource;
248
-
249
- const scopedSession = {
250
- ...session,
251
- note: (note: string) =>
252
- session.emit({
253
- id,
254
- kind: "annotate",
255
- message: note,
256
- }),
257
- } satisfies ScopedPlanStatusSession;
258
-
259
- return yield* (outputs[id] ??= yield* Effect.cached(
260
- Effect.gen(function* () {
261
- const report = (status: ApplyStatus) =>
262
- emit({
263
- kind: "status-change",
264
- id,
265
- type: node.resource.type,
266
- status,
267
- });
268
-
269
- const createOrUpdate = Effect.fn(function* ({
270
- node,
358
+ let attr: any;
359
+ if (
360
+ node.action === "create" &&
361
+ node.provider.precreate &&
362
+ // pre-create is only designed to ensure the resource exists, if we have state.attr, then it already exists and should be skipped
363
+ node.state?.attr === undefined
364
+ ) {
365
+ yield* report("pre-creating");
366
+
367
+ // stub the resource prior to resolving upstream resources or bindings if a stub is available
368
+ attr = yield* node.provider.precreate({
369
+ id,
370
+ news: node.props,
371
+ session: scopedSession,
372
+ instanceId,
373
+ });
374
+
375
+ yield* checkpoint(attr);
376
+ }
377
+
378
+ yield* report("attaching");
379
+
380
+ let bindingOutputs = yield* attachBindings({
381
+ resource,
382
+ bindings: node.bindings,
383
+ target: {
384
+ id,
385
+ props: news,
386
+ attr,
387
+ },
388
+ });
389
+
390
+ yield* report("creating");
391
+
392
+ attr = yield* node.provider.create({
393
+ id,
394
+ news,
395
+ instanceId,
396
+ bindings: bindingOutputs,
397
+ session: scopedSession,
398
+ });
399
+
400
+ yield* checkpoint(attr);
401
+
402
+ yield* report("post-attach");
403
+ bindingOutputs = yield* postAttachBindings({
404
+ resource,
405
+ bindings: node.bindings,
406
+ bindingOutputs,
407
+ target: {
408
+ id,
409
+ props: news,
410
+ attr,
411
+ },
412
+ });
413
+
414
+ yield* commit<CreatedResourceState>({
415
+ status: "created",
416
+ logicalId: id,
417
+ instanceId,
418
+ resourceType: node.resource.type,
419
+ props: news,
420
+ attr,
421
+ bindings: node.bindings.map((binding, i) => ({
422
+ ...binding,
423
+ attr: bindingOutputs[i],
424
+ })),
425
+ providerVersion: node.provider.version ?? 0,
426
+ downstream: node.downstream,
427
+ });
428
+
429
+ yield* report("created");
430
+
431
+ return attr;
432
+ } else if (node.action === "update") {
433
+ const upstream = Object.fromEntries(
434
+ yield* Effect.all(
435
+ Object.entries(Output.resolveUpstream(node.props)).map(([id]) =>
436
+ resolveUpstream(id).pipe(
437
+ Effect.map(({ upstreamAttr }) => [id, upstreamAttr]),
438
+ ),
439
+ ),
440
+ ),
441
+ );
442
+ const news = (yield* Output.evaluate(
443
+ node.props,
444
+ upstream,
445
+ )) as Record<string, any>;
446
+
447
+ const checkpoint = (attr: any) => {
448
+ if (node.state.status === "replaced") {
449
+ return commit<ReplacedResourceState>({
450
+ ...node.state,
451
+ attr,
452
+ props: news,
453
+ });
454
+ } else {
455
+ return commit<UpdatingReourceState>({
456
+ status: "updating",
457
+ logicalId: id,
458
+ instanceId,
459
+ resourceType: node.resource.type,
460
+ props: news,
271
461
  attr,
272
- phase,
273
- }: {
274
- node: Create<Resource> | Update<Resource>;
275
- attr: any;
276
- phase: "create" | "update";
277
- }) {
278
- yield* report(phase === "create" ? "creating" : "updating");
279
-
280
- let bindingOutputs = yield* attachBindings({
281
- resource,
282
- bindings: node.bindings,
283
- target: {
284
- id,
285
- props: node.news,
286
- attr,
287
- },
288
- });
289
-
290
- const output: any = yield* (
291
- phase === "create"
292
- ? node.provider.create
293
- : node.provider.update
294
- )({
295
- id,
296
- news: node.news,
297
- bindings: bindingOutputs,
298
- session: scopedSession,
299
- ...(node.action === "update"
300
- ? {
301
- output: node.output,
302
- olds: node.olds,
303
- }
304
- : {}),
305
- }).pipe(
306
- // TODO(sam): partial checkpoints
307
- // checkpoint,
308
- Effect.tap(() =>
309
- report(phase === "create" ? "created" : "updated"),
310
- ),
311
- );
312
-
313
- bindingOutputs = yield* postAttachBindings({
314
- resource,
315
- bindings: node.bindings,
316
- bindingOutputs,
317
- target: {
318
- id,
319
- props: node.news,
320
- attr,
321
- },
322
- });
323
-
324
- yield* saveState({
325
- output,
326
- bindings: node.bindings.map((binding, i) => ({
327
- ...binding,
328
- attr: bindingOutputs[i],
329
- })),
330
- });
331
-
332
- return output;
462
+ providerVersion: node.provider.version ?? 0,
463
+ bindings: node.bindings,
464
+ downstream: node.downstream,
465
+ old:
466
+ node.state.status === "updating"
467
+ ? node.state.old
468
+ : node.state,
333
469
  });
470
+ }
471
+ };
472
+
473
+ yield* checkpoint(node.state.attr);
474
+
475
+ yield* report("attaching");
476
+
477
+ let bindingOutputs = yield* attachBindings({
478
+ resource,
479
+ bindings: node.bindings,
480
+ target: {
481
+ id,
482
+ props: news,
483
+ attr: node.state.attr,
484
+ },
485
+ });
486
+
487
+ yield* report("updating");
488
+
489
+ const attr = yield* node.provider.update({
490
+ id,
491
+ news,
492
+ instanceId,
493
+ bindings: bindingOutputs,
494
+ session: scopedSession,
495
+ olds:
496
+ node.state.status === "created" ||
497
+ node.state.status === "updated" ||
498
+ node.state.status === "replaced"
499
+ ? node.state.props
500
+ : node.state.old.props,
501
+ output: node.state.attr,
502
+ });
503
+
504
+ yield* checkpoint(attr);
505
+
506
+ yield* report("post-attach");
507
+
508
+ bindingOutputs = yield* postAttachBindings({
509
+ resource,
510
+ bindings: node.bindings,
511
+ bindingOutputs,
512
+ target: {
513
+ id,
514
+ props: news,
515
+ attr,
516
+ },
517
+ });
518
+
519
+ if (node.state.status === "replaced") {
520
+ yield* commit<ReplacedResourceState>({
521
+ ...node.state,
522
+ attr,
523
+ props: news,
524
+ });
525
+ } else {
526
+ yield* commit<UpdatedResourceState>({
527
+ status: "updated",
528
+ logicalId: id,
529
+ instanceId,
530
+ resourceType: node.resource.type,
531
+ props: news,
532
+ attr,
533
+ bindings: node.bindings.map((binding, i) => ({
534
+ ...binding,
535
+ attr: bindingOutputs[i],
536
+ })),
537
+ providerVersion: node.provider.version ?? 0,
538
+ downstream: node.downstream,
539
+ });
540
+ }
334
541
 
335
- if (node.action === "noop") {
336
- return (yield* state.get(id))?.output;
337
- } else if (node.action === "create") {
338
- let attr: any;
339
- if (node.provider.precreate) {
340
- yield* Effect.logDebug("precreate", id);
341
- // stub the resource prior to resolving upstream resources or bindings if a stub is available
342
- attr = yield* node.provider.precreate({
343
- id,
344
- news: node.news,
345
- session: scopedSession,
346
- });
347
- }
348
-
349
- yield* Effect.logDebug("create", id);
350
- return yield* createOrUpdate({
351
- node,
352
- attr,
353
- phase: "create",
354
- });
355
- } else if (node.action === "update") {
356
- yield* Effect.logDebug("update", id);
357
- return yield* createOrUpdate({
358
- node,
359
- attr: node.attributes,
360
- phase: "update",
361
- });
362
- } else if (node.action === "delete") {
363
- yield* Effect.logDebug("delete", id);
364
- yield* Effect.all(
365
- node.downstream.map((dep) =>
366
- dep in plan.resources
367
- ? apply(
368
- plan.resources[
369
- dep
370
- ] as P["resources"][keyof P["resources"]],
371
- )
372
- : Effect.void,
373
- ),
374
- );
375
- yield* report("deleting");
376
-
377
- return yield* node.provider
378
- .delete({
379
- id,
380
- olds: node.olds,
381
- output: node.output,
382
- session: scopedSession,
383
- bindings: [],
384
- })
385
- .pipe(
386
- Effect.flatMap(() => state.delete(id)),
387
- Effect.tap(() => report("deleted")),
388
- );
389
- } else if (node.action === "replace") {
390
- const destroy = Effect.gen(function* () {
391
- yield* report("deleting");
392
- return yield* node.provider.delete({
393
- id,
394
- olds: node.olds,
395
- output: node.output,
396
- session: scopedSession,
397
- bindings: [],
398
- });
399
- });
400
- const create = Effect.gen(function* () {
401
- yield* report("creating");
402
- return yield* (
403
- node.provider
404
- .create({
405
- id,
406
- news: node.news,
407
- // TODO(sam): these need to only include attach actions
408
- bindings: yield* attachBindings({
409
- resource,
410
- bindings: node.bindings,
411
- target: {
412
- id,
413
- props: node.news,
414
- attr: node.attributes,
415
- },
416
- }),
417
- session: scopedSession,
418
- })
419
- // TODO(sam): delete and create will conflict here, we need to extend the state store for replace
420
- .pipe(
421
- checkpoint,
422
- Effect.tap(() => report("created")),
423
- )
424
- );
425
- });
426
- if (!node.deleteFirst) {
427
- yield* destroy;
428
- return outputs;
429
- } else {
430
- yield* destroy;
431
- return yield* create;
432
- }
433
- }
434
- }),
435
- ));
436
- }) as Effect.Effect<any, never, never>;
542
+ yield* report("updated");
543
+
544
+ return attr;
545
+ } else if (node.action === "replace") {
546
+ if (node.state.status === "replaced") {
547
+ // we've already created the replacement resource, return the output
548
+ return node.state.attr;
549
+ }
550
+ let state: ReplacingResourceState;
551
+ if (node.state.status !== "replacing") {
552
+ yield* commit<ReplacingResourceState>(
553
+ (state = {
554
+ status: "replacing",
555
+ logicalId: id,
556
+ instanceId,
557
+ resourceType: node.resource.type,
558
+ props: node.props,
559
+ attr: node.state.attr,
560
+ providerVersion: node.provider.version ?? 0,
561
+ deleteFirst: node.deleteFirst,
562
+ old: node.state,
563
+ downstream: node.downstream,
564
+ }),
565
+ );
566
+ } else {
567
+ state = node.state;
568
+ }
569
+ const upstream = Object.fromEntries(
570
+ yield* Effect.all(
571
+ Object.entries(Output.resolveUpstream(node.props)).map(([id]) =>
572
+ resolveUpstream(id).pipe(
573
+ Effect.map(({ upstreamAttr }) => [id, upstreamAttr]),
574
+ ),
575
+ ),
576
+ ),
577
+ );
578
+ const news = (yield* Output.evaluate(
579
+ node.props,
580
+ upstream,
581
+ )) as Record<string, any>;
582
+
583
+ const checkpoint = <
584
+ S extends ReplacingResourceState | ReplacedResourceState,
585
+ >({
586
+ status,
587
+ attr,
588
+ bindings,
589
+ }: Pick<S, "status" | "attr" | "bindings">) =>
590
+ commit<S>({
591
+ status,
592
+ logicalId: id,
593
+ instanceId,
594
+ resourceType: node.resource.type,
595
+ props: news,
596
+ attr,
597
+ providerVersion: node.provider.version ?? 0,
598
+ bindings: bindings ?? node.bindings,
599
+ downstream: node.downstream,
600
+ old: state.old,
601
+ deleteFirst: node.deleteFirst,
602
+ } as S);
603
+
604
+ let attr: any;
605
+ if (
606
+ node.provider.precreate &&
607
+ // pre-create is only designed to ensure the resource exists, if we have state.attr, then it already exists and should be skipped
608
+ node.state?.attr === undefined
609
+ ) {
610
+ yield* report("pre-creating");
611
+
612
+ // stub the resource prior to resolving upstream resources or bindings if a stub is available
613
+ attr = yield* node.provider.precreate({
614
+ id,
615
+ news: node.props,
616
+ session: scopedSession,
617
+ instanceId,
618
+ });
619
+
620
+ yield* checkpoint({
621
+ status: "replacing",
622
+ attr,
623
+ });
624
+ }
437
625
 
438
- const nodes = [
439
- ...Object.entries(plan.resources),
440
- ...Object.entries(plan.deletions),
441
- ];
626
+ yield* report("attaching");
627
+
628
+ let bindingOutputs = yield* attachBindings({
629
+ resource,
630
+ bindings: node.bindings,
631
+ target: {
632
+ id,
633
+ props: news,
634
+ attr,
635
+ },
636
+ });
637
+
638
+ yield* report("creating replacement");
639
+
640
+ attr = yield* node.provider.create({
641
+ id,
642
+ news,
643
+ instanceId,
644
+ bindings: bindingOutputs,
645
+ session: scopedSession,
646
+ });
647
+
648
+ yield* checkpoint({
649
+ status: "replacing",
650
+ attr,
651
+ });
652
+
653
+ yield* report("post-attach");
654
+
655
+ bindingOutputs = yield* postAttachBindings({
656
+ resource,
657
+ bindings: node.bindings,
658
+ bindingOutputs,
659
+ target: {
660
+ id,
661
+ props: news,
662
+ attr,
663
+ },
664
+ });
665
+
666
+ yield* checkpoint<ReplacedResourceState>({
667
+ status: "replaced",
668
+ attr,
669
+ bindings: node.bindings.map((binding, i) => ({
670
+ ...binding,
671
+ attr: bindingOutputs[i],
672
+ })),
673
+ });
674
+
675
+ yield* report("created");
676
+ return attr;
677
+ }
678
+ // @ts-expect-error
679
+ return yield* Effect.dieMessage(`Unknown action: ${node.action}`);
680
+ }),
681
+ ));
682
+ }) as Effect.Effect<any, never, never>;
683
+
684
+ return Object.fromEntries(
685
+ yield* Effect.all(
686
+ Object.entries(plan.resources).map(
687
+ Effect.fn(function* ([id, node]) {
688
+ return [id, yield* apply(node)];
689
+ }),
690
+ ),
691
+ ),
692
+ );
693
+ });
694
+
695
+ const collectGarbage = Effect.fnUntraced(function* (
696
+ plan: IPlan,
697
+ session: PlanStatusSession,
698
+ ) {
699
+ const state = yield* State;
700
+ const app = yield* App;
701
+
702
+ const deletions: {
703
+ [logicalId in string]: Effect.Effect<void, StateStoreError, never>;
704
+ } = {};
705
+
706
+ // delete all replaced resources
707
+ const replacedResources = yield* state.getReplacedResources({
708
+ stack: app.name,
709
+ stage: app.stage,
710
+ });
711
+
712
+ const deletionGraph = {
713
+ ...plan.deletions,
714
+ ...Object.fromEntries(
715
+ replacedResources.map((replaced) => [replaced.logicalId, replaced]),
716
+ ),
717
+ };
718
+
719
+ const deleteResource: (
720
+ node: Delete<Resource> | ReplacedResourceState,
721
+ ) => Effect.Effect<void, StateStoreError, never> = Effect.fnUntraced(
722
+ function* (node: Delete<Resource> | ReplacedResourceState) {
723
+ const isDeleteNode = (
724
+ node: Delete<Resource> | ReplacedResourceState,
725
+ ): node is Delete<Resource> => "action" in node;
726
+
727
+ const {
728
+ logicalId,
729
+ resourceType,
730
+ instanceId,
731
+ downstream,
732
+ props,
733
+ attr,
734
+ provider,
735
+ } = isDeleteNode(node)
736
+ ? {
737
+ logicalId: node.resource.id,
738
+ resourceType: node.resource.type,
739
+ instanceId: node.state.instanceId,
740
+ downstream: node.downstream,
741
+ props: node.state.props,
742
+ attr: node.state.attr,
743
+ provider: node.provider,
744
+ }
745
+ : {
746
+ logicalId: node.logicalId,
747
+ resourceType: node.old.resourceType,
748
+ instanceId: node.old.instanceId,
749
+ downstream: node.old.downstream,
750
+ props: node.old.props,
751
+ attr: node.old.attr,
752
+ provider: yield* getProviderByType(node.old.resourceType),
753
+ };
442
754
 
443
- const resources: any = Object.fromEntries(
755
+ const commit = <State extends ResourceState>(value: State) =>
756
+ state.set({
757
+ stack: app.name,
758
+ stage: app.stage,
759
+ resourceId: logicalId,
760
+ value,
761
+ });
762
+
763
+ const report = (status: ApplyStatus) =>
764
+ session.emit({
765
+ kind: "status-change",
766
+ id: logicalId,
767
+ type: resourceType,
768
+ status,
769
+ });
770
+
771
+ const scopedSession = {
772
+ ...session,
773
+ note: (note: string) =>
774
+ session.emit({
775
+ id: logicalId,
776
+ kind: "annotate",
777
+ message: note,
778
+ }),
779
+ } satisfies ScopedPlanStatusSession;
780
+
781
+ return yield* (deletions[logicalId] ??= yield* Effect.cached(
782
+ Effect.gen(function* () {
444
783
  yield* Effect.all(
445
- nodes.map(
446
- Effect.fn(function* ([id, node]) {
447
- return [id, yield* apply(node as CRUD)];
448
- }),
784
+ downstream.map((dep) =>
785
+ dep in deletionGraph
786
+ ? deleteResource(deletionGraph[dep] as Delete<Resource>)
787
+ : Effect.void,
449
788
  ),
450
- ),
451
- );
452
- yield* done();
453
- if (Object.keys(plan.resources).length === 0) {
454
- // all resources are deleted, return undefined
455
- return undefined;
456
- }
457
- return resources;
458
- }),
459
- ),
460
- ) as Effect.Effect<
461
- "update" extends P["phase"]
462
- ?
463
- | {
464
- [id in keyof P["resources"]]: P["resources"][id] extends
465
- | Delete<Resource>
466
- | undefined
467
- | never
468
- ? never
469
- : Simplify<P["resources"][id]["resource"]["attr"]>;
470
- }
471
- // union distribution isn't happening, so we gotta add this additional void here just in case
472
- | ("destroy" extends P["phase"] ? void : never)
473
- : void,
474
- Err | PlanRejected,
475
- Req
476
- >;
789
+ );
790
+
791
+ yield* report("deleting");
792
+
793
+ if (isDeleteNode(node)) {
794
+ yield* commit<DeletingResourceState>({
795
+ status: "deleting",
796
+ logicalId,
797
+ instanceId,
798
+ resourceType,
799
+ props,
800
+ attr,
801
+ downstream,
802
+ providerVersion: provider.version ?? 0,
803
+ bindings: node.bindings,
804
+ });
805
+ }
806
+
807
+ yield* provider.delete({
808
+ id: logicalId,
809
+ instanceId,
810
+ olds: props as never,
811
+ output: attr,
812
+ session: scopedSession,
813
+ bindings: [],
814
+ });
815
+
816
+ if (isDeleteNode(node)) {
817
+ // TODO(sam): should we commit a tombstone instead? and then clean up tombstones after all deletions are complete?
818
+ yield* state.delete({
819
+ stack: app.name,
820
+ stage: app.stage,
821
+ resourceId: logicalId,
822
+ });
823
+ yield* report("deleted");
824
+ } else {
825
+ yield* commit<CreatedResourceState>({
826
+ status: "created",
827
+ logicalId,
828
+ instanceId,
829
+ resourceType,
830
+ props: node.props,
831
+ attr: node.attr,
832
+ providerVersion: provider.version ?? 0,
833
+ downstream: node.downstream,
834
+ bindings: node.bindings,
835
+ });
836
+ yield* report("replaced");
837
+ }
838
+ }),
839
+ ));
840
+ },
841
+ );
842
+
843
+ yield* Effect.all(
844
+ Object.values(deletionGraph)
845
+ .filter((node) => node !== undefined)
846
+ .map(deleteResource),
847
+ );
848
+ });