alchemy 0.1.11 → 0.1.12

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 (277) hide show
  1. package/README.md +324 -0
  2. package/lib/apply.d.ts +2 -2
  3. package/lib/apply.d.ts.map +1 -1
  4. package/lib/apply.js +2 -2
  5. package/lib/apply.js.map +1 -1
  6. package/lib/aws/index.d.ts +1 -0
  7. package/lib/aws/index.d.ts.map +1 -1
  8. package/lib/aws/index.js +1 -0
  9. package/lib/aws/index.js.map +1 -1
  10. package/lib/aws/ses.d.ts +75 -0
  11. package/lib/aws/ses.d.ts.map +1 -0
  12. package/lib/aws/ses.js +197 -0
  13. package/lib/aws/ses.js.map +1 -0
  14. package/lib/cloudflare/api.d.ts +84 -0
  15. package/lib/cloudflare/api.d.ts.map +1 -0
  16. package/lib/cloudflare/api.js +178 -0
  17. package/lib/cloudflare/api.js.map +1 -0
  18. package/lib/cloudflare/asset-manifest.d.ts +15 -0
  19. package/lib/cloudflare/asset-manifest.d.ts.map +1 -0
  20. package/lib/cloudflare/asset-manifest.js +2 -0
  21. package/lib/cloudflare/asset-manifest.js.map +1 -0
  22. package/lib/cloudflare/auth.d.ts +52 -0
  23. package/lib/cloudflare/auth.d.ts.map +1 -0
  24. package/lib/cloudflare/auth.js +114 -0
  25. package/lib/cloudflare/auth.js.map +1 -0
  26. package/lib/cloudflare/bindings.d.ts +244 -0
  27. package/lib/cloudflare/bindings.d.ts.map +1 -0
  28. package/lib/cloudflare/bindings.js +7 -0
  29. package/lib/cloudflare/bindings.js.map +1 -0
  30. package/lib/cloudflare/generate-asset-manifest.d.ts +3 -0
  31. package/lib/cloudflare/generate-asset-manifest.d.ts.map +1 -0
  32. package/lib/cloudflare/generate-asset-manifest.js +69 -0
  33. package/lib/cloudflare/generate-asset-manifest.js.map +1 -0
  34. package/lib/cloudflare/index.d.ts +6 -0
  35. package/lib/cloudflare/index.d.ts.map +1 -0
  36. package/lib/cloudflare/index.js +6 -0
  37. package/lib/cloudflare/index.js.map +1 -0
  38. package/lib/cloudflare/kv-namespace.d.ts +61 -0
  39. package/lib/cloudflare/kv-namespace.d.ts.map +1 -0
  40. package/lib/cloudflare/kv-namespace.js +93 -0
  41. package/lib/cloudflare/kv-namespace.js.map +1 -0
  42. package/lib/cloudflare/state.d.ts +83 -0
  43. package/lib/cloudflare/state.d.ts.map +1 -0
  44. package/lib/cloudflare/state.js +109 -0
  45. package/lib/cloudflare/state.js.map +1 -0
  46. package/lib/cloudflare/static-site-router.d.ts +10 -0
  47. package/lib/cloudflare/static-site-router.d.ts.map +1 -0
  48. package/lib/cloudflare/static-site-router.js +88 -0
  49. package/lib/cloudflare/static-site-router.js.map +1 -0
  50. package/lib/cloudflare/static-site.d.ts +166 -0
  51. package/lib/cloudflare/static-site.d.ts.map +1 -0
  52. package/lib/cloudflare/static-site.js +152 -0
  53. package/lib/cloudflare/static-site.js.map +1 -0
  54. package/lib/cloudflare/types.d.ts +52 -0
  55. package/lib/cloudflare/types.d.ts.map +1 -0
  56. package/lib/cloudflare/types.js +21 -0
  57. package/lib/cloudflare/types.js.map +1 -0
  58. package/lib/cloudflare/upload-asset-manifest.d.ts +12 -0
  59. package/lib/cloudflare/upload-asset-manifest.d.ts.map +1 -0
  60. package/lib/cloudflare/upload-asset-manifest.js +55 -0
  61. package/lib/cloudflare/upload-asset-manifest.js.map +1 -0
  62. package/lib/cloudflare/worker.d.ts +87 -0
  63. package/lib/cloudflare/worker.d.ts.map +1 -0
  64. package/lib/cloudflare/worker.js +364 -0
  65. package/lib/cloudflare/worker.js.map +1 -0
  66. package/lib/esbuild.d.ts +82 -0
  67. package/lib/esbuild.d.ts.map +1 -1
  68. package/lib/esbuild.js +24 -17
  69. package/lib/esbuild.js.map +1 -1
  70. package/lib/fs.d.ts +1 -1
  71. package/lib/global.d.ts.map +1 -1
  72. package/lib/global.js +3 -1
  73. package/lib/global.js.map +1 -1
  74. package/lib/index.d.ts +0 -1
  75. package/lib/index.d.ts.map +1 -1
  76. package/lib/index.js +0 -1
  77. package/lib/index.js.map +1 -1
  78. package/lib/main.js +1 -1
  79. package/lib/main.js.map +1 -1
  80. package/lib/output.d.ts +17 -13
  81. package/lib/output.d.ts.map +1 -1
  82. package/lib/output.js +2 -2
  83. package/lib/output.js.map +1 -1
  84. package/lib/resource.d.ts +3 -2
  85. package/lib/resource.d.ts.map +1 -1
  86. package/lib/resource.js +2 -2
  87. package/lib/resource.js.map +1 -1
  88. package/lib/utils/content-type.d.ts +8 -0
  89. package/lib/utils/content-type.d.ts.map +1 -0
  90. package/lib/utils/content-type.js +41 -0
  91. package/lib/utils/content-type.js.map +1 -0
  92. package/package.json +26 -8
  93. package/src/apply.ts +5 -5
  94. package/src/aws/index.ts +1 -0
  95. package/src/aws/ses.ts +346 -0
  96. package/src/cloudflare/api.ts +276 -0
  97. package/src/cloudflare/asset-manifest.ts +16 -0
  98. package/src/cloudflare/auth.ts +187 -0
  99. package/src/cloudflare/bindings.ts +286 -0
  100. package/src/cloudflare/generate-asset-manifest.ts +93 -0
  101. package/src/cloudflare/index.ts +5 -0
  102. package/src/cloudflare/kv-namespace.ts +195 -0
  103. package/src/cloudflare/state.ts +138 -0
  104. package/src/cloudflare/static-site-router.ts +108 -0
  105. package/src/cloudflare/static-site.ts +379 -0
  106. package/src/cloudflare/types.ts +71 -0
  107. package/src/cloudflare/upload-asset-manifest.ts +83 -0
  108. package/src/cloudflare/worker.ts +568 -0
  109. package/src/esbuild.ts +31 -22
  110. package/src/global.ts +1 -0
  111. package/src/index.ts +0 -1
  112. package/src/main.ts +1 -1
  113. package/src/output.ts +32 -32
  114. package/src/resource.ts +11 -5
  115. package/src/utils/content-type.ts +42 -0
  116. package/lib/$.d.ts +0 -7
  117. package/lib/$.d.ts.map +0 -1
  118. package/lib/$.js +0 -31
  119. package/lib/$.js.map +0 -1
  120. package/lib/agent/ai.d.ts +0 -40
  121. package/lib/agent/ai.d.ts.map +0 -1
  122. package/lib/agent/ai.js +0 -132
  123. package/lib/agent/ai.js.map +0 -1
  124. package/lib/agent/anthropic.d.ts +0 -9
  125. package/lib/agent/anthropic.d.ts.map +0 -1
  126. package/lib/agent/anthropic.js +0 -25
  127. package/lib/agent/anthropic.js.map +0 -1
  128. package/lib/agent/dependencies.d.ts +0 -4
  129. package/lib/agent/dependencies.d.ts.map +0 -1
  130. package/lib/agent/dependencies.js +0 -48
  131. package/lib/agent/dependencies.js.map +0 -1
  132. package/lib/agent/file-context.d.ts +0 -13
  133. package/lib/agent/file-context.d.ts.map +0 -1
  134. package/lib/agent/file-context.js +0 -6
  135. package/lib/agent/file-context.js.map +0 -1
  136. package/lib/agent/index.d.ts +0 -10
  137. package/lib/agent/index.d.ts.map +0 -1
  138. package/lib/agent/index.js +0 -10
  139. package/lib/agent/index.js.map +0 -1
  140. package/lib/agent/model.d.ts +0 -8
  141. package/lib/agent/model.d.ts.map +0 -1
  142. package/lib/agent/model.js +0 -29
  143. package/lib/agent/model.js.map +0 -1
  144. package/lib/agent/module.d.ts +0 -81
  145. package/lib/agent/module.d.ts.map +0 -1
  146. package/lib/agent/module.js +0 -309
  147. package/lib/agent/module.js.map +0 -1
  148. package/lib/agent/openai.d.ts +0 -6
  149. package/lib/agent/openai.d.ts.map +0 -1
  150. package/lib/agent/openai.js +0 -41
  151. package/lib/agent/openai.js.map +0 -1
  152. package/lib/agent/program.d.ts +0 -12
  153. package/lib/agent/program.d.ts.map +0 -1
  154. package/lib/agent/program.js +0 -21
  155. package/lib/agent/program.js.map +0 -1
  156. package/lib/agent/prompts.d.ts +0 -4
  157. package/lib/agent/prompts.d.ts.map +0 -1
  158. package/lib/agent/prompts.js +0 -11
  159. package/lib/agent/prompts.js.map +0 -1
  160. package/lib/agent/scrape.d.ts +0 -71
  161. package/lib/agent/scrape.d.ts.map +0 -1
  162. package/lib/agent/scrape.js +0 -45
  163. package/lib/agent/scrape.js.map +0 -1
  164. package/lib/aws/auto/docs.d.ts +0 -11
  165. package/lib/aws/auto/docs.d.ts.map +0 -1
  166. package/lib/aws/auto/docs.js +0 -56
  167. package/lib/aws/auto/docs.js.map +0 -1
  168. package/lib/aws/auto/index.d.ts +0 -5
  169. package/lib/aws/auto/index.d.ts.map +0 -1
  170. package/lib/aws/auto/index.js +0 -5
  171. package/lib/aws/auto/index.js.map +0 -1
  172. package/lib/aws/auto/resource.d.ts +0 -24
  173. package/lib/aws/auto/resource.d.ts.map +0 -1
  174. package/lib/aws/auto/resource.js +0 -194
  175. package/lib/aws/auto/resource.js.map +0 -1
  176. package/lib/aws/auto/service.d.ts +0 -10
  177. package/lib/aws/auto/service.d.ts.map +0 -1
  178. package/lib/aws/auto/service.js +0 -34
  179. package/lib/aws/auto/service.js.map +0 -1
  180. package/lib/aws/auto/spec.d.ts +0 -83
  181. package/lib/aws/auto/spec.d.ts.map +0 -1
  182. package/lib/aws/auto/spec.js +0 -58
  183. package/lib/aws/auto/spec.js.map +0 -1
  184. package/lib/markdown/design.d.ts +0 -17
  185. package/lib/markdown/design.d.ts.map +0 -1
  186. package/lib/markdown/design.js +0 -92
  187. package/lib/markdown/design.js.map +0 -1
  188. package/lib/markdown/extract.d.ts +0 -7
  189. package/lib/markdown/extract.d.ts.map +0 -1
  190. package/lib/markdown/extract.js +0 -10
  191. package/lib/markdown/extract.js.map +0 -1
  192. package/lib/markdown/index.d.ts +0 -4
  193. package/lib/markdown/index.d.ts.map +0 -1
  194. package/lib/markdown/index.js +0 -4
  195. package/lib/markdown/index.js.map +0 -1
  196. package/lib/markdown/requirements.d.ts +0 -79
  197. package/lib/markdown/requirements.d.ts.map +0 -1
  198. package/lib/markdown/requirements.js +0 -105
  199. package/lib/markdown/requirements.js.map +0 -1
  200. package/lib/typescript/check-omission.d.ts +0 -2
  201. package/lib/typescript/check-omission.d.ts.map +0 -1
  202. package/lib/typescript/check-omission.js +0 -43
  203. package/lib/typescript/check-omission.js.map +0 -1
  204. package/lib/typescript/debug-type-errors.d.ts +0 -3
  205. package/lib/typescript/debug-type-errors.d.ts.map +0 -1
  206. package/lib/typescript/debug-type-errors.js +0 -35
  207. package/lib/typescript/debug-type-errors.js.map +0 -1
  208. package/lib/typescript/extract.d.ts +0 -7
  209. package/lib/typescript/extract.d.ts.map +0 -1
  210. package/lib/typescript/extract.js +0 -10
  211. package/lib/typescript/extract.js.map +0 -1
  212. package/lib/typescript/index.d.ts +0 -7
  213. package/lib/typescript/index.d.ts.map +0 -1
  214. package/lib/typescript/index.js +0 -7
  215. package/lib/typescript/index.js.map +0 -1
  216. package/lib/typescript/install-dependencies.d.ts +0 -29
  217. package/lib/typescript/install-dependencies.d.ts.map +0 -1
  218. package/lib/typescript/install-dependencies.js +0 -52
  219. package/lib/typescript/install-dependencies.js.map +0 -1
  220. package/lib/typescript/package.d.ts +0 -157
  221. package/lib/typescript/package.d.ts.map +0 -1
  222. package/lib/typescript/package.js +0 -115
  223. package/lib/typescript/package.js.map +0 -1
  224. package/lib/typescript/repair-omissions.d.ts +0 -3
  225. package/lib/typescript/repair-omissions.d.ts.map +0 -1
  226. package/lib/typescript/repair-omissions.js +0 -23
  227. package/lib/typescript/repair-omissions.js.map +0 -1
  228. package/lib/typescript/repair.d.ts +0 -3
  229. package/lib/typescript/repair.d.ts.map +0 -1
  230. package/lib/typescript/repair.js +0 -34
  231. package/lib/typescript/repair.js.map +0 -1
  232. package/lib/typescript/tsconfig.d.ts +0 -469
  233. package/lib/typescript/tsconfig.d.ts.map +0 -1
  234. package/lib/typescript/tsconfig.js +0 -143
  235. package/lib/typescript/tsconfig.js.map +0 -1
  236. package/lib/typescript/typescript.d.ts +0 -107
  237. package/lib/typescript/typescript.d.ts.map +0 -1
  238. package/lib/typescript/typescript.js +0 -180
  239. package/lib/typescript/typescript.js.map +0 -1
  240. package/lib/typescript/validate.d.ts +0 -10
  241. package/lib/typescript/validate.d.ts.map +0 -1
  242. package/lib/typescript/validate.js +0 -49
  243. package/lib/typescript/validate.js.map +0 -1
  244. package/src/$.ts +0 -36
  245. package/src/agent/ai.ts +0 -215
  246. package/src/agent/anthropic.ts +0 -31
  247. package/src/agent/dependencies.ts +0 -62
  248. package/src/agent/file-context.ts +0 -8
  249. package/src/agent/index.ts +0 -9
  250. package/src/agent/model.ts +0 -39
  251. package/src/agent/module.ts +0 -418
  252. package/src/agent/openai.ts +0 -50
  253. package/src/agent/program.md +0 -38
  254. package/src/agent/program.ts +0 -37
  255. package/src/agent/prompts.ts +0 -20
  256. package/src/agent/scrape.ts +0 -47
  257. package/src/aws/auto/cfn.json +0 -225310
  258. package/src/aws/auto/docs.ts +0 -73
  259. package/src/aws/auto/index.ts +0 -4
  260. package/src/aws/auto/resource.ts +0 -269
  261. package/src/aws/auto/service.ts +0 -51
  262. package/src/aws/auto/spec.ts +0 -160
  263. package/src/markdown/design.ts +0 -125
  264. package/src/markdown/extract.ts +0 -9
  265. package/src/markdown/index.ts +0 -3
  266. package/src/markdown/requirements.ts +0 -135
  267. package/src/typescript/check-omission.ts +0 -47
  268. package/src/typescript/debug-type-errors.ts +0 -42
  269. package/src/typescript/extract.ts +0 -9
  270. package/src/typescript/index.ts +0 -6
  271. package/src/typescript/install-dependencies.ts +0 -61
  272. package/src/typescript/package.ts +0 -143
  273. package/src/typescript/repair-omissions.ts +0 -31
  274. package/src/typescript/repair.ts +0 -45
  275. package/src/typescript/tsconfig.ts +0 -170
  276. package/src/typescript/typescript.ts +0 -217
  277. package/src/typescript/validate.ts +0 -73
