@payai/x402-next-starter 0.1.5 → 1.0.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 (36) hide show
  1. package/NOTICE +1 -1
  2. package/package.json +6 -2
  3. package/template/.env-local +10 -1
  4. package/template/.env.example +4 -0
  5. package/template/.prettierignore +1 -0
  6. package/template/README.md +204 -54
  7. package/template/app/api/weather/route.ts +69 -0
  8. package/template/app/globals.css +16 -20
  9. package/template/app/layout.tsx +5 -15
  10. package/template/app/page.tsx +21 -8
  11. package/template/app/protected/page.tsx +40 -3
  12. package/template/eslint.config.js +1 -1
  13. package/template/next-env.d.ts +6 -0
  14. package/template/next.config.ts +1 -13
  15. package/template/package.json +18 -16
  16. package/template/postcss.config.mjs +1 -1
  17. package/template/proxy.ts +78 -0
  18. package/template/public/favicon.ico +0 -0
  19. package/template/public/site.webmanifest +7 -9
  20. package/template/public/x402-icon-black.png +0 -0
  21. package/template/public/x402-logo-dark.png +0 -0
  22. package/template/tsconfig.json +9 -2
  23. package/template/turbo.json +9 -0
  24. package/template/app/assets/x402_wordmark_dark.png +0 -0
  25. package/template/app/assets/x402_wordmark_dark.svg +0 -4
  26. package/template/app/assets/x402_wordmark_light.svg +0 -1
  27. package/template/app/favicon.ico +0 -0
  28. package/template/middleware.ts +0 -31
  29. package/template/package-lock.json +0 -8726
  30. package/template/public/apple-touch-icon.png +0 -0
  31. package/template/public/favicon-96x96.png +0 -0
  32. package/template/public/favicon.svg +0 -3
  33. package/template/public/web-app-manifest-192x192.png +0 -0
  34. package/template/public/web-app-manifest-512x512.png +0 -0
  35. package/template/tailwind.config.ts +0 -18
  36. package/template/types/svg.d.ts +0 -5
package/NOTICE CHANGED
@@ -1,2 +1,2 @@
1
1
  This package includes portions derived from coinbase/x402 (examples/typescript/fullstack/next), Apache-2.0,
2
- commit 8d27e85f571bb9de62064a00f2f7abc0a63f6dd0. See LICENSE and upstream LICENSE notices.
2
+ commit 6a99ba028b0d657122db8a06328cd432bdd8c7c0. See LICENSE and upstream LICENSE notices.
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@payai/x402-next-starter",
3
- "version": "0.1.5",
3
+ "version": "1.0.0",
4
4
  "private": false,
5
+ "publishConfig": {
6
+ "access": "public",
7
+ "registry": "https://registry.npmjs.org"
8
+ },
5
9
  "description": "Create an x402 Next.js app in less than 2 minutes!",
6
10
  "type": "module",
7
11
  "bin": {
@@ -34,6 +38,6 @@
34
38
  "url": "https://github.com/PayAINetwork/x402-next-starter"
35
39
  },
36
40
  "config": {
37
- "x402NextVersion": "0.6.5"
41
+ "x402NextVersion": "2.3.0"
38
42
  }
39
43
  }
@@ -1,3 +1,12 @@
1
+ EVM_ADDRESS=
2
+ SVM_ADDRESS=
3
+ FACILITATOR_URL=
4
+
5
+ CDP_CLIENT_KEY=
6
+ APP_NAME=Next x402 Demo
7
+ APP_LOGO=/x402-icon-blue.png
8
+
9
+
1
10
  NEXT_PUBLIC_FACILITATOR_URL=https://facilitator.payai.network
11
+
2
12
  NETWORK=solana-devnet
3
- RESOURCE_WALLET_ADDRESS=
@@ -0,0 +1,4 @@
1
+
2
+ NEXT_PUBLIC_FACILITATOR_URL=https://facilitator.payai.network
3
+
4
+ NETWORK=solana-devnet
@@ -3,6 +3,7 @@ dist/
3
3
  node_modules/
4
4
  coverage/
5
5
  .github/
6
+ .next/
6
7
  src/client
7
8
  **/**/*.json
8
9
  *.md
@@ -1,30 +1,36 @@
1
1
  # x402-next Example App
2
2
 
3
- This is a Next.js application that demonstrates how to use the `x402-next` middleware to implement paywall functionality in your Next.js routes.
3
+ Next.js application demonstrating how to protect routes with a paywall using the `@x402/next` middleware.
4
4
 
