alchemy 0.1.11 → 0.1.13

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 (312) hide show
  1. package/README.md +324 -0
  2. package/lib/$.d.ts +8 -3
  3. package/lib/$.d.ts.map +1 -1
  4. package/lib/$.js +38 -27
  5. package/lib/$.js.map +1 -1
  6. package/lib/alchemize.d.ts.map +1 -1
  7. package/lib/alchemize.js +7 -0
  8. package/lib/alchemize.js.map +1 -1
  9. package/lib/apply.d.ts +2 -2
  10. package/lib/apply.d.ts.map +1 -1
  11. package/lib/apply.js +3 -3
  12. package/lib/apply.js.map +1 -1
  13. package/lib/aws/index.d.ts +1 -0
  14. package/lib/aws/index.d.ts.map +1 -1
  15. package/lib/aws/index.js +1 -0
  16. package/lib/aws/index.js.map +1 -1
  17. package/lib/aws/ses.d.ts +75 -0
  18. package/lib/aws/ses.d.ts.map +1 -0
  19. package/lib/aws/ses.js +197 -0
  20. package/lib/aws/ses.js.map +1 -0
  21. package/lib/cloudflare/api.d.ts +88 -0
  22. package/lib/cloudflare/api.d.ts.map +1 -0
  23. package/lib/cloudflare/api.js +184 -0
  24. package/lib/cloudflare/api.js.map +1 -0
  25. package/lib/cloudflare/asset-manifest.d.ts +15 -0
  26. package/lib/cloudflare/asset-manifest.d.ts.map +1 -0
  27. package/lib/cloudflare/asset-manifest.js +2 -0
  28. package/lib/cloudflare/asset-manifest.js.map +1 -0
  29. package/lib/cloudflare/auth.d.ts +52 -0
  30. package/lib/cloudflare/auth.d.ts.map +1 -0
  31. package/lib/cloudflare/auth.js +114 -0
  32. package/lib/cloudflare/auth.js.map +1 -0
  33. package/lib/cloudflare/bindings.d.ts +254 -0
  34. package/lib/cloudflare/bindings.d.ts.map +1 -0
  35. package/lib/cloudflare/bindings.js +2 -0
  36. package/lib/cloudflare/bindings.js.map +1 -0
  37. package/lib/cloudflare/durable-object-namespace.d.ts +23 -0
  38. package/lib/cloudflare/durable-object-namespace.d.ts.map +1 -0
  39. package/lib/cloudflare/durable-object-namespace.js +26 -0
  40. package/lib/cloudflare/durable-object-namespace.js.map +1 -0
  41. package/lib/cloudflare/generate-asset-manifest.d.ts +3 -0
  42. package/lib/cloudflare/generate-asset-manifest.d.ts.map +1 -0
  43. package/lib/cloudflare/generate-asset-manifest.js +69 -0
  44. package/lib/cloudflare/generate-asset-manifest.js.map +1 -0
  45. package/lib/cloudflare/index.d.ts +7 -0
  46. package/lib/cloudflare/index.d.ts.map +1 -0
  47. package/lib/cloudflare/index.js +7 -0
  48. package/lib/cloudflare/index.js.map +1 -0
  49. package/lib/cloudflare/kv-namespace.d.ts +61 -0
  50. package/lib/cloudflare/kv-namespace.d.ts.map +1 -0
  51. package/lib/cloudflare/kv-namespace.js +93 -0
  52. package/lib/cloudflare/kv-namespace.js.map +1 -0
  53. package/lib/cloudflare/state.d.ts +83 -0
  54. package/lib/cloudflare/state.d.ts.map +1 -0
  55. package/lib/cloudflare/state.js +109 -0
  56. package/lib/cloudflare/state.js.map +1 -0
  57. package/lib/cloudflare/static-site-router.d.ts +19 -0
  58. package/lib/cloudflare/static-site-router.d.ts.map +1 -0
  59. package/lib/cloudflare/static-site-router.js +110 -0
  60. package/lib/cloudflare/static-site-router.js.map +1 -0
  61. package/lib/cloudflare/static-site.d.ts +166 -0
  62. package/lib/cloudflare/static-site.d.ts.map +1 -0
  63. package/lib/cloudflare/static-site.js +168 -0
  64. package/lib/cloudflare/static-site.js.map +1 -0
  65. package/lib/cloudflare/types.d.ts +52 -0
  66. package/lib/cloudflare/types.d.ts.map +1 -0
  67. package/lib/cloudflare/types.js +21 -0
  68. package/lib/cloudflare/types.js.map +1 -0
  69. package/lib/cloudflare/upload-asset-manifest.d.ts +12 -0
  70. package/lib/cloudflare/upload-asset-manifest.d.ts.map +1 -0
  71. package/lib/cloudflare/upload-asset-manifest.js +55 -0
  72. package/lib/cloudflare/upload-asset-manifest.js.map +1 -0
  73. package/lib/cloudflare/worker-metadata.d.ts +130 -0
  74. package/lib/cloudflare/worker-metadata.d.ts.map +1 -0
  75. package/lib/cloudflare/worker-metadata.js +2 -0
  76. package/lib/cloudflare/worker-metadata.js.map +1 -0
  77. package/lib/cloudflare/worker-migration.d.ts +110 -0
  78. package/lib/cloudflare/worker-migration.d.ts.map +1 -0
  79. package/lib/cloudflare/worker-migration.js +11 -0
  80. package/lib/cloudflare/worker-migration.js.map +1 -0
  81. package/lib/cloudflare/worker.d.ts +105 -0
  82. package/lib/cloudflare/worker.d.ts.map +1 -0
  83. package/lib/cloudflare/worker.js +358 -0
  84. package/lib/cloudflare/worker.js.map +1 -0
  85. package/lib/destroy.d.ts +1 -1
  86. package/lib/destroy.d.ts.map +1 -1
  87. package/lib/destroy.js +2 -1
  88. package/lib/destroy.js.map +1 -1
  89. package/lib/esbuild.d.ts +83 -1
  90. package/lib/esbuild.d.ts.map +1 -1
  91. package/lib/esbuild.js +27 -18
  92. package/lib/esbuild.js.map +1 -1
  93. package/lib/fs.d.ts +1 -1
  94. package/lib/global.d.ts +0 -2
  95. package/lib/global.d.ts.map +1 -1
  96. package/lib/global.js +3 -3
  97. package/lib/global.js.map +1 -1
  98. package/lib/index.d.ts +1 -0
  99. package/lib/index.d.ts.map +1 -1
  100. package/lib/index.js +1 -0
  101. package/lib/index.js.map +1 -1
  102. package/lib/main.js +1 -1
  103. package/lib/main.js.map +1 -1
  104. package/lib/output.d.ts +18 -13
  105. package/lib/output.d.ts.map +1 -1
  106. package/lib/output.js +12 -5
  107. package/lib/output.js.map +1 -1
  108. package/lib/print.d.ts +7 -0
  109. package/lib/print.d.ts.map +1 -0
  110. package/lib/print.js +10 -0
  111. package/lib/print.js.map +1 -0
  112. package/lib/resource.d.ts +9 -4
  113. package/lib/resource.d.ts.map +1 -1
  114. package/lib/resource.js +127 -93
  115. package/lib/resource.js.map +1 -1
  116. package/lib/scope.d.ts +3 -0
  117. package/lib/scope.d.ts.map +1 -1
  118. package/lib/scope.js +4 -12
  119. package/lib/scope.js.map +1 -1
  120. package/lib/utils/content-type.d.ts +8 -0
  121. package/lib/utils/content-type.d.ts.map +1 -0
  122. package/lib/utils/content-type.js +41 -0
  123. package/lib/utils/content-type.js.map +1 -0
  124. package/package.json +27 -8
  125. package/src/$.ts +63 -30
  126. package/src/alchemize.ts +12 -0
  127. package/src/apply.ts +6 -6
  128. package/src/aws/index.ts +1 -0
  129. package/src/aws/ses.ts +346 -0
  130. package/src/cloudflare/api.ts +282 -0
  131. package/src/cloudflare/asset-manifest.ts +16 -0
  132. package/src/cloudflare/auth.ts +187 -0
  133. package/src/cloudflare/bindings.ts +297 -0
  134. package/src/cloudflare/durable-object-namespace.ts +43 -0
  135. package/src/cloudflare/generate-asset-manifest.ts +93 -0
  136. package/src/cloudflare/index.ts +6 -0
  137. package/src/cloudflare/kv-namespace.ts +195 -0
  138. package/src/cloudflare/state.ts +138 -0
  139. package/src/cloudflare/static-site-router.ts +150 -0
  140. package/src/cloudflare/static-site.ts +395 -0
  141. package/src/cloudflare/types.ts +71 -0
  142. package/src/cloudflare/upload-asset-manifest.ts +83 -0
  143. package/src/cloudflare/worker-metadata.ts +154 -0
  144. package/src/cloudflare/worker-migration.ts +145 -0
  145. package/src/cloudflare/worker.ts +666 -0
  146. package/src/destroy.ts +2 -2
  147. package/src/esbuild.ts +34 -22
  148. package/src/global.ts +1 -3
  149. package/src/index.ts +1 -1
  150. package/src/main.ts +1 -1
  151. package/src/output.ts +53 -37
  152. package/src/print.ts +18 -0
  153. package/src/resource.ts +163 -116
  154. package/src/scope.ts +7 -12
  155. package/src/utils/content-type.ts +42 -0
  156. package/lib/agent/ai.d.ts +0 -40
  157. package/lib/agent/ai.d.ts.map +0 -1
  158. package/lib/agent/ai.js +0 -132
  159. package/lib/agent/ai.js.map +0 -1
  160. package/lib/agent/anthropic.d.ts +0 -9
  161. package/lib/agent/anthropic.d.ts.map +0 -1
  162. package/lib/agent/anthropic.js +0 -25
  163. package/lib/agent/anthropic.js.map +0 -1
  164. package/lib/agent/dependencies.d.ts +0 -4
  165. package/lib/agent/dependencies.d.ts.map +0 -1
  166. package/lib/agent/dependencies.js +0 -48
  167. package/lib/agent/dependencies.js.map +0 -1
  168. package/lib/agent/file-context.d.ts +0 -13
  169. package/lib/agent/file-context.d.ts.map +0 -1
  170. package/lib/agent/file-context.js +0 -6
  171. package/lib/agent/file-context.js.map +0 -1
  172. package/lib/agent/index.d.ts +0 -10
  173. package/lib/agent/index.d.ts.map +0 -1
  174. package/lib/agent/index.js +0 -10
  175. package/lib/agent/index.js.map +0 -1
  176. package/lib/agent/model.d.ts +0 -8
  177. package/lib/agent/model.d.ts.map +0 -1
  178. package/lib/agent/model.js +0 -29
  179. package/lib/agent/model.js.map +0 -1
  180. package/lib/agent/module.d.ts +0 -81
  181. package/lib/agent/module.d.ts.map +0 -1
  182. package/lib/agent/module.js +0 -309
  183. package/lib/agent/module.js.map +0 -1
  184. package/lib/agent/openai.d.ts +0 -6
  185. package/lib/agent/openai.d.ts.map +0 -1
  186. package/lib/agent/openai.js +0 -41
  187. package/lib/agent/openai.js.map +0 -1
  188. package/lib/agent/program.d.ts +0 -12
  189. package/lib/agent/program.d.ts.map +0 -1
  190. package/lib/agent/program.js +0 -21
  191. package/lib/agent/program.js.map +0 -1
  192. package/lib/agent/prompts.d.ts +0 -4
  193. package/lib/agent/prompts.d.ts.map +0 -1
  194. package/lib/agent/prompts.js +0 -11
  195. package/lib/agent/prompts.js.map +0 -1
  196. package/lib/agent/scrape.d.ts +0 -71
  197. package/lib/agent/scrape.d.ts.map +0 -1
  198. package/lib/agent/scrape.js +0 -45
  199. package/lib/agent/scrape.js.map +0 -1
  200. package/lib/aws/auto/docs.d.ts +0 -11
  201. package/lib/aws/auto/docs.d.ts.map +0 -1
  202. package/lib/aws/auto/docs.js +0 -56
  203. package/lib/aws/auto/docs.js.map +0 -1
  204. package/lib/aws/auto/index.d.ts +0 -5
  205. package/lib/aws/auto/index.d.ts.map +0 -1
  206. package/lib/aws/auto/index.js +0 -5
  207. package/lib/aws/auto/index.js.map +0 -1
  208. package/lib/aws/auto/resource.d.ts +0 -24
  209. package/lib/aws/auto/resource.d.ts.map +0 -1
  210. package/lib/aws/auto/resource.js +0 -194
  211. package/lib/aws/auto/resource.js.map +0 -1
  212. package/lib/aws/auto/service.d.ts +0 -10
  213. package/lib/aws/auto/service.d.ts.map +0 -1
  214. package/lib/aws/auto/service.js +0 -34
  215. package/lib/aws/auto/service.js.map +0 -1
  216. package/lib/aws/auto/spec.d.ts +0 -83
  217. package/lib/aws/auto/spec.d.ts.map +0 -1
  218. package/lib/aws/auto/spec.js +0 -58
  219. package/lib/aws/auto/spec.js.map +0 -1
  220. package/lib/markdown/design.d.ts +0 -17
  221. package/lib/markdown/design.d.ts.map +0 -1
  222. package/lib/markdown/design.js +0 -92
  223. package/lib/markdown/design.js.map +0 -1
  224. package/lib/markdown/extract.d.ts +0 -7
  225. package/lib/markdown/extract.d.ts.map +0 -1
  226. package/lib/markdown/extract.js +0 -10
  227. package/lib/markdown/extract.js.map +0 -1
  228. package/lib/markdown/index.d.ts +0 -4
  229. package/lib/markdown/index.d.ts.map +0 -1
  230. package/lib/markdown/index.js +0 -4
  231. package/lib/markdown/index.js.map +0 -1
  232. package/lib/markdown/requirements.d.ts +0 -79
  233. package/lib/markdown/requirements.d.ts.map +0 -1
  234. package/lib/markdown/requirements.js +0 -105
  235. package/lib/markdown/requirements.js.map +0 -1
  236. package/lib/typescript/check-omission.d.ts +0 -2
  237. package/lib/typescript/check-omission.d.ts.map +0 -1
  238. package/lib/typescript/check-omission.js +0 -43
  239. package/lib/typescript/check-omission.js.map +0 -1
  240. package/lib/typescript/debug-type-errors.d.ts +0 -3
  241. package/lib/typescript/debug-type-errors.d.ts.map +0 -1
  242. package/lib/typescript/debug-type-errors.js +0 -35
  243. package/lib/typescript/debug-type-errors.js.map +0 -1
  244. package/lib/typescript/extract.d.ts +0 -7
  245. package/lib/typescript/extract.d.ts.map +0 -1
  246. package/lib/typescript/extract.js +0 -10
  247. package/lib/typescript/extract.js.map +0 -1
  248. package/lib/typescript/index.d.ts +0 -7
  249. package/lib/typescript/index.d.ts.map +0 -1
  250. package/lib/typescript/index.js +0 -7
  251. package/lib/typescript/index.js.map +0 -1
  252. package/lib/typescript/install-dependencies.d.ts +0 -29
  253. package/lib/typescript/install-dependencies.d.ts.map +0 -1
  254. package/lib/typescript/install-dependencies.js +0 -52
  255. package/lib/typescript/install-dependencies.js.map +0 -1
  256. package/lib/typescript/package.d.ts +0 -157
  257. package/lib/typescript/package.d.ts.map +0 -1
  258. package/lib/typescript/package.js +0 -115
  259. package/lib/typescript/package.js.map +0 -1
  260. package/lib/typescript/repair-omissions.d.ts +0 -3
  261. package/lib/typescript/repair-omissions.d.ts.map +0 -1
  262. package/lib/typescript/repair-omissions.js +0 -23
  263. package/lib/typescript/repair-omissions.js.map +0 -1
  264. package/lib/typescript/repair.d.ts +0 -3
  265. package/lib/typescript/repair.d.ts.map +0 -1
  266. package/lib/typescript/repair.js +0 -34
  267. package/lib/typescript/repair.js.map +0 -1
  268. package/lib/typescript/tsconfig.d.ts +0 -469
  269. package/lib/typescript/tsconfig.d.ts.map +0 -1
  270. package/lib/typescript/tsconfig.js +0 -143
  271. package/lib/typescript/tsconfig.js.map +0 -1
  272. package/lib/typescript/typescript.d.ts +0 -107
  273. package/lib/typescript/typescript.d.ts.map +0 -1
  274. package/lib/typescript/typescript.js +0 -180
  275. package/lib/typescript/typescript.js.map +0 -1
  276. package/lib/typescript/validate.d.ts +0 -10
  277. package/lib/typescript/validate.d.ts.map +0 -1
  278. package/lib/typescript/validate.js +0 -49
  279. package/lib/typescript/validate.js.map +0 -1
  280. package/src/agent/ai.ts +0 -215
  281. package/src/agent/anthropic.ts +0 -31
  282. package/src/agent/dependencies.ts +0 -62
  283. package/src/agent/file-context.ts +0 -8
  284. package/src/agent/index.ts +0 -9
  285. package/src/agent/model.ts +0 -39
  286. package/src/agent/module.ts +0 -418
  287. package/src/agent/openai.ts +0 -50
  288. package/src/agent/program.md +0 -38
  289. package/src/agent/program.ts +0 -37
  290. package/src/agent/prompts.ts +0 -20
  291. package/src/agent/scrape.ts +0 -47
  292. package/src/aws/auto/cfn.json +0 -225310
  293. package/src/aws/auto/docs.ts +0 -73
  294. package/src/aws/auto/index.ts +0 -4
  295. package/src/aws/auto/resource.ts +0 -269
  296. package/src/aws/auto/service.ts +0 -51
  297. package/src/aws/auto/spec.ts +0 -160
  298. package/src/markdown/design.ts +0 -125
  299. package/src/markdown/extract.ts +0 -9
  300. package/src/markdown/index.ts +0 -3
  301. package/src/markdown/requirements.ts +0 -135
  302. package/src/typescript/check-omission.ts +0 -47
  303. package/src/typescript/debug-type-errors.ts +0 -42
  304. package/src/typescript/extract.ts +0 -9
  305. package/src/typescript/index.ts +0 -6
  306. package/src/typescript/install-dependencies.ts +0 -61
  307. package/src/typescript/package.ts +0 -143
  308. package/src/typescript/repair-omissions.ts +0 -31
  309. package/src/typescript/repair.ts +0 -45
  310. package/src/typescript/tsconfig.ts +0 -170
  311. package/src/typescript/typescript.ts +0 -217
  312. package/src/typescript/validate.ts +0 -73
