alchemy 0.12.21 → 0.14.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 (319) hide show
  1. package/lib/ai/client.d.ts.map +1 -1
  2. package/lib/ai/client.js +3 -5
  3. package/lib/ai/client.js.map +1 -1
  4. package/lib/alchemy.d.ts +2 -1
  5. package/lib/alchemy.d.ts.map +1 -1
  6. package/lib/alchemy.js +87 -92
  7. package/lib/alchemy.js.map +1 -1
  8. package/lib/apply.d.ts.map +1 -1
  9. package/lib/apply.js +6 -1
  10. package/lib/apply.js.map +1 -1
  11. package/lib/aws/bucket.d.ts.map +1 -1
  12. package/lib/aws/bucket.js +55 -57
  13. package/lib/aws/bucket.js.map +1 -1
  14. package/lib/aws/function.d.ts.map +1 -1
  15. package/lib/aws/function.js +250 -252
  16. package/lib/aws/function.js.map +1 -1
  17. package/lib/aws/policy-attachment.d.ts.map +1 -1
  18. package/lib/aws/policy-attachment.js +4 -6
  19. package/lib/aws/policy-attachment.js.map +1 -1
  20. package/lib/aws/policy.d.ts.map +1 -1
  21. package/lib/aws/policy.js +63 -65
  22. package/lib/aws/policy.js.map +1 -1
  23. package/lib/aws/queue.js +88 -90
  24. package/lib/aws/queue.js.map +1 -1
  25. package/lib/aws/table.d.ts.map +1 -1
  26. package/lib/aws/table.js +95 -99
  27. package/lib/aws/table.js.map +1 -1
  28. package/lib/cloudflare/account-api-token.d.ts +1 -1
  29. package/lib/cloudflare/account-api-token.d.ts.map +1 -1
  30. package/lib/cloudflare/account-api-token.js +0 -1
  31. package/lib/cloudflare/account-api-token.js.map +1 -1
  32. package/lib/cloudflare/ai-gateway.js.map +1 -1
  33. package/lib/cloudflare/ai.d.ts +1 -1
  34. package/lib/cloudflare/ai.d.ts.map +1 -1
  35. package/lib/cloudflare/ai.js.map +1 -1
  36. package/lib/cloudflare/api.d.ts +5 -5
  37. package/lib/cloudflare/api.d.ts.map +1 -1
  38. package/lib/cloudflare/api.js +52 -15
  39. package/lib/cloudflare/api.js.map +1 -1
  40. package/lib/cloudflare/auth.d.ts +24 -55
  41. package/lib/cloudflare/auth.d.ts.map +1 -1
  42. package/lib/cloudflare/auth.js +15 -72
  43. package/lib/cloudflare/auth.js.map +1 -1
  44. package/lib/cloudflare/bucket.d.ts +2 -2
  45. package/lib/cloudflare/bucket.d.ts.map +1 -1
  46. package/lib/cloudflare/bucket.js +22 -24
  47. package/lib/cloudflare/bucket.js.map +1 -1
  48. package/lib/cloudflare/bundle/alias-plugin.d.ts +6 -0
  49. package/lib/cloudflare/bundle/alias-plugin.d.ts.map +1 -0
  50. package/lib/cloudflare/bundle/alias-plugin.js +36 -0
  51. package/lib/cloudflare/bundle/alias-plugin.js.map +1 -0
  52. package/lib/cloudflare/bundle/build-failures.d.ts +12 -0
  53. package/lib/cloudflare/bundle/build-failures.d.ts.map +1 -0
  54. package/lib/cloudflare/bundle/build-failures.js +45 -0
  55. package/lib/cloudflare/bundle/build-failures.js.map +1 -0
  56. package/lib/cloudflare/bundle/bundle-worker.d.ts +7 -0
  57. package/lib/cloudflare/bundle/bundle-worker.d.ts.map +1 -0
  58. package/lib/cloudflare/bundle/bundle-worker.js +69 -0
  59. package/lib/cloudflare/bundle/bundle-worker.js.map +1 -0
  60. package/lib/cloudflare/bundle/external.d.ts +1 -0
  61. package/lib/cloudflare/bundle/external.d.ts.map +1 -1
  62. package/lib/cloudflare/bundle/external.js +8 -1
  63. package/lib/cloudflare/bundle/external.js.map +1 -1
  64. package/lib/cloudflare/bundle/local-dev-cloudflare-shim.d.ts +1 -1
  65. package/lib/cloudflare/bundle/local-dev-cloudflare-shim.d.ts.map +1 -1
  66. package/lib/cloudflare/bundle/local-dev-cloudflare-shim.js +6 -6
  67. package/lib/cloudflare/bundle/local-dev-cloudflare-shim.js.map +1 -1
  68. package/lib/cloudflare/bundle/nodejs-compat-mode.d.ts +17 -0
  69. package/lib/cloudflare/bundle/nodejs-compat-mode.d.ts.map +1 -0
  70. package/lib/cloudflare/bundle/nodejs-compat-mode.js +27 -0
  71. package/lib/cloudflare/bundle/nodejs-compat-mode.js.map +1 -0
  72. package/lib/cloudflare/bundle/{hybrid-nodejs-compat.d.ts → nodejs-compat.d.ts} +2 -2
  73. package/lib/cloudflare/bundle/nodejs-compat.d.ts.map +1 -0
  74. package/lib/cloudflare/bundle/{hybrid-nodejs-compat.js → nodejs-compat.js} +2 -2
  75. package/lib/cloudflare/bundle/nodejs-compat.js.map +1 -0
  76. package/lib/cloudflare/custom-domain.d.ts.map +1 -1
  77. package/lib/cloudflare/custom-domain.js +3 -5
  78. package/lib/cloudflare/custom-domain.js.map +1 -1
  79. package/lib/cloudflare/d1-database.d.ts +1 -1
  80. package/lib/cloudflare/d1-database.d.ts.map +1 -1
  81. package/lib/cloudflare/d1-database.js +70 -72
  82. package/lib/cloudflare/d1-database.js.map +1 -1
  83. package/lib/cloudflare/d1-migrations.d.ts +1 -1
  84. package/lib/cloudflare/d1-migrations.d.ts.map +1 -1
  85. package/lib/cloudflare/d1-migrations.js +2 -3
  86. package/lib/cloudflare/d1-migrations.js.map +1 -1
  87. package/lib/cloudflare/dns-records.d.ts.map +1 -1
  88. package/lib/cloudflare/dns-records.js +2 -4
  89. package/lib/cloudflare/dns-records.js.map +1 -1
  90. package/lib/cloudflare/kv-namespace.d.ts +1 -1
  91. package/lib/cloudflare/kv-namespace.d.ts.map +1 -1
  92. package/lib/cloudflare/kv-namespace.js +24 -27
  93. package/lib/cloudflare/kv-namespace.js.map +1 -1
  94. package/lib/cloudflare/pipeline.d.ts +1 -1
  95. package/lib/cloudflare/pipeline.d.ts.map +1 -1
  96. package/lib/cloudflare/pipeline.js +32 -34
  97. package/lib/cloudflare/pipeline.js.map +1 -1
  98. package/lib/cloudflare/queue-consumer.d.ts +1 -1
  99. package/lib/cloudflare/queue-consumer.d.ts.map +1 -1
  100. package/lib/cloudflare/queue-consumer.js +27 -33
  101. package/lib/cloudflare/queue-consumer.js.map +1 -1
  102. package/lib/cloudflare/queue.d.ts +1 -1
  103. package/lib/cloudflare/queue.d.ts.map +1 -1
  104. package/lib/cloudflare/queue.js +36 -38
  105. package/lib/cloudflare/queue.js.map +1 -1
  106. package/lib/cloudflare/r2-rest-state-store.js +4 -8
  107. package/lib/cloudflare/r2-rest-state-store.js.map +1 -1
  108. package/lib/cloudflare/types.d.ts.map +1 -1
  109. package/lib/cloudflare/types.js +1 -3
  110. package/lib/cloudflare/types.js.map +1 -1
  111. package/lib/cloudflare/user.d.ts +51 -0
  112. package/lib/cloudflare/user.d.ts.map +1 -0
  113. package/lib/cloudflare/user.js +62 -0
  114. package/lib/cloudflare/user.js.map +1 -0
  115. package/lib/cloudflare/vectorize-index.d.ts.map +1 -1
  116. package/lib/cloudflare/vectorize-index.js +37 -39
  117. package/lib/cloudflare/vectorize-index.js.map +1 -1
  118. package/lib/cloudflare/vectorize-metadata-index.d.ts +1 -1
  119. package/lib/cloudflare/vectorize-metadata-index.d.ts.map +1 -1
  120. package/lib/cloudflare/vectorize-metadata-index.js +10 -13
  121. package/lib/cloudflare/vectorize-metadata-index.js.map +1 -1
  122. package/lib/cloudflare/worker.d.ts +18 -3
  123. package/lib/cloudflare/worker.d.ts.map +1 -1
  124. package/lib/cloudflare/worker.js +11 -54
  125. package/lib/cloudflare/worker.js.map +1 -1
  126. package/lib/cloudflare/workflow.d.ts +1 -1
  127. package/lib/cloudflare/workflow.d.ts.map +1 -1
  128. package/lib/cloudflare/workflow.js.map +1 -1
  129. package/lib/cloudflare/zone.d.ts.map +1 -1
  130. package/lib/cloudflare/zone.js +48 -50
  131. package/lib/cloudflare/zone.js.map +1 -1
  132. package/lib/dns/godaddy.js +1 -1
  133. package/lib/dns/import-dns.js +1 -1
  134. package/lib/dns/import-dns.js.map +1 -1
  135. package/lib/env.js +7 -9
  136. package/lib/env.js.map +1 -1
  137. package/lib/esbuild/bundle.d.ts.map +1 -1
  138. package/lib/esbuild/bundle.js +8 -11
  139. package/lib/esbuild/bundle.js.map +1 -1
  140. package/lib/fs/copy-file.d.ts.map +1 -1
  141. package/lib/fs/copy-file.js +27 -29
  142. package/lib/fs/copy-file.js.map +1 -1
  143. package/lib/fs/file.d.ts +1 -1
  144. package/lib/fs/file.d.ts.map +1 -1
  145. package/lib/fs/file.js +1 -1
  146. package/lib/fs/file.js.map +1 -1
  147. package/lib/fs/folder.d.ts.map +1 -1
  148. package/lib/fs/folder.js +1 -3
  149. package/lib/fs/folder.js.map +1 -1
  150. package/lib/github/client.d.ts.map +1 -1
  151. package/lib/github/client.js +3 -5
  152. package/lib/github/client.js.map +1 -1
  153. package/lib/github/repository-environment.d.ts.map +1 -1
  154. package/lib/github/repository-environment.js +173 -176
  155. package/lib/github/repository-environment.js.map +1 -1
  156. package/lib/github/secret.d.ts.map +1 -1
  157. package/lib/github/secret.js +97 -99
  158. package/lib/github/secret.js.map +1 -1
  159. package/lib/internal/docs/providers.d.ts.map +1 -1
  160. package/lib/internal/docs/providers.js +5 -7
  161. package/lib/internal/docs/providers.js.map +1 -1
  162. package/lib/neon/api.js +1 -2
  163. package/lib/neon/api.js.map +1 -1
  164. package/lib/neon/project.js +2 -2
  165. package/lib/neon/project.js.map +1 -1
  166. package/lib/os/exec.d.ts +5 -0
  167. package/lib/os/exec.d.ts.map +1 -1
  168. package/lib/os/exec.js +88 -56
  169. package/lib/os/exec.js.map +1 -1
  170. package/lib/scope.d.ts +3 -0
  171. package/lib/scope.d.ts.map +1 -1
  172. package/lib/scope.js +10 -3
  173. package/lib/scope.js.map +1 -1
  174. package/lib/secret.d.ts.map +1 -1
  175. package/lib/secret.js +1 -3
  176. package/lib/secret.js.map +1 -1
  177. package/lib/serde.d.ts.map +1 -1
  178. package/lib/serde.js +13 -15
  179. package/lib/serde.js.map +1 -1
  180. package/lib/stripe/price.d.ts.map +1 -1
  181. package/lib/stripe/price.js +69 -73
  182. package/lib/stripe/price.js.map +1 -1
  183. package/lib/stripe/product.d.ts.map +1 -1
  184. package/lib/stripe/product.js +50 -52
  185. package/lib/stripe/product.js.map +1 -1
  186. package/lib/stripe/webhook.d.ts.map +1 -1
  187. package/lib/stripe/webhook.js +51 -53
  188. package/lib/stripe/webhook.js.map +1 -1
  189. package/lib/test/bun.d.ts +1 -1
  190. package/lib/test/bun.d.ts.map +1 -1
  191. package/lib/test/bun.js +3 -8
  192. package/lib/test/bun.js.map +1 -1
  193. package/lib/test/prune.d.ts.map +1 -1
  194. package/lib/test/prune.js.map +1 -1
  195. package/lib/util/dedent.js +4 -4
  196. package/lib/util/dedent.js.map +1 -1
  197. package/lib/web/astro.js +1 -1
  198. package/lib/web/shadcn.js +4 -4
  199. package/lib/web/shadcn.js.map +1 -1
  200. package/lib/web/tailwind.d.ts.map +1 -1
  201. package/lib/web/tailwind.js +0 -1
  202. package/lib/web/tailwind.js.map +1 -1
  203. package/lib/web/vite.js +3 -3
  204. package/lib/web/vitepress/home-page.d.ts.map +1 -1
  205. package/lib/web/vitepress/home-page.js +6 -8
  206. package/lib/web/vitepress/home-page.js.map +1 -1
  207. package/lib/web/vitepress/process-front-matter-files.js +1 -1
  208. package/lib/web/vitepress/process-front-matter-files.js.map +1 -1
  209. package/lib/web/vitepress/vitepress.js +2 -2
  210. package/lib/web/vitepress/vitepress.js.map +1 -1
  211. package/package.json +4 -1
  212. package/src/ai/astro-file.ts +4 -4
  213. package/src/ai/client.ts +4 -5
  214. package/src/ai/css-file.ts +4 -4
  215. package/src/ai/document.ts +3 -3
  216. package/src/ai/html-file.ts +4 -4
  217. package/src/ai/json-file.ts +1 -1
  218. package/src/ai/typescript-file.ts +4 -4
  219. package/src/ai/vue-file.ts +4 -4
  220. package/src/ai/yaml-file.ts +1 -1
  221. package/src/alchemy.ts +111 -104
  222. package/src/apply.ts +13 -6
  223. package/src/aws/bucket.ts +72 -75
  224. package/src/aws/function.ts +296 -297
  225. package/src/aws/oidc/github-oidc-provider.ts +1 -1
  226. package/src/aws/oidc/oidc-provider.ts +15 -15
  227. package/src/aws/policy-attachment.ts +10 -11
  228. package/src/aws/policy.ts +91 -94
  229. package/src/aws/queue.ts +111 -111
  230. package/src/aws/role.ts +24 -24
  231. package/src/aws/ses.ts +17 -17
  232. package/src/aws/table.ts +114 -115
  233. package/src/cloudflare/account-api-token.ts +11 -11
  234. package/src/cloudflare/ai-gateway.ts +4 -4
  235. package/src/cloudflare/ai.ts +1 -1
  236. package/src/cloudflare/api-error.ts +1 -1
  237. package/src/cloudflare/api.ts +70 -25
  238. package/src/cloudflare/assets.ts +4 -4
  239. package/src/cloudflare/auth.ts +49 -164
  240. package/src/cloudflare/bucket.ts +62 -63
  241. package/src/cloudflare/bundle/alias-plugin.ts +48 -0
  242. package/src/cloudflare/bundle/build-failures.ts +55 -0
  243. package/src/cloudflare/bundle/bundle-worker.ts +89 -0
  244. package/src/cloudflare/bundle/external.ts +9 -1
  245. package/src/cloudflare/bundle/local-dev-cloudflare-shim.ts +7 -6
  246. package/src/cloudflare/bundle/nodejs-compat-mode.ts +49 -0
  247. package/src/cloudflare/bundle/{hybrid-nodejs-compat.ts → nodejs-compat.ts} +10 -10
  248. package/src/cloudflare/custom-domain.ts +27 -28
  249. package/src/cloudflare/d1-database.ts +101 -102
  250. package/src/cloudflare/d1-migrations.ts +10 -10
  251. package/src/cloudflare/dns-records.ts +23 -24
  252. package/src/cloudflare/durable-object-namespace.ts +1 -1
  253. package/src/cloudflare/event-source.ts +1 -1
  254. package/src/cloudflare/hyperdrive.ts +3 -3
  255. package/src/cloudflare/kv-namespace.ts +36 -37
  256. package/src/cloudflare/nuxt.ts +1 -1
  257. package/src/cloudflare/permission-groups.ts +7 -7
  258. package/src/cloudflare/pipeline.ts +55 -56
  259. package/src/cloudflare/queue-consumer.ts +46 -53
  260. package/src/cloudflare/queue.ts +56 -57
  261. package/src/cloudflare/r2-rest-state-store.ts +11 -11
  262. package/src/cloudflare/redwood.ts +1 -1
  263. package/src/cloudflare/tanstack-start.ts +1 -1
  264. package/src/cloudflare/types.ts +1 -2
  265. package/src/cloudflare/user.ts +142 -0
  266. package/src/cloudflare/vectorize-index.ts +60 -61
  267. package/src/cloudflare/vectorize-metadata-index.ts +25 -25
  268. package/src/cloudflare/vite.ts +1 -1
  269. package/src/cloudflare/website.ts +3 -3
  270. package/src/cloudflare/worker.ts +83 -108
  271. package/src/cloudflare/workflow.ts +4 -4
  272. package/src/cloudflare/wrangler.json.ts +4 -4
  273. package/src/cloudflare/zone.ts +69 -70
  274. package/src/destroy.ts +4 -4
  275. package/src/dns/godaddy.ts +1 -1
  276. package/src/dns/import-dns.ts +5 -5
  277. package/src/encrypt.ts +6 -6
  278. package/src/env.ts +10 -10
  279. package/src/esbuild/bundle.ts +14 -15
  280. package/src/fs/copy-file.ts +35 -36
  281. package/src/fs/file-system-state-store.ts +4 -4
  282. package/src/fs/file.ts +9 -8
  283. package/src/fs/folder.ts +6 -7
  284. package/src/github/client.ts +15 -14
  285. package/src/github/repository-environment.ts +206 -210
  286. package/src/github/secret.ts +111 -116
  287. package/src/internal/docs/providers.ts +16 -17
  288. package/src/neon/api-error.ts +1 -1
  289. package/src/neon/api.ts +6 -6
  290. package/src/neon/project.ts +18 -17
  291. package/src/os/exec.ts +107 -66
  292. package/src/resource.ts +3 -3
  293. package/src/scope.ts +14 -4
  294. package/src/secret.ts +2 -3
  295. package/src/serde.ts +30 -22
  296. package/src/stripe/price.ts +74 -77
  297. package/src/stripe/product.ts +55 -56
  298. package/src/stripe/webhook.ts +56 -57
  299. package/src/test/bun.ts +9 -13
  300. package/src/test/prune.ts +4 -4
  301. package/src/util/dedent.ts +4 -4
  302. package/src/web/astro.ts +10 -10
  303. package/src/web/shadcn-component.ts +4 -4
  304. package/src/web/shadcn.ts +10 -10
  305. package/src/web/tailwind.ts +2 -3
  306. package/src/web/vite.ts +11 -11
  307. package/src/web/vitepress/config.ts +2 -2
  308. package/src/web/vitepress/custom-theme.ts +7 -7
  309. package/src/web/vitepress/dependencies.ts +4 -4
  310. package/src/web/vitepress/home-page.ts +8 -9
  311. package/src/web/vitepress/process-front-matter-files.ts +5 -5
  312. package/src/web/vitepress/vitepress.ts +7 -7
  313. package/lib/cloudflare/account-id.d.ts +0 -6
  314. package/lib/cloudflare/account-id.d.ts.map +0 -1
  315. package/lib/cloudflare/account-id.js +0 -6
  316. package/lib/cloudflare/account-id.js.map +0 -1
  317. package/lib/cloudflare/bundle/hybrid-nodejs-compat.d.ts.map +0 -1
  318. package/lib/cloudflare/bundle/hybrid-nodejs-compat.js.map +0 -1
  319. package/src/cloudflare/account-id.ts +0 -12
