alchemy 0.63.0 → 0.64.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 (181) hide show
  1. package/bin/alchemy.js +97 -87
  2. package/bin/commands/init.ts +1 -1
  3. package/bin/services/github-workflow.ts +2 -2
  4. package/lib/alchemy.d.ts +4 -0
  5. package/lib/alchemy.d.ts.map +1 -1
  6. package/lib/alchemy.js +10 -4
  7. package/lib/alchemy.js.map +1 -1
  8. package/lib/apply.d.ts +3 -0
  9. package/lib/apply.d.ts.map +1 -1
  10. package/lib/apply.js +11 -1
  11. package/lib/apply.js.map +1 -1
  12. package/lib/cloudflare/bindings.d.ts +3 -1
  13. package/lib/cloudflare/bindings.d.ts.map +1 -1
  14. package/lib/cloudflare/bucket.d.ts +2 -1
  15. package/lib/cloudflare/bucket.d.ts.map +1 -1
  16. package/lib/cloudflare/bucket.js.map +1 -1
  17. package/lib/cloudflare/bundle/cloudflare-internal.d.ts +6 -0
  18. package/lib/cloudflare/bundle/cloudflare-internal.d.ts.map +1 -0
  19. package/lib/cloudflare/bundle/cloudflare-internal.js +39 -0
  20. package/lib/cloudflare/bundle/cloudflare-internal.js.map +1 -0
  21. package/lib/cloudflare/bundle/nodejs-builtin-modules.d.ts +2 -0
  22. package/lib/cloudflare/bundle/nodejs-builtin-modules.d.ts.map +1 -0
  23. package/lib/cloudflare/bundle/nodejs-builtin-modules.js +9 -0
  24. package/lib/cloudflare/bundle/nodejs-builtin-modules.js.map +1 -0
  25. package/lib/cloudflare/bundle/plugin-als-external.d.ts +6 -0
  26. package/lib/cloudflare/bundle/plugin-als-external.d.ts.map +1 -0
  27. package/lib/cloudflare/bundle/plugin-als-external.js +12 -0
  28. package/lib/cloudflare/bundle/plugin-als-external.js.map +1 -0
  29. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.d.ts +4 -1
  30. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.d.ts.map +1 -1
  31. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js +23 -23
  32. package/lib/cloudflare/bundle/plugin-hybrid-node-compat.js.map +1 -1
  33. package/lib/cloudflare/bundle/plugin-node-compat.d.ts +11 -0
  34. package/lib/cloudflare/bundle/plugin-node-compat.d.ts.map +1 -0
  35. package/lib/cloudflare/bundle/plugin-node-compat.js +93 -0
  36. package/lib/cloudflare/bundle/plugin-node-compat.js.map +1 -0
  37. package/lib/cloudflare/cloudflare-env-proxy.js +1 -1
  38. package/lib/cloudflare/cloudflare-env-proxy.js.map +1 -1
  39. package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
  40. package/lib/cloudflare/compatibility-date.gen.js +1 -1
  41. package/lib/cloudflare/d1-local-migrations.js.map +1 -1
  42. package/lib/cloudflare/durable-object-namespace.d.ts +1 -0
  43. package/lib/cloudflare/durable-object-namespace.d.ts.map +1 -1
  44. package/lib/cloudflare/hyperdrive.js +1 -1
  45. package/lib/cloudflare/hyperdrive.js.map +1 -1
  46. package/lib/cloudflare/website.d.ts.map +1 -1
  47. package/lib/cloudflare/website.js +80 -82
  48. package/lib/cloudflare/website.js.map +1 -1
  49. package/lib/cloudflare/worker-bundle.d.ts +6 -0
  50. package/lib/cloudflare/worker-bundle.d.ts.map +1 -1
  51. package/lib/cloudflare/worker-bundle.js +34 -10
  52. package/lib/cloudflare/worker-bundle.js.map +1 -1
  53. package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
  54. package/lib/cloudflare/worker-metadata.js +2 -1
  55. package/lib/cloudflare/worker-metadata.js.map +1 -1
  56. package/lib/cloudflare/worker.d.ts.map +1 -1
  57. package/lib/cloudflare/worker.js +24 -6
  58. package/lib/cloudflare/worker.js.map +1 -1
  59. package/lib/cloudflare/wrangler.json.d.ts +2 -4
  60. package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
  61. package/lib/cloudflare/wrangler.json.js +16 -14
  62. package/lib/cloudflare/wrangler.json.js.map +1 -1
  63. package/lib/context.d.ts +3 -1
  64. package/lib/context.d.ts.map +1 -1
  65. package/lib/context.js +2 -2
  66. package/lib/context.js.map +1 -1
  67. package/lib/destroy.d.ts +8 -0
  68. package/lib/destroy.d.ts.map +1 -1
  69. package/lib/destroy.js +20 -4
  70. package/lib/destroy.js.map +1 -1
  71. package/lib/neon/project.d.ts.map +1 -1
  72. package/lib/neon/project.js +4 -5
  73. package/lib/neon/project.js.map +1 -1
  74. package/lib/planetscale/api/client.gen.d.ts +1750 -0
  75. package/lib/planetscale/api/client.gen.d.ts.map +1 -0
  76. package/lib/planetscale/api/client.gen.js +2401 -0
  77. package/lib/planetscale/api/client.gen.js.map +1 -0
  78. package/lib/planetscale/api/index.d.ts +3 -0
  79. package/lib/planetscale/api/index.d.ts.map +1 -0
  80. package/lib/planetscale/api/index.js +4 -0
  81. package/lib/planetscale/api/index.js.map +1 -0
  82. package/lib/planetscale/api/types.gen.d.ts +12156 -0
  83. package/lib/planetscale/api/types.gen.d.ts.map +1 -0
  84. package/lib/planetscale/api/types.gen.js +2 -0
  85. package/lib/planetscale/api/types.gen.js.map +1 -0
  86. package/lib/planetscale/branch.d.ts +5 -8
  87. package/lib/planetscale/branch.d.ts.map +1 -1
  88. package/lib/planetscale/branch.js +111 -97
  89. package/lib/planetscale/branch.js.map +1 -1
  90. package/lib/planetscale/database.d.ts +2 -6
  91. package/lib/planetscale/database.d.ts.map +1 -1
  92. package/lib/planetscale/database.js +157 -118
  93. package/lib/planetscale/database.js.map +1 -1
  94. package/lib/planetscale/index.d.ts +1 -1
  95. package/lib/planetscale/index.d.ts.map +1 -1
  96. package/lib/planetscale/index.js +1 -1
  97. package/lib/planetscale/index.js.map +1 -1
  98. package/lib/planetscale/password.d.ts +4 -7
  99. package/lib/planetscale/password.d.ts.map +1 -1
  100. package/lib/planetscale/password.js +71 -56
  101. package/lib/planetscale/password.js.map +1 -1
  102. package/lib/planetscale/utils.d.ts +8 -6
  103. package/lib/planetscale/utils.d.ts.map +1 -1
  104. package/lib/planetscale/utils.js +85 -70
  105. package/lib/planetscale/utils.js.map +1 -1
  106. package/lib/scope.d.ts +5 -1
  107. package/lib/scope.d.ts.map +1 -1
  108. package/lib/scope.js +11 -3
  109. package/lib/scope.js.map +1 -1
  110. package/lib/upstash/redis.js +1 -1
  111. package/lib/upstash/redis.js.map +1 -1
  112. package/lib/util/dedent.d.ts +3 -14
  113. package/lib/util/dedent.d.ts.map +1 -1
  114. package/lib/util/dedent.js +28 -52
  115. package/lib/util/dedent.js.map +1 -1
  116. package/lib/util/idempotent-spawn.d.ts.map +1 -1
  117. package/lib/util/idempotent-spawn.js +21 -2
  118. package/lib/util/idempotent-spawn.js.map +1 -1
  119. package/lib/util/xml.js +1 -1
  120. package/lib/util/xml.js.map +1 -1
  121. package/package.json +4 -3
  122. package/src/alchemy.ts +14 -6
  123. package/src/apply.ts +13 -1
  124. package/src/cloudflare/bindings.ts +3 -1
  125. package/src/cloudflare/bucket.ts +3 -1
  126. package/src/cloudflare/bundle/cloudflare-internal.ts +42 -0
  127. package/src/cloudflare/bundle/nodejs-builtin-modules.ts +14 -0
  128. package/src/cloudflare/bundle/plugin-als-external.ts +13 -0
  129. package/src/cloudflare/bundle/plugin-hybrid-node-compat.ts +29 -28
  130. package/src/cloudflare/bundle/plugin-node-compat.ts +114 -0
  131. package/src/cloudflare/cloudflare-env-proxy.ts +1 -1
  132. package/src/cloudflare/compatibility-date.gen.ts +1 -1
  133. package/src/cloudflare/d1-local-migrations.ts +2 -2
  134. package/src/cloudflare/durable-object-namespace.ts +1 -1
  135. package/src/cloudflare/hyperdrive.ts +1 -1
  136. package/src/cloudflare/website.ts +88 -89
  137. package/src/cloudflare/worker-bundle.ts +46 -9
  138. package/src/cloudflare/worker-metadata.ts +3 -1
  139. package/src/cloudflare/worker.ts +35 -7
  140. package/src/cloudflare/wrangler.json.ts +108 -110
  141. package/src/context.ts +5 -3
  142. package/src/destroy.ts +26 -8
  143. package/src/neon/project.ts +4 -5
  144. package/src/planetscale/api/client.gen.ts +2882 -0
  145. package/src/planetscale/api/index.ts +4 -0
  146. package/src/planetscale/api/types.gen.ts +13041 -0
  147. package/src/planetscale/branch.ts +152 -169
  148. package/src/planetscale/database.ts +195 -204
  149. package/src/planetscale/index.ts +1 -1
  150. package/src/planetscale/password.ts +86 -85
  151. package/src/planetscale/utils.ts +141 -122
  152. package/src/scope.ts +12 -3
  153. package/src/upstash/redis.ts +1 -1
  154. package/src/util/dedent.ts +33 -61
  155. package/src/util/idempotent-spawn.ts +23 -2
  156. package/src/util/xml.ts +1 -1
  157. package/templates/astro/alchemy.run.ts +1 -3
  158. package/templates/hono/alchemy.run.ts +0 -1
  159. package/templates/nuxt/alchemy.run.ts +1 -3
  160. package/templates/react-router/alchemy.run.ts +1 -3
  161. package/templates/rwsdk/alchemy.run.ts +0 -2
  162. package/templates/sveltekit/alchemy.run.ts +1 -3
  163. package/templates/tanstack-start/alchemy.run.ts +1 -3
  164. package/templates/typescript/alchemy.run.ts +0 -1
  165. package/templates/vite/alchemy.run.ts +0 -1
  166. package/workers/cloudflare-state-store.js +58 -58
  167. package/lib/cloudflare/bundle/externals.d.ts +0 -3
  168. package/lib/cloudflare/bundle/externals.d.ts.map +0 -1
  169. package/lib/cloudflare/bundle/externals.js +0 -35
  170. package/lib/cloudflare/bundle/externals.js.map +0 -1
  171. package/lib/cloudflare/bundle/plugin-compat-warning.d.ts +0 -10
  172. package/lib/cloudflare/bundle/plugin-compat-warning.d.ts.map +0 -1
  173. package/lib/cloudflare/bundle/plugin-compat-warning.js +0 -67
  174. package/lib/cloudflare/bundle/plugin-compat-warning.js.map +0 -1
  175. package/lib/planetscale/api.d.ts +0 -53
  176. package/lib/planetscale/api.d.ts.map +0 -1
  177. package/lib/planetscale/api.js +0 -84
  178. package/lib/planetscale/api.js.map +0 -1
  179. package/src/cloudflare/bundle/externals.ts +0 -36
  180. package/src/cloudflare/bundle/plugin-compat-warning.ts +0 -94
  181. package/src/planetscale/api.ts +0 -115