@@ -0,0 +1,666 @@
1
+ import * as fs from "fs/promises";
2
+ import { apply } from "../apply";
3
+ import { Bundle, type BundleProps } from "../esbuild";
4
+ import { type Context, Resource } from "../resource";
5
+ import { type CloudflareApi, createCloudflareApi } from "./api";
6
+ import type { WorkerBinding, WorkerBindingSpec } from "./bindings";
7
+ import type { DurableObjectNamespace } from "./durable-object-namespace";
8
+ import { isDurableObjectNamespace } from "./durable-object-namespace";
9
+ import type { WorkerScriptMetadata } from "./worker-metadata";
10
+ import type { SingleStepMigration } from "./worker-migration";
11
+ /**
12
+ * Properties for creating or updating a Worker
13
+ */
14
+ export interface WorkerProps {
15
+ /**
16
+ * The worker script content (JavaScript or WASM)
17
+ * One of script, entryPoint, or bundle must be provided
18
+ */
19
+ script?: string;
20
+
21
+ /**
22
+ * Path to the entry point file
23
+ * Will be bundled using esbuild
24
+ * One of script, entryPoint, or bundle must be provided
25
+ */
26
+ entrypoint?: string;
27
+
28
+ /**
29
+ * Bundle options when using entryPoint
30
+ * Ignored if bundle is provided
31
+ */
32
+ bundle?: Omit<BundleProps, "entryPoint">;
33
+
34
+ /**
35
+ * Module format for the worker script
36
+ * 'esm' - ECMAScript modules (default)
37
+ * 'cjs' - CommonJS modules
38
+ * @default 'esm'
39
+ */
40
+ format?: "esm" | "cjs";
41
+
42
+ /**
43
+ * Name for the worker
44
+ * This is mandatory - must be explicitly specified
45
+ */
46
+ name: string;
47
+
48
+ /**
49
+ * Routes to associate with this worker
50
+ * Format: example.com/* or *.example.com/*
51
+ */
52
+ routes?: string[];
53
+
54
+ /**
55
+ * Bindings to attach to the worker
56
+ */
57
+ bindings?: WorkerBinding[];
58
+
59
+ /**
60
+ * Environment variables to attach to the worker
61
+ * These will be converted to plain_text bindings
62
+ */
63
+ env?: {
64
+ [key: string]: string;
65
+ };
66
+
67
+ /**
68
+ * Whether to enable a workers.dev URL for this worker
69
+ * If true, the worker will be available at {name}.{subdomain}.workers.dev
70
+ * @default false
71
+ */
72
+ url?: boolean;
73
+
74
+ /**
75
+ * Observability configuration for the worker
76
+ * Controls whether worker logs are enabled
77
+ * @default { enabled: true }
78
+ */
79
+ observability?: {
80
+ /**
81
+ * Whether to enable worker logs
82
+ * @default true
83
+ */
84
+ enabled?: boolean;
85
+ };
86
+
87
+ /**
88
+ * Migrations to apply to the worker
89
+ */
90
+ migrations?: SingleStepMigration;
91
+ }
92
+
93
+ /**
94
+ * Output returned after Worker creation/update
95
+ */
96
+ export interface WorkerOutput extends Omit<WorkerProps, "url"> {
97
+ /**
98
+ * The ID of the worker
99
+ */
100
+ id: string;
101
+
102
+ /**
103
+ * Time at which the worker was created
104
+ */
105
+ createdAt: number;
106
+
107
+ /**
108
+ * Time at which the worker was last updated
109
+ */
110
+ updatedAt: number;
111
+
112
+ /**
113
+ * The worker's URL if enabled
114
+ * Format: {name}.{subdomain}.workers.dev
115
+ */
116
+ url?: string;
117
+
118
+ /**
119
+ * The bindings that were created
120
+ */
121
+ bindings: WorkerBinding[];
122
+ }
123
+
124
+ export class Worker extends Resource(
125
+ "cloudflare::Worker",
126
+ {
127
+ alwaysUpdate: true,
128
+ },
129
+ async (ctx: Context<WorkerOutput>, props: WorkerProps) => {
130
+ // Create Cloudflare API client with automatic account discovery
131
+ const api = await createCloudflareApi();
132
+
133
+ // Use the provided name
134
+ const workerName = props.name;
135
+
136
+ // Validate input - we need either script, entryPoint, or bundle
137
+ if (!props.script && !props.entrypoint) {
138
+ throw new Error("One of script or entryPoint must be provided");
139
+ }
140
+
141
+ if (ctx.event === "delete") {
142
+ return deleteWorker(ctx, api, workerName);
143
+ } else if (ctx.event === "create") {
144
+ await assertWorkerDoesNotExist(ctx, api, workerName);
145
+ }
146
+
147
+ const oldBindings = await ctx.get<WorkerBinding[]>("bindings");
148
+
149
+ const scriptMetadata = await prepareWorkerMetadata(ctx, oldBindings, props);
150
+
151
+ // Get the script content - either from props.script, or by bundling
152
+ const scriptContent =
153
+ props.script ?? (await bundleWorkerScript(ctx, props));
154
+
155
+ // Upload the worker script
156
+ await putWorker(ctx, api, workerName, scriptContent, scriptMetadata);
157
+
158
+ // TODO: it is less than ideal that this can fail, resulting in state problem
159
+ await ctx.set("bindings", props.bindings);
160
+
161
+ // Handle routes if requested
162
+ await setupRoutes(api, workerName, props.routes || []);
163
+
164
+ // Handle worker URL if requested
165
+ const workerUrl = await configureURL(
166
+ ctx,
167
+ api,
168
+ workerName,
169
+ props.url ?? false,
170
+ );
171
+
172
+ // Get current timestamp
173
+ const now = Date.now();
174
+
175
+ // Construct the output
176
+ const output: WorkerOutput = {
177
+ id: workerName,
178
+ name: workerName,
179
+ script: scriptContent,
180
+ format: props.format || "esm", // Include format in the output
181
+ routes: props.routes || [],
182
+ bindings: props.bindings || [],
183
+ env: props.env,
184
+ observability: scriptMetadata.observability,
185
+ createdAt: now,
186
+ updatedAt: now,
187
+ url: workerUrl,
188
+ };
189
+
190
+ return output;
191
+ },
192
+ ) {}
193
+
194
+ async function deleteWorker(
195
+ ctx: Context<WorkerOutput>,
196
+ api: CloudflareApi,
197
+ workerName: string,
198
+ ) {
199
+ // Delete worker
200
+ const deleteResponse = await api.delete(
201
+ `/accounts/${api.accountId}/workers/scripts/${workerName}`,
202
+ );
203
+
204
+ // Check for success (2xx status code)
205
+ if (!deleteResponse.ok && deleteResponse.status !== 404) {
206
+ const errorData: any = await deleteResponse
207
+ .json()
208
+ .catch(() => ({ errors: [{ message: deleteResponse.statusText }] }));
209
+ console.error(
210
+ "Error deleting worker:",
211
+ errorData.errors?.[0]?.message || deleteResponse.statusText,
212
+ );
213
+ }
214
+
215
+ // Also remove any associated routes if they exist
216
+ if (ctx.output?.routes && ctx.output.routes.length > 0 && api.zoneId) {
217
+ // First get existing routes to find their IDs
218
+ const routesResponse = await api.get(`/zones/${api.zoneId}/workers/routes`);
219
+
220
+ if (!routesResponse.ok) {
221
+ throw new Error(
222
+ `Could not fetch routes for cleanup: ${routesResponse.status} ${routesResponse.statusText}`,
223
+ );
224
+ }
225
+ const routesData: any = await routesResponse.json();
226
+ const existingRoutes = routesData.result || [];
227
+
228
+ for (const route of existingRoutes) {
229
+ if (ctx.output.routes.includes(route.pattern)) {
230
+ // Delete the route
231
+ const routeDeleteResponse = await api.delete(
232
+ `/zones/${api.zoneId}/workers/routes/${route.id}`,
233
+ );
234
+
235
+ if (!routeDeleteResponse.ok) {
236
+ console.warn(
237
+ `Failed to delete route ${route.pattern}: ${routeDeleteResponse.status} ${routeDeleteResponse.statusText}`,
238
+ );
239
+ }
240
+ }
241
+ }
242
+ }
243
+
244
+ // Disable the URL if it was enabled
245
+ if (ctx.output?.url) {
246
+ try {
247
+ await api.post(
248
+ `/accounts/${api.accountId}/workers/scripts/${workerName}/subdomain`,
249
+ JSON.stringify({ enabled: false }),
250
+ {
251
+ headers: { "Content-Type": "application/json" },
252
+ },
253
+ );
254
+ } catch (error) {
255
+ console.warn("Failed to disable worker URL during deletion:", error);
256
+ }
257
+ }
258
+
259
+ // Return minimal output for deleted state
260
+ return;
261
+ }
262
+
263
+ async function putWorker(
264
+ ctx: Context<WorkerOutput>,
265
+ api: CloudflareApi,
266
+ workerName: string,
267
+ scriptContent: string,
268
+ scriptMetadata: WorkerMetadata,
269
+ ) {
270
+ const scriptName = scriptMetadata.main_module ?? scriptMetadata.body_part!;
271
+
272
+ // Create FormData for the upload
273
+ const formData = new FormData();
274
+
275
+ // Add the actual script content as a named file part
276
+ formData.append(
277
+ scriptName,
278
+ new Blob([scriptContent], {
279
+ type: scriptMetadata.main_module
280
+ ? "application/javascript+module"
281
+ : "application/javascript",
282
+ }),
283
+ scriptName,
284
+ );
285
+
286
+ console.log("Script Metadata:", JSON.stringify(scriptMetadata, null, 2));
287
+
288
+ // Add metadata as JSON
289
+ formData.append(
290
+ "metadata",
291
+ new Blob([JSON.stringify(scriptMetadata)], {
292
+ type: "application/json",
293
+ }),
294
+ );
295
+
296
+ // Upload worker script with bindings
297
+ const uploadResponse = await api.put(
298
+ `/accounts/${api.accountId}/workers/scripts/${workerName}`,
299
+ formData,
300
+ {
301
+ headers: {
302
+ "Content-Type": "multipart/form-data",
303
+ },
304
+ },
305
+ );
306
+
307
+ // Check if the upload was successful
308
+ if (!uploadResponse.ok) {
309
+ const errorData: any = await uploadResponse
310
+ .json()
311
+ .catch(() => ({ errors: [{ message: uploadResponse.statusText }] }));
312
+ throw new Error(
313
+ `Error uploading worker script '${workerName}': ${errorData.errors?.[0]?.message || uploadResponse.statusText}`,
314
+ );
315
+ }
316
+
317
+ return formData;
318
+ }
319
+
320
+ interface WorkerMetadata {
321
+ bindings: WorkerBinding[];
322
+ observability: {
323
+ enabled: boolean;
324
+ };
325
+ migrations?: SingleStepMigration;
326
+ main_module?: string;
327
+ body_part?: string;
328
+ tags?: string[];
329
+ }
330
+
331
+ async function prepareWorkerMetadata(
332
+ ctx: Context<WorkerOutput>,
333
+ oldBindings: WorkerBinding[] | undefined,
334
+ props: WorkerProps,
335
+ ): Promise<WorkerMetadata> {
336
+ // Prepare metadata with bindings
337
+ const meta: WorkerMetadata = {
338
+ bindings: [] as WorkerBinding[],
339
+ observability: {
340
+ enabled: props.observability?.enabled !== false,
341
+ },
342
+ tags: [`alchemy:id:${ctx.resourceFQN}`],
343
+ migrations: {
344
+ new_classes: props.migrations?.new_classes ?? [],
345
+ deleted_classes: props.migrations?.deleted_classes ?? [],
346
+ renamed_classes: props.migrations?.renamed_classes ?? [],
347
+ transferred_classes: props.migrations?.transferred_classes ?? [],
348
+ new_sqlite_classes: props.migrations?.new_sqlite_classes ?? [],
349
+ },
350
+ };
351
+
352
+ // Convert bindings to the format expected by the API
353
+ for (const binding of props.bindings ?? []) {
354
+ // Create a copy of the binding to avoid modifying the original
355
+ const bindingData = { ...binding };
356
+
357
+ if (isDurableObjectNamespace(bindingData)) {
358
+ const stableId = bindingData.id;
359
+ const className = bindingData.className;
360
+
361
+ meta.bindings.push({
362
+ type: "durable_object_namespace",
363
+ name: bindingData.bindingName,
364
+ class_name: className,
365
+ script_name: bindingData.scriptName,
366
+ environment: bindingData.environment,
367
+ namespace_id: bindingData.namespaceId,
368
+ });
369
+
370
+ const oldBinding: DurableObjectNamespace | undefined = oldBindings
371
+ ?.filter(isDurableObjectNamespace)
372
+ ?.find((b) => b.id === stableId);
373
+
374
+ if (!oldBinding) {
375
+ if (bindingData.sqlite) {
376
+ meta.migrations!.new_sqlite_classes!.push(className);
377
+ } else {
378
+ meta.migrations!.new_classes!.push(className);
379
+ }
380
+ } else if (oldBinding.className !== className) {
381
+ meta.migrations!.renamed_classes!.push({
382
+ from: oldBinding.className,
383
+ to: className,
384
+ });
385
+ }
386
+ } else {
387
+ // For all other binding types, use as-is
388
+ meta.bindings.push(bindingData as WorkerBindingSpec);
389
+ }
390
+ }
391
+
392
+ // Convert env variables to plain_text bindings
393
+ if (props.env) {
394
+ for (const [key, value] of Object.entries(props.env)) {
395
+ meta.bindings.push({
396
+ name: key,
397
+ type: "plain_text",
398
+ text: value,
399
+ });
400
+ }
401
+ }
402
+
403
+ // Determine if we're using ESM or service worker format
404
+ const isEsModule = props.format !== "cjs"; // Default to ESM unless CJS is specified
405
+ const scriptName = isEsModule ? "worker.js" : "script";
406
+
407
+ if (isEsModule) {
408
+ // For ES modules format
409
+ meta.main_module = scriptName;
410
+ } else {
411
+ // For service worker format (CJS)
412
+ meta.body_part = scriptName;
413
+ }
414
+ return meta;
415
+ }
416
+
417
+ async function assertWorkerDoesNotExist(
418
+ ctx: Context<WorkerOutput>,
419
+ api: CloudflareApi,
420
+ workerName: string,
421
+ ) {
422
+ const response = await api.get(
423
+ `/accounts/${api.accountId}/workers/scripts/${workerName}`,
424
+ );
425
+ if (response.status === 404) {
426
+ return true;
427
+ }
428
+ if (response.status === 200) {
429
+ const metadata = await getWorkerScriptMetadata(api, workerName);
430
+
431
+ if (!metadata) {
432
+ throw new Error(
433
+ `Worker exists but failed to fetch metadata: ${response.status} ${response.statusText}`,
434
+ );
435
+ }
436
+
437
+ if (
438
+ metadata.default_environment?.script.tags.includes(
439
+ `alchemy:id:${ctx.resourceFQN}`,
440
+ )
441
+ ) {
442
+ return true;
443
+ }
444
+
445
+ throw new Error(
446
+ `Worker with name '${workerName}' already exists. Please use a unique name.`,
447
+ );
448
+ } else {
449
+ throw new Error(
450
+ `Error checking if worker exists: ${response.status} ${response.statusText}`,
451
+ );
452
+ }
453
+ }
454
+
455
+ async function bundleWorkerScript(
456
+ ctx: Context<WorkerOutput>,
457
+ props: WorkerProps,
458
+ ) {
459
+ // Get the script content - either from props.script, or by bundling
460
+
461
+ // Create and use a Bundle resource with worker-optimized configuration
462
+ const defaultBundleOptions: Omit<BundleProps, "entryPoint"> = {
463
+ format: props.format === "cjs" ? "cjs" : "esm", // Use the specified format or default to ESM
464
+ target: "es2020",
465
+ platform: "browser",
466
+ minify: true,
467
+ options: {
468
+ keepNames: true, // Important for Durable Object classes
469
+ },
470
+ };
471
+
472
+ // Merge with user-provided options
473
+ const bundleOptions = {
474
+ ...defaultBundleOptions,
475
+ ...(props.bundle || {}),
476
+ };
477
+
478
+ // Create the bundle
479
+ const bundle = new Bundle("bundle", {
480
+ entryPoint: props.entrypoint!,
481
+ ...bundleOptions,
482
+ external: [...(bundleOptions.external ?? []), "cloudflare:workers"],
483
+ });
484
+
485
+ // Get the bundled content
486
+ const bundleOutput = await apply(bundle, {
487
+ quiet: ctx.quiet,
488
+ });
489
+ try {
490
+ return await fs.readFile(bundleOutput.path, "utf-8");
491
+ } catch (error) {
492
+ console.error("Error reading bundle:", error);
493
+ throw new Error("Error reading bundle");
494
+ }
495
+ }
496
+
497
+ async function setupRoutes(
498
+ api: CloudflareApi,
499
+ workerName: string,
500
+ routes: string[],
501
+ ) {
502
+ // Set up routes if provided
503
+ if (routes && routes.length > 0 && api.zoneId) {
504
+ // First get existing routes
505
+ const routesResponse = await api.get(`/zones/${api.zoneId}/workers/routes`);
506
+
507
+ if (!routesResponse.ok) {
508
+ throw new Error(
509
+ `Could not fetch routes: ${routesResponse.status} ${routesResponse.statusText}`,
510
+ );
511
+ }
512
+ const routesData: any = await routesResponse.json();
513
+ const existingRoutes = routesData.result || [];
514
+
515
+ // For each desired route
516
+ for (const pattern of routes) {
517
+ const existingRoute = existingRoutes.find(
518
+ (r: any) => r.pattern === pattern,
519
+ );
520
+
521
+ if (existingRoute) {
522
+ // Update if script name doesn't match
523
+ if (existingRoute.script !== workerName) {
524
+ const updateRouteResponse = await api.put(
525
+ `/zones/${api.zoneId}/workers/routes/${existingRoute.id}`,
526
+ {
527
+ pattern,
528
+ script: workerName,
529
+ },
530
+ );
531
+
532
+ if (!updateRouteResponse.ok) {
533
+ console.warn(
534
+ `Failed to update route ${pattern}: ${updateRouteResponse.status} ${updateRouteResponse.statusText}`,
535
+ );
536
+ }
537
+ }
538
+ } else {
539
+ // Create new route
540
+ const createRouteResponse = await api.post(
541
+ `/zones/${api.zoneId}/workers/routes`,
542
+ {
543
+ pattern,
544
+ script: workerName,
545
+ },
546
+ );
547
+
548
+ if (!createRouteResponse.ok) {
549
+ throw new Error(
550
+ `Failed to create route ${pattern}: ${createRouteResponse.status} ${createRouteResponse.statusText}`,
551
+ );
552
+ }
553
+ }
554
+ }
555
+ }
556
+ }
557
+
558
+ async function configureURL(
559
+ ctx: Context<WorkerOutput>,
560
+ api: CloudflareApi,
561
+ workerName: string,
562
+ url: boolean,
563
+ ) {
564
+ let workerUrl;
565
+ if (url) {
566
+ // Enable the workers.dev subdomain for this worker
567
+ await api.post(
568
+ `/accounts/${api.accountId}/workers/scripts/${workerName}/subdomain`,
569
+ { enabled: true, previews_enabled: true },
570
+ {
571
+ headers: { "Content-Type": "application/json" },
572
+ },
573
+ );
574
+
575
+ // Get the account's workers.dev subdomain
576
+ const subdomainResponse = await api.get(
577
+ `/accounts/${api.accountId}/workers/subdomain`,
578
+ );
579
+
580
+ if (!subdomainResponse.ok) {
581
+ throw new Error(
582
+ `Could not fetch workers.dev subdomain: ${subdomainResponse.status} ${subdomainResponse.statusText}`,
583
+ );
584
+ }
585
+ const subdomainData: {
586
+ result: {
587
+ subdomain: string;
588
+ };
589
+ } = await subdomainResponse.json();
590
+ const subdomain = subdomainData.result?.subdomain;
591
+
592
+ if (subdomain) {
593
+ workerUrl = `https://${workerName}.${subdomain}.workers.dev`;
594
+
595
+ // Add a delay when the subdomain is first created.
596
+ // This is to prevent an issue where a negative cache-hit
597
+ // causes the subdomain to be unavailable for 30 seconds.
598
+ if (ctx.event === "create" || !ctx.output?.url) {
599
+ await new Promise((resolve) => setTimeout(resolve, 3000));
600
+ }
601
+ }
602
+ } else if (url === false && ctx.output?.url) {
603
+ // Explicitly disable URL if it was previously enabled
604
+ const response = await api.post(
605
+ `/accounts/${api.accountId}/workers/scripts/${workerName}/subdomain`,
606
+ JSON.stringify({ enabled: false }),
607
+ {
608
+ headers: { "Content-Type": "application/json" },
609
+ },
610
+ );
611
+ if (!response.ok) {
612
+ throw new Error(
613
+ `Failed to disable worker URL: ${response.status} ${response.statusText}`,
614
+ );
615
+ }
616
+ }
617
+ return workerUrl;
618
+ }
619
+
620
+ async function getWorkerScriptMetadata(
621
+ api: CloudflareApi,
622
+ workerName: string,
623
+ ): Promise<WorkerScriptMetadata | undefined> {
624
+ const response = await api.get(
625
+ `/accounts/${api.accountId}/workers/services/${workerName}`,
626
+ );
627
+ if (response.status === 404) {
628
+ return undefined;
629
+ }
630
+ if (!response.ok) {
631
+ throw new Error(
632
+ `Error getting worker script metadata: ${response.status} ${response.statusText}`,
633
+ );
634
+ }
635
+ return ((await response.json()) as any).result as WorkerScriptMetadata;
636
+ }
637
+
638
+ async function getWorkerBindings(
639
+ api: CloudflareApi,
640
+ workerName: string,
641
+ environment = "production",
642
+ ) {
643
+ const response = await api.get(
644
+ `/accounts/${api.accountId}/workers/services/${workerName}/environments/${environment}/bindings`,
645
+ {
646
+ headers: {
647
+ Authorization: `Bearer ${process.env.CLOUDFLARE_API_TOKEN}`,
648
+ "Content-Type": "application/json",
649
+ },
650
+ },
651
+ );
652
+
653
+ if (response.status === 404) {
654
+ return undefined;
655
+ }
656
+
657
+ if (!response.ok) {
658
+ throw new Error(
659
+ `Failed to fetch bindings: ${response.status} ${response.statusText}`,
660
+ );
661
+ }
662
+
663
+ const data: any = await response.json();
664
+
665
+ return data.result;
666
+ }
package/src/destroy.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { defaultStage, defaultStateStore, rootScope } from "./global";
1
+ import { defaultStage, defaultStateStore } from "./global";
2
2
  import type { Output } from "./output";
3
3
  import { Provider, ResourceID, isResource } from "./resource";
4
- import type { Scope } from "./scope";
4
+ import { type Scope, rootScope } from "./scope";
5
5
  import type { State, StateStore } from "./state";
6
6
 
7
7
  export interface DestroyOptions {