alchemy 0.44.4 → 0.45.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 (258) hide show
  1. package/lib/alchemy.d.ts +3 -3
  2. package/lib/alchemy.d.ts.map +1 -1
  3. package/lib/alchemy.js +5 -2
  4. package/lib/alchemy.js.map +1 -1
  5. package/lib/build-date.d.ts +1 -1
  6. package/lib/build-date.js +1 -1
  7. package/lib/cloudflare/auth.js +1 -1
  8. package/lib/cloudflare/auth.js.map +1 -1
  9. package/lib/cloudflare/bundle/esbuild.d.ts +16 -0
  10. package/lib/cloudflare/bundle/esbuild.d.ts.map +1 -0
  11. package/lib/cloudflare/bundle/esbuild.js +159 -0
  12. package/lib/cloudflare/bundle/esbuild.js.map +1 -0
  13. package/lib/cloudflare/bundle/fs.d.ts +18 -0
  14. package/lib/cloudflare/bundle/fs.d.ts.map +1 -0
  15. package/lib/cloudflare/bundle/fs.js +75 -0
  16. package/lib/cloudflare/bundle/fs.js.map +1 -0
  17. package/lib/cloudflare/bundle/index.d.ts +3 -0
  18. package/lib/cloudflare/bundle/index.d.ts.map +1 -0
  19. package/lib/cloudflare/bundle/index.js +2 -0
  20. package/lib/cloudflare/bundle/index.js.map +1 -0
  21. package/lib/cloudflare/bundle/inline.d.ts +12 -0
  22. package/lib/cloudflare/bundle/inline.d.ts.map +1 -0
  23. package/lib/cloudflare/bundle/inline.js +32 -0
  24. package/lib/cloudflare/bundle/inline.js.map +1 -0
  25. package/lib/cloudflare/bundle/normalize.d.ts +21 -0
  26. package/lib/cloudflare/bundle/normalize.d.ts.map +1 -0
  27. package/lib/cloudflare/bundle/normalize.js +49 -0
  28. package/lib/cloudflare/bundle/normalize.js.map +1 -0
  29. package/lib/cloudflare/bundle/{nodejs-import-warning-plugin.d.ts → plugin-compat-warning.d.ts} +2 -2
  30. package/lib/cloudflare/bundle/plugin-compat-warning.d.ts.map +1 -0
  31. package/lib/cloudflare/bundle/{nodejs-import-warning-plugin.js → plugin-compat-warning.js} +3 -3
  32. package/lib/cloudflare/bundle/plugin-compat-warning.js.map +1 -0
  33. package/lib/cloudflare/bundle/plugin-hot-reload.d.ts +8 -0
  34. package/lib/cloudflare/bundle/plugin-hot-reload.d.ts.map +1 -0
  35. package/lib/cloudflare/bundle/plugin-hot-reload.js +19 -0
  36. package/lib/cloudflare/bundle/plugin-hot-reload.js.map +1 -0
  37. package/lib/cloudflare/bundle/{nodejs-compat.d.ts → plugin-hybrid-node-compat.d.ts} +2 -2
  38. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.d.ts.map +1 -0
  39. package/lib/cloudflare/bundle/{nodejs-compat.js → plugin-hybrid-node-compat.js} +10 -10
  40. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js.map +1 -0
  41. package/lib/cloudflare/bundle/plugin-wasm.d.ts +3 -0
  42. package/lib/cloudflare/bundle/plugin-wasm.d.ts.map +1 -0
  43. package/lib/cloudflare/bundle/plugin-wasm.js +55 -0
  44. package/lib/cloudflare/bundle/plugin-wasm.js.map +1 -0
  45. package/lib/cloudflare/bundle/shared.d.ts +30 -0
  46. package/lib/cloudflare/bundle/shared.d.ts.map +1 -0
  47. package/lib/cloudflare/bundle/shared.js +34 -0
  48. package/lib/cloudflare/bundle/shared.js.map +1 -0
  49. package/lib/cloudflare/bundle/{nodejs-compat-mode.d.ts → validate-node-compat.d.ts} +8 -4
  50. package/lib/cloudflare/bundle/validate-node-compat.d.ts.map +1 -0
  51. package/lib/cloudflare/bundle/{nodejs-compat-mode.js → validate-node-compat.js} +27 -7
  52. package/lib/cloudflare/bundle/validate-node-compat.js.map +1 -0
  53. package/lib/cloudflare/container.d.ts +231 -0
  54. package/lib/cloudflare/container.d.ts.map +1 -1
  55. package/lib/cloudflare/container.js +7 -0
  56. package/lib/cloudflare/container.js.map +1 -1
  57. package/lib/cloudflare/d1-migrations.d.ts +1 -0
  58. package/lib/cloudflare/d1-migrations.d.ts.map +1 -1
  59. package/lib/cloudflare/d1-migrations.js +18 -6
  60. package/lib/cloudflare/d1-migrations.js.map +1 -1
  61. package/lib/cloudflare/d1-state-store.d.ts +10 -0
  62. package/lib/cloudflare/d1-state-store.d.ts.map +1 -0
  63. package/lib/cloudflare/d1-state-store.js +70 -0
  64. package/lib/cloudflare/d1-state-store.js.map +1 -0
  65. package/lib/cloudflare/do-state-store/internal.js +1 -1
  66. package/lib/cloudflare/do-state-store/internal.js.map +1 -1
  67. package/lib/cloudflare/do-state-store/store.js +1 -1
  68. package/lib/cloudflare/do-state-store/store.js.map +1 -1
  69. package/lib/cloudflare/index.d.ts +3 -2
  70. package/lib/cloudflare/index.d.ts.map +1 -1
  71. package/lib/cloudflare/index.js +3 -2
  72. package/lib/cloudflare/index.js.map +1 -1
  73. package/lib/cloudflare/local-dev-cloudflare-shim.d.ts.map +1 -0
  74. package/lib/cloudflare/{bundle/local-dev-cloudflare-shim.js → local-dev-cloudflare-shim.js} +1 -1
  75. package/lib/cloudflare/local-dev-cloudflare-shim.js.map +1 -0
  76. package/lib/cloudflare/miniflare/http-server.d.ts.map +1 -0
  77. package/lib/cloudflare/miniflare/http-server.js.map +1 -0
  78. package/lib/cloudflare/{worker → miniflare}/miniflare-worker-options.d.ts +4 -3
  79. package/lib/cloudflare/miniflare/miniflare-worker-options.d.ts.map +1 -0
  80. package/lib/cloudflare/{worker → miniflare}/miniflare-worker-options.js +6 -3
  81. package/lib/cloudflare/miniflare/miniflare-worker-options.js.map +1 -0
  82. package/lib/cloudflare/miniflare/miniflare.d.ts.map +1 -0
  83. package/lib/cloudflare/{worker → miniflare}/miniflare.js +1 -1
  84. package/lib/cloudflare/miniflare/miniflare.js.map +1 -0
  85. package/lib/cloudflare/miniflare/mixed-mode.d.ts.map +1 -0
  86. package/lib/cloudflare/{worker → miniflare}/mixed-mode.js +1 -2
  87. package/lib/cloudflare/miniflare/mixed-mode.js.map +1 -0
  88. package/lib/cloudflare/user.js +1 -1
  89. package/lib/cloudflare/user.js.map +1 -1
  90. package/lib/cloudflare/worker/shared.d.ts +4 -0
  91. package/lib/cloudflare/worker/shared.d.ts.map +1 -0
  92. package/lib/cloudflare/worker/{get-worker-template.js → shared.js} +10 -1
  93. package/lib/cloudflare/worker/shared.js.map +1 -0
  94. package/lib/cloudflare/worker/tail.d.ts +18 -0
  95. package/lib/cloudflare/worker/tail.d.ts.map +1 -0
  96. package/lib/cloudflare/worker/tail.js +140 -0
  97. package/lib/cloudflare/worker/tail.js.map +1 -0
  98. package/lib/cloudflare/worker-metadata.d.ts +6 -2
  99. package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
  100. package/lib/cloudflare/worker-metadata.js +7 -23
  101. package/lib/cloudflare/worker-metadata.js.map +1 -1
  102. package/lib/cloudflare/worker-stub.d.ts.map +1 -1
  103. package/lib/cloudflare/worker-stub.js +8 -7
  104. package/lib/cloudflare/worker-stub.js.map +1 -1
  105. package/lib/cloudflare/worker-subdomain.d.ts +31 -0
  106. package/lib/cloudflare/worker-subdomain.d.ts.map +1 -0
  107. package/lib/cloudflare/worker-subdomain.js +55 -0
  108. package/lib/cloudflare/worker-subdomain.js.map +1 -0
  109. package/lib/cloudflare/worker.d.ts +30 -36
  110. package/lib/cloudflare/worker.d.ts.map +1 -1
  111. package/lib/cloudflare/worker.js +641 -569
  112. package/lib/cloudflare/worker.js.map +1 -1
  113. package/lib/fs/file-system-state-store.js +1 -1
  114. package/lib/fs/file-system-state-store.js.map +1 -1
  115. package/lib/scope.d.ts +2 -2
  116. package/lib/scope.d.ts.map +1 -1
  117. package/lib/scope.js +23 -8
  118. package/lib/scope.js.map +1 -1
  119. package/lib/serde.d.ts +1 -1
  120. package/lib/serde.d.ts.map +1 -1
  121. package/lib/serde.js +4 -4
  122. package/lib/serde.js.map +1 -1
  123. package/lib/sqlite/base-sqlite-state-store.d.ts +31 -0
  124. package/lib/sqlite/base-sqlite-state-store.d.ts.map +1 -0
  125. package/lib/sqlite/base-sqlite-state-store.js +124 -0
  126. package/lib/sqlite/base-sqlite-state-store.js.map +1 -0
  127. package/lib/sqlite/index.d.ts +2 -0
  128. package/lib/sqlite/index.d.ts.map +1 -0
  129. package/lib/sqlite/index.js +2 -0
  130. package/lib/sqlite/index.js.map +1 -0
  131. package/lib/sqlite/schema.d.ts +198 -0
  132. package/lib/sqlite/schema.d.ts.map +1 -0
  133. package/lib/sqlite/schema.js +23 -0
  134. package/lib/sqlite/schema.js.map +1 -0
  135. package/lib/sqlite/sqlite-state-store.d.ts +60 -0
  136. package/lib/sqlite/sqlite-state-store.d.ts.map +1 -0
  137. package/lib/sqlite/sqlite-state-store.js +83 -0
  138. package/lib/sqlite/sqlite-state-store.js.map +1 -0
  139. package/lib/test/bun.d.ts.map +1 -1
  140. package/lib/test/bun.js +0 -6
  141. package/lib/test/bun.js.map +1 -1
  142. package/lib/test/vitest.d.ts.map +1 -1
  143. package/lib/test/vitest.js +19 -5
  144. package/lib/test/vitest.js.map +1 -1
  145. package/lib/util/memoize.d.ts +1 -1
  146. package/lib/util/memoize.d.ts.map +1 -1
  147. package/lib/util/memoize.js +11 -1
  148. package/lib/util/memoize.js.map +1 -1
  149. package/package.json +12 -3
  150. package/src/alchemy.ts +8 -6
  151. package/src/build-date.ts +1 -1
  152. package/src/cloudflare/auth.ts +1 -1
  153. package/src/cloudflare/bundle/esbuild.ts +194 -0
  154. package/src/cloudflare/bundle/fs.ts +98 -0
  155. package/src/cloudflare/bundle/index.ts +6 -0
  156. package/src/cloudflare/bundle/inline.ts +42 -0
  157. package/src/cloudflare/bundle/normalize.ts +76 -0
  158. package/src/cloudflare/bundle/{nodejs-import-warning-plugin.ts → plugin-compat-warning.ts} +4 -2
  159. package/src/cloudflare/bundle/plugin-hot-reload.ts +27 -0
  160. package/src/cloudflare/bundle/{nodejs-compat.ts → plugin-hybrid-node-compat.ts} +10 -10
  161. package/src/cloudflare/bundle/plugin-wasm.ts +62 -0
  162. package/src/cloudflare/bundle/shared.ts +73 -0
  163. package/src/cloudflare/bundle/{nodejs-compat-mode.ts → validate-node-compat.ts} +41 -16
  164. package/src/cloudflare/container.ts +296 -0
  165. package/src/cloudflare/d1-migrations.ts +29 -16
  166. package/src/cloudflare/d1-state-store.ts +105 -0
  167. package/src/cloudflare/do-state-store/internal.ts +1 -1
  168. package/src/cloudflare/do-state-store/store.ts +1 -1
  169. package/src/cloudflare/index.ts +3 -2
  170. package/src/cloudflare/{bundle/local-dev-cloudflare-shim.ts → local-dev-cloudflare-shim.ts} +1 -1
  171. package/src/cloudflare/{worker → miniflare}/miniflare-worker-options.ts +14 -6
  172. package/src/cloudflare/{worker → miniflare}/miniflare.ts +1 -1
  173. package/src/cloudflare/{worker → miniflare}/mixed-mode.ts +1 -2
  174. package/src/cloudflare/user.ts +1 -1
  175. package/src/cloudflare/worker/{subdomain.ts → shared.ts} +14 -0
  176. package/src/cloudflare/worker/tail.ts +287 -0
  177. package/src/cloudflare/worker-metadata.ts +18 -26
  178. package/src/cloudflare/worker-stub.ts +9 -7
  179. package/src/cloudflare/worker-subdomain.ts +123 -0
  180. package/src/cloudflare/worker.ts +863 -760
  181. package/src/fs/file-system-state-store.ts +1 -1
  182. package/src/scope.ts +30 -11
  183. package/src/serde.ts +4 -4
  184. package/src/sqlite/base-sqlite-state-store.ts +199 -0
  185. package/src/sqlite/index.ts +1 -0
  186. package/src/sqlite/schema.ts +37 -0
  187. package/src/sqlite/sqlite-state-store.ts +174 -0
  188. package/src/test/bun.ts +0 -8
  189. package/src/test/vitest.ts +19 -8
  190. package/src/util/memoize.ts +13 -1
  191. package/templates/astro/types/env.d.ts +1 -1
  192. package/templates/nuxt/types/env.d.ts +1 -1
  193. package/templates/react-router/types/env.d.ts +1 -1
  194. package/templates/rwsdk/types/env.d.ts +1 -1
  195. package/templates/sveltekit/types/env.d.ts +1 -1
  196. package/templates/tanstack-start/types/env.d.ts +1 -1
  197. package/templates/typescript/types/env.d.ts +1 -1
  198. package/templates/vite/types/env.d.ts +1 -1
  199. package/lib/cloudflare/bundle/alias-plugin.d.ts +0 -6
  200. package/lib/cloudflare/bundle/alias-plugin.d.ts.map +0 -1
  201. package/lib/cloudflare/bundle/alias-plugin.js +0 -36
  202. package/lib/cloudflare/bundle/alias-plugin.js.map +0 -1
  203. package/lib/cloudflare/bundle/build-failures.d.ts +0 -12
  204. package/lib/cloudflare/bundle/build-failures.d.ts.map +0 -1
  205. package/lib/cloudflare/bundle/build-failures.js +0 -45
  206. package/lib/cloudflare/bundle/build-failures.js.map +0 -1
  207. package/lib/cloudflare/bundle/bundle-worker-dev.d.ts +0 -26
  208. package/lib/cloudflare/bundle/bundle-worker-dev.d.ts.map +0 -1
  209. package/lib/cloudflare/bundle/bundle-worker-dev.js +0 -88
  210. package/lib/cloudflare/bundle/bundle-worker-dev.js.map +0 -1
  211. package/lib/cloudflare/bundle/bundle-worker.d.ts +0 -13
  212. package/lib/cloudflare/bundle/bundle-worker.d.ts.map +0 -1
  213. package/lib/cloudflare/bundle/bundle-worker.js +0 -98
  214. package/lib/cloudflare/bundle/bundle-worker.js.map +0 -1
  215. package/lib/cloudflare/bundle/external.d.ts +0 -4
  216. package/lib/cloudflare/bundle/external.d.ts.map +0 -1
  217. package/lib/cloudflare/bundle/external.js +0 -40
  218. package/lib/cloudflare/bundle/external.js.map +0 -1
  219. package/lib/cloudflare/bundle/local-dev-cloudflare-shim.d.ts.map +0 -1
  220. package/lib/cloudflare/bundle/local-dev-cloudflare-shim.js.map +0 -1
  221. package/lib/cloudflare/bundle/nodejs-compat-mode.d.ts.map +0 -1
  222. package/lib/cloudflare/bundle/nodejs-compat-mode.js.map +0 -1
  223. package/lib/cloudflare/bundle/nodejs-compat.d.ts.map +0 -1
  224. package/lib/cloudflare/bundle/nodejs-compat.js.map +0 -1
  225. package/lib/cloudflare/bundle/nodejs-import-warning-plugin.d.ts.map +0 -1
  226. package/lib/cloudflare/bundle/nodejs-import-warning-plugin.js.map +0 -1
  227. package/lib/cloudflare/bundle/wasm-plugin.d.ts +0 -3
  228. package/lib/cloudflare/bundle/wasm-plugin.d.ts.map +0 -1
  229. package/lib/cloudflare/bundle/wasm-plugin.js +0 -53
  230. package/lib/cloudflare/bundle/wasm-plugin.js.map +0 -1
  231. package/lib/cloudflare/worker/get-worker-template.d.ts +0 -2
  232. package/lib/cloudflare/worker/get-worker-template.d.ts.map +0 -1
  233. package/lib/cloudflare/worker/get-worker-template.js.map +0 -1
  234. package/lib/cloudflare/worker/http-server.d.ts.map +0 -1
  235. package/lib/cloudflare/worker/http-server.js.map +0 -1
  236. package/lib/cloudflare/worker/miniflare-worker-options.d.ts.map +0 -1
  237. package/lib/cloudflare/worker/miniflare-worker-options.js.map +0 -1
  238. package/lib/cloudflare/worker/miniflare.d.ts.map +0 -1
  239. package/lib/cloudflare/worker/miniflare.js.map +0 -1
  240. package/lib/cloudflare/worker/mixed-mode.d.ts.map +0 -1
  241. package/lib/cloudflare/worker/mixed-mode.js.map +0 -1
  242. package/lib/cloudflare/worker/subdomain.d.ts +0 -3
  243. package/lib/cloudflare/worker/subdomain.d.ts.map +0 -1
  244. package/lib/cloudflare/worker/subdomain.js +0 -10
  245. package/lib/cloudflare/worker/subdomain.js.map +0 -1
  246. package/src/cloudflare/bundle/alias-plugin.ts +0 -48
  247. package/src/cloudflare/bundle/build-failures.ts +0 -55
  248. package/src/cloudflare/bundle/bundle-worker-dev.ts +0 -129
  249. package/src/cloudflare/bundle/bundle-worker.ts +0 -150
  250. package/src/cloudflare/bundle/external.ts +0 -41
  251. package/src/cloudflare/bundle/wasm-plugin.ts +0 -57
  252. package/src/cloudflare/worker/get-worker-template.ts +0 -14
  253. /package/lib/cloudflare/{bundle/local-dev-cloudflare-shim.d.ts → local-dev-cloudflare-shim.d.ts} +0 -0
  254. /package/lib/cloudflare/{worker → miniflare}/http-server.d.ts +0 -0
  255. /package/lib/cloudflare/{worker → miniflare}/http-server.js +0 -0
  256. /package/lib/cloudflare/{worker → miniflare}/miniflare.d.ts +0 -0
  257. /package/lib/cloudflare/{worker → miniflare}/mixed-mode.d.ts +0 -0
  258. /package/src/cloudflare/{worker → miniflare}/http-server.ts +0 -0