@@ -147,7 +147,7 @@ export const VueFile = Resource(
147
147
  async function (
148
148
  this: Context<VueFile>,
149
149
  id: string,
150
- props: VueFileProps
150
+ props: VueFileProps,
151
151
  ): Promise<VueFile> {
152
152
  // Handle deletion phase
153
153
  if (this.phase === "delete") {
@@ -189,7 +189,7 @@ export const VueFile = Resource(
189
189
 
190
190
  if (retryResult.error) {
191
191
  throw new Error(
192
- `Failed to generate valid Vue code: ${retryResult.error}`
192
+ `Failed to generate valid Vue code: ${retryResult.error}`,
193
193
  );
194
194
  }
195
195
 
@@ -206,7 +206,7 @@ export const VueFile = Resource(
206
206
  createdAt: Date.now(),
207
207
  updatedAt: Date.now(),
208
208
  });
209
- }
209
+ },
210
210
  );
211
211
 
212
212
  /**
@@ -217,7 +217,7 @@ export const VueFile = Resource(
217
217
  * @returns The extracted Vue code or error message
218
218
  */
219
219
  async function extractVueCode(
220
- text: string
220
+ text: string,
221
221
  ): Promise<{ code: string; error?: string }> {
222
222
  const vueCodeRegex = /```vue\s*([\s\S]*?)```/g;
223
223
  const matches = Array.from(text.matchAll(vueCodeRegex));
@@ -298,7 +298,7 @@ export const YAMLFile = Resource("ai::YAMLFile", async function <
298
298
  * @returns The extracted YAML or error message
299
299
  */
300
300
  async function extractYAMLContent(
301
- text: string
301
+ text: string,
302
302
  ): Promise<{ content: string; error?: string }> {
303
303
  // Check for yaml or yml fence blocks
304
304
  const yamlCodeRegex = /```(yaml|yml)\s*([\s\S]*?)```/g;
package/src/alchemy.ts CHANGED
@@ -98,10 +98,12 @@ async function _alchemy(
98
98
  ): Promise<Scope | string | never> {
99
99
  if (typeof args[0] === "string") {
100
100
  const [appName, options] = args as [string, AlchemyOptions?];
101
+ const phase = options?.phase ?? "up";
101
102
  const root = new Scope({
102
103
  ...options,
103
104
  appName,
104
105
  stage: options?.stage,
106
+ phase,
105
107
  });
106
108
  root.enter();
107
109
  if (options?.phase === "destroy") {
@@ -109,118 +111,123 @@ async function _alchemy(
109
111
  return process.exit(0);
110
112
  }
111
113
  return root;
112
- } else {
113
- const [template, ...values] = args;
114
- const [, secondLine] = template[0].split("\n");
115
- const leadingSpaces = secondLine
116
- ? secondLine.match(/^(\s*)/)?.[1]?.length || 0
117
- : 0;
118
- const indent = " ".repeat(leadingSpaces);
114
+ }
115
+ const [template, ...values] = args;
116
+ const [, secondLine] = template[0].split("\n");
117
+ const leadingSpaces = secondLine
118
+ ? secondLine.match(/^(\s*)/)?.[1]?.length || 0
119
+ : 0;
120
+ const indent = " ".repeat(leadingSpaces);
119
121
 
120
- const [{ isFileRef }, { isFileCollection }] = await Promise.all([
121
- import("./fs/file-ref"),
122
- import("./fs/file-collection"),
123
- ]);
122
+ const [{ isFileRef }, { isFileCollection }] = await Promise.all([
123
+ import("./fs/file-ref.js"),
124
+ import("./fs/file-collection.js"),
125
+ ]);
124
126
 
125
- const appendices: Record<string, string> = {};
127
+ const appendices: Record<string, string> = {};
126
128
 
127
- const stringValues = await Promise.all(
128
- values.map(async function resolve(value): Promise<string> {
129
- if (typeof value === "string") {
130
- return indent + value;
131
- } else if (value === null) {
132
- return "null";
133
- } else if (value === undefined) {
134
- return "undefined";
135
- } else if (
136
- typeof value === "number" ||
137
- typeof value === "boolean" ||
138
- typeof value === "bigint"
139
- ) {
140
- return value.toString();
141
- } else if (value instanceof Promise) {
142
- return resolve(await value);
143
- } else if (isFileRef(value)) {
144
- if (!(value.path in appendices)) {
145
- appendices[value.path] = await fs.readFile(value.path, "utf-8");
146
- }
129
+ const stringValues = await Promise.all(
130
+ values.map(async function resolve(value): Promise<string> {
131
+ if (typeof value === "string") {
132
+ return indent + value;
133
+ }
134
+ if (value === null) {
135
+ return "null";
136
+ }
137
+ if (value === undefined) {
138
+ return "undefined";
139
+ }
140
+ if (
141
+ typeof value === "number" ||
142
+ typeof value === "boolean" ||
143
+ typeof value === "bigint"
144
+ ) {
145
+ return value.toString();
146
+ }
147
+ if (value instanceof Promise) {
148
+ return resolve(await value);
149
+ }
150
+ if (isFileRef(value)) {
151
+ if (!(value.path in appendices)) {
152
+ appendices[value.path] = await fs.readFile(value.path, "utf-8");
153
+ }
154
+ return `[${path.basename(value.path)}](${value.path})`;
155
+ }
156
+ if (isFileCollection(value)) {
157
+ return Object.entries(value.files)
158
+ .map(([filePath, content]) => {
159
+ appendices[filePath] = content;
160
+ return `[${path.basename(filePath)}](${filePath})`;
161
+ })
162
+ .join("\n\n");
163
+ }
164
+ if (Array.isArray(value)) {
165
+ return (
166
+ await Promise.all(
167
+ value.map(async (value, i) => `${i}. ${await resolve(value)}`),
168
+ )
169
+ ).join("\n");
170
+ }
171
+ if (typeof value === "object" && typeof value.path === "string") {
172
+ if (typeof value.content === "string") {
173
+ appendices[value.path] = value.content;
147
174
  return `[${path.basename(value.path)}](${value.path})`;
148
- } else if (isFileCollection(value)) {
149
- return Object.entries(value.files)
150
- .map(([filePath, content]) => {
151
- appendices[filePath] = content;
152
- return `[${path.basename(filePath)}](${filePath})`;
153
- })
154
- .join("\n\n");
155
- } else if (Array.isArray(value)) {
156
- return (
157
- await Promise.all(
158
- value.map(async (value, i) => `${i}. ${await resolve(value)}`)
159
- )
160
- ).join("\n");
161
- } else if (
162
- typeof value === "object" &&
163
- typeof value.path === "string"
164
- ) {
165
- if (typeof value.content === "string") {
166
- appendices[value.path] = value.content;
167
- return `[${path.basename(value.path)}](${value.path})`;
168
- } else {
169
- appendices[value.path] = await fs.readFile(value.path, "utf-8");
170
- return `[${path.basename(value.path)}](${value.path})`;
171
- }
172
- } else if (typeof value === "object") {
173
- return (
174
- await Promise.all(
175
- Object.entries(value).map(async ([key, value]) => {
176
- return `* ${key}: ${await resolve(value)}`;
177
- })
178
- )
179
- ).join("\n");
180
- } else {
181
- // TODO: support other types
182
- console.log(value);
183
- throw new Error(`Unsupported value type: ${value}`);
184
175
  }
185
- })
186
- );
187
-
188
- // Construct the string template by joining template parts with interpolated values
189
- const lines = template
190
- .map((part) =>
191
- part
192
- .split("\n")
193
- .map((line) =>
194
- line.startsWith(indent) ? line.slice(indent.length) : line
176
+ appendices[value.path] = await fs.readFile(value.path, "utf-8");
177
+ return `[${path.basename(value.path)}](${value.path})`;
178
+ }
179
+ if (typeof value === "object") {
180
+ return (
181
+ await Promise.all(
182
+ Object.entries(value).map(async ([key, value]) => {
183
+ return `* ${key}: ${await resolve(value)}`;
184
+ }),
195
185
  )
196
- .join("\n")
197
- )
198
- .flatMap((part, i) =>
199
- i < stringValues.length ? [part, stringValues[i] ?? ""] : [part]
200
- )
201
- .join("")
202
- .split("\n");
186
+ ).join("\n");
187
+ }
188
+ // TODO: support other types
189
+ console.log(value);
190
+ throw new Error(`Unsupported value type: ${value}`);
191
+ }),
192
+ );
203
193
 
204
- // Collect and sort appendices by file path
205
- return [
206
- // format the user prompt and trim the first line if it's empty
207
- lines.length > 1 && lines[0].replaceAll(" ", "").length === 0
208
- ? lines.slice(1).join("\n")
209
- : lines.join("\n"),
194
+ // Construct the string template by joining template parts with interpolated values
195
+ const lines = template
196
+ .map((part) =>
197
+ part
198
+ .split("\n")
199
+ .map((line) =>
200
+ line.startsWith(indent) ? line.slice(indent.length) : line,
201
+ )
202
+ .join("\n"),
203
+ )
204
+ .flatMap((part, i) =>
205
+ i < stringValues.length ? [part, stringValues[i] ?? ""] : [part],
206
+ )
207
+ .join("")
208
+ .split("\n");
210
209
 
211
- // sort appendices by path and include at the end of the prompt
212
- Object.entries(appendices)
213
- .sort(([a], [b]) => a.localeCompare(b))
214
- .map(([filePath, content]) => {
215
- const extension = path.extname(filePath).slice(1);
216
- const codeTag = extension ? extension : "";
217
- return `// ${filePath}\n\`\`\`${codeTag}\n${content}\n\`\`\``;
218
- })
219
- .join("\n\n"),
220
- ].join("\n");
221
- }
210
+ // Collect and sort appendices by file path
211
+ return [
212
+ // format the user prompt and trim the first line if it's empty
213
+ lines.length > 1 && lines[0].replaceAll(" ", "").length === 0
214
+ ? lines.slice(1).join("\n")
215
+ : lines.join("\n"),
216
+
217
+ // sort appendices by path and include at the end of the prompt
218
+ Object.entries(appendices)
219
+ .sort(([a], [b]) => a.localeCompare(b))
220
+ .map(([filePath, content]) => {
221
+ const extension = path.extname(filePath).slice(1);
222
+ const codeTag = extension ? extension : "";
223
+ return `// ${filePath}\n\`\`\`${codeTag}\n${content}\n\`\`\``;
224
+ })
225
+ .join("\n\n"),
226
+ ].join("\n");
222
227
  }
223
228
 
229
+ export type Phase = "up" | "destroy" | "read";
230
+
224
231
  export interface AlchemyOptions {
225
232
  /**
226
233
  * The name of the application.
@@ -231,7 +238,7 @@ export interface AlchemyOptions {
231
238
  *
232
239
  * @default "up"
233
240
  */
234
- phase?: "up" | "destroy";
241
+ phase?: Phase;
235
242
  /**
236
243
  * Name to scope the resource state under (e.g. `.alchemy/{stage}/..`).
237
244
  *
@@ -337,7 +344,7 @@ async function run<T>(
337
344
  await _scope.parent!.state.set(id, resource);
338
345
  _scope.parent!.resources.set(
339
346
  id,
340
- Object.assign(Promise.resolve(resource), output) as PendingResource
347
+ Object.assign(Promise.resolve(resource), output) as PendingResource,
341
348
  );
342
349
  }
343
350
  return await _scope.run(async () => fn.bind(_scope)(_scope));
package/src/apply.ts CHANGED
@@ -18,7 +18,7 @@ export interface ApplyOptions {
18
18
  export async function apply<Out extends Resource>(
19
19
  resource: PendingResource<Out>,
20
20
  props: ResourceProps | undefined,
21
- options?: ApplyOptions
21
+ options?: ApplyOptions,
22
22
  ): Promise<Awaited<Out>> {
23
23
  const scope = resource.Scope;
24
24
  try {
@@ -29,6 +29,14 @@ export async function apply<Out extends Resource>(
29
29
  if (provider === undefined) {
30
30
  throw new Error(`Provider "${resource.Kind}" not found`);
31
31
  }
32
+ if (scope.phase === "read") {
33
+ if (state === undefined) {
34
+ throw new Error(
35
+ `Resource "${resource.FQN}" not found and running in 'read' phase.`,
36
+ );
37
+ }
38
+ return state.output as Awaited<Out>;
39
+ }
32
40
  if (state === undefined) {
33
41
  state = {
34
42
  kind: resource.Kind,
@@ -79,7 +87,7 @@ export async function apply<Out extends Resource>(
79
87
 
80
88
  if (!quiet) {
81
89
  console.log(
82
- `${phase === "create" ? "Create" : "Update"}: "${resource.FQN}"`
90
+ `${phase === "create" ? "Create" : "Update"}: "${resource.FQN}"`,
83
91
  );
84
92
  }
85
93
 
@@ -99,7 +107,7 @@ export async function apply<Out extends Resource>(
99
107
  replace: () => {
100
108
  if (isReplaced) {
101
109
  console.warn(
102
- `Resource ${resource.Kind} ${resource.FQN} is already marked as REPLACE`
110
+ `Resource ${resource.Kind} ${resource.FQN} is already marked as REPLACE`,
103
111
  );
104
112
  return;
105
113
  }
@@ -112,11 +120,11 @@ export async function apply<Out extends Resource>(
112
120
  {
113
121
  isResource: true,
114
122
  },
115
- async () => provider.handler.bind(ctx)(resource.ID, props)
123
+ async () => provider.handler.bind(ctx)(resource.ID, props),
116
124
  );
117
125
  if (!quiet) {
118
126
  console.log(
119
- `${phase === "create" ? "Created" : "Updated"}: "${resource.FQN}"`
127
+ `${phase === "create" ? "Created" : "Updated"}: "${resource.FQN}"`,
120
128
  );
121
129
  }
122
130
 
@@ -136,7 +144,6 @@ export async function apply<Out extends Resource>(
136
144
  // }
137
145
  return output as any;
138
146
  } catch (error) {
139
- console.error(new Error().stack);
140
147
  scope.fail();
141
148
  throw error;
142
149
  }
package/src/aws/bucket.ts CHANGED
@@ -136,99 +136,96 @@ export const Bucket = Resource(
136
136
  client.send(
137
137
  new DeleteBucketCommand({
138
138
  Bucket: props.bucketName,
139
- })
140
- )
139
+ }),
140
+ ),
141
141
  );
142
142
  return this.destroy();
143
- } else {
144
- try {
145
- // Check if bucket exists
143
+ }
144
+ try {
145
+ // Check if bucket exists
146
+ await client.send(
147
+ new HeadBucketCommand({
148
+ Bucket: props.bucketName,
149
+ }),
150
+ );
151
+
152
+ // Update tags if they changed and bucket exists
153
+ if (this.phase === "update" && props.tags) {
146
154
  await client.send(
147
- new HeadBucketCommand({
155
+ new PutBucketTaggingCommand({
148
156
  Bucket: props.bucketName,
149
- })
157
+ Tagging: {
158
+ TagSet: Object.entries(props.tags).map(([Key, Value]) => ({
159
+ Key,
160
+ Value,
161
+ })),
162
+ },
163
+ }),
150
164
  );
151
-
152
- // Update tags if they changed and bucket exists
153
- if (this.phase === "update" && props.tags) {
154
- await client.send(
155
- new PutBucketTaggingCommand({
156
- Bucket: props.bucketName,
165
+ }
166
+ } catch (error: any) {
167
+ if (error.name === "NotFound") {
168
+ // Create bucket if it doesn't exist
169
+ await client.send(
170
+ new CreateBucketCommand({
171
+ Bucket: props.bucketName,
172
+ // Add tags during creation if specified
173
+ ...(props.tags && {
157
174
  Tagging: {
158
175
  TagSet: Object.entries(props.tags).map(([Key, Value]) => ({
159
176
  Key,
160
177
  Value,
161
178
  })),
162
179
  },
163
- })
164
- );
165
- }
166
- } catch (error: any) {
167
- if (error.name === "NotFound") {
168
- // Create bucket if it doesn't exist
169
- await client.send(
170
- new CreateBucketCommand({
171
- Bucket: props.bucketName,
172
- // Add tags during creation if specified
173
- ...(props.tags && {
174
- Tagging: {
175
- TagSet: Object.entries(props.tags).map(([Key, Value]) => ({
176
- Key,
177
- Value,
178
- })),
179
- },
180
- }),
181
- })
182
- );
183
- } else {
184
- throw error;
185
- }
180
+ }),
181
+ }),
182
+ );
183
+ } else {
184
+ throw error;
186
185
  }
186
+ }
187
187
 
188
- // Get bucket details
189
- const [locationResponse, versioningResponse, aclResponse] =
190
- await Promise.all([
191
- client.send(
192
- new GetBucketLocationCommand({ Bucket: props.bucketName })
193
- ),
194
- client.send(
195
- new GetBucketVersioningCommand({ Bucket: props.bucketName })
196
- ),
197
- client.send(new GetBucketAclCommand({ Bucket: props.bucketName })),
198
- ]);
188
+ // Get bucket details
189
+ const [locationResponse, versioningResponse, aclResponse] =
190
+ await Promise.all([
191
+ client.send(new GetBucketLocationCommand({ Bucket: props.bucketName })),
192
+ client.send(
193
+ new GetBucketVersioningCommand({ Bucket: props.bucketName }),
194
+ ),
195
+ client.send(new GetBucketAclCommand({ Bucket: props.bucketName })),
196
+ ]);
199
197
 
200
- const region = locationResponse.LocationConstraint || "us-east-1";
198
+ const region = locationResponse.LocationConstraint || "us-east-1";
201
199
 
202
- // Get tags if they exist
203
- let tags = props.tags;
204
- if (!tags) {
205
- try {
206
- const taggingResponse = await client.send(
207
- new GetBucketTaggingCommand({ Bucket: props.bucketName })
208
- );
209
- tags = Object.fromEntries(
210
- taggingResponse.TagSet?.map(({ Key, Value }) => [Key, Value]) || []
211
- );
212
- } catch (error: any) {
213
- if (error.name !== "NoSuchTagSet") {
214
- throw error;
215
- }
200
+ // Get tags if they exist
201
+ let tags = props.tags;
202
+ if (!tags) {
203
+ try {
204
+ const taggingResponse = await client.send(
205
+ new GetBucketTaggingCommand({ Bucket: props.bucketName }),
206
+ );
207
+ tags = Object.fromEntries(
208
+ taggingResponse.TagSet?.map(({ Key, Value }) => [Key, Value]) || [],
209
+ );
210
+ } catch (error: any) {
211
+ if (error.name !== "NoSuchTagSet") {
212
+ throw error;
216
213
  }
217
214
  }
218
-
219
- return this({
220
- bucketName: props.bucketName,
221
- arn: `arn:aws:s3:::${props.bucketName}`,
222
- bucketDomainName: `${props.bucketName}.s3.amazonaws.com`,
223
- bucketRegionalDomainName: `${props.bucketName}.s3.${region}.amazonaws.com`,
224
- region,
225
- hostedZoneId: getHostedZoneId(region),
226
- versioningEnabled: versioningResponse.Status === "Enabled",
227
- acl: aclResponse.Grants?.[0]?.Permission?.toLowerCase(),
228
- ...(tags && { tags }),
229
- });
230
215
  }
231
- }
216
+
217
+ return this({
218
+ bucketName: props.bucketName,
219
+ arn: `arn:aws:s3:::${props.bucketName}`,
220
+ bucketDomainName: `${props.bucketName}.s3.amazonaws.com`,
221
+ bucketRegionalDomainName: `${props.bucketName}.s3.${region}.amazonaws.com`,
222
+ region,
223
+ hostedZoneId: getHostedZoneId(region),
224
+ versioningEnabled: versioningResponse.Status === "Enabled",
225
+ acl: aclResponse.Grants?.[0]?.Permission?.toLowerCase(),
226
+ ...(tags && { tags }),
227
+ });
228
+ },
232
229
  );
233
230
 
234
231
  /**