UNPKG

create-better-t-stack

Version:

A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations

265 lines (218 loc) 65.5 kB
#!/usr/bin/env node import{cancel as j,intro as us,log as I,outro as ms,spinner as fs}from"@clack/prompts";import{Command as hs}from"commander";import h from"picocolors";import we from"node:path";import{fileURLToPath as zt}from"node:url";var K=()=>{let e=process.env.npm_config_user_agent;return e?.startsWith("pnpm")?"pnpm":e?.startsWith("bun")?"bun":"npm"};var Nt=zt(import.meta.url),Ut=we.dirname(Nt),v=we.join(Ut,"../"),y={projectName:"my-better-t-app",frontend:["tanstack-router"],database:"sqlite",orm:"drizzle",auth:!0,addons:[],examples:[],git:!0,packageManager:K(),noInstall:!1,dbSetup:"none",backend:"hono",runtime:"bun"},ne={"better-auth":"^1.2.4","@better-auth/expo":"^1.2.5","drizzle-orm":"^0.38.4","drizzle-kit":"^0.30.5","@libsql/client":"^0.14.0",pg:"^8.14.1","@types/pg":"^8.11.11",mysql2:"^3.14.0","@prisma/client":"^6.5.0",prisma:"^6.5.0","vite-plugin-pwa":"^0.21.2","@vite-pwa/assets-generator":"^0.2.6","@tauri-apps/cli":"^2.4.0","@biomejs/biome":"1.9.4",husky:"^9.1.7","lint-staged":"^15.5.0","@hono/node-server":"^1.14.0",tsx:"^4.19.2","@types/node":"^22.13.11","@types/bun":"^1.2.6","@elysiajs/node":"^1.2.6","@elysiajs/cors":"^1.2.0","@elysiajs/trpc":"^1.1.0",elysia:"^1.2.25","@hono/trpc-server":"^0.3.4",hono:"^4.7.5",cors:"^2.8.5",express:"^5.1.0","@types/express":"^5.0.1","@types/cors":"^2.8.17",ai:"^4.2.8","@ai-sdk/google":"^1.2.3","@prisma/extension-accelerate":"^1.3.0"};import or from"node:path";import{cancel as ir,spinner as cr}from"@clack/prompts";import pr from"fs-extra";import ot from"picocolors";import F from"node:path";import k from"fs-extra";import _t from"node:path";import Pe from"fs-extra";var g=e=>{let{dependencies:a=[],devDependencies:t=[],projectDir:r}=e,s=_t.join(r,"package.json"),n=Pe.readJSONSync(s);n.dependencies||(n.dependencies={}),n.devDependencies||(n.devDependencies={});for(let o of a){let i=ne[o];n.dependencies[o]=i}for(let o of t){let i=ne[o];n.devDependencies[o]=i}Pe.writeJSONSync(s,n,{spaces:2})};import qt from"node:path";import{log as Vt,spinner as Gt}from"@clack/prompts";import{execa as Wt}from"execa";import ye from"picocolors";async function je(e,a){let t=Gt();try{t.start("Setting up Starlight documentation site...");let r,s;switch(a){case"npm":r="npx",s=["create-astro@latest"];break;case"pnpm":r="pnpm",s=["dlx","create-astro@latest"];break;case"bun":r="bunx",s=["create-astro@latest"];break;default:r="npx",s=["create-astro@latest"]}s=[...s,"docs","--template","starlight","--no-install","--add","tailwind","--no-git","--skip-houston"],await Wt(r,s,{cwd:qt.join(e,"apps"),env:{CI:"true"}}),t.stop("Starlight documentation site setup successfully!")}catch(r){throw t.stop(ye.red("Failed to set up Starlight documentation site")),r instanceof Error&&Vt.error(ye.red(r.message)),r}}import X from"node:path";import{log as Jt,spinner as Ht}from"@clack/prompts";import{execa as Qt}from"execa";import Y from"fs-extra";import ve from"picocolors";async function ke(e,a,t){let r=Ht(),s=X.join(e,"apps/web");if(await Y.pathExists(s))try{r.start("Setting up Tauri desktop app support..."),g({devDependencies:["@tauri-apps/cli"],projectDir:s});let n=X.join(s,"package.json");if(await Y.pathExists(n)){let d=await Y.readJson(n);d.scripts={...d.scripts,tauri:"tauri","desktop:dev":"tauri dev","desktop:build":"tauri build"},await Y.writeJson(n,d,{spaces:2})}let o,i;switch(a){case"npm":o="npx",i=["@tauri-apps/cli@latest"];break;case"pnpm":o="pnpm",i=["dlx","@tauri-apps/cli@latest"];break;case"bun":o="bunx",i=["@tauri-apps/cli@latest"];break;default:o="npx",i=["@tauri-apps/cli@latest"]}let c=t.includes("react-router")?"http://localhost:5173":"http://localhost:3001";i=[...i,"init",`--app-name=${X.basename(e)}`,`--window-title=${X.basename(e)}`,"--frontend-dist=dist",`--dev-url=${c}`,`--before-dev-command=${a} run dev`,`--before-build-command=${a} run build`],await Qt(o,i,{cwd:s,env:{CI:"true"}}),r.stop("Tauri desktop app support configured successfully!")}catch(n){throw r.stop(ve.red("Failed to set up Tauri")),n instanceof Error&&Jt.error(ve.red(n.message)),n}}async function xe(e,a,t,r){let s=r.includes("react-router")||r.includes("tanstack-router");a.includes("pwa")&&s&&await Zt(e,r),a.includes("tauri")&&s&&await ke(e,t,r),a.includes("biome")&&await Xt(e),a.includes("husky")&&await Yt(e),a.includes("starlight")&&await je(e,t)}function Kt(e,a){return F.join(e,"apps/web")}async function Xt(e){let a=F.join(v,"template/with-biome");await k.pathExists(a)&&await k.copy(a,e,{overwrite:!0}),g({devDependencies:["@biomejs/biome"],projectDir:e});let t=F.join(e,"package.json");if(await k.pathExists(t)){let r=await k.readJson(t);r.scripts={...r.scripts,check:"biome check --write ."},await k.writeJson(t,r,{spaces:2})}}async function Yt(e){let a=F.join(v,"template/with-husky");await k.pathExists(a)&&await k.copy(a,e,{overwrite:!0}),g({devDependencies:["husky","lint-staged"],projectDir:e});let t=F.join(e,"package.json");if(await k.pathExists(t)){let r=await k.readJson(t);r.scripts={...r.scripts,prepare:"husky"},r["lint-staged"]={"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":["biome check --write ."]},await k.writeJson(t,r,{spaces:2})}}async function Zt(e,a){let t=F.join(v,"template/with-pwa");await k.pathExists(t)&&await k.copy(t,e,{overwrite:!0});let r=Kt(e,a);if(!await k.pathExists(r))return;g({dependencies:["vite-plugin-pwa"],devDependencies:["@vite-pwa/assets-generator"],projectDir:r});let s=F.join(r,"vite.config.ts");if(await k.pathExists(s)){let o=await k.readFile(s,"utf8");if(!o.includes("vite-plugin-pwa")){let p=o.match(/^import .* from ['"](.*)['"]/m);p?o=o.replace(p[0],`import { VitePWA } from "vite-plugin-pwa"; ${p[0]}`):o=`import { VitePWA } from "vite-plugin-pwa"; ${o}`}let i=`VitePWA({ registerType: "autoUpdate", manifest: { name: "My App", short_name: "My App", description: "My App", theme_color: "#0c0c0c", }, pwaAssets: { disabled: false, config: true, }, devOptions: { enabled: true, }, })`;o.includes("VitePWA(")||(a.includes("react-router")?o=o.replace(/plugins: \[\s*tailwindcss\(\)/,`plugins: [ tailwindcss(), ${i}`):a.includes("tanstack-router")?o=o.replace(/plugins: \[\s*tailwindcss\(\)/,`plugins: [ tailwindcss(), ${i}`):o=o.replace(/plugins: \[/,`plugins: [ ${i},`)),await k.writeFile(s,o)}let n=F.join(r,"package.json");if(await k.pathExists(n)){let o=await k.readJson(n);o.scripts={...o.scripts,"generate-pwa-assets":"pwa-assets-generator"},await k.writeJson(n,o,{spaces:2})}}import oe from"node:path";import{log as $e}from"@clack/prompts";import Ae from"picocolors";function Se(e=32){let a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",t="",r=a.length;for(let s=0;s<e;s++)t+=a.charAt(Math.floor(Math.random()*r));return t}async function Ee(e,a,t=[]){if(!a)return;let r=oe.join(e,"apps/server"),s=oe.join(e,"apps/web"),n=oe.join(e,"apps/native");try{g({dependencies:["better-auth"],projectDir:r}),(t.includes("react-router")||t.includes("tanstack-router")||t.includes("tanstack-start"))&&g({dependencies:["better-auth"],projectDir:s}),t.includes("native")&&(g({dependencies:["better-auth","@better-auth/expo"],projectDir:n}),g({dependencies:["@better-auth/expo"],projectDir:r}))}catch(o){throw $e.error(Ae.red("Failed to configure authentication")),o instanceof Error&&$e.error(Ae.red(o.message)),o}}import ea from"node:path";async function Te(e,a,t){let r=ea.join(e,"apps/server"),s=[],n=[];a==="hono"?(s.push("hono","@hono/trpc-server"),t==="node"&&(s.push("@hono/node-server"),n.push("tsx","@types/node"))):a==="elysia"?(s.push("elysia","@elysiajs/cors","@elysiajs/trpc"),t==="node"&&(s.push("@elysiajs/node"),n.push("tsx","@types/node"))):a==="express"&&(s.push("express","cors"),n.push("@types/express","@types/cors"),t==="node"&&n.push("tsx","@types/node")),t==="bun"&&n.push("@types/bun"),g({dependencies:s,devDependencies:n,projectDir:r})}import ta from"node:path";import aa from"fs-extra";async function De(e,a){let t=ta.join(e,"README.md"),r=ra(a);try{await aa.writeFile(t,r)}catch(s){console.error("Failed to create README.md file:",s)}}function ra(e){let{projectName:a,packageManager:t,database:r,auth:s,addons:n=[],orm:o="drizzle",runtime:i="bun",frontend:p=["tanstack-router"]}=e,c=p.includes("react-router"),d=p.includes("tanstack-router"),w=p.includes("native"),$=t==="npm"?"npm run":t,A=c?"5173":"3001";return`# ${a} This project was created with [Better-T-Stack](https://github.com/AmanVarshney01/create-better-t-stack), a modern TypeScript stack that combines React, ${d?"TanStack Router":"React Router"}, Hono, tRPC, and more. ## Features ${sa(r,s,n,o,i,p)} ## Getting Started First, install the dependencies: \`\`\`bash ${t} install \`\`\` ${na(r,s,$,o)} Then, run the development server: \`\`\`bash ${$} dev \`\`\` ${d||c?`Open [http://localhost:${A}](http://localhost:${A}) in your browser to see the web application.`:""} ${w?`Use the Expo Go app to run the mobile application. `:""} The API is running at [http://localhost:3000](http://localhost:3000). ${n.includes("pwa")&&c?` ## PWA Support with React Router v7 There is a known compatibility issue between VitePWA and React Router v7. See: https://github.com/vite-pwa/vite-plugin-pwa/issues/809 `:""} ## Project Structure \`\`\` ${a}/ \u251C\u2500\u2500 apps/ ${d||c?`\u2502 \u251C\u2500\u2500 web/ # Frontend application (React, ${d?"TanStack Router":"React Router"}) `:""}${w?`\u2502 \u251C\u2500\u2500 native/ # Mobile application (React Native, Expo) `:""}\u2502 \u2514\u2500\u2500 server/ # Backend API (Hono, tRPC) \`\`\` ## Available Scripts ${oa($,r,o,s,w)} `}function sa(e,a,t,r,s,n){let o=n.includes("tanstack-router"),i=n.includes("react-router"),p=n.includes("native"),c=["- **TypeScript** - For type safety and improved developer experience"];o?c.push("- **TanStack Router** - File-based routing with full type safety"):i&&c.push("- **React Router** - Declarative routing for React"),p&&(c.push("- **React Native** - Build mobile apps using React"),c.push("- **Expo** - Tools for React Native development")),c.push("- **TailwindCSS** - Utility-first CSS for rapid UI development","- **shadcn/ui** - Reusable UI components","- **Hono** - Lightweight, performant server framework","- **tRPC** - End-to-end type-safe APIs",`- **${s==="bun"?"Bun":"Node.js"}** - Runtime environment`),e!=="none"&&c.push(`- **${r==="drizzle"?"Drizzle":"Prisma"}** - TypeScript-first ORM`,`- **${e==="sqlite"?"SQLite/Turso":"PostgreSQL"}** - Database engine`),a&&c.push("- **Authentication** - Email & password authentication with Better Auth");for(let d of t)d==="pwa"?c.push("- **PWA** - Progressive Web App support"):d==="tauri"?c.push("- **Tauri** - Build native desktop applications"):d==="biome"?c.push("- **Biome** - Linting and formatting"):d==="husky"&&c.push("- **Husky** - Git hooks for code quality");return c.join(` `)}function na(e,a,t,r){if(e==="none")return"";let s=`## Database Setup `;return e==="sqlite"?s+=`This project uses SQLite${r==="drizzle"?" with Drizzle ORM":" with Prisma"}. 1. Start the local SQLite database: \`\`\`bash cd apps/server && ${t} db:local \`\`\` 2. Update your \`.env\` file in the \`apps/server\` directory with the appropriate connection details if needed. `:e==="postgres"&&(s+=`This project uses PostgreSQL${r==="drizzle"?" with Drizzle ORM":" with Prisma"}. 1. Make sure you have a PostgreSQL database set up. 2. Update your \`apps/server/.env\` file with your PostgreSQL connection details. `),s+=` ${a?"4":"3"}. ${r==="prisma"?`Generate the Prisma client and push the schema: \`\`\`bash ${t} db:push \`\`\``:`Apply the schema to your database: \`\`\`bash ${t} db:push \`\`\``} `,s}function oa(e,a,t,r,s){let n=`- \`${e} dev\`: Start both web and server in development mode - \`${e} build\`: Build both web and server - \`${e} dev:web\`: Start only the web application - \`${e} dev:server\`: Start only the server - \`${e} check-types\`: Check TypeScript types across all apps`;return s&&(n+=` - \`${e} dev:native\`: Start the React Native/Expo development server`),a!=="none"&&(n+=` - \`${e} db:push\`: Push schema changes to database - \`${e} db:studio\`: Open database studio UI`,a==="sqlite"&&t==="drizzle"&&(n+=` - \`cd apps/server && ${e} db:local\`: Start the local SQLite database`)),n}import ze from"node:path";import{log as Ca,spinner as Ra}from"@clack/prompts";import Ia from"fs-extra";import Ne from"picocolors";import Ie from"node:path";import{cancel as ia,isCancel as ca,log as z,text as pa}from"@clack/prompts";import{execa as la}from"execa";import ie from"fs-extra";import q from"picocolors";import{execa as Ce}from"execa";async function Z(e){try{return process.platform==="win32"?(await Ce("where",[e])).exitCode===0:(await Ce("which",[e])).exitCode===0}catch{return!1}}async function da(){return Z("atlas")}async function ua(e){try{if(!await da())return z.error(q.red("MongoDB Atlas CLI not found.")),z.info(q.yellow("Please install it from: https://www.mongodb.com/docs/atlas/cli/current/install-atlas-cli/")),null;z.info(q.yellow("Setting up MongoDB Atlas...")),await la("atlas",["deployments","setup"],{cwd:e,stdio:"inherit"});let t=await pa({message:"Paste your complete MongoDB connection string:",placeholder:"mongodb://USERNAME:PASSWORD@HOST/DATABASE",validate(r){if(!r)return"Please enter a connection string";if(!r.startsWith("mongodb"))return"URL should start with mongodb"}});return ca(t)?(ia("MongoDB setup cancelled"),null):{connectionString:t}}catch(a){return a instanceof Error&&z.error(q.red(a.message)),null}}async function ce(e,a){let t=Ie.join(e,"apps/server",".env"),r="";await ie.pathExists(t)&&(r=await ie.readFile(t,"utf8"));let s=a?`DATABASE_URL="${a.connectionString}"`:'DATABASE_URL="mongodb://localhost:27017/mydb"';r.includes("DATABASE_URL=")?r=r.replace(/DATABASE_URL=.*(\r?\n|$)/,`${s}$1`):r+=` ${s}`,await ie.writeFile(t,r.trim())}function Re(){z.info(`MongoDB Atlas Setup: 1. Install Atlas CLI: https://www.mongodb.com/docs/atlas/cli/stable/install-atlas-cli/ 2. Run 'atlas deployments setup' and follow prompts 3. Get your connection string from the output 4. Format: mongodb+srv://USERNAME:PASSWORD@CLUSTER.mongodb.net/DATABASE_NAME 5. Add to .env as DATABASE_URL="your_connection_string"`)}async function Fe(e){let a=Ie.join(e,"apps/server");try{let t=await ua(a);t?(await ce(e,t),z.success(q.green("MongoDB Atlas connection string saved to .env file!"))):(await ce(e),Re())}catch(t){z.error(q.red(`Error during MongoDB Atlas setup: ${t}`)),await ce(e),Re()}}import W from"node:path";import{cancel as ma,isCancel as fa,log as B,password as ha}from"@clack/prompts";import{execa as ga}from"execa";import O from"fs-extra";import V from"picocolors";async function ba(e,a){try{B.info(V.blue("Initializing Prisma PostgreSQL"));let t=W.join(e,"prisma");await O.ensureDir(t),await ga(a==="npm"?"npx":a==="pnpm"?"pnpm dlx":"bunx",["prisma","init","--db"],{cwd:e,stdio:"inherit"}),B.info(V.yellow(`Please copy the Prisma Postgres URL from the output above. It looks like: prisma+postgres://accelerate.prisma-data.net/?api_key=...`));let s=await ha({message:"Paste your Prisma Postgres database URL:",validate(n){if(!n)return"Please enter a database URL";if(!n.startsWith("prisma+postgres://"))return"URL should start with prisma+postgres://"}});return fa(s)?(ma("Database setup cancelled"),null):{databaseUrl:s}}catch(t){return t instanceof Error&&B.error(V.red(t.message)),null}}async function pe(e,a){let t=W.join(e,"apps/server",".env"),r="";await O.pathExists(t)&&(r=await O.readFile(t,"utf8"));let s=a?`DATABASE_URL="${a.databaseUrl}"`:'DATABASE_URL="postgresql://postgres:postgres@localhost:5432/mydb?schema=public"';r.includes("DATABASE_URL=")?r=r.replace(/DATABASE_URL=.*(\r?\n|$)/,`${s}$1`):r+=` ${s}`,await O.writeFile(t,r.trim())}function Oe(){B.info(`Manual Prisma PostgreSQL Setup Instructions: 1. Visit https://console.prisma.io and create an account 2. Create a new PostgreSQL database from the dashboard 3. Get your database URL 4. Add the database URL to the .env file in apps/server/.env DATABASE_URL="your_database_url"`)}async function Be(e,a="npm"){let t=W.join(e,"apps/server");try{let r=await ba(t,a);r?(await pe(e,r),await wa(t),B.success(V.green("Prisma PostgreSQL database configured successfully!"))):(await pe(e),Oe())}catch(r){B.error(V.red(`Error during Prisma PostgreSQL setup: ${r}`)),await pe(e),Oe(),B.info("Setup completed with manual configuration required.")}}async function wa(e){try{g({dependencies:["@prisma/extension-accelerate"],projectDir:e});let a=W.join(e,"prisma/index.ts");await O.writeFile(a,` import { PrismaClient } from '@prisma/client'; import { withAccelerate } from "@prisma/extension-accelerate"; const prisma = new PrismaClient().$extends(withAccelerate()); export default prisma; `.trim());let r=W.join(e,"src/db/index.ts");if(await O.pathExists(r)){let s=await O.readFile(r,"utf8");s.includes("@prisma/extension-accelerate")||(s=`import { withAccelerate } from "@prisma/extension-accelerate"; ${s}`,s=s.replace("export const db = new PrismaClient();","export const db = new PrismaClient().$extends(withAccelerate());"),await O.writeFile(r,s))}}catch{B.warn(V.yellow("Could not add Prisma Accelerate extension automatically"))}}import Pa from"node:os";import Le from"node:path";import{cancel as le,confirm as ya,isCancel as de,log as G,select as ja,spinner as ue,text as va}from"@clack/prompts";import{$ as R}from"execa";import ka from"fs-extra";import T from"picocolors";async function xa(){return Z("turso")}async function $a(){try{return!(await R`turso auth whoami`).stdout.includes("You are not logged in")}catch{return!1}}async function Aa(){let e=ue();try{return e.start("Logging in to Turso..."),await R`turso auth login`,e.stop("Logged in to Turso successfully!"),!0}catch(a){throw e.stop(T.red("Failed to log in to Turso")),a}}async function Sa(e){let a=ue();try{if(a.start("Installing Turso CLI..."),e)await R`brew install tursodatabase/tap/turso`;else{let{stdout:t}=await R`curl -sSfL https://get.tur.so/install.sh`;await R`bash -c '${t}'`}return a.stop("Turso CLI installed successfully!"),!0}catch(t){throw t instanceof Error&&t.message.includes("User force closed")?(a.stop(),G.warn(T.yellow("Turso CLI installation cancelled by user")),new Error("Installation cancelled")):(a.stop(T.red("Failed to install Turso CLI")),t)}}async function Ea(){try{let{stdout:e}=await R`turso group list`,a=e.trim().split(` `);return a.length<=1?[]:a.slice(1).map(r=>{let[s,n,o,i]=r.trim().split(/\s{2,}/);return{name:s,locations:n,version:o,status:i}})}catch(e){return console.error("Error fetching Turso groups:",e),[]}}async function Ta(){let e=await Ea();if(e.length===0)return null;if(e.length===1)return e[0].name;let a=e.map(r=>({value:r.name,label:`${r.name} (${r.locations})`})),t=await ja({message:"Select a Turso database group:",options:a});return de(t)&&(le(T.red("Operation cancelled")),process.exit(0)),t}async function Da(e,a){try{a?await R`turso db create ${e} --group ${a}`:await R`turso db create ${e}`}catch(s){throw s instanceof Error&&s.message.includes("already exists")?new Error("DATABASE_EXISTS"):s}let{stdout:t}=await R`turso db show ${e} --url`,{stdout:r}=await R`turso db tokens create ${e}`;return{dbUrl:t.trim(),authToken:r.trim()}}async function J(e,a){let t=Le.join(e,"apps/server",".env"),r=a?`DATABASE_URL="${a.dbUrl}" DATABASE_AUTH_TOKEN="${a.authToken}"`:`DATABASE_URL= DATABASE_AUTH_TOKEN=`;await ka.writeFile(t,r)}function ee(){G.info(`Manual Turso Setup Instructions: 1. Visit https://turso.tech and create an account 2. Create a new database from the dashboard 3. Get your database URL and authentication token 4. Add these credentials to the .env file in apps/server/.env DATABASE_URL=your_database_url DATABASE_AUTH_TOKEN=your_auth_token`)}async function Me(e,a){if(!a){await J(e),G.info(T.blue("Skipping Turso setup. Setting up empty configuration.")),ee();return}let t=Pa.platform(),r=t==="darwin";if(!(t!=="win32")){G.warn(T.yellow("Automatic Turso setup is not supported on Windows.")),await J(e),ee();return}try{if(!await xa()){let w=await ya({message:"Would you like to install Turso CLI?",initialValue:!0});if(de(w)&&(le(T.red("Operation cancelled")),process.exit(0)),!w){await J(e),ee();return}await Sa(r)}await $a()||await Aa();let i=await Ta(),p=!1,c="",d=Le.basename(e);for(;!p;){let w=await va({message:"Enter a name for your database:",defaultValue:d,initialValue:d,placeholder:d});de(w)&&(le(T.red("Operation cancelled")),process.exit(0)),c=w;let $=ue();try{$.start(`Creating Turso database "${c}"${i?` in group "${i}"`:""}...`);let A=await Da(c,i);await J(e,A),$.stop("Turso database configured successfully!"),p=!0}catch(A){if(A instanceof Error&&A.message==="DATABASE_EXISTS")$.stop(T.yellow(`Database "${T.red(c)}" already exists`)),d=`${c}-${Math.floor(Math.random()*1e3)}`;else throw $.stop(T.red("Failed to create Turso database")),A}}}catch(n){G.error(T.red(`Error during Turso setup: ${n}`)),await J(e),ee(),G.success("Setup completed with manual configuration required.")}}async function Ue(e,a,t,r,s,n,o){let i=Ra(),p=ze.join(e,"apps/server");if(a==="none"){await Ia.remove(ze.join(p,"src/db"));return}try{t==="prisma"?g({dependencies:["@prisma/client"],devDependencies:["prisma"],projectDir:p}):t==="drizzle"&&(a==="sqlite"?g({dependencies:["drizzle-orm","@libsql/client"],devDependencies:["drizzle-kit"],projectDir:p}):a==="postgres"?g({dependencies:["drizzle-orm","pg"],devDependencies:["drizzle-kit","@types/pg"],projectDir:p}):a==="mysql"&&g({dependencies:["drizzle-orm","mysql2"],devDependencies:["drizzle-kit"],projectDir:p})),a==="sqlite"&&s?await Me(e,t==="drizzle"):a==="postgres"&&t==="prisma"&&n?await Be(e,r):a==="mongodb"&&o&&await Fe(e)}catch(c){throw i.stop(Ne.red("Failed to set up database")),c instanceof Error&&Ca.error(Ne.red(c.message)),c}}import N from"node:path";import te from"fs-extra";async function me(e,a){await te.ensureDir(N.dirname(e));let t="";await te.pathExists(e)&&(t=await te.readFile(e,"utf8"));let r=!1;for(let{key:s,value:n,condition:o}of a)if(o){let i=new RegExp(`^${s}=.*$`,"m");i.test(t)?n&&(t=t.replace(i,`${s}=${n}`),r=!0):(t+=` ${s}=${n}`,r=!0)}r&&await te.writeFile(e,t.trim())}async function _e(e,a){let t=N.join(e,"apps/server"),r=N.join(t,".env"),s=a.frontend.includes("react-router"),n=a.frontend.includes("tanstack-router"),o=a.frontend.includes("tanstack-start"),i=s||n||o,p="http://localhost:3000";s?p="http://localhost:5173":(n||o)&&(p="http://localhost:3001");let c="",d=a.dbSetup==="turso"||a.dbSetup==="prisma-postgres"||a.dbSetup==="mongodb-atlas";d||(a.database==="postgres"?c="postgresql://postgres:postgres@localhost:5432/mydb?schema=public":a.database==="mysql"?c="mysql://root:password@localhost:3306/mydb":a.database==="mongodb"?c="mongodb://localhost:27017/mydatabase":a.database==="sqlite"&&(c="file:./local.db"));let w=[{key:"CORS_ORIGIN",value:p,condition:!0},{key:"BETTER_AUTH_SECRET",value:Se(),condition:!!a.auth},{key:"BETTER_AUTH_URL",value:"http://localhost:3000",condition:!!a.auth},{key:"DATABASE_URL",value:c,condition:a.database!=="none"&&c!==""&&!d},{key:"GOOGLE_GENERATIVE_AI_API_KEY",value:"",condition:a.examples?.includes("ai")||!1}];if(await me(r,w),i){let $=N.join(e,"apps/web"),A=[{key:"VITE_SERVER_URL",value:"http://localhost:3000",condition:!0}];await me(N.join($,".env"),A)}if(a.frontend.includes("native")){let $=N.join(e,"apps/native"),A=[{key:"EXPO_PUBLIC_SERVER_URL",value:"http://localhost:3000",condition:!0}];await me(N.join($,".env"),A)}}import x from"node:path";import b from"fs-extra";async function qe(e,a,t,r,s,n=["tanstack-router"]){let o=n.includes("tanstack-router"),i=n.includes("tanstack-start"),p=n.includes("react-router"),c=o||p||i,d;o?d="web-tanstack-router":i?d="web-tanstack-start":d="web-react-router";let w=await b.pathExists(x.join(e,"apps/web"));a.includes("todo")&&c&&w?await La(e,t,r,d):await Na(e,t),a.includes("ai")&&(s==="hono"||s==="express")&&c&&w&&await Fa(e,d)}async function Fa(e,a){let t=x.join(v,"template/examples/ai");if(await b.pathExists(t)){let r=x.join(t,`apps/${a}/src/routes/ai.tsx`),s=x.join(e,"apps/web/src/routes/ai.tsx");await b.pathExists(r)&&await b.copy(r,s,{overwrite:!0}),await Ba(e,a);let n=x.join(e,"apps/web");g({dependencies:["ai"],projectDir:n});let o=x.join(e,"apps/server");g({dependencies:["ai","@ai-sdk/google"],projectDir:o}),await Oa(e)}}async function Oa(e){let a=x.join(e,"apps/server/src/index.ts");if(await b.pathExists(a)){let t=await b.readFile(a,"utf8"),r=t.includes("hono"),s=t.includes("express");if(r){let n=`import { streamText } from "ai"; import { google } from "@ai-sdk/google"; import { stream } from "hono/streaming";`,o=` // AI chat endpoint app.post("/ai", async (c) => { const body = await c.req.json(); const messages = body.messages || []; const result = streamText({ model: google("gemini-1.5-flash"), messages, }); c.header("X-Vercel-AI-Data-Stream", "v1"); c.header("Content-Type", "text/plain; charset=utf-8"); return stream(c, (stream) => stream.pipe(result.toDataStream())); });`;if(t.includes("import {")){let p=t.lastIndexOf("import"),c=t.indexOf(` `,p);t=`${t.substring(0,c+1)} ${n} ${t.substring(c+1)}`}else t=`${n} ${t}`;let i=t.indexOf('app.use("/trpc"')||t.indexOf("app.use(trpc(");if(i!==-1)t=`${t.substring(0,i)}${o} ${t.substring(i)}`;else{let p=t.indexOf("export default");p!==-1?t=`${t.substring(0,p)}${o} ${t.substring(p)}`:t=`${t} ${o}`}}else if(s){let n=`import { streamText } from "ai"; import { google } from "@ai-sdk/google";`,o=` // AI chat endpoint app.post("/ai", async (req, res) => { const { messages = [] } = req.body; const result = streamText({ model: google("gemini-1.5-flash"), messages, }); result.pipeDataStreamToResponse(res); });`;if(t.includes("import {")||t.includes("import ")){let p=t.lastIndexOf("import"),c=t.indexOf(` `,p);t=`${t.substring(0,c+1)} ${n} ${t.substring(c+1)}`}else t=`${n} ${t}`;let i=t.indexOf('app.use("/trpc"');if(i!==-1)t=`${t.substring(0,i)}${o} ${t.substring(i)}`;else{let p=t.indexOf("app.listen(");if(p!==-1){let c=t.lastIndexOf(` `,p);t=`${t.substring(0,c)}${o} ${t.substring(c)}`}else t=`${t} ${o}`}}await b.writeFile(a,t)}}async function Ba(e,a){let t=x.join(e,"apps/web/src/components/header.tsx");if(await b.pathExists(t)){let r=await b.readFile(t,"utf8"),s=/const links = \[\s*([^;]*?)\s*\];/s,n=r.match(s);if(n){let o=n[1];if(!o.includes('"/ai"')){let i=`const links = [ ${o}${o.trim().endsWith(",")?"":","} { to: "/ai", label: "AI Chat" }, ];`;r=r.replace(s,i),await b.writeFile(t,r)}}}}async function La(e,a,t,r){let s=x.join(v,"template/examples/todo");if(await b.pathExists(s)){let n=x.join(s,`apps/${r}/src/routes/todos.tsx`),o=x.join(e,"apps/web/src/routes/todos.tsx");if(await b.pathExists(n)&&await b.copy(n,o,{overwrite:!0}),a!=="none"){let i=x.join(s,`apps/server/src/routers/with-${a}-todo.ts`),p=x.join(e,"apps/server/src/routers/todo.ts");await b.pathExists(i)&&await b.copy(i,p,{overwrite:!0}),await Ma(e)}await za(e,r)}}async function Ma(e){let a=x.join(e,"apps/server/src/routers/index.ts");if(await b.pathExists(a)){let t=await b.readFile(a,"utf8");if(!t.includes("import { todoRouter }")){let r=t.lastIndexOf("import"),s=t.indexOf(` `,r);s!==-1?t=`${t.slice(0,s)} import { todoRouter } from "./todo";${t.slice(s)}`:t=`import { todoRouter } from "./todo"; ${t}`;let n=t.indexOf("export const appRouter = router({");if(n!==-1){let o=t.indexOf("{",n)+1;t=`${t.slice(0,o)} todo: todoRouter,${t.slice(o)}`}await b.writeFile(a,t)}}}async function za(e,a){let t=x.join(e,"apps/web/src/components/header.tsx");if(await b.pathExists(t)){let r=await b.readFile(t,"utf8"),s=/const links = \[\s*([^;]*?)\s*\];/s,n=r.match(s);if(n){let o=n[1];if(!o.includes('"/todos"')){let i=`const links = [ ${o}${o.trim().endsWith(",")?"":","} { to: "/todos", label: "Todos" }, ];`;r=r.replace(s,i),await b.writeFile(t,r)}}}}async function Na(e,a){if(a==="drizzle"){let r=x.join(e,"apps/server/src/db/schema/todo.ts");await b.pathExists(r)&&await b.remove(r)}else if(a==="prisma"){let r=x.join(e,"apps/server/prisma/schema/todo.prisma");await b.pathExists(r)&&await b.remove(r)}let t=x.join(e,"apps/server/src/routers/todo.ts");await b.pathExists(t)&&await b.remove(t),await Ua(e)}async function Ua(e){let a=x.join(e,"apps/server/src/routers/index.ts");if(await b.pathExists(a)){let t=await b.readFile(a,"utf8");t=t.replace(/import { todoRouter } from ".\/todo";/,""),t=t.replace(/todo: todoRouter,/,""),await b.writeFile(a,t)}}import{log as Ve,spinner as Ge}from"@clack/prompts";import{$ as We}from"execa";import ae from"picocolors";async function Je({projectDir:e,packageManager:a,addons:t=[]}){let r=Ge();try{r.start(`Running ${a} install...`),await We({cwd:e,stderr:"inherit"})`${a} install`,r.stop("Dependencies installed successfully"),(t.includes("biome")||t.includes("husky"))&&await _a(e,a)}catch(s){throw r.stop(ae.red("Failed to install dependencies")),s instanceof Error&&Ve.error(ae.red(`Installation error: ${s.message}`)),s}}async function _a(e,a){let t=Ge();try{t.start("Running Biome format check..."),await We({cwd:e,stderr:"inherit"})`${a} biome check --write .`,t.stop("Biome check completed successfully")}catch{t.stop(ae.yellow("Biome check encountered issues")),Ve.warn(ae.yellow("Some files may need manual formatting"))}}import{note as qa}from"@clack/prompts";import P from"picocolors";function He(e,a,t,r,s,n,o,i,p){let c=t==="npm"?"npm run":t,d=`cd ${a}`,w=n?.includes("husky")||n?.includes("biome"),$=e!=="none"?Wa(e,s,c,o):"",A=n?.includes("tauri")?Ja(c):"",Q=w?Ga(c):"",_=i?.includes("native")?Va():"",E=n?.includes("pwa")&&i?.includes("react-router")?Ha():"",D=n?.includes("starlight")?Qa(c):"",C=i?.includes("tanstack-router"),Ot=i?.includes("tanstack-start"),ge=i?.includes("react-router"),be=C||ge||Ot,Bt=i?.includes("native"),Lt=be||Bt,Mt=ge?"5173":"3001";qa(`${P.cyan("1.")} ${d} ${r?"":`${P.cyan("2.")} ${t} install `}${P.cyan(r?"2.":"3.")} ${c} dev ${P.bold("Your project will be available at:")} ${Lt?`${be?`${P.cyan("\u2022")} Frontend: http://localhost:${Mt} `:""}`:`${P.yellow("NOTE:")} You are creating a backend-only app (no frontend selected) `}${P.cyan("\u2022")} API: http://localhost:3000 ${n?.includes("starlight")?`${P.cyan("\u2022")} Docs: http://localhost:4321 `:""}${_?` ${_.trim()}`:""}${$?` ${$.trim()}`:""}${A?` ${A.trim()}`:""}${Q?` ${Q.trim()}`:""}${E?` ${E.trim()}`:""}${D?` ${D.trim()}`:""} ${P.bold("Like Better-T Stack?")} Please consider giving us a star on GitHub: ${P.cyan("https://github.com/AmanVarshney01/create-better-t-stack")}`,"Next steps")}function Va(){return`${P.yellow("NOTE:")} For Expo connectivity issues, update apps/native/.env with your local IP: EXPO_PUBLIC_SERVER_URL=http://192.168.0.103:3000 `}function Ga(e){return`${P.bold("Linting and formatting:")} ${P.cyan("\u2022")} Format and lint fix: ${`${e} check`} `}function Wa(e,a,t,r){let s=[];return a==="prisma"?(e==="sqlite"&&s.push(`${P.yellow("NOTE:")} Turso support with Prisma is in Early Access and requires additional setup.`,"Learn more at: https://www.prisma.io/docs/orm/overview/databases/turso"),r==="bun"&&s.push(`${P.yellow("NOTE:")} Prisma with Bun may require additional configuration. If you encounter errors, follow the guidance provided in the error messages`),s.push(`${P.cyan("\u2022")} Apply schema: ${`${t} db:push`}`),s.push(`${P.cyan("\u2022")} Database UI: ${`${t} db:studio`}`)):a==="drizzle"&&(s.push(`${P.cyan("\u2022")} Apply schema: ${`${t} db:push`}`),s.push(`${P.cyan("\u2022")} Database UI: ${`${t} db:studio`}`)),s.length?`${P.bold("Database commands:")} ${s.join(` `)} `:""}function Ja(e){return` ${P.bold("Desktop app with Tauri:")} ${P.cyan("\u2022")} Start desktop app: ${`cd apps/web && ${e} desktop:dev`} ${P.cyan("\u2022")} Build desktop app: ${`cd apps/web && ${e} desktop:build`} ${P.yellow("NOTE:")} Tauri requires Rust and platform-specific dependencies. See: https://v2.tauri.app/start/prerequisites/ `}function Ha(){return`${P.bold("PWA with React Router v7:")} ${P.yellow("NOTE:")} There is a known compatibility issue between VitePWA and React Router v7. See: https://github.com/vite-pwa/vite-plugin-pwa/issues/809 `}function Qa(e){return`${P.bold("Documentation with Starlight:")} ${P.cyan("\u2022")} Start docs site: ${`cd apps/docs && ${e} dev`} ${P.cyan("\u2022")} Build docs site: ${`cd apps/docs && ${e} build`} `}import re from"node:path";import{log as Ka}from"@clack/prompts";import{$ as Qe,execa as Xa}from"execa";import L from"fs-extra";import Ya from"picocolors";async function Ke(e,a){await Za(e,a),await er(e,a)}async function Za(e,a){let t=re.join(e,"package.json");if(await L.pathExists(t)){let r=await L.readJson(t);r.name=a.projectName;let{stdout:s}=await Xa(a.packageManager,["-v"],{cwd:e});if(r.packageManager=`${a.packageManager}@${s.trim()}`,await L.writeJson(t,r,{spaces:2}),a.packageManager==="pnpm"){let n=re.join(v,"template/with-pnpm/pnpm-workspace.yaml"),o=re.join(e,"pnpm-workspace.yaml");await L.pathExists(n)&&await L.copy(n,o)}}}async function er(e,a){let t=re.join(e,"apps/server/package.json");if(await L.pathExists(t)){let r=await L.readJson(t);a.database!=="none"&&(a.database==="sqlite"&&(r.scripts["db:local"]="turso dev --db-file local.db"),a.orm==="prisma"?(r.scripts["db:push"]="prisma db push --schema ./prisma/schema",r.scripts["db:studio"]="prisma studio"):a.orm==="drizzle"&&(r.scripts["db:push"]="drizzle-kit push",r.scripts["db:studio"]="drizzle-kit studio")),await L.writeJson(t,r,{spaces:2})}}async function Xe(e,a){if(!a)return;if((await Qe({cwd:e,reject:!1,stderr:"pipe"})`git --version`).exitCode!==0){Ka.warn(Ya.yellow("Git is not installed"));return}let r=await Qe({cwd:e,reject:!1,stderr:"pipe"})`git init`;if(r.exitCode!==0)throw new Error(`Git initialization failed: ${r.stderr}`)}import se from"node:path";import M from"fs-extra";async function Ye(e,a,t){let r=se.join(e,"apps/server"),s=se.join(r,"src/index.ts"),n=await M.readFile(s,"utf-8");a==="bun"?await tr(r,s,n,t):a==="node"&&await ar(r,s,n,t)}async function tr(e,a,t,r){let s=se.join(e,"package.json"),n=await M.readJson(s);if(n.scripts={...n.scripts,dev:"bun run --hot src/index.ts",start:"bun run dist/src/index.js"},await M.writeJson(s,n,{spaces:2}),g({devDependencies:["@types/bun"],projectDir:e}),r==="hono"){let o=`${t} export default app; `;await M.writeFile(a,o)}}async function ar(e,a,t,r){let s=se.join(e,"package.json"),n=await M.readJson(s);if(n.scripts={...n.scripts,dev:"tsx watch src/index.ts",start:"node dist/src/index.js"},await M.writeJson(s,n,{spaces:2}),g({devDependencies:["tsx","@types/node"],projectDir:e}),r==="hono"){g({dependencies:["@hono/node-server"],projectDir:e});let o=`import { serve } from "@hono/node-server"; `,i=` serve( { fetch: app.fetch, port: 3000, }, (info) => { console.log(\`Server is running on http://localhost:\${info.port}\`); }, ); `;if(!t.includes("@hono/node-server")){let p=t.lastIndexOf("import"),c=t.substring(0,p),d=t.substring(p),w=c+o+d+i;await M.writeFile(a,w)}}else if(r==="elysia"&&(g({dependencies:["@elysiajs/node"],projectDir:e}),!t.includes("@elysiajs/node"))){let o=`import { node } from "@elysiajs/node"; `,i=t.indexOf(` `,t.indexOf("import")),p=t.substring(0,i+1),c=t.substring(i+1),d=p+o+c;d=d.replace(/const app = new Elysia\([^)]*\)/,"const app = new Elysia({ adapter: node() })"),await M.writeFile(a,d)}}import u from"node:path";import l from"fs-extra";async function Ze(e){let a=u.join(v,"template/base");if(!await l.pathExists(a))throw new Error(`Template directory not found: ${a}`);await l.ensureDir(e);let t=await l.readdir(a);for(let n of t){let o=u.join(a,n),i=u.join(e,n);n!=="apps"&&(await l.stat(o).then(p=>p.isDirectory())?await l.copy(o,i):await l.copy(o,i))}await l.ensureDir(u.join(e,"apps"));let r=u.join(a,"apps/server"),s=u.join(e,"apps/server");await l.pathExists(r)&&await l.copy(r,s)}async function et(e,a){let t=a.includes("tanstack-router"),r=a.includes("tanstack-start"),s=a.includes("react-router"),n=a.includes("native");if(t||s||r){let o=u.join(e,"apps/web");await l.ensureDir(o);let i=u.join(v,"template/base/apps/web-base");await l.pathExists(i)&&await l.copy(i,o);let p="web-react-router";t?p="web-tanstack-router":r&&(p="web-tanstack-start");let c=u.join(v,`template/base/apps/${p}`);await l.pathExists(c)&&await l.copy(c,o,{overwrite:!0});let d=u.join(o,"package.json");if(await l.pathExists(d)){let w=await l.readJson(d);w.name="web",await l.writeJson(d,w,{spaces:2})}}if(n){let o=u.join(v,"template/base/apps/native"),i=u.join(e,"apps/native");await l.pathExists(o)&&await l.copy(o,i),await l.writeFile(u.join(e,".npmrc"),`node-linker=hoisted `)}}async function tt(e,a){let t=u.join(v,`template/with-${a}`);await l.pathExists(t)&&await l.copy(t,e,{overwrite:!0})}async function at(e,a,t,r){if(a==="none"||t==="none")return;let s=u.join(v,sr(a,t));if(await l.pathExists(s)&&(await l.copy(s,e,{overwrite:!0}),!r)){if(a==="prisma"){let n=u.join(e,"apps/server/prisma/schema/auth.prisma");await l.pathExists(n)&&await l.remove(n)}else if(a==="drizzle"){let n=u.join(e,"apps/server/src/db/schema/auth.ts");await l.pathExists(n)&&await l.remove(n)}}}async function rt(e,a,t,r,s,n){if(!a)return;let o=u.join(v,"template/with-auth");if(await l.pathExists(o)){let i=n.includes("react-router"),p=n.includes("tanstack-router"),c=n.includes("tanstack-start");if(i||p||c){let E=u.join(e,"apps/web"),D=u.join(o,"apps/web-base");if(await l.pathExists(D)&&await l.copy(D,E,{overwrite:!0}),i){let C=u.join(o,"apps/web-react-router");await l.pathExists(C)&&await l.copy(C,E,{overwrite:!0})}if(p){let C=u.join(o,"apps/web-tanstack-router");await l.pathExists(C)&&await l.copy(C,E,{overwrite:!0})}if(c){let C=u.join(o,"apps/web-tanstack-start");await l.pathExists(C)&&await l.copy(C,E,{overwrite:!0})}}let d=u.join(o,"apps/server/src"),w=u.join(e,"apps/server/src");await l.copy(u.join(d,"lib/trpc.ts"),u.join(w,"lib/trpc.ts"),{overwrite:!0}),await l.copy(u.join(d,"routers/index.ts"),u.join(w,"routers/index.ts"),{overwrite:!0});let $=`with-${t}-context.ts`;await l.copy(u.join(d,"lib",$),u.join(w,"lib/context.ts"),{overwrite:!0});let A=`with-${t}-index.ts`;await l.copy(u.join(d,A),u.join(w,"index.ts"),{overwrite:!0});let Q=nr(r,s),_=u.join(d,Q);if(await l.pathExists(_)){let E=await l.readdir(_);for(let D of E)await l.copy(u.join(_,D),u.join(w,"lib",D),{overwrite:!0})}if(n.includes("native")){let E=u.join(o,"apps/native"),D=u.join(e,"apps/native");await l.pathExists(E)&&await l.copy(E,D,{overwrite:!0}),g({dependencies:["@better-auth/expo"],projectDir:u.join(e,"apps/server")}),await rr(e,r,s)}}}async function rr(e,a,t){let r=u.join(e,"apps/server"),s;if(a==="drizzle"?t==="sqlite"?s=u.join(r,"src/lib/auth.ts"):t==="postgres"&&(s=u.join(r,"src/lib/auth.ts")):a==="prisma"&&(t==="sqlite"?s=u.join(r,"src/lib/auth.ts"):t==="postgres"&&(s=u.join(r,"src/lib/auth.ts"))),s&&await l.pathExists(s)){let n=await l.readFile(s,"utf8");if(!n.includes("@better-auth/expo")){let o=`import { expo } from "@better-auth/expo"; `,i=n.lastIndexOf("import"),p=n.indexOf(` `,i)+1;n=n.substring(0,p)+o+n.substring(p)}n.includes("plugins:")?n.includes("expo()")||(n=n.replace(/plugins: \[(.*?)\]/s,(o,i)=>`plugins: [${i}${i.trim()?", ":""}expo()]`)):n=n.replace(/}\);/,` plugins: [expo()], });`),n.includes("my-better-t-app://")||(n=n.replace(/trustedOrigins: \[(.*?)\]/s,(o,i)=>`trustedOrigins: [${i}${i.trim()?", ":""}"my-better-t-app://"]`)),await l.writeFile(s,n)}}async function st(e){let a=await nt(e);for(let t of a)if(await l.pathExists(t)){let r=u.join(u.dirname(t),".gitignore");await l.move(t,r,{overwrite:!0})}}async function nt(e){let a=[],t=u.join(e,"_gitignore");await l.pathExists(t)&&a.push(t);try{let r=await l.readdir(e,{withFileTypes:!0});for(let s of r)if(s.isDirectory()&&s.name!=="node_modules"){let n=u.join(e,s.name),o=await nt(n);a.push(...o)}}catch{}return a}function sr(e,a){if(e==="drizzle"){if(a==="sqlite")return"template/with-drizzle-sqlite";if(a==="postgres")return"template/with-drizzle-postgres";if(a==="mysql")return"template/with-drizzle-mysql"}if(e==="prisma"){if(a==="sqlite")return"template/with-prisma-sqlite";if(a==="postgres")return"template/with-prisma-postgres";if(a==="mysql")return"template/with-prisma-mysql";if(a==="mongodb")return"template/with-prisma-mongodb"}return"template/base"}function nr(e,a){if(e==="drizzle"){if(a==="sqlite")return"with-drizzle-sqlite-lib";if(a==="postgres")return"with-drizzle-postgres-lib";if(a==="mysql")return"with-drizzle-mysql-lib"}if(e==="prisma"){if(a==="sqlite")return"with-prisma-sqlite-lib";if(a==="postgres")return"with-prisma-postgres-lib";if(a==="mysql")return"with-prisma-mysql-lib";if(a==="mongodb")return"with-prisma-mongodb-lib"}throw new Error("Invalid ORM or database configuration for auth setup")}async function it(e){let a=cr(),t=or.resolve(process.cwd(),e.projectName);try{return await pr.ensureDir(t),await Ze(t),await et(t,e.frontend),await st(t),await tt(t,e.backend),await Te(t,e.backend,e.runtime),await at(t,e.orm,e.database,e.auth),await Ue(t,e.database,e.orm,e.packageManager,e.dbSetup==="turso",e.dbSetup==="prisma-postgres",e.dbSetup==="mongodb-atlas"),await rt(t,e.auth,e.backend,e.orm,e.database,e.frontend),await Ee(t,e.auth,e.frontend),await Ye(t,e.runtime,e.backend),await qe(t,e.examples,e.orm,e.auth,e.backend,e.frontend),await _e(t,e),await Xe(t,e.git),e.addons.length>0&&await xe(t,e.addons,e.packageManager,e.frontend),await Ke(t,e),await De(t,e),e.noInstall||await Je({projectDir:t,packageManager:e.packageManager,addons:e.addons}),He(e.database,e.projectName,e.packageManager,!e.noInstall,e.orm,e.addons,e.runtime,e.frontend),t}catch(r){throw a.message(ot.red("Failed")),r instanceof Error&&(ir(ot.red(`Error during project creation: ${r.message}`)),process.exit(1)),r}}import{cancel as is,group as cs}from"@clack/prompts";import ps from"picocolors";import{cancel as lr,isCancel as dr,multiselect as ur}from"@clack/prompts";import mr from"picocolors";async function ct(e,a){if(e!==void 0)return e;let t=a?.includes("react-router")||a?.includes("tanstack-router"),r=[{value:"starlight",label:"Starlight",hint:"Add Astro Starlight documentation site"},{value:"biome",label:"Biome",hint:"Add Biome for linting and formatting"},{value:"husky",label:"Husky",hint:"Add Git hooks with Husky, lint-staged (requires Biome)"}],n=t?[...[{value:"pwa",label:"PWA (Progressive Web App)",hint:"Make your app installable and work offline"},{value:"tauri",label:"Tauri Desktop App",hint:"Build native desktop apps from your web frontend"}],...r]:r,o=y.addons.filter(p=>t||p!=="pwa"&&p!=="tauri"),i=await ur({message:"Select addons",options:n,initialValues:o,required:!1});return dr(i)&&(lr(mr.red("Operation cancelled")),process.exit(0)),i.includes("husky")&&!i.includes("biome")&&i.push("biome"),i}import{cancel as fr,confirm as hr,isCancel as gr}from"@clack/prompts";import br from"picocolors";async function pt(e,a,t){if(!a)return!1;if(e!==void 0)return e;let r=await hr({message:"Add authentication with Better-Auth?",initialValue:y.auth});return gr(r)&&(fr(br.red("Operation cancelled")),process.exit(0)),r}import{cancel as wr,isCancel as Pr,select as yr}from"@clack/prompts";import jr from"picocolors";async function lt(e){if(e!==void 0)return e;let a=await yr({message:"Select backend framework",options:[{value:"hono",label:"Hono",hint:"Lightweight, ultrafast web framework"},{value:"express",label:"Express",hint:"Fast, unopinionated, minimalist web framework for Node.js"},{value:"elysia",label:"Elysia",hint:"Ergonomic web framework for building backend servers"}],initialValue:y.backend});return Pr(a)&&(wr(jr.red("Operation cancelled")),process.exit(0)),a}import{cancel as vr,isCancel as kr,select as xr}from"@clack/prompts";import $r from"picocolors";async function dt(e){if(e!==void 0)return e;let a=await xr({message:"Select database",options:[{value:"none",label:"None",hint:"No database setup"},{value:"sqlite",label:"SQLite",hint:"lightweight, server-less, embedded relational database management system"},{value:"postgres",label:"PostgreSQL",hint:"powerful, open source object-relational database system"},{value:"mysql",label:"MySQL",hint:"popular open-source relational database system"},{value:"mongodb",label:"MongoDB",hint:"open-source NoSQL database that stores data in JSON-like documents called BSON"}],initialValue:y.database});return kr(a)&&(vr($r.red("Operation cancelled")),process.exit(0)),a}import{cancel as Ar,isCancel as Sr,select as Er}from"@clack/prompts";import Tr from"picocolors";async function ut(e,a,t){if(a!==void 0)return a;if(e==="sqlite"&&t==="prisma")return"none";let r=[];if(e==="sqlite")r=[{value:"turso",label:"Turso",hint:"SQLite for Production. Powered by libSQL"},{value:"none",label:"None",hint:"Manual setup"}];else if(e==="postgres"&&t==="prisma")r=[{value:"prisma-postgres",label:"Prisma Postgres",hint:"Instant Postgres for Global Applications"},{value:"none",label:"None",hint:"Manual setup"}];else if(e==="mongodb")r=[{value:"mongodb-atlas",label:"MongoDB Atlas",hint:"The most effective way to deploy MongoDB"},{value:"none",label:"None",hint:"Manual setup"}];else return"none";let s=await Er({message:`Select ${e} setup option`,options:r,initialValue:"none"});return Sr(s)&&(Ar(Tr.red("Operation cancelled")),process.exit(0)),s}import{cancel as Dr,isCancel as Cr,multiselect as mt}from"@clack/prompts";import Rr from"picocolors";async function ft(e,a,t,r){if(e!==void 0)return e;if(a==="none")return[];if(!(t?.includes("react-router")||t?.includes("tanstack-router")||t?.includes("tanstack-start")))return[];let n=[];return r==="elysia"&&(n=await mt({message:"Include examples",options:[{value:"todo",label:"Todo App",hint:"A simple CRUD example app"}],required:!1,initialValues:y.examples})),(r==="hono"||r==="express")&&(n=await mt({message:"Include examples",options:[{value:"todo",label:"Todo App",hint:"A simple CRUD example app"},{value:"ai",label:"AI Chat",hint:"A simple AI chat interface using AI SDK"}],required:!1,initialValues:y.examples})),Cr(n)&&(Dr(Rr.red("Operation cancelled")),process.exit(0)),n}import{cancel as ht,isCancel as gt,multiselect as Ir,select as Fr}from"@clack/prompts";import bt from"picocolors";async function wt(e){if(e!==void 0)return e;let a=await Ir({message:"Select platforms to develop for",options:[{value:"web",label:"Web",hint:"React Web Application"},{value:"native",label:"Native",hint:"Create a React Native/Expo app"}],required:!1,initialValues:y.frontend.some(r=>r==="tanstack-router"||r==="react-router"||r==="tanstack-start")?["web"]:[]});gt(a)&&(ht(bt.red("Operation cancelled")),process.exit(0));let t=[];if(a.includes("web")){let r=await Fr({message:"Choose frontend framework",options:[{value:"tanstack-router",label:"TanStack Router",hint:"Modern and scalable routing for React Applications"},{value:"react-router",label:"React Router",hint:"A user\u2011obsessed, standards\u2011focused, multi\u2011strategy router you can deploy anywhere."},{value:"tanstack-start",label:"TanStack Start (beta)",hint:"SSR, Streaming, Server Functions, API Routes, bundling and more powered by TanStack Router and Vite."}],initialValue:y.frontend.find(s=>s==="tanstack-router"||s==="react-router"||s==="tanstack-start")||"tanstack-router"});gt(r)&&(ht(bt.red("Operation cancelled")),process.exit(0)),t.push(r)}return a.includes("native")&&t.push("native"),t}import{cancel as Or,confirm as Br,isCancel as Lr}from"@clack/prompts";import Mr from"picocolors";async function Pt(e){if(e!==void 0)return e;let a=await Br({message:"Initialize git repository?",initialValue:y.git});return Lr(a)&&(Or(Mr.red("Operation cancelled")),process.exit(0)),a}import{cancel as zr,confirm as Nr,isCancel as Ur}from"@clack/prompts";import _r from"picocolors";async function yt(e){if(e!==void 0)return e;let a=await Nr({message:"Install dependencies?",initialValue:!y.noInstall});return Ur(a)&&(zr(_r.red("Operation cancelled")),process.exit(0)),!a}import{cancel as qr,isCancel as Vr,log as Gr,select as Wr}from"@clack/prompts";import Jr from"picocolors";async function jt(e,a,t){if(!a)return"none";if(e!==void 0)return e;if(t==="mongodb")return Gr.info("Only Prisma is supported with MongoDB."),"prisma";let r=await Wr({message:"Select ORM",options:[{value:"drizzle",label:"Drizzle",hint:"lightweight and performant TypeScript ORM"},{value:"prisma",label:"Prisma",hint:"Powerful, feature-rich ORM"}],initialValue:y.orm});return Vr(r)&&(qr(Jr.red("Operation cancelled")),process.exit(0)),r}import{cancel as Hr,isCancel as Qr,select as Kr}from"@clack/prompts";import Xr from"picocolors";async function vt(e){if(e!==void 0)return e;let a=K(),t=await Kr({message:"Choose package manager",options:[{value:"npm",label:"npm",hint:"Node Package Manager"},{value:"pnpm",label:"pnpm",hint:"Fast, disk space efficient package manager"},{value:"bun",label:"bun",hint:"All-in-one JavaScript runtime & toolkit"}],initialValue:a});return Qr(t)&&(Hr(Xr.red("Operation cancelled")),process.exit(0)),t}import H from"node:path";import{cancel as Yr,isCancel as Zr,text as es}from"@clack/prompts";import U from"fs-extra";import ts from"picocolors";var as=["<",">",":",'"',"|","?","*"],kt=255;function xt(e){if(e!=="."){if(!e)return"Project name cannot be empty";if(e.length>kt)return`Project name must be less than ${kt} characters`;if(as.some(a=>e.includes(a)))return"Project name contains invalid characters";if(e.startsWith(".")||e.startsWith("-"))return"Project name cannot start with a dot or dash";if(e.toLowerCase()==="node_modules")return"Project name is reserved"}}async function $t(e){if(e)if(e==="."){let n=process.cwd();if(U.readdirSync(n).length===0)return e}else{let n=H.basename(e);if(!xt(n)){let i=H.resolve(process.cwd(),e);if(!U.pathExistsSync(i)||U.readdirSync(i).length===0)return e}}let a=!1,t="",r=y.projectName,s=1;for(;U.pathExistsSync(H.resolve(process.cwd(),r));)r=`${y.projectName}-${s}`,s++;for(;!a;){let n=await es({message:"Enter your project name or path (relative to current di