@@ -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
+ }
@@ -31,7 +31,7 @@ export const getPlatformProxyOptions = (
31
31
  };
32
32
  if (!persist) {
33
33
  const message =
34
- "[Alchemy] Persistence for local bindings is disabled. Some bindings may not work as expected. To enable, remove the \`persist\` option from getPlatformProxyOptions.";
34
+ "[Alchemy] Persistence for local bindings is disabled. Some bindings may not work as expected. To enable, remove the `persist` option from getPlatformProxyOptions.";
35
35
  if (!warned.has(message)) {
36
36
  console.warn(message);
37
37
  warned.add(message);
@@ -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-23";
7
+ export const DEFAULT_COMPATIBILITY_DATE = "2025-08-26";
@@ -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,7 +19,7 @@ export type DurableObjectNamespace<T = any> = {
19
19
  environment?: string;
20
20
  sqlite?: boolean;
21
21
  namespaceId?: string;
22
- // @ts-ignore - phantom type
22
+ /** @internal */
23
23
  __service__: T;
24
24
  };
25
25
 
@@ -537,7 +537,7 @@ const normalizePort = (
537
537
  return port;
538
538
  }
539
539
  if (port) {
540
- return Number.parseInt(port);
540
+ return Number.parseInt(port, 10);
541
541
  }
542
542
  return scheme === "postgres" ? 5432 : 3306;
543
543
  };
@@ -1,7 +1,6 @@
1
1
  import assert from "node:assert";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
- import { alchemy } from "../alchemy.ts";
5
4
  import { Exec } from "../os/index.ts";
6
5
  import { Scope } from "../scope.ts";
7
6
  import { isSecret } from "../secret.ts";
@@ -255,101 +254,101 @@ export async function Website<B extends Bindings>(
255
254
  entrypoint: path.relative(paths.cwd, paths.entrypoint),
256
255
  } as WorkerProps<B> & { name: string };
257
256
 
258
- return await alchemy.run(id, { parent: Scope.current }, async (scope) => {
259
- if (!workerProps.entrypoint) {
260
- await fs.mkdir(path.dirname(paths.entrypoint), { recursive: true });
261
- const content =
262
- script ??
263
- dedent`
264
- export default {
265
- async fetch(request, env) {
266
- return new Response("Not Found", { status: 404 });
267
- },
268
- };`;
269
- await fs.writeFile(paths.entrypoint, content);
270
- }
257
+ if (!workerProps.entrypoint) {
258
+ await fs.mkdir(path.dirname(paths.entrypoint), { recursive: true });
259
+ const content =
260
+ script ??
261
+ dedent`
262
+ export default {
263
+ async fetch(request, env) {
264
+ return new Response("Not Found", { status: 404 });
265
+ },
266
+ };`;
267
+ await fs.writeFile(paths.entrypoint, content);
268
+ }
271
269
 
272
- await writeMiniflareSymlink(paths.cwd);
270
+ await writeMiniflareSymlink(paths.cwd);
273
271
 
274
- await WranglerJson("wrangler.jsonc", {
275
- path: path.relative(paths.cwd, paths.wrangler.path),
276
- worker,
277
- assets: {
278
- binding: "ASSETS",
279
- directory: path.relative(paths.cwd, paths.assets),
280
- },
281
- main: path.relative(paths.cwd, paths.wrangler.main),
282
- secrets,
283
- transform: {
284
- wrangler: props.wrangler?.transform,
285
- },
286
- });
272
+ await WranglerJson({
273
+ path: path.relative(paths.cwd, paths.wrangler.path),
274
+ worker,
275
+ assets: {
276
+ binding: "ASSETS",
277
+ directory: path.relative(paths.cwd, paths.assets),
278
+ },
279
+ main: path.relative(paths.cwd, paths.wrangler.main),
280
+ secrets,
281
+ transform: {
282
+ wrangler: props.wrangler?.transform,
283
+ },
284
+ });
287
285
 
288
- if (build && !scope.local) {
289
- await Exec("build", {
290
- cwd: path.relative(process.cwd(), paths.cwd),
291
- command: build.command,
292
- env: {
293
- ...env,
294
- ...(typeof build === "object" ? build.env : {}),
295
- NODE_ENV: "production",
296
- },
297
- memoize: typeof build === "object" ? build.memoize : undefined,
298
- });
299
- }
286
+ const scope = Scope.current;
300
287
 
301
- let url: string | undefined;
302
- if (dev && scope.local) {
303
- url = await scope.spawn(name, {
304
- cmd: typeof dev === "string" ? dev : dev.command,
305
- cwd: paths.cwd,
306
- extract: (line) => {
307
- const URL_REGEX =
308
- /http:\/\/(localhost|0\.0\.0\.0|127\.0\.0\.1|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):\d+\/?/;
309
- const match = line
310
- .replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "")
311
- .match(URL_REGEX);
312
- if (match) {
313
- return match[0];
314
- }
315
- },
316
- env: {
317
- ...Object.fromEntries(
318
- Object.entries(env ?? {}).flatMap(([key, value]) => {
319
- if (isSecret(value)) {
320
- return [[key, value.unencrypted]];
321
- }
322
- if (typeof value === "string") {
323
- return [[key, value]];
324
- }
325
- return [];
326
- }),
327
- ),
328
- ...(typeof dev === "object" ? dev.env : {}),
329
- FORCE_COLOR: "1",
330
- ...process.env,
331
- // NOTE: we must set this to ensure the user does not accidentally set `NODE_ENV=production`
332
- // which breaks `vite dev` (it won't, for example, re-write `process.env.TSS_APP_BASE` in the `.js` client side bundle)
333
- NODE_ENV: "development",
334
- },
335
- });
336
- }
288
+ if (build && !scope.local) {
289
+ await Exec(`${id}-build`, {
290
+ cwd: path.relative(process.cwd(), paths.cwd),
291
+ command: build.command,
292
+ env: {
293
+ ...env,
294
+ ...(typeof build === "object" ? build.env : {}),
295
+ NODE_ENV: "production",
296
+ },
297
+ memoize: typeof build === "object" ? build.memoize : undefined,
298
+ });
299
+ }
337
300
 
338
- return (await Worker("worker", {
339
- ...worker,
340
- bindings: {
341
- ...worker.bindings,
342
- ...(!scope.local
343
- ? {
344
- ASSETS: await Assets("assets", {
345
- path: path.relative(process.cwd(), paths.assets),
346
- }),
301
+ let url: string | undefined;
302
+ if (dev && scope.local) {
303
+ url = await scope.spawn(name, {
304
+ cmd: typeof dev === "string" ? dev : dev.command,
305
+ cwd: paths.cwd,
306
+ extract: (line) => {
307
+ const URL_REGEX =
308
+ /http:\/\/(localhost|0\.0\.0\.0|127\.0\.0\.1|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):\d+\/?/;
309
+ const match = line
310
+ .replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "")
311
+ .match(URL_REGEX);
312
+ if (match) {
313
+ return match[0];
314
+ }
315
+ },
316
+ env: {
317
+ ...Object.fromEntries(
318
+ Object.entries(env ?? {}).flatMap(([key, value]) => {
319
+ if (isSecret(value)) {
320
+ return [[key, value.unencrypted]];
321
+ }
322
+ if (typeof value === "string") {
323
+ return [[key, value]];
347
324
  }
348
- : {}),
325
+ return [];
326
+ }),
327
+ ),
328
+ ...(typeof dev === "object" ? dev.env : {}),
329
+ FORCE_COLOR: "1",
330
+ ...process.env,
331
+ // NOTE: we must set this to ensure the user does not accidentally set `NODE_ENV=production`
332
+ // which breaks `vite dev` (it won't, for example, re-write `process.env.TSS_APP_BASE` in the `.js` client side bundle)
333
+ NODE_ENV: "development",
349
334
  },
350
- dev: url ? { url } : undefined,
351
- })) as Website<B>;
352
- });
335
+ });
336
+ }
337
+
338
+ return (await Worker(id, {
339
+ ...worker,
340
+ bindings: {
341
+ ...worker.bindings,
342
+ ...(!scope.local
343
+ ? {
344
+ ASSETS: await Assets("assets", {
345
+ path: path.relative(process.cwd(), paths.assets),
346
+ }),
347
+ }
348
+ : {}),
349
+ },
350
+ dev: url ? { url } : undefined,
351
+ })) as Website<B>;
353
352
  }