@@ -0,0 +1,568 @@
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 { createCloudflareApi } from "./api";
6
+ import type { WorkerBinding } from "./bindings";
7
+
8
+ /**
9
+ * Properties for creating or updating a Worker
10
+ */
11
+ export interface WorkerProps {
12
+ /**
13
+ * The worker script content (JavaScript or WASM)
14
+ * One of script, entryPoint, or bundle must be provided
15
+ */
16
+ script?: string;
17
+
18
+ /**
19
+ * Path to the entry point file
20
+ * Will be bundled using esbuild
21
+ * One of script, entryPoint, or bundle must be provided
22
+ */
23
+ entrypoint?: string;
24
+
25
+ /**
26
+ * Reference to a pre-configured Bundle resource
27
+ * One of script, entryPoint, or bundle must be provided
28
+ */
29
+ bundle?: Bundle;
30
+
31
+ /**
32
+ * Bundle options when using entryPoint
33
+ * Ignored if bundle is provided
34
+ */
35
+ bundleOptions?: Omit<BundleProps, "entryPoint">;
36
+
37
+ /**
38
+ * Module format for the worker script
39
+ * 'esm' - ECMAScript modules (default)
40
+ * 'cjs' - CommonJS modules
41
+ * @default 'esm'
42
+ */
43
+ format?: "esm" | "cjs";
44
+
45
+ /**
46
+ * Name for the worker
47
+ * This is mandatory - must be explicitly specified
48
+ */
49
+ name: string;
50
+
51
+ /**
52
+ * Routes to associate with this worker
53
+ * Format: example.com/* or *.example.com/*
54
+ */
55
+ routes?: string[];
56
+
57
+ /**
58
+ * Bindings to attach to the worker
59
+ */
60
+ bindings?: WorkerBinding[];
61
+
62
+ /**
63
+ * Whether the worker should be deployed to production
64
+ * @default true
65
+ */
66
+ production?: boolean;
67
+
68
+ /**
69
+ * Whether to enable a workers.dev URL for this worker
70
+ * If true, the worker will be available at {name}.{subdomain}.workers.dev
71
+ * @default false
72
+ */
73
+ url?: boolean;
74
+ }
75
+
76
+ /**
77
+ * Output returned after Worker creation/update
78
+ */
79
+ export interface WorkerOutput extends Omit<WorkerProps, "url"> {
80
+ /**
81
+ * The ID of the worker
82
+ */
83
+ id: string;
84
+
85
+ /**
86
+ * Time at which the worker was created
87
+ */
88
+ createdAt: number;
89
+
90
+ /**
91
+ * Time at which the worker was last updated
92
+ */
93
+ updatedAt: number;
94
+
95
+ /**
96
+ * The worker's URL if enabled
97
+ * Format: {name}.{subdomain}.workers.dev
98
+ */
99
+ url?: string;
100
+ }
101
+
102
+ export class Worker extends Resource(
103
+ "cloudflare::Worker",
104
+ async (ctx: Context<WorkerOutput>, props: WorkerProps) => {
105
+ // Create Cloudflare API client with automatic account discovery
106
+ const api = await createCloudflareApi();
107
+
108
+ // Validate that a name is provided
109
+ if (!props.name) {
110
+ throw new Error("Worker name is required - must be explicitly specified");
111
+ }
112
+
113
+ // Use the provided name
114
+ const workerName = props.name;
115
+
116
+ // Validate input - we need either script, entryPoint, or bundle
117
+ if (!props.script && !props.entrypoint && !props.bundle) {
118
+ throw new Error("One of script, entryPoint, or bundle must be provided");
119
+ }
120
+
121
+ // For create operations, check if a worker with the same name already exists
122
+ if (ctx.event === "create") {
123
+ const checkExistsResponse = await api.get(
124
+ `/accounts/${api.accountId}/workers/scripts/${workerName}`,
125
+ );
126
+
127
+ if (checkExistsResponse.ok) {
128
+ throw new Error(
129
+ `Worker with name '${workerName}' already exists. Please use a unique name.`,
130
+ );
131
+ } else if (checkExistsResponse.status !== 404) {
132
+ // If it's neither a success nor a 404, it's an unexpected error
133
+ const errorData: any = await checkExistsResponse.json().catch(() => ({
134
+ errors: [{ message: checkExistsResponse.statusText }],
135
+ }));
136
+ throw new Error(
137
+ `Error checking if worker exists: ${errorData.errors?.[0]?.message || checkExistsResponse.statusText}`,
138
+ );
139
+ }
140
+ // If it's a 404, that means the worker doesn't exist yet, which is what we want
141
+ }
142
+
143
+ if (ctx.event === "delete") {
144
+ // Delete worker
145
+ const deleteResponse = await api.delete(
146
+ `/accounts/${api.accountId}/workers/scripts/${workerName}`,
147
+ );
148
+
149
+ // Check for success (2xx status code)
150
+ if (!deleteResponse.ok && deleteResponse.status !== 404) {
151
+ const errorData: any = await deleteResponse
152
+ .json()
153
+ .catch(() => ({ errors: [{ message: deleteResponse.statusText }] }));
154
+ console.error(
155
+ "Error deleting worker:",
156
+ errorData.errors?.[0]?.message || deleteResponse.statusText,
157
+ );
158
+ }
159
+
160
+ // Also remove any associated routes if they exist
161
+ if (ctx.output?.routes && ctx.output.routes.length > 0 && api.zoneId) {
162
+ // First get existing routes to find their IDs
163
+ const routesResponse = await api.get(
164
+ `/zones/${api.zoneId}/workers/routes`,
165
+ );
166
+
167
+ if (routesResponse.ok) {
168
+ const routesData: any = await routesResponse.json();
169
+ const existingRoutes = routesData.result || [];
170
+
171
+ for (const route of existingRoutes) {
172
+ if (ctx.output.routes.includes(route.pattern)) {
173
+ // Delete the route
174
+ const routeDeleteResponse = await api.delete(
175
+ `/zones/${api.zoneId}/workers/routes/${route.id}`,
176
+ );
177
+
178
+ if (!routeDeleteResponse.ok) {
179
+ console.warn(
180
+ `Failed to delete route ${route.pattern}: ${routeDeleteResponse.status} ${routeDeleteResponse.statusText}`,
181
+ );
182
+ }
183
+ }
184
+ }
185
+ } else {
186
+ console.warn(
187
+ "Could not fetch routes for cleanup:",
188
+ routesResponse.status,
189
+ routesResponse.statusText,
190
+ );
191
+ }
192
+ }
193
+
194
+ // Disable the URL if it was enabled
195
+ if (ctx.output?.url) {
196
+ try {
197
+ await api.post(
198
+ `/accounts/${api.accountId}/workers/scripts/${workerName}/subdomain`,
199
+ JSON.stringify({ enabled: false }),
200
+ {
201
+ headers: { "Content-Type": "application/json" },
202
+ },
203
+ );
204
+ } catch (error) {
205
+ console.warn("Failed to disable worker URL during deletion:", error);
206
+ }
207
+ }
208
+
209
+ // Return minimal output for deleted state
210
+ return {
211
+ ...props,
212
+ id: "",
213
+ createdAt: 0,
214
+ updatedAt: 0,
215
+ url: undefined,
216
+ };
217
+ } else {
218
+ // Get the script content - either from props.script, or by bundling
219
+ let scriptContent: string;
220
+
221
+ if (props.script) {
222
+ // Use the provided script
223
+ scriptContent = props.script;
224
+ } else if (props.bundle) {
225
+ // Use a pre-configured Bundle resource
226
+ const bundleOutput = await apply(props.bundle);
227
+ scriptContent = await fs.readFile(bundleOutput.path, "utf-8");
228
+ } else if (props.entrypoint) {
229
+ // Create and use a Bundle resource with worker-optimized configuration
230
+ const defaultBundleOptions: Omit<BundleProps, "entryPoint"> = {
231
+ format: props.format === "cjs" ? "cjs" : "esm", // Use the specified format or default to ESM
232
+ target: "es2020",
233
+ platform: "browser",
234
+ minify: true,
235
+ options: {
236
+ keepNames: true, // Important for Durable Object classes
237
+ },
238
+ };
239
+
240
+ // Merge with user-provided options
241
+ const bundleOptions = {
242
+ ...defaultBundleOptions,
243
+ ...(props.bundleOptions || {}),
244
+ };
245
+
246
+ // Create the bundle
247
+ const bundle = new Bundle("bundle", {
248
+ entryPoint: props.entrypoint,
249
+ ...bundleOptions,
250
+ });
251
+
252
+ // Get the bundled content
253
+ const bundleOutput = await apply(bundle);
254
+ try {
255
+ scriptContent = await fs.readFile(bundleOutput.path, "utf-8");
256
+ } catch (error) {
257
+ console.error("Error reading bundle:", error);
258
+ throw new Error("Error reading bundle");
259
+ }
260
+ } else {
261
+ throw new Error("No script source provided"); // Shouldn't reach here due to earlier validation
262
+ }
263
+ // Prepare metadata with bindings
264
+ const metadata: any = {
265
+ bindings: [],
266
+ };
267
+
268
+ // Convert bindings to the format expected by the API
269
+ if (props.bindings && props.bindings.length > 0) {
270
+ for (const binding of props.bindings) {
271
+ const bindingData: any = {
272
+ name: binding.name,
273
+ type: binding.type,
274
+ };
275
+
276
+ // Map binding properties to the API format based on binding type
277
+ switch (binding.type) {
278
+ case "kv_namespace":
279
+ bindingData.namespace_id = (binding as any).namespace_id;
280
+ break;
281
+ case "durable_object_namespace":
282
+ bindingData.class_name = (binding as any).class_name;
283
+ if ((binding as any).namespace_id) {
284
+ bindingData.namespace_id = (binding as any).namespace_id;
285
+ }
286
+ if ((binding as any).script_name) {
287
+ bindingData.script_name = (binding as any).script_name;
288
+ }
289
+ if ((binding as any).environment) {
290
+ bindingData.environment = (binding as any).environment;
291
+ }
292
+ break;
293
+ case "secret_text":
294
+ bindingData.text = (binding as any).text;
295
+ break;
296
+ case "r2_bucket":
297
+ bindingData.bucket_name = (binding as any).bucket_name;
298
+ break;
299
+ case "analytics_engine":
300
+ bindingData.dataset = (binding as any).dataset;
301
+ break;
302
+ case "service":
303
+ bindingData.service = (binding as any).service;
304
+ if ((binding as any).environment) {
305
+ bindingData.environment = (binding as any).environment;
306
+ }
307
+ if ((binding as any).namespace) {
308
+ bindingData.namespace = (binding as any).namespace;
309
+ }
310
+ break;
311
+ case "d1":
312
+ bindingData.id = (binding as any).id;
313
+ break;
314
+ case "queue":
315
+ bindingData.queue_name = (binding as any).queue_name;
316
+ break;
317
+ case "json":
318
+ bindingData.json = (binding as any).json;
319
+ break;
320
+ case "hyperdrive":
321
+ bindingData.id = (binding as any).id;
322
+ break;
323
+ case "vectorize":
324
+ bindingData.index_name = (binding as any).index_name;
325
+ break;
326
+ case "dispatch_namespace":
327
+ bindingData.namespace = (binding as any).namespace;
328
+ if ((binding as any).outbound) {
329
+ bindingData.outbound = (binding as any).outbound;
330
+ }
331
+ break;
332
+ case "mtls_certificate":
333
+ bindingData.certificate_id = (binding as any).certificate_id;
334
+ break;
335
+ case "plain_text":
336
+ bindingData.text = (binding as any).text;
337
+ break;
338
+ case "wasm_module":
339
+ bindingData.module = (binding as any).module;
340
+ break;
341
+ case "tail_consumer":
342
+ bindingData.service = (binding as any).service;
343
+ break;
344
+ }
345
+
346
+ metadata.bindings.push(bindingData);
347
+ }
348
+ }
349
+
350
+ // Create FormData for the upload
351
+ const formData = new FormData();
352
+
353
+ // Determine if we're using ESM or service worker format
354
+ const isEsModule = props.format !== "cjs"; // Default to ESM unless CJS is specified
355
+ const scriptName = isEsModule ? "worker.js" : "script";
356
+ const contentType = isEsModule
357
+ ? "application/javascript+module"
358
+ : "application/javascript";
359
+
360
+ // Prepare metadata
361
+ const updatedMetadata = {
362
+ ...metadata,
363
+ };
364
+
365
+ // Add Durable Object migrations if there are any DO bindings
366
+ const durableObjectBindings = props.bindings?.filter(
367
+ (binding) =>
368
+ binding.type === "durable_object_namespace" &&
369
+ // Only include self-referencing DOs (ones defined in this script)
370
+ (binding as any).script_name === props.name,
371
+ );
372
+
373
+ if (durableObjectBindings && durableObjectBindings.length > 0) {
374
+ // Add migrations metadata for Durable Objects
375
+ updatedMetadata.migrations = {
376
+ new_classes: durableObjectBindings.map(
377
+ (binding) => (binding as any).class_name, // Just the class name as a string
378
+ ),
379
+ deleted_classes: [],
380
+ renamed_classes: [],
381
+ // Version 2 migrations
382
+ new_sqlite_classes: [],
383
+ updated_sqlite_classes: [],
384
+ renamed_sqlite_classes: [],
385
+ };
386
+ }
387
+
388
+ if (isEsModule) {
389
+ // For ES modules format
390
+ updatedMetadata.main_module = scriptName;
391
+ } else {
392
+ // For service worker format (CJS)
393
+ updatedMetadata.body_part = scriptName;
394
+ }
395
+
396
+ // Add the actual script content as a named file part
397
+ formData.append(
398
+ scriptName,
399
+ new Blob([scriptContent], { type: contentType }),
400
+ scriptName,
401
+ );
402
+
403
+ // Add metadata as JSON
404
+ formData.append(
405
+ "metadata",
406
+ new Blob([JSON.stringify(updatedMetadata)], {
407
+ type: "application/json",
408
+ }),
409
+ );
410
+
411
+ // Upload worker script with bindings
412
+ const uploadResponse = await api.put(
413
+ `/accounts/${api.accountId}/workers/scripts/${workerName}`,
414
+ formData,
415
+ );
416
+
417
+ // Check if the upload was successful
418
+ if (!uploadResponse.ok) {
419
+ const errorData: any = await uploadResponse
420
+ .json()
421
+ .catch(() => ({ errors: [{ message: uploadResponse.statusText }] }));
422
+ throw new Error(
423
+ `Error uploading worker script: ${errorData.errors?.[0]?.message || uploadResponse.statusText}`,
424
+ );
425
+ }
426
+
427
+ // Set up routes if provided
428
+ if (props.routes && props.routes.length > 0 && api.zoneId) {
429
+ // First get existing routes
430
+ const routesResponse = await api.get(
431
+ `/zones/${api.zoneId}/workers/routes`,
432
+ );
433
+
434
+ if (routesResponse.ok) {
435
+ const routesData: any = await routesResponse.json();
436
+ const existingRoutes = routesData.result || [];
437
+
438
+ // For each desired route
439
+ for (const pattern of props.routes) {
440
+ const existingRoute = existingRoutes.find(
441
+ (r: any) => r.pattern === pattern,
442
+ );
443
+
444
+ if (existingRoute) {
445
+ // Update if script name doesn't match
446
+ if (existingRoute.script !== workerName) {
447
+ const updateRouteResponse = await api.put(
448
+ `/zones/${api.zoneId}/workers/routes/${existingRoute.id}`,
449
+ {
450
+ pattern,
451
+ script: workerName,
452
+ },
453
+ );
454
+
455
+ if (!updateRouteResponse.ok) {
456
+ console.warn(
457
+ `Failed to update route ${pattern}: ${updateRouteResponse.status} ${updateRouteResponse.statusText}`,
458
+ );
459
+ }
460
+ }
461
+ } else {
462
+ // Create new route
463
+ const createRouteResponse = await api.post(
464
+ `/zones/${api.zoneId}/workers/routes`,
465
+ {
466
+ pattern,
467
+ script: workerName,
468
+ },
469
+ );
470
+
471
+ if (!createRouteResponse.ok) {
472
+ console.warn(
473
+ `Failed to create route ${pattern}: ${createRouteResponse.status} ${createRouteResponse.statusText}`,
474
+ );
475
+ }
476
+ }
477
+ }
478
+ } else {
479
+ console.warn(
480
+ "Could not fetch routes:",
481
+ routesResponse.status,
482
+ routesResponse.statusText,
483
+ );
484
+ }
485
+ }
486
+
487
+ // Handle worker URL if requested
488
+ let workerUrl;
489
+ if (props.url) {
490
+ try {
491
+ // Enable the workers.dev subdomain for this worker
492
+ await api.post(
493
+ `/accounts/${api.accountId}/workers/scripts/${workerName}/subdomain`,
494
+ { enabled: true, previews_enabled: true },
495
+ {
496
+ headers: { "Content-Type": "application/json" },
497
+ },
498
+ );
499
+
500
+ // Get the account's workers.dev subdomain
501
+ const subdomainResponse = await api.get(
502
+ `/accounts/${api.accountId}/workers/subdomain`,
503
+ );
504
+
505
+ if (subdomainResponse.ok) {
506
+ const subdomainData: {
507
+ result: {
508
+ subdomain: string;
509
+ };
510
+ } = await subdomainResponse.json();
511
+ const subdomain = subdomainData.result?.subdomain;
512
+
513
+ if (subdomain) {
514
+ workerUrl = `${workerName}.${subdomain}.workers.dev`;
515
+
516
+ // Add a delay when the subdomain is first created.
517
+ // This is to prevent an issue where a negative cache-hit
518
+ // causes the subdomain to be unavailable for 30 seconds.
519
+ if (ctx.event === "create" || !ctx.output?.url) {
520
+ await new Promise((resolve) => setTimeout(resolve, 3000));
521
+ }
522
+ }
523
+ } else {
524
+ console.warn(
525
+ "Could not fetch workers.dev subdomain:",
526
+ subdomainResponse.status,
527
+ subdomainResponse.statusText,
528
+ );
529
+ }
530
+ } catch (error) {
531
+ console.warn("Failed to enable worker URL:", error);
532
+ }
533
+ } else if (props.url === false && ctx.output?.url) {
534
+ // Explicitly disable URL if it was previously enabled
535
+ try {
536
+ await api.post(
537
+ `/accounts/${api.accountId}/workers/scripts/${workerName}/subdomain`,
538
+ JSON.stringify({ enabled: false }),
539
+ {
540
+ headers: { "Content-Type": "application/json" },
541
+ },
542
+ );
543
+ } catch (error) {
544
+ console.warn("Failed to disable worker URL:", error);
545
+ }
546
+ }
547
+
548
+ // Get current timestamp
549
+ const now = Date.now();
550
+
551
+ // Construct the output
552
+ const output: WorkerOutput = {
553
+ id: workerName,
554
+ name: workerName,
555
+ script: scriptContent,
556
+ format: props.format || "esm", // Include format in the output
557
+ routes: props.routes || [],
558
+ bindings: props.bindings || [],
559
+ production: props.production !== false,
560
+ createdAt: now,
561
+ updatedAt: now,
562
+ url: workerUrl,
563
+ };
564
+
565
+ return output;
566
+ }
567
+ },
568
+ ) {}
package/src/esbuild.ts CHANGED
@@ -62,12 +62,7 @@ export class Bundle extends Resource(
62
62
  "esbuild::Bundle",
63
63
  async (ctx, props: BundleProps): Promise<BundleOutput> => {
64
64
  // Determine output path
65
- const outputPath =
66
- props.outfile ||
67
- path.join(
68
- props.outdir || "dist",
69
- path.basename(props.entryPoint, path.extname(props.entryPoint)) + ".js",
70
- );
65
+ const outputPath = getOutputPath(props);
71
66
 
72
67
  // Ensure output directory exists
73
68
  await fs.promises.mkdir(path.dirname(outputPath), { recursive: true });
@@ -79,22 +74,7 @@ export class Bundle extends Resource(
79
74
  return { path: outputPath, hash: "" };
80
75
  }
81
76
 
82
- // Build the bundle
83
- const result = await esbuild.build({
84
- entryPoints: [props.entryPoint],
85
- outfile: outputPath,
86
- bundle: true,
87
- format: props.format,
88
- target: props.target,
89
- minify: props.minify,
90
- sourcemap: props.sourcemap,
91
- external: props.external,
92
- platform: props.platform,
93
- metafile: true,
94
- write: true,
95
- ...props.options,
96
- });
97
-
77
+ const result = await bundle(props);
98
78
  // Calculate hash of the output
99
79
  const contents = await fs.promises.readFile(outputPath);
100
80
  const hash = crypto.createHash("sha256").update(contents).digest("hex");
@@ -126,3 +106,32 @@ export class Bundle extends Resource(
126
106
  return ctx.get<string>("hash");
127
107
  }
128
108
  }
109
+
110
+ export async function bundle(props: BundleProps) {
111
+ const outputPath = getOutputPath(props);
112
+ // Build the bundle
113
+ return await esbuild.build({
114
+ entryPoints: [props.entryPoint],
115
+ outfile: outputPath,
116
+ bundle: true,
117
+ format: props.format,
118
+ target: props.target,
119
+ minify: props.minify,
120
+ sourcemap: props.sourcemap,
121
+ external: props.external,
122
+ platform: props.platform,
123
+ metafile: true,
124
+ write: true,
125
+ ...props.options,
126
+ });
127
+ }
128
+
129
+ function getOutputPath(props: BundleProps) {
130
+ return (
131
+ props.outfile ||
132
+ path.join(
133
+ props.outdir || "dist",
134
+ path.basename(props.entryPoint, path.extname(props.entryPoint)) + ".js",
135
+ )
136
+ );
137
+ }
package/src/global.ts CHANGED
@@ -14,6 +14,7 @@ const alchemy = path.resolve(process.cwd(), "alchemy.ts");
14
14
  let _config: Config | undefined;
15
15
 
16
16
  const exists =
17
+ // @ts-ignore
17
18
  fs.promises.exists ??
18
19
  async function exists(path: string): Promise<boolean> {
19
20
  try {
package/src/index.ts CHANGED
@@ -1,4 +1,3 @@
1
- export * from "./$";
2
1
  export * from "./alchemize";
3
2
  export * from "./error";
4
3
  export * from "./global";
package/src/main.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  import { parseArgs } from "util";
3
3
 
4
4
  const { values, positionals } = parseArgs({
5
- args: Bun.argv.slice(2),
5
+ args: process.argv.slice(2),
6
6
  options: {
7
7
  help: {
8
8
  type: "boolean",