alchemy 0.63.1 → 0.65.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 (235) hide show
  1. package/bin/alchemy.js +7664 -5857
  2. package/bin/commands/init.ts +130 -9
  3. package/bin/constants.ts +4 -0
  4. package/bin/services/cdp-manager/cdp-proxy.ts +69 -0
  5. package/bin/services/cdp-manager/server.ts +173 -0
  6. package/bin/services/execute-alchemy.ts +83 -4
  7. package/bin/types.ts +1 -0
  8. package/lib/alchemy.d.ts +4 -0
  9. package/lib/alchemy.d.ts.map +1 -1
  10. package/lib/alchemy.js +10 -4
  11. package/lib/alchemy.js.map +1 -1
  12. package/lib/apply.d.ts +3 -0
  13. package/lib/apply.d.ts.map +1 -1
  14. package/lib/apply.js +11 -1
  15. package/lib/apply.js.map +1 -1
  16. package/lib/cloudflare/bindings.d.ts +3 -1
  17. package/lib/cloudflare/bindings.d.ts.map +1 -1
  18. package/lib/cloudflare/bucket.d.ts +2 -1
  19. package/lib/cloudflare/bucket.d.ts.map +1 -1
  20. package/lib/cloudflare/bucket.js.map +1 -1
  21. package/lib/cloudflare/bundle/cloudflare-internal.d.ts +6 -0
  22. package/lib/cloudflare/bundle/cloudflare-internal.d.ts.map +1 -0
  23. package/lib/cloudflare/bundle/cloudflare-internal.js +39 -0
  24. package/lib/cloudflare/bundle/cloudflare-internal.js.map +1 -0
  25. package/lib/cloudflare/bundle/nodejs-builtin-modules.d.ts +2 -0
  26. package/lib/cloudflare/bundle/nodejs-builtin-modules.d.ts.map +1 -0
  27. package/lib/cloudflare/bundle/nodejs-builtin-modules.js +9 -0
  28. package/lib/cloudflare/bundle/nodejs-builtin-modules.js.map +1 -0
  29. package/lib/cloudflare/bundle/plugin-als-external.d.ts +6 -0
  30. package/lib/cloudflare/bundle/plugin-als-external.d.ts.map +1 -0
  31. package/lib/cloudflare/bundle/plugin-als-external.js +12 -0
  32. package/lib/cloudflare/bundle/plugin-als-external.js.map +1 -0
  33. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.d.ts +4 -1
  34. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.d.ts.map +1 -1
  35. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js +23 -23
  36. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js.map +1 -1
  37. package/lib/cloudflare/bundle/plugin-node-compat.d.ts +11 -0
  38. package/lib/cloudflare/bundle/plugin-node-compat.d.ts.map +1 -0
  39. package/lib/cloudflare/bundle/plugin-node-compat.js +93 -0
  40. package/lib/cloudflare/bundle/plugin-node-compat.js.map +1 -0
  41. package/lib/cloudflare/bundle/plugin-wasm.d.ts.map +1 -1
  42. package/lib/cloudflare/bundle/plugin-wasm.js +20 -11
  43. package/lib/cloudflare/bundle/plugin-wasm.js.map +1 -1
  44. package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
  45. package/lib/cloudflare/compatibility-date.gen.js +1 -1
  46. package/lib/cloudflare/d1-local-migrations.js.map +1 -1
  47. package/lib/cloudflare/durable-object-namespace.d.ts +1 -0
  48. package/lib/cloudflare/durable-object-namespace.d.ts.map +1 -1
  49. package/lib/cloudflare/durable-object-namespace.js.map +1 -1
  50. package/lib/cloudflare/hyperdrive.d.ts +7 -2
  51. package/lib/cloudflare/hyperdrive.d.ts.map +1 -1
  52. package/lib/cloudflare/hyperdrive.js +39 -57
  53. package/lib/cloudflare/hyperdrive.js.map +1 -1
  54. package/lib/cloudflare/index.d.ts +1 -0
  55. package/lib/cloudflare/index.d.ts.map +1 -1
  56. package/lib/cloudflare/index.js +1 -0
  57. package/lib/cloudflare/index.js.map +1 -1
  58. package/lib/cloudflare/next.d.ts +13 -0
  59. package/lib/cloudflare/next.d.ts.map +1 -0
  60. package/lib/cloudflare/next.js +91 -0
  61. package/lib/cloudflare/next.js.map +1 -0
  62. package/lib/cloudflare/website.d.ts.map +1 -1
  63. package/lib/cloudflare/website.js +80 -82
  64. package/lib/cloudflare/website.js.map +1 -1
  65. package/lib/cloudflare/worker-assets.d.ts +3 -3
  66. package/lib/cloudflare/worker-assets.d.ts.map +1 -1
  67. package/lib/cloudflare/worker-assets.js +22 -47
  68. package/lib/cloudflare/worker-assets.js.map +1 -1
  69. package/lib/cloudflare/worker-bundle.d.ts +6 -0
  70. package/lib/cloudflare/worker-bundle.d.ts.map +1 -1
  71. package/lib/cloudflare/worker-bundle.js +34 -10
  72. package/lib/cloudflare/worker-bundle.js.map +1 -1
  73. package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
  74. package/lib/cloudflare/worker-metadata.js +1 -0
  75. package/lib/cloudflare/worker-metadata.js.map +1 -1
  76. package/lib/cloudflare/worker.d.ts.map +1 -1
  77. package/lib/cloudflare/worker.js +24 -6
  78. package/lib/cloudflare/worker.js.map +1 -1
  79. package/lib/cloudflare/wrangler.json.d.ts +4 -6
  80. package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
  81. package/lib/cloudflare/wrangler.json.js +20 -15
  82. package/lib/cloudflare/wrangler.json.js.map +1 -1
  83. package/lib/context.d.ts +3 -1
  84. package/lib/context.d.ts.map +1 -1
  85. package/lib/context.js +2 -2
  86. package/lib/context.js.map +1 -1
  87. package/lib/destroy.d.ts +8 -0
  88. package/lib/destroy.d.ts.map +1 -1
  89. package/lib/destroy.js +20 -4
  90. package/lib/destroy.js.map +1 -1
  91. package/lib/planetscale/api/client.gen.d.ts +1750 -0
  92. package/lib/planetscale/api/client.gen.d.ts.map +1 -0
  93. package/lib/planetscale/api/client.gen.js +2401 -0
  94. package/lib/planetscale/api/client.gen.js.map +1 -0
  95. package/lib/planetscale/api/index.d.ts +3 -0
  96. package/lib/planetscale/api/index.d.ts.map +1 -0
  97. package/lib/planetscale/api/index.js +4 -0
  98. package/lib/planetscale/api/index.js.map +1 -0
  99. package/lib/planetscale/api/types.gen.d.ts +12156 -0
  100. package/lib/planetscale/api/types.gen.d.ts.map +1 -0
  101. package/lib/planetscale/api/types.gen.js +2 -0
  102. package/lib/planetscale/api/types.gen.js.map +1 -0
  103. package/lib/planetscale/branch.d.ts +5 -8
  104. package/lib/planetscale/branch.d.ts.map +1 -1
  105. package/lib/planetscale/branch.js +122 -97
  106. package/lib/planetscale/branch.js.map +1 -1
  107. package/lib/planetscale/database.d.ts +24 -11
  108. package/lib/planetscale/database.d.ts.map +1 -1
  109. package/lib/planetscale/database.js +163 -118
  110. package/lib/planetscale/database.js.map +1 -1
  111. package/lib/planetscale/index.d.ts +2 -1
  112. package/lib/planetscale/index.d.ts.map +1 -1
  113. package/lib/planetscale/index.js +2 -1
  114. package/lib/planetscale/index.js.map +1 -1
  115. package/lib/planetscale/password.d.ts +7 -8
  116. package/lib/planetscale/password.d.ts.map +1 -1
  117. package/lib/planetscale/password.js +74 -57
  118. package/lib/planetscale/password.js.map +1 -1
  119. package/lib/planetscale/role.d.ts +126 -0
  120. package/lib/planetscale/role.d.ts.map +1 -0
  121. package/lib/planetscale/role.js +144 -0
  122. package/lib/planetscale/role.js.map +1 -0
  123. package/lib/planetscale/utils.d.ts +23 -7
  124. package/lib/planetscale/utils.d.ts.map +1 -1
  125. package/lib/planetscale/utils.js +169 -75
  126. package/lib/planetscale/utils.js.map +1 -1
  127. package/lib/scope.d.ts +5 -1
  128. package/lib/scope.d.ts.map +1 -1
  129. package/lib/scope.js +11 -3
  130. package/lib/scope.js.map +1 -1
  131. package/lib/secret.d.ts +0 -3
  132. package/lib/secret.d.ts.map +1 -1
  133. package/lib/secret.js +0 -2
  134. package/lib/secret.js.map +1 -1
  135. package/lib/state/cloudflare-state-store.js +3 -3
  136. package/lib/state/cloudflare-state-store.js.map +1 -1
  137. package/lib/test/bun.d.ts +2 -2
  138. package/lib/test/bun.d.ts.map +1 -1
  139. package/lib/test/vitest.d.ts +2 -2
  140. package/lib/test/vitest.d.ts.map +1 -1
  141. package/lib/test/vitest.js +4 -4
  142. package/lib/test/vitest.js.map +1 -1
  143. package/lib/util/content-type.js +2 -2
  144. package/lib/util/content-type.js.map +1 -1
  145. package/lib/util/dedent.d.ts +3 -14
  146. package/lib/util/dedent.d.ts.map +1 -1
  147. package/lib/util/dedent.js +33 -45
  148. package/lib/util/dedent.js.map +1 -1
  149. package/lib/util/memoize.d.ts.map +1 -1
  150. package/lib/util/memoize.js +11 -1
  151. package/lib/util/memoize.js.map +1 -1
  152. package/package.json +8 -3
  153. package/src/alchemy.ts +14 -6
  154. package/src/apply.ts +13 -1
  155. package/src/cloudflare/bindings.ts +3 -1
  156. package/src/cloudflare/bucket.ts +3 -1
  157. package/src/cloudflare/bundle/cloudflare-internal.ts +42 -0
  158. package/src/cloudflare/bundle/nodejs-builtin-modules.ts +14 -0
  159. package/src/cloudflare/bundle/plugin-als-external.ts +13 -0
  160. package/src/cloudflare/bundle/plugin-hybrid-node-compat.ts +29 -28
  161. package/src/cloudflare/bundle/plugin-node-compat.ts +114 -0
  162. package/src/cloudflare/bundle/plugin-wasm.ts +34 -14
  163. package/src/cloudflare/compatibility-date.gen.ts +1 -1
  164. package/src/cloudflare/d1-local-migrations.ts +2 -2
  165. package/src/cloudflare/durable-object-namespace.ts +1 -0
  166. package/src/cloudflare/hyperdrive.ts +136 -95
  167. package/src/cloudflare/index.ts +1 -0
  168. package/src/cloudflare/next.ts +123 -0
  169. package/src/cloudflare/website.ts +88 -89
  170. package/src/cloudflare/worker-assets.ts +43 -105
  171. package/src/cloudflare/worker-bundle.ts +46 -9
  172. package/src/cloudflare/worker-metadata.ts +2 -0
  173. package/src/cloudflare/worker.ts +35 -7
  174. package/src/cloudflare/wrangler.json.ts +114 -113
  175. package/src/context.ts +5 -3
  176. package/src/destroy.ts +26 -8
  177. package/src/planetscale/api/client.gen.ts +2882 -0
  178. package/src/planetscale/api/index.ts +4 -0
  179. package/src/planetscale/api/types.gen.ts +13041 -0
  180. package/src/planetscale/branch.ts +170 -162
  181. package/src/planetscale/database.ts +263 -243
  182. package/src/planetscale/index.ts +2 -1
  183. package/src/planetscale/password.ts +89 -86
  184. package/src/planetscale/role.ts +278 -0
  185. package/src/planetscale/utils.ts +289 -128
  186. package/src/scope.ts +12 -3
  187. package/src/secret.ts +0 -4
  188. package/src/state/cloudflare-state-store.ts +4 -4
  189. package/src/test/bun.ts +2 -2
  190. package/src/test/vitest.ts +6 -6
  191. package/src/util/content-type.ts +2 -2
  192. package/src/util/dedent.ts +34 -50
  193. package/src/util/memoize.ts +13 -1
  194. package/templates/astro/alchemy.run.ts +1 -3
  195. package/templates/hono/alchemy.run.ts +0 -1
  196. package/templates/nextjs/README.md +85 -0
  197. package/templates/nextjs/_env +1 -0
  198. package/templates/nextjs/_env.example +1 -0
  199. package/templates/nextjs/_gitignore +46 -0
  200. package/templates/nextjs/alchemy.run.ts +16 -0
  201. package/templates/nextjs/eslint.config.mjs +16 -0
  202. package/templates/nextjs/next-env.d.ts +5 -0
  203. package/templates/nextjs/next.config.ts +10 -0
  204. package/templates/nextjs/open-next.config.ts +5 -0
  205. package/templates/nextjs/package.json +31 -0
  206. package/templates/nextjs/src/app/api/hello/route.ts +19 -0
  207. package/templates/nextjs/src/app/favicon.ico +0 -0
  208. package/templates/nextjs/src/app/globals.css +72 -0
  209. package/templates/nextjs/src/app/layout.tsx +19 -0
  210. package/templates/nextjs/src/app/page.tsx +55 -0
  211. package/templates/nextjs/tsconfig.json +33 -0
  212. package/templates/nextjs/types/env.d.ts +14 -0
  213. package/templates/nuxt/alchemy.run.ts +1 -3
  214. package/templates/react-router/alchemy.run.ts +1 -3
  215. package/templates/rwsdk/alchemy.run.ts +0 -2
  216. package/templates/sveltekit/alchemy.run.ts +1 -3
  217. package/templates/tanstack-start/alchemy.run.ts +1 -3
  218. package/templates/typescript/alchemy.run.ts +0 -1
  219. package/templates/vite/alchemy.run.ts +0 -1
  220. package/workers/cloudflare-state-store.js +58 -58
  221. package/lib/cloudflare/bundle/externals.d.ts +0 -3
  222. package/lib/cloudflare/bundle/externals.d.ts.map +0 -1
  223. package/lib/cloudflare/bundle/externals.js +0 -35
  224. package/lib/cloudflare/bundle/externals.js.map +0 -1
  225. package/lib/cloudflare/bundle/plugin-compat-warning.d.ts +0 -10
  226. package/lib/cloudflare/bundle/plugin-compat-warning.d.ts.map +0 -1
  227. package/lib/cloudflare/bundle/plugin-compat-warning.js +0 -67
  228. package/lib/cloudflare/bundle/plugin-compat-warning.js.map +0 -1
  229. package/lib/planetscale/api.d.ts +0 -53
  230. package/lib/planetscale/api.d.ts.map +0 -1
  231. package/lib/planetscale/api.js +0 -84
  232. package/lib/planetscale/api.js.map +0 -1
  233. package/src/cloudflare/bundle/externals.ts +0 -36
  234. package/src/cloudflare/bundle/plugin-compat-warning.ts +0 -94
  235. package/src/planetscale/api.ts +0 -115
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alchemy",
3
- "version": "0.63.1",
3
+ "version": "0.65.0",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Sam Goodwin <sam@alchemy.run>",
6
6
  "homepage": "https://alchemy.run",