354
353
 
355
354
  async function writeMiniflareSymlink(cwd: string) {
@@ -4,11 +4,12 @@ import { err, ok, type Result } from "neverthrow";
4
4
  import fs from "node:fs/promises";
5
5
  import path from "pathe";
6
6
  import { logger } from "../util/logger.ts";
7
- import { external, external_als } from "./bundle/externals.ts";
7
+ import { cloudflareInternalPlugin } from "./bundle/cloudflare-internal.ts";
8
8
  import { esbuildPluginAlias } from "./bundle/plugin-alias.ts";
9
- import { esbuildPluginCompatWarning } from "./bundle/plugin-compat-warning.ts";
9
+ import { esbuildPluginAsyncLocalStorage } from "./bundle/plugin-als-external.ts";
10
10
  import { createHotReloadPlugin } from "./bundle/plugin-hot-reload.ts";
11
11
  import { esbuildPluginHybridNodeCompat } from "./bundle/plugin-hybrid-node-compat.ts";
12
+ import { esbuildPluginNodeCompatImports } from "./bundle/plugin-node-compat.ts";
12
13
  import { createWasmPlugin } from "./bundle/plugin-wasm.ts";
13
14
  import { validateNodeCompat } from "./bundle/validate-node-compat.ts";
14
15
 
@@ -76,6 +77,8 @@ export function normalizeWorkerBundle(props: {
76
77
  cwd: props.cwd,
77
78
  outdir: props.outdir,
78
79
  sourcemap: props.sourceMap !== false ? true : undefined,
80
+ compatibilityDate: props.compatibilityDate,
81
+ compatibilityFlags: props.compatibilityFlags,
79
82
  ...props.bundle,
80
83
  }),
81
84
  );
