@coinbase/create-cdp-app 0.0.0 → 0.0.6

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/README.md ADDED
@@ -0,0 +1,51 @@
1
+ This package contains the `@coinbase/create-cdp-app` CLI command, which allows developers to quickly bootstrap a React App using the CDP Web SDK.
2
+
3
+ ## Quickstart
4
+
5
+ This guide will help you get started with `@coinbase/create-cdp-app`. You'll learn how to create a new CDP-enabled React application with just a few commands.
6
+
7
+ ### Prerequisites
8
+
9
+ Before you begin, make sure you have one of the following package managers installed:
10
+ - [pnpm](https://pnpm.io/) (recommended)
11
+ - [npm](https://www.npmjs.com/)
12
+ - [yarn](https://yarnpkg.com/)
13
+
14
+ Gather your project ID and whitelist your app from the CDP Portal:
15
+ 1. Sign in or create an account on the [CDP Portal](https://portal.cdp.coinbase.com)
16
+ 2. Copy your Project ID from the dashboard
17
+ 3. Go to the [Embedded Wallets CORS settings](https://portal.cdp.coinbase.com/products/embedded-wallets/cors)
18
+ 4. Click add origin and whitelist `http://localhost:3000` (or wherever your app will run)
19
+
20
+ ### Create Your First CDP App
21
+
22
+ You can create a new CDP app using any of these methods:
23
+
24
+ ```bash
25
+ # Using pnpm
26
+ pnpm create @coinbase/cdp-app
27
+
28
+ # Using npm
29
+ npm create @coinbase/cdp-app@latest
30
+
31
+ # Using yarn
32
+ yarn create @coinbase/cdp-app
33
+ ```
34
+
35
+ The CLI will guide you through the setup process:
36
+ 1. Enter your project name (defaults to "cdp-app")
37
+ 2. Select a template (currently supports "React Components")
38
+ 3. Enter your CDP Project ID
39
+ 4. Confirm you have whitelisted your app domain on the CDP Portal
40
+ 5. Confirm directory overwrite if needed
41
+
42
+ ## Available Templates
43
+
44
+ Currently, `create-cdp-app` offers the following template:
45
+
46
+ - **React Components** (`react-components`): A full-featured React application template that includes:
47
+ - Vite for fast development and building
48
+ - TypeScript for type safety
49
+ - CDP React components for authentication
50
+ - Example transaction components
51
+ - Base Sepolia integration
package/dist/index.js ADDED
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { reset, blue, red, green } from "kolorist";
6
+ import prompts from "prompts";
7
+ function prepareProjectDirectory(targetDir, shouldOverwrite) {
8
+ const root = path.join(process.cwd(), targetDir);
9
+ if (!fs.existsSync(root)) {
10
+ fs.mkdirSync(root, { recursive: true });
11
+ }
12
+ return root;
13
+ }
14
+ function customizePackageJson(templateDir, projectName) {
15
+ const packageJsonPath = path.join(templateDir, "package.json");
16
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
17
+ packageJson.name = projectName;
18
+ return JSON.stringify(packageJson, null, 2) + "\n";
19
+ }
20
+ function customizeEnv(templateDir, projectId) {
21
+ const exampleEnvPath = path.join(templateDir, "env.example");
22
+ const exampleEnv = fs.readFileSync(exampleEnvPath, "utf-8");
23
+ const envContent = exampleEnv.replace(
24
+ /VITE_CDP_PROJECT_ID=.*(\r?\n|$)/,
25
+ `VITE_CDP_PROJECT_ID=${projectId}
26
+ `
27
+ );
28
+ return envContent;
29
+ }
30
+ function copyFile(filePath, destPath) {
31
+ const stat = fs.statSync(filePath);
32
+ if (stat.isDirectory()) {
33
+ copyDir(filePath, destPath);
34
+ } else {
35
+ fs.copyFileSync(filePath, destPath);
36
+ }
37
+ }
38
+ function copyDir(srcDir, destDir) {
39
+ fs.mkdirSync(destDir, { recursive: true });
40
+ for (const file of fs.readdirSync(srcDir)) {
41
+ const srcFile = path.resolve(srcDir, file);
42
+ const destFile = path.resolve(destDir, file);
43
+ copyFile(srcFile, destFile);
44
+ }
45
+ }
46
+ function isDirEmpty(dirPath) {
47
+ const files = fs.readdirSync(dirPath);
48
+ return files.length === 0 || files.length === 1 && files[0] === ".git";
49
+ }
50
+ function detectPackageManager() {
51
+ const userAgent = process.env.npm_config_user_agent;
52
+ if (userAgent) {
53
+ if (userAgent.startsWith("yarn")) return "yarn";
54
+ if (userAgent.startsWith("pnpm")) return "pnpm";
55
+ if (userAgent.startsWith("npm")) return "npm";
56
+ }
57
+ return "npm";
58
+ }
59
+ const TEMPLATES = [
60
+ {
61
+ name: "react-components",
62
+ display: "React Components",
63
+ color: blue
64
+ }
65
+ ];
66
+ const defaultTargetDir = "cdp-app";
67
+ const fileRenames = {
68
+ _gitignore: ".gitignore"
69
+ };
70
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
71
+ async function init() {
72
+ const { projectName, template, targetDirectory, projectId } = await getProjectDetails();
73
+ console.log(`
74
+ Scaffolding project in ${targetDirectory}...`);
75
+ const root = prepareProjectDirectory(targetDirectory);
76
+ const templateDir = path.resolve(fileURLToPath(import.meta.url), "../..", `template-${template}`);
77
+ copyTemplateFiles(templateDir, root, projectName, projectId);
78
+ printNextSteps(root);
79
+ }
80
+ async function getProjectDetails() {
81
+ let targetDir = process.argv[2];
82
+ const defaultProjectName = targetDir ?? defaultTargetDir;
83
+ try {
84
+ const result = await prompts(
85
+ [
86
+ {
87
+ type: targetDir ? null : "text",
88
+ name: "projectName",
89
+ message: reset("Project name:"),
90
+ initial: defaultProjectName,
91
+ onState: (state) => {
92
+ targetDir = String(state.value).trim() || defaultProjectName;
93
+ }
94
+ },
95
+ {
96
+ type: "select",
97
+ name: "template",
98
+ message: reset("Select a template:"),
99
+ initial: 0,
100
+ choices: TEMPLATES.map((template) => ({
101
+ title: template.color(template.display || template.name),
102
+ value: template.name
103
+ }))
104
+ },
105
+ {
106
+ type: "text",
107
+ name: "projectId",
108
+ message: reset(
109
+ "CDP Project ID (Find your project ID at https://portal.cdp.coinbase.com/projects/overview):"
110
+ ),
111
+ validate: (value) => {
112
+ if (!value || !uuidRegex.test(value)) {
113
+ return "Project ID is required";
114
+ }
115
+ return true;
116
+ },
117
+ initial: ""
118
+ },
119
+ {
120
+ type: "text",
121
+ name: "corsConfirmation",
122
+ message: reset(
123
+ "Confirm you have whitelisted 'http://localhost:3000' at https://portal.cdp.coinbase.com/products/embedded-wallets/cors by typing 'y'"
124
+ ),
125
+ validate: (value) => {
126
+ if (value !== "y") {
127
+ return "You must whitelist your app domain for your app to be functional.";
128
+ }
129
+ return true;
130
+ },
131
+ initial: ""
132
+ },
133
+ {
134
+ type: () => !fs.existsSync(targetDir) || isDirEmpty(targetDir) ? null : "confirm",
135
+ name: "overwrite",
136
+ message: () => (targetDir === "." ? "Current directory" : `Target directory "${targetDir}"`) + " is not empty. Remove existing files and continue?"
137
+ },
138
+ {
139
+ type: (_, { overwrite }) => {
140
+ if (overwrite === false) {
141
+ throw new Error(red("✖") + " Operation cancelled");
142
+ }
143
+ return null;
144
+ },
145
+ name: "overwriteChecker"
146
+ }
147
+ ],
148
+ {
149
+ onCancel: () => {
150
+ throw new Error(red("✖") + " Operation cancelled");
151
+ }
152
+ }
153
+ );
154
+ return {
155
+ projectName: result.projectName,
156
+ template: result.template,
157
+ targetDirectory: targetDir,
158
+ projectId: result.projectId
159
+ };
160
+ } catch (cancelled) {
161
+ if (cancelled instanceof Error) {
162
+ console.log(cancelled.message);
163
+ }
164
+ process.exit(0);
165
+ }
166
+ }
167
+ function printNextSteps(projectRoot) {
168
+ const packageManager = detectPackageManager();
169
+ console.log(green("\nDone. Now run your app:\n"));
170
+ if (projectRoot !== process.cwd()) {
171
+ console.log(`cd ${path.relative(process.cwd(), projectRoot)}`);
172
+ }
173
+ console.log(`${packageManager} install`);
174
+ console.log(`${packageManager} dev`);
175
+ }
176
+ function copyTemplateFiles(templateDir, root, projectName, projectId) {
177
+ const writeFileToTarget = (file, content) => {
178
+ const targetPath = path.join(root, fileRenames[file] ?? file);
179
+ if (content) {
180
+ fs.writeFileSync(targetPath, content);
181
+ } else {
182
+ copyFile(path.join(templateDir, file), targetPath);
183
+ }
184
+ };
185
+ const files = fs.readdirSync(templateDir);
186
+ for (const file of files) {
187
+ if (file === "package.json") {
188
+ const customizedPackageJson = customizePackageJson(templateDir, projectName);
189
+ writeFileToTarget(file, customizedPackageJson);
190
+ } else if (file === "env.example" && projectId) {
191
+ const customizedEnv = customizeEnv(templateDir, projectId);
192
+ writeFileToTarget(file);
193
+ console.log("Copying project id to .env");
194
+ writeFileToTarget(".env", customizedEnv);
195
+ } else {
196
+ writeFileToTarget(file);
197
+ }
198
+ }
199
+ }
200
+ init().catch((e) => {
201
+ console.error(e);
202
+ process.exit(1);
203
+ });
204
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/utils.ts","../src/index.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\n/**\n * Prepare the project directory\n *\n * @param targetDir - The target directory for the project\n * @param shouldOverwrite - Whether to overwrite the existing directory\n * @returns The path to the prepared project directory\n */\nexport function prepareProjectDirectory(targetDir: string, shouldOverwrite: boolean): string {\n const root = path.join(process.cwd(), targetDir);\n\n if (shouldOverwrite) {\n emptyDir(root);\n } else if (!fs.existsSync(root)) {\n fs.mkdirSync(root, { recursive: true });\n }\n\n return root;\n}\n\n/**\n * Customize package.json for the new project\n *\n * @param templateDir - The directory containing the template files\n * @param projectName - The name of the project\n * @returns The customized package.json content\n */\nexport function customizePackageJson(templateDir: string, projectName: string): string {\n const packageJsonPath = path.join(templateDir, \"package.json\");\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, \"utf-8\"));\n packageJson.name = projectName;\n return JSON.stringify(packageJson, null, 2) + \"\\n\";\n}\n\n/**\n * Set up the .env file for the new project\n *\n * @param templateDir - The directory containing the template files\n * @param projectId - The project ID\n * @returns The customized .env content\n */\nexport function customizeEnv(templateDir: string, projectId: string): string {\n const exampleEnvPath = path.join(templateDir, \"env.example\");\n const exampleEnv = fs.readFileSync(exampleEnvPath, \"utf-8\");\n const envContent = exampleEnv.replace(\n /VITE_CDP_PROJECT_ID=.*(\\r?\\n|$)/,\n `VITE_CDP_PROJECT_ID=${projectId}\\n`,\n );\n return envContent;\n}\n\n/**\n * Copy a file or directory recursively\n *\n * @param filePath - The source path\n * @param destPath - The destination path\n */\nexport function copyFile(filePath: string, destPath: string): void {\n const stat = fs.statSync(filePath);\n if (stat.isDirectory()) {\n copyDir(filePath, destPath);\n } else {\n fs.copyFileSync(filePath, destPath);\n }\n}\n\n/**\n * Copy a directory recursively\n *\n * @param srcDir - The source directory path\n * @param destDir - The destination directory path\n */\nfunction copyDir(srcDir: string, destDir: string): void {\n fs.mkdirSync(destDir, { recursive: true });\n for (const file of fs.readdirSync(srcDir)) {\n const srcFile = path.resolve(srcDir, file);\n const destFile = path.resolve(destDir, file);\n copyFile(srcFile, destFile);\n }\n}\n\n/**\n * Check if a directory is empty\n *\n * @param dirPath - The path to the directory\n * @returns True if the directory is empty, false otherwise\n */\nexport function isDirEmpty(dirPath: string): boolean {\n const files = fs.readdirSync(dirPath);\n return files.length === 0 || (files.length === 1 && files[0] === \".git\");\n}\n\n/**\n * Empty a directory while preserving .git\n *\n * @param dirPath - The path to the directory\n */\nfunction emptyDir(dirPath: string): void {\n if (!fs.existsSync(dirPath)) {\n return;\n }\n for (const file of fs.readdirSync(dirPath)) {\n if (file === \".git\") {\n continue;\n }\n fs.rmSync(path.resolve(dirPath, file), { recursive: true, force: true });\n }\n}\n\n/**\n * Detect which package manager invoked the create command\n *\n * @returns The detected package manager or 'pnpm' as default\n */\nexport function detectPackageManager(): \"npm\" | \"pnpm\" | \"yarn\" {\n const userAgent = process.env.npm_config_user_agent;\n\n if (userAgent) {\n if (userAgent.startsWith(\"yarn\")) return \"yarn\";\n if (userAgent.startsWith(\"pnpm\")) return \"pnpm\";\n if (userAgent.startsWith(\"npm\")) return \"npm\";\n }\n\n return \"npm\"; // Default to npm if we can't detect\n}\n","#!/usr/bin/env node\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { blue, red, green, reset } from \"kolorist\";\nimport prompts from \"prompts\";\n\nimport {\n prepareProjectDirectory,\n isDirEmpty,\n customizePackageJson,\n copyFile,\n customizeEnv,\n detectPackageManager,\n} from \"./utils.js\";\n\n// Available templates for project creation\nconst TEMPLATES = [\n {\n name: \"react-components\",\n display: \"React Components\",\n color: blue,\n },\n];\n\nconst defaultTargetDir = \"cdp-app\";\n\nconst fileRenames: Record<string, string | undefined> = {\n _gitignore: \".gitignore\",\n};\n\ninterface ProjectOptions {\n projectName: string;\n template: string;\n targetDirectory: string;\n projectId: string;\n}\n\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Initialize a new CDP app project\n */\nasync function init(): Promise<void> {\n const { projectName, template, targetDirectory, projectId } = await getProjectDetails();\n\n console.log(`\\nScaffolding project in ${targetDirectory}...`);\n\n const root = prepareProjectDirectory(targetDirectory, false);\n const templateDir = path.resolve(fileURLToPath(import.meta.url), \"../..\", `template-${template}`);\n\n copyTemplateFiles(templateDir, root, projectName, projectId);\n printNextSteps(root);\n}\n\n/**\n * Get project details from command line arguments or prompt the user\n *\n * @returns The project details\n */\nasync function getProjectDetails(): Promise<ProjectOptions> {\n // Get target directory from command line args (first non-option argument)\n let targetDir = process.argv[2];\n const defaultProjectName = targetDir ?? defaultTargetDir;\n\n try {\n const result = await prompts(\n [\n {\n type: targetDir ? null : \"text\",\n name: \"projectName\",\n message: reset(\"Project name:\"),\n initial: defaultProjectName,\n onState: state => {\n targetDir = String(state.value).trim() || defaultProjectName;\n },\n },\n {\n type: \"select\",\n name: \"template\",\n message: reset(\"Select a template:\"),\n initial: 0,\n choices: TEMPLATES.map(template => ({\n title: template.color(template.display || template.name),\n value: template.name,\n })),\n },\n {\n type: \"text\",\n name: \"projectId\",\n message: reset(\n \"CDP Project ID (Find your project ID at https://portal.cdp.coinbase.com/projects/overview):\",\n ),\n validate: value => {\n if (!value || !uuidRegex.test(value)) {\n return \"Project ID is required\";\n }\n return true;\n },\n initial: \"\",\n },\n {\n type: \"text\",\n name: \"corsConfirmation\",\n message: reset(\n \"Confirm you have whitelisted 'http://localhost:3000' at https://portal.cdp.coinbase.com/products/embedded-wallets/cors by typing 'y'\",\n ),\n validate: value => {\n if (value !== \"y\") {\n return \"You must whitelist your app domain for your app to be functional.\";\n }\n return true;\n },\n initial: \"\",\n },\n {\n type: () => (!fs.existsSync(targetDir) || isDirEmpty(targetDir) ? null : \"confirm\"),\n name: \"overwrite\",\n message: () =>\n (targetDir === \".\" ? \"Current directory\" : `Target directory \"${targetDir}\"`) +\n \" is not empty. Remove existing files and continue?\",\n },\n {\n type: (_, { overwrite }: { overwrite?: boolean }) => {\n if (overwrite === false) {\n throw new Error(red(\"✖\") + \" Operation cancelled\");\n }\n return null;\n },\n name: \"overwriteChecker\",\n },\n ],\n {\n onCancel: () => {\n throw new Error(red(\"✖\") + \" Operation cancelled\");\n },\n },\n );\n\n return {\n projectName: result.projectName,\n template: result.template,\n targetDirectory: targetDir,\n projectId: result.projectId,\n };\n } catch (cancelled: unknown) {\n if (cancelled instanceof Error) {\n console.log(cancelled.message);\n }\n process.exit(0);\n }\n}\n\n/**\n * Print next steps for the user\n *\n * @param projectRoot - The root directory of the project\n */\nfunction printNextSteps(projectRoot: string): void {\n const packageManager = detectPackageManager();\n\n console.log(green(\"\\nDone. Now run your app:\\n\"));\n if (projectRoot !== process.cwd()) {\n console.log(`cd ${path.relative(process.cwd(), projectRoot)}`);\n }\n console.log(`${packageManager} install`);\n console.log(`${packageManager} dev`);\n}\n\n/**\n * Copy template files to the project directory\n *\n * @param templateDir - The directory containing the template files\n * @param root - The root directory of the project\n * @param projectName - The name of the project\n * @param projectId - The project ID\n */\nfunction copyTemplateFiles(\n templateDir: string,\n root: string,\n projectName: string,\n projectId?: string,\n): void {\n const writeFileToTarget = (file: string, content?: string) => {\n const targetPath = path.join(root, fileRenames[file] ?? file);\n if (content) {\n fs.writeFileSync(targetPath, content);\n } else {\n copyFile(path.join(templateDir, file), targetPath);\n }\n };\n\n const files = fs.readdirSync(templateDir);\n for (const file of files) {\n if (file === \"package.json\") {\n const customizedPackageJson = customizePackageJson(templateDir, projectName);\n writeFileToTarget(file, customizedPackageJson);\n } else if (file === \"env.example\" && projectId) {\n const customizedEnv = customizeEnv(templateDir, projectId);\n writeFileToTarget(file);\n console.log(\"Copying project id to .env\");\n writeFileToTarget(\".env\", customizedEnv);\n } else {\n writeFileToTarget(file);\n }\n }\n}\n\ninit().catch(e => {\n console.error(e);\n process.exit(1);\n});\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,8 +1,33 @@
1
1
  {
2
2
  "name": "@coinbase/create-cdp-app",
3
- "version": "0.0.0",
4
- "description": "Placeholder package",
5
- "main": "index.js",
6
- "author": "Coinbase Inc.",
7
- "license": "Apache-2.0"
8
- }
3
+ "version": "0.0.6",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "bin": {
8
+ "create-cdp-app": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist/**",
12
+ "!dist/**/*.tsbuildinfo",
13
+ "template-*"
14
+ ],
15
+ "dependencies": {
16
+ "kolorist": "^1.8.0",
17
+ "prompts": "^2.4.2"
18
+ },
19
+ "devDependencies": {
20
+ "@types/prompts": "^2.4.9"
21
+ },
22
+ "scripts": {
23
+ "build": "pnpm run check:types && vite build --config vite.config.ts",
24
+ "postbuild": "chmod +x dist/index.js",
25
+ "build:watch": "vite build --config vite.config.ts --watch",
26
+ "check:types": "tsc --noEmit",
27
+ "clean": "rm -rf dist",
28
+ "clean:all": "pnpm clean && rm -rf node_modules",
29
+ "size-limit": "size-limit",
30
+ "test": "vitest",
31
+ "test:run": "vitest --run"
32
+ }
33
+ }
@@ -0,0 +1,72 @@
1
+ # CDP React App
2
+
3
+ This project was generated with [`@coinbase/create-cdp-app`](https://github.com/coinbase/cdp-web) using the React Components template.
4
+
5
+ ## Project Structure
6
+
7
+ ```
8
+ src/
9
+ ├── App.tsx # Main application component with CDP initialization and CDP AuthButton
10
+ ├── Transaction.tsx # Example transaction flow using CDP Hooks
11
+ ├── assets/ # Static assets
12
+ ├── index.css # Global styles
13
+ └── main.tsx # Entry point with CDP provider setup
14
+ ```
15
+
16
+ ## Getting Started
17
+
18
+ First, make sure you have your CDP Project ID:
19
+
20
+ 1. Sign in or create an account on the [CDP Portal](https://portal.cdp.coinbase.com)
21
+ 2. Copy your Project ID from the dashboard
22
+ 3. Go to the [Embedded Wallets CORS settings](https://portal.cdp.coinbase.com/products/embedded-wallets/cors)
23
+ 4. Click add origin and whitelist `http://localhost:3000` (or wherever your app will run)
24
+
25
+ Then, copy the `env.example` file to `.env`, and populate the `VITE_CDP_PROJECT_ID` with your project id.
26
+
27
+ Now you can start the development server:
28
+
29
+ Using npm:
30
+ ```bash
31
+ # Install dependencies
32
+ npm install
33
+
34
+ # Start the development server
35
+ npm run dev
36
+ ```
37
+
38
+ Using yarn:
39
+ ```bash
40
+ # Install dependencies
41
+ yarn
42
+
43
+ # Start the development server
44
+ yarn dev
45
+ ```
46
+
47
+ Using pnpm:
48
+ ```bash
49
+ # Install dependencies
50
+ pnpm install
51
+
52
+ # Start the development server
53
+ pnpm dev
54
+ ```
55
+
56
+ Visit [http://localhost:3000](http://localhost:3000) to see your app.
57
+
58
+ ## Features
59
+
60
+ This template comes with:
61
+ - CDP React components for authentication
62
+ - Example transaction components for Base Sepolia
63
+ - Vite for fast development and building
64
+ - TypeScript for type safety
65
+ - ESLint for code quality
66
+
67
+ ## Learn More
68
+
69
+ - [CDP Documentation](https://docs.cloud.coinbase.com/cdp/docs)
70
+ - [CDP React Documentation](https://docs.cloud.coinbase.com/cdp/docs/react-components)
71
+ - [CDP Portal](https://portal.cdp.coinbase.com)
72
+ - [Vite Documentation](https://vitejs.dev)
@@ -0,0 +1,28 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/extensions.json
18
+ .idea
19
+ .DS_Store
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
25
+
26
+ # Env files
27
+ .env
28
+ .env.*
@@ -0,0 +1,3 @@
1
+ # CDP Configuration
2
+ VITE_CDP_PROJECT_ID=example-id
3
+
@@ -0,0 +1,23 @@
1
+ import js from "@eslint/js";
2
+ import globals from "globals";
3
+ import reactHooks from "eslint-plugin-react-hooks";
4
+ import reactRefresh from "eslint-plugin-react-refresh";
5
+ import tseslint from "typescript-eslint";
6
+ import { globalIgnores } from "eslint/config";
7
+
8
+ export default tseslint.config([
9
+ globalIgnores(["dist"]),
10
+ {
11
+ files: ["**/*.{ts,tsx}"],
12
+ extends: [
13
+ js.configs.recommended,
14
+ tseslint.configs.recommended,
15
+ reactHooks.configs["recommended-latest"],
16
+ reactRefresh.configs.vite,
17
+ ],
18
+ languageOptions: {
19
+ ecmaVersion: 2020,
20
+ globals: globals.browser,
21
+ },
22
+ },
23
+ ]);
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Vite + React + TS</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "cdp-react-components",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc -b && vite build",
9
+ "lint": "eslint .",
10
+ "preview": "vite preview"
11
+ },
12
+ "dependencies": {
13
+ "react": "^19.1.0",
14
+ "react-dom": "^19.1.0",
15
+ "@coinbase/cdp-react": "latest",
16
+ "@coinbase/cdp-hooks": "latest",
17
+ "@coinbase/cdp-core": "latest"
18
+ },
19
+ "devDependencies": {
20
+ "@eslint/js": "^9.30.1",
21
+ "@types/react": "^19.1.8",
22
+ "@types/react-dom": "^19.1.6",
23
+ "@vitejs/plugin-react": "^4.6.0",
24
+ "eslint": "^9.30.1",
25
+ "eslint-plugin-react-hooks": "^5.2.0",
26
+ "eslint-plugin-react-refresh": "^0.4.20",
27
+ "globals": "^16.3.0",
28
+ "typescript": "~5.8.3",
29
+ "typescript-eslint": "^8.35.1",
30
+ "vite": "^7.0.4"
31
+ }
32
+ }