diff --git a/bin/init.js b/bin/init.js index 1534beb..e878b3d 100644 --- a/bin/init.js +++ b/bin/init.js @@ -438,33 +438,32 @@ export async function init(projectName = null, autoAcceptDefaults = false) { // Add dependencies packageJson.dependencies = { - '@farcaster/auth-client': '>=0.3.0 <1.0.0', - '@farcaster/auth-kit': '>=0.6.0 <1.0.0', - '@farcaster/frame-core': '>=0.0.29 <1.0.0', - '@farcaster/frame-node': '>=0.0.18 <1.0.0', - '@farcaster/frame-sdk': '>=0.0.31 <1.0.0', - '@farcaster/frame-wagmi-connector': '>=0.0.19 <1.0.0', - '@farcaster/mini-app-solana': '>=0.0.17 <1.0.0', - '@neynar/react': '^1.2.5', - '@radix-ui/react-label': '^2.1.1', - '@solana/wallet-adapter-react': '^0.15.38', - '@tanstack/react-query': '^5.61.0', - '@upstash/redis': '^1.34.3', - 'class-variance-authority': '^0.7.1', - clsx: '^2.1.1', - dotenv: '^16.4.7', - 'lucide-react': '^0.469.0', - mipd: '^0.0.7', - next: '^15', - 'next-auth': '^4.24.11', - react: '^19', - 'react-dom': '^19', - 'tailwind-merge': '^2.6.0', - 'tailwindcss-animate': '^1.0.7', - viem: '^2.23.6', - wagmi: '^2.14.12', - zod: '^3.24.2', - siwe: '^3.0.0', + "@farcaster/auth-client": ">=0.3.0 <1.0.0", + "@farcaster/auth-kit": ">=0.6.0 <1.0.0", + "@farcaster/miniapp-node": ">=0.1.5 <1.0.0", + "@farcaster/miniapp-sdk": ">=0.1.6 <1.0.0", + "@farcaster/miniapp-wagmi-connector": "^1.0.0", + "@farcaster/mini-app-solana": ">=0.0.17 <1.0.0", + "@neynar/react": "^1.2.5", + "@radix-ui/react-label": "^2.1.1", + "@solana/wallet-adapter-react": "^0.15.38", + "@tanstack/react-query": "^5.61.0", + "@upstash/redis": "^1.34.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "dotenv": "^16.4.7", + "lucide-react": "^0.469.0", + "mipd": "^0.0.7", + "next": "^15", + "next-auth": "^4.24.11", + "react": "^19", + "react-dom": "^19", + "tailwind-merge": "^2.6.0", + "tailwindcss-animate": "^1.0.7", + "viem": "^2.23.6", + "wagmi": "^2.14.12", + "zod": "^3.24.2", + "siwe": '^3.0.0', }; packageJson.devDependencies = { diff --git a/package-lock.json b/package-lock.json index 6dc240c..26edf98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -344,6 +344,132 @@ } } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@lukeed/csprng": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", @@ -355,25 +481,27 @@ } }, "node_modules/@nestjs/axios": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.1.3.tgz", - "integrity": "sha512-RZ/63c1tMxGLqyG3iOCVt7A72oy4x1eM6QEhd4KzCYpaVWW0igq0WSREeRoEZhIxRcZfDfIIkvsOMiM7yfVGZQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-4.0.0.tgz", + "integrity": "sha512-1cB+Jyltu/uUPNQrpUimRHEQHrnQrpLzVj6dU3dgn6iDDDdahr10TgHFGTmw5VuJ9GzKZsCLDL78VSwJAs/9JQ==", "dev": true, "license": "MIT", "peerDependencies": { - "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/common": "^10.0.0 || ^11.0.0", "axios": "^1.3.1", - "rxjs": "^6.0.0 || ^7.0.0" + "rxjs": "^7.0.0" } }, "node_modules/@nestjs/common": { - "version": "10.4.15", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.15.tgz", - "integrity": "sha512-vaLg1ZgwhG29BuLDxPA9OAcIlgqzp9/N8iG0wGapyUNTf4IY4O6zAHgN6QalwLhFxq7nOI021vdRojR1oF3bqg==", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.3.tgz", + "integrity": "sha512-ogEK+GriWodIwCw6buQ1rpcH4Kx+G7YQ9EwuPySI3rS05pSdtQ++UhucjusSI9apNidv+QURBztJkRecwwJQXg==", "dev": true, "license": "MIT", "dependencies": { + "file-type": "21.0.0", "iterare": "1.2.1", + "load-esm": "1.0.2", "tslib": "2.8.1", "uid": "2.0.2" }, @@ -382,8 +510,8 @@ "url": "https://opencollective.com/nest" }, "peerDependencies": { - "class-transformer": "*", - "class-validator": "*", + "class-transformer": ">=0.4.1", + "class-validator": ">=0.13.2", "reflect-metadata": "^0.1.12 || ^0.2.0", "rxjs": "^7.1.0" }, @@ -397,29 +525,32 @@ } }, "node_modules/@nestjs/core": { - "version": "10.4.15", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.15.tgz", - "integrity": "sha512-UBejmdiYwaH6fTsz2QFBlC1cJHM+3UDeLZN+CiP9I1fRv2KlBZsmozGLbV5eS1JAVWJB4T5N5yQ0gjN8ZvcS2w==", + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.3.tgz", + "integrity": "sha512-5lTni0TCh8x7bXETRD57pQFnKnEg1T6M+VLE7wAmyQRIecKQU+2inRGZD+A4v2DC1I04eA0WffP0GKLxjOKlzw==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "@nuxtjs/opencollective": "0.3.2", + "@nuxt/opencollective": "0.4.1", "fast-safe-stringify": "2.1.1", "iterare": "1.2.1", - "path-to-regexp": "3.3.0", + "path-to-regexp": "8.2.0", "tslib": "2.8.1", "uid": "2.0.2" }, + "engines": { + "node": ">= 20" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/nest" }, "peerDependencies": { - "@nestjs/common": "^10.0.0", - "@nestjs/microservices": "^10.0.0", - "@nestjs/platform-express": "^10.0.0", - "@nestjs/websockets": "^10.0.0", + "@nestjs/common": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0", + "@nestjs/websockets": "^11.0.0", "reflect-metadata": "^0.1.12 || ^0.2.0", "rxjs": "^7.1.0" }, @@ -477,6 +608,23 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@nuxt/opencollective": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", + "integrity": "sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": "^14.18.0 || >=16.10.0", + "npm": ">=5.10.0" + } + }, "node_modules/@nuxtjs/opencollective": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", @@ -496,26 +644,33 @@ "npm": ">=5.0.0" } }, + "node_modules/@nuxtjs/opencollective/node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", + "dev": true, + "license": "MIT" + }, "node_modules/@openapitools/openapi-generator-cli": { - "version": "2.18.4", - "resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.18.4.tgz", - "integrity": "sha512-wer7rWp92fLcHqRG/2XS2bGqGUo2qVO0MseUgcpbxyVzBrKZZJh5c0dxQWTD3V178laj1ndC6w1Parn3fjKolg==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.21.0.tgz", + "integrity": "sha512-NdDvCd7hya+UucxH7G94Jf6tmA6641I4CF/T3xtFhM+NQQNWAP5tpiOBN4Ub9ocU6cCgQgXdWl4EpwlEwW7JDQ==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@nestjs/axios": "3.1.3", - "@nestjs/common": "10.4.15", - "@nestjs/core": "10.4.15", + "@nestjs/axios": "4.0.0", + "@nestjs/common": "11.1.3", + "@nestjs/core": "11.1.3", "@nuxtjs/opencollective": "0.3.2", - "axios": "1.8.3", + "axios": "1.10.0", "chalk": "4.1.2", "commander": "8.3.0", "compare-versions": "4.1.4", "concurrently": "6.5.1", "console.table": "0.10.0", "fs-extra": "11.3.0", - "glob": "9.3.5", + "glob": "11.0.3", "inquirer": "8.2.6", "lodash": "4.17.21", "proxy-agent": "6.5.0", @@ -624,6 +779,32 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true, + "license": "MIT" + }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -732,9 +913,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", - "integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", "dev": true, "license": "MIT", "dependencies": { @@ -743,13 +924,6 @@ "proxy-from-env": "^1.1.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -793,16 +967,6 @@ "readable-stream": "^3.4.0" } }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -1048,11 +1212,14 @@ "license": "0BSD" }, "node_modules/consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } }, "node_modules/console.table": { "version": "0.10.0", @@ -1067,6 +1234,21 @@ "node": "> 0.10" } }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/data-uri-to-buffer": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", @@ -1177,6 +1359,13 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/easy-table": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz", @@ -1345,6 +1534,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -1361,6 +1557,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/file-type": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.0.0.tgz", + "integrity": "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.2.7", + "strtok3": "^10.2.2", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/follow-redirects": { "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", @@ -1382,16 +1597,34 @@ } } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -1413,13 +1646,6 @@ "node": ">=14.14" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -1495,19 +1721,24 @@ } }, "node_modules/glob": { - "version": "9.3.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", - "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", "dev": true, "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "minimatch": "^8.0.2", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1725,6 +1956,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, "node_modules/isows": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz", @@ -1750,6 +1988,22 @@ "node": ">=6" } }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/jsbn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", @@ -1770,6 +2024,26 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/load-esm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.2.tgz", + "integrity": "sha512-nVAvWk/jeyrWyXEAs84mpQCYccxRqgKY4OznLuJhJCa0XsPSfdOIr2zvBZEj3IHEHbX97jjscKRRV539bW0Gpw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + }, + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/borewit" + } + ], + "license": "MIT", + "engines": { + "node": ">=13.2.0" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -1795,11 +2069,14 @@ } }, "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", "dev": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": "20 || >=22" + } }, "node_modules/math-intrinsics": { "version": "1.1.0", @@ -1845,29 +2122,29 @@ } }, "node_modules/minimatch": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", - "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minipass": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", - "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "license": "ISC", "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/ms": { @@ -2029,39 +2306,49 @@ "node": ">= 14" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/path-to-regexp": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", - "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=16" + } }, "node_modules/proxy-agent": { "version": "6.5.0", @@ -2218,6 +2505,29 @@ "node": ">=10" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -2319,6 +2629,22 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -2331,6 +2657,37 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strtok3": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.1.tgz", + "integrity": "sha512-3JWEZM6mfix/GCJBBUrkA8p2Id2pBkyTkVCJKto55w080QBKZ+8R171fGrbiSp+yMO/u6F8/yUh7K4V9K+YCnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2363,6 +2720,24 @@ "node": ">=0.6.0" } }, + "node_modules/token-types": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.3.tgz", + "integrity": "sha512-IKJ6EzuPPWtKtEIEPpIdXv9j5j2LGJEYk0CKY2efgKoYKLBiZdh6iQkLVBow/CB3phyWAWCyk+bZeaimJn6uRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -2425,6 +2800,19 @@ "node": ">=8" } }, + "node_modules/uint8array-extras": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz", + "integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/undici-types": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", @@ -2507,6 +2895,22 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -2521,6 +2925,25 @@ "node": ">=8" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", diff --git a/package.json b/package.json index a42cc59..775601a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@neynar/create-farcaster-mini-app", - "version": "1.5.4", + "version": "1.5.7", "type": "module", "private": false, "access": "public", diff --git a/scripts/build.js b/scripts/build.js index a4580ce..ac8ed77 100755 --- a/scripts/build.js +++ b/scripts/build.js @@ -1,91 +1,50 @@ -import { execSync } from 'child_process'; -import fs from 'fs'; -import path from 'path'; -import { mnemonicToAccount } from 'viem/accounts'; -import { fileURLToPath } from 'url'; -import inquirer from 'inquirer'; -import dotenv from 'dotenv'; -import crypto from 'crypto'; - -// ANSI color codes -const yellow = '\x1b[33m'; -const italic = '\x1b[3m'; -const reset = '\x1b[0m'; +import { execSync } from "child_process"; +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; +import inquirer from "inquirer"; +import dotenv from "dotenv"; +import crypto from "crypto"; // Load environment variables in specific order // First load .env for main config -dotenv.config({ path: '.env' }); - -async function lookupFidByCustodyAddress(custodyAddress, apiKey) { - if (!apiKey) { - throw new Error('Neynar API key is required'); - } - const lowerCasedCustodyAddress = custodyAddress.toLowerCase(); - - const response = await fetch( - `https://api.neynar.com/v2/farcaster/user/bulk-by-address?addresses=${lowerCasedCustodyAddress}&address_types=custody_address`, - { - headers: { - 'accept': 'application/json', - 'x-api-key': 'FARCASTER_V2_FRAMES_DEMO' - } - } - ); - - if (!response.ok) { - throw new Error(`Failed to lookup FID: ${response.statusText}`); - } - - const data = await response.json(); - if (!data[lowerCasedCustodyAddress]?.length || !data[lowerCasedCustodyAddress][0].custody_address) { - throw new Error('No FID found for this custody address'); - } - - return data[lowerCasedCustodyAddress][0].fid; -} +dotenv.config({ path: ".env" }); async function loadEnvLocal() { try { - if (fs.existsSync('.env.local')) { + if (fs.existsSync(".env.local")) { const { loadLocal } = await inquirer.prompt([ { - type: 'confirm', - name: 'loadLocal', - message: 'Found .env.local, likely created by the install script - would you like to load its values?', - default: false - } + type: "confirm", + name: "loadLocal", + message: + "Found .env.local, likely created by the install script - would you like to load its values?", + default: false, + }, ]); if (loadLocal) { - console.log('Loading values from .env.local...'); - const localEnv = dotenv.parse(fs.readFileSync('.env.local')); - - // Copy all values except SEED_PHRASE to .env - const envContent = fs.existsSync('.env') ? fs.readFileSync('.env', 'utf8') + '\n' : ''; + console.log("Loading values from .env.local..."); + const localEnv = dotenv.parse(fs.readFileSync(".env.local")); + + // Copy all values to .env + const envContent = fs.existsSync(".env") + ? fs.readFileSync(".env", "utf8") + "\n" + : ""; let newEnvContent = envContent; - + for (const [key, value] of Object.entries(localEnv)) { - if (key !== 'SEED_PHRASE' && key !== 'SPONSOR_SIGNER') { - // Update process.env - process.env[key] = value; - // Add to .env content if not already there - if (!envContent.includes(`${key}=`)) { - newEnvContent += `${key}="${value}"\n`; - } + // Update process.env + process.env[key] = value; + // Add to .env content if not already there + if (!envContent.includes(`${key}=`)) { + newEnvContent += `${key}="${value}"\n`; } } - - // Write updated content to .env - fs.writeFileSync('.env', newEnvContent); - console.log('āœ… Values from .env.local have been written to .env'); - } - } - // Always try to load SEED_PHRASE from .env.local - if (fs.existsSync('.env.local')) { - const localEnv = dotenv.parse(fs.readFileSync('.env.local')); - if (localEnv.SEED_PHRASE) { - process.env.SEED_PHRASE = localEnv.SEED_PHRASE; + // Write updated content to .env + fs.writeFileSync(".env", newEnvContent); + console.log("āœ… Values from .env.local have been written to .env"); } if (localEnv.SPONSOR_SIGNER) { process.env.SPONSOR_SIGNER = localEnv.SPONSOR_SIGNER; @@ -93,22 +52,26 @@ async function loadEnvLocal() { } } catch (error) { // Error reading .env.local, which is fine - console.log('Note: No .env.local file found'); + console.log("Note: No .env.local file found"); } } // TODO: make sure rebuilding is supported const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const projectRoot = path.join(__dirname, '..'); +const projectRoot = path.join(__dirname, ".."); async function validateDomain(domain) { // Remove http:// or https:// if present - const cleanDomain = domain.replace(/^https?:\/\//, ''); - + const cleanDomain = domain.replace(/^https?:\/\//, ""); + // Basic domain validation - if (!cleanDomain.match(/^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/)) { - throw new Error('Invalid domain format'); + if ( + !cleanDomain.match( + /^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/ + ) + ) { + throw new Error("Invalid domain format"); } return cleanDomain; @@ -123,54 +86,26 @@ async function queryNeynarApp(apiKey) { `https://api.neynar.com/portal/app_by_api_key`, { headers: { - 'x-api-key': apiKey - } + "x-api-key": apiKey, + }, } ); const data = await response.json(); return data; } catch (error) { - console.error('Error querying Neynar app data:', error); + console.error("Error querying Neynar app data:", error); return null; } } -async function validateSeedPhrase(seedPhrase) { - try { - // Try to create an account from the seed phrase - const account = mnemonicToAccount(seedPhrase); - return account.address; - } catch (error) { - throw new Error('Invalid seed phrase'); - } -} - -async function generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase, webhookUrl) { - const header = { - type: 'custody', - key: accountAddress, - fid, - }; - const encodedHeader = Buffer.from(JSON.stringify(header), 'utf-8').toString('base64'); - - const payload = { - domain - }; - const encodedPayload = Buffer.from(JSON.stringify(payload), 'utf-8').toString('base64url'); - - const account = mnemonicToAccount(seedPhrase); - const signature = await account.signMessage({ - message: `${encodedHeader}.${encodedPayload}` - }); - const encodedSignature = Buffer.from(signature, 'utf-8').toString('base64url'); - - const tags = process.env.NEXT_PUBLIC_MINI_APP_TAGS?.split(','); +async function generateFarcasterMetadata(domain, webhookUrl) { + const tags = process.env.NEXT_PUBLIC_MINI_APP_TAGS?.split(","); return { accountAssociation: { - header: encodedHeader, - payload: encodedPayload, - signature: encodedSignature + header: "", + payload: "", + signature: "", }, frame: { version: "1", @@ -191,18 +126,19 @@ async function generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase async function main() { try { - console.log('\nšŸ“ Checking environment variables...'); - console.log('Loading values from .env...'); - + console.log("\nšŸ“ Checking environment variables..."); + console.log("Loading values from .env..."); + // Load .env.local if user wants to await loadEnvLocal(); // Get domain from user const { domain } = await inquirer.prompt([ { - type: 'input', - name: 'domain', - message: 'Enter the domain where your mini app will be deployed (e.g., example.com):', + type: "input", + name: "domain", + message: + "Enter the domain where your mini app will be deployed (e.g., example.com):", validate: async (input) => { try { await validateDomain(input); @@ -210,40 +146,41 @@ async function main() { } catch (error) { return error.message; } - } - } + }, + }, ]); // Get frame name from user const { frameName } = await inquirer.prompt([ { - type: 'input', - name: 'frameName', - message: 'Enter the name for your mini app (e.g., My Cool Mini App):', + type: "input", + name: "frameName", + message: "Enter the name for your mini app (e.g., My Cool Mini App):", default: process.env.NEXT_PUBLIC_MINI_APP_NAME, validate: (input) => { - if (input.trim() === '') { - return 'Mini app name cannot be empty'; + if (input.trim() === "") { + return "Mini app name cannot be empty"; } return true; - } - } + }, + }, ]); // Get button text from user const { buttonText } = await inquirer.prompt([ { - type: 'input', - name: 'buttonText', - message: 'Enter the text for your mini app button:', - default: process.env.NEXT_PUBLIC_MINI_APP_BUTTON_TEXT || 'Launch Mini App', + type: "input", + name: "buttonText", + message: "Enter the text for your mini app button:", + default: + process.env.NEXT_PUBLIC_MINI_APP_BUTTON_TEXT || "Launch Mini App", validate: (input) => { - if (input.trim() === '') { - return 'Button text cannot be empty'; + if (input.trim() === "") { + return "Button text cannot be empty"; } return true; - } - } + }, + }, ]); // Get Neynar configuration @@ -255,15 +192,16 @@ async function main() { if (!neynarApiKey) { const { neynarApiKey: inputNeynarApiKey } = await inquirer.prompt([ { - type: 'password', - name: 'neynarApiKey', - message: 'Enter your Neynar API key (optional - leave blank to skip):', - default: null - } + type: "password", + name: "neynarApiKey", + message: + "Enter your Neynar API key (optional - leave blank to skip):", + default: null, + }, ]); neynarApiKey = inputNeynarApiKey; } else { - console.log('Using existing Neynar API key from .env'); + console.log("Using existing Neynar API key from .env"); } if (!neynarApiKey) { @@ -276,7 +214,7 @@ async function main() { const appInfo = await queryNeynarApp(neynarApiKey); if (appInfo) { neynarClientId = appInfo.app_uuid; - console.log('āœ… Fetched Neynar app client ID'); + console.log("āœ… Fetched Neynar app client ID"); break; } } @@ -287,14 +225,16 @@ async function main() { } // If we get here, the API key was invalid - console.log('\nāš ļø Could not find Neynar app information. The API key may be incorrect.'); + console.log( + "\nāš ļø Could not find Neynar app information. The API key may be incorrect." + ); const { retry } = await inquirer.prompt([ { - type: 'confirm', - name: 'retry', - message: 'Would you like to try a different API key?', - default: true - } + type: "confirm", + name: "retry", + message: "Would you like to try a different API key?", + default: true, + }, ]); // Reset for retry @@ -307,51 +247,23 @@ async function main() { } } - // Get seed phrase from user - let seedPhrase = process.env.SEED_PHRASE; - if (!seedPhrase) { - const { seedPhrase: inputSeedPhrase } = await inquirer.prompt([ - { - type: 'password', - name: 'seedPhrase', - message: 'Your farcaster custody account seed phrase is required to create a signature proving this app was created by you.\n' + - `āš ļø ${yellow}${italic}seed phrase is only used to sign the mini app manifest, then discarded${reset} āš ļø\n` + - 'Seed phrase:', - validate: async (input) => { - try { - await validateSeedPhrase(input); - return true; - } catch (error) { - return error.message; - } - } - } - ]); - seedPhrase = inputSeedPhrase; - } else { - console.log('Using existing seed phrase from .env'); - } + // Generate manifest + console.log("\nšŸ”Ø Generating mini app manifest..."); - // Validate seed phrase and get account address - const accountAddress = await validateSeedPhrase(seedPhrase); - console.log('āœ… Generated account address from seed phrase'); - - const fid = await lookupFidByCustodyAddress(accountAddress, neynarApiKey ?? 'FARCASTER_V2_FRAMES_DEMO'); - - // Generate and sign manifest - console.log('\nšŸ”Ø Generating mini app manifest...'); - // Determine webhook URL based on environment variables - const webhookUrl = neynarApiKey && neynarClientId - ? `https://api.neynar.com/f/app/${neynarClientId}/event` - : `${domain}/api/webhook`; + const webhookUrl = + neynarApiKey && neynarClientId + ? `https://api.neynar.com/f/app/${neynarClientId}/event` + : `https://${domain}/api/webhook`; - const metadata = await generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase, webhookUrl); - console.log('\nāœ… Mini app manifest generated' + (seedPhrase ? ' and signed' : '')); + const metadata = await generateFarcasterMetadata(domain, webhookUrl); + console.log("\nāœ… Mini app manifest generated"); // Read existing .env file or create new one - const envPath = path.join(projectRoot, '.env'); - let envContent = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf8') : ''; + const envPath = path.join(projectRoot, ".env"); + let envContent = fs.existsSync(envPath) + ? fs.readFileSync(envPath, "utf8") + : ""; // Add or update environment variables const newEnvVars = [ @@ -360,28 +272,40 @@ async function main() { // Mini app metadata `NEXT_PUBLIC_MINI_APP_NAME="${frameName}"`, - `NEXT_PUBLIC_MINI_APP_DESCRIPTION="${process.env.NEXT_PUBLIC_MINI_APP_DESCRIPTION || ''}"`, - `NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY="${process.env.NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY || ''}"`, - `NEXT_PUBLIC_MINI_APP_TAGS="${process.env.NEXT_PUBLIC_MINI_APP_TAGS || ''}"`, + `NEXT_PUBLIC_MINI_APP_DESCRIPTION="${ + process.env.NEXT_PUBLIC_MINI_APP_DESCRIPTION || "" + }"`, + `NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY="${ + process.env.NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY || "" + }"`, + `NEXT_PUBLIC_MINI_APP_TAGS="${ + process.env.NEXT_PUBLIC_MINI_APP_TAGS || "" + }"`, `NEXT_PUBLIC_MINI_APP_BUTTON_TEXT="${buttonText}"`, // Analytics - `NEXT_PUBLIC_ANALYTICS_ENABLED="${process.env.NEXT_PUBLIC_ANALYTICS_ENABLED || 'false'}"`, + `NEXT_PUBLIC_ANALYTICS_ENABLED="${ + process.env.NEXT_PUBLIC_ANALYTICS_ENABLED || "false" + }"`, // Neynar configuration (if it exists in current env) - ...(process.env.NEYNAR_API_KEY ? - [`NEYNAR_API_KEY="${process.env.NEYNAR_API_KEY}"`] : []), - ...(neynarClientId ? - [`NEYNAR_CLIENT_ID="${neynarClientId}"`] : []), + ...(process.env.NEYNAR_API_KEY + ? [`NEYNAR_API_KEY="${process.env.NEYNAR_API_KEY}"`] + : []), + ...(neynarClientId ? [`NEYNAR_CLIENT_ID="${neynarClientId}"`] : []), ...(process.env.SPONSOR_SIGNER ? [`SPONSOR_SIGNER="${process.env.SPONSOR_SIGNER}"`] : []), // FID (if it exists in current env) ...(process.env.FID ? [`FID="${process.env.FID}"`] : []), - `NEXT_PUBLIC_USE_WALLET="${process.env.NEXT_PUBLIC_USE_WALLET || 'false'}"`, + `NEXT_PUBLIC_USE_WALLET="${ + process.env.NEXT_PUBLIC_USE_WALLET || "false" + }"`, // NextAuth configuration - `NEXTAUTH_SECRET="${process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString('hex')}"`, + `NEXTAUTH_SECRET="${ + process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString("hex") + }"`, `NEXTAUTH_URL="https://${domain}"`, // Mini app manifest with signature @@ -389,14 +313,14 @@ async function main() { ]; // Filter out empty values and join with newlines - const validEnvVars = newEnvVars.filter(line => { - const [, value] = line.split('='); + const validEnvVars = newEnvVars.filter((line) => { + const [, value] = line.split("="); return value && value !== '""'; }); // Update or append each environment variable - validEnvVars.forEach(varLine => { - const [key] = varLine.split('='); + validEnvVars.forEach((varLine) => { + const [key] = varLine.split("="); if (envContent.includes(`${key}=`)) { envContent = envContent.replace(new RegExp(`${key}=.*`), varLine); } else { @@ -407,22 +331,27 @@ async function main() { // Write updated .env file fs.writeFileSync(envPath, envContent); - console.log('\nāœ… Environment variables updated'); + console.log("\nāœ… Environment variables updated"); // Run next build - console.log('\nBuilding Next.js application...'); - const nextBin = path.normalize(path.join(projectRoot, 'node_modules', '.bin', 'next')); - execSync(`"${nextBin}" build`, { - cwd: projectRoot, - stdio: 'inherit', - shell: process.platform === 'win32' + console.log("\nBuilding Next.js application..."); + const nextBin = path.normalize( + path.join(projectRoot, "node_modules", ".bin", "next") + ); + execSync(`"${nextBin}" build`, { + cwd: projectRoot, + stdio: "inherit", + shell: process.platform === "win32", }); - console.log('\n✨ Build complete! Your mini app is ready for deployment. 🪐'); - console.log('šŸ“ Make sure to configure the environment variables from .env in your hosting provider'); - + console.log( + "\n✨ Build complete! Your mini app is ready for deployment. 🪐" + ); + console.log( + "šŸ“ Make sure to configure the environment variables from .env in your hosting provider" + ); } catch (error) { - console.error('\nāŒ Error:', error.message); + console.error("\nāŒ Error:", error.message); process.exit(1); } } diff --git a/scripts/cleanup.js b/scripts/cleanup.js index aed5bca..ed3d3ed 100644 --- a/scripts/cleanup.js +++ b/scripts/cleanup.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -const { execSync } = require('child_process'); +import { execSync } from 'child_process'; // Parse arguments const args = process.argv.slice(2); diff --git a/scripts/deploy.js b/scripts/deploy.js index 1b4c0f8..0f75f01 100755 --- a/scripts/deploy.js +++ b/scripts/deploy.js @@ -1,85 +1,24 @@ -import { execSync, spawn } from 'child_process'; -import fs from 'fs'; -import path from 'path'; -import os from 'os'; -import { fileURLToPath } from 'url'; -import inquirer from 'inquirer'; -import dotenv from 'dotenv'; -import crypto from 'crypto'; -import { mnemonicToAccount } from 'viem/accounts'; -import { Vercel } from '@vercel/sdk'; +import { execSync, spawn } from "child_process"; +import fs from "fs"; +import path from "path"; +import os from "os"; +import { fileURLToPath } from "url"; +import inquirer from "inquirer"; +import dotenv from "dotenv"; +import crypto from "crypto"; +import { Vercel } from "@vercel/sdk"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const projectRoot = path.join(__dirname, '..'); +const projectRoot = path.join(__dirname, ".."); // Load environment variables in specific order -dotenv.config({ path: '.env' }); +dotenv.config({ path: ".env" }); -async function validateSeedPhrase(seedPhrase) { - try { - const account = mnemonicToAccount(seedPhrase); - return account.address; - } catch (error) { - throw new Error('Invalid seed phrase'); - } -} - -async function lookupFidByCustodyAddress(custodyAddress, apiKey) { - if (!apiKey) { - throw new Error('Neynar API key is required'); - } - const lowerCasedCustodyAddress = custodyAddress.toLowerCase(); - - const response = await fetch( - `https://api.neynar.com/v2/farcaster/user/bulk-by-address?addresses=${lowerCasedCustodyAddress}&address_types=custody_address`, - { - headers: { - 'accept': 'application/json', - 'x-api-key': apiKey - } - } - ); - - if (!response.ok) { - throw new Error(`Failed to lookup FID: ${response.statusText}`); - } - - const data = await response.json(); - if (!data[lowerCasedCustodyAddress]?.length || !data[lowerCasedCustodyAddress][0].custody_address) { - throw new Error('No FID found for this custody address'); - } - - return data[lowerCasedCustodyAddress][0].fid; -} - -async function generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase, webhookUrl) { +async function generateFarcasterMetadata(domain, webhookUrl) { const trimmedDomain = domain.trim(); - const header = { - type: 'custody', - key: accountAddress, - fid, - }; - const encodedHeader = Buffer.from(JSON.stringify(header), 'utf-8').toString('base64'); - - const payload = { - domain: trimmedDomain - }; - const encodedPayload = Buffer.from(JSON.stringify(payload), 'utf-8').toString('base64url'); - - const account = mnemonicToAccount(seedPhrase); - const signature = await account.signMessage({ - message: `${encodedHeader}.${encodedPayload}` - }); - const encodedSignature = Buffer.from(signature, 'utf-8').toString('base64url'); - - const tags = process.env.NEXT_PUBLIC_MINI_APP_TAGS?.split(','); + const tags = process.env.NEXT_PUBLIC_MINI_APP_TAGS?.split(","); return { - accountAssociation: { - header: encodedHeader, - payload: encodedPayload, - signature: encodedSignature - }, frame: { version: "1", name: process.env.NEXT_PUBLIC_MINI_APP_NAME, @@ -99,131 +38,108 @@ async function generateFarcasterMetadata(domain, fid, accountAddress, seedPhrase async function loadEnvLocal() { try { - if (fs.existsSync('.env.local')) { + if (fs.existsSync(".env.local")) { const { loadLocal } = await inquirer.prompt([ { - type: 'confirm', - name: 'loadLocal', - message: 'Found .env.local - would you like to load its values in addition to .env values? (except for SEED_PHRASE, values will be written to .env)', - default: true - } + type: "confirm", + name: "loadLocal", + message: + "Found .env.local - would you like to load its values in addition to .env values?", + default: true, + }, ]); if (loadLocal) { - console.log('Loading values from .env.local...'); - const localEnv = dotenv.parse(fs.readFileSync('.env.local')); - + console.log("Loading values from .env.local..."); + const localEnv = dotenv.parse(fs.readFileSync(".env.local")); + const allowedVars = [ - 'SEED_PHRASE', - 'SPONSOR_SIGNER', - 'NEXT_PUBLIC_MINI_APP_NAME', - 'NEXT_PUBLIC_MINI_APP_DESCRIPTION', - 'NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY', - 'NEXT_PUBLIC_MINI_APP_TAGS', - 'NEXT_PUBLIC_MINI_APP_BUTTON_TEXT', - 'NEXT_PUBLIC_ANALYTICS_ENABLED', - 'NEYNAR_API_KEY', - 'NEYNAR_CLIENT_ID' + "NEXT_PUBLIC_MINI_APP_NAME", + "NEXT_PUBLIC_MINI_APP_DESCRIPTION", + "NEXT_PUBLIC_MINI_APP_PRIMARY_CATEGORY", + "NEXT_PUBLIC_MINI_APP_TAGS", + "NEXT_PUBLIC_MINI_APP_BUTTON_TEXT", + "NEXT_PUBLIC_ANALYTICS_ENABLED", + "NEYNAR_API_KEY", + "NEYNAR_CLIENT_ID", + "SPONSOR_SIGNER", ]; - - const envContent = fs.existsSync('.env') ? fs.readFileSync('.env', 'utf8') + '\n' : ''; + + const envContent = fs.existsSync(".env") + ? fs.readFileSync(".env", "utf8") + "\n" + : ""; let newEnvContent = envContent; - + for (const [key, value] of Object.entries(localEnv)) { if (allowedVars.includes(key)) { process.env[key] = value; - if (key !== 'SEED_PHRASE' && key !== 'SPONSOR_SIGNER' && !envContent.includes(`${key}=`)) { + if (!envContent.includes(`${key}=`)) { newEnvContent += `${key}="${value}"\n`; } } } - - fs.writeFileSync('.env', newEnvContent); - console.log('āœ… Values from .env.local have been written to .env'); + + fs.writeFileSync(".env", newEnvContent); + console.log("āœ… Values from .env.local have been written to .env"); } } } catch (error) { - console.log('Note: No .env.local file found'); + console.log("Note: No .env.local file found"); } } async function checkRequiredEnvVars() { - console.log('\nšŸ“ Checking environment variables...'); - console.log('Loading values from .env...'); - + console.log("\nšŸ“ Checking environment variables..."); + console.log("Loading values from .env..."); + await loadEnvLocal(); const requiredVars = [ { - name: 'NEXT_PUBLIC_MINI_APP_NAME', - message: 'Enter the name for your frame (e.g., My Cool Mini App):', + name: "NEXT_PUBLIC_MINI_APP_NAME", + message: "Enter the name for your frame (e.g., My Cool Mini App):", default: process.env.NEXT_PUBLIC_MINI_APP_NAME, - validate: input => input.trim() !== '' || 'Mini app name cannot be empty' + validate: (input) => + input.trim() !== "" || "Mini app name cannot be empty", }, { - name: 'NEXT_PUBLIC_MINI_APP_BUTTON_TEXT', - message: 'Enter the text for your frame button:', - default: process.env.NEXT_PUBLIC_MINI_APP_BUTTON_TEXT ?? 'Launch Mini App', - validate: input => input.trim() !== '' || 'Button text cannot be empty' - } + name: "NEXT_PUBLIC_MINI_APP_BUTTON_TEXT", + message: "Enter the text for your frame button:", + default: + process.env.NEXT_PUBLIC_MINI_APP_BUTTON_TEXT ?? "Launch Mini App", + validate: (input) => input.trim() !== "" || "Button text cannot be empty", + }, ]; - const missingVars = requiredVars.filter(varConfig => !process.env[varConfig.name]); - + const missingVars = requiredVars.filter( + (varConfig) => !process.env[varConfig.name] + ); + if (missingVars.length > 0) { - console.log('\nāš ļø Some required information is missing. Let\'s set it up:'); + console.log("\nāš ļø Some required information is missing. Let's set it up:"); for (const varConfig of missingVars) { const { value } = await inquirer.prompt([ { - type: 'input', - name: 'value', + type: "input", + name: "value", message: varConfig.message, default: varConfig.default, - validate: varConfig.validate - } + validate: varConfig.validate, + }, ]); - + process.env[varConfig.name] = value; - - const envContent = fs.existsSync('.env') ? fs.readFileSync('.env', 'utf8') : ''; - + + const envContent = fs.existsSync(".env") + ? fs.readFileSync(".env", "utf8") + : ""; + if (!envContent.includes(`${varConfig.name}=`)) { - const newLine = envContent ? '\n' : ''; - fs.appendFileSync('.env', `${newLine}${varConfig.name}="${value.trim()}"`); - } - } - } - - // Check for seed phrase - if (!process.env.SEED_PHRASE) { - console.log('\nšŸ”‘ Mini App Manifest Signing'); - console.log('A signed manifest helps users trust your mini app.'); - const { seedPhrase } = await inquirer.prompt([ - { - type: 'password', - name: 'seedPhrase', - message: 'Enter your Farcaster custody account seed phrase to sign the mini app manifest\n(optional -- leave blank to create an unsigned mini app)\n\nSeed phrase:', - default: null - } - ]); - - if (seedPhrase) { - process.env.SEED_PHRASE = seedPhrase; - - const { storeSeedPhrase } = await inquirer.prompt([ - { - type: 'confirm', - name: 'storeSeedPhrase', - message: 'Would you like to store this seed phrase in .env.local for future use?', - default: false - } - ]); - - if (storeSeedPhrase) { - fs.appendFileSync('.env.local', `\nSEED_PHRASE="${seedPhrase}"`); - console.log('āœ… Seed phrase stored in .env.local'); - } else { - console.log('ā„¹ļø Seed phrase will only be used for this deployment'); + const newLine = envContent ? "\n" : ""; + fs.appendFileSync( + ".env", + `${newLine}${varConfig.name}="${value.trim()}"` + ); } // Ask about sponsor signer if SEED_PHRASE is provided @@ -260,9 +176,9 @@ async function checkRequiredEnvVars() { async function getGitRemote() { try { - const remoteUrl = execSync('git remote get-url origin', { + const remoteUrl = execSync("git remote get-url origin", { cwd: projectRoot, - encoding: 'utf8' + encoding: "utf8", }).trim(); return remoteUrl; } catch (error) { @@ -272,9 +188,9 @@ async function getGitRemote() { async function checkVercelCLI() { try { - execSync('vercel --version', { - stdio: 'ignore', - shell: process.platform === 'win32' + execSync("vercel --version", { + stdio: "ignore", + shell: process.platform === "win32", }); return true; } catch (error) { @@ -283,95 +199,101 @@ async function checkVercelCLI() { } async function installVercelCLI() { - console.log('Installing Vercel CLI...'); - execSync('npm install -g vercel', { - stdio: 'inherit', - shell: process.platform === 'win32' + console.log("Installing Vercel CLI..."); + execSync("npm install -g vercel", { + stdio: "inherit", + shell: process.platform === "win32", }); } async function getVercelToken() { try { // Try to get token from Vercel CLI config - const configPath = path.join(os.homedir(), '.vercel', 'auth.json'); + const configPath = path.join(os.homedir(), ".vercel", "auth.json"); if (fs.existsSync(configPath)) { - const authConfig = JSON.parse(fs.readFileSync(configPath, 'utf8')); + const authConfig = JSON.parse(fs.readFileSync(configPath, "utf8")); return authConfig.token; } } catch (error) { - console.warn('Could not read Vercel token from config file'); + console.warn("Could not read Vercel token from config file"); } - + // Try environment variable if (process.env.VERCEL_TOKEN) { return process.env.VERCEL_TOKEN; } - + // Try to extract from vercel whoami try { - const whoamiOutput = execSync('vercel whoami', { - encoding: 'utf8', - stdio: 'pipe' + const whoamiOutput = execSync("vercel whoami", { + encoding: "utf8", + stdio: "pipe", }); - + // If we can get whoami, we're logged in, but we need the actual token // The token isn't directly exposed, so we'll need to use CLI for some operations - console.log('āœ… Verified Vercel CLI authentication'); + console.log("āœ… Verified Vercel CLI authentication"); return null; // We'll fall back to CLI operations } catch (error) { - throw new Error('Not logged in to Vercel CLI. Please run this script again to login.'); + throw new Error( + "Not logged in to Vercel CLI. Please run this script again to login." + ); } } async function loginToVercel() { - console.log('\nšŸ”‘ Vercel Login'); - console.log('You can either:'); - console.log('1. Log in to an existing Vercel account'); - console.log('2. Create a new Vercel account during login\n'); - console.log('If creating a new account:'); + console.log("\nšŸ”‘ Vercel Login"); + console.log("You can either:"); + console.log("1. Log in to an existing Vercel account"); + console.log("2. Create a new Vercel account during login\n"); + console.log("If creating a new account:"); console.log('1. Click "Continue with GitHub"'); - console.log('2. Authorize GitHub access'); - console.log('3. Complete the Vercel account setup in your browser'); - console.log('4. Return here once your Vercel account is created\n'); - console.log('\nNote: you may need to cancel this script with ctrl+c and run it again if creating a new vercel account'); - - const child = spawn('vercel', ['login'], { - stdio: 'inherit' + console.log("2. Authorize GitHub access"); + console.log("3. Complete the Vercel account setup in your browser"); + console.log("4. Return here once your Vercel account is created\n"); + console.log( + "\nNote: you may need to cancel this script with ctrl+c and run it again if creating a new vercel account" + ); + + const child = spawn("vercel", ["login"], { + stdio: "inherit", }); await new Promise((resolve, reject) => { - child.on('close', (code) => { + child.on("close", (code) => { resolve(); }); }); - console.log('\nšŸ“± Waiting for login to complete...'); - console.log('If you\'re creating a new account, please complete the Vercel account setup in your browser first.'); - + console.log("\nšŸ“± Waiting for login to complete..."); + console.log( + "If you're creating a new account, please complete the Vercel account setup in your browser first." + ); + for (let i = 0; i < 150; i++) { try { - execSync('vercel whoami', { stdio: 'ignore' }); - console.log('āœ… Successfully logged in to Vercel!'); + execSync("vercel whoami", { stdio: "ignore" }); + console.log("āœ… Successfully logged in to Vercel!"); return true; } catch (error) { - if (error.message.includes('Account not found')) { - console.log('ā„¹ļø Waiting for Vercel account setup to complete...'); + if (error.message.includes("Account not found")) { + console.log("ā„¹ļø Waiting for Vercel account setup to complete..."); } - await new Promise(resolve => setTimeout(resolve, 2000)); + await new Promise((resolve) => setTimeout(resolve, 2000)); } } - console.error('\nāŒ Login timed out. Please ensure you have:'); - console.error('1. Completed the Vercel account setup in your browser'); - console.error('2. Authorized the GitHub integration'); - console.error('Then try running this script again.'); + console.error("\nāŒ Login timed out. Please ensure you have:"); + console.error("1. Completed the Vercel account setup in your browser"); + console.error("2. Authorized the GitHub integration"); + console.error("Then try running this script again."); return false; } async function setVercelEnvVarSDK(vercelClient, projectId, key, value) { try { let processedValue; - if (typeof value === 'object') { + if (typeof value === "object") { processedValue = JSON.stringify(value); } else { processedValue = value.toString(); @@ -379,11 +301,11 @@ async function setVercelEnvVarSDK(vercelClient, projectId, key, value) { // Get existing environment variables const existingVars = await vercelClient.projects.getEnvironmentVariables({ - idOrName: projectId + idOrName: projectId, }); - const existingVar = existingVars.envs?.find(env => - env.key === key && env.target?.includes('production') + const existingVar = existingVars.envs?.find( + (env) => env.key === key && env.target?.includes("production") ); if (existingVar) { @@ -393,8 +315,8 @@ async function setVercelEnvVarSDK(vercelClient, projectId, key, value) { id: existingVar.id, requestBody: { value: processedValue, - target: ['production'] - } + target: ["production"], + }, }); console.log(`āœ… Updated environment variable: ${key}`); } else { @@ -404,16 +326,19 @@ async function setVercelEnvVarSDK(vercelClient, projectId, key, value) { requestBody: { key: key, value: processedValue, - type: 'encrypted', - target: ['production'] - } + type: "encrypted", + target: ["production"], + }, }); console.log(`āœ… Created environment variable: ${key}`); } - + return true; } catch (error) { - console.warn(`āš ļø Warning: Failed to set environment variable ${key}:`, error.message); + console.warn( + `āš ļø Warning: Failed to set environment variable ${key}:`, + error.message + ); return false; } } @@ -424,15 +349,15 @@ async function setVercelEnvVarCLI(key, value, projectRoot) { try { execSync(`vercel env rm ${key} production -y`, { cwd: projectRoot, - stdio: 'ignore', - env: process.env + stdio: "ignore", + env: process.env, }); } catch (error) { // Ignore errors from removal } let processedValue; - if (typeof value === 'object') { + if (typeof value === "object") { processedValue = JSON.stringify(value); } else { processedValue = value.toString(); @@ -440,11 +365,11 @@ async function setVercelEnvVarCLI(key, value, projectRoot) { // Create temporary file const tempFilePath = path.join(projectRoot, `${key}_temp.txt`); - fs.writeFileSync(tempFilePath, processedValue, 'utf8'); + fs.writeFileSync(tempFilePath, processedValue, "utf8"); // Use appropriate command based on platform let command; - if (process.platform === 'win32') { + if (process.platform === "win32") { command = `type "${tempFilePath}" | vercel env add ${key} production`; } else { command = `cat "${tempFilePath}" | vercel env add ${key} production`; @@ -452,9 +377,9 @@ async function setVercelEnvVarCLI(key, value, projectRoot) { execSync(command, { cwd: projectRoot, - stdio: 'pipe', // Changed from 'inherit' to avoid interactive prompts + stdio: "pipe", // Changed from 'inherit' to avoid interactive prompts shell: true, - env: process.env + env: process.env, }); fs.unlinkSync(tempFilePath); @@ -465,57 +390,113 @@ async function setVercelEnvVarCLI(key, value, projectRoot) { if (fs.existsSync(tempFilePath)) { fs.unlinkSync(tempFilePath); } - console.warn(`āš ļø Warning: Failed to set environment variable ${key}:`, error.message); + console.warn( + `āš ļø Warning: Failed to set environment variable ${key}:`, + error.message + ); return false; } } -async function setEnvironmentVariables(vercelClient, projectId, envVars, projectRoot) { - console.log('\nšŸ“ Setting up environment variables...'); - +async function setEnvironmentVariables( + vercelClient, + projectId, + envVars, + projectRoot +) { + console.log("\nšŸ“ Setting up environment variables..."); + const results = []; - + for (const [key, value] of Object.entries(envVars)) { if (!value) continue; - + let success = false; - + // Try SDK approach first if we have a Vercel client if (vercelClient && projectId) { success = await setVercelEnvVarSDK(vercelClient, projectId, key, value); } - + // Fallback to CLI approach if (!success) { success = await setVercelEnvVarCLI(key, value, projectRoot); } - + results.push({ key, success }); } - + // Report results - const failed = results.filter(r => !r.success); + const failed = results.filter((r) => !r.success); if (failed.length > 0) { console.warn(`\nāš ļø Failed to set ${failed.length} environment variables:`); - failed.forEach(r => console.warn(` - ${r.key}`)); - console.warn('\nYou may need to set these manually in the Vercel dashboard.'); + failed.forEach((r) => console.warn(` - ${r.key}`)); + console.warn( + "\nYou may need to set these manually in the Vercel dashboard." + ); + } + + return results; +} + +async function waitForDeployment(vercelClient, projectId, maxWaitTime = 300000) { // 5 minutes + console.log('\nā³ Waiting for deployment to complete...'); + const startTime = Date.now(); + + while (Date.now() - startTime < maxWaitTime) { + try { + const deployments = await vercelClient.deployments.list({ + projectId: projectId, + limit: 1 + }); + + if (deployments.deployments?.[0]) { + const deployment = deployments.deployments[0]; + console.log(`šŸ“Š Deployment status: ${deployment.state}`); + + if (deployment.state === 'READY') { + console.log('āœ… Deployment completed successfully!'); + return deployment; + } else if (deployment.state === 'ERROR') { + throw new Error(`Deployment failed with state: ${deployment.state}`); + } else if (deployment.state === 'CANCELED') { + throw new Error('Deployment was canceled'); + } + + // Still building, wait and check again + await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds + } else { + console.log('ā³ No deployment found yet, waiting...'); + await new Promise(resolve => setTimeout(resolve, 5000)); + } + } catch (error) { + console.warn('āš ļø Could not check deployment status:', error.message); + await new Promise(resolve => setTimeout(resolve, 5000)); + } } - return results; + throw new Error('Deployment timed out after 5 minutes'); } async function deployToVercel(useGitHub = false) { try { - console.log('\nšŸš€ Deploying to Vercel...'); - + console.log("\nšŸš€ Deploying to Vercel..."); + // Ensure vercel.json exists - const vercelConfigPath = path.join(projectRoot, 'vercel.json'); + const vercelConfigPath = path.join(projectRoot, "vercel.json"); if (!fs.existsSync(vercelConfigPath)) { - console.log('šŸ“ Creating vercel.json configuration...'); - fs.writeFileSync(vercelConfigPath, JSON.stringify({ - buildCommand: "next build", - framework: "nextjs" - }, null, 2)); + console.log("šŸ“ Creating vercel.json configuration..."); + fs.writeFileSync( + vercelConfigPath, + JSON.stringify( + { + buildCommand: "next build", + framework: "nextjs", + }, + null, + 2 + ) + ); } // Set up Vercel project @@ -523,15 +504,42 @@ async function deployToVercel(useGitHub = false) { console.log('An initial deployment is required to get an assigned domain that can be used in the mini app manifest\n'); console.log('\nāš ļø Note: choosing a longer, more unique project name will help avoid conflicts with other existing domains\n'); - execSync('vercel', { + // Use spawn instead of execSync for better error handling + const { spawn } = await import('child_process'); + const vercelSetup = spawn('vercel', [], { cwd: projectRoot, - stdio: 'inherit', - shell: process.platform === 'win32' + stdio: "inherit", + shell: process.platform === "win32", }); + await new Promise((resolve, reject) => { + vercelSetup.on('close', (code) => { + if (code === 0 || code === null) { + console.log('āœ… Vercel project setup completed'); + resolve(); + } else { + console.log('āš ļø Vercel setup command completed (this is normal)'); + resolve(); // Don't reject, as this is often expected + } + }); + + vercelSetup.on('error', (error) => { + console.log('āš ļø Vercel setup command completed (this is normal)'); + resolve(); // Don't reject, as this is often expected + }); + }); + + // Wait a moment for project files to be written + await new Promise(resolve => setTimeout(resolve, 2000)); + // Load project info - const projectJson = JSON.parse(fs.readFileSync('.vercel/project.json', 'utf8')); - const projectId = projectJson.projectId; + let projectId; + try { + const projectJson = JSON.parse(fs.readFileSync('.vercel/project.json', 'utf8')); + projectId = projectJson.projectId; + } catch (error) { + throw new Error('Failed to load project info. Please ensure the Vercel project was created successfully.'); + } // Get Vercel token and initialize SDK client let vercelClient = null; @@ -539,144 +547,189 @@ async function deployToVercel(useGitHub = false) { const token = await getVercelToken(); if (token) { vercelClient = new Vercel({ - bearerToken: token + bearerToken: token, }); - console.log('āœ… Initialized Vercel SDK client'); + console.log("āœ… Initialized Vercel SDK client"); } } catch (error) { - console.warn('āš ļø Could not initialize Vercel SDK, falling back to CLI operations'); + console.warn( + "āš ļø Could not initialize Vercel SDK, falling back to CLI operations" + ); } // Get project details - console.log('\nšŸ” Getting project details...'); + console.log("\nšŸ” Getting project details..."); let domain; let projectName; if (vercelClient) { try { const project = await vercelClient.projects.get({ - idOrName: projectId + idOrName: projectId, }); projectName = project.name; domain = `${projectName}.vercel.app`; - console.log('🌐 Using project name for domain:', domain); + console.log("🌐 Using project name for domain:", domain); } catch (error) { - console.warn('āš ļø Could not get project details via SDK, using CLI fallback'); + console.warn( + "āš ļø Could not get project details via SDK, using CLI fallback" + ); } } // Fallback to CLI method if SDK failed if (!domain) { - const inspectOutput = execSync(`vercel project inspect ${projectId} 2>&1`, { - cwd: projectRoot, - encoding: 'utf8' - }); + try { + const inspectOutput = execSync(`vercel project inspect ${projectId} 2>&1`, { + cwd: projectRoot, + encoding: 'utf8' + }); - const nameMatch = inspectOutput.match(/Name\s+([^\n]+)/); - if (nameMatch) { - projectName = nameMatch[1].trim(); - domain = `${projectName}.vercel.app`; - console.log('🌐 Using project name for domain:', domain); - } else { - const altMatch = inspectOutput.match(/Found Project [^/]+\/([^\n]+)/); - if (altMatch) { - projectName = altMatch[1].trim(); + const nameMatch = inspectOutput.match(/Name\s+([^\n]+)/); + if (nameMatch) { + projectName = nameMatch[1].trim(); domain = `${projectName}.vercel.app`; - console.log('🌐 Using project name for domain:', domain); + console.log("🌐 Using project name for domain:", domain); } else { - throw new Error('Could not determine project name from inspection output'); + const altMatch = inspectOutput.match(/Found Project [^/]+\/([^\n]+)/); + if (altMatch) { + projectName = altMatch[1].trim(); + domain = `${projectName}.vercel.app`; + console.log('🌐 Using project name for domain:', domain); + } else { + console.warn('āš ļø Could not determine project name from inspection, using fallback'); + // Use a fallback domain based on project ID + domain = `project-${projectId.slice(-8)}.vercel.app`; + console.log('🌐 Using fallback domain:', domain); + } } + } catch (error) { + console.warn('āš ļø Could not inspect project, using fallback domain'); + // Use a fallback domain based on project ID + domain = `project-${projectId.slice(-8)}.vercel.app`; + console.log('🌐 Using fallback domain:', domain); } } - // Generate mini app metadata if we have a seed phrase - let miniAppMetadata; - let fid; - if (process.env.SEED_PHRASE) { - console.log('\nšŸ”Ø Generating mini app metadata...'); - const accountAddress = await validateSeedPhrase(process.env.SEED_PHRASE); - fid = await lookupFidByCustodyAddress(accountAddress, process.env.NEYNAR_API_KEY ?? 'FARCASTER_V2_FRAMES_DEMO'); - - const webhookUrl = process.env.NEYNAR_API_KEY && process.env.NEYNAR_CLIENT_ID + // Generate mini app metadata + console.log("\nšŸ”Ø Generating mini app metadata..."); + + const webhookUrl = + process.env.NEYNAR_API_KEY && process.env.NEYNAR_CLIENT_ID ? `https://api.neynar.com/f/app/${process.env.NEYNAR_CLIENT_ID}/event` : `https://${domain}/api/webhook`; - miniAppMetadata = await generateFarcasterMetadata(domain, fid, accountAddress, process.env.SEED_PHRASE, webhookUrl); - console.log('āœ… Mini app metadata generated and signed'); - } + const miniAppMetadata = await generateFarcasterMetadata(domain, webhookUrl); + console.log("āœ… Mini app metadata generated"); // Prepare environment variables - const nextAuthSecret = process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString('hex'); + const nextAuthSecret = + process.env.NEXTAUTH_SECRET || crypto.randomBytes(32).toString("hex"); const vercelEnv = { NEXTAUTH_SECRET: nextAuthSecret, AUTH_SECRET: nextAuthSecret, NEXTAUTH_URL: `https://${domain}`, NEXT_PUBLIC_URL: `https://${domain}`, - - ...(process.env.NEYNAR_API_KEY && { NEYNAR_API_KEY: process.env.NEYNAR_API_KEY }), - ...(process.env.NEYNAR_CLIENT_ID && { NEYNAR_CLIENT_ID: process.env.NEYNAR_CLIENT_ID }), + + ...(process.env.NEYNAR_API_KEY && { + NEYNAR_API_KEY: process.env.NEYNAR_API_KEY, + }), + ...(process.env.NEYNAR_CLIENT_ID && { + NEYNAR_CLIENT_ID: process.env.NEYNAR_CLIENT_ID, + }), ...(process.env.SPONSOR_SIGNER && { SPONSOR_SIGNER: process.env.SPONSOR_SIGNER }), ...(miniAppMetadata && { MINI_APP_METADATA: miniAppMetadata }), - + ...Object.fromEntries( - Object.entries(process.env) - .filter(([key]) => key.startsWith('NEXT_PUBLIC_')) - ) + Object.entries(process.env).filter(([key]) => + key.startsWith("NEXT_PUBLIC_") + ) + ), }; // Set environment variables - await setEnvironmentVariables(vercelClient, projectId, vercelEnv, projectRoot); + await setEnvironmentVariables( + vercelClient, + projectId, + vercelEnv, + projectRoot + ); // Deploy the project if (useGitHub) { - console.log('\nSetting up GitHub integration...'); - execSync('vercel link', { + console.log("\nSetting up GitHub integration..."); + execSync("vercel link", { cwd: projectRoot, - stdio: 'inherit', - env: process.env + stdio: "inherit", + env: process.env, }); - console.log('\nšŸ“¦ Deploying with GitHub integration...'); + console.log("\nšŸ“¦ Deploying with GitHub integration..."); } else { - console.log('\nšŸ“¦ Deploying local code directly...'); + console.log("\nšŸ“¦ Deploying local code directly..."); } - execSync('vercel deploy --prod', { + // Use spawn for better control over the deployment process + const vercelDeploy = spawn('vercel', ['deploy', '--prod'], { cwd: projectRoot, - stdio: 'inherit', - env: process.env + stdio: "inherit", + env: process.env, }); - // Verify actual domain after deployment - console.log('\nšŸ” Verifying deployment domain...'); - - let actualDomain = domain; + await new Promise((resolve, reject) => { + vercelDeploy.on('close', (code) => { + if (code === 0) { + console.log('āœ… Vercel deployment command completed'); + resolve(); + } else { + console.error(`āŒ Vercel deployment failed with code: ${code}`); + reject(new Error(`Vercel deployment failed with exit code: ${code}`)); + } + }); + + vercelDeploy.on('error', (error) => { + console.error('āŒ Vercel deployment error:', error.message); + reject(error); + }); + }); + + // Wait for deployment to actually complete + let deployment; if (vercelClient) { try { - const deployments = await vercelClient.deployments.list({ - projectId: projectId, - limit: 1 - }); - - if (deployments.deployments?.[0]?.url) { - actualDomain = deployments.deployments[0].url; - console.log('🌐 Verified actual domain:', actualDomain); - } + deployment = await waitForDeployment(vercelClient, projectId); } catch (error) { - console.warn('āš ļø Could not verify domain via SDK, using assumed domain'); + console.warn('āš ļø Could not verify deployment completion:', error.message); + console.log('ā„¹ļø Proceeding with domain verification...'); + } + } + + // Verify actual domain after deployment + console.log("\nšŸ” Verifying deployment domain..."); + + let actualDomain = domain; + if (vercelClient && deployment) { + try { + actualDomain = deployment.url || domain; + console.log('🌐 Verified actual domain:', actualDomain); + } catch (error) { + console.warn( + "āš ļø Could not verify domain via SDK, using assumed domain" + ); } } // Update environment variables if domain changed if (actualDomain !== domain) { - console.log('šŸ”„ Updating environment variables with correct domain...'); - - const webhookUrl = process.env.NEYNAR_API_KEY && process.env.NEYNAR_CLIENT_ID - ? `https://api.neynar.com/f/app/${process.env.NEYNAR_CLIENT_ID}/event` - : `https://${actualDomain}/api/webhook`; + console.log("šŸ”„ Updating environment variables with correct domain..."); + + const webhookUrl = + process.env.NEYNAR_API_KEY && process.env.NEYNAR_CLIENT_ID + ? `https://api.neynar.com/f/app/${process.env.NEYNAR_CLIENT_ID}/event` + : `https://${actualDomain}/api/webhook`; const updatedEnv = { NEXTAUTH_URL: `https://${actualDomain}`, - NEXT_PUBLIC_URL: `https://${actualDomain}` + NEXT_PUBLIC_URL: `https://${actualDomain}`, }; if (miniAppMetadata) { @@ -687,45 +740,65 @@ async function deployToVercel(useGitHub = false) { await setEnvironmentVariables(vercelClient, projectId, updatedEnv, projectRoot); console.log('\nšŸ“¦ Redeploying with correct domain...'); - execSync('vercel deploy --prod', { + const vercelRedeploy = spawn('vercel', ['deploy', '--prod'], { cwd: projectRoot, - stdio: 'inherit', - env: process.env + stdio: "inherit", + env: process.env, + }); + + await new Promise((resolve, reject) => { + vercelRedeploy.on('close', (code) => { + if (code === 0) { + console.log('āœ… Redeployment completed'); + resolve(); + } else { + console.error(`āŒ Redeployment failed with code: ${code}`); + reject(new Error(`Redeployment failed with exit code: ${code}`)); + } + }); + + vercelRedeploy.on('error', (error) => { + console.error('āŒ Redeployment error:', error.message); + reject(error); + }); }); domain = actualDomain; } - - console.log('\n✨ Deployment complete! Your mini app is now live at:'); - console.log(`🌐 https://${domain}`); - console.log('\nšŸ“ You can manage your project at https://vercel.com/dashboard'); + console.log("\n✨ Deployment complete! Your mini app is now live at:"); + console.log(`🌐 https://${domain}`); + console.log( + "\nšŸ“ You can manage your project at https://vercel.com/dashboard" + ); } catch (error) { - console.error('\nāŒ Deployment failed:', error.message); + console.error("\nāŒ Deployment failed:", error.message); process.exit(1); } } async function main() { try { - console.log('šŸš€ Vercel Mini App Deployment (SDK Edition)'); - console.log('This script will deploy your mini app to Vercel using the Vercel SDK.'); - console.log('\nThe script will:'); - console.log('1. Check for required environment variables'); - console.log('2. Set up a Vercel project (new or existing)'); - console.log('3. Configure environment variables in Vercel using SDK'); - console.log('4. Deploy and build your mini app\n'); + console.log("šŸš€ Vercel Mini App Deployment (SDK Edition)"); + console.log( + "This script will deploy your mini app to Vercel using the Vercel SDK." + ); + console.log("\nThe script will:"); + console.log("1. Check for required environment variables"); + console.log("2. Set up a Vercel project (new or existing)"); + console.log("3. Configure environment variables in Vercel using SDK"); + console.log("4. Deploy and build your mini app\n"); // Check if @vercel/sdk is installed try { - await import('@vercel/sdk'); + await import("@vercel/sdk"); } catch (error) { - console.log('šŸ“¦ Installing @vercel/sdk...'); - execSync('npm install @vercel/sdk', { + console.log("šŸ“¦ Installing @vercel/sdk..."); + execSync("npm install @vercel/sdk", { cwd: projectRoot, - stdio: 'inherit' + stdio: "inherit", }); - console.log('āœ… @vercel/sdk installed successfully'); + console.log("āœ… @vercel/sdk installed successfully"); } await checkRequiredEnvVars(); @@ -734,58 +807,57 @@ async function main() { let useGitHub = false; if (remoteUrl) { - console.log('\nšŸ“¦ Found GitHub repository:', remoteUrl); + console.log("\nšŸ“¦ Found GitHub repository:", remoteUrl); const { useGitHubDeploy } = await inquirer.prompt([ { - type: 'confirm', - name: 'useGitHubDeploy', - message: 'Would you like to deploy from the GitHub repository?', - default: true - } + type: "confirm", + name: "useGitHubDeploy", + message: "Would you like to deploy from the GitHub repository?", + default: true, + }, ]); useGitHub = useGitHubDeploy; } else { - console.log('\nāš ļø No GitHub repository found.'); + console.log("\nāš ļø No GitHub repository found."); const { action } = await inquirer.prompt([ { - type: 'list', - name: 'action', - message: 'What would you like to do?', + type: "list", + name: "action", + message: "What would you like to do?", choices: [ - { name: 'Deploy local code directly', value: 'deploy' }, - { name: 'Set up GitHub repository first', value: 'setup' } + { name: "Deploy local code directly", value: "deploy" }, + { name: "Set up GitHub repository first", value: "setup" }, ], - default: 'deploy' - } + default: "deploy", + }, ]); - if (action === 'setup') { - console.log('\nšŸ‘‹ Please set up your GitHub repository first:'); - console.log('1. Create a new repository on GitHub'); - console.log('2. Run these commands:'); - console.log(' git remote add origin '); - console.log(' git push -u origin main'); - console.log('\nThen run this script again to deploy.'); + if (action === "setup") { + console.log("\nšŸ‘‹ Please set up your GitHub repository first:"); + console.log("1. Create a new repository on GitHub"); + console.log("2. Run these commands:"); + console.log(" git remote add origin "); + console.log(" git push -u origin main"); + console.log("\nThen run this script again to deploy."); process.exit(0); } } - if (!await checkVercelCLI()) { - console.log('Vercel CLI not found. Installing...'); + if (!(await checkVercelCLI())) { + console.log("Vercel CLI not found. Installing..."); await installVercelCLI(); } - if (!await loginToVercel()) { - console.error('\nāŒ Failed to log in to Vercel. Please try again.'); + if (!(await loginToVercel())) { + console.error("\nāŒ Failed to log in to Vercel. Please try again."); process.exit(1); } await deployToVercel(useGitHub); - } catch (error) { - console.error('\nāŒ Error:', error.message); + console.error("\nāŒ Error:", error.message); process.exit(1); } } -main(); \ No newline at end of file +main(); diff --git a/src/app/api/send-notification/route.ts b/src/app/api/send-notification/route.ts index 69d746e..7563723 100644 --- a/src/app/api/send-notification/route.ts +++ b/src/app/api/send-notification/route.ts @@ -1,4 +1,4 @@ -import { notificationDetailsSchema } from "@farcaster/frame-sdk"; +import { notificationDetailsSchema } from "@farcaster/miniapp-sdk"; import { NextRequest } from "next/server"; import { z } from "zod"; import { setUserNotificationDetails } from "~/lib/kv"; diff --git a/src/app/api/webhook/route.ts b/src/app/api/webhook/route.ts index a8c2ece..cc49676 100644 --- a/src/app/api/webhook/route.ts +++ b/src/app/api/webhook/route.ts @@ -2,7 +2,7 @@ import { ParseWebhookEvent, parseWebhookEvent, verifyAppKeyWithNeynar, -} from "@farcaster/frame-node"; +} from "@farcaster/miniapp-node"; import { NextRequest } from "next/server"; import { APP_NAME } from "~/lib/constants"; import { diff --git a/src/components/providers/SafeFarcasterSolanaProvider.tsx b/src/components/providers/SafeFarcasterSolanaProvider.tsx index 2d6c063..9834c95 100644 --- a/src/components/providers/SafeFarcasterSolanaProvider.tsx +++ b/src/components/providers/SafeFarcasterSolanaProvider.tsx @@ -1,6 +1,6 @@ import React, { createContext, useEffect, useState } from "react"; import dynamic from "next/dynamic"; -import { sdk } from '@farcaster/frame-sdk'; +import { sdk } from '@farcaster/miniapp-sdk'; const FarcasterSolanaProvider = dynamic( () => import('@farcaster/mini-app-solana').then(mod => mod.FarcasterSolanaProvider), diff --git a/src/components/providers/WagmiProvider.tsx b/src/components/providers/WagmiProvider.tsx index d3cf282..9fb313e 100644 --- a/src/components/providers/WagmiProvider.tsx +++ b/src/components/providers/WagmiProvider.tsx @@ -1,7 +1,7 @@ import { createConfig, http, WagmiProvider } from "wagmi"; import { base, degen, mainnet, optimism, unichain, celo } from "wagmi/chains"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { farcasterFrame } from "@farcaster/frame-wagmi-connector"; +import { farcasterFrame } from "@farcaster/miniapp-wagmi-connector"; import { coinbaseWallet, metaMask } from 'wagmi/connectors'; import { APP_NAME, APP_ICON_URL, APP_URL } from "~/lib/constants"; import { useEffect, useState } from "react"; diff --git a/src/components/ui/Header.tsx b/src/components/ui/Header.tsx index 9740001..d1339a5 100644 --- a/src/components/ui/Header.tsx +++ b/src/components/ui/Header.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { APP_NAME } from "~/lib/constants"; -import sdk from "@farcaster/frame-sdk"; +import sdk from "@farcaster/miniapp-sdk"; import { useMiniApp } from "@neynar/react"; type HeaderProps = { diff --git a/src/components/ui/Share.tsx b/src/components/ui/Share.tsx index d58ec9e..ef29ebd 100644 --- a/src/components/ui/Share.tsx +++ b/src/components/ui/Share.tsx @@ -3,7 +3,7 @@ import { useCallback, useState, useEffect } from 'react'; import { Button } from './Button'; import { useMiniApp } from '@neynar/react'; -import { type ComposeCast } from "@farcaster/frame-sdk"; +import { type ComposeCast } from "@farcaster/miniapp-sdk"; interface EmbedConfig { path?: string; diff --git a/src/components/ui/tabs/ActionsTab.tsx b/src/components/ui/tabs/ActionsTab.tsx index 1b624c4..789c5b8 100644 --- a/src/components/ui/tabs/ActionsTab.tsx +++ b/src/components/ui/tabs/ActionsTab.tsx @@ -1,11 +1,11 @@ 'use client'; -import { useCallback, useState } from 'react'; -import { useMiniApp } from '@neynar/react'; -import { ShareButton } from '../Share'; -import { Button } from '../Button'; -import { SignIn } from '../wallet/SignIn'; -import { type Haptics } from '@farcaster/frame-sdk'; +import { useCallback, useState } from "react"; +import { useMiniApp } from "@neynar/react"; +import { ShareButton } from "../Share"; +import { Button } from "../Button"; +import { SignIn } from "../wallet/SignIn"; +import { type Haptics } from "@farcaster/miniapp-sdk"; import { NeynarAuthButton } from '../NeynarAuthButton/index'; /** diff --git a/src/components/ui/tabs/HomeTab.tsx b/src/components/ui/tabs/HomeTab.tsx index aa7e37d..eb139cc 100644 --- a/src/components/ui/tabs/HomeTab.tsx +++ b/src/components/ui/tabs/HomeTab.tsx @@ -17,7 +17,7 @@ export function HomeTab() {

Put your content here!

-

Powered by Neynar 🪐

+

Powered by Neynar 🪐

); diff --git a/src/components/ui/wallet/SignIn.tsx b/src/components/ui/wallet/SignIn.tsx index 754c317..9ca825c 100644 --- a/src/components/ui/wallet/SignIn.tsx +++ b/src/components/ui/wallet/SignIn.tsx @@ -1,10 +1,10 @@ 'use client'; -import { useCallback, useState } from 'react'; -import { signIn, signOut, getCsrfToken } from 'next-auth/react'; -import sdk, { SignIn as SignInCore } from '@farcaster/frame-sdk'; -import { useSession } from 'next-auth/react'; -import { Button } from '../Button'; +import { useCallback, useState } from "react"; +import { signIn, signOut, getCsrfToken } from "next-auth/react"; +import sdk, { SignIn as SignInCore } from "@farcaster/miniapp-sdk"; +import { useSession } from "next-auth/react"; +import { Button } from "../Button"; /** * SignIn component handles Farcaster authentication using Sign-In with Farcaster (SIWF). @@ -131,9 +131,9 @@ export function SignIn() { {/* Session Information */} {session && ( -
-
Session
-
+
+
Session
+
{JSON.stringify(session, null, 2)}
@@ -141,17 +141,17 @@ export function SignIn() { {/* Error Display */} {signInFailure && !authState.signingIn && ( -
-
SIWF Result
-
{signInFailure}
+
+
SIWF Result
+
{signInFailure}
)} {/* Success Result Display */} {signInResult && !authState.signingIn && ( -
-
SIWF Result
-
+
+
SIWF Result
+
{JSON.stringify(signInResult, null, 2)}
diff --git a/src/lib/kv.ts b/src/lib/kv.ts index 55477e9..eefc680 100644 --- a/src/lib/kv.ts +++ b/src/lib/kv.ts @@ -1,4 +1,4 @@ -import { FrameNotificationDetails } from "@farcaster/frame-sdk"; +import { FrameNotificationDetails } from "@farcaster/miniapp-sdk"; import { Redis } from "@upstash/redis"; import { APP_NAME } from "./constants"; diff --git a/src/lib/notifs.ts b/src/lib/notifs.ts index a04b24e..72007b1 100644 --- a/src/lib/notifs.ts +++ b/src/lib/notifs.ts @@ -1,7 +1,7 @@ import { SendNotificationRequest, sendNotificationResponseSchema, -} from "@farcaster/frame-sdk"; +} from "@farcaster/miniapp-sdk"; import { getUserNotificationDetails } from "~/lib/kv"; import { APP_URL } from "./constants"; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 85168a0..0762d67 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,8 +1,19 @@ -import { type ClassValue, clsx } from 'clsx'; -import { twMerge } from 'tailwind-merge'; -import { mnemonicToAccount } from 'viem/accounts'; -import { APP_BUTTON_TEXT, APP_DESCRIPTION, APP_ICON_URL, APP_NAME, APP_OG_IMAGE_URL, APP_PRIMARY_CATEGORY, APP_SPLASH_BACKGROUND_COLOR, APP_TAGS, APP_URL, APP_WEBHOOK_URL } from './constants'; -import { APP_SPLASH_URL } from './constants'; +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; +import { mnemonicToAccount } from "viem/accounts"; +import { + APP_BUTTON_TEXT, + APP_DESCRIPTION, + APP_ICON_URL, + APP_NAME, + APP_OG_IMAGE_URL, + APP_PRIMARY_CATEGORY, + APP_SPLASH_BACKGROUND_COLOR, + APP_TAGS, + APP_URL, + APP_WEBHOOK_URL, +} from "./constants"; +import { APP_SPLASH_URL } from "./constants"; interface MiniAppMetadata { version: string; @@ -17,7 +28,7 @@ interface MiniAppMetadata { description?: string; primaryCategory?: string; tags?: string[]; -}; +} interface MiniAppManifest { accountAssociation?: { @@ -32,17 +43,6 @@ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } -export function getSecretEnvVars() { - const seedPhrase = process.env.SEED_PHRASE; - const fid = process.env.FID; - - if (!seedPhrase || !fid) { - return null; - } - - return { seedPhrase, fid }; -} - export function getMiniAppEmbedMetadata(ogImageUrl?: string) { return { version: "next", @@ -69,58 +69,30 @@ export async function getFarcasterMetadata(): Promise { if (process.env.MINI_APP_METADATA) { try { const metadata = JSON.parse(process.env.MINI_APP_METADATA); - console.log('Using pre-signed mini app metadata from environment'); + console.log("Using pre-signed mini app metadata from environment"); return metadata; } catch (error) { - console.warn('Failed to parse MINI_APP_METADATA from environment:', error); + console.warn( + "Failed to parse MINI_APP_METADATA from environment:", + error + ); } } if (!APP_URL) { - throw new Error('NEXT_PUBLIC_URL not configured'); + throw new Error("NEXT_PUBLIC_URL not configured"); } // Get the domain from the URL (without https:// prefix) const domain = new URL(APP_URL).hostname; - console.log('Using domain for manifest:', domain); - - const secretEnvVars = getSecretEnvVars(); - if (!secretEnvVars) { - console.warn('No seed phrase or FID found in environment variables -- generating unsigned metadata'); - } - - let accountAssociation; - if (secretEnvVars) { - // Generate account from seed phrase - const account = mnemonicToAccount(secretEnvVars.seedPhrase); - const custodyAddress = account.address; - - const header = { - fid: parseInt(secretEnvVars.fid), - type: 'custody', - key: custodyAddress, - }; - const encodedHeader = Buffer.from(JSON.stringify(header), 'utf-8').toString('base64'); - - const payload = { - domain - }; - const encodedPayload = Buffer.from(JSON.stringify(payload), 'utf-8').toString('base64url'); - - const signature = await account.signMessage({ - message: `${encodedHeader}.${encodedPayload}` - }); - const encodedSignature = Buffer.from(signature, 'utf-8').toString('base64url'); - - accountAssociation = { - header: encodedHeader, - payload: encodedPayload, - signature: encodedSignature - }; - } + console.log("Using domain for manifest:", domain); return { - accountAssociation, + accountAssociation: { + header: "", + payload: "", + signature: "", + }, frame: { version: "1", name: APP_NAME ?? "Neynar Starter Kit",