@@ -248,6 +251,8 @@ export namespace WorkerBundleSource {
248
251
  entrypoint: string;
249
252
  cwd: string;
250
253
  outdir: string;
254
+ compatibilityDate: string;
255
+ compatibilityFlags: string[];
251
256
  }
252
257
 
253
258
  export class ESBuild implements WorkerBundleSource {
@@ -328,6 +333,8 @@ export namespace WorkerBundleSource {
328
333
  nodeCompat,
329
334
  cwd,
330
335
  format,
336
+ compatibilityDate: _compatibilityDate,
337
+ compatibilityFlags: _compatibilityFlags,
331
338
  ...props
332
339
  } = this.props;
333
340
  return {
@@ -356,19 +363,49 @@ export namespace WorkerBundleSource {
356
363
  conditions: props.conditions ?? ["workerd", "worker", "browser"],
357
364
  plugins: [
358
365
  esbuildPluginAlias(props.alias ?? {}, this.props.cwd),
359
- nodeCompat === "v2"
360
- ? esbuildPluginHybridNodeCompat()
361
- : esbuildPluginCompatWarning(nodeCompat ?? null),
366
+ cloudflareInternalPlugin,
367
+ ...this.getNodeJSCompatPlugins({
368
+ mode: nodeCompat,
369
+ compatibilityDate: this.props.compatibilityDate,
370
+ compatibilityFlags: this.props.compatibilityFlags,
371
+ }),
362
372
  ...(props.plugins ?? []),
363
373
  ...additionalPlugins,
364
374
  ],
365
- external: [
366
- ...(nodeCompat === "als" ? external_als : external),
367
- ...(props.external ?? []),
368
- ],
375
+ external: [...(props.external ?? [])],
369
376
  } satisfies esbuild.BuildOptions;
370
377
  }
371
378
 
379
+ /**
380
+ * Returns the list of ESBuild plugins to use for a given compat mode.
381
+ */
382
+ private getNodeJSCompatPlugins({
383
+ mode,
384
+ compatibilityDate,
385
+ compatibilityFlags,
386
+ }: {
387
+ mode: "als" | "v2" | null;
388
+ compatibilityDate?: string;
389
+ compatibilityFlags?: string[];
390
+ }): esbuild.Plugin[] {
391
+ switch (mode) {
392
+ case "als":
393
+ return [
394
+ esbuildPluginAsyncLocalStorage,
395
+ esbuildPluginNodeCompatImports(mode),
396
+ ];
397
+ case "v2":
398
+ return [
399
+ esbuildPluginHybridNodeCompat({
400
+ compatibilityDate,
401
+ compatibilityFlags,
402
+ }),
403
+ ];
404
+ case null:
405
+ return [esbuildPluginNodeCompatImports(mode)];
406
+ }
407
+ }
408
+
372
409
  private async formatBuildOutput(
373
410
  metafile: esbuild.Metafile,
374
411
  ): Promise<WorkerBundle> {
@@ -442,6 +442,8 @@ export async function prepareWorkerMetadata(
442
442
  type: "r2_bucket",
443
443
  name: bindingName,
444
444
  bucket_name: binding.name,
445
+ jurisdiction:
446
+ binding.jurisdiction === "default" ? undefined : binding.jurisdiction,
445
447
  });
446
448
  } else if (binding.type === "secrets_store_secret") {
447
449
  meta.bindings.push({
@@ -651,7 +653,7 @@ export function bumpMigrationTagVersion(tag?: string) {
651
653
  if (!tag.match(/^v\d+$/)) {
652
654
  throw new Error(`Invalid tag format: ${tag}. Expected format: v<number>`);
653
655
  }
654
- return `v${Number.parseInt(tag.slice(1)) + 1}`;
656
+ return `v${Number.parseInt(tag.slice(1), 10) + 1}`;
655
657
  }
656
658
  return undefined;
657
659
  }