create-better-t-stack 2.0.6 → 2.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import Go from"node:path";import{cancel as Jo,intro as Qo,log as _,outro as Ho}from"@clack/prompts";import{consola as v}from"consola";import sr from"fs-extra";import z from"picocolors";import Yo from"yargs";import{hideBin as Ko}from"yargs/helpers";import Le from"node:path";import{fileURLToPath as mr}from"node:url";var re=()=>{let e=process.env.npm_config_user_agent;return e?.startsWith("pnpm")?"pnpm":e?.startsWith("bun")?"bun":"npm"};var fr=mr(import.meta.url),hr=Le.dirname(fr),P=Le.join(hr,"../"),g={projectName:"my-better-t-app",frontend:["tanstack-router"],database:"sqlite",orm:"drizzle",auth:!0,addons:[],examples:[],git:!0,packageManager:re(),install:!0,dbSetup:"none",backend:"hono",runtime:"bun",api:"trpc"},be={"better-auth":"^1.2.6","@better-auth/expo":"^1.2.6","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.6.0",prisma:"^6.6.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.6",cors:"^2.8.5",express:"^5.1.0","@types/express":"^5.0.1","@types/cors":"^2.8.17",turbo:"^2.4.2",ai:"^4.2.8","@ai-sdk/google":"^1.2.3","@prisma/extension-accelerate":"^1.3.0","@orpc/server":"^1.0.3","@orpc/react-query":"^1.0.3","@orpc/client":"^1.0.3","@trpc/tanstack-react-query":"^11.0.0","@trpc/server":"^11.0.0","@trpc/client":"^11.0.0"};import Ba from"node:path";import{cancel as Na,log as Ia,spinner as La}from"@clack/prompts";import Ma from"fs-extra";import Ft from"picocolors";import H from"node:path";import B from"fs-extra";import gr from"node:path";import Me from"fs-extra";var h=async e=>{let{dependencies:r=[],devDependencies:t=[],projectDir:a}=e,o=gr.join(a,"package.json"),n=await Me.readJson(o);n.dependencies||(n.dependencies={}),n.devDependencies||(n.devDependencies={});for(let i of r){let s=be[i];s?n.dependencies[i]=s:console.warn(`Warning: Dependency ${i} not found in version map.`)}for(let i of t){let s=be[i];s?n.devDependencies[i]=s:console.warn(`Warning: Dev dependency ${i} not found in version map.`)}await Me.writeJson(o,n,{spaces:2})};import Oe from"node:path";import{spinner as br}from"@clack/prompts";import wr from"consola";import{execa as yr}from"execa";import Ue from"picocolors";function R(e,r){switch(e){case"pnpm":return`pnpm dlx ${r}`;case"bun":return`bunx ${r}`;default:return`npx ${r}`}}async function _e(e){let{projectName:r,packageManager:t}=e,a=Oe.resolve(process.cwd(),r),o=br();try{o.start("Setting up Starlight docs...");let s=`create-astro@latest ${["docs","--template","starlight","--no-install","--add","tailwind","--no-git","--skip-houston"].join(" ")}`,c=R(t,s);await yr(c,{cwd:Oe.join(a,"apps"),env:{CI:"true"},shell:!0}),o.stop("Starlight docs setup successfully!")}catch(n){throw o.stop(Ue.red("Failed to set up Starlight docs")),n instanceof Error&&wr.error(Ue.red(n.message)),n}}import Q from"node:path";import{spinner as Pr}from"@clack/prompts";import{consola as vr}from"consola";import{execa as jr}from"execa";import ae from"fs-extra";import ze from"picocolors";async function We(e){let{projectName:r,packageManager:t,frontend:a}=e,o=Q.resolve(process.cwd(),r),n=Pr(),i=Q.join(o,"apps/web");if(await ae.pathExists(i))try{n.start("Setting up Tauri desktop app support..."),await h({devDependencies:["@tauri-apps/cli"],projectDir:i});let s=Q.join(i,"package.json");if(await ae.pathExists(s)){let S=await ae.readJson(s);S.scripts={...S.scripts,tauri:"tauri","desktop:dev":"tauri dev","desktop:build":"tauri build"},await ae.writeJson(s,S,{spaces:2})}let p=a.includes("react-router")?"http://localhost:5173":"http://localhost:3001",f=`@tauri-apps/cli@latest ${["init",`--app-name=${Q.basename(o)}`,`--window-title=${Q.basename(o)}`,"--frontend-dist=../dist",`--dev-url=${p}`,`--before-dev-command="${t} run dev"`,`--before-build-command="${t} run build"`].join(" ")}`,k=R(t,f);await jr(k,{cwd:i,env:{CI:"true"},shell:!0}),n.stop("Tauri desktop app support configured successfully!")}catch(s){throw n.stop(ze.red("Failed to set up Tauri")),s instanceof Error&&vr.error(ze.red(s.message)),s}}async function qe(e){let{projectName:r,addons:t,frontend:a}=e,o=H.resolve(process.cwd(),r),n=a.includes("react-router")||a.includes("tanstack-router");t.includes("turborepo")&&await h({devDependencies:["turbo"],projectDir:o}),t.includes("pwa")&&n&&await Ar(o,a),t.includes("tauri")&&n&&await We(e),t.includes("biome")&&await xr(o),t.includes("husky")&&await Sr(o),t.includes("starlight")&&await _e(e)}function kr(e,r){return H.join(e,"apps/web")}async function xr(e){await h({devDependencies:["@biomejs/biome"],projectDir:e});let r=H.join(e,"package.json");if(await B.pathExists(r)){let t=await B.readJson(r);t.scripts={...t.scripts,check:"biome check --write ."},await B.writeJson(r,t,{spaces:2})}}async function Sr(e){await h({devDependencies:["husky","lint-staged"],projectDir:e});let r=H.join(e,"package.json");if(await B.pathExists(r)){let t=await B.readJson(r);t.scripts={...t.scripts,prepare:"husky"},t["lint-staged"]={"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":["biome check --write ."]},await B.writeJson(r,t,{spaces:2})}}async function Ar(e,r){let t=kr(e,r);if(!await B.pathExists(t))return;await h({dependencies:["vite-plugin-pwa"],devDependencies:["@vite-pwa/assets-generator"],projectDir:t});let a=H.join(t,"package.json");if(await B.pathExists(a)){let o=await B.readJson(a);o.scripts={...o.scripts,"generate-pwa-assets":"pwa-assets-generator"},await B.writeJson(a,o,{spaces:2})}}import*as Y from"node:path";async function Ve(e){let{api:r,projectName:t}=e,a=Y.resolve(process.cwd(),t),o=Y.join(a,"apps/web"),n=Y.join(a,"apps/server");r==="orpc"&&(await h({dependencies:["@orpc/react-query","@orpc/server","@orpc/client"],projectDir:o}),await h({dependencies:["@orpc/server","@orpc/client"],projectDir:n})),r==="trpc"&&(await h({dependencies:["@trpc/tanstack-react-query","@trpc/server","@trpc/client"],projectDir:o}),await h({dependencies:["@trpc/server","@trpc/client"],projectDir:n}))}import oe from"node:path";import Ge from"consola";import Je from"picocolors";function Qe(e=32){let r="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",t="",a=r.length;for(let o=0;o<e;o++)t+=r.charAt(Math.floor(Math.random()*a));return t}async function He(e){let{projectName:r,auth:t,frontend:a}=e;if(!t)return;let o=oe.resolve(process.cwd(),r),n=oe.join(o,"apps/server"),i=oe.join(o,"apps/web"),s=oe.join(o,"apps/native");try{await h({dependencies:["better-auth"],projectDir:n}),(a.includes("react-router")||a.includes("tanstack-router")||a.includes("tanstack-start"))&&await h({dependencies:["better-auth"],projectDir:i}),a.includes("native")&&(await h({dependencies:["better-auth","@better-auth/expo"],projectDir:s}),await h({dependencies:["@better-auth/expo"],projectDir:n}))}catch(c){throw Ge.error(Je.red("Failed to configure authentication")),c instanceof Error&&Ge.error(Je.red(c.message)),c}}import Ye from"node:path";async function Ke(e){let{projectName:r,backend:t,runtime:a,api:o}=e,n=Ye.resolve(process.cwd(),r),i=t,s=Ye.join(n,"apps/server"),c=[],p=[];i==="hono"?(c.push("hono"),o==="trpc"&&c.push("@hono/trpc-server"),a==="node"&&(c.push("@hono/node-server"),p.push("tsx","@types/node"))):i==="elysia"?(c.push("elysia","@elysiajs/cors"),o==="trpc"&&c.push("@elysiajs/trpc"),a==="node"&&(c.push("@elysiajs/node"),p.push("tsx","@types/node"))):i==="express"&&(c.push("express","cors"),p.push("@types/express","@types/cors"),a==="node"&&p.push("tsx","@types/node")),a==="bun"&&p.push("@types/bun"),await h({dependencies:c,devDependencies:p,projectDir:s})}import Dr from"node:path";import $r from"consola";import Cr from"fs-extra";async function Xe(e,r){let t=Dr.join(e,"README.md"),a=Er(r);try{await Cr.writeFile(t,a)}catch(o){$r.error("Failed to create README.md file:",o)}}function Er(e){let{projectName:r,packageManager:t,database:a,auth:o,addons:n=[],orm:i="drizzle",runtime:s="bun",frontend:c=["tanstack-router"],backend:p="hono"}=e,w=c.includes("react-router"),y=c.includes("tanstack-router"),f=c.includes("native"),k=c.includes("next"),S=c.includes("tanstack-start"),E=t==="npm"?"npm run":t,x="3001";return w?x="5173":k&&(x="3000"),`# ${r}
2
+ import Ho from"node:path";import{cancel as Yo,intro as Ko,log as _,outro as Xo}from"@clack/prompts";import{consola as k}from"consola";import ir from"fs-extra";import z from"picocolors";import Zo from"yargs";import{hideBin as en}from"yargs/helpers";import Le from"node:path";import{fileURLToPath as fr}from"node:url";var re=()=>{let e=process.env.npm_config_user_agent;return e?.startsWith("pnpm")?"pnpm":e?.startsWith("bun")?"bun":"npm"};var hr=fr(import.meta.url),gr=Le.dirname(hr),v=Le.join(gr,"../"),g={projectName:"my-better-t-app",frontend:["tanstack-router"],database:"sqlite",orm:"drizzle",auth:!0,addons:[],examples:[],git:!0,packageManager:re(),install:!0,dbSetup:"none",backend:"hono",runtime:"bun",api:"trpc"},be={"better-auth":"^1.2.6","@better-auth/expo":"^1.2.6","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.6.0",prisma:"^6.6.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.6",cors:"^2.8.5",express:"^5.1.0","@types/express":"^5.0.1","@types/cors":"^2.8.17",turbo:"^2.4.2",ai:"^4.2.8","@ai-sdk/google":"^1.2.3","@prisma/extension-accelerate":"^1.3.0","@orpc/server":"^1.0.3","@orpc/react-query":"^1.0.3","@orpc/client":"^1.0.3","@trpc/tanstack-react-query":"^11.0.0","@trpc/server":"^11.0.0","@trpc/client":"^11.0.0"};import La from"node:path";import{cancel as Ma,log as Oa,spinner as Ua}from"@clack/prompts";import _a from"fs-extra";import Rt from"picocolors";import H from"node:path";import B from"fs-extra";import br from"node:path";import Me from"fs-extra";var w=async e=>{let{dependencies:r=[],devDependencies:t=[],projectDir:a}=e,o=br.join(a,"package.json"),n=await Me.readJson(o);n.dependencies||(n.dependencies={}),n.devDependencies||(n.devDependencies={});for(let s of r){let i=be[s];i?n.dependencies[s]=i:console.warn(`Warning: Dependency ${s} not found in version map.`)}for(let s of t){let i=be[s];i?n.devDependencies[s]=i:console.warn(`Warning: Dev dependency ${s} not found in version map.`)}await Me.writeJson(o,n,{spaces:2})};import Oe from"node:path";import{spinner as wr}from"@clack/prompts";import yr from"consola";import{execa as Pr}from"execa";import Ue from"picocolors";function R(e,r){switch(e){case"pnpm":return`pnpm dlx ${r}`;case"bun":return`bunx ${r}`;default:return`npx ${r}`}}async function _e(e){let{projectName:r,packageManager:t}=e,a=Oe.resolve(process.cwd(),r),o=wr();try{o.start("Setting up Starlight docs...");let i=`create-astro@latest ${["docs","--template","starlight","--no-install","--add","tailwind","--no-git","--skip-houston"].join(" ")}`,c=R(t,i);await Pr(c,{cwd:Oe.join(a,"apps"),env:{CI:"true"},shell:!0}),o.stop("Starlight docs setup successfully!")}catch(n){throw o.stop(Ue.red("Failed to set up Starlight docs")),n instanceof Error&&yr.error(Ue.red(n.message)),n}}import Q from"node:path";import{spinner as vr}from"@clack/prompts";import{consola as jr}from"consola";import{execa as kr}from"execa";import ae from"fs-extra";import ze from"picocolors";async function We(e){let{projectName:r,packageManager:t,frontend:a}=e,o=Q.resolve(process.cwd(),r),n=vr(),s=Q.join(o,"apps/web");if(await ae.pathExists(s))try{n.start("Setting up Tauri desktop app support..."),await w({devDependencies:["@tauri-apps/cli"],projectDir:s});let i=Q.join(s,"package.json");if(await ae.pathExists(i)){let j=await ae.readJson(i);j.scripts={...j.scripts,tauri:"tauri","desktop:dev":"tauri dev","desktop:build":"tauri build"},await ae.writeJson(i,j,{spaces:2})}let l=a.includes("react-router")?"http://localhost:5173":"http://localhost:3001",p=`@tauri-apps/cli@latest ${["init",`--app-name=${Q.basename(o)}`,`--window-title=${Q.basename(o)}`,"--frontend-dist=../dist",`--dev-url=${l}`,`--before-dev-command="${t} run dev"`,`--before-build-command="${t} run build"`].join(" ")}`,b=R(t,p);await kr(b,{cwd:s,env:{CI:"true"},shell:!0}),n.stop("Tauri desktop app support configured successfully!")}catch(i){throw n.stop(ze.red("Failed to set up Tauri")),i instanceof Error&&jr.error(ze.red(i.message)),i}}async function qe(e){let{projectName:r,addons:t,frontend:a}=e,o=H.resolve(process.cwd(),r),n=a.includes("react-router")||a.includes("tanstack-router");t.includes("turborepo")&&await w({devDependencies:["turbo"],projectDir:o}),t.includes("pwa")&&n&&await Dr(o,a),t.includes("tauri")&&n&&await We(e),t.includes("biome")&&await Ar(o),t.includes("husky")&&await Sr(o),t.includes("starlight")&&await _e(e)}function xr(e,r){return H.join(e,"apps/web")}async function Ar(e){await w({devDependencies:["@biomejs/biome"],projectDir:e});let r=H.join(e,"package.json");if(await B.pathExists(r)){let t=await B.readJson(r);t.scripts={...t.scripts,check:"biome check --write ."},await B.writeJson(r,t,{spaces:2})}}async function Sr(e){await w({devDependencies:["husky","lint-staged"],projectDir:e});let r=H.join(e,"package.json");if(await B.pathExists(r)){let t=await B.readJson(r);t.scripts={...t.scripts,prepare:"husky"},t["lint-staged"]={"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":["biome check --write ."]},await B.writeJson(r,t,{spaces:2})}}async function Dr(e,r){let t=xr(e,r);if(!await B.pathExists(t))return;await w({dependencies:["vite-plugin-pwa"],devDependencies:["@vite-pwa/assets-generator"],projectDir:t});let a=H.join(t,"package.json");if(await B.pathExists(a)){let o=await B.readJson(a);o.scripts={...o.scripts,"generate-pwa-assets":"pwa-assets-generator"},await B.writeJson(a,o,{spaces:2})}}import*as Y from"node:path";import $r from"fs-extra";async function Ve(e){let{api:r,projectName:t}=e,a=Y.resolve(process.cwd(),t),o=Y.join(a,"apps/web"),n=Y.join(a,"apps/server"),s=await $r.pathExists(o);r==="orpc"&&(s&&await w({dependencies:["@orpc/react-query","@orpc/server","@orpc/client"],projectDir:o}),await w({dependencies:["@orpc/server","@orpc/client"],projectDir:n})),r==="trpc"&&(s&&await w({dependencies:["@trpc/tanstack-react-query","@trpc/server","@trpc/client"],projectDir:o}),await w({dependencies:["@trpc/server","@trpc/client"],projectDir:n}))}import oe from"node:path";import Ge from"consola";import Je from"fs-extra";import Qe from"picocolors";async function He(e){let{projectName:r,auth:t,frontend:a}=e;if(!t)return;let o=oe.resolve(process.cwd(),r),n=oe.join(o,"apps/server"),s=oe.join(o,"apps/web"),i=oe.join(o,"apps/native"),c=await Je.pathExists(s),l=await Je.pathExists(i);try{await w({dependencies:["better-auth"],projectDir:n}),(a.includes("react-router")||a.includes("tanstack-router")||a.includes("tanstack-start")||a.includes("next"))&&c&&await w({dependencies:["better-auth"],projectDir:s}),a.includes("native")&&l&&(await w({dependencies:["better-auth","@better-auth/expo"],projectDir:i}),await w({dependencies:["@better-auth/expo"],projectDir:n}))}catch(d){Ge.error(Qe.red("Failed to configure authentication dependencies")),d instanceof Error&&Ge.error(Qe.red(d.message))}}function Ye(e=32){let r="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",t="",a=r.length;for(let o=0;o<e;o++)t+=r.charAt(Math.floor(Math.random()*a));return t}import Ke from"node:path";async function Xe(e){let{projectName:r,backend:t,runtime:a,api:o}=e,n=Ke.resolve(process.cwd(),r),s=t,i=Ke.join(n,"apps/server"),c=[],l=[];s==="hono"?(c.push("hono"),o==="trpc"&&c.push("@hono/trpc-server"),a==="node"&&(c.push("@hono/node-server"),l.push("tsx","@types/node"))):s==="elysia"?(c.push("elysia","@elysiajs/cors"),o==="trpc"&&c.push("@elysiajs/trpc"),a==="node"&&(c.push("@elysiajs/node"),l.push("tsx","@types/node"))):s==="express"&&(c.push("express","cors"),l.push("@types/express","@types/cors"),a==="node"&&l.push("tsx","@types/node")),a==="bun"&&l.push("@types/bun"),await w({dependencies:c,devDependencies:l,projectDir:i})}import Er from"node:path";import Cr from"consola";import Tr from"fs-extra";async function Ze(e,r){let t=Er.join(e,"README.md"),a=Fr(r);try{await Tr.writeFile(t,a)}catch(o){Cr.error("Failed to create README.md file:",o)}}function Fr(e){let{projectName:r,packageManager:t,database:a,auth:o,addons:n=[],orm:s="drizzle",runtime:i="bun",frontend:c=["tanstack-router"],backend:l="hono"}=e,d=c.includes("react-router"),y=c.includes("tanstack-router"),p=c.includes("native"),b=c.includes("next"),j=c.includes("tanstack-start"),A=t==="npm"?"npm run":t,S="3001";return d?S="5173":b&&(S="3000"),`# ${r}
3
3
 
4
- This project was created with [Better-T-Stack](https://github.com/AmanVarshney01/create-better-t-stack), a modern TypeScript stack that combines React, ${y?"TanStack Router":w?"React Router":k?"Next.js":S?"TanStack Start":""}, ${p[0].toUpperCase()+p.slice(1)}, tRPC, and more.
4
+ This project was created with [Better-T-Stack](https://github.com/AmanVarshney01/create-better-t-stack), a modern TypeScript stack that combines React, ${y?"TanStack Router":d?"React Router":b?"Next.js":j?"TanStack Start":""}, ${l[0].toUpperCase()+l.slice(1)}, tRPC, and more.
5
5
 
6
6
  ## Features
7
7
 
8
- ${Tr(a,o,n,i,s,c,p)}
8
+ ${Rr(a,o,n,s,i,c,l)}
9
9
 
10
10
  ## Getting Started
11
11
 
@@ -15,20 +15,20 @@ First, install the dependencies:
15
15
  ${t} install
16
16
  \`\`\`
17
17
 
18
- ${Fr(a,o,E,i)}
18
+ ${Br(a,o,A,s)}
19
19
 
20
20
  Then, run the development server:
21
21
 
22
22
  \`\`\`bash
23
- ${E} dev
23
+ ${A} dev
24
24
  \`\`\`
25
25
 
26
- ${y||w||k||S?`Open [http://localhost:${x}](http://localhost:${x}) in your browser to see the web application.`:""}
27
- ${f?`Use the Expo Go app to run the mobile application.
26
+ ${y||d||b||j?`Open [http://localhost:${S}](http://localhost:${S}) in your browser to see the web application.`:""}
27
+ ${p?`Use the Expo Go app to run the mobile application.
28
28
  `:""}
29
29
  The API is running at [http://localhost:3000](http://localhost:3000).
30
30
 
31
- ${n.includes("pwa")&&w?`
31
+ ${n.includes("pwa")&&d?`
32
32
  ## PWA Support with React Router v7
33
33
 
34
34
  There is a known compatibility issue between VitePWA and React Router v7.
@@ -40,17 +40,17 @@ See: https://github.com/vite-pwa/vite-plugin-pwa/issues/809
40
40
  \`\`\`
41
41
  ${r}/
42
42
  \u251C\u2500\u2500 apps/
43
- ${y||w||k||S?`\u2502 \u251C\u2500\u2500 web/ # Frontend application (${y?"React + TanStack Router":w?"React + React Router":k?"Next.js":"React + TanStack Start"})
44
- `:""}${f?`\u2502 \u251C\u2500\u2500 native/ # Mobile application (React Native, Expo)
43
+ ${y||d||b||j?`\u2502 \u251C\u2500\u2500 web/ # Frontend application (${y?"React + TanStack Router":d?"React + React Router":b?"Next.js":"React + TanStack Start"})
44
+ `:""}${p?`\u2502 \u251C\u2500\u2500 native/ # Mobile application (React Native, Expo)
45
45
  `:""}${n.includes("starlight")?`\u2502 \u251C\u2500\u2500 docs/ # Documentation site (Astro Starlight)
46
- `:""}\u2502 \u2514\u2500\u2500 server/ # Backend API (${p[0].toUpperCase()+p.slice(1)}, tRPC)
46
+ `:""}\u2502 \u2514\u2500\u2500 server/ # Backend API (${l[0].toUpperCase()+l.slice(1)}, tRPC)
47
47
  \`\`\`
48
48
 
49
49
  ## Available Scripts
50
50
 
51
- ${Rr(E,a,i,o,f,n,p)}
52
- `}function Tr(e,r,t,a,o,n,i){let s=n.includes("tanstack-router"),c=n.includes("react-router"),p=n.includes("native"),w=n.includes("next"),y=n.includes("tanstack-start"),f=["- **TypeScript** - For type safety and improved developer experience"];s?f.push("- **TanStack Router** - File-based routing with full type safety"):c?f.push("- **React Router** - Declarative routing for React"):w?f.push("- **Next.js** - Full-stack React framework"):y&&f.push("- **TanStack Start** - SSR framework with TanStack Router"),p&&(f.push("- **React Native** - Build mobile apps using React"),f.push("- **Expo** - Tools for React Native development")),f.push("- **TailwindCSS** - Utility-first CSS for rapid UI development","- **shadcn/ui** - Reusable UI components"),i==="hono"?f.push("- **Hono** - Lightweight, performant server framework"):i==="express"?f.push("- **Express** - Fast, unopinionated web framework"):i==="elysia"?f.push("- **Elysia** - Type-safe, high-performance framework"):i==="next"&&f.push("- **Next.js** - Full-stack React framework"),f.push("- **tRPC** - End-to-end type-safe APIs",`- **${o==="bun"?"Bun":"Node.js"}** - Runtime environment`),e!=="none"&&f.push(`- **${a==="drizzle"?"Drizzle":"Prisma"}** - TypeScript-first ORM`,`- **${e==="sqlite"?"SQLite/Turso":e==="postgres"?"PostgreSQL":e==="mysql"?"MySQL":"MongoDB"}** - Database engine`),r&&f.push("- **Authentication** - Email & password authentication with Better Auth");for(let k of t)k==="pwa"?f.push("- **PWA** - Progressive Web App support"):k==="tauri"?f.push("- **Tauri** - Build native desktop applications"):k==="biome"?f.push("- **Biome** - Linting and formatting"):k==="husky"?f.push("- **Husky** - Git hooks for code quality"):k==="starlight"&&f.push("- **Starlight** - Documentation site with Astro");return f.join(`
53
- `)}function Fr(e,r,t,a){if(e==="none")return"";let o=`## Database Setup
51
+ ${Nr(A,a,s,o,p,n,l)}
52
+ `}function Rr(e,r,t,a,o,n,s){let i=n.includes("tanstack-router"),c=n.includes("react-router"),l=n.includes("native"),d=n.includes("next"),y=n.includes("tanstack-start"),p=["- **TypeScript** - For type safety and improved developer experience"];i?p.push("- **TanStack Router** - File-based routing with full type safety"):c?p.push("- **React Router** - Declarative routing for React"):d?p.push("- **Next.js** - Full-stack React framework"):y&&p.push("- **TanStack Start** - SSR framework with TanStack Router"),l&&(p.push("- **React Native** - Build mobile apps using React"),p.push("- **Expo** - Tools for React Native development")),p.push("- **TailwindCSS** - Utility-first CSS for rapid UI development","- **shadcn/ui** - Reusable UI components"),s==="hono"?p.push("- **Hono** - Lightweight, performant server framework"):s==="express"?p.push("- **Express** - Fast, unopinionated web framework"):s==="elysia"?p.push("- **Elysia** - Type-safe, high-performance framework"):s==="next"&&p.push("- **Next.js** - Full-stack React framework"),p.push("- **tRPC** - End-to-end type-safe APIs",`- **${o==="bun"?"Bun":"Node.js"}** - Runtime environment`),e!=="none"&&p.push(`- **${a==="drizzle"?"Drizzle":"Prisma"}** - TypeScript-first ORM`,`- **${e==="sqlite"?"SQLite/Turso":e==="postgres"?"PostgreSQL":e==="mysql"?"MySQL":"MongoDB"}** - Database engine`),r&&p.push("- **Authentication** - Email & password authentication with Better Auth");for(let b of t)b==="pwa"?p.push("- **PWA** - Progressive Web App support"):b==="tauri"?p.push("- **Tauri** - Build native desktop applications"):b==="biome"?p.push("- **Biome** - Linting and formatting"):b==="husky"?p.push("- **Husky** - Git hooks for code quality"):b==="starlight"&&p.push("- **Starlight** - Documentation site with Astro");return p.join(`
53
+ `)}function Br(e,r,t,a){if(e==="none")return"";let o=`## Database Setup
54
54
 
55
55
  `;return e==="sqlite"?o+=`This project uses SQLite${a==="drizzle"?" with Drizzle ORM":" with Prisma"}.
56
56
 
@@ -80,45 +80,45 @@ ${t} db:push
80
80
  \`\`\`bash
81
81
  ${t} db:push
82
82
  \`\`\``}
83
- `,o}function Rr(e,r,t,a,o,n,i){let s=`- \`${e} dev\`: Start all applications in development mode
83
+ `,o}function Nr(e,r,t,a,o,n,s){let i=`- \`${e} dev\`: Start all applications in development mode
84
84
  - \`${e} build\`: Build all applications
85
85
  - \`${e} dev:web\`: Start only the web application
86
86
  - \`${e} dev:server\`: Start only the server
87
- - \`${e} check-types\`: Check TypeScript types across all apps`;return o&&(s+=`
88
- - \`${e} dev:native\`: Start the React Native/Expo development server`),r!=="none"&&(s+=`
87
+ - \`${e} check-types\`: Check TypeScript types across all apps`;return o&&(i+=`
88
+ - \`${e} dev:native\`: Start the React Native/Expo development server`),r!=="none"&&(i+=`
89
89
  - \`${e} db:push\`: Push schema changes to database
90
- - \`${e} db:studio\`: Open database studio UI`,r==="sqlite"&&t==="drizzle"&&(s+=`
91
- - \`cd apps/server && ${e} db:local\`: Start the local SQLite database`)),n.includes("biome")&&(s+=`
92
- - \`${e} check\`: Run Biome formatting and linting`),n.includes("pwa")&&(s+=`
93
- - \`cd apps/web && ${e} generate-pwa-assets\`: Generate PWA assets`),n.includes("tauri")&&(s+=`
90
+ - \`${e} db:studio\`: Open database studio UI`,r==="sqlite"&&t==="drizzle"&&(i+=`
91
+ - \`cd apps/server && ${e} db:local\`: Start the local SQLite database`)),n.includes("biome")&&(i+=`
92
+ - \`${e} check\`: Run Biome formatting and linting`),n.includes("pwa")&&(i+=`
93
+ - \`cd apps/web && ${e} generate-pwa-assets\`: Generate PWA assets`),n.includes("tauri")&&(i+=`
94
94
  - \`cd apps/web && ${e} desktop:dev\`: Start Tauri desktop app in development
95
- - \`cd apps/web && ${e} desktop:build\`: Build Tauri desktop app`),n.includes("starlight")&&(s+=`
95
+ - \`cd apps/web && ${e} desktop:build\`: Build Tauri desktop app`),n.includes("starlight")&&(i+=`
96
96
  - \`cd apps/docs && ${e} dev\`: Start documentation site
97
- - \`cd apps/docs && ${e} build\`: Build documentation site`),s}import Ce from"node:path";import{spinner as ua}from"@clack/prompts";import ma from"consola";import fa from"fs-extra";import pt from"picocolors";import se from"node:path";import{cancel as Br,isCancel as Nr,log as W,spinner as tt,text as Ir}from"@clack/prompts";import ie from"consola";import{execa as Lr}from"execa";import K from"fs-extra";import D from"picocolors";import{execa as Ze}from"execa";async function ne(e){try{return process.platform==="win32"?(await Ze("where",[e])).exitCode===0:(await Ze("which",[e])).exitCode===0}catch{return!1}}async function Mr(){let e=tt();e.start("Checking for MongoDB Atlas CLI");try{let r=await ne("atlas");return e.stop(r?"MongoDB Atlas CLI found":D.yellow("MongoDB Atlas CLI not found")),r}catch{return e.stop(D.red("Error checking for MongoDB Atlas CLI")),!1}}async function Or(e){try{if(!await Mr())return ie.error(D.red("MongoDB Atlas CLI not found.")),W.info(D.yellow("Please install it from: https://www.mongodb.com/docs/atlas/cli/current/install-atlas-cli/")),null;W.info(D.blue("Running MongoDB Atlas setup...")),await Lr("atlas",["deployments","setup"],{cwd:e,stdio:"inherit"}),W.info(D.green("Atlas setup complete!"));let t=await Ir({message:"Enter your MongoDB connection string:",placeholder:"mongodb+srv://username:password@cluster.mongodb.net/database",validate(a){if(!a)return"Please enter a connection string";if(!a.startsWith("mongodb"))return"URL should start with mongodb:// or mongodb+srv://"}});return Nr(t)?(Br("MongoDB setup cancelled"),null):{connectionString:t}}catch(r){return r instanceof Error&&ie.error(D.red(r.message)),null}}async function we(e,r){try{let t=se.join(e,"apps/server",".env");await K.ensureDir(se.dirname(t));let a="";await K.pathExists(t)&&(a=await K.readFile(t,"utf8"));let o=r?`DATABASE_URL="${r.connectionString}"`:'DATABASE_URL="mongodb://localhost:27017/mydb"';a.includes("DATABASE_URL=")?a=a.replace(/DATABASE_URL=.*(\r?\n|$)/,`${o}$1`):a+=`
98
- ${o}`,await K.writeFile(t,a.trim())}catch(t){throw ie.error("Failed to update environment configuration"),t}}function et(){W.info(`
99
- ${D.green("MongoDB Atlas Manual Setup Instructions:")}
97
+ - \`cd apps/docs && ${e} build\`: Build documentation site`),i}import Ee from"node:path";import{spinner as fa}from"@clack/prompts";import ha from"consola";import ga from"fs-extra";import lt from"picocolors";import se from"node:path";import{cancel as Ir,isCancel as Lr,log as W,spinner as rt,text as Mr}from"@clack/prompts";import ie from"consola";import{execa as Or}from"execa";import K from"fs-extra";import $ from"picocolors";import{execa as et}from"execa";async function ne(e){try{return process.platform==="win32"?(await et("where",[e])).exitCode===0:(await et("which",[e])).exitCode===0}catch{return!1}}async function Ur(){let e=rt();e.start("Checking for MongoDB Atlas CLI");try{let r=await ne("atlas");return e.stop(r?"MongoDB Atlas CLI found":$.yellow("MongoDB Atlas CLI not found")),r}catch{return e.stop($.red("Error checking for MongoDB Atlas CLI")),!1}}async function _r(e){try{if(!await Ur())return ie.error($.red("MongoDB Atlas CLI not found.")),W.info($.yellow("Please install it from: https://www.mongodb.com/docs/atlas/cli/current/install-atlas-cli/")),null;W.info($.blue("Running MongoDB Atlas setup...")),await Or("atlas",["deployments","setup"],{cwd:e,stdio:"inherit"}),W.info($.green("Atlas setup complete!"));let t=await Mr({message:"Enter your MongoDB connection string:",placeholder:"mongodb+srv://username:password@cluster.mongodb.net/database",validate(a){if(!a)return"Please enter a connection string";if(!a.startsWith("mongodb"))return"URL should start with mongodb:// or mongodb+srv://"}});return Lr(t)?(Ir("MongoDB setup cancelled"),null):{connectionString:t}}catch(r){return r instanceof Error&&ie.error($.red(r.message)),null}}async function we(e,r){try{let t=se.join(e,"apps/server",".env");await K.ensureDir(se.dirname(t));let a="";await K.pathExists(t)&&(a=await K.readFile(t,"utf8"));let o=r?`DATABASE_URL="${r.connectionString}"`:'DATABASE_URL="mongodb://localhost:27017/mydb"';a.includes("DATABASE_URL=")?a=a.replace(/DATABASE_URL=.*(\r?\n|$)/,`${o}$1`):a+=`
98
+ ${o}`,await K.writeFile(t,a.trim())}catch(t){throw ie.error("Failed to update environment configuration"),t}}function tt(){W.info(`
99
+ ${$.green("MongoDB Atlas Manual Setup Instructions:")}
100
100
 
101
101
  1. Install Atlas CLI:
102
- ${D.blue("https://www.mongodb.com/docs/atlas/cli/stable/install-atlas-cli/")}
102
+ ${$.blue("https://www.mongodb.com/docs/atlas/cli/stable/install-atlas-cli/")}
103
103
 
104
104
  2. Run the following command and follow the prompts:
105
- ${D.blue("atlas deployments setup")}
105
+ ${$.blue("atlas deployments setup")}
106
106
 
107
107
  3. Get your connection string from the Atlas dashboard:
108
- Format: ${D.dim("mongodb+srv://USERNAME:PASSWORD@CLUSTER.mongodb.net/DATABASE_NAME")}
108
+ Format: ${$.dim("mongodb+srv://USERNAME:PASSWORD@CLUSTER.mongodb.net/DATABASE_NAME")}
109
109
 
110
110
  4. Add the connection string to your .env file:
111
- ${D.dim('DATABASE_URL="your_connection_string"')}
112
- `)}async function rt(e){let{projectName:r}=e,t=se.resolve(process.cwd(),r),a=tt();a.start("Setting up MongoDB Atlas");let o=se.join(t,"apps/server");try{await K.ensureDir(o),a.stop("Starting MongoDB Atlas setup");let n=await Or(o);n?(await we(t,n),W.success(D.green("MongoDB Atlas setup complete! Connection saved to .env file."))):(W.warn(D.yellow("Falling back to local MongoDB configuration")),await we(t),et())}catch(n){a.stop(D.red("MongoDB Atlas setup failed")),ie.error(D.red(`Error during MongoDB Atlas setup: ${n instanceof Error?n.message:String(n)}`));try{await we(t),et()}catch{}}}import M from"node:path";import{cancel as Ur,isCancel as _r,log as X,password as zr,spinner as Pe}from"@clack/prompts";import{consola as ve}from"consola";import{execa as Wr}from"execa";import N from"fs-extra";import q from"picocolors";async function qr(e,r){let t=Pe();try{t.start("Initializing Prisma PostgreSQL");let a=M.join(e,"prisma");await N.ensureDir(a),t.stop("Initializing Prisma. Follow the prompts below:");let o=R(r,"prisma init --db");await Wr(o,{cwd:e,stdio:"inherit",shell:!0}),X.info(q.yellow(`Please copy the Prisma Postgres URL from the output above.
113
- It looks like: prisma+postgres://accelerate.prisma-data.net/?api_key=...`));let n=await zr({message:"Paste your Prisma Postgres database URL:",validate(i){if(!i)return"Please enter a database URL";if(!i.startsWith("prisma+postgres://"))return"URL should start with prisma+postgres://"}});return _r(n)?(Ur("Database setup cancelled"),null):{databaseUrl:n}}catch(a){return t.stop(q.red("Failed to initialize Prisma PostgreSQL")),a instanceof Error&&ve.error(a.message),null}}async function ye(e,r){try{let t=M.join(e,"apps/server",".env");await N.ensureDir(M.dirname(t));let a="";await N.pathExists(t)&&(a=await N.readFile(t,"utf8"));let o=r?`DATABASE_URL="${r.databaseUrl}"`:'DATABASE_URL="postgresql://postgres:postgres@localhost:5432/mydb?schema=public"';a.includes("DATABASE_URL=")?a=a.replace(/DATABASE_URL=.*(\r?\n|$)/,`${o}$1`):a+=`
114
- ${o}`,await N.writeFile(t,a.trim())}catch(t){throw ve.error("Failed to update environment configuration"),t}}function at(){X.info(`Manual Prisma PostgreSQL Setup Instructions:
111
+ ${$.dim('DATABASE_URL="your_connection_string"')}
112
+ `)}async function at(e){let{projectName:r}=e,t=se.resolve(process.cwd(),r),a=rt();a.start("Setting up MongoDB Atlas");let o=se.join(t,"apps/server");try{await K.ensureDir(o),a.stop("Starting MongoDB Atlas setup");let n=await _r(o);n?(await we(t,n),W.success($.green("MongoDB Atlas setup complete! Connection saved to .env file."))):(W.warn($.yellow("Falling back to local MongoDB configuration")),await we(t),tt())}catch(n){a.stop($.red("MongoDB Atlas setup failed")),ie.error($.red(`Error during MongoDB Atlas setup: ${n instanceof Error?n.message:String(n)}`));try{await we(t),tt()}catch{}}}import M from"node:path";import{cancel as zr,isCancel as Wr,log as X,password as qr,spinner as Pe}from"@clack/prompts";import{consola as ve}from"consola";import{execa as Vr}from"execa";import N from"fs-extra";import q from"picocolors";async function Gr(e,r){let t=Pe();try{t.start("Initializing Prisma PostgreSQL");let a=M.join(e,"prisma");await N.ensureDir(a),t.stop("Initializing Prisma. Follow the prompts below:");let o=R(r,"prisma init --db");await Vr(o,{cwd:e,stdio:"inherit",shell:!0}),X.info(q.yellow(`Please copy the Prisma Postgres URL from the output above.
113
+ It looks like: prisma+postgres://accelerate.prisma-data.net/?api_key=...`));let n=await qr({message:"Paste your Prisma Postgres database URL:",validate(s){if(!s)return"Please enter a database URL";if(!s.startsWith("prisma+postgres://"))return"URL should start with prisma+postgres://"}});return Wr(n)?(zr("Database setup cancelled"),null):{databaseUrl:n}}catch(a){return t.stop(q.red("Failed to initialize Prisma PostgreSQL")),a instanceof Error&&ve.error(a.message),null}}async function ye(e,r){try{let t=M.join(e,"apps/server",".env");await N.ensureDir(M.dirname(t));let a="";await N.pathExists(t)&&(a=await N.readFile(t,"utf8"));let o=r?`DATABASE_URL="${r.databaseUrl}"`:'DATABASE_URL="postgresql://postgres:postgres@localhost:5432/mydb?schema=public"';a.includes("DATABASE_URL=")?a=a.replace(/DATABASE_URL=.*(\r?\n|$)/,`${o}$1`):a+=`
114
+ ${o}`,await N.writeFile(t,a.trim())}catch(t){throw ve.error("Failed to update environment configuration"),t}}function ot(){X.info(`Manual Prisma PostgreSQL Setup Instructions:
115
115
 
116
116
  1. Visit https://console.prisma.io and create an account
117
117
  2. Create a new PostgreSQL database from the dashboard
118
118
  3. Get your database URL
119
119
  4. Add the database URL to the .env file in apps/server/.env
120
120
 
121
- DATABASE_URL="your_database_url"`)}async function Vr(e){try{await h({dependencies:["@prisma/extension-accelerate"],projectDir:e});let r=M.join(e,"prisma/index.ts");await N.writeFile(r,`
121
+ DATABASE_URL="your_database_url"`)}async function Jr(e){try{await w({dependencies:["@prisma/extension-accelerate"],projectDir:e});let r=M.join(e,"prisma/index.ts");await N.writeFile(r,`
122
122
  import { PrismaClient } from '@prisma/client';
123
123
  import { withAccelerate } from "@prisma/extension-accelerate";
124
124
 
@@ -126,10 +126,10 @@ const prisma = new PrismaClient().$extends(withAccelerate());
126
126
 
127
127
  export default prisma;
128
128
  `.trim());let a=M.join(e,"src/db/index.ts");if(await N.pathExists(a)){let o=await N.readFile(a,"utf8");o.includes("@prisma/extension-accelerate")||(o=`import { withAccelerate } from "@prisma/extension-accelerate";
129
- ${o}`,o=o.replace("export const db = new PrismaClient();","export const db = new PrismaClient().$extends(withAccelerate());"),await N.writeFile(a,o))}return!0}catch{return X.warn(q.yellow("Could not add Prisma Accelerate extension automatically")),!1}}async function ot(e){let{projectName:r,packageManager:t}=e,a=M.resolve(process.cwd(),r),o=M.join(a,"apps/server"),n=Pe();n.start("Setting up Prisma PostgreSQL");try{await N.ensureDir(o),n.stop("Starting Prisma setup");let i=await qr(o,t);if(i)await ye(a,i),await Vr(o),X.success(q.green("Prisma PostgreSQL database configured successfully!"));else{let s=Pe();s.start("Setting up fallback configuration"),await ye(a),s.stop("Manual setup required"),at()}}catch(i){n.stop(q.red("Prisma PostgreSQL setup failed")),ve.error(q.red(`Error during Prisma PostgreSQL setup: ${i instanceof Error?i.message:String(i)}`));try{await ye(a),at()}catch{}X.info("Setup completed with manual configuration required.")}}import Gr from"node:os";import pe from"node:path";import{cancel as ke,confirm as Jr,isCancel as xe,log as V,select as Qr,spinner as G,text as Hr}from"@clack/prompts";import Yr from"consola";import{$ as I}from"execa";import nt from"fs-extra";import $ from"picocolors";async function Kr(){return ne("turso")}async function Xr(){try{return!(await I`turso auth whoami`).stdout.includes("You are not logged in")}catch{return!1}}async function Zr(){let e=G();try{return e.start("Logging in to Turso..."),await I`turso auth login`,e.stop("Logged in to Turso successfully!"),!0}catch(r){throw e.stop($.red("Failed to log in to Turso")),r}}async function ea(e){let r=G();try{if(r.start("Installing Turso CLI..."),e)await I`brew install tursodatabase/tap/turso`;else{let{stdout:t}=await I`curl -sSfL https://get.tur.so/install.sh`;await I`bash -c '${t}'`}return r.stop("Turso CLI installed successfully!"),!0}catch(t){throw t instanceof Error&&t.message.includes("User force closed")?(r.stop("Turso CLI installation cancelled"),V.warn($.yellow("Turso CLI installation cancelled by user")),new Error("Installation cancelled")):(r.stop($.red("Failed to install Turso CLI")),t)}}async function ta(){let e=G();try{e.start("Fetching Turso groups...");let{stdout:r}=await I`turso group list`,t=r.trim().split(`
130
- `);if(t.length<=1)return e.stop("No Turso groups found"),[];let a=t.slice(1).map(o=>{let[n,i,s,c]=o.trim().split(/\s{2,}/);return{name:n,locations:i,version:s,status:c}});return e.stop(`Found ${a.length} Turso groups`),a}catch(r){return e.stop($.red("Error fetching Turso groups")),console.error("Error fetching Turso groups:",r),[]}}async function ra(){let e=await ta();if(e.length===0)return null;if(e.length===1)return V.info(`Using the only available group: ${$.blue(e[0].name)}`),e[0].name;let r=e.map(a=>({value:a.name,label:`${a.name} (${a.locations})`})),t=await Qr({message:"Select a Turso database group:",options:r});return xe(t)&&(ke($.red("Operation cancelled")),process.exit(0)),t}async function aa(e,r){let t=G();try{t.start(`Creating Turso database "${e}"${r?` in group "${r}"`:""}...`),r?await I`turso db create ${e} --group ${r}`:await I`turso db create ${e}`,t.stop(`Created database "${e}"`)}catch(a){throw t.stop($.red(`Failed to create database "${e}"`)),a instanceof Error&&a.message.includes("already exists")?new Error("DATABASE_EXISTS"):a}t.start("Retrieving database connection details...");try{let{stdout:a}=await I`turso db show ${e} --url`,{stdout:o}=await I`turso db tokens create ${e}`;return t.stop("Retrieved database connection details"),{dbUrl:a.trim(),authToken:o.trim()}}catch(a){throw t.stop($.red("Failed to retrieve database connection details")),a}}async function ce(e,r){let t=pe.join(e,"apps/server",".env"),a=r?`DATABASE_URL="${r.dbUrl}"
129
+ ${o}`,o=o.replace("export const db = new PrismaClient();","export const db = new PrismaClient().$extends(withAccelerate());"),await N.writeFile(a,o))}return!0}catch{return X.warn(q.yellow("Could not add Prisma Accelerate extension automatically")),!1}}async function nt(e){let{projectName:r,packageManager:t}=e,a=M.resolve(process.cwd(),r),o=M.join(a,"apps/server"),n=Pe();n.start("Setting up Prisma PostgreSQL");try{await N.ensureDir(o),n.stop("Starting Prisma setup");let s=await Gr(o,t);if(s)await ye(a,s),await Jr(o),X.success(q.green("Prisma PostgreSQL database configured successfully!"));else{let i=Pe();i.start("Setting up fallback configuration"),await ye(a),i.stop("Manual setup required"),ot()}}catch(s){n.stop(q.red("Prisma PostgreSQL setup failed")),ve.error(q.red(`Error during Prisma PostgreSQL setup: ${s instanceof Error?s.message:String(s)}`));try{await ye(a),ot()}catch{}X.info("Setup completed with manual configuration required.")}}import Qr from"node:os";import pe from"node:path";import{cancel as ke,confirm as Hr,isCancel as xe,log as V,select as Yr,spinner as G,text as Kr}from"@clack/prompts";import Xr from"consola";import{$ as I}from"execa";import st from"fs-extra";import E from"picocolors";async function Zr(){return ne("turso")}async function ea(){try{return!(await I`turso auth whoami`).stdout.includes("You are not logged in")}catch{return!1}}async function ta(){let e=G();try{return e.start("Logging in to Turso..."),await I`turso auth login`,e.stop("Logged in to Turso successfully!"),!0}catch(r){throw e.stop(E.red("Failed to log in to Turso")),r}}async function ra(e){let r=G();try{if(r.start("Installing Turso CLI..."),e)await I`brew install tursodatabase/tap/turso`;else{let{stdout:t}=await I`curl -sSfL https://get.tur.so/install.sh`;await I`bash -c '${t}'`}return r.stop("Turso CLI installed successfully!"),!0}catch(t){throw t instanceof Error&&t.message.includes("User force closed")?(r.stop("Turso CLI installation cancelled"),V.warn(E.yellow("Turso CLI installation cancelled by user")),new Error("Installation cancelled")):(r.stop(E.red("Failed to install Turso CLI")),t)}}async function aa(){let e=G();try{e.start("Fetching Turso groups...");let{stdout:r}=await I`turso group list`,t=r.trim().split(`
130
+ `);if(t.length<=1)return e.stop("No Turso groups found"),[];let a=t.slice(1).map(o=>{let[n,s,i,c]=o.trim().split(/\s{2,}/);return{name:n,locations:s,version:i,status:c}});return e.stop(`Found ${a.length} Turso groups`),a}catch(r){return e.stop(E.red("Error fetching Turso groups")),console.error("Error fetching Turso groups:",r),[]}}async function oa(){let e=await aa();if(e.length===0)return null;if(e.length===1)return V.info(`Using the only available group: ${E.blue(e[0].name)}`),e[0].name;let r=e.map(a=>({value:a.name,label:`${a.name} (${a.locations})`})),t=await Yr({message:"Select a Turso database group:",options:r});return xe(t)&&(ke(E.red("Operation cancelled")),process.exit(0)),t}async function na(e,r){let t=G();try{t.start(`Creating Turso database "${e}"${r?` in group "${r}"`:""}...`),r?await I`turso db create ${e} --group ${r}`:await I`turso db create ${e}`,t.stop(`Created database "${e}"`)}catch(a){throw t.stop(E.red(`Failed to create database "${e}"`)),a instanceof Error&&a.message.includes("already exists")?new Error("DATABASE_EXISTS"):a}t.start("Retrieving database connection details...");try{let{stdout:a}=await I`turso db show ${e} --url`,{stdout:o}=await I`turso db tokens create ${e}`;return t.stop("Retrieved database connection details"),{dbUrl:a.trim(),authToken:o.trim()}}catch(a){throw t.stop(E.red("Failed to retrieve database connection details")),a}}async function ce(e,r){let t=pe.join(e,"apps/server",".env"),a=r?`DATABASE_URL="${r.dbUrl}"
131
131
  DATABASE_AUTH_TOKEN="${r.authToken}"`:`DATABASE_URL=
132
- DATABASE_AUTH_TOKEN=`;await nt.ensureDir(pe.dirname(t)),await nt.writeFile(t,a)}function je(){V.info(`Manual Turso Setup Instructions:
132
+ DATABASE_AUTH_TOKEN=`;await st.ensureDir(pe.dirname(t)),await st.writeFile(t,a)}function je(){V.info(`Manual Turso Setup Instructions:
133
133
 
134
134
  1. Visit https://turso.tech and create an account
135
135
  2. Create a new database from the dashboard
@@ -137,62 +137,62 @@ DATABASE_AUTH_TOKEN=`;await nt.ensureDir(pe.dirname(t)),await nt.writeFile(t,a)}
137
137
  4. Add these credentials to the .env file in apps/server/.env
138
138
 
139
139
  DATABASE_URL=your_database_url
140
- DATABASE_AUTH_TOKEN=your_auth_token`)}async function st(e){let{projectName:r,orm:t}=e,a=pe.resolve(process.cwd(),r),o=t==="drizzle",n=G();n.start("Setting up Turso database");try{let i=Gr.platform(),s=i==="darwin",c=i==="linux";if(i==="win32"){n.stop($.yellow("Turso setup not supported on Windows")),V.warn($.yellow("Automatic Turso setup is not supported on Windows.")),await ce(a),je();return}if(n.stop("Checking Turso CLI"),!await Kr()){let x=await Jr({message:"Would you like to install Turso CLI?",initialValue:!0});if(xe(x)&&(ke($.red("Operation cancelled")),process.exit(0)),!x){await ce(a),je();return}await ea(s)}await Xr()||await Zr();let f=await ra(),k=!1,S="",E=pe.basename(a);for(;!k;){let x=await Hr({message:"Enter a name for your database:",defaultValue:E,initialValue:E,placeholder:E});xe(x)&&(ke($.red("Operation cancelled")),process.exit(0)),S=x;try{let F=await aa(S,f),te=G();te.start("Writing configuration to .env file"),await ce(a,F),te.stop("Turso database configured successfully!"),k=!0}catch(F){if(F instanceof Error&&F.message==="DATABASE_EXISTS")V.warn($.yellow(`Database "${$.red(S)}" already exists`)),E=`${S}-${Math.floor(Math.random()*1e3)}`;else throw F}}}catch(i){n.stop($.red("Failed to set up Turso database")),Yr.error($.red(`Error during Turso setup: ${i instanceof Error?i.message:String(i)}`)),await ce(a),je(),V.success("Setup completed with manual configuration required.")}}import Z from"node:path";import{cancel as oa,isCancel as na,log as De,spinner as Se,text as sa}from"@clack/prompts";import{consola as le}from"consola";import{execa as ia}from"execa";import Ae from"fs-extra";import O from"picocolors";async function $e(e,r,t){let a=Se();try{let o=R(e,r);a&&a.start(t);let n=await ia(o,{shell:!0});return a&&a.stop(t),n}catch(o){throw a&&a.stop(O.red(`Failed: ${t}`)),o}}async function ca(e){try{let t=await $e(e,"neonctl projects list");return!t.stdout.includes("not authenticated")&&!t.stdout.includes("error")}catch{return!1}}async function pa(e){try{return await $e(e,"neonctl auth","Authenticating with Neon..."),De.success("Authenticated with Neon successfully!"),!0}catch(r){throw le.error(O.red("Failed to authenticate with Neon")),r}}async function la(e,r){try{let t=`neonctl projects create --name "${e}" --output json`,{stdout:a}=await $e(r,t,`Creating Neon project "${e}"...`),o=JSON.parse(a);if(o.project&&o.connection_uris&&o.connection_uris.length>0){let n=o.project.id,i=o.connection_uris[0].connection_uri,s=o.connection_uris[0].connection_parameters;return{connectionString:i,projectId:n,dbName:s.database,roleName:s.role}}return le.error(O.red("Failed to extract connection information from response")),null}catch(t){throw le.error(O.red("Failed to create Neon project")),t}}async function it(e,r){let t=Z.join(e,"apps/server",".env"),a=r?`DATABASE_URL="${r.connectionString}"`:'DATABASE_URL="postgresql://postgres:postgres@localhost:5432/mydb?schema=public"';return await Ae.ensureDir(Z.dirname(t)),await Ae.writeFile(t,a),!0}function da(){De.info(`Manual Neon PostgreSQL Setup Instructions:
140
+ DATABASE_AUTH_TOKEN=your_auth_token`)}async function it(e){let{projectName:r,orm:t}=e,a=pe.resolve(process.cwd(),r),o=t==="drizzle",n=G();n.start("Setting up Turso database");try{let s=Qr.platform(),i=s==="darwin",c=s==="linux";if(s==="win32"){n.stop(E.yellow("Turso setup not supported on Windows")),V.warn(E.yellow("Automatic Turso setup is not supported on Windows.")),await ce(a),je();return}if(n.stop("Checking Turso CLI"),!await Zr()){let S=await Hr({message:"Would you like to install Turso CLI?",initialValue:!0});if(xe(S)&&(ke(E.red("Operation cancelled")),process.exit(0)),!S){await ce(a),je();return}await ra(i)}await ea()||await ta();let p=await oa(),b=!1,j="",A=pe.basename(a);for(;!b;){let S=await Kr({message:"Enter a name for your database:",defaultValue:A,initialValue:A,placeholder:A});xe(S)&&(ke(E.red("Operation cancelled")),process.exit(0)),j=S;try{let F=await na(j,p),te=G();te.start("Writing configuration to .env file"),await ce(a,F),te.stop("Turso database configured successfully!"),b=!0}catch(F){if(F instanceof Error&&F.message==="DATABASE_EXISTS")V.warn(E.yellow(`Database "${E.red(j)}" already exists`)),A=`${j}-${Math.floor(Math.random()*1e3)}`;else throw F}}}catch(s){n.stop(E.red("Failed to set up Turso database")),Xr.error(E.red(`Error during Turso setup: ${s instanceof Error?s.message:String(s)}`)),await ce(a),je(),V.success("Setup completed with manual configuration required.")}}import Z from"node:path";import{cancel as sa,isCancel as ia,log as De,spinner as Ae,text as ca}from"@clack/prompts";import{consola as le}from"consola";import{execa as pa}from"execa";import Se from"fs-extra";import O from"picocolors";async function $e(e,r,t){let a=Ae();try{let o=R(e,r);a&&a.start(t);let n=await pa(o,{shell:!0});return a&&a.stop(t),n}catch(o){throw a&&a.stop(O.red(`Failed: ${t}`)),o}}async function la(e){try{let t=await $e(e,"neonctl projects list");return!t.stdout.includes("not authenticated")&&!t.stdout.includes("error")}catch{return!1}}async function da(e){try{return await $e(e,"neonctl auth","Authenticating with Neon..."),De.success("Authenticated with Neon successfully!"),!0}catch(r){throw le.error(O.red("Failed to authenticate with Neon")),r}}async function ua(e,r){try{let t=`neonctl projects create --name "${e}" --output json`,{stdout:a}=await $e(r,t,`Creating Neon project "${e}"...`),o=JSON.parse(a);if(o.project&&o.connection_uris&&o.connection_uris.length>0){let n=o.project.id,s=o.connection_uris[0].connection_uri,i=o.connection_uris[0].connection_parameters;return{connectionString:s,projectId:n,dbName:i.database,roleName:i.role}}return le.error(O.red("Failed to extract connection information from response")),null}catch(t){throw le.error(O.red("Failed to create Neon project")),t}}async function ct(e,r){let t=Z.join(e,"apps/server",".env"),a=r?`DATABASE_URL="${r.connectionString}"`:'DATABASE_URL="postgresql://postgres:postgres@localhost:5432/mydb?schema=public"';return await Se.ensureDir(Z.dirname(t)),await Se.writeFile(t,a),!0}function ma(){De.info(`Manual Neon PostgreSQL Setup Instructions:
141
141
 
142
142
  1. Visit https://neon.tech and create an account
143
143
  2. Create a new project from the dashboard
144
144
  3. Get your connection string
145
145
  4. Add the database URL to the .env file in apps/server/.env
146
146
 
147
- DATABASE_URL="your_connection_string"`)}async function ct(e){let{projectName:r,packageManager:t}=e,a=Z.resolve(process.cwd(),r),o=Se();o.start("Setting up Neon PostgreSQL");try{let n=await ca(t);o.stop("Setting up Neon PostgreSQL"),n||(De.info("Please authenticate with Neon to continue:"),await pa(t));let i=Z.basename(a),s=await sa({message:"Enter a name for your Neon project:",defaultValue:i,initialValue:i});na(s)&&(oa(O.red("Operation cancelled")),process.exit(0));let c=await la(s,t);if(!c)throw new Error("Failed to create project - couldn't get connection information");let p=Se();p.start("Configuring database connection"),await Ae.ensureDir(Z.join(a,"apps/server")),await it(a,c),p.stop("Neon database configured successfully!")}catch(n){o.stop(O.red("Neon PostgreSQL setup failed")),n instanceof Error&&le.error(O.red(n.message)),await it(a),da()}}async function lt(e){let{projectName:r,database:t,orm:a,dbSetup:o}=e,n=Ce.resolve(process.cwd(),r),i=ua(),s=Ce.join(n,"apps/server");if(t==="none"){await fa.remove(Ce.join(s,"src/db"));return}try{a==="prisma"?await h({dependencies:["@prisma/client"],devDependencies:["prisma"],projectDir:s}):a==="drizzle"&&(t==="sqlite"?await h({dependencies:["drizzle-orm","@libsql/client"],devDependencies:["drizzle-kit"],projectDir:s}):t==="postgres"?await h({dependencies:["drizzle-orm","pg"],devDependencies:["drizzle-kit","@types/pg"],projectDir:s}):t==="mysql"&&await h({dependencies:["drizzle-orm","mysql2"],devDependencies:["drizzle-kit"],projectDir:s})),t==="sqlite"&&o==="turso"?await st(e):t==="postgres"?a==="prisma"&&o==="prisma-postgres"?await ot(e):o==="neon"&&await ct(e):t==="mongodb"&&o==="mongodb-atlas"&&await rt(e)}catch(c){throw i.stop(pt.red("Failed to set up database")),c instanceof Error&&ma.error(pt.red(c.message)),c}}import L from"node:path";import de from"fs-extra";async function Ee(e,r){await de.ensureDir(L.dirname(e));let t="";await de.pathExists(e)&&(t=await de.readFile(e,"utf8"));let a=!1;for(let{key:o,value:n,condition:i}of r)if(i){let s=new RegExp(`^${o}=.*$`,"m");s.test(t)?n&&(t=t.replace(s,`${o}=${n}`),a=!0):(t+=`
148
- ${o}=${n}`,a=!0)}a&&await de.writeFile(e,t.trim())}async function dt(e){let{projectName:r}=e,t=L.resolve(process.cwd(),r),a=e,o=L.join(t,"apps/server"),n=L.join(o,".env"),i=a.frontend.includes("react-router"),s=a.frontend.includes("tanstack-router"),c=a.frontend.includes("tanstack-start"),p=a.frontend.includes("next"),w=i||s||c||p,y="http://localhost:3000";i?y="http://localhost:5173":(s||c||p)&&(y="http://localhost:3001");let f="",k=a.dbSetup==="turso"||a.dbSetup==="prisma-postgres"||a.dbSetup==="mongodb-atlas"||a.dbSetup==="neon";k||(a.database==="postgres"?f="postgresql://postgres:postgres@localhost:5432/mydb?schema=public":a.database==="mysql"?f="mysql://root:password@localhost:3306/mydb":a.database==="mongodb"?f="mongodb://localhost:27017/mydatabase":a.database==="sqlite"&&(f="file:./local.db"));let S=[{key:"CORS_ORIGIN",value:y,condition:!0},{key:"BETTER_AUTH_SECRET",value:Qe(),condition:!!a.auth},{key:"BETTER_AUTH_URL",value:"http://localhost:3000",condition:!!a.auth},{key:"DATABASE_URL",value:f,condition:a.database!=="none"&&f!==""&&!k},{key:"GOOGLE_GENERATIVE_AI_API_KEY",value:"",condition:a.examples?.includes("ai")||!1}];if(await Ee(n,S),w){let E=L.join(t,"apps/web"),x="VITE_SERVER_URL";p&&(x="NEXT_PUBLIC_SERVER_URL");let F=[{key:x,value:"http://localhost:3000",condition:!0}];await Ee(L.join(E,".env"),F)}if(a.frontend.includes("native")){let E=L.join(t,"apps/native"),x=[{key:"EXPO_PUBLIC_SERVER_URL",value:"http://localhost:3000",condition:!0}];await Ee(L.join(E,".env"),x)}}import Te from"node:path";async function ut(e){let{projectName:r,examples:t}=e,a=Te.resolve(process.cwd(),r);if(t.includes("ai")){let o=Te.join(a,"apps/web");await h({dependencies:["ai"],projectDir:o});let n=Te.join(a,"apps/server");await h({dependencies:["ai","@ai-sdk/google"],projectDir:n})}}import{log as ha,spinner as mt}from"@clack/prompts";import ga from"consola";import{$ as ft}from"execa";import ue from"picocolors";async function ht({projectDir:e,packageManager:r,addons:t=[]}){let a=mt();try{a.start(`Running ${r} install...`),await ft({cwd:e,stderr:"inherit"})`${r} install`,a.stop("Dependencies installed successfully"),(t.includes("biome")||t.includes("husky"))&&await ba(e,r)}catch(o){throw a.stop(ue.red("Failed to install dependencies")),o instanceof Error&&ga.error(ue.red(`Installation error: ${o.message}`)),o}}async function ba(e,r){let t=mt();try{t.start("Running Biome format check..."),await ft({cwd:e,stderr:"inherit"})`${r} biome check --write .`,t.stop("Biome check completed successfully")}catch{t.stop(ue.yellow("Biome check encountered issues")),ha.warn(ue.yellow("Some files may need manual formatting"))}}import{consola as wa}from"consola";import b from"picocolors";function gt(e){let{database:r,projectName:t,packageManager:a,depsInstalled:o,orm:n,addons:i,runtime:s,frontend:c}=e,p=a==="npm"?"npm run":a,w=`cd ${t}`,y=i?.includes("husky")||i?.includes("biome"),f=r!=="none"?va(r,n,p,s):"",k=i?.includes("tauri")?ja(p):"",S=y?Pa(p):"",E=c?.includes("native")?ya():"",x=i?.includes("pwa")&&c?.includes("react-router")?ka():"",F=i?.includes("starlight")?xa(p):"",te=c?.includes("tanstack-router"),cr=c?.includes("tanstack-start"),Ne=c?.includes("react-router"),Ie=te||Ne||cr,pr=c?.includes("native"),lr=Ie||pr,dr=Ne?"5173":"3001",ur=R(a,"taze -r");wa.box(`${b.bold("Next steps")}
149
- ${b.cyan("1.")} ${w}
150
- ${o?"":`${b.cyan("2.")} ${a} install
151
- `}${b.cyan(o?"2.":"3.")} ${p} dev
152
-
153
- ${b.bold("Your project will be available at:")}
154
- ${lr?`${Ie?`${b.cyan("\u2022")} Frontend: http://localhost:${dr}
155
- `:""}`:`${b.yellow("NOTE:")} You are creating a backend-only app (no frontend selected)
156
- `}${b.cyan("\u2022")} Backend: http://localhost:3000
157
- ${i?.includes("starlight")?`${b.cyan("\u2022")} Docs: http://localhost:4321
158
- `:""}${E?`
159
- ${E.trim()}`:""}${f?`
160
- ${f.trim()}`:""}${k?`
161
- ${k.trim()}`:""}${S?`
162
- ${S.trim()}`:""}${x?`
163
- ${x.trim()}`:""}${F?`
147
+ DATABASE_URL="your_connection_string"`)}async function pt(e){let{projectName:r,packageManager:t}=e,a=Z.resolve(process.cwd(),r),o=Ae();o.start("Setting up Neon PostgreSQL");try{let n=await la(t);o.stop("Setting up Neon PostgreSQL"),n||(De.info("Please authenticate with Neon to continue:"),await da(t));let s=Z.basename(a),i=await ca({message:"Enter a name for your Neon project:",defaultValue:s,initialValue:s});ia(i)&&(sa(O.red("Operation cancelled")),process.exit(0));let c=await ua(i,t);if(!c)throw new Error("Failed to create project - couldn't get connection information");let l=Ae();l.start("Configuring database connection"),await Se.ensureDir(Z.join(a,"apps/server")),await ct(a,c),l.stop("Neon database configured successfully!")}catch(n){o.stop(O.red("Neon PostgreSQL setup failed")),n instanceof Error&&le.error(O.red(n.message)),await ct(a),ma()}}async function dt(e){let{projectName:r,database:t,orm:a,dbSetup:o}=e,n=Ee.resolve(process.cwd(),r),s=fa(),i=Ee.join(n,"apps/server");if(t==="none"){await ga.remove(Ee.join(i,"src/db"));return}try{a==="prisma"?await w({dependencies:["@prisma/client"],devDependencies:["prisma"],projectDir:i}):a==="drizzle"&&(t==="sqlite"?await w({dependencies:["drizzle-orm","@libsql/client"],devDependencies:["drizzle-kit"],projectDir:i}):t==="postgres"?await w({dependencies:["drizzle-orm","pg"],devDependencies:["drizzle-kit","@types/pg"],projectDir:i}):t==="mysql"&&await w({dependencies:["drizzle-orm","mysql2"],devDependencies:["drizzle-kit"],projectDir:i})),t==="sqlite"&&o==="turso"?await it(e):t==="postgres"?a==="prisma"&&o==="prisma-postgres"?await nt(e):o==="neon"&&await pt(e):t==="mongodb"&&o==="mongodb-atlas"&&await at(e)}catch(c){throw s.stop(lt.red("Failed to set up database")),c instanceof Error&&ha.error(lt.red(c.message)),c}}import L from"node:path";import de from"fs-extra";async function Ce(e,r){await de.ensureDir(L.dirname(e));let t="";await de.pathExists(e)&&(t=await de.readFile(e,"utf8"));let a=!1;for(let{key:o,value:n,condition:s}of r)if(s){let i=new RegExp(`^${o}=.*$`,"m");i.test(t)?n&&(t=t.replace(i,`${o}=${n}`),a=!0):(t+=`
148
+ ${o}=${n}`,a=!0)}a&&await de.writeFile(e,t.trim())}async function ut(e){let{projectName:r}=e,t=L.resolve(process.cwd(),r),a=e,o=L.join(t,"apps/server"),n=L.join(o,".env"),s=a.frontend.includes("react-router"),i=a.frontend.includes("tanstack-router"),c=a.frontend.includes("tanstack-start"),l=a.frontend.includes("next"),d=s||i||c||l,y="http://localhost:3000";s?y="http://localhost:5173":(i||c||l)&&(y="http://localhost:3001");let p="",b=a.dbSetup==="turso"||a.dbSetup==="prisma-postgres"||a.dbSetup==="mongodb-atlas"||a.dbSetup==="neon";b||(a.database==="postgres"?p="postgresql://postgres:postgres@localhost:5432/mydb?schema=public":a.database==="mysql"?p="mysql://root:password@localhost:3306/mydb":a.database==="mongodb"?p="mongodb://localhost:27017/mydatabase":a.database==="sqlite"&&(p="file:./local.db"));let j=[{key:"CORS_ORIGIN",value:y,condition:!0},{key:"BETTER_AUTH_SECRET",value:Ye(),condition:!!a.auth},{key:"BETTER_AUTH_URL",value:"http://localhost:3000",condition:!!a.auth},{key:"DATABASE_URL",value:p,condition:a.database!=="none"&&p!==""&&!b},{key:"GOOGLE_GENERATIVE_AI_API_KEY",value:"",condition:a.examples?.includes("ai")||!1}];if(await Ce(n,j),d){let A=L.join(t,"apps/web"),S="VITE_SERVER_URL";l&&(S="NEXT_PUBLIC_SERVER_URL");let F=[{key:S,value:"http://localhost:3000",condition:!0}];await Ce(L.join(A,".env"),F)}if(a.frontend.includes("native")){let A=L.join(t,"apps/native"),S=[{key:"EXPO_PUBLIC_SERVER_URL",value:"http://localhost:3000",condition:!0}];await Ce(L.join(A,".env"),S)}}import Te from"node:path";import ba from"fs-extra";async function mt(e){let{projectName:r,examples:t}=e,a=Te.resolve(process.cwd(),r);if(t.includes("ai")){let o=Te.join(a,"apps/web"),n=Te.join(a,"apps/server");await ba.pathExists(o)&&await w({dependencies:["ai"],projectDir:o}),await w({dependencies:["ai","@ai-sdk/google"],projectDir:n})}}import{log as wa,spinner as ft}from"@clack/prompts";import ya from"consola";import{$ as ht}from"execa";import ue from"picocolors";async function gt({projectDir:e,packageManager:r,addons:t=[]}){let a=ft();try{a.start(`Running ${r} install...`),await ht({cwd:e,stderr:"inherit"})`${r} install`,a.stop("Dependencies installed successfully"),(t.includes("biome")||t.includes("husky"))&&await Pa(e,r)}catch(o){throw a.stop(ue.red("Failed to install dependencies")),o instanceof Error&&ya.error(ue.red(`Installation error: ${o.message}`)),o}}async function Pa(e,r){let t=ft();try{t.start("Running Biome format check..."),await ht({cwd:e,stderr:"inherit"})`${r} biome check --write .`,t.stop("Biome check completed successfully")}catch{t.stop(ue.yellow("Biome check encountered issues")),wa.warn(ue.yellow("Some files may need manual formatting"))}}import{consola as va}from"consola";import P from"picocolors";function bt(e){let{database:r,projectName:t,packageManager:a,depsInstalled:o,orm:n,addons:s,runtime:i,frontend:c}=e,l=a==="npm"?"npm run":a,d=`cd ${t}`,y=s?.includes("husky")||s?.includes("biome"),p=r!=="none"?xa(r,n,l,i):"",b=s?.includes("tauri")?Aa(l):"",j=y?ka(l):"",A=c?.includes("native")?ja():"",S=s?.includes("pwa")&&c?.includes("react-router")?Sa():"",F=s?.includes("starlight")?Da(l):"",te=c?.includes("tanstack-router"),pr=c?.includes("tanstack-start"),Ne=c?.includes("react-router"),Ie=te||Ne||pr,lr=c?.includes("native"),dr=Ie||lr,ur=Ne?"5173":"3001",mr=R(a,"taze -r");va.box(`${P.bold("Next steps")}
149
+ ${P.cyan("1.")} ${d}
150
+ ${o?"":`${P.cyan("2.")} ${a} install
151
+ `}${P.cyan(o?"2.":"3.")} ${l} dev
152
+
153
+ ${P.bold("Your project will be available at:")}
154
+ ${dr?`${Ie?`${P.cyan("\u2022")} Frontend: http://localhost:${ur}
155
+ `:""}`:`${P.yellow("NOTE:")} You are creating a backend-only app (no frontend selected)
156
+ `}${P.cyan("\u2022")} Backend: http://localhost:3000
157
+ ${s?.includes("starlight")?`${P.cyan("\u2022")} Docs: http://localhost:4321
158
+ `:""}${A?`
159
+ ${A.trim()}`:""}${p?`
160
+ ${p.trim()}`:""}${b?`
161
+ ${b.trim()}`:""}${j?`
162
+ ${j.trim()}`:""}${S?`
163
+ ${S.trim()}`:""}${F?`
164
164
  ${F.trim()}`:""}
165
165
 
166
- ${b.bold(`Update all dependencies:
167
- `)}${b.cyan(ur)}
166
+ ${P.bold(`Update all dependencies:
167
+ `)}${P.cyan(mr)}
168
168
 
169
- ${b.bold("Like Better-T Stack?")} Please consider giving us a star on GitHub:
170
- ${b.cyan("https://github.com/AmanVarshney01/create-better-t-stack")}`)}function ya(){return`${b.yellow("NOTE:")} For Expo connectivity issues, update apps/native/.env
169
+ ${P.bold("Like Better-T Stack?")} Please consider giving us a star on GitHub:
170
+ ${P.cyan("https://github.com/AmanVarshney01/create-better-t-stack")}`)}function ja(){return`${P.yellow("NOTE:")} For Expo connectivity issues, update apps/native/.env
171
171
  with your local IP:
172
172
  EXPO_PUBLIC_SERVER_URL=http://192.168.0.103:3000
173
- `}function Pa(e){return`${b.bold("Linting and formatting:")}
174
- ${b.cyan("\u2022")} Format and lint fix: ${`${e} check`}
173
+ `}function ka(e){return`${P.bold("Linting and formatting:")}
174
+ ${P.cyan("\u2022")} Format and lint fix: ${`${e} check`}
175
175
 
176
- `}function va(e,r,t,a){let o=[];return r==="prisma"?(e==="sqlite"&&o.push(`${b.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"),a==="bun"&&o.push(`${b.yellow("NOTE:")} Prisma with Bun may require additional configuration. If you encounter errors,
177
- follow the guidance provided in the error messages`),o.push(`${b.cyan("\u2022")} Apply schema: ${`${t} db:push`}`),o.push(`${b.cyan("\u2022")} Database UI: ${`${t} db:studio`}`)):r==="drizzle"&&(o.push(`${b.cyan("\u2022")} Apply schema: ${`${t} db:push`}`),o.push(`${b.cyan("\u2022")} Database UI: ${`${t} db:studio`}`)),o.length?`${b.bold("Database commands:")}
176
+ `}function xa(e,r,t,a){let o=[];return r==="prisma"?(e==="sqlite"&&o.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"),a==="bun"&&o.push(`${P.yellow("NOTE:")} Prisma with Bun may require additional configuration. If you encounter errors,
177
+ follow the guidance provided in the error messages`),o.push(`${P.cyan("\u2022")} Apply schema: ${`${t} db:push`}`),o.push(`${P.cyan("\u2022")} Database UI: ${`${t} db:studio`}`)):r==="drizzle"&&(o.push(`${P.cyan("\u2022")} Apply schema: ${`${t} db:push`}`),o.push(`${P.cyan("\u2022")} Database UI: ${`${t} db:studio`}`)),o.length?`${P.bold("Database commands:")}
178
178
  ${o.join(`
179
179
  `)}
180
180
 
181
- `:""}function ja(e){return`
182
- ${b.bold("Desktop app with Tauri:")}
183
- ${b.cyan("\u2022")} Start desktop app: ${`cd apps/web && ${e} desktop:dev`}
184
- ${b.cyan("\u2022")} Build desktop app: ${`cd apps/web && ${e} desktop:build`}
185
- ${b.yellow("NOTE:")} Tauri requires Rust and platform-specific dependencies.
181
+ `:""}function Aa(e){return`
182
+ ${P.bold("Desktop app with Tauri:")}
183
+ ${P.cyan("\u2022")} Start desktop app: ${`cd apps/web && ${e} desktop:dev`}
184
+ ${P.cyan("\u2022")} Build desktop app: ${`cd apps/web && ${e} desktop:build`}
185
+ ${P.yellow("NOTE:")} Tauri requires Rust and platform-specific dependencies.
186
186
  See: https://v2.tauri.app/start/prerequisites/
187
187
 
188
- `}function ka(){return`${b.bold("PWA with React Router v7:")}
189
- ${b.yellow("NOTE:")} There is a known compatibility issue between VitePWA and React Router v7.
188
+ `}function Sa(){return`${P.bold("PWA with React Router v7:")}
189
+ ${P.yellow("NOTE:")} There is a known compatibility issue between VitePWA and React Router v7.
190
190
  See: https://github.com/vite-pwa/vite-plugin-pwa/issues/809
191
- `}function xa(e){return`${b.bold("Documentation with Starlight:")}
192
- ${b.cyan("\u2022")} Start docs site: ${`cd apps/docs && ${e} dev`}
193
- ${b.cyan("\u2022")} Build docs site: ${`cd apps/docs && ${e} build`}
194
- `}import wt from"node:path";import{log as Sa}from"@clack/prompts";import{$ as bt,execa as Aa}from"execa";import J from"fs-extra";import Da from"picocolors";async function yt(e,r){await $a(e,r),await Ca(e,r)}async function $a(e,r){let t=wt.join(e,"package.json");if(await J.pathExists(t)){let a=await J.readJson(t);a.name=r.projectName;let o={dev:"turbo dev",build:"turbo build","check-types":"turbo check-types","dev:native":"turbo -F native dev","dev:web":"turbo -F web dev","dev:server":"turbo -F server dev","db:push":"turbo -F server db:push","db:studio":"turbo -F server db:studio"},n={dev:"pnpm -r dev",build:"pnpm -r build","check-types":"pnpm -r check-types","dev:native":"pnpm --filter native dev","dev:web":"pnpm --filter web dev","dev:server":"pnpm --filter server dev","db:push":"pnpm --filter server db:push","db:studio":"pnpm --filter server db:studio"},i={dev:"npm run dev --workspaces",build:"npm run build --workspaces","check-types":"npm run check-types --workspaces","dev:native":"npm run dev --workspace native","dev:web":"npm run dev --workspace web","dev:server":"npm run dev --workspace server","db:push":"npm run db:push --workspace server","db:studio":"npm run db:studio --workspace server"},s={dev:"bun run --filter '*' dev",build:"bun run --filter '*' build","check-types":"bun run --filter '*' check-types","dev:native":"bun run --filter native dev","dev:web":"bun run --filter web dev","dev:server":"bun run --filter server dev","db:push":"bun run --filter server db:push","db:studio":"bun run --filter server db:studio"};r.addons.includes("turborepo")?a.scripts=o:r.packageManager==="pnpm"?a.scripts=n:r.packageManager==="npm"?a.scripts=i:r.packageManager==="bun"?a.scripts=s:a.scripts={};let{stdout:c}=await Aa(r.packageManager,["-v"],{cwd:e});a.packageManager=`${r.packageManager}@${c.trim()}`,await J.writeJson(t,a,{spaces:2})}}async function Ca(e,r){let t=wt.join(e,"apps/server/package.json");if(await J.pathExists(t)){let a=await J.readJson(t);r.database!=="none"&&(r.database==="sqlite"&&r.orm==="drizzle"&&(a.scripts["db:local"]="turso dev --db-file local.db"),r.orm==="prisma"?(a.scripts["db:push"]="prisma db push --schema ./prisma/schema",a.scripts["db:studio"]="prisma studio"):r.orm==="drizzle"&&(a.scripts["db:push"]="drizzle-kit push",a.scripts["db:studio"]="drizzle-kit studio")),await J.writeJson(t,a,{spaces:2})}}async function Pt(e,r){if(!r)return;if((await bt({cwd:e,reject:!1,stderr:"pipe"})`git --version`).exitCode!==0){Sa.warn(Da.yellow("Git is not installed"));return}let a=await bt({cwd:e,reject:!1,stderr:"pipe"})`git init`;if(a.exitCode!==0)throw new Error(`Git initialization failed: ${a.stderr}`)}import me from"node:path";import fe from"fs-extra";async function vt(e){let{projectName:r,runtime:t,backend:a}=e,o=me.resolve(process.cwd(),r);if(a==="next")return;let n=me.join(o,"apps/server");t==="bun"?await Ea(n,a):t==="node"&&await Ta(n,a)}async function Ea(e,r){let t=me.join(e,"package.json"),a=await fe.readJson(t);a.scripts={...a.scripts,dev:"bun run --hot src/index.ts",start:"bun run dist/src/index.js"},await fe.writeJson(t,a,{spaces:2}),await h({devDependencies:["@types/bun"],projectDir:e})}async function Ta(e,r){let t=me.join(e,"package.json"),a=await fe.readJson(t);a.scripts={...a.scripts,dev:"tsx watch src/index.ts",start:"node dist/src/index.js"},await fe.writeJson(t,a,{spaces:2}),await h({devDependencies:["tsx","@types/node"],projectDir:e}),r==="hono"?await h({dependencies:["@hono/node-server"],projectDir:e}):r==="elysia"&&await h({dependencies:["@elysiajs/node"],projectDir:e})}import l from"node:path";import A from"consola";import m from"fs-extra";import{globby as jt}from"globby";import C from"picocolors";import Fa from"node:path";import Ra from"consola";import Fe from"fs-extra";import he from"handlebars";async function Re(e,r,t){try{let a=await Fe.readFile(e,"utf-8"),n=he.compile(a)(t);await Fe.ensureDir(Fa.dirname(r)),await Fe.writeFile(r,n)}catch(a){throw Ra.error(`Error processing template ${e}:`,a),new Error(`Failed to process template ${e}`)}}he.registerHelper("or",(e,r)=>e||r);he.registerHelper("eq",(e,r)=>e===r);he.registerHelper("includes",(e,r)=>Array.isArray(e)&&e.includes(r));async function j(e,r,t,a,o=!0){let n=await jt(e,{cwd:r,dot:!0,onlyFiles:!0,absolute:!1});for(let i of n){let s=l.join(r,i),c=i;i.endsWith(".hbs")&&(c=i.slice(0,-4));let p=l.join(t,c);if(await m.ensureDir(l.dirname(p)),s.endsWith(".hbs"))await Re(s,p,a);else{if(!o&&await m.pathExists(p))continue;await m.copy(s,p,{overwrite:!0})}}}async function kt(e,r){let t=l.join(P,"templates/base");await j(["package.json","_gitignore"],t,e,r)}async function xt(e,r){let t=r.frontend.filter(o=>o==="tanstack-router"||o==="react-router"||o==="tanstack-start"||o==="next"),a=r.frontend.includes("native");if(t.length>0){let o=l.join(e,"apps/web");await m.ensureDir(o);let n=l.join(P,"templates/frontend/web-base");await m.pathExists(n)&&await j("**/*",n,o,r);for(let p of t){let w=l.join(P,`templates/frontend/${p}`);await m.pathExists(w)&&await j("**/*",w,o,r)}let i=t[0],s=l.join(P,`templates/api/${r.api}/web/base`);await m.pathExists(s)&&await j("**/*",s,o,r);let c=l.join(P,`templates/api/${r.api}/web/${i}`);await m.pathExists(c)&&await j("**/*",c,o,r)}if(a){let o=l.join(e,"apps/native");await m.ensureDir(o);let n=l.join(P,"templates/frontend/native");await m.pathExists(n)&&await j("**/*",n,o,r);let i=l.join(P,`templates/api/${r.api}/native`);await m.pathExists(i)&&await j("**/*",i,o,r)}}async function St(e,r){if(r.backend==="none")return;let t=l.join(e,"apps/server");await m.ensureDir(t);let a=l.join(P,"templates/backend/server-base");await m.pathExists(a)?await j("**/*",a,t,r):A.warn(C.yellow(`Warning: server-base template not found at ${a}`));let o=l.join(P,`templates/backend/${r.backend}`);await m.pathExists(o)?await j("**/*",o,t,r):A.warn(C.yellow(`Warning: Backend template directory not found, skipping: ${o}`));let n=l.join(P,`templates/api/${r.api}/server/base`);await m.pathExists(n)&&await j("**/*",n,t,r);let i=l.join(P,`templates/api/${r.api}/server/${r.backend}`);await m.pathExists(i)&&await j("**/*",i,t,r)}async function At(e,r){if(r.orm==="none"||r.database==="none")return;let t=l.join(e,"apps/server");await m.ensureDir(t);let a=l.join(P,`templates/db/${r.orm}/${r.database}`);await m.pathExists(a)?await j("**/*",a,t,r):A.warn(C.yellow(`Warning: Database/ORM template directory not found, skipping: ${a}`))}async function Dt(e,r){if(!r.auth)return;let t=l.join(e,"apps/server"),a=l.join(e,"apps/web"),o=l.join(e,"apps/native"),n=r.frontend.filter(s=>s==="tanstack-router"||s==="react-router"||s==="tanstack-start"||s==="next"),i=r.frontend.includes("native");if(await m.pathExists(t)){let s=l.join(P,"templates/auth/server/base");await m.pathExists(s)?await j("**/*",s,t,r):A.warn(C.yellow(`Warning: Base auth server template not found at ${s}`));let c=l.join(P,"templates/auth/server/next");if(await m.pathExists(c)?await j("**/*",c,t,r):A.warn(C.yellow(`Warning: Next auth server template not found at ${c}`)),r.orm!=="none"&&r.database!=="none"){let p=r.orm,w=r.database,y="";p==="drizzle"?y=l.join(P,`templates/auth/server/db/drizzle/${w}`):p==="prisma"&&(y=l.join(P,`templates/auth/server/db/prisma/${w}`)),y&&await m.pathExists(y)?await j("**/*",y,t,r):A.warn(C.yellow(`Warning: Auth template for ${p}/${w} not found at ${y}`))}}else A.warn(C.yellow("Warning: apps/server directory does not exist, skipping server-side auth setup."));if(n.length>0&&await m.pathExists(a)){let s=l.join(P,"templates/auth/web/base");await m.pathExists(s)?await j("**/*",s,a,r):A.warn(C.yellow(`Warning: Base auth web template not found at ${s}`));for(let c of n){let p=l.join(P,`templates/auth/web/${c}`);await m.pathExists(p)?await j("**/*",p,a,r):A.warn(C.yellow(`Warning: Auth web template for ${c} not found at ${p}`))}}if(i&&await m.pathExists(o)){let s=l.join(P,"templates/auth/native");await m.pathExists(s)?await j("**/*",s,o,r):A.warn(C.yellow(`Warning: Auth native template not found at ${s}`))}}async function $t(e,r){if(r.addons.includes("turborepo")){let t=l.join(P,"templates/addons/turborepo");await m.pathExists(t)?await j("**/*",t,e,r):A.warn(C.yellow("Warning: Turborepo addon template not found."))}if(r.addons.includes("husky")){let t=l.join(P,"templates/addons/husky");await m.pathExists(t)?await j("**/*",t,e,r):A.warn(C.yellow("Warning: Husky addon template not found."))}if(r.addons.includes("biome")){let t=l.join(P,"templates/addons/biome");await m.pathExists(t)?await j("**/*",t,e,r):A.warn(C.yellow("Warning: Biome addon template not found."))}if(r.addons.includes("pwa")){let t=l.join(P,"templates/addons/pwa/apps/web"),a=l.join(e,"apps/web");await m.pathExists(t)?await m.pathExists(a)?await j("**/*",t,a,r):A.warn(C.yellow("Warning: apps/web directory not found, cannot setup PWA addon.")):A.warn(C.yellow("Warning: PWA addon template not found."))}}async function Ct(e,r){if(!r.examples||r.examples.length===0)return;let t=l.join(e,"apps/server"),a=l.join(e,"apps/web");for(let o of r.examples){let n=l.join(P,`templates/examples/${o}`);if(await m.pathExists(t)){let i=l.join(n,"server");if(await m.pathExists(i)&&r.orm!=="none"){let s=l.join(i,r.orm,"base");if(await m.pathExists(s)&&await j("**/*",s,t,r,!1),r.database!=="none"){let c=l.join(i,r.orm,r.database);await m.pathExists(c)&&await j("**/*",c,t,r,!1)}}}if(await m.pathExists(a)){let i=l.join(n,"web");if(await m.pathExists(i)){let s=r.frontend.filter(c=>["next","react-router","tanstack-router","tanstack-start"].includes(c));for(let c of s){let p=l.join(i,c);await m.pathExists(p)&&await j("**/*",p,a,r,!1)}}}}}async function Et(e,r){let t=await jt(["**/.gitignore.hbs","**/_gitignore"],{cwd:e,dot:!0,onlyFiles:!0,absolute:!0,ignore:["**/node_modules/**","**/.git/**"]});for(let a of t){let o=l.dirname(a),n=l.basename(a),i=l.join(o,".gitignore");try{n===".gitignore.hbs"?(await Re(a,i,r),await m.remove(a)):n==="_gitignore"&&await m.move(a,i,{overwrite:!0})}catch(s){A.error(`Error processing gitignore file ${a}:`,s)}}}async function Tt(e,r){if(r.packageManager==="pnpm"){let t=l.join(P,"templates/extras/pnpm-workspace.yaml"),a=l.join(e,"pnpm-workspace.yaml");await m.pathExists(t)?await m.copy(t,a):A.warn(C.yellow("Warning: pnpm-workspace.yaml template not found."))}}async function Rt(e){let r=La(),t=Ba.resolve(process.cwd(),e.projectName);try{return await Ma.ensureDir(t),await kt(t,e),await xt(t,e),await St(t,e),await Ke(e),await At(t,e),await lt(e),await Dt(t,e),await He(e),await $t(t,e),e.addons.length>0&&e.addons[0]!=="none"&&await qe(e),await Ct(t,e),await Tt(t,e),e.examples.length>0&&e.examples[0]!=="none"&&await ut(e),await Ve(e),await vt(e),await dt(e),await yt(t,e),await Xe(t,e),await Pt(t,e.git),await Et(t,e),Ia.success("Project template successfully scaffolded!"),e.install&&await ht({projectDir:t,packageManager:e.packageManager,addons:e.addons}),gt({...e,depsInstalled:e.install}),t}catch(a){throw r.stop(Ft.red("Failed")),a instanceof Error&&(Na(Ft.red(`Error during project creation: ${a.message}`)),console.error(a.stack),process.exit(1)),a}}import{cancel as _o,group as zo}from"@clack/prompts";import Wo from"picocolors";import{cancel as Oa,isCancel as Ua,multiselect as _a}from"@clack/prompts";import za from"picocolors";async function Bt(e,r){if(e!==void 0)return e;let t=r?.includes("react-router")||r?.includes("tanstack-router"),a=[{value:"turborepo",label:"Turborepo (Recommended)",hint:"Optimize builds for monorepos"},{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)"}],o=[{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"}],n=t?[...a,...o]:a,i=g.addons.filter(c=>t||c!=="pwa"&&c!=="tauri"),s=await _a({message:"Select addons",options:n,initialValues:i,required:!1});return Ua(s)&&(Oa(za.red("Operation cancelled")),process.exit(0)),s.includes("husky")&&!s.includes("biome")&&s.push("biome"),s}import{cancel as Wa,isCancel as qa,select as Va}from"@clack/prompts";import Ga from"picocolors";async function Nt(e,r){if(e)return e;let t=r?.includes("native"),a=[{value:"trpc",label:"tRPC",hint:"End-to-end typesafe APIs made easy"},{value:"orpc",label:"oRPC",hint:"End-to-end type-safe APIs that adhere to OpenAPI standards"}];t&&(a=[{value:"trpc",label:"tRPC",hint:"End-to-end typesafe APIs made easy (Required for Native frontend)"}]);let o=await Va({message:"Select API type",options:a,initialValue:t?"trpc":g.api});return qa(o)&&(Wa(Ga.red("Operation cancelled")),process.exit(0)),t&&o!=="trpc"?"trpc":o}import{cancel as Ja,confirm as Qa,isCancel as Ha}from"@clack/prompts";import Ya from"picocolors";async function It(e,r){if(!r)return!1;if(e!==void 0)return e;let t=await Qa({message:"Add authentication with Better-Auth?",initialValue:g.auth});return Ha(t)&&(Ja(Ya.red("Operation cancelled")),process.exit(0)),t}import{cancel as Ka,isCancel as Xa,select as Za}from"@clack/prompts";import eo from"picocolors";async function Lt(e){if(e!==void 0)return e;let r=await Za({message:"Select backend framework",options:[{value:"hono",label:"Hono",hint:"Lightweight, ultrafast web framework"},{value:"next",label:"Next.js",hint:"Full-stack framework with API routes"},{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:g.backend});return Xa(r)&&(Ka(eo.red("Operation cancelled")),process.exit(0)),r}import{cancel as to,isCancel as ro,select as ao}from"@clack/prompts";import oo from"picocolors";async function Mt(e){if(e!==void 0)return e;let r=await ao({message:"Select database",options:[{value:"none",label:"None",hint:"No database setup"},{value:"sqlite",label:"SQLite",hint:"lightweight, server-less, embedded relational database"},{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:g.database});return ro(r)&&(to(oo.red("Operation cancelled")),process.exit(0)),r}import{cancel as no,isCancel as so,select as io}from"@clack/prompts";import co from"picocolors";async function Ot(e,r,t){if(r!==void 0)return r;if(e==="sqlite"&&t==="prisma")return"none";let a=[];if(e==="sqlite")a=[{value:"turso",label:"Turso",hint:"SQLite for Production. Powered by libSQL"},{value:"none",label:"None",hint:"Manual setup"}];else if(e==="postgres")a=[{value:"neon",label:"Neon Postgres",hint:"Serverless Postgres with branching capability"},...t==="prisma"?[{value:"prisma-postgres",label:"Prisma Postgres",hint:"Instant Postgres for Global Applications"}]:[],{value:"none",label:"None",hint:"Manual setup"}];else if(e==="mongodb")a=[{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 o=await io({message:`Select ${e} setup option`,options:a,initialValue:"none"});return so(o)&&(no(co.red("Operation cancelled")),process.exit(0)),o}import{cancel as po,isCancel as lo,multiselect as Ut}from"@clack/prompts";import uo from"picocolors";async function _t(e,r,t,a){if(e!==void 0)return e;if(r==="none")return[];if(!(t?.includes("react-router")||t?.includes("tanstack-router")||t?.includes("tanstack-start")))return[];let n=[];return a==="elysia"&&(n=await Ut({message:"Include examples",options:[{value:"todo",label:"Todo App",hint:"A simple CRUD example app"}],required:!1,initialValues:g.examples})),(a==="hono"||a==="express")&&(n=await Ut({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:g.examples})),lo(n)&&(po(uo.red("Operation cancelled")),process.exit(0)),n}import{cancel as zt,isCancel as Wt,multiselect as mo,select as fo}from"@clack/prompts";import qt from"picocolors";async function Vt(e){if(e!==void 0)return e;let r=await mo({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:g.frontend.some(a=>a==="tanstack-router"||a==="react-router"||a==="tanstack-start"||a==="next")?["web"]:[]});Wt(r)&&(zt(qt.red("Operation cancelled")),process.exit(0));let t=[];if(r.includes("web")){let a=await fo({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"},{value:"next",label:"Next.js",hint:"The React Framework for the Web"},{value:"tanstack-start",label:"TanStack Start (beta)",hint:"SSR, Server Functions, API Routes and more with TanStack Router"}],initialValue:g.frontend.find(o=>o==="tanstack-router"||o==="react-router"||o==="tanstack-start"||o==="next")||"tanstack-router"});Wt(a)&&(zt(qt.red("Operation cancelled")),process.exit(0)),t.push(a)}return r.includes("native")&&t.push("native"),t}import{cancel as ho,confirm as go,isCancel as bo}from"@clack/prompts";import wo from"picocolors";async function Gt(e){if(e!==void 0)return e;let r=await go({message:"Initialize git repository?",initialValue:g.git});return bo(r)&&(ho(wo.red("Operation cancelled")),process.exit(0)),r}import{cancel as yo,confirm as Po,isCancel as vo}from"@clack/prompts";import jo from"picocolors";async function Jt(e){if(e!==void 0)return e;let r=await Po({message:"Install dependencies?",initialValue:g.install});return vo(r)&&(yo(jo.red("Operation cancelled")),process.exit(0)),r}import{cancel as ko,isCancel as xo,log as So,select as Ao}from"@clack/prompts";import Do from"picocolors";async function Qt(e,r,t){if(!r)return"none";if(e!==void 0)return e;if(t==="mongodb")return So.info("Only Prisma is supported with MongoDB."),"prisma";let a=await Ao({message:"Select ORM",options:[{value:"drizzle",label:"Drizzle",hint:"lightweight and performant TypeScript ORM"},{value:"prisma",label:"Prisma",hint:"Powerful, feature-rich ORM"}],initialValue:g.orm});return xo(a)&&(ko(Do.red("Operation cancelled")),process.exit(0)),a}import{cancel as $o,isCancel as Co,select as Eo}from"@clack/prompts";import To from"picocolors";async function Ht(e){if(e!==void 0)return e;let r=re(),t=await Eo({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:r});return Co(t)&&($o(To.red("Operation cancelled")),process.exit(0)),t}import ee from"node:path";import{cancel as Fo,isCancel as Ro,text as Bo}from"@clack/prompts";import U from"fs-extra";import No from"picocolors";var Io=["<",">",":",'"',"|","?","*"],Yt=255;function Kt(e){if(e!=="."){if(!e)return"Project name cannot be empty";if(e.length>Yt)return`Project name must be less than ${Yt} characters`;if(Io.some(r=>e.includes(r)))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 ge(e){if(e)if(e==="."){let n=process.cwd();if(U.readdirSync(n).length===0)return e}else{let n=ee.basename(e);if(!Kt(n)){let s=ee.resolve(process.cwd(),e);if(!U.pathExistsSync(s)||U.readdirSync(s).length===0)return e}}let r=!1,t="",a=g.projectName,o=1;for(;U.pathExistsSync(ee.resolve(process.cwd(),a));)a=`${g.projectName}-${o}`,o++;for(;!r;){let n=await Bo({message:"Enter your project name or path (relative to current directory)",placeholder:a,initialValue:e,defaultValue:a,validate:i=>{let s=i.trim()||a;if(s==="."){if(U.readdirSync(process.cwd()).length>0)return"Current directory is not empty. Please choose a different directory.";r=!0;return}let c=ee.resolve(process.cwd(),s),p=ee.basename(c),w=Kt(p);if(w)return w;if(!c.startsWith(process.cwd()))return"Project path must be within current directory";if(U.pathExistsSync(c)&&U.readdirSync(c).length>0)return`Directory "${s}" already exists and is not empty. Please choose a different name or path.`;r=!0}});Ro(n)&&(Fo(No.red("Operation cancelled.")),process.exit(0)),t=n||a}return t}import{cancel as Lo,isCancel as Mo,select as Oo}from"@clack/prompts";import Uo from"picocolors";async function Xt(e,r){if(e!==void 0)return e;if(r==="next")return"node";let t=await Oo({message:"Select runtime",options:[{value:"bun",label:"Bun",hint:"Fast all-in-one JavaScript runtime"},{value:"node",label:"Node.js",hint:"Traditional Node.js runtime"}],initialValue:g.runtime});return Mo(t)&&(Lo(Uo.red("Operation cancelled")),process.exit(0)),t}async function Zt(e){let r=await zo({projectName:async()=>ge(e.projectName),frontend:()=>Vt(e.frontend),backend:()=>Lt(e.backend),runtime:({results:t})=>Xt(e.runtime,t.backend),database:()=>Mt(e.database),orm:({results:t})=>Qt(e.orm,t.database!=="none",t.database),api:({results:t})=>Nt(e.api,t.frontend),auth:({results:t})=>It(e.auth,t.database!=="none"),addons:({results:t})=>Bt(e.addons,t.frontend),examples:({results:t})=>_t(e.examples,t.database,t.frontend,t.backend),dbSetup:({results:t})=>Ot(t.database??"none",e.dbSetup,t.orm),git:()=>Gt(e.git),packageManager:()=>Ht(e.packageManager),install:()=>Jt(e.install)},{onCancel:()=>{_o(Wo.red("Operation cancelled")),process.exit(0)}});return{projectName:r.projectName,frontend:r.frontend,database:r.database,orm:r.orm,auth:r.auth,addons:r.addons,examples:r.examples,git:r.git,packageManager:r.packageManager,install:r.install,dbSetup:r.dbSetup,backend:r.backend,runtime:r.runtime,api:r.api}}import T from"picocolors";function Be(e){let r=[];if(e.projectName&&r.push(`${T.blue("Project Name:")} ${e.projectName}`),e.frontend!==void 0){let t=Array.isArray(e.frontend)?e.frontend:[e.frontend],a=t.length>0&&t[0]!==void 0&&t[0]!==""?t.join(", "):"none";r.push(`${T.blue("Frontend:")} ${a}`)}if(e.backend!==void 0&&r.push(`${T.blue("Backend Framework:")} ${String(e.backend)}`),e.runtime!==void 0&&r.push(`${T.blue("Runtime:")} ${String(e.runtime)}`),e.api!==void 0&&r.push(`${T.blue("API:")} ${String(e.api)}`),e.database!==void 0&&r.push(`${T.blue("Database:")} ${String(e.database)}`),e.orm!==void 0&&r.push(`${T.blue("ORM:")} ${String(e.orm)}`),e.auth!==void 0){let t=typeof e.auth=="boolean"?e.auth?"Yes":"No":String(e.auth);r.push(`${T.blue("Authentication:")} ${t}`)}if(e.addons!==void 0){let t=Array.isArray(e.addons)?e.addons:[e.addons],a=t.length>0&&t[0]!==void 0?t.join(", "):"none";r.push(`${T.blue("Addons:")} ${a}`)}if(e.examples!==void 0){let t=Array.isArray(e.examples)?e.examples:[e.examples],a=t.length>0&&t[0]!==void 0?t.join(", "):"none";r.push(`${T.blue("Examples:")} ${a}`)}if(e.git!==void 0){let t=typeof e.git=="boolean"?e.git?"Yes":"No":String(e.git);r.push(`${T.blue("Git Init:")} ${t}`)}if(e.packageManager!==void 0&&r.push(`${T.blue("Package Manager:")} ${String(e.packageManager)}`),e.install!==void 0){let t=typeof e.install=="boolean"?e.install?"Yes":"No":String(e.install);r.push(`${T.blue("Install Dependencies:")} ${t}`)}return e.dbSetup!==void 0&&r.push(`${T.blue("Database Setup:")} ${String(e.dbSetup)}`),r.length===0?T.yellow("No configuration selected."):r.join(`
195
- `)}function er(e){let r=[];e.database==="none"?r.push("--database none"):(r.push(`--database ${e.database}`),e.orm&&r.push(`--orm ${e.orm}`),e.dbSetup&&r.push(`--db-setup ${e.dbSetup}`)),e.api&&r.push(`--api ${e.api}`),r.push(e.auth?"--auth":"--no-auth"),r.push(e.git?"--git":"--no-git"),r.push(e.install?"--install":"--no-install"),e.runtime&&r.push(`--runtime ${e.runtime}`),e.backend&&r.push(`--backend ${e.backend}`),e.frontend&&e.frontend.length>0&&r.push(`--frontend ${e.frontend.join(" ")}`),e.addons&&e.addons.length>0?r.push(`--addons ${e.addons.join(" ")}`):r.push("--addons none"),e.examples&&e.examples.length>0?r.push(`--examples ${e.examples.join(" ")}`):r.push("--examples none"),e.packageManager&&r.push(`--package-manager ${e.packageManager}`);let t="",a=e.packageManager;a==="npm"?t="npx create-better-t-stack@latest":a==="pnpm"?t="pnpm create better-t-stack@latest":a==="bun"&&(t="bun create better-t-stack@latest");let o=e.projectName?` ${e.projectName}`:"";return`${t}${o} ${r.join(" ")}`}import qo from"node:path";import Vo from"fs-extra";var tr=()=>{let e=qo.join(P,"package.json");return Vo.readJSONSync(e).version??"1.0.0"};import rr from"gradient-string";var ar=`
191
+ `}function Da(e){return`${P.bold("Documentation with Starlight:")}
192
+ ${P.cyan("\u2022")} Start docs site: ${`cd apps/docs && ${e} dev`}
193
+ ${P.cyan("\u2022")} Build docs site: ${`cd apps/docs && ${e} build`}
194
+ `}import yt from"node:path";import{log as $a}from"@clack/prompts";import{$ as wt,execa as Ea}from"execa";import J from"fs-extra";import Ca from"picocolors";async function Pt(e,r){await Ta(e,r),await Fa(e,r)}async function Ta(e,r){let t=yt.join(e,"package.json");if(await J.pathExists(t)){let a=await J.readJson(t);a.name=r.projectName;let o={dev:"turbo dev",build:"turbo build","check-types":"turbo check-types","dev:native":"turbo -F native dev","dev:web":"turbo -F web dev","dev:server":"turbo -F server dev","db:push":"turbo -F server db:push","db:studio":"turbo -F server db:studio"},n={dev:"pnpm -r dev",build:"pnpm -r build","check-types":"pnpm -r check-types","dev:native":"pnpm --filter native dev","dev:web":"pnpm --filter web dev","dev:server":"pnpm --filter server dev","db:push":"pnpm --filter server db:push","db:studio":"pnpm --filter server db:studio"},s={dev:"npm run dev --workspaces",build:"npm run build --workspaces","check-types":"npm run check-types --workspaces","dev:native":"npm run dev --workspace native","dev:web":"npm run dev --workspace web","dev:server":"npm run dev --workspace server","db:push":"npm run db:push --workspace server","db:studio":"npm run db:studio --workspace server"},i={dev:"bun run --filter '*' dev",build:"bun run --filter '*' build","check-types":"bun run --filter '*' check-types","dev:native":"bun run --filter native dev","dev:web":"bun run --filter web dev","dev:server":"bun run --filter server dev","db:push":"bun run --filter server db:push","db:studio":"bun run --filter server db:studio"};r.addons.includes("turborepo")?a.scripts=o:r.packageManager==="pnpm"?a.scripts=n:r.packageManager==="npm"?a.scripts=s:r.packageManager==="bun"?a.scripts=i:a.scripts={};let{stdout:c}=await Ea(r.packageManager,["-v"],{cwd:e});a.packageManager=`${r.packageManager}@${c.trim()}`,await J.writeJson(t,a,{spaces:2})}}async function Fa(e,r){let t=yt.join(e,"apps/server/package.json");if(await J.pathExists(t)){let a=await J.readJson(t);r.database!=="none"&&(r.database==="sqlite"&&r.orm==="drizzle"&&(a.scripts["db:local"]="turso dev --db-file local.db"),r.orm==="prisma"?(a.scripts["db:push"]="prisma db push --schema ./prisma/schema",a.scripts["db:studio"]="prisma studio"):r.orm==="drizzle"&&(a.scripts["db:push"]="drizzle-kit push",a.scripts["db:studio"]="drizzle-kit studio")),await J.writeJson(t,a,{spaces:2})}}async function vt(e,r){if(!r)return;if((await wt({cwd:e,reject:!1,stderr:"pipe"})`git --version`).exitCode!==0){$a.warn(Ca.yellow("Git is not installed"));return}let a=await wt({cwd:e,reject:!1,stderr:"pipe"})`git init`;if(a.exitCode!==0)throw new Error(`Git initialization failed: ${a.stderr}`)}import me from"node:path";import fe from"fs-extra";async function jt(e){let{projectName:r,runtime:t,backend:a}=e,o=me.resolve(process.cwd(),r);if(a==="next")return;let n=me.join(o,"apps/server");t==="bun"?await Ra(n,a):t==="node"&&await Ba(n,a)}async function Ra(e,r){let t=me.join(e,"package.json"),a=await fe.readJson(t);a.scripts={...a.scripts,dev:"bun run --hot src/index.ts",start:"bun run dist/src/index.js"},await fe.writeJson(t,a,{spaces:2}),await w({devDependencies:["@types/bun"],projectDir:e})}async function Ba(e,r){let t=me.join(e,"package.json"),a=await fe.readJson(t);a.scripts={...a.scripts,dev:"tsx watch src/index.ts",start:"node dist/src/index.js"},await fe.writeJson(t,a,{spaces:2}),await w({devDependencies:["tsx","@types/node"],projectDir:e}),r==="hono"?await w({dependencies:["@hono/node-server"],projectDir:e}):r==="elysia"&&await w({dependencies:["@elysiajs/node"],projectDir:e})}import u from"node:path";import D from"consola";import h from"fs-extra";import{globby as kt}from"globby";import C from"picocolors";import Na from"node:path";import Ia from"consola";import Fe from"fs-extra";import he from"handlebars";async function Re(e,r,t){try{let a=await Fe.readFile(e,"utf-8"),n=he.compile(a)(t);await Fe.ensureDir(Na.dirname(r)),await Fe.writeFile(r,n)}catch(a){throw Ia.error(`Error processing template ${e}:`,a),new Error(`Failed to process template ${e}`)}}he.registerHelper("or",(e,r)=>e||r);he.registerHelper("eq",(e,r)=>e===r);he.registerHelper("includes",(e,r)=>Array.isArray(e)&&e.includes(r));async function x(e,r,t,a,o=!0){let n=await kt(e,{cwd:r,dot:!0,onlyFiles:!0,absolute:!1});for(let s of n){let i=u.join(r,s),c=s;s.endsWith(".hbs")&&(c=s.slice(0,-4));let l=u.join(t,c);if(await h.ensureDir(u.dirname(l)),i.endsWith(".hbs"))await Re(i,l,a);else{if(!o&&await h.pathExists(l))continue;await h.copy(i,l,{overwrite:!0})}}}async function xt(e,r){let t=u.join(v,"templates/base");await x(["package.json","_gitignore"],t,e,r)}async function At(e,r){let t=r.frontend.filter(o=>o==="tanstack-router"||o==="react-router"||o==="tanstack-start"||o==="next"),a=r.frontend.includes("native");if(t.length>0){let o=u.join(e,"apps/web");await h.ensureDir(o);let n=u.join(v,"templates/frontend/web-base");await h.pathExists(n)&&await x("**/*",n,o,r);for(let l of t){let d=u.join(v,`templates/frontend/${l}`);await h.pathExists(d)&&await x("**/*",d,o,r)}let s=t[0],i=u.join(v,`templates/api/${r.api}/web/base`);await h.pathExists(i)&&await x("**/*",i,o,r);let c=u.join(v,`templates/api/${r.api}/web/${s}`);await h.pathExists(c)&&await x("**/*",c,o,r)}if(a){let o=u.join(e,"apps/native");await h.ensureDir(o);let n=u.join(v,"templates/frontend/native");await h.pathExists(n)&&await x("**/*",n,o,r);let s=u.join(v,`templates/api/${r.api}/native`);await h.pathExists(s)&&await x("**/*",s,o,r)}}async function St(e,r){if(r.backend==="none")return;let t=u.join(e,"apps/server");await h.ensureDir(t);let a=u.join(v,"templates/backend/server-base");await h.pathExists(a)?await x("**/*",a,t,r):D.warn(C.yellow(`Warning: server-base template not found at ${a}`));let o=u.join(v,`templates/backend/${r.backend}`);await h.pathExists(o)?await x("**/*",o,t,r):D.warn(C.yellow(`Warning: Backend template directory not found, skipping: ${o}`));let n=u.join(v,`templates/api/${r.api}/server/base`);await h.pathExists(n)&&await x("**/*",n,t,r);let s=u.join(v,`templates/api/${r.api}/server/${r.backend}`);await h.pathExists(s)&&await x("**/*",s,t,r)}async function Dt(e,r){if(r.orm==="none"||r.database==="none")return;let t=u.join(e,"apps/server");await h.ensureDir(t);let a=u.join(v,`templates/db/${r.orm}/${r.database}`);await h.pathExists(a)?await x("**/*",a,t,r):D.warn(C.yellow(`Warning: Database/ORM template directory not found, skipping: ${a}`))}async function $t(e,r){if(!r.auth)return;let t=u.join(e,"apps/server"),a=u.join(e,"apps/web"),o=u.join(e,"apps/native"),n=await h.pathExists(t),s=await h.pathExists(a),i=await h.pathExists(o),c=r.frontend.filter(d=>d==="tanstack-router"||d==="react-router"||d==="tanstack-start"||d==="next"),l=r.frontend.includes("native");if(n){let d=u.join(v,"templates/auth/server/base");if(await h.pathExists(d)?await x("**/*",d,t,r):D.warn(C.yellow(`Warning: Base auth server template not found at ${d}`)),r.backend==="next"){let y=u.join(v,"templates/auth/server/next");await h.pathExists(y)?await x("**/*",y,t,r):D.warn(C.yellow(`Warning: Next auth server template not found at ${y}`))}if(r.orm!=="none"&&r.database!=="none"){let y=r.orm,p=r.database,b="";y==="drizzle"?b=u.join(v,`templates/auth/server/db/drizzle/${p}`):y==="prisma"&&(b=u.join(v,`templates/auth/server/db/prisma/${p}`)),b&&await h.pathExists(b)?await x("**/*",b,t,r):D.warn(C.yellow(`Warning: Auth template for ${y}/${p} not found at ${b}`))}}else D.warn(C.yellow("Warning: apps/server directory does not exist, skipping server-side auth template setup."));if(c.length>0&&s){let d=u.join(v,"templates/auth/web/base");await h.pathExists(d)?await x("**/*",d,a,r):D.warn(C.yellow(`Warning: Base auth web template not found at ${d}`));for(let y of c){let p=u.join(v,`templates/auth/web/${y}`);await h.pathExists(p)?await x("**/*",p,a,r):D.warn(C.yellow(`Warning: Auth web template for ${y} not found at ${p}`))}}if(l&&i){let d=u.join(v,"templates/auth/native");await h.pathExists(d)?await x("**/*",d,o,r):D.warn(C.yellow(`Warning: Auth native template not found at ${d}`))}}async function Et(e,r){if(r.addons.includes("turborepo")){let t=u.join(v,"templates/addons/turborepo");await h.pathExists(t)?await x("**/*",t,e,r):D.warn(C.yellow("Warning: Turborepo addon template not found."))}if(r.addons.includes("husky")){let t=u.join(v,"templates/addons/husky");await h.pathExists(t)?await x("**/*",t,e,r):D.warn(C.yellow("Warning: Husky addon template not found."))}if(r.addons.includes("biome")){let t=u.join(v,"templates/addons/biome");await h.pathExists(t)?await x("**/*",t,e,r):D.warn(C.yellow("Warning: Biome addon template not found."))}if(r.addons.includes("pwa")){let t=u.join(v,"templates/addons/pwa/apps/web"),a=u.join(e,"apps/web"),o=await h.pathExists(a);await h.pathExists(t)?o?await x("**/*",t,a,r):D.warn(C.yellow("Warning: apps/web directory not found, cannot setup PWA addon template.")):D.warn(C.yellow("Warning: PWA addon template not found."))}}async function Ct(e,r){if(!r.examples||r.examples.length===0)return;let t=u.join(e,"apps/server"),a=u.join(e,"apps/web"),o=await h.pathExists(t),n=await h.pathExists(a);for(let s of r.examples){let i=u.join(v,`templates/examples/${s}`);if(o){let c=u.join(i,"server");if(await h.pathExists(c)&&r.orm!=="none"){let l=u.join(c,r.orm,"base");if(await h.pathExists(l)&&await x("**/*",l,t,r,!1),r.database!=="none"){let d=u.join(c,r.orm,r.database);await h.pathExists(d)&&await x("**/*",d,t,r,!1)}}}if(n){let c=u.join(i,"web");if(await h.pathExists(c)){let l=r.frontend.filter(d=>["next","react-router","tanstack-router","tanstack-start"].includes(d));for(let d of l){let y=u.join(c,d);await h.pathExists(y)&&await x("**/*",y,a,r,!1)}}}}}async function Tt(e,r){let t=await kt(["**/.gitignore.hbs","**/_gitignore"],{cwd:e,dot:!0,onlyFiles:!0,absolute:!0,ignore:["**/node_modules/**","**/.git/**"]});for(let a of t){let o=u.dirname(a),n=u.basename(a),s=u.join(o,".gitignore");try{n===".gitignore.hbs"?(await Re(a,s,r),await h.remove(a)):n==="_gitignore"&&await h.move(a,s,{overwrite:!0})}catch(i){D.error(`Error processing gitignore file ${a}:`,i)}}}async function Ft(e,r){if(r.packageManager==="pnpm"){let t=u.join(v,"templates/extras/pnpm-workspace.yaml"),a=u.join(e,"pnpm-workspace.yaml");await h.pathExists(t)?await h.copy(t,a):D.warn(C.yellow("Warning: pnpm-workspace.yaml template not found."))}}async function Bt(e){let r=Ua(),t=La.resolve(process.cwd(),e.projectName);try{return await _a.ensureDir(t),await xt(t,e),await At(t,e),await St(t,e),await Xe(e),await Dt(t,e),await dt(e),await $t(t,e),await He(e),await Et(t,e),e.addons.length>0&&e.addons[0]!=="none"&&await qe(e),await Ct(t,e),await Ft(t,e),e.examples.length>0&&e.examples[0]!=="none"&&await mt(e),await Ve(e),await jt(e),await ut(e),await Pt(t,e),await Ze(t,e),await vt(t,e.git),await Tt(t,e),Oa.success("Project template successfully scaffolded!"),e.install&&await gt({projectDir:t,packageManager:e.packageManager,addons:e.addons}),bt({...e,depsInstalled:e.install}),t}catch(a){throw r.stop(Rt.red("Failed")),a instanceof Error&&(Ma(Rt.red(`Error during project creation: ${a.message}`)),console.error(a.stack),process.exit(1)),a}}import{cancel as qo,group as Vo}from"@clack/prompts";import Go from"picocolors";import{cancel as za,isCancel as Wa,multiselect as qa}from"@clack/prompts";import Va from"picocolors";async function Nt(e,r){if(e!==void 0)return e;let t=r?.includes("react-router")||r?.includes("tanstack-router"),a=[{value:"turborepo",label:"Turborepo (Recommended)",hint:"Optimize builds for monorepos"},{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)"}],o=[{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"}],n=t?[...a,...o]:a,s=g.addons.filter(c=>t||c!=="pwa"&&c!=="tauri"),i=await qa({message:"Select addons",options:n,initialValues:s,required:!1});return Wa(i)&&(za(Va.red("Operation cancelled")),process.exit(0)),i.includes("husky")&&!i.includes("biome")&&i.push("biome"),i}import{cancel as Ga,isCancel as Ja,select as Qa}from"@clack/prompts";import Ha from"picocolors";async function It(e,r){if(e)return e;let t=r?.includes("native"),a=[{value:"trpc",label:"tRPC",hint:"End-to-end typesafe APIs made easy"},{value:"orpc",label:"oRPC",hint:"End-to-end type-safe APIs that adhere to OpenAPI standards"}];t&&(a=[{value:"trpc",label:"tRPC",hint:"End-to-end typesafe APIs made easy (Required for Native frontend)"}]);let o=await Qa({message:"Select API type",options:a,initialValue:t?"trpc":g.api});return Ja(o)&&(Ga(Ha.red("Operation cancelled")),process.exit(0)),t&&o!=="trpc"?"trpc":o}import{cancel as Ya,confirm as Ka,isCancel as Xa}from"@clack/prompts";import Za from"picocolors";async function Lt(e,r){if(!r)return!1;if(e!==void 0)return e;let t=await Ka({message:"Add authentication with Better-Auth?",initialValue:g.auth});return Xa(t)&&(Ya(Za.red("Operation cancelled")),process.exit(0)),t}import{cancel as eo,isCancel as to,select as ro}from"@clack/prompts";import ao from"picocolors";async function Mt(e){if(e!==void 0)return e;let r=await ro({message:"Select backend framework",options:[{value:"hono",label:"Hono",hint:"Lightweight, ultrafast web framework"},{value:"next",label:"Next.js",hint:"Full-stack framework with API routes"},{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:g.backend});return to(r)&&(eo(ao.red("Operation cancelled")),process.exit(0)),r}import{cancel as oo,isCancel as no,select as so}from"@clack/prompts";import io from"picocolors";async function Ot(e){if(e!==void 0)return e;let r=await so({message:"Select database",options:[{value:"none",label:"None",hint:"No database setup"},{value:"sqlite",label:"SQLite",hint:"lightweight, server-less, embedded relational database"},{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:g.database});return no(r)&&(oo(io.red("Operation cancelled")),process.exit(0)),r}import{cancel as co,isCancel as po,select as lo}from"@clack/prompts";import uo from"picocolors";async function Ut(e,r,t){if(r!==void 0)return r;if(e==="sqlite"&&t==="prisma")return"none";let a=[];if(e==="sqlite")a=[{value:"turso",label:"Turso",hint:"SQLite for Production. Powered by libSQL"},{value:"none",label:"None",hint:"Manual setup"}];else if(e==="postgres")a=[{value:"neon",label:"Neon Postgres",hint:"Serverless Postgres with branching capability"},...t==="prisma"?[{value:"prisma-postgres",label:"Prisma Postgres",hint:"Instant Postgres for Global Applications"}]:[],{value:"none",label:"None",hint:"Manual setup"}];else if(e==="mongodb")a=[{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 o=await lo({message:`Select ${e} setup option`,options:a,initialValue:"none"});return po(o)&&(co(uo.red("Operation cancelled")),process.exit(0)),o}import{cancel as mo,isCancel as fo,multiselect as _t}from"@clack/prompts";import ho from"picocolors";async function zt(e,r,t,a){if(e!==void 0)return e;if(r==="none")return[];if(!(t?.includes("react-router")||t?.includes("tanstack-router")||t?.includes("tanstack-start")))return[];let n=[];return a==="elysia"&&(n=await _t({message:"Include examples",options:[{value:"todo",label:"Todo App",hint:"A simple CRUD example app"}],required:!1,initialValues:g.examples})),(a==="hono"||a==="express")&&(n=await _t({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:g.examples})),fo(n)&&(mo(ho.red("Operation cancelled")),process.exit(0)),n}import{cancel as Wt,isCancel as qt,multiselect as go,select as bo}from"@clack/prompts";import Vt from"picocolors";async function Gt(e){if(e!==void 0)return e;let r=await go({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:g.frontend.some(a=>a==="tanstack-router"||a==="react-router"||a==="tanstack-start"||a==="next")?["web"]:[]});qt(r)&&(Wt(Vt.red("Operation cancelled")),process.exit(0));let t=[];if(r.includes("web")){let a=await bo({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"},{value:"next",label:"Next.js",hint:"The React Framework for the Web"},{value:"tanstack-start",label:"TanStack Start (beta)",hint:"SSR, Server Functions, API Routes and more with TanStack Router"}],initialValue:g.frontend.find(o=>o==="tanstack-router"||o==="react-router"||o==="tanstack-start"||o==="next")||"tanstack-router"});qt(a)&&(Wt(Vt.red("Operation cancelled")),process.exit(0)),t.push(a)}return r.includes("native")&&t.push("native"),t}import{cancel as wo,confirm as yo,isCancel as Po}from"@clack/prompts";import vo from"picocolors";async function Jt(e){if(e!==void 0)return e;let r=await yo({message:"Initialize git repository?",initialValue:g.git});return Po(r)&&(wo(vo.red("Operation cancelled")),process.exit(0)),r}import{cancel as jo,confirm as ko,isCancel as xo}from"@clack/prompts";import Ao from"picocolors";async function Qt(e){if(e!==void 0)return e;let r=await ko({message:"Install dependencies?",initialValue:g.install});return xo(r)&&(jo(Ao.red("Operation cancelled")),process.exit(0)),r}import{cancel as So,isCancel as Do,log as $o,select as Eo}from"@clack/prompts";import Co from"picocolors";async function Ht(e,r,t){if(!r)return"none";if(e!==void 0)return e;if(t==="mongodb")return $o.info("Only Prisma is supported with MongoDB."),"prisma";let a=await Eo({message:"Select ORM",options:[{value:"drizzle",label:"Drizzle",hint:"lightweight and performant TypeScript ORM"},{value:"prisma",label:"Prisma",hint:"Powerful, feature-rich ORM"}],initialValue:g.orm});return Do(a)&&(So(Co.red("Operation cancelled")),process.exit(0)),a}import{cancel as To,isCancel as Fo,select as Ro}from"@clack/prompts";import Bo from"picocolors";async function Yt(e){if(e!==void 0)return e;let r=re(),t=await Ro({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:r});return Fo(t)&&(To(Bo.red("Operation cancelled")),process.exit(0)),t}import ee from"node:path";import{cancel as No,isCancel as Io,text as Lo}from"@clack/prompts";import U from"fs-extra";import Mo from"picocolors";var Oo=["<",">",":",'"',"|","?","*"],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(Oo.some(r=>e.includes(r)))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 ge(e){if(e)if(e==="."){let n=process.cwd();if(U.readdirSync(n).length===0)return e}else{let n=ee.basename(e);if(!Xt(n)){let i=ee.resolve(process.cwd(),e);if(!U.pathExistsSync(i)||U.readdirSync(i).length===0)return e}}let r=!1,t="",a=g.projectName,o=1;for(;U.pathExistsSync(ee.resolve(process.cwd(),a));)a=`${g.projectName}-${o}`,o++;for(;!r;){let n=await Lo({message:"Enter your project name or path (relative to current directory)",placeholder:a,initialValue:e,defaultValue:a,validate:s=>{let i=s.trim()||a;if(i==="."){if(U.readdirSync(process.cwd()).length>0)return"Current directory is not empty. Please choose a different directory.";r=!0;return}let c=ee.resolve(process.cwd(),i),l=ee.basename(c),d=Xt(l);if(d)return d;if(!c.startsWith(process.cwd()))return"Project path must be within current directory";if(U.pathExistsSync(c)&&U.readdirSync(c).length>0)return`Directory "${i}" already exists and is not empty. Please choose a different name or path.`;r=!0}});Io(n)&&(No(Mo.red("Operation cancelled.")),process.exit(0)),t=n||a}return t}import{cancel as Uo,isCancel as _o,select as zo}from"@clack/prompts";import Wo from"picocolors";async function Zt(e,r){if(e!==void 0)return e;if(r==="next")return"node";let t=await zo({message:"Select runtime",options:[{value:"bun",label:"Bun",hint:"Fast all-in-one JavaScript runtime"},{value:"node",label:"Node.js",hint:"Traditional Node.js runtime"}],initialValue:g.runtime});return _o(t)&&(Uo(Wo.red("Operation cancelled")),process.exit(0)),t}async function er(e){let r=await Vo({projectName:async()=>ge(e.projectName),frontend:()=>Gt(e.frontend),backend:()=>Mt(e.backend),runtime:({results:t})=>Zt(e.runtime,t.backend),database:()=>Ot(e.database),orm:({results:t})=>Ht(e.orm,t.database!=="none",t.database),api:({results:t})=>It(e.api,t.frontend),auth:({results:t})=>Lt(e.auth,t.database!=="none"),addons:({results:t})=>Nt(e.addons,t.frontend),examples:({results:t})=>zt(e.examples,t.database,t.frontend,t.backend),dbSetup:({results:t})=>Ut(t.database??"none",e.dbSetup,t.orm),git:()=>Jt(e.git),packageManager:()=>Yt(e.packageManager),install:()=>Qt(e.install)},{onCancel:()=>{qo(Go.red("Operation cancelled")),process.exit(0)}});return{projectName:r.projectName,frontend:r.frontend,database:r.database,orm:r.orm,auth:r.auth,addons:r.addons,examples:r.examples,git:r.git,packageManager:r.packageManager,install:r.install,dbSetup:r.dbSetup,backend:r.backend,runtime:r.runtime,api:r.api}}import T from"picocolors";function Be(e){let r=[];if(e.projectName&&r.push(`${T.blue("Project Name:")} ${e.projectName}`),e.frontend!==void 0){let t=Array.isArray(e.frontend)?e.frontend:[e.frontend],a=t.length>0&&t[0]!==void 0&&t[0]!==""?t.join(", "):"none";r.push(`${T.blue("Frontend:")} ${a}`)}if(e.backend!==void 0&&r.push(`${T.blue("Backend Framework:")} ${String(e.backend)}`),e.runtime!==void 0&&r.push(`${T.blue("Runtime:")} ${String(e.runtime)}`),e.api!==void 0&&r.push(`${T.blue("API:")} ${String(e.api)}`),e.database!==void 0&&r.push(`${T.blue("Database:")} ${String(e.database)}`),e.orm!==void 0&&r.push(`${T.blue("ORM:")} ${String(e.orm)}`),e.auth!==void 0){let t=typeof e.auth=="boolean"?e.auth?"Yes":"No":String(e.auth);r.push(`${T.blue("Authentication:")} ${t}`)}if(e.addons!==void 0){let t=Array.isArray(e.addons)?e.addons:[e.addons],a=t.length>0&&t[0]!==void 0?t.join(", "):"none";r.push(`${T.blue("Addons:")} ${a}`)}if(e.examples!==void 0){let t=Array.isArray(e.examples)?e.examples:[e.examples],a=t.length>0&&t[0]!==void 0?t.join(", "):"none";r.push(`${T.blue("Examples:")} ${a}`)}if(e.git!==void 0){let t=typeof e.git=="boolean"?e.git?"Yes":"No":String(e.git);r.push(`${T.blue("Git Init:")} ${t}`)}if(e.packageManager!==void 0&&r.push(`${T.blue("Package Manager:")} ${String(e.packageManager)}`),e.install!==void 0){let t=typeof e.install=="boolean"?e.install?"Yes":"No":String(e.install);r.push(`${T.blue("Install Dependencies:")} ${t}`)}return e.dbSetup!==void 0&&r.push(`${T.blue("Database Setup:")} ${String(e.dbSetup)}`),r.length===0?T.yellow("No configuration selected."):r.join(`
195
+ `)}function tr(e){let r=[];e.database==="none"?r.push("--database none"):(r.push(`--database ${e.database}`),e.orm&&r.push(`--orm ${e.orm}`),e.dbSetup&&r.push(`--db-setup ${e.dbSetup}`)),e.api&&r.push(`--api ${e.api}`),r.push(e.auth?"--auth":"--no-auth"),r.push(e.git?"--git":"--no-git"),r.push(e.install?"--install":"--no-install"),e.runtime&&r.push(`--runtime ${e.runtime}`),e.backend&&r.push(`--backend ${e.backend}`),e.frontend&&e.frontend.length>0&&r.push(`--frontend ${e.frontend.join(" ")}`),e.addons&&e.addons.length>0?r.push(`--addons ${e.addons.join(" ")}`):r.push("--addons none"),e.examples&&e.examples.length>0?r.push(`--examples ${e.examples.join(" ")}`):r.push("--examples none"),e.packageManager&&r.push(`--package-manager ${e.packageManager}`);let t="",a=e.packageManager;a==="npm"?t="npx create-better-t-stack@latest":a==="pnpm"?t="pnpm create better-t-stack@latest":a==="bun"&&(t="bun create better-t-stack@latest");let o=e.projectName?` ${e.projectName}`:"";return`${t}${o} ${r.join(" ")}`}import Jo from"node:path";import Qo from"fs-extra";var rr=()=>{let e=Jo.join(v,"package.json");return Qo.readJSONSync(e).version??"1.0.0"};import ar from"gradient-string";var or=`
196
196
  \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
197
197
  \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
198
198
  \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
@@ -206,10 +206,10 @@ ${b.cyan("\u2022")} Build docs site: ${`cd apps/docs && ${e} build`}
206
206
  \u2588\u2588\u2551 \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2588\u2588\u2557
207
207
  \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2557
208
208
  \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
209
- `,or={pink:"#F5C2E7",mauve:"#CBA6F7",red:"#F38BA8",maroon:"#E78284",peach:"#FAB387",yellow:"#F9E2AF",green:"#A6E3A1",teal:"#94E2D5",sky:"#89DCEB",sapphire:"#74C7EC",lavender:"#B4BEFE"},nr=()=>{let e=process.stdout.columns||80,r=ar.split(`
210
- `),t=Math.max(...r.map(a=>a.length));e<t?console.log(rr(Object.values(or)).multiline(`
209
+ `,nr={pink:"#F5C2E7",mauve:"#CBA6F7",red:"#F38BA8",maroon:"#E78284",peach:"#FAB387",yellow:"#F9E2AF",green:"#A6E3A1",teal:"#94E2D5",sky:"#89DCEB",sapphire:"#74C7EC",lavender:"#B4BEFE"},sr=()=>{let e=process.stdout.columns||80,r=or.split(`
210
+ `),t=Math.max(...r.map(a=>a.length));e<t?console.log(ar(Object.values(nr)).multiline(`
211
211
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
212
212
  \u2551 Better T-Stack \u2551
213
213
  \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
214
- `)):console.log(rr(Object.values(or)).multiline(ar))};var ir=()=>process.exit(0);process.on("SIGINT",ir);process.on("SIGTERM",ir);async function Xo(){let e=Date.now();try{let t=await Yo(Ko(process.argv)).scriptName("create-better-t-stack").usage("$0 [project-directory] [options]","Create a new Better-T Stack project").positional("project-directory",{describe:"Project name/directory",type:"string"}).option("yes",{alias:"y",type:"boolean",describe:"Use default configuration and skip prompts",default:!1}).option("database",{type:"string",describe:"Database type",choices:["none","sqlite","postgres","mysql","mongodb"]}).option("orm",{type:"string",describe:"ORM type",choices:["drizzle","prisma","none"]}).option("auth",{type:"boolean",describe:"Include authentication"}).option("frontend",{type:"array",string:!0,describe:"Frontend types",choices:["tanstack-router","react-router","tanstack-start","next","native","none"]}).option("addons",{type:"array",string:!0,describe:"Additional addons",choices:["pwa","tauri","starlight","biome","husky","turborepo","none"]}).option("examples",{type:"array",string:!0,describe:"Examples to include",choices:["todo","ai","none"]}).option("git",{type:"boolean",describe:"Initialize git repository"}).option("package-manager",{alias:"pm",type:"string",describe:"Package manager",choices:["npm","pnpm","bun"]}).option("install",{type:"boolean",describe:"Install dependencies"}).option("db-setup",{type:"string",describe:"Database setup",choices:["turso","neon","prisma-postgres","mongodb-atlas","none"]}).option("backend",{type:"string",describe:"Backend framework",choices:["hono","express","next","elysia"]}).option("runtime",{type:"string",describe:"Runtime",choices:["bun","node"]}).option("api",{type:"string",describe:"API type",choices:["trpc","orpc"]}).completion().recommendCommands().version(tr()).alias("version","v").help().alias("help","h").strict().wrap(null).parse(),a=t.projectDirectory;nr();let o=Zo(t,a);Qo(z.magenta("Creating a new Better-T-Stack project")),!t.yes&&Object.keys(o).length>0&&(_.info(z.yellow("Using these pre-selected options:")),_.message(Be(o)),_.message(""));let n;t.yes?(n={...g,projectName:a??g.projectName,...o},_.info(z.yellow("Using these default/flag options:")),_.message(Be(n)),_.message("")):n=await Zt(o);let i=Go.resolve(process.cwd(),n.projectName);if(sr.pathExistsSync(i)&&sr.readdirSync(i).length>0){let c=await ge();n.projectName=c}await Rt(n),_.success(z.blue(`You can reproduce this setup with the following command:
215
- ${er(n)}`));let s=((Date.now()-e)/1e3).toFixed(2);Ho(z.magenta(`Project created successfully in ${z.bold(s)} seconds!`))}catch(r){r instanceof Error?(r.name==="YError"?Jo(z.red(`Invalid arguments: ${r.message}`)):(v.error(`An unexpected error occurred: ${r.message}`),v.error(r.stack)),process.exit(1)):(v.error("An unexpected error occurred."),console.error(r),process.exit(1))}}function Zo(e,r){let t={};if(e.database&&(t.database=e.database),e.orm&&(e.orm==="none"?t.orm="none":t.orm=e.orm),(t.database??e.database)==="mongodb"&&(t.orm??e.orm)==="drizzle"&&(v.fatal("MongoDB is only available with Prisma. Cannot use --database mongodb with --orm drizzle"),process.exit(1)),e.dbSetup){let s=e.dbSetup;s!=="none"?(t.dbSetup=s,s==="turso"?(e.database&&e.database!=="sqlite"&&(v.fatal(`Turso setup requires a SQLite database. Cannot use --db-setup turso with --database ${e.database}`),process.exit(1)),t.database="sqlite",e.orm==="prisma"&&(v.fatal("Turso setup is not compatible with Prisma. Cannot use --db-setup turso with --orm prisma"),process.exit(1)),t.orm="drizzle"):s==="prisma-postgres"?(e.database&&e.database!=="postgres"&&(v.fatal("Prisma PostgreSQL setup requires PostgreSQL database. Cannot use --db-setup prisma-postgres with a different database type."),process.exit(1)),t.database="postgres",e.orm&&e.orm!=="prisma"&&e.orm!=="none"&&(v.fatal("Prisma PostgreSQL setup requires Prisma ORM. Cannot use --db-setup prisma-postgres with a different ORM."),process.exit(1)),t.orm="prisma"):s==="mongodb-atlas"?(e.database&&e.database!=="mongodb"&&(v.fatal("MongoDB Atlas setup requires MongoDB database. Cannot use --db-setup mongodb-atlas with a different database type."),process.exit(1)),t.database="mongodb",t.orm="prisma"):s==="neon"&&(e.database&&e.database!=="postgres"&&(v.fatal("Neon PostgreSQL setup requires PostgreSQL database. Cannot use --db-setup neon with a different database type."),process.exit(1)),t.database="postgres")):t.dbSetup="none"}if((t.database??e.database)==="none"){e.auth===!0&&(v.fatal("Authentication requires a database. Cannot use --auth with --database none."),process.exit(1));let s=t.orm??e.orm;s&&s!=="none"&&(v.fatal(`Cannot use ORM with no database. Cannot use --orm ${s} with --database none.`),process.exit(1)),t.orm="none";let c=t.dbSetup??e.dbSetup;c&&c!=="none"&&(v.fatal(`Database setup requires a database. Cannot use --db-setup ${c} with --database none.`),process.exit(1)),t.dbSetup="none"}if(e.auth!==void 0&&(t.auth=e.auth),e.backend&&(t.backend=e.backend),e.runtime&&(t.runtime=e.runtime),e.frontend&&e.frontend.length>0)if(e.frontend.includes("none"))e.frontend.length>1&&(v.fatal("Cannot combine 'none' with other frontend options."),process.exit(1)),t.frontend=[];else{let s=e.frontend.filter(p=>p!=="none");s.filter(p=>p==="tanstack-router"||p==="react-router"||p==="tanstack-start"||p==="next").length>1&&(v.fatal("Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next"),process.exit(1)),t.frontend=s}e.api&&(t.api=e.api);let o=t.frontend??e.frontend?.filter(s=>s!=="none")??(e.yes?g.frontend:void 0),n=o?.includes("native"),i=t.api??(e.yes?g.api:void 0);if(n&&i==="orpc"&&(v.fatal("oRPC API is not supported when using the 'native' frontend. Please use --api trpc or remove 'native' from --frontend."),process.exit(1)),n&&i!=="trpc"&&(!e.api||e.yes&&e.api!=="orpc")&&(t.api="trpc"),e.addons&&e.addons.length>0)if(e.addons.includes("none"))e.addons.length>1&&(v.fatal("Cannot combine 'none' with other addons."),process.exit(1)),t.addons=[];else{let s=e.addons.filter(y=>y!=="none"),c=["pwa","tauri"],p=s.some(y=>c.includes(y)),w=o?.some(y=>y==="tanstack-router"||y==="react-router");p&&!w&&(e.frontend?(v.fatal("PWA and Tauri addons require tanstack-router or react-router. Cannot use these addons with your frontend selection."),process.exit(1)):e.yes&&(v.fatal("PWA and Tauri addons require tanstack-router or react-router (default frontend incompatible)."),process.exit(1))),s.includes("husky")&&!s.includes("biome")&&s.push("biome"),t.addons=[...new Set(s)]}if(e.examples&&e.examples.length>0)if(e.examples.includes("none"))e.examples.length>1&&(v.fatal("Cannot combine 'none' with other examples."),process.exit(1)),t.examples=[];else{let s=e.examples.filter(w=>w!=="none"),c=t.backend??e.backend;s.includes("ai")&&c==="elysia"&&!(e.yes&&g.backend!=="elysia")&&(v.fatal("AI example is only compatible with Hono backend. Cannot use --examples ai with --backend elysia"),process.exit(1)),o?.some(w=>["tanstack-router","react-router","tanstack-start","next"].includes(w))||(e.frontend?(v.fatal("Examples require a web frontend (tanstack-router, react-router, tanstack-start, or next). Cannot use --examples with your frontend selection."),process.exit(1)):e.yes&&(v.fatal("Examples require a web frontend (tanstack-router, react-router, tanstack-start, or next) (default frontend incompatible)."),process.exit(1))),t.examples=s}return e.packageManager&&(t.packageManager=e.packageManager),e.git!==void 0&&(t.git=e.git),e.install!==void 0&&(t.install=e.install),r&&(t.projectName=r),t}Xo().catch(e=>{v.error("Aborting installation due to unexpected error..."),e instanceof Error?(v.error(e.message),console.error(e.stack)):(v.error("An unknown error has occurred. Please open an issue on GitHub with the below:"),console.error(e)),process.exit(1)});
214
+ `)):console.log(ar(Object.values(nr)).multiline(or))};var cr=()=>process.exit(0);process.on("SIGINT",cr);process.on("SIGTERM",cr);async function tn(){let e=Date.now();try{let t=await Zo(en(process.argv)).scriptName("create-better-t-stack").usage("$0 [project-directory] [options]","Create a new Better-T Stack project").positional("project-directory",{describe:"Project name/directory",type:"string"}).option("yes",{alias:"y",type:"boolean",describe:"Use default configuration and skip prompts",default:!1}).option("database",{type:"string",describe:"Database type",choices:["none","sqlite","postgres","mysql","mongodb"]}).option("orm",{type:"string",describe:"ORM type",choices:["drizzle","prisma","none"]}).option("auth",{type:"boolean",describe:"Include authentication (use --no-auth to exclude)"}).option("frontend",{type:"array",string:!0,describe:"Frontend types",choices:["tanstack-router","react-router","tanstack-start","next","native","none"]}).option("addons",{type:"array",string:!0,describe:"Additional addons",choices:["pwa","tauri","starlight","biome","husky","turborepo","none"]}).option("examples",{type:"array",string:!0,describe:"Examples to include",choices:["todo","ai","none"]}).option("git",{type:"boolean",describe:"Initialize git repository (use --no-git to skip)"}).option("package-manager",{alias:"pm",type:"string",describe:"Package manager",choices:["npm","pnpm","bun"]}).option("install",{type:"boolean",describe:"Install dependencies (use --no-install to skip)"}).option("db-setup",{type:"string",describe:"Database setup",choices:["turso","neon","prisma-postgres","mongodb-atlas","none"]}).option("backend",{type:"string",describe:"Backend framework",choices:["hono","express","next","elysia"]}).option("runtime",{type:"string",describe:"Runtime",choices:["bun","node"]}).option("api",{type:"string",describe:"API type",choices:["trpc","orpc"]}).completion().recommendCommands().version(rr()).alias("version","v").help().alias("help","h").strict().wrap(null).parse(),a=t.projectDirectory;sr();let o=rn(t,a);Ko(z.magenta("Creating a new Better-T-Stack project")),!t.yes&&Object.keys(o).length>0&&(_.info(z.yellow("Using these pre-selected options:")),_.message(Be(o)),_.message(""));let n;t.yes?(n={...g,projectName:a??g.projectName,...o},n.database==="none"&&(n.orm="none",n.auth=!1,n.dbSetup="none",n.examples=n.examples.filter(c=>c!=="todo")),_.info(z.yellow("Using these default/flag options:")),_.message(Be(n)),_.message("")):n=await er(o);let s=Ho.resolve(process.cwd(),n.projectName);if(ir.pathExistsSync(s)&&ir.readdirSync(s).length>0){let c=await ge();n.projectName=c}await Bt(n),_.success(z.blue(`You can reproduce this setup with the following command:
215
+ ${tr(n)}`));let i=((Date.now()-e)/1e3).toFixed(2);Xo(z.magenta(`Project created successfully in ${z.bold(i)} seconds!`))}catch(r){r instanceof Error?(r.name==="YError"?Yo(z.red(`Invalid arguments: ${r.message}`)):(k.error(`An unexpected error occurred: ${r.message}`),k.error(r.stack)),process.exit(1)):(k.error("An unexpected error occurred."),console.error(r),process.exit(1))}}function rn(e,r){let t={};if(e.database&&(t.database=e.database),e.orm&&(t.orm=e.orm),e.auth!==void 0&&(t.auth=e.auth),e.git!==void 0&&(t.git=e.git),e.install!==void 0&&(t.install=e.install),e.backend&&(t.backend=e.backend),e.runtime&&(t.runtime=e.runtime),e.frontend&&e.frontend.length>0)if(e.frontend.includes("none"))e.frontend.length>1&&(k.fatal("Cannot combine 'none' with other frontend options."),process.exit(1)),t.frontend=[];else{let p=e.frontend.filter(j=>j!=="none");p.filter(j=>j==="tanstack-router"||j==="react-router"||j==="tanstack-start"||j==="next").length>1&&(k.fatal("Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next"),process.exit(1)),t.frontend=p}e.api&&(t.api=e.api),e.addons&&e.addons.length>0&&(e.addons.includes("none")?(e.addons.length>1&&(k.fatal("Cannot combine 'none' with other addons."),process.exit(1)),t.addons=[]):t.addons=e.addons.filter(p=>p!=="none")),e.examples&&e.examples.length>0&&(e.examples.includes("none")?(e.examples.length>1&&(k.fatal("Cannot combine 'none' with other examples."),process.exit(1)),t.examples=[]):t.examples=e.examples.filter(p=>p!=="none")),e.packageManager&&(t.packageManager=e.packageManager),r&&(t.projectName=r),e.dbSetup&&(t.dbSetup=e.dbSetup);let a=t.database??(e.yes?g.database:void 0),o=t.orm??(e.yes?g.orm:void 0),n=t.auth??(e.yes?g.auth:void 0),s=t.dbSetup??(e.yes?g.dbSetup:void 0),i=t.examples??(e.yes?g.examples:void 0),c=t.frontend??(e.yes?g.frontend:void 0),l=t.api??(e.yes?g.api:void 0),d=t.backend??(e.yes?g.backend:void 0);if(a==="none"&&(o&&o!=="none"&&(k.fatal(`Cannot use ORM '--orm ${o}' when database is 'none'.`),process.exit(1)),t.orm="none",n===!0&&(k.fatal("Authentication requires a database. Cannot use --auth when database is 'none'."),process.exit(1)),t.auth=!1,s&&s!=="none"&&(k.fatal(`Database setup '--db-setup ${s}' requires a database. Cannot use when database is 'none'.`),process.exit(1)),t.dbSetup="none",i?.includes("todo")&&(k.fatal("The 'todo' example requires a database. Cannot use --examples todo when database is 'none'."),process.exit(1)),t.examples&&(t.examples=t.examples.filter(p=>p!=="todo"))),a==="mongodb"&&o==="drizzle"&&(k.fatal("MongoDB is only available with Prisma. Cannot use --database mongodb with --orm drizzle"),process.exit(1)),t.dbSetup&&t.dbSetup!=="none"){let p=t.dbSetup;p==="turso"?(a&&a!=="sqlite"&&(k.fatal(`Turso setup requires SQLite. Cannot use --db-setup turso with --database ${a}`),process.exit(1)),o==="prisma"&&(k.fatal("Turso setup is not compatible with Prisma. Cannot use --db-setup turso with --orm prisma"),process.exit(1)),t.database="sqlite",t.orm="drizzle"):p==="prisma-postgres"?(a&&a!=="postgres"&&(k.fatal(`Prisma PostgreSQL setup requires PostgreSQL. Cannot use --db-setup prisma-postgres with --database ${a}.`),process.exit(1)),o&&o!=="prisma"&&o!=="none"&&(k.fatal(`Prisma PostgreSQL setup requires Prisma ORM. Cannot use --db-setup prisma-postgres with --orm ${o}.`),process.exit(1)),t.database="postgres",t.orm="prisma"):p==="mongodb-atlas"?(a&&a!=="mongodb"&&(k.fatal(`MongoDB Atlas setup requires MongoDB. Cannot use --db-setup mongodb-atlas with --database ${a}.`),process.exit(1)),o&&o!=="prisma"&&o!=="none"&&(k.fatal(`MongoDB Atlas setup requires Prisma ORM. Cannot use --db-setup mongodb-atlas with --orm ${o}.`),process.exit(1)),t.database="mongodb",t.orm="prisma"):p==="neon"&&(a&&a!=="postgres"&&(k.fatal(`Neon PostgreSQL setup requires PostgreSQL. Cannot use --db-setup neon with --database ${a}.`),process.exit(1)),t.database="postgres")}let y=c?.includes("native");if(y&&l==="orpc"&&(k.fatal("oRPC API is not supported with 'native' frontend. Please use --api trpc or remove 'native' from --frontend."),process.exit(1)),y&&l!=="trpc"&&(!e.api||e.yes&&e.api!=="orpc")&&(t.api="trpc"),t.addons&&t.addons.length>0){let p=["pwa","tauri"],b=t.addons.some(A=>p.includes(A)),j=c?.some(A=>A==="tanstack-router"||A==="react-router");b&&!j&&(k.fatal("PWA and Tauri addons require tanstack-router or react-router. Cannot use these addons with your frontend selection."),process.exit(1)),t.addons.includes("husky")&&!t.addons.includes("biome")&&t.addons.push("biome"),t.addons=[...new Set(t.addons)]}return t.examples&&t.examples.length>0&&(t.examples.includes("ai")&&d==="elysia"&&(k.fatal("AI example is not compatible with Elysia backend. Cannot use --examples ai with --backend elysia"),process.exit(1)),c?.some(b=>["tanstack-router","react-router","tanstack-start","next"].includes(b))||(k.fatal("Examples require a web frontend (tanstack-router, react-router, tanstack-start, or next). Cannot use --examples with your frontend selection."),process.exit(1))),t}tn().catch(e=>{k.error("Aborting installation due to unexpected error..."),e instanceof Error?(k.error(e.message),console.error(e.stack)):(k.error("An unknown error has occurred. Please open an issue on GitHub with the below:"),console.error(e)),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "2.0.6",
3
+ "version": "2.0.8",
4
4
  "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -96,8 +96,6 @@ export async function createContext(opts: CreateExpressContextOptions) {
96
96
  }
97
97
 
98
98
  {{else}}
99
- // Default or fallback context if backend is not recognized or none
100
- // This might need adjustment based on your default behavior
101
99
  export async function createContext() {
102
100
  return {
103
101
  session: null,
@@ -68,5 +68,5 @@ const app = new Elysia()
68
68
  {{/if}}
69
69
  .get("/", () => "OK")
70
70
  .listen(3000, () => {
71
- console.log(`Server is running on http://localhost:3000`);
71
+ console.log("Server is running on http://localhost:3000");
72
72
  });
@@ -70,7 +70,6 @@ app.use("/trpc/*", trpcServer({
70
70
  {{/if}}
71
71
 
72
72
  {{#if (includes examples "ai")}}
73
- // AI chat endpoint
74
73
  app.post("/ai", async (c) => {
75
74
  const body = await c.req.json();
76
75
  const messages = body.messages || [];
@@ -3,7 +3,7 @@ import { Stack } from "expo-router";
3
3
  import {
4
4
  DarkTheme,
5
5
  DefaultTheme,
6
- Theme,
6
+ type Theme,
7
7
  ThemeProvider,
8
8
  } from "@react-navigation/native";
9
9
  import { StatusBar } from "expo-status-bar";