alchemy 0.64.0 → 0.65.1
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.
- package/bin/alchemy.js +7570 -5773
- package/bin/commands/init.ts +130 -9
- package/bin/constants.ts +4 -0
- package/bin/services/cdp-manager/cdp-proxy.ts +69 -0
- package/bin/services/cdp-manager/server.ts +173 -0
- package/bin/services/execute-alchemy.ts +83 -4
- package/bin/types.ts +1 -0
- package/lib/cloudflare/bundle/plugin-node-compat.js +2 -2
- package/lib/cloudflare/bundle/plugin-node-compat.js.map +1 -1
- package/lib/cloudflare/bundle/plugin-wasm.d.ts.map +1 -1
- package/lib/cloudflare/bundle/plugin-wasm.js +20 -11
- package/lib/cloudflare/bundle/plugin-wasm.js.map +1 -1
- package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
- package/lib/cloudflare/compatibility-date.gen.js +1 -1
- package/lib/cloudflare/hyperdrive.d.ts +7 -2
- package/lib/cloudflare/hyperdrive.d.ts.map +1 -1
- package/lib/cloudflare/hyperdrive.js +39 -57
- package/lib/cloudflare/hyperdrive.js.map +1 -1
- package/lib/cloudflare/index.d.ts +1 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +1 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/next.d.ts +13 -0
- package/lib/cloudflare/next.d.ts.map +1 -0
- package/lib/cloudflare/next.js +91 -0
- package/lib/cloudflare/next.js.map +1 -0
- package/lib/cloudflare/worker-assets.d.ts +3 -3
- package/lib/cloudflare/worker-assets.d.ts.map +1 -1
- package/lib/cloudflare/worker-assets.js +22 -47
- package/lib/cloudflare/worker-assets.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +2 -2
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +4 -1
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/planetscale/branch.d.ts.map +1 -1
- package/lib/planetscale/branch.js +36 -25
- package/lib/planetscale/branch.js.map +1 -1
- package/lib/planetscale/database.d.ts +23 -6
- package/lib/planetscale/database.d.ts.map +1 -1
- package/lib/planetscale/database.js +11 -5
- package/lib/planetscale/database.js.map +1 -1
- package/lib/planetscale/index.d.ts +1 -0
- package/lib/planetscale/index.d.ts.map +1 -1
- package/lib/planetscale/index.js +1 -0
- package/lib/planetscale/index.js.map +1 -1
- package/lib/planetscale/password.d.ts +3 -1
- package/lib/planetscale/password.d.ts.map +1 -1
- package/lib/planetscale/password.js +3 -1
- package/lib/planetscale/password.js.map +1 -1
- package/lib/planetscale/role.d.ts +126 -0
- package/lib/planetscale/role.d.ts.map +1 -0
- package/lib/planetscale/role.js +144 -0
- package/lib/planetscale/role.js.map +1 -0
- package/lib/planetscale/utils.d.ts +17 -3
- package/lib/planetscale/utils.d.ts.map +1 -1
- package/lib/planetscale/utils.js +110 -31
- package/lib/planetscale/utils.js.map +1 -1
- package/lib/secret.d.ts +0 -3
- package/lib/secret.d.ts.map +1 -1
- package/lib/secret.js +0 -2
- package/lib/secret.js.map +1 -1
- package/lib/state/cloudflare-state-store.js +3 -3
- package/lib/state/cloudflare-state-store.js.map +1 -1
- package/lib/test/bun.d.ts +2 -2
- package/lib/test/bun.d.ts.map +1 -1
- package/lib/test/vitest.d.ts +2 -2
- package/lib/test/vitest.d.ts.map +1 -1
- package/lib/test/vitest.js +4 -4
- package/lib/test/vitest.js.map +1 -1
- package/lib/util/content-type.js +2 -2
- package/lib/util/content-type.js.map +1 -1
- package/lib/util/dedent.d.ts.map +1 -1
- package/lib/util/dedent.js +30 -18
- package/lib/util/dedent.js.map +1 -1
- package/lib/util/memoize.d.ts +1 -1
- package/lib/util/memoize.d.ts.map +1 -1
- package/lib/util/memoize.js +13 -3
- package/lib/util/memoize.js.map +1 -1
- package/package.json +5 -1
- package/src/cloudflare/bundle/plugin-node-compat.ts +2 -2
- package/src/cloudflare/bundle/plugin-wasm.ts +34 -14
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/hyperdrive.ts +136 -95
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/next.ts +123 -0
- package/src/cloudflare/worker-assets.ts +43 -105
- package/src/cloudflare/wrangler.json.ts +6 -3
- package/src/planetscale/branch.ts +52 -27
- package/src/planetscale/database.ts +75 -46
- package/src/planetscale/index.ts +1 -0
- package/src/planetscale/password.ts +3 -1
- package/src/planetscale/role.ts +278 -0
- package/src/planetscale/utils.ts +172 -30
- package/src/secret.ts +0 -4
- package/src/state/cloudflare-state-store.ts +4 -4
- package/src/test/bun.ts +2 -2
- package/src/test/vitest.ts +6 -6
- package/src/util/content-type.ts +2 -2
- package/src/util/dedent.ts +33 -21
- package/src/util/memoize.ts +15 -3
- package/templates/nextjs/README.md +85 -0
- package/templates/nextjs/_env +1 -0
- package/templates/nextjs/_env.example +1 -0
- package/templates/nextjs/_gitignore +46 -0
- package/templates/nextjs/alchemy.run.ts +16 -0
- package/templates/nextjs/eslint.config.mjs +16 -0
- package/templates/nextjs/next-env.d.ts +5 -0
- package/templates/nextjs/next.config.ts +10 -0
- package/templates/nextjs/open-next.config.ts +5 -0
- package/templates/nextjs/package.json +31 -0
- package/templates/nextjs/src/app/api/hello/route.ts +19 -0
- package/templates/nextjs/src/app/favicon.ico +0 -0
- package/templates/nextjs/src/app/globals.css +72 -0
- package/templates/nextjs/src/app/layout.tsx +19 -0
- package/templates/nextjs/src/app/page.tsx +55 -0
- package/templates/nextjs/tsconfig.json +33 -0
- package/templates/nextjs/types/env.d.ts +14 -0
package/src/util/dedent.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import assert from "node:assert";
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Tagged template literal for removing indentation from a block of text.
|
|
5
3
|
*
|
|
@@ -10,34 +8,48 @@ export function dedent(strings: TemplateStringsArray, ...values: unknown[]) {
|
|
|
10
8
|
const raw = String.raw({ raw: strings }, ...values);
|
|
11
9
|
// Split the string by lines
|
|
12
10
|
let lines = raw.split("\n");
|
|
13
|
-
assert(lines.length > 0);
|
|
14
11
|
|
|
15
|
-
//
|
|
16
|
-
|
|
12
|
+
// Remove leading blank lines
|
|
13
|
+
while (lines.length > 0 && lines[0].trim() === "") {
|
|
14
|
+
lines = lines.slice(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Remove trailing blank lines
|
|
18
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
|
|
17
19
|
lines = lines.slice(0, lines.length - 1);
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
// If no content lines remain, return empty string
|
|
23
|
+
if (lines.length === 0) {
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Find the minimum-length indent from content lines (non-blank lines)
|
|
24
28
|
let minIndentLength = Number.POSITIVE_INFINITY;
|
|
25
|
-
for (const line of lines
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
+
}
|
|
30
35
|
}
|
|
31
36
|
}
|
|
32
37
|
|
|
33
|
-
// If
|
|
34
|
-
if (
|
|
35
|
-
|
|
38
|
+
// If no indentation found, return lines as-is
|
|
39
|
+
if (minIndentLength === Number.POSITIVE_INFINITY) {
|
|
40
|
+
return lines.join("\n");
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
// Remove indent from all lines,
|
|
39
|
-
lines = lines.map((line) =>
|
|
40
|
-
line.
|
|
41
|
-
|
|
43
|
+
// Remove indent from all lines, preserving blank lines
|
|
44
|
+
lines = lines.map((line) => {
|
|
45
|
+
if (line.trim() === "") {
|
|
46
|
+
return line; // Preserve blank lines
|
|
47
|
+
}
|
|
48
|
+
return line.startsWith(" ".repeat(minIndentLength)) ||
|
|
49
|
+
line.startsWith("\t".repeat(minIndentLength))
|
|
50
|
+
? line.substring(minIndentLength)
|
|
51
|
+
: line;
|
|
52
|
+
});
|
|
53
|
+
|
|
42
54
|
return lines.join("\n");
|
|
43
55
|
}
|
package/src/util/memoize.ts
CHANGED
|
@@ -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
|
|
@@ -24,6 +25,17 @@ export function memoize<F extends (...args: any[]) => Promise<any>>(
|
|
|
24
25
|
};
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
export
|
|
28
|
-
return createHash("sha256")
|
|
29
|
-
|
|
28
|
+
export function defaultKeyFn(...args: any[]): string {
|
|
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");
|
|
41
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Welcome to Next.js on Cloudflare!
|
|
2
|
+
|
|
3
|
+
[](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,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
|
+
}
|
|
Binary file
|
|
@@ -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
|
+
}
|