@@ -1,6 +1,8 @@
1
+ import kleur from "kleur";
1
2
  import { spawn } from "node:child_process";
2
3
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync, } from "node:fs";
3
- import path from "node:path";
4
+ import { isDeepStrictEqual } from "node:util";
5
+ import path from "pathe";
4
6
  import { BUILD_DATE } from "../build-date.js";
5
7
  import { InnerResourceScope, Resource, ResourceKind } from "../resource.js";
6
8
  import { getBindKey, tryGetBinding } from "../runtime/bind.js";
@@ -9,7 +11,6 @@ import { bootstrapPlugin } from "../runtime/plugin.js";
9
11
  import { Scope } from "../scope.js";
10
12
  import { Secret, secret } from "../secret.js";
11
13
  import { serializeScope } from "../serde.js";
12
- import { getContentType } from "../util/content-type.js";
13
14
  import { DeferredPromise } from "../util/deferred-promise.js";
14
15
  import { logger } from "../util/logger.js";
15
16
  import { withExponentialBackoff } from "../util/retry.js";
@@ -17,14 +18,15 @@ import { CloudflareApiError, handleApiError } from "./api-error.js";
17
18
  import { createCloudflareApi, } from "./api.js";
18
19
  import { Json, } from "./bindings.js";
19
20
  import { isBucket } from "./bucket.js";