5
5
  ## Prerequisites
6
6
 
7
7
  - Node.js v20+ (install via [nvm](https://github.com/nvm-sh/nvm))
8
8
  - pnpm v10 (install via [pnpm.io/installation](https://pnpm.io/installation))
9
- - A valid Ethereum address for receiving payments
9
+ - Valid EVM and SVM addresses for receiving payments
10
+ - URL of a facilitator supporting the desired payment network, see [facilitator list](https://www.x402.org/ecosystem?category=facilitators)
10
11
 
11
12
  ## Setup
12
13
 
13
- 1. Copy `.env-local` to `.env` and add your Ethereum address to receive payments:
14
+ 1. Copy `.env-local` to `.env`:
14
15
 
15
16
  ```bash
16
17
  cp .env-local .env
17
18
  ```
18
19
 
20
+ and fill required environment variables:
21
+
22
+ - `FACILITATOR_URL` - Facilitator endpoint URL
23
+ - `EVM_ADDRESS` - Ethereum address to receive payments
24
+ - `SVM_ADDRESS` - Solana address to receive payments
25
+
19
26
  2. Install and build all packages from the typescript examples root:
20
27
  ```bash
21
28
  cd ../../
22
- pnpm install
23
- pnpm build
29
+ pnpm install && pnpm build
24
30
  cd fullstack/next
25
31
  ```
26
32
 
27
- 2. Install and start the Next.js example:
33
+ 3. Run the server:
28
34
  ```bash
29
35
  pnpm dev
30
36
  ```
@@ -34,94 +40,238 @@ pnpm dev
34
40
  The app includes protected routes that require payment to access:
35
41
 
36
42
  ### Protected Page Route
37
- The `/protected` route requires a payment of $0.01 to access. The route is protected using the x402-next middleware:
43
+
44
+ The `/protected` route is protected using `paymentProxy`. Page routes are protected using this approach:
38
45
 
39
46
  ```typescript
40
- // middleware.ts
41
- import { paymentMiddleware, Network, Resource } from "x402-next";
47
+ // proxy.ts
48
+ import { paymentProxy } from "@x402/next";
49
+ import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
50
+ import { registerExactEvmScheme } from "@x402/evm/exact/server";
51
+ import { registerExactSvmScheme } from "@x402/svm/exact/server";
52
+ import { createPaywall } from "@x402/paywall";
53
+ import { evmPaywall } from "@x402/paywall/evm";
54
+ import { svmPaywall } from "@x402/paywall/svm";
42
55
 
43
- const facilitatorUrl = process.env.NEXT_PUBLIC_FACILITATOR_URL as Resource;
44
- const payTo = process.env.RESOURCE_WALLET_ADDRESS as Address;
45
- const network = process.env.NETWORK as Network;
56
+ const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
57
+ const server = new x402ResourceServer(facilitatorClient);
46
58
 
47
- export const middleware = paymentMiddleware(
48
- payTo,
59
+ // Register schemes
60
+ registerExactEvmScheme(server);
61
+ registerExactSvmScheme(server);
62
+
63
+ // Build paywall using builder pattern
64
+ const paywall = createPaywall()
65
+ .withNetwork(evmPaywall)
66
+ .withNetwork(svmPaywall)
67
+ .withConfig({
68
+ appName: "Next x402 Demo",
69
+ appLogo: "/x402-icon-blue.png",
70
+ testnet: true,
71
+ })
72
+ .build();
73
+
74
+ export const proxy = paymentProxy(
49
75
  {
50
76
  "/protected": {
51
- price: "$0.01",
52
- network,
53
- config: {
54
- description: "Access to protected content",
55
- },
77
+ accepts: [
78
+ {
79
+ scheme: "exact",
80
+ price: "$0.001",
81
+ network: "eip155:84532",
82
+ payTo: evmAddress,
83
+ },
84
+ {
85
+ scheme: "exact",
86
+ price: "$0.001",
87
+ network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
88
+ payTo: svmAddress,
89
+ },
90
+ ],
91
+ description: "Premium music: x402 Remix",
92
+ mimeType: "text/html",
56
93
  },
57
94
  },
58
- {
59
- url: facilitatorUrl,
60
- },
95
+ server,
96
+ undefined, // paywallConfig (using custom paywall instead)
97
+ paywall, // custom paywall provider
61
98
  );
62
99
 
63
- // Configure which paths the middleware should run on
64
100
  export const config = {
65
101
  matcher: ["/protected/:path*"],
66
102
  };
67
103
  ```
68
104
 
105
+ ### Weather API Route (using withX402)
106
+
107
+ The `/api/weather` route demonstrates the `withX402` wrapper for individual API routes:
108
+
109
+ ```typescript
110
+ // app/api/weather/route.ts
111
+ import { NextRequest, NextResponse } from "next/server";
112
+ import { withX402 } from "@x402/next";
113
+ import { server, paywall, evmAddress, svmAddress } from "../../../proxy";
114
+
115
+ const handler = async (_: NextRequest) => {
116
+ return NextResponse.json({
117
+ report: {
118
+ weather: "sunny",
119
+ temperature: 72,
120
+ },
121
+ });
122
+ };
123
+
124
+ export const GET = withX402(
125
+ handler,
126
+ {
127
+ accepts: [
128
+ {
129
+ scheme: "exact",
130
+ price: "$0.001",
131
+ network: "eip155:84532",
132
+ payTo: evmAddress,
133
+ },
134
+ {
135
+ scheme: "exact",
136
+ price: "$0.001",
137
+ network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
138
+ payTo: svmAddress,
139
+ },
140
+ ],
141
+ description: "Access to weather API",
142
+ mimeType: "application/json",
143
+ },
144
+ server,
145
+ undefined, // paywallConfig (using custom paywall from proxy.ts)
146
+ paywall,
147
+ );
148
+ ```
149
+
69
150
  ## Response Format
70
151
 
71
152
  ### Payment Required (402)
153
+
154
+ ```
155
+ HTTP/1.1 402 Payment Required
156
+ Content-Type: application/json; charset=utf-8
157
+ PAYMENT-REQUIRED: <base64-encoded JSON>
158
+
159
+ {}
160
+ ```
161
+
162
+ The `PAYMENT-REQUIRED` header contains base64-encoded JSON with the payment requirements.
163
+ Note: `amount` is in atomic units (e.g., 1000 = 0.001 USDC, since USDC has 6 decimals):
164
+
72
165
  ```json
73
166
  {
74
- "error": "X-PAYMENT header is required",
75
- "paymentRequirements": {
76
- "scheme": "exact",
77
- "network": "base",
78
- "maxAmountRequired": "1000",
79
- "resource": "http://localhost:3000/protected",
80
- "description": "Access to protected content",
81
- "mimeType": "",
82
- "payTo": "0xYourAddress",
83
- "maxTimeoutSeconds": 60,
84
- "asset": "0x...",
85
- "outputSchema": null,
86
- "extra": null
87
- }
167
+ "x402Version": 2,
168
+ "error": "Payment required",
169
+ "resource": {
170
+ "url": "http://localhost:3000/api/weather",
171
+ "description": "Access to weather API",
172
+ "mimeType": "application/json"
173
+ },
174
+ "accepts": [
175
+ {
176
+ "scheme": "exact",
177
+ "network": "eip155:84532",
178
+ "amount": "1000",
179
+ "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
180
+ "payTo": "0x...",
181
+ "maxTimeoutSeconds": 300,
182
+ "extra": {
183
+ "name": "USDC",
184
+ "version": "2",
185
+ "resourceUrl": "http://localhost:4021/weather"
186
+ }
187
+ }
188
+ ]
88
189
  }
89
190
  ```
90
191
 
91
192
  ### Successful Response
92
- ```ts
93
- // Headers
193
+
194
+ ```
195
+ HTTP/1.1 200 OK
196
+ Content-Type: application/json; charset=utf-8
197
+ PAYMENT-RESPONSE: <base64-encoded JSON>
198
+
199
+ {"report":{"weather":"sunny","temperature":72}}
200
+ ```
201
+
202
+ The `PAYMENT-RESPONSE` header contains base64-encoded JSON with the settlement details:
203
+
204
+ ```json
94
205
  {
95
- "X-PAYMENT-RESPONSE": "..." // Encoded response object
206
+ "success": true,
207
+ "transaction": "0x...",
208
+ "network": "eip155:84532",
209
+ "payer": "0x...",
210
+ "requirements": {
211
+ "scheme": "exact",
212
+ "network": "eip155:84532",
213
+ "amount": "1000",
214
+ "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
215
+ "payTo": "0x...",
216
+ "maxTimeoutSeconds": 300,
217
+ "extra": {
218
+ "name": "USDC",
219
+ "version": "2",
220
+ "resourceUrl": "http://localhost:4021/weather"
221
+ }
222
+ }
96
223
  }
97
224
  ```
98
225
 
226
+ ## paymentProxy vs withX402
227
+
228
+ The `paymentProxy` function is used to protect page routes. It can also protect API routes, however this will charge clients for failed API responses.
229
+
230
+ The `withX402` function wraps API route handlers. This is the recommended approach to protect API routes as it guarantees payment settlement only AFTER successful API responses (status < 400).
231
+
232
+ | Approach | Use Case |
233
+ |----------|----------|
234
+ | `paymentProxy` | Protecting page routes or multiple routes with a single configuration |
235
+ | `withX402` | Protecting individual API routes where you need precise control over settlement timing |
236
+
99
237
  ## Extending the Example
100
238
 
101
- To add more protected routes, update the middleware configuration:
239
+ To add more protected routes, update the proxy configuration:
102
240
 
103
241
  ```typescript
104
- export const middleware = paymentMiddleware(
105
- payTo,
242
+ export const proxy = paymentProxy(
106
243
  {
107
244
  "/protected": {
108
- price: "$0.01",
109
- network,
110
- config: {
111
- description: "Access to protected content",
245
+ accepts: {
246
+ scheme: "exact",
247
+ price: "$0.001",
248
+ network: "eip155:84532",
249
+ payTo: evmAddress,
112
250
  },
251
+ description: "Access to protected content",
113
252
  },
114
- "/api/premium": {
115
- price: "$0.10",
116
- network,
117
- config: {
118
- description: "Premium API access",
253
+ "/premium": {
254
+ accepts: {
255
+ scheme: "exact",
256
+ price: "$0.10",
257
+ network: "eip155:84532",
258
+ payTo: evmAddress,
119
259
  },
260
+ description: "Premium content access",
120
261
  },
121
- }
262
+ },
263
+ server,
264
+ undefined,
265
+ paywall,
122
266
  );
123
267
 
124
268
  export const config = {
125
- matcher: ["/protected/:path*", "/api/premium/:path*"],
269
+ matcher: ["/protected/:path*", "/premium/:path*"],
126
270
  };
127
271
  ```
272
+
273
+ **Network identifiers** use [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md) format, for example:
274
+ - `eip155:84532` — Base Sepolia
275
+ - `eip155:8453` — Base Mainnet
276
+ - `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` — Solana Devnet
277
+ - `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` — Solana Mainnet
@@ -0,0 +1,69 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { withX402 } from "@x402/next";
3
+ import { declareDiscoveryExtension } from "@x402/extensions/bazaar";
4
+ import { server, paywall, evmAddress, svmAddress } from "../../../proxy";
5
+
6
+ /**
7
+ * Weather API endpoint handler
8
+ *
9
+ * This handler returns weather data after payment verification.
10
+ * Payment is only settled after a successful response (status < 400).
11
+ *
12
+ * @param _ - Incoming Next.js request
13
+ * @returns JSON response with weather data
14
+ */
15
+ const handler = async (_: NextRequest) => {
16
+ return NextResponse.json(
17
+ {
18
+ report: {
19
+ weather: "sunny",
20
+ temperature: 72,
21
+ },
22
+ },
23
+ { status: 200 },
24
+ );
25
+ };
26
+
27
+ /**
28
+ * Protected weather API endpoint using withX402 wrapper
29
+ *
30
+ * This demonstrates the v2 withX402 wrapper for individual API routes.
31
+ * Unlike middleware, withX402 guarantees payment settlement only after
32
+ * the handler returns a successful response (status < 400).
33
+ */
34
+ export const GET = withX402(
35
+ handler,
36
+ {
37
+ accepts: [
38
+ {
39
+ scheme: "exact",
40
+ price: "$0.001",
41
+ network: "eip155:84532", // base-sepolia
42
+ payTo: evmAddress,
43
+ },
44
+ {
45
+ scheme: "exact",
46
+ price: "$0.001",
47
+ network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", // solana devnet
48
+ payTo: svmAddress,
49
+ },
50
+ ],
51
+ description: "Access to weather API",
52
+ mimeType: "application/json",
53
+ extensions: {
54
+ ...declareDiscoveryExtension({
55
+ output: {
56
+ example: {
57
+ report: {
58
+ weather: "sunny",
59
+ temperature: 72,
60
+ },
61
+ },
62
+ },
63
+ }),
64
+ },
65
+ },
66
+ server,
67
+ undefined, // paywallConfig (using custom paywall from proxy.ts)
68
+ paywall,
69
+ );
@@ -1,33 +1,29 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
1
+ @import "tailwindcss";
4
2
 
5
- :root {
6
- --background: #ffffff;
7
- --foreground: #171717;
3
+ @theme {
4
+ --color-background: #ffffff;
5
+ --color-foreground: #171717;
8
6
  }
9
7
 
10
8
  @media (prefers-color-scheme: dark) {
11
- :root {
12
- --background: #1a1a2e;
13
- --foreground: #ededed;
9
+ @theme {
10
+ --color-background: #1a1a2e;
11
+ --color-foreground: #ededed;
14
12
  }
15
13
  }
16
14
 
17
15
  body {
18
- color: var(--foreground);
19
- background: var(--background);
16
+ color: var(--color-foreground);
17
+ background: var(--color-background);
20
18
  font-family: Arial, Helvetica, sans-serif;
21
19
  }
22
20
 
23
- @layer utilities {
24
- .full-bleed {
25
- width: 100vw;
26
- margin-left: calc(50% - 50vw);
27
- }
21
+ @utility full-bleed {
22
+ width: 100vw;
23
+ margin-left: calc(50% - 50vw);
24
+ }
28
25
 
29
- .unset-full-bleed {
30
- width: unset;
31
- margin-left: unset;
32
- }
26
+ @utility unset-full-bleed {
27
+ width: unset;
28
+ margin-left: unset;
33
29
  }
@@ -13,8 +13,8 @@ const geistMono = Geist_Mono({
13
13
  });
14
14
 
15
15
  export const metadata: Metadata = {
16
- title: 'x402.org',
17
- description: 'A chain-agnostic protocol for web payments',
16
+ title: "x402 Next.js Demo",
17
+ description: "A chain-agnostic protocol for web payments",
18
18
  };
19
19
 
20
20
  export default function RootLayout({
@@ -25,19 +25,8 @@ export default function RootLayout({
25
25
  return (
26
26
  <html lang="en">
27
27
  <head>
28
- <link rel="icon" href="/favicon.ico" sizes="any" />
29
- <link
30
- rel="icon"
31
- type="image/png"
32
- href="/favicon-96x96.png"
33
- sizes="96x96"
34
- />
35
- <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
36
- <link
37
- rel="apple-touch-icon"
38
- sizes="180x180"
39
- href="/apple-touch-icon.png"
40
- />
28
+ <link rel="icon" type="image/png" href="/x402-icon-black.png" />
29
+ <link rel="apple-touch-icon" href="/x402-icon-black.png" />
41
30
  <meta name="apple-mobile-web-app-title" content="x402" />
42
31
  <link rel="manifest" href="/site.webmanifest" />
43
32
  </head>
@@ -49,3 +38,4 @@ export default function RootLayout({
49
38
  </html>
50
39
  );
51
40
  }
41
+
@@ -1,5 +1,5 @@
1
- import Link from 'next/link';
2
- import WordmarkCondensed from './assets/x402_wordmark_light.svg';
1
+ import Link from "next/link";
2
+ import Image from "next/image";
3
3
 
4
4
  export default function Home() {
5
5
  return (
@@ -8,8 +8,14 @@ export default function Home() {
8
8
  {/* Hero Section */}
9
9
  <section className="max-w-6xl mx-auto px-4 py-20 lg:py-28">
10
10
  <div className="text-center">
11
- <div className="w-64 mb-6 mx-auto">
12
- <WordmarkCondensed className="mx-auto" />
11
+ <div className="mb-6">
12
+ <Image
13
+ src="/x402-logo-dark.png"
14
+ alt="x402 logo"
15
+ width={320}
16
+ height={160}
17
+ className="mx-auto"
18
+ />
13
19
  </div>
14
20
  <p className="text-xl text-gray-600 mb-8 font-mono">
15
21
  Fullstack demo powered by Next.js
@@ -19,14 +25,20 @@ export default function Home() {
19
25
  href="/protected"
20
26
  className="px-6 py-3 bg-blue-600 hover:bg-blue-700 rounded-lg font-mono transition-colors text-white"
21
27
  >
22
- Live demo
28
+ Protected page
29
+ </Link>
30
+ <Link
31
+ href="/api/weather"
32
+ className="px-6 py-3 bg-blue-600 hover:bg-blue-700 rounded-lg font-mono transition-colors text-white"
33
+ >
34
+ Protected API
23
35
  </Link>
24
36
  </div>
25
37
  </div>
26
38
  </section>
27
39
  </div>
28
40
  <footer className="py-8 text-center text-sm text-gray-500">
29
- By using this site, you agree to be bound by the{' '}
41
+ By using this site, you agree to be bound by the{" "}
30
42
  <a
31
43
  href="https://www.coinbase.com/legal/developer-platform/terms-of-service"
32
44
  target="_blank"
@@ -34,8 +46,8 @@ export default function Home() {
34
46
  className="text-blue-500"
35
47
  >
36
48
  CDP Terms of Service
37
- </a>{' '}
38
- and{' '}
49
+ </a>{" "}
50
+ and{" "}
39
51
  <a
40
52
  href="https://www.coinbase.com/legal/privacy"
41
53
  target="_blank"
@@ -49,3 +61,4 @@ export default function Home() {
49
61
  </div>
50
62
  );
51
63
  }
64
+
@@ -6,11 +6,48 @@ export default function ProtectedPage() {
6
6
  <p className="text-lg">
7
7
  Your payment was successful! Enjoy this banger song.
8
8
  </p>
9
- <iframe width="100%" height="300" scrolling="no" frameBorder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/2044190296&color=%23ff5500&auto_play=true&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"></iframe>
10
- <div style={{ fontSize: '10px', color: '#cccccc', lineBreak: 'anywhere', wordBreak: 'normal', overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis', fontFamily: 'Interstate,Lucida Grande,Lucida Sans Unicode,Lucida Sans,Garuda,Verdana,Tahoma,sans-serif', fontWeight: '100' }}>
11
- <a href="https://soundcloud.com/dan-kim-675678711" title="danXkim" target="_blank" style={{ color: '#cccccc', textDecoration: 'none' }}>danXkim</a> · <a href="https://soundcloud.com/dan-kim-675678711/x402" title="x402 (DJ Reppel Remix)" target="_blank" style={{ color: '#cccccc', textDecoration: 'none' }}>x402 (DJ Reppel Remix)</a>
9
+ <iframe
10
+ width="100%"
11
+ height="300"
12
+ scrolling="no"
13
+ frameBorder="no"
14
+ allow="autoplay"
15
+ src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/2044190296&color=%23ff5500&auto_play=true&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"
16
+ ></iframe>
17
+ <div
18
+ style={{
19
+ fontSize: "10px",
20
+ color: "#cccccc",
21
+ lineBreak: "anywhere",
22
+ wordBreak: "normal",
23
+ overflow: "hidden",
24
+ whiteSpace: "nowrap",
25
+ textOverflow: "ellipsis",
26
+ fontFamily:
27
+ "Interstate,Lucida Grande,Lucida Sans Unicode,Lucida Sans,Garuda,Verdana,Tahoma,sans-serif",
28
+ fontWeight: "100",
29
+ }}
30
+ >
31
+ <a
32
+ href="https://soundcloud.com/dan-kim-675678711"
33
+ title="danXkim"
34
+ target="_blank"
35
+ style={{ color: "#cccccc", textDecoration: "none" }}
36
+ >
37
+ danXkim
38
+ </a>{" "}
39
+ ·{" "}
40
+ <a
41
+ href="https://soundcloud.com/dan-kim-675678711/x402"
42
+ title="x402 (DJ Reppel Remix)"
43
+ target="_blank"
44
+ style={{ color: "#cccccc", textDecoration: "none" }}
45
+ >
46
+ x402 (DJ Reppel Remix)
47
+ </a>
12
48
  </div>
13
49
  </div>
14
50
  </div>
15
51
  );
16
52
  }
53
+
@@ -7,7 +7,7 @@ import importPlugin from "eslint-plugin-import";
7
7
 
8
8
  export default [
9
9
  {
10
- ignores: ["dist/**", "node_modules/**", ".next/**"],
10
+ ignores: ["dist/**", "node_modules/**", ".next/**", "next-env.d.ts"],
11
11
  },
12
12
  {
13
13
  files: ["**/*.ts"],
@@ -0,0 +1,6 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+ import "./.next/types/routes.d.ts";
4
+
5
+ // NOTE: This file should not be edited
6
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.