@@ -147,7 +147,7 @@
147
147
  },
148
148
  "dependencies": {
149
149
  "@aws-sdk/credential-providers": "^3.0.0",
150
- "@cloudflare/unenv-preset": "^2.6.1",
150
+ "@cloudflare/unenv-preset": "^2.6.3",
151
151
  "@iarna/toml": "^2.2.5",
152
152
  "@smithy/node-config-provider": "^4.0.0",
153
153
  "aws4fetch": "^1.0.20",
@@ -182,9 +182,10 @@
182
182
  "@aws-sdk/client-ssm": "^3.0.0",
183
183
  "@aws-sdk/client-sts": "^3.0.0",
184
184
  "@cloudflare/vite-plugin": "^1.10.1",
185
- "@cloudflare/workers-types": "*",
185
+ "@cloudflare/workers-types": "^4.20250805.0",
186
186
  "@libsql/client": "^0.15.12",
187
187
  "@octokit/rest": "^21.1.1",
188
+ "@opennextjs/cloudflare": "^1.6.5",
188
189
  "astro": "^5.13.2",
189
190
  "drizzle-orm": "^0.44.2",
190
191
  "rwsdk": "^0.1.36",
@@ -224,6 +225,9 @@
224
225
  "@cloudflare/workers-types": {
225
226
  "optional": true
226
227
  },
228
+ "@opennextjs/cloudflare": {
229
+ "optional": true
230
+ },
227
231
  "@octokit/rest": {
228
232
  "optional": true
229
233
  },
@@ -269,6 +273,7 @@
269
273
  "@types/libsodium-wrappers": "^0.7.14",
270
274
  "@types/node": "latest",
271
275
  "@types/ws": "^8.18.1",
276
+ "@workos-inc/node": "^7.69.1",
272
277
  "arktype": "^2.1.16",
273
278
  "astro": "^5.13.2",
274
279
  "braintrust": "^0.0.201",
package/src/alchemy.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { execArgv } from "node:process";
2
2
  import { onExit } from "signal-exit";
3
- import { ReplacedSignal } from "./apply.ts";
4
- import { DestroyStrategy, DestroyedSignal, destroy } from "./destroy.ts";
3
+ import { isReplacedSignal } from "./apply.ts";
4
+ import { DestroyStrategy, destroy, isDestroyedSignal } from "./destroy.ts";
5
5
  import { env } from "./env.ts";
6
6
  import {
7
7
  ResourceFQN,
@@ -239,6 +239,10 @@ export interface AlchemyOptions {
239
239
  * @default "sequential"
240
240
  */
241
241
  destroyStrategy?: DestroyStrategy;
242
+ /**
243
+ * If true, children of the resource will not be destroyed (but their state will be deleted).
244
+ */
245
+ noop?: boolean;
242
246
  /**
243
247
  * If true, will not print any Create/Update/Delete messages.
244
248
  *
@@ -327,6 +331,7 @@ async function run<T>(
327
331
  scopeName: id,
328
332
  telemetryClient,
329
333
  });
334
+ let noop = options?.noop ?? false;
330
335
  try {
331
336
  if (options?.isResource !== true && _scope.parent) {
332
337
  // TODO(sam): this is an awful hack to differentiate between naked scopes and resources
@@ -364,13 +369,16 @@ async function run<T>(
364
369
  }
365
370
  return await _scope.run(async () => fn.bind(_scope)(_scope));
366
371
  } catch (error) {
367
- if (
368
- !(error instanceof DestroyedSignal || error instanceof ReplacedSignal)
369
- ) {
372
+ if (!(isDestroyedSignal(error) || isReplacedSignal(error))) {
370
373
  _scope.fail();
371
374
  }
375
+ if (isDestroyedSignal(error)) {
376
+ noop = noop || error.noop;
377
+ }
372
378
  throw error;
373
379
  } finally {
374
- await _scope.finalize();
380
+ await _scope.finalize({
381
+ noop,
382
+ });
375
383
  }
376
384
  }
package/src/apply.ts CHANGED
@@ -22,6 +22,7 @@ import type { Telemetry } from "./util/telemetry/index.ts";
22
22
  export interface ApplyOptions {
23
23
  quiet?: boolean;
24
24
  alwaysUpdate?: boolean;
25
+ noop?: boolean;
25
26
  }
26
27
 
27
28
  export function apply<Out extends Resource>(
@@ -32,7 +33,12 @@ export function apply<Out extends Resource>(
32
33
  return _apply(resource, props, options);
33
34
  }
34
35
 
36
+ export function isReplacedSignal(error: any): error is ReplacedSignal {
37
+ return error instanceof Error && (error as any).kind === "ReplacedSignal";
38
+ }
39
+
35
40
  export class ReplacedSignal extends Error {
41
+ readonly kind = "ReplacedSignal";
36
42
  public force: boolean;
37
43
 
38
44
  constructor(force?: boolean) {
@@ -185,6 +191,7 @@ async function _apply<Out extends Resource>(
185
191
  isResource: true,
186
192
  parent: scope,
187
193
  destroyStrategy: provider.options?.destroyStrategy ?? "sequential",
194
+ noop: options?.noop,
188
195
  },
189
196
  async () =>
190
197
  await provider.handler.bind(ctx)(resource[ResourceID], props),
@@ -199,6 +206,7 @@ async function _apply<Out extends Resource>(
199
206
  props: state.oldProps,
200
207
  output: oldOutput,
201
208
  },
209
+ noop: options?.noop,
202
210
  });
203
211
  } else {
204
212
  if (
@@ -219,7 +227,11 @@ async function _apply<Out extends Resource>(
219
227
 
220
228
  output = await alchemy.run(
221
229
  resource[ResourceID],
222
- { isResource: true, parent: scope },
230
+ {
231
+ isResource: true,
232
+ parent: scope,
233
+ noop: options?.noop,
234
+ },
223
235
  async () =>
224
236
  provider.handler.bind(
225
237
  context({
@@ -10,7 +10,7 @@ import type { AnalyticsEngineDataset } from "./analytics-engine.ts";
10
10
  import type { Assets } from "./assets.ts";
11
11
  import type { Bound } from "./bound.ts";
12
12
  import type { BrowserRendering } from "./browser-rendering.ts";
13
- import type { R2Bucket } from "./bucket.ts";
13
+ import type { R2Bucket, R2BucketJurisdiction } from "./bucket.ts";
14
14
  import type { Container } from "./container.ts";
15
15
  import type { D1Database } from "./d1-database.ts";
16
16
  import type { DispatchNamespace } from "./dispatch-namespace.ts";
@@ -317,6 +317,8 @@ export interface WorkerBindingR2Bucket {
317
317
  type: "r2_bucket";
318
318
  /** Bucket name */
319
319
  bucket_name: string;
320
+ /** Jurisdiction */
321
+ jurisdiction?: R2BucketJurisdiction;
320
322
  }
321
323
 
322
324
  /**
@@ -15,6 +15,8 @@ import {
15
15
  } from "./api.ts";
16
16
  import { deleteMiniflareBinding } from "./miniflare/delete.ts";
17
17
 
18
+ export type R2BucketJurisdiction = "default" | "eu" | "fedramp";
19
+
18
20
  /**
19
21
  * Properties for creating or updating an R2 Bucket
20
22
  */
@@ -44,7 +46,7 @@ export interface BucketProps extends CloudflareApiOptions {
44
46
  * Optional jurisdiction for the bucket
45
47
  * Determines the regulatory jurisdiction the bucket data falls under
46
48
  */
47
- jurisdiction?: "default" | "eu" | "fedramp";
49
+ jurisdiction?: R2BucketJurisdiction;
48
50
 
49
51
  /**
50
52
  * Whether to allow public access through the r2.dev subdomain
@@ -0,0 +1,42 @@
1
+ import type { Plugin } from "esbuild";
2
+ import { dedent } from "../../util/dedent.ts";
3
+
4
+ /**
5
+ * An esbuild plugin that will mark any `cloudflare:...` imports as external.
6
+ */
7
+ export const cloudflareInternalPlugin: Plugin = {
8
+ name: "cloudflare-internal-imports",
9
+ setup(pluginBuild) {
10
+ const paths = new Set();
11
+ pluginBuild.onStart(() => paths.clear());
12
+ pluginBuild.onResolve({ filter: /^cloudflare:.*/ }, (args) => {
13
+ paths.add(args.path);
14
+ return { external: true };
15
+ });
16
+ pluginBuild.onEnd(() => {
17
+ if (pluginBuild.initialOptions.format === "iife" && paths.size > 0) {
18
+ // If we are bundling in "Service Worker" mode,
19
+ // imports of external modules such as `cloudflare:...`,
20
+ // which won't be inlined/bundled by esbuild, are invalid.
21
+ const pathList = new Intl.ListFormat("en-US").format(
22
+ Array.from(paths.keys())
23
+ .map((p) => `"${p}"`)
24
+ .sort(),
25
+ );
26
+ return {
27
+ errors: [
28
+ {
29
+ text: dedent`
30
+ Unexpected external import of ${pathList}.
31
+ Your worker has no default export, which means it is assumed to be a Service Worker format Worker.
32
+ Did you mean to create a ES Module format Worker?
33
+ If so, try adding \`export default { ... }\` in your entry-point.
34
+ See https://developers.cloudflare.com/workers/reference/migrate-to-module-workers/.
35
+ `,
36
+ },
37
+ ],
38
+ };
39
+ }
40
+ });
41
+ },
42
+ };
@@ -0,0 +1,14 @@
1
+ import { builtinModules } from "node:module";
2
+
3
+ export const NODEJS_MODULES_RE = new RegExp(
4
+ `^(node:)?(${builtinModules
5
+ .filter(
6
+ (m) =>
7
+ ![
8
+ // in some runtimes (like bun), `ws` is a built-in module but is not in `node`
9
+ // bundling for `nodejs_compat` should not polyfill these modules
10
+ "ws",
11
+ ].includes(m),
12
+ )
13
+ .join("|")})$`,
14
+ );
@@ -0,0 +1,13 @@
1
+ import type { Plugin } from "esbuild";
2
+
3
+ /**
4
+ * An esbuild plugin that will mark `node:async_hooks` imports as external.
5
+ */
6
+ export const esbuildPluginAsyncLocalStorage: Plugin = {
7
+ name: "async-local-storage-imports",
8
+ setup(pluginBuild) {
9
+ pluginBuild.onResolve({ filter: /^node:async_hooks(\/|$)/ }, () => {
10
+ return { external: true };
11
+ });
12
+ },
13
+ };
@@ -4,9 +4,10 @@
4
4
 
5
5
  import type { Plugin, PluginBuild } from "esbuild";
6
6
  import assert from "node:assert";
7
- import module, { createRequire } from "node:module";
7
+ import { createRequire } from "node:module";
8
8
  import nodePath from "node:path";
9
9
  import { dedent } from "../../util/dedent.ts";
10
+ import { NODEJS_MODULES_RE } from "./nodejs-builtin-modules.ts";
10
11
 
11
12
  const _require =
12
13
  typeof require === "undefined" ? createRequire(import.meta.url) : require;
@@ -19,15 +20,34 @@ const REQUIRED_UNENV_ALIAS_NAMESPACE = "required-unenv-alias";
19
20
  *
20
21
  * @returns ESBuild plugin
21
22
  */
22
- export function esbuildPluginHybridNodeCompat(): Plugin {
23
+ export function esbuildPluginHybridNodeCompat({
24
+ compatibilityDate,
25
+ compatibilityFlags,
26
+ }: {
27
+ compatibilityDate?: string;
28
+ compatibilityFlags?: string[];
29
+ }): Plugin {
23
30
  return {
24
31
  name: "hybrid-nodejs_compat",
25
- setup: async (build) => {
32
+ async setup(build) {
26
33
  // `unenv` and `@cloudflare/unenv-preset` only publish esm
27
34
  const { defineEnv } = await import("unenv");
28
- const { cloudflare } = await import("@cloudflare/unenv-preset");
35
+ const { getCloudflarePreset } = await import("@cloudflare/unenv-preset");
36
+
29
37
  const { alias, inject, external, polyfill } = defineEnv({
30
- presets: [cloudflare],
38
+ presets: [
39
+ getCloudflarePreset({
40
+ compatibilityDate,
41
+ compatibilityFlags,
42
+ }),
43
+ {
44
+ alias: {
45
+ // Force esbuild to use the node implementation of debug instead of unenv's no-op stub.
46
+ // The alias is processed by handleUnenvAliasedPackages which uses require.resolve().
47
+ debug: "debug",
48
+ },
49
+ },
50
+ ],
31
51
  npmShims: true,
32
52
  }).env;
33
53
 
@@ -39,19 +59,6 @@ export function esbuildPluginHybridNodeCompat(): Plugin {
39
59
  };
40
60
  }
41
61
 
42
- const NODEJS_MODULES_RE = new RegExp(
43
- `^(node:)?(${module.builtinModules
44
- .filter(
45
- (m) =>
46
- ![
47
- // in some runtimes (like bun), `ws` is a built-in module but is not in `node`
48
- // bundling for `nodejs_compat` should not polyfill these modules
49
- "ws",
50
- ].includes(m),
51
- )
52
- .join("|")})$`,
53
- );
54
-
55
62
  /**
56
63
  * If we are bundling a "Service Worker" formatted Worker, imports of external modules,
57
64
  * which won't be inlined/bundled by esbuild, are invalid.
@@ -108,8 +115,8 @@ function handleRequireCallsToNodeJSBuiltins(build: PluginBuild) {
108
115
  ({ path }) => {
109
116
  return {
110
117
  contents: dedent`
111
- import libDefault from '${path}';
112
- module.exports = libDefault;`,
118
+ import libDefault from '${path}';
119
+ module.exports = libDefault;`,
113
120
  loader: "js",
114
121
  };
115
122
  },
@@ -133,7 +140,7 @@ function handleUnenvAliasedPackages(
133
140
  for (const [module, unresolvedAlias] of Object.entries(alias)) {
134
141
  try {
135
142
  aliasAbsolute[module] = _require.resolve(unresolvedAlias);
136
- } catch (_e) {
143
+ } catch {
137
144
  // this is an alias for package that is not installed in the current app => ignore
138
145
  }
139
146
  }
@@ -144,17 +151,12 @@ function handleUnenvAliasedPackages(
144
151
 
145
152
  build.onResolve({ filter: UNENV_ALIAS_RE }, (args) => {
146
153
  const unresolvedAlias = alias[args.path];
147
-
148
154
  // Convert `require()` calls for NPM packages to a virtual ES Module that can be imported avoiding the require calls.
149
155
  // Note: Does not apply to Node.js packages that are handled in `handleRequireCallsToNodeJSBuiltins`
150
156
  if (
151
157
  args.kind === "require-call" &&
152
158
  (unresolvedAlias.startsWith("unenv/npm/") ||
153
- unresolvedAlias.startsWith("unenv/mock/") ||
154
- // this does not exist in the original wrangler code but does seem to resolve the problem
155
- // TODO(sam): is this the right solution? Why does the same code work in wrangler?
156
- unresolvedAlias.startsWith("@cloudflare/unenv-preset/npm/") ||
157
- unresolvedAlias.startsWith("@cloudflare/unenv-preset/mock/"))
159
+ unresolvedAlias.startsWith("unenv/mock/"))
158
160
  ) {
159
161
  return {
160
162
  path: args.path,
@@ -200,7 +202,6 @@ function handleNodeJSGlobals(
200
202
  ) {
201
203
  const UNENV_VIRTUAL_MODULE_RE = /_virtual_unenv_global_polyfill-(.+)$/;
202
204
  const prefix = nodePath.resolve(
203
- // getBasePath(),
204
205
  import.meta.dirname,
205
206
  "_virtual_unenv_global_polyfill-",
206
207
  );
@@ -0,0 +1,114 @@
1
+ import type { Plugin } from "esbuild";
2
+ import type { NodeJSCompatMode } from "miniflare";
3
+ import { relative } from "node:path";
4
+ import chalk from "picocolors";
5
+ import { dedent } from "../../util/dedent.ts";
6
+ import { logger } from "../../util/logger.ts";
7
+ import { NODEJS_MODULES_RE } from "./nodejs-builtin-modules.ts";
8
+
9
+ /**
10
+ * An esbuild plugin that will:
11
+ * - mark any `node:...` imports as external
12
+ * - warn if there are node imports (if not in v1 mode)
13
+ *
14
+ * Applies to: null, als, legacy and v1 modes.
15
+ */
16
+ export const esbuildPluginNodeCompatImports = (
17
+ mode: NodeJSCompatMode,
18
+ ): Plugin => ({
19
+ name: "nodejs_compat-imports",
20
+ setup(pluginBuild) {
21
+ // Infinite loop detection
22
+ const seen = new Set<string>();
23
+
24
+ // Prevent multiple warnings per package
25
+ const warnedPackages = new Map<string, string[]>();
26
+
27
+ pluginBuild.onStart(() => {
28
+ seen.clear();
29
+ warnedPackages.clear();
30
+ });
31
+ pluginBuild.onResolve(
32
+ { filter: NODEJS_MODULES_RE },
33
+ async ({ path, kind, resolveDir, importer }) => {
34
+ const specifier = `${path}:${kind}:${resolveDir}:${importer}`;
35
+ if (seen.has(specifier)) {
36
+ return;
37
+ }
38
+
39
+ seen.add(specifier);
40
+ // Try to resolve this import as a normal package
41
+ const result = await pluginBuild.resolve(path, {
42
+ kind,
43
+ resolveDir,
44
+ importer,
45
+ });
46
+
47
+ if (result.errors.length > 0) {
48
+ // esbuild couldn't resolve the package
49
+ // We should warn the user, but not fail the build
50
+ const pathWarnedPackages = warnedPackages.get(path) ?? [];
51
+ pathWarnedPackages.push(importer);
52
+ warnedPackages.set(path, pathWarnedPackages);
53
+
54
+ return { external: true };
55
+ }
56
+ // This is a normal package—don't treat it specially
57
+ return result;
58
+ },
59
+ );
60
+
61
+ /**
62
+ * If we are bundling a "Service Worker" formatted Worker, imports of external modules,
63
+ * which won't be inlined/bundled by esbuild, are invalid.
64
+ *
65
+ * This `onEnd()` handler will error if it identifies node.js external imports.
66
+ */
67
+ pluginBuild.onEnd(() => {
68
+ if (
69
+ pluginBuild.initialOptions.format === "iife" &&
70
+ warnedPackages.size > 0
71
+ ) {
72
+ const paths = new Intl.ListFormat("en-US").format(
73
+ Array.from(warnedPackages.keys())
74
+ .map((p) => `"${p}"`)
75
+ .sort(),
76
+ );
77
+ return {
78
+ errors: [
79
+ {
80
+ text: dedent`
81
+ Unexpected external import of ${paths}.
82
+ Your worker has no default export, which means it is assumed to be a Service Worker format Worker.
83
+ Did you mean to create a ES Module format Worker?
84
+ If so, try adding \`export default { ... }\` in your entry-point.
85
+ See https://developers.cloudflare.com/workers/reference/migrate-to-module-workers/.
86
+ `,
87
+ },
88
+ ],
89
+ };
90
+ }
91
+ });
92
+
93
+ // Wait until the build finishes to log warnings, so that all files which import a package
94
+ // can be collated
95
+ pluginBuild.onEnd(() => {
96
+ if (mode !== "v1") {
97
+ warnedPackages.forEach((importers: string[], path: string) => {
98
+ logger.warn(
99
+ dedent`
100
+ The package "${path}" wasn't found on the file system but is built into node.
101
+ Your Worker may throw errors at runtime unless you enable the "nodejs_compat" compatibility flag. Refer to https://developers.cloudflare.com/workers/runtime-apis/nodejs/ for more details. Imported from:
102
+ ${toList(importers, pluginBuild.initialOptions.absWorkingDir)}`,
103
+ );
104
+ });
105
+ }
106
+ });
107
+ },
108
+ });
109
+
110
+ function toList(items: string[], absWorkingDir: string | undefined): string {
111
+ return items
112
+ .map((i) => ` - ${chalk.blue(relative(absWorkingDir ?? "/", i))}`)
113
+ .join("\n");
114
+ }
@@ -12,21 +12,41 @@ export function createWasmPlugin() {
12
12
  build.onStart(() => {
13
13
  modules.clear();
14
14
  });
15
- build.onResolve({ filter: /\.wasm$/ }, async (args) => {
16
- if (!modules.has(args.path)) {
17
- const name = path.normalize(args.path);
18
- assert(build.initialOptions.outdir, "outdir is required");
19
- await fs.mkdir(build.initialOptions.outdir, { recursive: true });
20
- await fs.copyFile(
21
- path.join(args.resolveDir, name),
22
- path.join(build.initialOptions.outdir, name),
23
- );
24
- modules.set(args.path, {
25
- type: "wasm",
26
- path: name,
27
- });
15
+
16
+ // Handle imports like `import "./foo.wasm"` and `import "./foo.wasm?module"`
17
+ // TODO(john): Figure out why this suddenly became necessary
18
+ build.onResolve({ filter: /\.wasm(\?.*)?$/ }, async (args) => {
19
+ const resolved = modules.get(args.path);
20
+ if (resolved) {
21
+ return { path: resolved.path, external: true };
28
22
  }
29
- return { external: true };
23
+
24
+ // Normalize path and remove the `?module` query param so we have the actual file name to copy
25
+ const name = path.normalize(args.path).replace(/\?.*$/, "");
26
+
27
+ // Resolve path to outdir (required for monorepos if the workdir is not the same as process.cwd())
28
+ assert(
29
+ build.initialOptions.absWorkingDir && build.initialOptions.outdir,
30
+ "Missing absWorkingDir or outdir from esbuild options",
31
+ );
32
+ const outdir = path.resolve(
33
+ build.initialOptions.absWorkingDir,
34
+ build.initialOptions.outdir,
35
+ );
36
+
37
+ // Copy to outdir so it's included in the upload
38
+ await fs.mkdir(outdir, { recursive: true });
39
+ await fs.copyFile(
40
+ path.join(args.resolveDir, name),
41
+ path.join(outdir, name),
42
+ );
43
+ modules.set(args.path, {
44
+ type: "wasm",
45
+ path: name,
46
+ });
47
+
48
+ // Resolve to the normalized file name (the `?module` query param is not needed in workerd)
49
+ return { path: name, external: true };
30
50
  });
31
51
  },
32
52
  };
@@ -4,4 +4,4 @@
4
4
  /**
5
5
  * The default Cloudflare Workers compatibility date, set based on the latest workerd release at the time of build.
6
6
  */
7
- export const DEFAULT_COMPATIBILITY_DATE = "2025-08-26";
7
+ export const DEFAULT_COMPATIBILITY_DATE = "2025-09-06";
@@ -20,8 +20,8 @@ export const applyLocalD1Migrations = async (
20
20
  });
21
21
  try {
22
22
  await miniflare.ready;
23
- const db = await miniflare.getD1Database("DB");
24
- const session = db.withSession("first-primary");
23
+ const db: D1Database = await miniflare.getD1Database("DB");
24
+ const session: D1DatabaseSession = db.withSession("first-primary");
25
25
  await session
26
26
  .prepare(
27
27
  `CREATE TABLE IF NOT EXISTS ${options.migrationsTable} (
@@ -19,6 +19,7 @@ export type DurableObjectNamespace<T = any> = {
19
19
  environment?: string;
20
20
  sqlite?: boolean;
21
21
  namespaceId?: string;
22
+ /** @internal */
22
23
  __service__: T;
23
24
  };
24
25