20
- import { createWorkerDevContext } from "./bundle/bundle-worker-dev.js";
21
- import { bundleWorkerScript, } from "./bundle/bundle-worker.js";
21
+ import { normalizeWorkerBundle, } from "./bundle/index.js";
22
+ import { wrap } from "./bundle/normalize.js";
22
23
  import { ContainerApplication } from "./container.js";
23
24
  import { CustomDomain } from "./custom-domain.js";
24
25
  import { isD1Database } from "./d1-database.js";
25
26
  import { DurableObjectNamespace, isDurableObjectNamespace, } from "./durable-object-namespace.js";
26
27
  import { isQueueEventSource, } from "./event-source.js";
27
28
  import { isKVNamespace } from "./kv-namespace.js";
29
+ import { miniflareServer } from "./miniflare/miniflare.js";
28
30
  import { isPipeline } from "./pipeline.js";
29
31
  import { QueueConsumer, deleteQueueConsumer, listQueueConsumersForWorker, } from "./queue-consumer.js";
30
32
  import { isQueue } from "./queue.js";
@@ -33,8 +35,8 @@ import { isVectorizeIndex } from "./vectorize-index.js";
33
35
  import { uploadAssets } from "./worker-assets.js";
34
36
  import { bumpMigrationTagVersion, prepareWorkerMetadata, } from "./worker-metadata.js";
35
37
  import { WorkerStub, isWorkerStub } from "./worker-stub.js";
36
- import { miniflareServer } from "./worker/miniflare.js";
37
- import { getAccountSubdomain } from "./worker/subdomain.js";
38
+ import { WorkerSubdomain, disableWorkerSubdomain } from "./worker-subdomain.js";
39
+ import { createTail } from "./worker/tail.js";
38
40
  import { Workflow, isWorkflow, upsertWorkflow } from "./workflow.js";
