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
@@ -1,71 +1,55 @@
1
1
  /**
2
- * Removes common indentation from template literals to make them more readable in code.
3
- * This allows writing multi-line strings with proper indentation in the source code
4
- * while removing that indentation in the output string.
2
+ * Tagged template literal for removing indentation from a block of text.
5
3
  *
6
- * @example
7
- * const message = dedent`
8
- * This is a multi-line string
9
- * that will have its indentation removed.
10
- * This line has extra indentation that will be preserved.
11
- * `;
12
- * // Result:
13
- * // "This is a multi-line string
14
- * // that will have its indentation removed.
15
- * // This line has extra indentation that will be preserved."
4
+ * If the first line is empty, it will be ignored.
16
5
  */
17
- export function dedent(
18
- strings: TemplateStringsArray,
19
- ...values: any[]
20
- ): string {
21
- // Combine the template strings and values
22
- const result = strings.reduce((acc, str, i) => {
23
- return acc + str + (values[i] !== undefined ? values[i] : "");
24
- }, "");
6
+ export function dedent(strings: TemplateStringsArray, ...values: unknown[]) {
7
+ // Convert template literal arguments back to a regular string
8
+ const raw = String.raw({ raw: strings }, ...values);
9
+ // Split the string by lines
10
+ let lines = raw.split("\n");
25
11
 
26
- // Split into lines
27
- const lines = result.split("\n");
28
-
29
- // Remove ALL leading blank/whitespace-only lines
12
+ // Remove leading blank lines
30
13
  while (lines.length > 0 && lines[0].trim() === "") {
31
- lines.shift();
14
+ lines = lines.slice(1);
32
15
  }
33
16
 
34
- // Remove ALL trailing blank/whitespace-only lines
17
+ // Remove trailing blank lines
35
18
  while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
36
- lines.pop();
19
+ lines = lines.slice(0, lines.length - 1);
37
20
  }
38
21
 
39
- // If there are no lines left after trimming, return an empty string
22
+ // If no content lines remain, return empty string
40
23
  if (lines.length === 0) {
41
24
  return "";
42
25
  }
43
26
 
44
- // Find the minimum indentation level (excluding empty/whitespace lines)
45
- const minIndent = lines
46
- .filter((line) => line.trim().length > 0) // Only consider non-blank lines for indent calculation
47
- .reduce((min, line) => {
48
- const indent = line.match(/^[ \t]*/)?.[0].length || 0;
49
- // Handle potentially infinite min if all lines are blank (though handled by early return)
50
- return indent < min ? indent : min;
51
- }, Number.POSITIVE_INFINITY);
27
+ // Find the minimum-length indent from content lines (non-blank lines)
28
+ let minIndentLength = Number.POSITIVE_INFINITY;
29
+ for (const line of lines) {
30
+ if (line.trim() !== "") {
31
+ const indent = line.match(/^[ \t]*/)?.[0];
32
+ if (indent != null && indent.length < minIndentLength) {
33
+ minIndentLength = indent.length;
34
+ }
35
+ }
36
+ }
52
37
 
53
- // If minIndent remains Infinity (only blank lines existed), return empty string.
54
- // This case should be caught by the lines.length check above, but belt-and-suspenders.
55
- if (minIndent === Number.POSITIVE_INFINITY) {
56
- return "";
38
+ // If no indentation found, return lines as-is
39
+ if (minIndentLength === Number.POSITIVE_INFINITY) {
40
+ return lines.join("\n");
57
41
  }
58
42
 
59
- // Remove the common indentation from each line
60
- const dedented = lines.map((line) => {
61
- // Preserve internal blank lines as empty strings, don't try to substring them
62
- if (line.trim().length === 0) {
63
- return "";
43
+ // Remove indent from all lines, preserving blank lines
44
+ lines = lines.map((line) => {
45
+ if (line.trim() === "") {
46
+ return line; // Preserve blank lines
64
47
  }
65
- // Apply dedent only to lines with content
66
- return line.substring(minIndent);
48
+ return line.startsWith(" ".repeat(minIndentLength)) ||
49
+ line.startsWith("\t".repeat(minIndentLength))
50
+ ? line.substring(minIndentLength)
51
+ : line;
67
52
  });
68
53
 
69
- // Join the lines back together
70
- return dedented.join("\n");
54
+ return lines.join("\n");
71
55
  }
@@ -1,4 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { isSecret } from "../secret.ts";
2
3
 
3
4
  type AsyncReturnType<T> = T extends (...args: any[]) => Promise<infer R>
4
5
  ? R
@@ -25,5 +26,16 @@ export function memoize<F extends (...args: any[]) => Promise<any>>(
25
26
  }
26
27
 
27
28
  export const defaultKeyFn = (...args: any[]) => {
28
- return createHash("sha256").update(JSON.stringify(args)).digest("hex");
29
+ return createHash("sha256")
30
+ .update(
31
+ JSON.stringify(args, (_, value) => {
32
+ // Secret names may differ between instantiations,
33
+ // so we unwrap it to make the key deterministic.
34
+ if (isSecret(value)) {
35
+ return value.unencrypted;
36
+ }
37
+ return value;
38
+ }),
39
+ )
40
+ .digest("hex");
29
41
  };
@@ -5,9 +5,7 @@ import { Astro } from "alchemy/cloudflare";
5
5
 
6
6
  const app = await alchemy("{projectName}");
7
7
 
8
- export const worker = await Astro("website", {
9
- name: `${app.name}-${app.stage}-website`,
10
- });
8
+ export const worker = await Astro("website");
11
9
 
12
10
  console.log({
13
11
  url: worker.url,
@@ -10,7 +10,6 @@ const kv = await KVNamespace("kv", {
10
10
  });
11
11
 
12
12
  export const worker = await Worker("worker", {
13
- name: `${app.name}-${app.stage}`,
14
13
  entrypoint: "src/index.ts",
15
14
  bindings: {
16
15
  KV: kv,
@@ -0,0 +1,85 @@
1
+ # Welcome to Next.js on Cloudflare!
2
+
3
+ [![Deployed with Alchemy](https://alchemy.run/alchemy-badge.svg)](https://alchemy.run)
4
+
5
+ A modern, production-ready template for building full-stack React applications using Next.js on Cloudflare Workers.
6
+
7
+ ## Features
8
+
9
+ - 🚀 Server-side rendering with React Server Components
10
+ - ⚡️ Hot Module Replacement (HMR)
11
+ - 📦 Asset bundling and optimization via OpenNext
12
+ - 🔄 Server Actions and API routes
13
+ - 🔒 TypeScript by default
14
+ - 🌐 Cloudflare Workers runtime
15
+ - 📖 [Next.js docs](https://nextjs.org/docs)
16
+
17
+ ## Getting Started
18
+
19
+ ### Installation
20
+
21
+ Install the dependencies:
22
+
23
+ ```bash
24
+ npm install
25
+ ```
26
+
27
+ ### Development
28
+
29
+ Start the development server with HMR:
30
+
31
+ ```bash
32
+ npm run dev
33
+ ```
34
+
35
+ Your application will be available at `http://localhost:3000`.
36
+
37
+ ## Building for Production
38
+
39
+ Create a production build:
40
+
41
+ ```bash
42
+ npm run build
43
+ ```
44
+
45
+ ## Deployment
46
+
47
+ Deploy to Cloudflare Workers:
48
+
49
+ ```bash
50
+ npm run deploy
51
+ ```
52
+
53
+ ## Preview
54
+
55
+ Preview the production build locally:
56
+
57
+ ```bash
58
+ npm run preview
59
+ ```
60
+
61
+ ## Destroy
62
+
63
+ Clean up all Cloudflare resources:
64
+
65
+ ```bash
66
+ npm run destroy
67
+ ```
68
+
69
+ ## Project Structure
70
+
71
+ - `src/app/` - Next.js App Router pages and components
72
+ - `public/` - Static assets
73
+ - `alchemy.run.ts` - Infrastructure configuration
74
+ - `next.config.ts` - Next.js configuration
75
+ - `open-next.config.ts` - OpenNext configuration for Cloudflare
76
+
77
+ ## Learn More
78
+
79
+ - [Next.js Documentation](https://nextjs.org/docs)
80
+ - [Alchemy Documentation](https://alchemy.run)
81
+ - [OpenNext for Cloudflare](https://opennext.js.org/cloudflare)
82
+
83
+ ---
84
+
85
+ Built with ❤️ using Next.js and Alchemy.
@@ -0,0 +1 @@
1
+ ALCHEMY_PASSWORD=change-me
@@ -0,0 +1 @@
1
+ ALCHEMY_PASSWORD=change-me
@@ -0,0 +1,46 @@
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.js
7
+ .yarn/install-state.gz
8
+
9
+ # testing
10
+ /coverage
11
+
12
+ # next.js
13
+ /.next/
14
+ /out/
15
+
16
+ # production
17
+ /build
18
+
19
+ # misc
20
+ .DS_Store
21
+ *.pem
22
+
23
+ # debug
24
+ npm-debug.log*
25
+ yarn-debug.log*
26
+ yarn-error.log*
27
+
28
+ # local env files
29
+ .env
30
+ .env.*
31
+ !.env.example
32
+
33
+ # vercel
34
+ .vercel
35
+
36
+ # typescript
37
+ *.tsbuildinfo
38
+ next-env.d.ts
39
+
40
+ # alchemy
41
+ .alchemy/
42
+ .wrangler/
43
+ wrangler.jsonc
44
+
45
+ # opennext
46
+ .open-next/
@@ -0,0 +1,16 @@
1
+ /// <reference types="@types/node" />
2
+
3
+ import alchemy from "alchemy";
4
+ import { Nextjs } from "alchemy/cloudflare";
5
+
6
+ const app = await alchemy("{projectName}");
7
+
8
+ export const website = await Nextjs("website", {
9
+ name: `${app.name}-${app.stage}-website`,
10
+ });
11
+
12
+ console.log({
13
+ url: website.url,
14
+ });
15
+
16
+ await app.finalize();
@@ -0,0 +1,16 @@
1
+ import { dirname } from "path";
2
+ import { fileURLToPath } from "url";
3
+ import { FlatCompat } from "@eslint/eslintrc";
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+
8
+ const compat = new FlatCompat({
9
+ baseDirectory: __dirname,
10
+ });
11
+
12
+ const eslintConfig = [
13
+ ...compat.extends("next/core-web-vitals", "next/typescript"),
14
+ ];
15
+
16
+ export default eslintConfig;
@@ -0,0 +1,5 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+
4
+ // NOTE: This file should not be edited
5
+ // see https://nextjs.org/docs/basic-features/typescript for more information.
@@ -0,0 +1,10 @@
1
+ import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
2
+ import type { NextConfig } from "next";
3
+
4
+ const nextConfig: NextConfig = {
5
+ /* config options here */
6
+ };
7
+
8
+ export default nextConfig;
9
+
10
+ initOpenNextCloudflareForDev();
@@ -0,0 +1,5 @@
1
+ import { defineCloudflareConfig } from "@opennextjs/cloudflare";
2
+
3
+ export default defineCloudflareConfig({
4
+ // Configure OpenNext for Cloudflare Workers
5
+ });
@@ -0,0 +1,31 @@
1
+ {
2
+ "type": "module",
3
+ "name": "@alchemy.run/nextjs-template",
4
+ "private": true,
5
+ "scripts": {
6
+ "build": "next build",
7
+ "deploy": "alchemy deploy",
8
+ "destroy": "alchemy destroy",
9
+ "dev": "alchemy dev",
10
+ "lint": "next lint",
11
+ "start": "next start"
12
+ },
13
+ "dependencies": {
14
+ "@opennextjs/cloudflare": "^1.6.5",
15
+ "next": "15.4.6",
16
+ "react": "19.1.0",
17
+ "react-dom": "19.1.0"
18
+ },
19
+ "devDependencies": {
20
+ "@types/node": "^24.3.0",
21
+ "@types/react": "^19",
22
+ "@types/react-dom": "^19",
23
+ "alchemy": "workspace:*",
24
+ "@cloudflare/workers-types": "^4.20250805.0",
25
+ "eslint": "^9",
26
+ "eslint-config-next": "15.4.6",
27
+ "sharp": "^0.34.3",
28
+ "typescript": "^5.8.3",
29
+ "wrangler": "^4.30.0"
30
+ }
31
+ }
@@ -0,0 +1,19 @@
1
+ import { NextRequest } from "next/server";
2
+
3
+ export async function GET(request: NextRequest) {
4
+ return Response.json({
5
+ message: "Hello from Next.js on Cloudflare Workers!",
6
+ timestamp: new Date().toISOString(),
7
+ url: request.url,
8
+ });
9
+ }
10
+
11
+ export async function POST(request: NextRequest) {
12
+ const body = await request.json();
13
+
14
+ return Response.json({
15
+ message: "POST received",
16
+ data: body,
17
+ timestamp: new Date().toISOString(),
18
+ });
19
+ }
@@ -0,0 +1,72 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ padding: 0;
4
+ margin: 0;
5
+ }
6
+
7
+ html,
8
+ body {
9
+ max-width: 100vw;
10
+ overflow-x: hidden;
11
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
12
+ line-height: 1.6;
13
+ color: #333;
14
+ }
15
+
16
+ .container {
17
+ max-width: 800px;
18
+ margin: 0 auto;
19
+ padding: 2rem;
20
+ }
21
+
22
+ h1 {
23
+ font-size: 2.5rem;
24
+ margin-bottom: 1rem;
25
+ color: #2563eb;
26
+ }
27
+
28
+ h2 {
29
+ font-size: 1.5rem;
30
+ margin: 2rem 0 1rem 0;
31
+ color: #1f2937;
32
+ }
33
+
34
+ p {
35
+ margin-bottom: 1rem;
36
+ }
37
+
38
+ a {
39
+ color: #2563eb;
40
+ text-decoration: underline;
41
+ }
42
+
43
+ a:hover {
44
+ text-decoration: none;
45
+ }
46
+
47
+ code {
48
+ background: #f3f4f6;
49
+ padding: 0.2rem 0.4rem;
50
+ border-radius: 0.25rem;
51
+ font-family: 'SF Mono', Consolas, monospace;
52
+ font-size: 0.875rem;
53
+ }
54
+
55
+ section {
56
+ margin: 2rem 0;
57
+ }
58
+
59
+ ul {
60
+ padding-left: 2rem;
61
+ }
62
+
63
+ li {
64
+ margin: 0.5rem 0;
65
+ }
66
+
67
+ footer {
68
+ margin-top: 3rem;
69
+ padding-top: 2rem;
70
+ border-top: 1px solid #e5e7eb;
71
+ color: #6b7280;
72
+ }
@@ -0,0 +1,19 @@
1
+ import type { Metadata } from "next";
2
+ import "./globals.css";
3
+
4
+ export const metadata: Metadata = {
5
+ title: "Next.js on Cloudflare Workers",
6
+ description: "Built with Alchemy",
7
+ };
8
+
9
+ export default function RootLayout({
10
+ children,
11
+ }: {
12
+ children: React.ReactNode;
13
+ }) {
14
+ return (
15
+ <html lang="en">
16
+ <body>{children}</body>
17
+ </html>
18
+ );
19
+ }
@@ -0,0 +1,55 @@
1
+ export default function Home() {
2
+ return (
3
+ <main className="container">
4
+ <h1>Welcome to Next.js on Cloudflare Workers!</h1>
5
+ <p>
6
+ This is a Next.js application deployed to Cloudflare Workers using{" "}
7
+ <a
8
+ href="https://alchemy.run"
9
+ target="_blank"
10
+ rel="noopener noreferrer"
11
+ >
12
+ Alchemy
13
+ </a>.
14
+ </p>
15
+
16
+ <section>
17
+ <h2>Features</h2>
18
+ <ul>
19
+ <li>🚀 Server-side rendering</li>
20
+ <li>⚡️ Hot Module Replacement</li>
21
+ <li>📦 Asset optimization</li>
22
+ <li>🔄 Server Actions</li>
23
+ <li>🔒 TypeScript</li>
24
+ <li>🌐 Edge runtime</li>
25
+ </ul>
26
+ </section>
27
+
28
+ <section>
29
+ <h2>Get Started</h2>
30
+ <p>Edit <code>src/app/page.tsx</code> and save to see your changes.</p>
31
+ </section>
32
+
33
+ <footer>
34
+ <p>
35
+ Learn more:{" "}
36
+ <a
37
+ href="https://nextjs.org/docs"
38
+ target="_blank"
39
+ rel="noopener noreferrer"
40
+ >
41
+ Next.js Docs
42
+ </a>{" "}
43
+ |{" "}
44
+ <a
45
+ href="https://alchemy.run"
46
+ target="_blank"
47
+ rel="noopener noreferrer"
48
+ >
49
+ Alchemy Docs
50
+ </a>
51
+ </p>
52
+ </footer>
53
+ </main>
54
+ );
55
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["dom", "dom.iterable", "es6"],
4
+ "allowJs": true,
5
+ "skipLibCheck": true,
6
+ "strict": true,
7
+ "noEmit": true,
8
+ "esModuleInterop": true,
9
+ "module": "esnext",
10
+ "moduleResolution": "bundler",
11
+ "resolveJsonModule": true,
12
+ "isolatedModules": true,
13
+ "jsx": "preserve",
14
+ "incremental": true,
15
+ "plugins": [
16
+ {
17
+ "name": "next"
18
+ }
19
+ ],
20
+ "paths": {
21
+ "@/*": ["./src/*"]
22
+ },
23
+ "types": ["@cloudflare/workers-types", "./types/env.d.ts"]
24
+ },
25
+ "include": [
26
+ "next-env.d.ts",
27
+ "**/*.ts",
28
+ "**/*.tsx",
29
+ ".next/types/**/*.ts",
30
+ "alchemy.run.ts"
31
+ ],
32
+ "exclude": ["node_modules"]
33
+ }
@@ -0,0 +1,14 @@
1
+ // Auto-generated Cloudflare binding types.
2
+ // @see https://alchemy.run/concepts/bindings/#type-safe-bindings
3
+
4
+ import type { website } from "./alchemy.run.ts";
5
+
6
+ declare global {
7
+ type CloudflareEnv = typeof website.Env;
8
+ }
9
+
10
+ declare module "cloudflare:workers" {
11
+ namespace Cloudflare {
12
+ export interface Env extends CloudflareEnv {}
13
+ }
14
+ }
@@ -5,9 +5,7 @@ import { Nuxt } from "alchemy/cloudflare";
5
5
 
6
6
  const app = await alchemy("{projectName}");
7
7
 
8
- export const worker = await Nuxt("website", {
9
- name: `${app.name}-${app.stage}-website`,
10
- });
8
+ export const worker = await Nuxt("website");
11
9
 
12
10
  console.log({
13
11
  url: worker.url,
@@ -5,9 +5,7 @@ import { ReactRouter } from "alchemy/cloudflare";
5
5
 
6
6
  const app = await alchemy("{projectName}");
7
7
 
8
- export const worker = await ReactRouter("website", {
9
- name: `${app.name}-${app.stage}-website`,
10
- });
8
+ export const worker = await ReactRouter("website");
11
9
 
12
10
  console.log({
13
11
  url: worker.url,
@@ -5,12 +5,10 @@ import { D1Database, DurableObjectNamespace, Redwood } from "alchemy/cloudflare"
5
5
  const app = await alchemy("{projectName}");
6
6
 
7
7
  const database = await D1Database("database", {
8
- name: `${app.name}-${app.stage}-database`,
9
8
  migrationsDir: "migrations",
10
9
  });
11
10
 
12
11
  export const worker = await Redwood("website", {
13
- name: `${app.name}-${app.stage}-website`,
14
12
  bindings: {
15
13
  AUTH_SECRET_KEY: alchemy.secret(process.env.AUTH_SECRET_KEY),
16
14
  DB: database,
@@ -5,9 +5,7 @@ import { SvelteKit } from "alchemy/cloudflare";
5
5
 
6
6
  const app = await alchemy("{projectName}");
7
7
 
8
- export const worker = await SvelteKit("website", {
9
- name: `${app.name}-${app.stage}-website`,
10
- });
8
+ export const worker = await SvelteKit("website");
11
9
 
12
10
  console.log({
13
11
  url: worker.url,
@@ -5,9 +5,7 @@ import { TanStackStart } from "alchemy/cloudflare";
5
5
 
6
6
  const app = await alchemy("{projectName}");
7
7
 
8
- export const worker = await TanStackStart("website", {
9
- name: `${app.name}-${app.stage}-website`,
10
- });
8
+ export const worker = await TanStackStart("website");
11
9
 
12
10
  console.log({
13
11
  url: worker.url,
@@ -6,7 +6,6 @@ import { Worker } from "alchemy/cloudflare";
6
6
  const app = await alchemy("{projectName}");
7
7
 
8
8
  export const worker = await Worker("worker", {
9
- name: `${app.name}-${app.stage}-website`,
10
9
  entrypoint: "src/worker.ts",
11
10
  });
12
11