39
41
  export function isWorker(resource) {
40
42
  return resource[ResourceKind] === "cloudflare::Worker";
@@ -224,174 +226,60 @@ export const DEFAULT_COMPATIBILITY_DATE = BUILD_DATE;
224
226
  export const _Worker = Resource("cloudflare::Worker", {
225
227
  alwaysUpdate: true,
226
228
  }, async function (id, props) {
227
- if (props.noBundle && !props.entrypoint) {
228
- throw new Error("entrypoint must be provided when noBundle is true");
229
- }
230
- // Use the provided name
231
- const workerName = props.name ?? id;
232
229
  if (props.projectRoot) {
233
230
  logger.warn("projectRoot is deprecated, use cwd instead");
234
231
  props.cwd = props.projectRoot;
235
232
  }
236
- const cwd = props.cwd ? path.resolve(props.cwd) : process.cwd();
233
+ const cwd = path.resolve(props.cwd ?? process.cwd());
237
234
  const relativeCwd = cwd === process.cwd() ? undefined : path.relative(process.cwd(), cwd);
235
+ const workerName = props.name ?? id;
238
236
  const compatibilityDate = props.compatibilityDate ?? DEFAULT_COMPATIBILITY_DATE;
239
237
  const compatibilityFlags = props.compatibilityFlags ?? [];
240
- if (this.scope.dev && this.phase !== "delete" && props.dev !== false) {
241
- // Get current timestamp
242
- const now = Date.now();
243
- if (typeof props.dev === "object" && "command" in props.dev) {
244
- upsertDevCommand({
245
- id,
246
- command: props.dev.command,
247
- cwd: props.dev.cwd ?? cwd,
248
- env: props.env ?? {},
249
- });
250
- return this({
251
- type: "service",
252
- id,
253
- entrypoint: props.entrypoint,
254
- name: workerName,
255
- cwd: relativeCwd,
256
- compatibilityDate,
257
- compatibilityFlags,
258
- format: props.format || "esm", // Include format in the output
259
- bindings: props.bindings ?? {},
260
- env: props.env,
261
- observability: props.observability,
262
- createdAt: now,
263
- updatedAt: now,
264
- eventSources: props.eventSources,
265
- url: props.dev.url,
266
- dev: props.dev,
267
- // Include assets configuration in the output
268
- assets: props.assets,
269
- // Include cron triggers in the output
270
- crons: props.crons,
271
- // phantom property
272
- Env: undefined,
273
- });
274
- }
275
- const sharedOptions = {
276
- name: workerName,
277
- compatibilityDate,
278
- compatibilityFlags,
279
- bindings: props.bindings ?? {},
280
- port: typeof props.dev === "object" ? props.dev.port : undefined,
281
- };
238
+ const dispatchNamespace = typeof props.namespace === "string"
239
+ ? props.namespace
240
+ : props.namespace?.namespaceName;
241
+ const dev = normalizeDev(this, props.dev);
242
+ const [bundle, error] = wrap(() => normalizeWorkerBundle({
243
+ entrypoint: props.entrypoint,
244
+ script: props.script,
245
+ format: props.format,
246
+ noBundle: props.noBundle,
247
+ rules: "rules" in props ? props.rules : undefined,
248
+ bundle: props.bundle,
249
+ cwd,
250
+ compatibilityDate,
251
+ compatibilityFlags,
252
+ outdir: props.bundle?.outdir ??
253
+ path.join(process.cwd(), ".alchemy", ...this.scope.chain, id),
254
+ sourceMap: "sourceMap" in props ? props.sourceMap : undefined,
255
+ }));
256
+ if (dev.local) {
282
257
  let url;
283
- // If entrypoint is provided, set up hot reloading with esbuild context
284
- if (props.entrypoint) {
285
- const startPromise = new DeferredPromise();
286
- await createWorkerDevContext(workerName, {
287
- ...props,
288
- entrypoint: props.entrypoint,
289
- cwd,
290
- compatibilityDate,
291
- compatibilityFlags,
292
- }, {
293
- onBuildStart: () => {
294
- logger.task("miniflare-server", {
295
- message: `${startPromise.status === "pending" ? "Building" : "Rebuilding"}`,
296
- status: "pending",
297
- resource: id,
298
- prefix: "build",
299
- prefixColor: "cyanBright",
300
- });
301
- },
302
- onBuildEnd: async (newScript) => {
303
- try {
304
- // Hot reload callback - update the miniflare worker
305
- const server = await miniflareServer.push({
306
- ...sharedOptions,
307
- script: newScript,
308
- });
309
- if (startPromise.status === "pending") {
310
- logger.task("miniflare-server", {
311
- message: `Started Miniflare server at ${server.url}`,
312
- status: "success",
313
- resource: id,
314
- prefix: "ready",
315
- prefixColor: "cyanBright",
316
- });
317
- startPromise.resolve(server.url);
318
- }
319
- else {
320
- logger.task("miniflare-server", {
321
- message: `Updated Miniflare server at ${server.url}`,
322
- status: "success",
323
- resource: id,
324
- prefix: "reload",
325
- prefixColor: "cyanBright",
326
- });
327
- }
328
- }
329
- catch (error) {
330
- if (startPromise.status === "pending") {
331
- logger.error(error);
332
- logger.task("miniflare-server", {
333
- message: "Failed to start Miniflare server",
334
- status: "failure",
335
- resource: id,
336
- prefix: "error",
337
- prefixColor: "redBright",
338
- });
339
- startPromise.reject(new Error(`Failed to start Miniflare server for worker "${workerName}"`, { cause: error }));
340
- }
341
- else {
342
- logger.task("miniflare-server", {
343
- message: "Failed to update Miniflare server",
344
- status: "failure",
345
- resource: id,
346
- prefix: "error",
347
- prefixColor: "redBright",
348
- });
349
- logger.error(error);
350
- }
351
- }
352
- },
353
- onBuildError: (errors) => {
354
- if (startPromise.status === "pending") {
355
- logger.task("miniflare-server", {
356
- message: "Failed to build worker",
357
- status: "failure",
358
- resource: id,
359
- prefix: "error",
360
- prefixColor: "redBright",
361
- });
362
- startPromise.reject(errors);
363
- }
364
- else {
365
- logger.task("miniflare-server", {
366
- message: "Failed to rebuild worker",
367
- status: "failure",
368
- resource: id,
369
- prefix: "error",
370
- prefixColor: "redBright",
371
- });
372
- logger.error(errors);
373
- }
374
- },
375
- });
376
- url = await startPromise.value;
258
+ if (error) {
259
+ throw error;
377
260
  }
378
- else {
379
- // Fallback to one-time bundling for inline scripts
380
- const scriptContent = props.script ??
381
- (await bundleWorkerScript({
382
- name: workerName,
383
- ...props,
384
- cwd,
261
+ switch (dev.type) {
262
+ case "command":
263
+ createDevCommand({
264
+ id,
265
+ command: dev.command,
266
+ cwd: dev.cwd ?? process.cwd(),
267
+ env: props.env ?? {},
268
+ });
269
+ url = dev.url;
270
+ break;
271
+ case "miniflare": {
272
+ url = await createMiniflare({
273
+ id,
274
+ workerName,
385
275
  compatibilityDate,
386
276
  compatibilityFlags,
387
- }));
388
- const server = await miniflareServer.push({
389
- ...sharedOptions,
390
- script: typeof scriptContent === "string"
391
- ? scriptContent
392
- : scriptContent[props.entrypoint].toString(),
393
- });
394
- url = server.url;
277
+ bindings: props.bindings,
278
+ bundle,
279
+ port: dev.port,
280
+ });
281
+ break;
282
+ }
395
283
  }
396
284
  return this({
397
285
  type: "service",
@@ -402,11 +290,11 @@ export const _Worker = Resource("cloudflare::Worker", {
402
290
  compatibilityDate,
403
291
  compatibilityFlags,
404
292
  format: props.format || "esm", // Include format in the output
405
- bindings: props.bindings ?? {},
293
+ bindings: normalizeExportBindings(workerName, props.bindings),
406
294
  env: props.env,
407
295
  observability: props.observability,
408
- createdAt: now,
409
- updatedAt: now,
296
+ createdAt: this.output?.createdAt ?? Date.now(),
297
+ updatedAt: Date.now(),
410
298
  eventSources: props.eventSources,
411
299
  url,
412
300
  dev: props.dev,
@@ -418,59 +306,48 @@ export const _Worker = Resource("cloudflare::Worker", {
418
306
  Env: undefined,
419
307
  });
420
308
  }
421
- // Create Cloudflare API client with automatic account discovery
422
309
  const api = await createCloudflareApi(props);
423
- const uploadWorkerScript = async (props) => {
424
- // Get the script content - either from props.script, or by bundling
425
- const scriptBundle = props.script ??
426
- (await bundleWorkerScript({
427
- ...props,
428
- name: workerName,
429
- cwd,
430
- compatibilityDate,
431
- compatibilityFlags,
432
- }));
433
- // Find any assets bindings
434
- const assetsBindings = [];
435
- const workflowsBindings = [];
436
- const containersBindings = [];
437
- if (props.bindings) {
438
- for (const [bindingName, binding] of Object.entries(props.bindings)) {
439
- if (typeof binding === "object") {
440
- if (binding.type === "assets") {
441
- assetsBindings.push({ name: bindingName, assets: binding });
442
- }
443
- else if (binding.type === "workflow") {
444
- workflowsBindings.push(binding);
445
- }
446
- else if (binding.type === "container") {
447
- containersBindings.push(binding);
448
- }
449
- }
310
+ if (this.phase === "delete") {
311
+ await bundle?.delete?.();
312
+ if (!props.version) {
313
+ await deleteQueueConsumers(api, workerName);
314
+ await deleteWorker(api, {
315
+ scriptName: workerName,
316
+ dispatchNamespace,
317
+ });
318
+ }
319
+ return this.destroy();
320
+ }
321
+ else if (error) {
322
+ throw error;
323
+ }
324
+ let assetsBinding;
325
+ const workflowsBindings = [];
326
+ const containersBindings = [];
327
+ for (const binding of Object.values(props.bindings ?? {})) {
328
+ if (typeof binding === "object") {
329
+ if (binding.type === "assets") {
330
+ assetsBinding = binding;
331
+ }
332
+ else if (binding.type === "workflow") {
333
+ workflowsBindings.push(binding);
334
+ }
335
+ else if (binding.type === "container") {
336
+ containersBindings.push(binding);
450
337
  }
451
338
  }
452
- // Get dispatch namespace if specified
453
- const dispatchNamespace = props.namespace
454
- ? typeof props.namespace === "string"
455
- ? props.namespace
456
- : props.namespace.namespace
457
- : undefined;
458
- // Upload any assets and get completion tokens
459
- let assetUploadResult;
460
- if (assetsBindings.length > 0) {
461
- // We'll use the first asset binding for now
462
- // In the future, we might want to support multiple asset bindings
463
- const assetBinding = assetsBindings[0];
464
- // Upload the assets and get the completion token
465
- assetUploadResult = await uploadAssets(api, {
339
+ }
340
+ const putWorkerWithAssets = async (props, scriptBundle) => {
341
+ const assetUploadResult = assetsBinding
342
+ ? await uploadAssets(api, {
466
343
  workerName,
467
- assets: assetBinding.assets,
344
+ assets: assetsBinding,
468
345
  assetConfig: props.assets,
469
346
  namespace: dispatchNamespace,
470
- });
471
- }
347
+ })
348
+ : undefined;
472
349
  // Deploy worker (either as version or live worker)
473
- const versionResult = await putWorker(api, {
350
+ return await putWorker(api, {
474
351
  ...props,
475
352
  workerName,
476
353
  scriptBundle,
@@ -480,115 +357,14 @@ export const _Worker = Resource("cloudflare::Worker", {
480
357
  compatibilityFlags,
481
358
  assetUploadResult,
482
359
  });
483
- versionResult.versionId;
484
- for (const workflow of workflowsBindings) {
485
- if (workflow.scriptName === undefined ||
486
- workflow.scriptName === workerName) {
487
- await upsertWorkflow(api, {
488
- workflowName: workflow.workflowName,
489
- className: workflow.className,
490
- scriptName: workflow.scriptName ?? workerName,
491
- });
492
- }
493
- }
494
- for (const container of containersBindings) {
495
- async function findNamespaceId() {
496
- const response = await api.get(`/accounts/${api.accountId}/workers/scripts/${workerName}/versions/${versionResult.deploymentId}`);
497
- const result = (await response.json());
498
- const namespaceId = result.result.resources.bindings.find((binding) => binding.type === "durable_object_namespace" &&
499
- binding.class_name === container.className)?.namespace_id;
500
- if (!namespaceId) {
501
- throw new Error(`Namespace ID not found for container ${container.id}`);
502
- }
503
- return namespaceId;
504
- }
505
- await ContainerApplication(container.id, {
506
- accountId: props.accountId,
507
- apiKey: props.apiKey,
508
- apiToken: props.apiToken,
509
- baseUrl: props.baseUrl,
510
- email: props.email,
511
- image: container.image,
512
- name: container.id,
513
- instanceType: container.instanceType,
514
- observability: container.observability,
515
- durableObjects: {
516
- namespaceId: await findNamespaceId(),
517
- },
518
- schedulingPolicy: container.schedulingPolicy,
519
- });
520
- }
521
- await Promise.all(props.eventSources?.map((eventSource) => {
522
- if (isQueue(eventSource) || isQueueEventSource(eventSource)) {
523
- const queue = isQueue(eventSource)
524
- ? eventSource
525
- : eventSource.queue;
526
- return QueueConsumer(`${queue.id}-consumer`, {
527
- queue,
528
- scriptName: workerName,
529
- accountId: api.accountId,
530
- adopt: props.adopt,
531
- settings: isQueueEventSource(eventSource)
532
- ? eventSource.settings
533
- : queue.dlq
534
- ? { deadLetterQueue: queue.dlq }
535
- : undefined,
536
- });
537
- }
538
- throw new Error(`Unsupported event source type: ${eventSource}`);
539
- }) ?? []);
540
- // Handle worker URL if requested
541
- let workerUrl;
542
- if (props.version) {
543
- // For versions, use the preview URL if available
544
- workerUrl = versionResult?.previewUrl;
545
- }
546
- else if (!props.namespace) {
547
- // namespaces don't support URLs
548
- // For regular workers, use the normal URL configuration
549
- workerUrl = await configureURL(this, api, {
550
- ...props,
551
- workerName,
552
- url: props.url ?? true,
553
- });
554
- }
555
- // Get current timestamp
556
- const now = Date.now();
557
- // Update cron triggers
558
- if (props.crons) {
559
- const res = await api.put(`/accounts/${api.accountId}/workers/scripts/${workerName}/schedules`, props.crons.map((cron) => ({ cron })));
560
- if (!res.ok) {
561
- await handleApiError(res, "updating cron triggers", "worker", workerName);
562
- }
563
- }
564
- return { scriptBundle, workerUrl, now, versionResult };
565
360
  };
566
- if (this.phase === "delete") {
567
- // only delete the worker if it's not a version
568
- if (!props.version) {
569
- // Delete any queue consumers attached to this worker first
570
- await deleteQueueConsumers(api, workerName);
571
- await withExponentialBackoff(() => deleteWorker(api, {
572
- workerName,
573
- namespace: typeof props.namespace === "string"
574
- ? props.namespace
575
- : props.namespace?.namespaceId,
576
- url: this.output.url,
577
- }), (err) => (err.status === 400 &&
578
- err.message.includes("is still referenced by service bindings in Workers")) ||
579
- err.status === 500 ||
580
- err.status === 503, 10, 100);
581
- }
582
- return this.destroy();
583
- }
584
361
  if (this.phase === "create") {
585
362
  if (props.version) {
586
363
  // When version is specified, we adopt existing workers or create them if they don't exist
587
- const workerExists = await checkWorkerExists(api, workerName);
588
- if (!workerExists) {
364
+ if (!(await workerExists(api, workerName))) {
589
365
  // Create the base worker first if it doesn't exist
590
366
  const baseWorkerProps = { ...props, version: undefined };
591
- await uploadWorkerScript(baseWorkerProps);
367
+ await putWorkerWithAssets(baseWorkerProps, await bundle.create());
592
368
  }
593
369
  // We always "adopt" when publishing versions
594
370
  }
@@ -596,73 +372,136 @@ export const _Worker = Resource("cloudflare::Worker", {
596
372
  await assertWorkerDoesNotExist(api, workerName);
597
373
  }
598
374
  }
599
- const { workerUrl, now } = await uploadWorkerScript(props);
600
- function ensureNoDuplicates(name, items) {
601
- const duplicates = items.filter((pattern, index) => items.indexOf(pattern) !== index);
602
- if (duplicates.length > 0) {
603
- throw new Error(`Duplicate ${name} found: ${duplicates.join(", ")}`);
604
- }
605
- }
606
- let createdDomains;
607
- if (props.domains?.length) {
608
- ensureNoDuplicates("Custom Domain", props.domains.map((domain) => typeof domain === "string" ? domain : domain.domainName));
609
- createdDomains = await Promise.all(props.domains.map(async (customDomain) => {
610
- const domainName = typeof customDomain === "string"
611
- ? customDomain
612
- : customDomain.domainName;
613
- return await CustomDomain(domainName, {
614
- workerName,
615
- name: domainName,
616
- zoneId: typeof customDomain === "string"
617
- ? undefined
618
- : customDomain.zoneId,
619
- adopt: typeof customDomain === "string"
620
- ? (props.adopt ?? false)
621
- : (customDomain.adopt ?? props.adopt),
622
- });
375
+ let putWorkerResult;
376
+ if (dev.type === "remote") {
377
+ // todo(john): clean this up and add log tail
378
+ const controller = new AbortController();
379
+ cleanups.push(() => controller.abort());
380
+ const watcher = await bundle.watch(controller.signal);
381
+ const promise = new DeferredPromise();
382
+ void watcher.pipeTo(new WritableStream({
383
+ async write(chunk) {
384
+ switch (chunk.type) {
385
+ case "start":
386
+ logger.task("", {
387
+ message: "start",
388
+ status: "pending",
389
+ resource: id,
390
+ prefix: "build",
391
+ prefixColor: "cyanBright",
392
+ });
393
+ break;
394
+ case "end": {
395
+ if (promise.status === "pending") {
396
+ await putWorkerWithAssets(props, chunk.result)
397
+ .then((result) => promise.resolve(result))
398
+ .catch((error) => {
399
+ controller.abort();
400
+ promise.reject(error);
401
+ });
402
+ return;
403
+ }
404
+ logger.task("", {
405
+ message: "reload",
406
+ status: "success",
407
+ resource: id,
408
+ prefix: "build",
409
+ prefixColor: "cyanBright",
410
+ });
411
+ await putWorker(api, {
412
+ ...props,
413
+ workerName,
414
+ scriptBundle: chunk.result,
415
+ dispatchNamespace,
416
+ version: props.version,
417
+ compatibilityDate,
418
+ compatibilityFlags,
419
+ assetUploadResult: assetsBinding
420
+ ? {
421
+ keepAssets: true,
422
+ assetConfig: props.assets,
423
+ }
424
+ : undefined,
425
+ unstable_cacheWorkerSettings: true,
426
+ });
427
+ logger.task("", {
428
+ message: "updated",
429
+ status: "success",
430
+ resource: id,
431
+ prefix: "build",
432
+ prefixColor: "greenBright",
433
+ });
434
+ break;
435
+ }
436
+ case "error":
437
+ logger.task("", {
438
+ message: "error",
439
+ status: "failure",
440
+ resource: id,
441
+ prefix: "build",
442
+ prefixColor: "redBright",
443
+ });
444
+ logger.error(chunk.errors);
445
+ break;
446
+ }
447
+ },
623
448
  }));
449
+ putWorkerResult = await promise.value;
450
+ const tail = await createTail(api, id, workerName);
451
+ cleanups.push(() => tail.close());
624
452
  }
625
- // Create routes if provided and capture their outputs
626
- let createdRoutes;
627
- if (props.routes && props.routes.length > 0) {
628
- ensureNoDuplicates("Route", props.routes.map((route) => typeof route === "string" ? route : route.pattern));
629
- // Create Route resources for each route and capture their outputs
630
- createdRoutes = await Promise.all(props.routes.map(async (routeConfig) => {
631
- const pattern = typeof routeConfig === "string" ? routeConfig : routeConfig.pattern;
632
- return await Route(pattern, {
633
- pattern,
634
- script: workerName,
635
- zoneId: typeof routeConfig === "string" ? undefined : routeConfig.zoneId, // Route resource will handle inference if not provided
636
- adopt: typeof routeConfig === "string"
637
- ? (props.adopt ?? false)
638
- : (routeConfig.adopt ?? props.adopt),
639
- accountId: props.accountId,
640
- apiKey: props.apiKey,
641
- apiToken: props.apiToken,
642
- baseUrl: props.baseUrl,
643
- email: props.email,
644
- });
645
- }));
453
+ else {
454
+ putWorkerResult = await putWorkerWithAssets(props, await bundle.create());
646
455
  }
647
- function exportBindings() {
648
- return Object.fromEntries(Object.entries(props.bindings ?? {}).map(([bindingName, binding]) => [
649
- bindingName,
650
- isDurableObjectNamespace(binding) &&
651
- binding.scriptName === undefined
652
- ? new DurableObjectNamespace(binding.id, {
653
- ...binding,
654
- // re-export this binding mapping to the host worker (this worker)
655
- scriptName: workerName,
656
- })
657
- : isWorkflow(binding) && binding.scriptName === undefined
658
- ? new Workflow(binding.id, {
659
- ...binding,
660
- // re-export this binding mapping to the host worker (this worker)
661
- scriptName: workerName,
662
- })
663
- : binding,
664
- ]));
456
+ const tasks = [];
457
+ for (const workflow of workflowsBindings) {
458
+ if (workflow.scriptName === undefined ||
459
+ workflow.scriptName === workerName) {
460
+ tasks.push(upsertWorkflow(api, {
461
+ workflowName: workflow.workflowName,
462
+ className: workflow.className,
463
+ scriptName: workflow.scriptName ?? workerName,
464
+ }));
465
+ }
466
+ }
467
+ if (containersBindings.length > 0) {
468
+ tasks.push(getVersionMetadata(api, workerName, putWorkerResult.deployment_id).then((versionMetadata) => provisionContainers(api, {
469
+ scriptName: workerName,
470
+ containers: containersBindings,
471
+ bindings: versionMetadata.resources.bindings,
472
+ })));
473
+ }
474
+ if (!isDeepStrictEqual(props.crons, this.output?.crons)) {
475
+ tasks.push(api.put(`/accounts/${api.accountId}/workers/scripts/${workerName}/schedules`, props.crons?.map((cron) => ({ cron })) ?? []));
665
476
  }
477
+ tasks.push(provisionEventSources(api, {
478
+ scriptName: workerName,
479
+ eventSources: props.eventSources,
480
+ }));
481
+ const [domains, routes, subdomain] = await Promise.all([
482
+ // TODO: can you provision domains and routes in parallel, or is there a dependency?
483
+ provisionDomains(api, {
484
+ scriptName: workerName,
485
+ adopt: props.adopt,
486
+ domains: props.domains,
487
+ }),
488
+ provisionRoutes(api, {
489
+ scriptName: workerName,
490
+ adopt: props.adopt,
491
+ routes: props.routes,
492
+ }),
493
+ provisionSubdomain(api, {
494
+ scriptName: workerName,
495
+ enable: props.url ?? dispatchNamespace === undefined,
496
+ previewVersionId: props.version && putWorkerResult.metadata.has_preview
497
+ ? putWorkerResult.id
498
+ : undefined,
499
+ retain: !!props.version,
500
+ forceDelete: this.phase === "create" && !!props.adopt && props.url === false,
501
+ }),
502
+ ...tasks,
503
+ ]);
504
+ const now = Date.now();
666
505
  // Construct the output
667
506
  return this({
668
507
  ...props,
@@ -674,22 +513,22 @@ export const _Worker = Resource("cloudflare::Worker", {
674
513
  compatibilityDate,
675
514
  compatibilityFlags,
676
515
  format: props.format || "esm", // Include format in the output
677
- bindings: exportBindings(),
516
+ bindings: normalizeExportBindings(workerName, props.bindings),
678
517
  env: props.env,
679
518
  observability: props.observability,
680
- createdAt: now,
519
+ createdAt: this.output?.createdAt ?? now,
681
520
  updatedAt: now,
682
521
  eventSources: props.eventSources,
683
- url: workerUrl,
522
+ url: subdomain?.url,
684
523
  dev: props.dev,
685
524
  // Include assets configuration in the output
686
525
  assets: props.assets,
687
526
  // Include cron triggers in the output
688
527
  crons: props.crons,
689
528
  // Include the created routes in the output
690
- routes: createdRoutes,
529
+ routes,
691
530
  // Include the created domains in the output
692
- domains: createdDomains,
531
+ domains,
693
532
  // Include the dispatch namespace in the output
694
533
  namespace: props.namespace,
695
534
  // Include version information in the output
@@ -698,31 +537,324 @@ export const _Worker = Resource("cloudflare::Worker", {
698
537
  Env: undefined,
699
538
  });
700
539
  });
701
- export async function deleteWorker(api, props) {
702
- const workerName = props.workerName;
703
- // Delete worker
704
- const deleteResponse = await api.delete(props.namespace
705
- ? `/accounts/${api.accountId}/workers/dispatch/namespaces/${props.namespace}/scripts/${workerName}?force=true`
706
- : `/accounts/${api.accountId}/workers/scripts/${workerName}?force=true`);
707
- // Check for success (2xx status code)
708
- if (!deleteResponse.ok && deleteResponse.status !== 404) {
709
- await handleApiError(deleteResponse, "delete", "worker", workerName);
710
- }
711
- // Disable the URL if it was enabled
712
- if (props.url) {
713
- try {
714
- await api.post(`/accounts/${api.accountId}/workers/scripts/${workerName}/subdomain`, JSON.stringify({ enabled: false }), {
715
- headers: { "Content-Type": "application/json" },
540
+ const cleanups = [];
541
+ let exiting = false;
542
+ process.on("SIGINT", async () => {
543
+ if (cleanups.length > 0) {
544
+ if (!exiting) {
545
+ exiting = true;
546
+ logger.log(`\n${kleur.gray("Exiting...")}`);
547
+ }
548
+ // TODO: for some reason this runs twice...
549
+ // and this whole thing feels hacky anyway
550
+ await Promise.allSettled(cleanups.map((cleanup) => cleanup()));
551
+ }
552
+ process.exit(0);
553
+ });
554
+ const normalizeDev = (ctx, dev) => {
555
+ if (!ctx.scope.dev || ctx.phase === "delete" || dev === false) {
556
+ return {
557
+ type: "none",
558
+ local: false,
559
+ };
560
+ }
561
+ const devObj = dev === true ? {} : (dev ?? {});
562
+ if ("command" in devObj) {
563
+ // Commands are always local
564
+ return {
565
+ type: "command",
566
+ ...devObj,
567
+ local: true,
568
+ };
569
+ }
570
+ if (devObj.remote === false || ctx.scope.dev === "prefer-local") {
571
+ return {
572
+ type: "miniflare",
573
+ port: devObj.port,
574
+ local: true,
575
+ };
576
+ }
577
+ return {
578
+ type: "remote",
579
+ local: false,
580
+ };
581
+ };
582
+ const assertUnique = (inputs, key, message) => {
583
+ const ids = inputs.map((input) => input[key]);
584
+ const duplicates = ids.filter((id, index) => ids.indexOf(id) !== index);
585
+ if (duplicates.length > 0) {
586
+ throw new Error(`Duplicate ${message} found: ${duplicates.join(", ")}`);
587
+ }
588
+ };
589
+ const normalizeExportBindings = (scriptName, bindings = {}) => {
590
+ return Object.fromEntries(Object.entries(bindings).map(([bindingName, binding]) => [
591
+ bindingName,
592
+ isDurableObjectNamespace(binding) && binding.scriptName === undefined
593
+ ? new DurableObjectNamespace(binding.id, {
594
+ ...binding,
595
+ // re-export this binding mapping to the host worker (this worker)
596
+ scriptName,
597
+ })
598
+ : isWorkflow(binding) && binding.scriptName === undefined
599
+ ? new Workflow(binding.id, {
600
+ ...binding,
601
+ // re-export this binding mapping to the host worker (this worker)
602
+ scriptName,
603
+ })
604
+ : binding,
605
+ ]));
606
+ };
607
+ const normalizeApiOptions = (api) => ({
608
+ accountId: api.accountId,
609
+ apiKey: api.apiKey,
610
+ apiToken: api.apiToken,
611
+ email: api.email,
612
+ baseUrl: api.baseUrl,
613
+ });
614
+ async function provisionContainers(api, props) {
615
+ if (!props.containers?.length) {
616
+ return;
617
+ }
618
+ return await Promise.all(props.containers.map((container) => {
619
+ const namespaceId = props.bindings.find((binding) => binding.type === "durable_object_namespace" &&
620
+ binding.class_name === container.className)?.namespace_id;
621
+ if (!namespaceId) {
622
+ throw new Error(`Namespace ID not found for container ${container.id}`);
623
+ }
624
+ return ContainerApplication(container.id, {
625
+ image: container.image,
626
+ name: container.id,
627
+ instanceType: container.instanceType,
628
+ observability: container.observability,
629
+ durableObjects: {
630
+ namespaceId,
631
+ },
632
+ schedulingPolicy: container.schedulingPolicy,
633
+ adopt: container.adopt,
634
+ ...normalizeApiOptions(api),
635
+ });
636
+ }));
637
+ }
638
+ async function provisionEventSources(api, props) {
639
+ if (!props.eventSources?.length) {
640
+ return;
641
+ }
642
+ return await Promise.all(props.eventSources.map((eventSource) => {
643
+ if (isQueue(eventSource)) {
644
+ return QueueConsumer(`${eventSource.id}-consumer`, {
645
+ queue: eventSource,
646
+ scriptName: props.scriptName,
647
+ settings: eventSource.dlq
648
+ ? { deadLetterQueue: eventSource.dlq }
649
+ : undefined,
650
+ ...normalizeApiOptions(api),
716
651
  });
717
652
  }
718
- catch (error) {
719
- logger.warn("Failed to disable worker URL during deletion:", error);
653
+ if (isQueueEventSource(eventSource)) {
654
+ return QueueConsumer(`${eventSource.queue.id}-consumer`, {
655
+ queue: eventSource.queue,
656
+ scriptName: props.scriptName,
657
+ settings: eventSource.settings,
658
+ ...normalizeApiOptions(api),
659
+ });
720
660
  }
661
+ throw new Error(`Unsupported event source: ${eventSource}`);
662
+ }));
663
+ }
664
+ async function provisionDomains(api, props) {
665
+ if (!props.domains?.length) {
666
+ return;
667
+ }
668
+ const domains = props.domains.map((domain) => {
669
+ if (typeof domain === "string") {
670
+ return {
671
+ name: domain,
672
+ zoneId: undefined,
673
+ adopt: props.adopt,
674
+ };
675
+ }
676
+ return {
677
+ name: domain.domainName,
678
+ zoneId: domain.zoneId,
679
+ adopt: domain.adopt ?? props.adopt,
680
+ };
681
+ });
682
+ assertUnique(domains, "name", "Custom Domain");
683
+ return await Promise.all(domains.map(async (domain) => {
684
+ return await CustomDomain(domain.name, {
685
+ workerName: props.scriptName,
686
+ name: domain.name,
687
+ zoneId: domain.zoneId,
688
+ adopt: domain.adopt,
689
+ ...normalizeApiOptions(api),
690
+ });
691
+ }));
692
+ }
693
+ async function provisionRoutes(api, props) {
694
+ if (!props.routes?.length) {
695
+ return;
696
+ }
697
+ const routes = props.routes.map((route) => {
698
+ if (typeof route === "string") {
699
+ return {
700
+ pattern: route,
701
+ adopt: props.adopt,
702
+ };
703
+ }
704
+ return {
705
+ pattern: route.pattern,
706
+ zoneId: route.zoneId,
707
+ adopt: route.adopt ?? props.adopt,
708
+ };
709
+ });
710
+ assertUnique(routes, "pattern", "Route");
711
+ return await Promise.all(routes.map(async (route) => {
712
+ return await Route(route.pattern, {
713
+ pattern: route.pattern,
714
+ script: props.scriptName,
715
+ zoneId: route.zoneId,
716
+ adopt: route.adopt,
717
+ ...normalizeApiOptions(api),
718
+ });
719
+ }));
720
+ }
721
+ async function provisionSubdomain(api, props) {
722
+ if (props.enable) {
723
+ return await WorkerSubdomain("url", {
724
+ scriptName: props.scriptName,
725
+ previewVersionId: props.previewVersionId,
726
+ retain: props.retain,
727
+ ...normalizeApiOptions(api),
728
+ });
729
+ }
730
+ if (props.forceDelete) {
731
+ await disableWorkerSubdomain(api, props.scriptName);
721
732
  }
722
- // Return minimal output for deleted state
723
- return;
724
733
  }
725
- function upsertDevCommand(props) {
734
+ async function createMiniflare(props) {
735
+ const sharedOptions = {
736
+ name: props.workerName,
737
+ compatibilityDate: props.compatibilityDate,
738
+ compatibilityFlags: props.compatibilityFlags,
739
+ bindings: props.bindings,
740
+ port: props.port,
741
+ };
742
+ const startPromise = new DeferredPromise();
743
+ const controller = new AbortController();
744
+ cleanups.push(() => controller.abort());
745
+ (await props.bundle.watch(controller.signal))
746
+ .pipeThrough(new TransformStream({
747
+ async transform(chunk, controller) {
748
+ switch (chunk.type) {
749
+ case "start": {
750
+ controller.enqueue({ type: "build-start" });
751
+ break;
752
+ }
753
+ case "end": {
754
+ controller.enqueue({ type: "build-end" });
755
+ try {
756
+ const server = await miniflareServer.push({
757
+ ...sharedOptions,
758
+ bundle: chunk.result,
759
+ });
760
+ controller.enqueue({
761
+ type: "miniflare-ready",
762
+ url: server.url,
763
+ });
764
+ }
765
+ catch (error) {
766
+ controller.enqueue({
767
+ type: "miniflare-error",
768
+ error: error,
769
+ });
770
+ }
771
+ break;
772
+ }
773
+ case "error": {
774
+ controller.enqueue({
775
+ type: "build-error",
776
+ errors: chunk.errors,
777
+ });
778
+ break;
779
+ }
780
+ }
781
+ },
782
+ }))
783
+ .pipeTo(new WritableStream({
784
+ write: async (chunk) => {
785
+ switch (chunk.type) {
786
+ case "build-start": {
787
+ logger.task("", {
788
+ message: "start",
789
+ status: "pending",
790
+ resource: props.id,
791
+ prefix: "build",
792
+ prefixColor: "cyanBright",
793
+ });
794
+ break;
795
+ }
796
+ case "build-end": {
797
+ logger.task("", {
798
+ message: startPromise.status === "pending" ? "load" : "reload",
799
+ status: "success",
800
+ resource: props.id,
801
+ prefix: "build",
802
+ prefixColor: "greenBright",
803
+ });
804
+ break;
805
+ }
806
+ case "build-error": {
807
+ logger.task("", {
808
+ message: "error",
809
+ status: "failure",
810
+ resource: props.id,
811
+ prefix: "build",
812
+ prefixColor: "redBright",
813
+ });
814
+ if (startPromise.status === "pending") {
815
+ controller.abort();
816
+ startPromise.reject(chunk.errors);
817
+ }
818
+ else {
819
+ logger.error(chunk.errors);
820
+ }
821
+ break;
822
+ }
823
+ case "miniflare-ready": {
824
+ logger.task("", {
825
+ message: `ready at ${chunk.url}`,
826
+ status: "success",
827
+ resource: props.id,
828
+ prefix: "miniflare",
829
+ prefixColor: "greenBright",
830
+ });
831
+ if (startPromise.status === "pending") {
832
+ startPromise.resolve(chunk.url);
833
+ }
834
+ break;
835
+ }
836
+ case "miniflare-error": {
837
+ logger.task("", {
838
+ message: "error",
839
+ status: "failure",
840
+ resource: props.id,
841
+ prefix: "miniflare",
842
+ prefixColor: "redBright",
843
+ });
844
+ if (startPromise.status === "pending") {
845
+ startPromise.reject(chunk.error);
846
+ }
847
+ else {
848
+ logger.error(chunk.error);
849
+ }
850
+ break;
851
+ }
852
+ }
853
+ },
854
+ }));
855
+ return await startPromise.value;
856
+ }
857
+ function createDevCommand(props) {
726
858
  const persistFile = path.join(process.cwd(), ".alchemy", `${props.id}.pid`);
727
859
  if (existsSync(persistFile)) {
728
860
  const pid = Number.parseInt(readFileSync(persistFile, "utf8"));
@@ -749,104 +881,92 @@ function upsertDevCommand(props) {
749
881
  },
750
882
  stdio: ["inherit", "inherit", "inherit"],
751
883
  });
752
- process.on("SIGINT", () => {
884
+ cleanups.push(() => {
753
885
  try {
754
886
  unlinkSync(persistFile);
755
887
  }
756
888
  catch {
757
889
  // ignore
758
890
  }
759
- proc.kill("SIGINT");
760
- process.exit(0);
891
+ proc.kill();
761
892
  });
762
893
  if (proc.pid) {
763
894
  mkdirSync(path.dirname(persistFile), { recursive: true });
764
895
  writeFileSync(persistFile, proc.pid.toString());
765
896
  }
766
897
  }
898
+ async function prepareWorkerUpload(api, props) {
899
+ const scriptMetadata = await prepareWorkerMetadata(api, props);
900
+ if (props.format === "cjs") {
901
+ scriptMetadata.body_part = props.scriptBundle.entrypoint;
902
+ }
903
+ else {
904
+ scriptMetadata.main_module = props.scriptBundle.entrypoint;
905
+ }
906
+ const body = new FormData();
907
+ for await (const file of props.scriptBundle.files) {
908
+ body.append(file.name, file);
909
+ }
910
+ // Prepare metadata - add version annotations if this is a version
911
+ const finalMetadata = props.version
912
+ ? {
913
+ ...scriptMetadata,
914
+ // Exclude migrations for worker versions - they're not allowed
915
+ migrations: undefined,
916
+ annotations: {
917
+ "workers/tag": props.version,
918
+ "workers/message": `Version ${props.version}`,
919
+ },
920
+ }
921
+ : {
922
+ ...scriptMetadata,
923
+ migrations: scriptMetadata.migrations
924
+ ? {
925
+ ...scriptMetadata.migrations,
926
+ old_tag: props.migrationTag,
927
+ new_tag: bumpMigrationTagVersion(props.migrationTag),
928
+ }
929
+ : undefined,
930
+ };
931
+ body.append("metadata", new Blob([JSON.stringify(finalMetadata)], {
932
+ type: "application/json",
933
+ }));
934
+ let endpoint;
935
+ let method;
936
+ if (props.version) {
937
+ if (props.dispatchNamespace) {
938
+ throw new Error("Worker Preview Versions are not supported in Workers for Platforms");
939
+ }
940
+ // Upload worker version using the versions API
941
+ endpoint = `/accounts/${api.accountId}/workers/scripts/${props.workerName}/versions`;
942
+ method = "POST";
943
+ }
944
+ else {
945
+ // Upload worker script with bindings
946
+ endpoint = props.dispatchNamespace
947
+ ? `/accounts/${api.accountId}/workers/dispatch/namespaces/${props.dispatchNamespace}/scripts/${props.workerName}`
948
+ : `/accounts/${api.accountId}/workers/scripts/${props.workerName}`;
949
+ method = "PUT";
950
+ }
951
+ return {
952
+ body,
953
+ endpoint,
954
+ method,
955
+ };
956
+ }
767
957
  export async function putWorker(api, props) {
768
958
  const {
769
959
  //
770
- dispatchNamespace, migrationTag, workerName, scriptBundle, version, } = props;
771
- const scriptMetadata = await prepareWorkerMetadata(api, props);
960
+ workerName, version, } = props;
772
961
  return withExponentialBackoff(async () => {
773
- const scriptName = scriptMetadata.main_module ?? scriptMetadata.body_part;
774
- // Create FormData for the upload
775
- const formData = new FormData();
776
- function addFile(fileName, content) {
777
- const contentType = getContentType(fileName) ?? "application/null";
778
- formData.append(fileName, new Blob([content], {
779
- type: contentType === "application/javascript" &&
780
- scriptMetadata.main_module
781
- ? "application/javascript+module"
782
- : contentType,
783
- }), fileName);
784
- }
785
- if (typeof scriptBundle === "string") {
786
- addFile(scriptName, scriptBundle);
787
- }
788
- else {
789
- for (const [fileName, content] of Object.entries(scriptBundle)) {
790
- addFile(fileName, content);
791
- }
792
- }
793
- // Prepare metadata - add version annotations if this is a version
794
- const finalMetadata = version
795
- ? {
796
- ...scriptMetadata,
797
- // Exclude migrations for worker versions - they're not allowed
798
- migrations: undefined,
799
- annotations: {
800
- "workers/tag": version,
801
- "workers/message": `Version ${version}`,
802
- },
803
- }
804
- : {
805
- ...scriptMetadata,
806
- migrations: scriptMetadata.migrations
807
- ? {
808
- ...scriptMetadata.migrations,
809
- old_tag: migrationTag,
810
- new_tag: bumpMigrationTagVersion(migrationTag),
811
- }
812
- : undefined,
813
- };
814
- if (process.env.DEBUG) {
815
- console.log(`metadata(${scriptName}):`, finalMetadata);
816
- }
817
- // Add metadata as JSON
818
- formData.append("metadata", new Blob([JSON.stringify(finalMetadata)], {
819
- type: "application/json",
820
- }));
821
- // Determine endpoint and HTTP method
822
- let endpoint;
823
- let method;
824
- if (version) {
825
- if (dispatchNamespace) {
826
- throw new Error("Worker Preview Versions are not supported in Workers for Platforms");
827
- }
828
- // Upload worker version using the versions API
829
- endpoint = `/accounts/${api.accountId}/workers/scripts/${workerName}/versions`;
830
- method = "POST";
831
- }
832
- else {
833
- // Upload worker script with bindings
834
- endpoint = dispatchNamespace
835
- ? `/accounts/${api.accountId}/workers/dispatch/namespaces/${dispatchNamespace}/scripts/${workerName}`
836
- : `/accounts/${api.accountId}/workers/scripts/${workerName}`;
837
- method = "PUT";
838
- }
839
- const uploadResponse = method === "PUT"
840
- ? await api.put(endpoint, formData, {
841
- headers: {
842
- "Content-Type": "multipart/form-data",
843
- },
844
- })
845
- : await api.post(endpoint, formData, {
846
- headers: {
847
- "Content-Type": "multipart/form-data",
848
- },
849
- });
962
+ const { body, endpoint, method } = await prepareWorkerUpload(api, props);
963
+ const uploadResponse = await api.fetch(endpoint, {
964
+ method,
965
+ headers: {
966
+ "Content-Type": "multipart/form-data",
967
+ },
968
+ body,
969
+ });
850
970
  // Check if the upload was successful
851
971
  if (!uploadResponse.ok) {
852
972
  try {
@@ -880,33 +1000,7 @@ export async function putWorker(api, props) {
880
1000
  }
881
1001
  }
882
1002
  const responseData = (await uploadResponse.json());
883
- const result = responseData.result;
884
- // Handle version response
885
- if (props.version) {
886
- // Get the account's workers.dev subdomain to construct preview URL
887
- let previewUrl;
888
- if (result.metadata?.has_preview) {
889
- // Need to get the subdomain
890
- const subdomainResponse = await api.get(`/accounts/${api.accountId}/workers/subdomain`);
891
- if (subdomainResponse.ok) {
892
- const subdomainData = await subdomainResponse.json();
893
- const subdomain = subdomainData.result?.subdomain;
894
- if (subdomain) {
895
- // Preview URL format: <FIRST_8_CHARS_OF_VERSION_ID>-<WORKER_NAME>.<SUBDOMAIN>.workers.dev
896
- const versionPrefix = result.id.substring(0, 8);
897
- previewUrl = `https://${versionPrefix}-${workerName}.${subdomain}.workers.dev`;
898
- }
899
- }
900
- }
901
- return {
902
- versionId: result.id,
903
- previewUrl,
904
- deploymentId: result.deployment_id,
905
- };
906
- }
907
- return {
908
- deploymentId: result.deployment_id,
909
- };
1003
+ return responseData.result;
910
1004
  }, (err) => err.status === 404 ||
911
1005
  err.status === 500 ||
912
1006
  err.status === 503 ||
@@ -915,79 +1009,57 @@ export async function putWorker(api, props) {
915
1009
  err.status === 400 &&
916
1010
  err.message.match(/binding.*failed to generate/)), 10, 100);
917
1011
  }
918
- export async function checkWorkerExists(api, workerName) {
919
- const response = await api.get(`/accounts/${api.accountId}/workers/scripts/${workerName}`);
920
- return response.status === 200;
1012
+ async function workerExists(api, scriptName) {
1013
+ const res = await api.get(`/accounts/${api.accountId}/workers/scripts/${scriptName}`);
1014
+ return res.status === 200;
921
1015
  }
922
- export async function assertWorkerDoesNotExist(api, workerName) {
923
- const response = await api.get(`/accounts/${api.accountId}/workers/scripts/${workerName}`);
1016
+ async function assertWorkerDoesNotExist(api, scriptName) {
1017
+ const response = await api.get(`/accounts/${api.accountId}/workers/scripts/${scriptName}`);
924
1018
  if (response.status === 404) {
925
1019
  return true;
926
1020
  }
927
1021
  if (response.status === 200) {
928
- const metadata = await getWorkerScriptMetadata(api, workerName);
1022
+ const metadata = await getScriptMetadata(api, scriptName);
929
1023
  if (!metadata) {
930
1024
  throw new Error(`Worker exists but failed to fetch metadata: ${response.status} ${response.statusText}`);
931
1025
  }
932
- throw new Error(`Worker with name '${workerName}' already exists. Please use a unique name.`);
1026
+ throw new Error(`Worker with name '${scriptName}' already exists. Please use a unique name.`);
933
1027
  }
934
1028
  throw new Error(`Error checking if worker exists: ${response.status} ${response.statusText} ${await response.text()}`);
935
1029
  }
936
- export async function configureURL(ctx, api, props) {
937
- const { workerName, url } = props;
938
- let workerUrl;
939
- if (url) {
940
- // Enable the workers.dev subdomain for this worker
941
- await api.post(`/accounts/${api.accountId}/workers/scripts/${workerName}/subdomain`, { enabled: true, previews_enabled: true }, {
942
- headers: { "Content-Type": "application/json" },
943
- });
944
- // Get the account's workers.dev subdomain
945
- const subdomain = await getAccountSubdomain(api);
946
- if (subdomain) {
947
- workerUrl = `https://${workerName}.${subdomain}.workers.dev`;
948
- }
949
- }
950
- else if (url === false &&
951
- (ctx.output?.url || (ctx.phase === "create" && props.adopt))) {
952
- // Explicitly disable URL if it was previously enabled
953
- const response = await api.post(`/accounts/${api.accountId}/workers/scripts/${workerName}/subdomain`, JSON.stringify({ enabled: false }), {
954
- headers: { "Content-Type": "application/json" },
955
- });
956
- if (!response.ok) {
957
- throw new Error(`Failed to disable worker URL: ${response.status} ${response.statusText}`);
958
- }
959
- }
960
- return workerUrl;
961
- }
962
- export async function getWorkerSubdomainConfig(api, workerName) {
963
- // Query Cloudflare API to verify subdomain is disabled
964
- const subdomainResponse = await api.get(`/accounts/${api.accountId}/workers/scripts/${workerName}/subdomain`);
965
- if (subdomainResponse.status === 404) {
966
- return undefined;
967
- }
968
- const subdomainData = await subdomainResponse.json();
969
- return subdomainData.result;
970
- }
971
- export async function getWorkerScriptMetadata(api, workerName) {
972
- const response = await api.get(`/accounts/${api.accountId}/workers/services/${workerName}`);
973
- if (response.status === 404) {
974
- return undefined;
1030
+ async function getScriptMetadata(api, scriptName) {
1031
+ const res = await api.get(`/accounts/${api.accountId}/workers/services/${scriptName}`);
1032
+ if (res.status === 404) {
1033
+ return;
975
1034
  }
976
- if (!response.ok) {
977
- throw new Error(`Error getting worker script metadata: ${response.status} ${response.statusText}`);
1035
+ if (!res.ok) {
1036
+ throw new Error(`Error getting worker script metadata: ${res.status} ${res.statusText}`);
978
1037
  }
979
- return (await response.json()).result;
1038
+ const json = (await res.json());
1039
+ return json.result;
980
1040
  }
981
- /**
982
- * Lists and deletes all queue consumers for a specific worker
983
- * @param ctx Worker context containing eventSources
984
- * @param api CloudflareApi instance
985
- * @param workerName Name of the worker script
986
- */
987
- async function deleteQueueConsumers(api, workerName) {
988
- const consumers = await listQueueConsumersForWorker(api, workerName);
1041
+ async function deleteQueueConsumers(api, scriptName) {
1042
+ const consumers = await listQueueConsumersForWorker(api, scriptName);
989
1043
  await Promise.all(consumers.map(async (consumer) => {
990
1044
  await deleteQueueConsumer(api, consumer.queueId, consumer.consumerId);
991
1045
  }));
992
1046
  }
1047
+ export async function deleteWorker(api, props) {
1048
+ await withExponentialBackoff(async () => {
1049
+ const deleteResponse = await api.delete(props.dispatchNamespace
1050
+ ? `/accounts/${api.accountId}/workers/dispatch/namespaces/${props.dispatchNamespace}/scripts/${props.scriptName}?force=true`
1051
+ : `/accounts/${api.accountId}/workers/scripts/${props.scriptName}?force=true`);
1052
+ if (!deleteResponse.ok && deleteResponse.status !== 404) {
1053
+ await handleApiError(deleteResponse, "delete", "worker", props.scriptName);
1054
+ }
1055
+ }, (err) => (err.status === 400 &&
1056
+ err.message.includes("is still referenced by service bindings in Workers")) ||
1057
+ err.status === 500 ||
1058
+ err.status === 503, 10, 100);
1059
+ }
1060
+ async function getVersionMetadata(api, scriptName, deploymentId) {
1061
+ const response = await api.get(`/accounts/${api.accountId}/workers/scripts/${scriptName}/versions/${deploymentId}`);
1062
+ const result = (await response.json());
1063
+ return result.result;
1064
+ }
993
1065
  //# sourceMappingURL=worker.js.map