create-better-t-stack 2.8.4 → 2.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -16
- package/dist/index.js +5 -5
- package/package.json +1 -1
- package/templates/api/orpc/server/base/src/lib/orpc.ts.hbs +6 -1
- package/templates/auth/native/unistyles/app/(drawer)/index.tsx.hbs +179 -0
- package/templates/auth/native/unistyles/components/sign-in.tsx.hbs +134 -0
- package/templates/auth/native/unistyles/components/sign-up.tsx.hbs +152 -0
- package/templates/frontend/native/unistyles/_gitignore +24 -0
- package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/_layout.tsx +34 -0
- package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/index.tsx +29 -0
- package/templates/frontend/native/unistyles/app/(drawer)/(tabs)/two.tsx +29 -0
- package/templates/frontend/native/unistyles/app/(drawer)/_layout.tsx +59 -0
- package/templates/frontend/native/unistyles/app/(drawer)/index.tsx.hbs +115 -0
- package/templates/frontend/native/unistyles/app/+html.tsx +48 -0
- package/templates/frontend/native/unistyles/app/+not-found.tsx +34 -0
- package/templates/frontend/native/unistyles/app/_layout.tsx.hbs +77 -0
- package/templates/frontend/native/unistyles/app/modal.tsx +29 -0
- package/templates/frontend/native/unistyles/app.json +44 -0
- package/templates/frontend/native/unistyles/babel.config.js +20 -0
- package/templates/frontend/native/unistyles/breakpoints.ts +9 -0
- package/templates/frontend/native/unistyles/components/container.tsx +20 -0
- package/templates/frontend/native/unistyles/components/header-button.tsx +31 -0
- package/templates/frontend/native/unistyles/components/tabbar-icon.tsx +15 -0
- package/templates/frontend/native/unistyles/expo-env.d.ts +3 -0
- package/templates/frontend/native/unistyles/index.js +2 -0
- package/templates/frontend/native/unistyles/metro.config.js +20 -0
- package/templates/frontend/native/unistyles/package.json +47 -0
- package/templates/frontend/native/unistyles/theme.ts +35 -0
- package/templates/frontend/native/unistyles/tsconfig.json +12 -0
- package/templates/frontend/native/unistyles/unistyles.ts +27 -0
- package/templates/frontend/react/web-base/src/components/header.tsx.hbs +5 -6
- /package/templates/auth/native/{lib/auth-client.ts → native-base/lib/auth-client.ts.hbs} +0 -0
- /package/templates/auth/native/{app → nativewind/app}/(drawer)/index.tsx.hbs +0 -0
- /package/templates/auth/native/{components → nativewind/components}/sign-in.tsx.hbs +0 -0
- /package/templates/auth/native/{components → nativewind/components}/sign-up.tsx.hbs +0 -0
- /package/templates/frontend/native/{assets → native-base/assets}/adaptive-icon.png +0 -0
- /package/templates/frontend/native/{assets → native-base/assets}/favicon.png +0 -0
- /package/templates/frontend/native/{assets → native-base/assets}/icon.png +0 -0
- /package/templates/frontend/native/{assets → native-base/assets}/splash.png +0 -0
- /package/templates/frontend/native/{_gitignore → nativewind/_gitignore} +0 -0
- /package/templates/frontend/native/{app → nativewind/app}/(drawer)/(tabs)/_layout.tsx +0 -0
- /package/templates/frontend/native/{app → nativewind/app}/(drawer)/(tabs)/index.tsx +0 -0
- /package/templates/frontend/native/{app → nativewind/app}/(drawer)/(tabs)/two.tsx +0 -0
- /package/templates/frontend/native/{app → nativewind/app}/(drawer)/_layout.tsx +0 -0
- /package/templates/frontend/native/{app → nativewind/app}/(drawer)/index.tsx.hbs +0 -0
- /package/templates/frontend/native/{app → nativewind/app}/+html.tsx +0 -0
- /package/templates/frontend/native/{app → nativewind/app}/+not-found.tsx +0 -0
- /package/templates/frontend/native/{app → nativewind/app}/_layout.tsx.hbs +0 -0
- /package/templates/frontend/native/{app → nativewind/app}/modal.tsx +0 -0
- /package/templates/frontend/native/{app-env.d.ts → nativewind/app-env.d.ts} +0 -0
- /package/templates/frontend/native/{app.json → nativewind/app.json} +0 -0
- /package/templates/frontend/native/{babel.config.js → nativewind/babel.config.js} +0 -0
- /package/templates/frontend/native/{components → nativewind/components}/container.tsx +0 -0
- /package/templates/frontend/native/{components → nativewind/components}/header-button.tsx +0 -0
- /package/templates/frontend/native/{components → nativewind/components}/tabbar-icon.tsx +0 -0
- /package/templates/frontend/native/{global.css → nativewind/global.css} +0 -0
- /package/templates/frontend/native/{lib → nativewind/lib}/android-navigation-bar.tsx +0 -0
- /package/templates/frontend/native/{lib → nativewind/lib}/constants.ts +0 -0
- /package/templates/frontend/native/{lib → nativewind/lib}/use-color-scheme.ts +0 -0
- /package/templates/frontend/native/{metro.config.js → nativewind/metro.config.js} +0 -0
- /package/templates/frontend/native/{package.json → nativewind/package.json} +0 -0
- /package/templates/frontend/native/{tailwind.config.js → nativewind/tailwind.config.js} +0 -0
- /package/templates/frontend/native/{tsconfig.json → nativewind/tsconfig.json} +0 -0
package/README.md
CHANGED
|
@@ -23,21 +23,21 @@ Follow the prompts to configure your project or use the `--yes` flag for default
|
|
|
23
23
|
|
|
24
24
|
## Features
|
|
25
25
|
|
|
26
|
-
| Category
|
|
27
|
-
|
|
28
|
-
| **TypeScript**
|
|
29
|
-
| **Frontend**
|
|
30
|
-
| **Backend**
|
|
31
|
-
| **API Layer**
|
|
32
|
-
| **Runtime**
|
|
33
|
-
| **Database**
|
|
34
|
-
| **ORM**
|
|
35
|
-
| **Database Setup**
|
|
36
|
-
| **Authentication**
|
|
37
|
-
| **Styling**
|
|
38
|
-
| **Addons**
|
|
39
|
-
| **Examples**
|
|
40
|
-
| **Developer Experience** | • Automatic Git initialization<br>• Package manager choice (npm, pnpm, bun)<br>• Automatic dependency installation
|
|
26
|
+
| Category | Options |
|
|
27
|
+
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
|
+
| **TypeScript** | End-to-end type safety across all parts of your application |
|
|
29
|
+
| **Frontend** | • React with TanStack Router<br>• React with React Router<br>• React with TanStack Start (SSR)<br>• Next.js<br>• SvelteKit<br>• Nuxt (Vue)<br>• SolidJS<br>• React Native with NativeWind (via Expo)<br>• React Native with Unistyles (via Expo)<br>• None |
|
|
30
|
+
| **Backend** | • Hono<br>• Express<br>• Elysia<br>• Next.js API routes<br>• Convex<br>• None |
|
|
31
|
+
| **API Layer** | • tRPC (type-safe APIs)<br>• oRPC (OpenAPI-compatible type-safe APIs)<br>• None |
|
|
32
|
+
| **Runtime** | • Bun<br>• Node.js |
|
|
33
|
+
| **Database** | • SQLite<br>• PostgreSQL<br>• MySQL<br>• MongoDB<br>• None |
|
|
34
|
+
| **ORM** | • Drizzle (TypeScript-first)<br>• Prisma (feature-rich)<br>• Mongoose (for MongoDB)<br>• None |
|
|
35
|
+
| **Database Setup** | • Turso (SQLite)<br>• Neon (PostgreSQL)<br>• Prisma Postgres (via Prisma Accelerate)<br>• MongoDB Atlas<br>• None (manual setup) |
|
|
36
|
+
| **Authentication** | Better-Auth (email/password, with more options coming soon) |
|
|
37
|
+
| **Styling** | Tailwind CSS with shadcn/ui components |
|
|
38
|
+
| **Addons** | • PWA support<br>• Tauri (desktop applications)<br>• Starlight (documentation site)<br>• Biome (linting and formatting)<br>• Husky (Git hooks)<br>• Turborepo (optimized builds) |
|
|
39
|
+
| **Examples** | • Todo app<br>• AI Chat interface (using Vercel AI SDK) |
|
|
40
|
+
| **Developer Experience** | • Automatic Git initialization<br>• Package manager choice (npm, pnpm, bun)<br>• Automatic dependency installation |
|
|
41
41
|
|
|
42
42
|
## Usage
|
|
43
43
|
|
|
@@ -51,7 +51,7 @@ Options:
|
|
|
51
51
|
--orm <type> ORM type (none, drizzle, prisma, mongoose)
|
|
52
52
|
--auth Include authentication
|
|
53
53
|
--no-auth Exclude authentication
|
|
54
|
-
--frontend <types...> Frontend types (tanstack-router, react-router, tanstack-start, next, nuxt, svelte, solid, native, none)
|
|
54
|
+
--frontend <types...> Frontend types (tanstack-router, react-router, tanstack-start, next, nuxt, svelte, solid, native-nativewind, native-unistyles, none)
|
|
55
55
|
--addons <types...> Additional addons (pwa, tauri, starlight, biome, husky, turborepo, none)
|
|
56
56
|
--examples <types...> Examples to include (todo, ai, none)
|
|
57
57
|
--git Initialize git repository
|
|
@@ -119,6 +119,7 @@ npx create-better-t-stack my-app --addons starlight
|
|
|
119
119
|
## Compatibility Notes
|
|
120
120
|
|
|
121
121
|
- **Convex backend**: Automatically disables authentication, database, ORM, and API options
|
|
122
|
+
- **Backend 'none'**: If selected, this option will force related options like API, ORM, database, authentication, and runtime to 'none'. Examples will also be disabled (set to none/empty).
|
|
122
123
|
- **SvelteKit, Nuxt, and SolidJS** frontends are only compatible with oRPC API layer
|
|
123
124
|
- **PWA support** requires React with TanStack Router, React Router, or SolidJS
|
|
124
125
|
- **Tauri desktop app** requires React (TanStack Router/React Router), Nuxt, SvelteKit, or SolidJS
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import e from"node:path";import{cancel as t,confirm as n,group as r,intro as i,isCancel as a,log as o,multiselect as s,outro as c,password as l,select as u,spinner as d,text as f}from"@clack/prompts";import p,{consola as m}from"consola";import h from"fs-extra";import g from"picocolors";import _ from"yargs";import{hideBin as v}from"yargs/helpers";import{fileURLToPath as y}from"node:url";import{$ as b,execa as x}from"execa";import S from"node:os";import{globby as C}from"globby";import w from"handlebars";import T from"gradient-string";const E=()=>{let e=process.env.npm_config_user_agent;return e?.startsWith(`pnpm`)?`pnpm`:e?.startsWith(`bun`)?`bun`:`npm`},D=y(import.meta.url),O=e.dirname(D),k=e.join(O,`../`),A={projectName:`my-better-t-app`,projectDir:e.resolve(process.cwd(),`my-better-t-app`),relativePath:`my-better-t-app`,frontend:[`tanstack-router`],database:`sqlite`,orm:`drizzle`,auth:!0,addons:[],examples:[],git:!0,packageManager:E(),install:!0,dbSetup:`none`,backend:`hono`,runtime:`bun`,api:`trpc`},j={"better-auth":`^1.2.7`,"@better-auth/expo":`^1.2.7`,"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.7.0`,prisma:`^6.7.0`,mongoose:`^8.14.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`,"@ai-sdk/vue":`^1.2.8`,"@ai-sdk/svelte":`^2.1.9`,"@prisma/extension-accelerate":`^1.3.0`,"@orpc/server":`^1.2.0`,"@orpc/client":`^1.2.0`,"@orpc/react-query":`^1.2.0`,"@orpc/solid-query":`^1.2.0`,"@orpc/vue-query":`^1.2.0`,"@orpc/svelte-query":`^1.2.0`,"@trpc/tanstack-react-query":`^11.0.0`,"@trpc/server":`^11.0.0`,"@trpc/client":`^11.0.0`,convex:`^1.23.0`,"@convex-dev/react-query":`^0.0.0-alpha.8`,"convex-svelte":`^0.0.11`,"@tanstack/svelte-query":`^5.74.4`,"@tanstack/react-query-devtools":`^5.69.0`,"@tanstack/react-query":`^5.69.0`,"@tanstack/solid-query":`^5.75.0`,"@tanstack/solid-query-devtools":`^5.75.0`},M=async t=>{let{dependencies:n=[],devDependencies:r=[],projectDir:i}=t,a=e.join(i,`package.json`),o=await h.readJson(a);o.dependencies||={},o.devDependencies||={};for(let e of n){let t=j[e];t?o.dependencies[e]=t:console.warn(`Warning: Dependency ${e} not found in version map.`)}for(let e of r){let t=j[e];t?o.devDependencies[e]=t:console.warn(`Warning: Dev dependency ${e} not found in version map.`)}await h.writeJson(a,o,{spaces:2})};function N(e,t){switch(e){case`pnpm`:return`pnpm dlx ${t}`;case`bun`:return`bunx ${t}`;default:return`npx ${t}`}}async function ee(t){let{projectName:n,packageManager:r,projectDir:i}=t,a=d();try{a.start(`Setting up Starlight docs...`);let t=[`docs`,`--template`,`starlight`,`--no-install`,`--add`,`tailwind`,`--no-git`,`--skip-houston`],n=t.join(` `),o=`create-astro@latest ${n}`,s=N(r,o);await x(s,{cwd:e.join(i,`apps`),env:{CI:`true`},shell:!0}),a.stop(`Starlight docs setup successfully!`)}catch(e){a.stop(g.red(`Failed to set up Starlight docs`)),e instanceof Error&&p.error(g.red(e.message))}}async function te(t){let{projectName:n,packageManager:r,frontend:i,projectDir:a}=t,o=d(),s=e.join(a,`apps/web`);if(await h.pathExists(s))try{o.start(`Setting up Tauri desktop app support...`),await M({devDependencies:[`@tauri-apps/cli`],projectDir:s});let t=e.join(s,`package.json`);if(await h.pathExists(t)){let e=await h.readJson(t);e.scripts={...e.scripts,tauri:`tauri`,"desktop:dev":`tauri dev`,"desktop:build":`tauri build`},await h.writeJson(t,e,{spaces:2})}let n=i.includes(`tanstack-router`),c=i.includes(`react-router`),l=i.includes(`nuxt`),u=i.includes(`svelte`),d=i.includes(`solid`),f=c||u?`http://localhost:5173`:`http://localhost:3001`,p=l?`../.output/public`:u?`../build`:`../dist`,m=[`init`,`--app-name=${e.basename(a)}`,`--window-title=${e.basename(a)}`,`--frontend-dist=${p}`,`--dev-url=${f}`,`--before-dev-command=\"${r} run dev\"`,`--before-build-command=\"${r} run build\"`],g=m.join(` `),_=`@tauri-apps/cli@latest ${g}`,v=N(r,_);await x(v,{cwd:s,env:{CI:`true`},shell:!0}),o.stop(`Tauri desktop app support configured successfully!`)}catch(e){o.stop(g.red(`Failed to set up Tauri`)),e instanceof Error&&m.error(g.red(e.message))}}async function ne(e){let{projectName:t,addons:n,frontend:r,projectDir:i}=e,a=r.includes(`react-router`)||r.includes(`tanstack-router`),o=r.includes(`nuxt`),s=r.includes(`svelte`),c=r.includes(`solid`);n.includes(`turborepo`)&&await M({devDependencies:[`turbo`],projectDir:i}),n.includes(`pwa`)&&(a||c)&&await oe(i,r),n.includes(`tauri`)&&(a||o||s||c)&&await te(e),n.includes(`biome`)&&await ie(i),n.includes(`husky`)&&await ae(i),n.includes(`starlight`)&&await ee(e)}function re(t,n){return n.some(e=>[`react-router`,`tanstack-router`,`nuxt`,`svelte`,`solid`].includes(e)),e.join(t,`apps/web`)}async function ie(t){await M({devDependencies:[`@biomejs/biome`],projectDir:t});let n=e.join(t,`package.json`);if(await h.pathExists(n)){let e=await h.readJson(n);e.scripts={...e.scripts,check:`biome check --write .`},await h.writeJson(n,e,{spaces:2})}}async function ae(t){await M({devDependencies:[`husky`,`lint-staged`],projectDir:t});let n=e.join(t,`package.json`);if(await h.pathExists(n)){let e=await h.readJson(n);e.scripts={...e.scripts,prepare:`husky`},e[`lint-staged`]={"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":[`biome check --write .`]},await h.writeJson(n,e,{spaces:2})}}async function oe(t,n){let r=n.some(e=>[`react-router`,`tanstack-router`,`solid`].includes(e));if(!r)return;let i=re(t,n);if(!await h.pathExists(i))return;await M({dependencies:[`vite-plugin-pwa`],devDependencies:[`@vite-pwa/assets-generator`],projectDir:i});let a=e.join(i,`package.json`);if(await h.pathExists(a)){let e=await h.readJson(a);e.scripts={...e.scripts,"generate-pwa-assets":`pwa-assets-generator`},await h.writeJson(a,e,{spaces:2})}}async function se(t){let{api:n,projectName:r,frontend:i,backend:a,packageManager:o,projectDir:s}=t,c=a===`convex`,l=e.join(s,`apps/web`),u=e.join(s,`apps/native`),d=await h.pathExists(l),f=await h.pathExists(u),p=i.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),m=i.includes(`nuxt`),g=i.includes(`svelte`),_=i.includes(`solid`);if(!c&&n!==`none`){let r=e.join(s,`apps/server`),i=await h.pathExists(r);i&&(n===`orpc`?await M({dependencies:[`@orpc/server`,`@orpc/client`],projectDir:r}):n===`trpc`&&(await M({dependencies:[`@trpc/server`,`@trpc/client`],projectDir:r}),t.backend===`hono`?await M({dependencies:[`@hono/trpc-server`],projectDir:r}):t.backend===`elysia`&&await M({dependencies:[`@elysiajs/trpc`],projectDir:r}))),d&&(p?n===`orpc`?await M({dependencies:[`@orpc/react-query`,`@orpc/client`,`@orpc/server`],projectDir:l}):n===`trpc`&&await M({dependencies:[`@trpc/tanstack-react-query`,`@trpc/client`,`@trpc/server`],projectDir:l}):m?n===`orpc`&&await M({dependencies:[`@orpc/vue-query`,`@orpc/client`,`@orpc/server`],projectDir:l}):g?n===`orpc`&&await M({dependencies:[`@orpc/svelte-query`,`@orpc/client`,`@orpc/server`,`@tanstack/svelte-query`],projectDir:l}):_&&n===`orpc`&&await M({dependencies:[`@orpc/solid-query`,`@orpc/client`,`@orpc/server`,`@tanstack/solid-query`],projectDir:l})),f&&(n===`trpc`?await M({dependencies:[`@trpc/tanstack-react-query`,`@trpc/client`,`@trpc/server`],projectDir:u}):n===`orpc`&&await M({dependencies:[`@orpc/react-query`,`@orpc/client`,`@orpc/server`],projectDir:u}))}let v=[`react-router`,`tanstack-router`,`tanstack-start`,`next`,`native`],y=i.includes(`solid`),b=i.some(e=>v.includes(e));if(b&&!c){let t=[`@tanstack/react-query`],n=[`@tanstack/react-query-devtools`],r=i.some(e=>e!==`native`&&v.includes(e)),a=i.includes(`native`);if(r&&d){let r=e.join(l,`package.json`);if(await h.pathExists(r))try{await M({dependencies:t,devDependencies:n,projectDir:l})}catch{}}if(a&&f){let n=e.join(u,`package.json`);if(await h.pathExists(n))try{await M({dependencies:t,projectDir:u})}catch{}}}if(y&&!c){let t=[`@tanstack/solid-query`],n=[`@tanstack/solid-query-devtools`];if(d){let r=e.join(l,`package.json`);if(await h.pathExists(r))try{await M({dependencies:t,devDependencies:n,projectDir:l})}catch{}}}if(c){if(d){let t=e.join(l,`package.json`);if(await h.pathExists(t))try{let e=[`convex`];i.includes(`tanstack-start`)&&e.push(`@convex-dev/react-query`),g&&e.push(`convex-svelte`),await M({dependencies:e,projectDir:l})}catch{}}if(f){let t=e.join(u,`package.json`);if(await h.pathExists(t))try{await M({dependencies:[`convex`],projectDir:u})}catch{}}let t=`@${r}/backend`,n=o===`npm`?`*`:`workspace:*`,a=async(e,t,n)=>{try{let r=await h.readJson(e);r.dependencies||={},r.dependencies[t]!==n&&(r.dependencies[t]=n,await h.writeJson(e,r,{spaces:2}))}catch{}};if(d){let r=e.join(l,`package.json`);await h.pathExists(r)&&await a(r,t,n)}if(f){let r=e.join(u,`package.json`);await h.pathExists(r)&&await a(r,t,n)}}}async function ce(t){let{projectName:n,auth:r,frontend:i,backend:a,projectDir:o}=t;if(a===`convex`||!r)return;let s=e.join(o,`apps/server`),c=e.join(o,`apps/web`),l=e.join(o,`apps/native`),u=await h.pathExists(c),d=await h.pathExists(l),f=await h.pathExists(s);try{f&&await M({dependencies:[`better-auth`],projectDir:s});let e=i.some(e=>[`react-router`,`tanstack-router`,`tanstack-start`,`next`,`nuxt`,`svelte`].includes(e));e&&u&&await M({dependencies:[`better-auth`],projectDir:c}),i.includes(`native`)&&d&&(await M({dependencies:[`better-auth`,`@better-auth/expo`],projectDir:l}),f&&await M({dependencies:[`@better-auth/expo`],projectDir:s}))}catch(e){p.error(g.red(`Failed to configure authentication dependencies`)),e instanceof Error&&p.error(g.red(e.message))}}function le(e=32){let t=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`,n=``,r=t.length;for(let i=0;i<e;i++)n+=t.charAt(Math.floor(Math.random()*r));return n}async function ue(t){let{projectName:n,backend:r,runtime:i,api:a,projectDir:o}=t;if(r===`convex`)return;let s=r,c=e.join(o,`apps/server`),l=[],u=[];s===`hono`?(l.push(`hono`),a===`trpc`&&l.push(`@hono/trpc-server`),i===`node`&&(l.push(`@hono/node-server`),u.push(`tsx`,`@types/node`))):s===`elysia`?(l.push(`elysia`,`@elysiajs/cors`),a===`trpc`&&l.push(`@elysiajs/trpc`),i===`node`&&(l.push(`@elysiajs/node`),u.push(`tsx`,`@types/node`))):s===`express`&&(l.push(`express`,`cors`),u.push(`@types/express`,`@types/cors`),i===`node`&&u.push(`tsx`,`@types/node`)),i===`bun`&&u.push(`@types/bun`),(l.length>0||u.length>0)&&await M({dependencies:l,devDependencies:u,projectDir:c})}async function de(t,n){let r=e.join(t,`README.md`),i=fe(n);try{await h.writeFile(r,i)}catch(e){p.error(`Failed to create README.md file:`,e)}}function fe(e){let{projectName:t,packageManager:n,database:r,auth:i,addons:a=[],orm:o=`drizzle`,runtime:s=`bun`,frontend:c=[`tanstack-router`],backend:l=`hono`,api:u=`trpc`}=e,d=l===`convex`,f=c.includes(`react-router`),p=c.includes(`tanstack-router`),m=c.includes(`native`),h=c.includes(`next`),g=c.includes(`tanstack-start`),_=c.includes(`svelte`),v=c.includes(`solid`),y=c.includes(`nuxt`),b=n===`npm`?`npm run`:n,x=`3001`;return(f||_)&&(x=`5173`),`# ${t}
|
|
2
|
+
import e from"node:path";import{cancel as t,confirm as n,group as r,intro as i,isCancel as a,log as o,multiselect as s,outro as c,password as l,select as u,spinner as d,text as f}from"@clack/prompts";import p,{consola as m}from"consola";import h from"fs-extra";import g from"picocolors";import _ from"yargs";import{hideBin as v}from"yargs/helpers";import{fileURLToPath as y}from"node:url";import{$ as b,execa as x}from"execa";import S from"node:os";import{globby as C}from"globby";import w from"handlebars";import T from"gradient-string";const E=()=>{let e=process.env.npm_config_user_agent;return e?.startsWith(`pnpm`)?`pnpm`:e?.startsWith(`bun`)?`bun`:`npm`},D=y(import.meta.url),O=e.dirname(D),k=e.join(O,`../`),A={projectName:`my-better-t-app`,projectDir:e.resolve(process.cwd(),`my-better-t-app`),relativePath:`my-better-t-app`,frontend:[`tanstack-router`],database:`sqlite`,orm:`drizzle`,auth:!0,addons:[],examples:[],git:!0,packageManager:E(),install:!0,dbSetup:`none`,backend:`hono`,runtime:`bun`,api:`trpc`},j={"better-auth":`^1.2.7`,"@better-auth/expo":`^1.2.7`,"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.7.0`,prisma:`^6.7.0`,mongoose:`^8.14.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`,"@ai-sdk/vue":`^1.2.8`,"@ai-sdk/svelte":`^2.1.9`,"@prisma/extension-accelerate":`^1.3.0`,"@orpc/server":`^1.2.0`,"@orpc/client":`^1.2.0`,"@orpc/react-query":`^1.2.0`,"@orpc/solid-query":`^1.2.0`,"@orpc/vue-query":`^1.2.0`,"@orpc/svelte-query":`^1.2.0`,"@trpc/tanstack-react-query":`^11.0.0`,"@trpc/server":`^11.0.0`,"@trpc/client":`^11.0.0`,convex:`^1.23.0`,"@convex-dev/react-query":`^0.0.0-alpha.8`,"convex-svelte":`^0.0.11`,"@tanstack/svelte-query":`^5.74.4`,"@tanstack/react-query-devtools":`^5.69.0`,"@tanstack/react-query":`^5.69.0`,"@tanstack/solid-query":`^5.75.0`,"@tanstack/solid-query-devtools":`^5.75.0`},M=async t=>{let{dependencies:n=[],devDependencies:r=[],projectDir:i}=t,a=e.join(i,`package.json`),o=await h.readJson(a);o.dependencies||={},o.devDependencies||={};for(let e of n){let t=j[e];t?o.dependencies[e]=t:console.warn(`Warning: Dependency ${e} not found in version map.`)}for(let e of r){let t=j[e];t?o.devDependencies[e]=t:console.warn(`Warning: Dev dependency ${e} not found in version map.`)}await h.writeJson(a,o,{spaces:2})};function N(e,t){switch(e){case`pnpm`:return`pnpm dlx ${t}`;case`bun`:return`bunx ${t}`;default:return`npx ${t}`}}async function ee(t){let{projectName:n,packageManager:r,projectDir:i}=t,a=d();try{a.start(`Setting up Starlight docs...`);let t=[`docs`,`--template`,`starlight`,`--no-install`,`--add`,`tailwind`,`--no-git`,`--skip-houston`],n=t.join(` `),o=`create-astro@latest ${n}`,s=N(r,o);await x(s,{cwd:e.join(i,`apps`),env:{CI:`true`},shell:!0}),a.stop(`Starlight docs setup successfully!`)}catch(e){a.stop(g.red(`Failed to set up Starlight docs`)),e instanceof Error&&p.error(g.red(e.message))}}async function te(t){let{projectName:n,packageManager:r,frontend:i,projectDir:a}=t,o=d(),s=e.join(a,`apps/web`);if(await h.pathExists(s))try{o.start(`Setting up Tauri desktop app support...`),await M({devDependencies:[`@tauri-apps/cli`],projectDir:s});let t=e.join(s,`package.json`);if(await h.pathExists(t)){let e=await h.readJson(t);e.scripts={...e.scripts,tauri:`tauri`,"desktop:dev":`tauri dev`,"desktop:build":`tauri build`},await h.writeJson(t,e,{spaces:2})}let n=i.includes(`tanstack-router`),c=i.includes(`react-router`),l=i.includes(`nuxt`),u=i.includes(`svelte`),d=i.includes(`solid`),f=c||u?`http://localhost:5173`:`http://localhost:3001`,p=l?`../.output/public`:u?`../build`:`../dist`,m=[`init`,`--app-name=${e.basename(a)}`,`--window-title=${e.basename(a)}`,`--frontend-dist=${p}`,`--dev-url=${f}`,`--before-dev-command=\"${r} run dev\"`,`--before-build-command=\"${r} run build\"`],g=m.join(` `),_=`@tauri-apps/cli@latest ${g}`,v=N(r,_);await x(v,{cwd:s,env:{CI:`true`},shell:!0}),o.stop(`Tauri desktop app support configured successfully!`)}catch(e){o.stop(g.red(`Failed to set up Tauri`)),e instanceof Error&&m.error(g.red(e.message))}}async function ne(e){let{projectName:t,addons:n,frontend:r,projectDir:i}=e,a=r.includes(`react-router`)||r.includes(`tanstack-router`),o=r.includes(`nuxt`),s=r.includes(`svelte`),c=r.includes(`solid`);n.includes(`turborepo`)&&await M({devDependencies:[`turbo`],projectDir:i}),n.includes(`pwa`)&&(a||c)&&await oe(i,r),n.includes(`tauri`)&&(a||o||s||c)&&await te(e),n.includes(`biome`)&&await ie(i),n.includes(`husky`)&&await ae(i),n.includes(`starlight`)&&await ee(e)}function re(t,n){return n.some(e=>[`react-router`,`tanstack-router`,`nuxt`,`svelte`,`solid`].includes(e)),e.join(t,`apps/web`)}async function ie(t){await M({devDependencies:[`@biomejs/biome`],projectDir:t});let n=e.join(t,`package.json`);if(await h.pathExists(n)){let e=await h.readJson(n);e.scripts={...e.scripts,check:`biome check --write .`},await h.writeJson(n,e,{spaces:2})}}async function ae(t){await M({devDependencies:[`husky`,`lint-staged`],projectDir:t});let n=e.join(t,`package.json`);if(await h.pathExists(n)){let e=await h.readJson(n);e.scripts={...e.scripts,prepare:`husky`},e[`lint-staged`]={"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":[`biome check --write .`]},await h.writeJson(n,e,{spaces:2})}}async function oe(t,n){let r=n.some(e=>[`react-router`,`tanstack-router`,`solid`].includes(e));if(!r)return;let i=re(t,n);if(!await h.pathExists(i))return;await M({dependencies:[`vite-plugin-pwa`],devDependencies:[`@vite-pwa/assets-generator`],projectDir:i});let a=e.join(i,`package.json`);if(await h.pathExists(a)){let e=await h.readJson(a);e.scripts={...e.scripts,"generate-pwa-assets":`pwa-assets-generator`},await h.writeJson(a,e,{spaces:2})}}async function se(t){let{api:n,projectName:r,frontend:i,backend:a,packageManager:o,projectDir:s}=t,c=a===`convex`,l=e.join(s,`apps/web`),u=e.join(s,`apps/native`),d=await h.pathExists(l),f=await h.pathExists(u),p=i.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),m=i.includes(`nuxt`),g=i.includes(`svelte`),_=i.includes(`solid`);if(!c&&n!==`none`){let r=e.join(s,`apps/server`),i=await h.pathExists(r);i&&(n===`orpc`?await M({dependencies:[`@orpc/server`,`@orpc/client`],projectDir:r}):n===`trpc`&&(await M({dependencies:[`@trpc/server`,`@trpc/client`],projectDir:r}),t.backend===`hono`?await M({dependencies:[`@hono/trpc-server`],projectDir:r}):t.backend===`elysia`&&await M({dependencies:[`@elysiajs/trpc`],projectDir:r}))),d&&(p?n===`orpc`?await M({dependencies:[`@orpc/react-query`,`@orpc/client`,`@orpc/server`],projectDir:l}):n===`trpc`&&await M({dependencies:[`@trpc/tanstack-react-query`,`@trpc/client`,`@trpc/server`],projectDir:l}):m?n===`orpc`&&await M({dependencies:[`@orpc/vue-query`,`@orpc/client`,`@orpc/server`],projectDir:l}):g?n===`orpc`&&await M({dependencies:[`@orpc/svelte-query`,`@orpc/client`,`@orpc/server`,`@tanstack/svelte-query`],projectDir:l}):_&&n===`orpc`&&await M({dependencies:[`@orpc/solid-query`,`@orpc/client`,`@orpc/server`,`@tanstack/solid-query`],projectDir:l})),f&&(n===`trpc`?await M({dependencies:[`@trpc/tanstack-react-query`,`@trpc/client`,`@trpc/server`],projectDir:u}):n===`orpc`&&await M({dependencies:[`@orpc/react-query`,`@orpc/client`,`@orpc/server`],projectDir:u}))}let v=[`react-router`,`tanstack-router`,`tanstack-start`,`next`,`native-nativewind`,`native-unistyles`],y=i.includes(`solid`),b=i.some(e=>v.includes(e));if(b&&!c){let t=[`@tanstack/react-query`],n=[`@tanstack/react-query-devtools`],r=i.some(e=>e!==`native-nativewind`&&e!==`native-unistyles`&&v.includes(e)),a=i.includes(`native-nativewind`)||i.includes(`native-unistyles`);if(r&&d){let r=e.join(l,`package.json`);if(await h.pathExists(r))try{await M({dependencies:t,devDependencies:n,projectDir:l})}catch{}}if(a&&f){let n=e.join(u,`package.json`);if(await h.pathExists(n))try{await M({dependencies:t,projectDir:u})}catch{}}}if(y&&!c){let t=[`@tanstack/solid-query`],n=[`@tanstack/solid-query-devtools`];if(d){let r=e.join(l,`package.json`);if(await h.pathExists(r))try{await M({dependencies:t,devDependencies:n,projectDir:l})}catch{}}}if(c){if(d){let t=e.join(l,`package.json`);if(await h.pathExists(t))try{let e=[`convex`];i.includes(`tanstack-start`)&&e.push(`@convex-dev/react-query`),g&&e.push(`convex-svelte`),await M({dependencies:e,projectDir:l})}catch{}}if(f){let t=e.join(u,`package.json`);if(await h.pathExists(t))try{await M({dependencies:[`convex`],projectDir:u})}catch{}}let t=`@${r}/backend`,n=o===`npm`?`*`:`workspace:*`,a=async(e,t,n)=>{try{let r=await h.readJson(e);r.dependencies||={},r.dependencies[t]!==n&&(r.dependencies[t]=n,await h.writeJson(e,r,{spaces:2}))}catch{}};if(d){let r=e.join(l,`package.json`);await h.pathExists(r)&&await a(r,t,n)}if(f){let r=e.join(u,`package.json`);await h.pathExists(r)&&await a(r,t,n)}}}async function ce(t){let{projectName:n,auth:r,frontend:i,backend:a,projectDir:o}=t;if(a===`convex`||!r)return;let s=e.join(o,`apps/server`),c=e.join(o,`apps/web`),l=e.join(o,`apps/native`),u=await h.pathExists(c),d=await h.pathExists(l),f=await h.pathExists(s);try{f&&await M({dependencies:[`better-auth`],projectDir:s});let e=i.some(e=>[`react-router`,`tanstack-router`,`tanstack-start`,`next`,`nuxt`,`svelte`].includes(e));e&&u&&await M({dependencies:[`better-auth`],projectDir:c}),(i.includes(`native-nativewind`)||i.includes(`native-unistyles`))&&d&&(await M({dependencies:[`better-auth`,`@better-auth/expo`],projectDir:l}),f&&await M({dependencies:[`@better-auth/expo`],projectDir:s}))}catch(e){p.error(g.red(`Failed to configure authentication dependencies`)),e instanceof Error&&p.error(g.red(e.message))}}function le(e=32){let t=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`,n=``,r=t.length;for(let i=0;i<e;i++)n+=t.charAt(Math.floor(Math.random()*r));return n}async function ue(t){let{projectName:n,backend:r,runtime:i,api:a,projectDir:o}=t;if(r===`convex`)return;let s=r,c=e.join(o,`apps/server`),l=[],u=[];s===`hono`?(l.push(`hono`),a===`trpc`&&l.push(`@hono/trpc-server`),i===`node`&&(l.push(`@hono/node-server`),u.push(`tsx`,`@types/node`))):s===`elysia`?(l.push(`elysia`,`@elysiajs/cors`),a===`trpc`&&l.push(`@elysiajs/trpc`),i===`node`&&(l.push(`@elysiajs/node`),u.push(`tsx`,`@types/node`))):s===`express`&&(l.push(`express`,`cors`),u.push(`@types/express`,`@types/cors`),i===`node`&&u.push(`tsx`,`@types/node`)),i===`bun`&&u.push(`@types/bun`),(l.length>0||u.length>0)&&await M({dependencies:l,devDependencies:u,projectDir:c})}async function de(t,n){let r=e.join(t,`README.md`),i=fe(n);try{await h.writeFile(r,i)}catch(e){p.error(`Failed to create README.md file:`,e)}}function fe(e){let{projectName:t,packageManager:n,database:r,auth:i,addons:a=[],orm:o=`drizzle`,runtime:s=`bun`,frontend:c=[`tanstack-router`],backend:l=`hono`,api:u=`trpc`}=e,d=l===`convex`,f=c.includes(`react-router`),p=c.includes(`tanstack-router`),m=c.includes(`native-nativewind`)||c.includes(`native-unistyles`),h=c.includes(`next`),g=c.includes(`tanstack-start`),_=c.includes(`svelte`),v=c.includes(`solid`),y=c.includes(`nuxt`),b=n===`npm`?`npm run`:n,x=`3001`;return(f||_)&&(x=`5173`),`# ${t}
|
|
3
3
|
|
|
4
4
|
This project was created with [Better-T-Stack](https://github.com/AmanVarshney01/create-better-t-stack), a modern TypeScript stack that combines ${p?`React, TanStack Router`:f?`React, React Router`:h?`Next.js`:g?`React, TanStack Start`:_?`SvelteKit`:y?`Nuxt`:v?`SolidJS`:``}, ${l[0].toUpperCase()+l.slice(1)}${d?``:`, ${u.toUpperCase()}`}, and more.
|
|
5
5
|
|
|
@@ -58,7 +58,7 @@ ${p||f||h||g||_||y||v?`│ ├── web/ # Frontend application (${p?
|
|
|
58
58
|
## Available Scripts
|
|
59
59
|
|
|
60
60
|
${he(b,r,o,i,m,a,l)}
|
|
61
|
-
`}function pe(e,t,n,r,i,a,o,s){let c=o===`convex`,l=a.includes(`tanstack-router`),u=a.includes(`react-router`),d=a.includes(`native`),f=a.includes(`next`),p=a.includes(`tanstack-start`),m=a.includes(`svelte`),h=a.includes(`nuxt`),g=a.includes(`solid`),_=[`- **TypeScript** - For type safety and improved developer experience`];l?_.push(`- **TanStack Router** - File-based routing with full type safety`):u?_.push(`- **React Router** - Declarative routing for React`):f?_.push(`- **Next.js** - Full-stack React framework`):p?_.push(`- **TanStack Start** - SSR framework with TanStack Router`):m?_.push(`- **SvelteKit** - Web framework for building Svelte apps`):h?_.push(`- **Nuxt** - The Intuitive Vue Framework`):g&&_.push(`- **SolidJS** - Simple and performant reactivity`),d&&(_.push(`- **React Native** - Build mobile apps using React`),_.push(`- **Expo** - Tools for React Native development`)),_.push(`- **TailwindCSS** - Utility-first CSS for rapid UI development`,`- **shadcn/ui** - Reusable UI components`),c?_.push(`- **Convex** - Reactive backend-as-a-service platform`):(o===`hono`?_.push(`- **Hono** - Lightweight, performant server framework`):o===`express`?_.push(`- **Express** - Fast, unopinionated web framework`):o===`elysia`?_.push(`- **Elysia** - Type-safe, high-performance framework`):o===`next`&&_.push(`- **Next.js** - Full-stack React framework`),s===`trpc`?_.push(`- **tRPC** - End-to-end type-safe APIs`):s===`orpc`&&_.push(`- **oRPC** - End-to-end type-safe APIs with OpenAPI integration`),_.push(`- **${i===`bun`?`Bun`:`Node.js`}** - Runtime environment`)),e!==`none`&&!c&&_.push(`- **${r===`drizzle`?`Drizzle`:r===`prisma`?`Prisma`:`Mongoose`}** - TypeScript-first ORM`,`- **${e===`sqlite`?`SQLite/Turso`:e===`postgres`?`PostgreSQL`:e===`mysql`?`MySQL`:`MongoDB`}** - Database engine`),t&&!c&&_.push(`- **Authentication** - Email & password authentication with Better Auth`);for(let e of n)e===`pwa`?_.push(`- **PWA** - Progressive Web App support`):e===`tauri`?_.push(`- **Tauri** - Build native desktop applications`):e===`biome`?_.push(`- **Biome** - Linting and formatting`):e===`husky`?_.push(`- **Husky** - Git hooks for code quality`):e===`starlight`?_.push(`- **Starlight** - Documentation site with Astro`):e===`turborepo`&&_.push(`- **Turborepo** - Optimized monorepo build system`);return _.join(`
|
|
61
|
+
`}function pe(e,t,n,r,i,a,o,s){let c=o===`convex`,l=a.includes(`tanstack-router`),u=a.includes(`react-router`),d=a.includes(`native-nativewind`)||a.includes(`native-unistyles`),f=a.includes(`next`),p=a.includes(`tanstack-start`),m=a.includes(`svelte`),h=a.includes(`nuxt`),g=a.includes(`solid`),_=[`- **TypeScript** - For type safety and improved developer experience`];l?_.push(`- **TanStack Router** - File-based routing with full type safety`):u?_.push(`- **React Router** - Declarative routing for React`):f?_.push(`- **Next.js** - Full-stack React framework`):p?_.push(`- **TanStack Start** - SSR framework with TanStack Router`):m?_.push(`- **SvelteKit** - Web framework for building Svelte apps`):h?_.push(`- **Nuxt** - The Intuitive Vue Framework`):g&&_.push(`- **SolidJS** - Simple and performant reactivity`),d&&(_.push(`- **React Native** - Build mobile apps using React`),_.push(`- **Expo** - Tools for React Native development`)),_.push(`- **TailwindCSS** - Utility-first CSS for rapid UI development`,`- **shadcn/ui** - Reusable UI components`),c?_.push(`- **Convex** - Reactive backend-as-a-service platform`):(o===`hono`?_.push(`- **Hono** - Lightweight, performant server framework`):o===`express`?_.push(`- **Express** - Fast, unopinionated web framework`):o===`elysia`?_.push(`- **Elysia** - Type-safe, high-performance framework`):o===`next`&&_.push(`- **Next.js** - Full-stack React framework`),s===`trpc`?_.push(`- **tRPC** - End-to-end type-safe APIs`):s===`orpc`&&_.push(`- **oRPC** - End-to-end type-safe APIs with OpenAPI integration`),_.push(`- **${i===`bun`?`Bun`:`Node.js`}** - Runtime environment`)),e!==`none`&&!c&&_.push(`- **${r===`drizzle`?`Drizzle`:r===`prisma`?`Prisma`:`Mongoose`}** - TypeScript-first ORM`,`- **${e===`sqlite`?`SQLite/Turso`:e===`postgres`?`PostgreSQL`:e===`mysql`?`MySQL`:`MongoDB`}** - Database engine`),t&&!c&&_.push(`- **Authentication** - Email & password authentication with Better Auth`);for(let e of n)e===`pwa`?_.push(`- **PWA** - Progressive Web App support`):e===`tauri`?_.push(`- **Tauri** - Build native desktop applications`):e===`biome`?_.push(`- **Biome** - Linting and formatting`):e===`husky`?_.push(`- **Husky** - Git hooks for code quality`):e===`starlight`?_.push(`- **Starlight** - Documentation site with Astro`):e===`turborepo`&&_.push(`- **Turborepo** - Optimized monorepo build system`);return _.join(`
|
|
62
62
|
`)}function me(e,t,n,r){if(e===`none`)return``;let i=`## Database Setup
|
|
63
63
|
|
|
64
64
|
`;return e===`sqlite`?i+=`This project uses SQLite${r===`drizzle`?` with Drizzle ORM`:` with Prisma`}.
|
|
@@ -153,9 +153,9 @@ DATABASE_AUTH_TOKEN=your_auth_token`)}async function ke(r){let{projectName:i,orm
|
|
|
153
153
|
|
|
154
154
|
DATABASE_URL="your_connection_string"`)}async function Pe(n){let{projectName:r,packageManager:i,projectDir:s}=n,c=d();c.start(`Setting up Neon PostgreSQL`);try{let n=await Ae(i);c.stop(`Setting up Neon PostgreSQL`),n||(o.info(`Please authenticate with Neon to continue:`),await je(i));let r=e.basename(s),l=await f({message:`Enter a name for your Neon project:`,defaultValue:r,initialValue:r});a(l)&&(t(g.red(`Operation cancelled`)),process.exit(0));let u=await Me(l,i);if(!u)throw Error(`Failed to create project - couldn't get connection information`);let p=d();p.start(`Configuring database connection`),await h.ensureDir(e.join(s,`apps/server`)),await H(s,u),p.stop(`Neon database configured successfully!`)}catch(e){c.stop(g.red(`Neon PostgreSQL setup failed`)),e instanceof Error&&m.error(g.red(e.message)),await H(s),Ne()}}async function Fe(t){let{projectName:n,database:r,orm:i,dbSetup:a,backend:o,projectDir:s}=t;if(o===`convex`||r===`none`){if(o!==`convex`){let t=e.join(s,`apps/server`),n=e.join(t,`src/db`);await h.pathExists(n)&&await h.remove(n)}return}let c=d(),l=e.join(s,`apps/server`);if(await h.pathExists(l))try{i===`prisma`?await M({dependencies:[`@prisma/client`],devDependencies:[`prisma`],projectDir:l}):i===`drizzle`?r===`sqlite`?await M({dependencies:[`drizzle-orm`,`@libsql/client`],devDependencies:[`drizzle-kit`],projectDir:l}):r===`postgres`?await M({dependencies:[`drizzle-orm`,`pg`],devDependencies:[`drizzle-kit`,`@types/pg`],projectDir:l}):r===`mysql`&&await M({dependencies:[`drizzle-orm`,`mysql2`],devDependencies:[`drizzle-kit`],projectDir:l}):i===`mongoose`&&await M({dependencies:[`mongoose`],devDependencies:[],projectDir:l}),r===`sqlite`&&a===`turso`?await ke(t):r===`postgres`?i===`prisma`&&a===`prisma-postgres`?await xe(t):a===`neon`&&await Pe(t):r===`mongodb`&&a===`mongodb-atlas`&&await ve(t)}catch(e){c.stop(g.red(`Failed to set up database`)),e instanceof Error&&p.error(g.red(e.message))}}async function U(t,n){await h.ensureDir(e.dirname(t));let r=``;await h.pathExists(t)&&(r=await h.readFile(t,`utf8`));let i=!1,a=``;for(let{key:e,value:t,condition:o}of n)if(o){let n=RegExp(`^${e}=.*$`,`m`),o=t??``;if(n.test(r)){let t=r.match(n);t&&t[0]!==`${e}=${o}`&&(r=r.replace(n,`${e}=${o}`),i=!0)}else a+=`${e}=${o}\n`,i=!0}a&&(r.length>0&&!r.endsWith(`
|
|
155
155
|
`)&&(r+=`
|
|
156
|
-
`),r+=a),i&&await h.writeFile(t,r.trimEnd())}async function Ie(t){let{projectName:n,backend:r,frontend:i,database:a,orm:o,auth:s,examples:c,dbSetup:l,projectDir:u}=t,d=i.includes(`react-router`),f=i.includes(`tanstack-router`),p=i.includes(`tanstack-start`),m=i.includes(`next`),g=i.includes(`nuxt`),_=i.includes(`svelte`),v=i.includes(`solid`),y=d||f||p||m||g||v||_;if(y){let t=e.join(u,`apps/web`);if(await h.pathExists(t)){let n=`VITE_SERVER_URL`,i=`http://localhost:3000`;m?n=`NEXT_PUBLIC_SERVER_URL`:g?n=`NUXT_PUBLIC_SERVER_URL`:_&&(n=`PUBLIC_SERVER_URL`),r===`convex`&&(n=m?`NEXT_PUBLIC_CONVEX_URL`:g?`NUXT_PUBLIC_CONVEX_URL`:_?`PUBLIC_CONVEX_URL`:`VITE_CONVEX_URL`,i=`https://<YOUR_CONVEX_URL>`);let a=[{key:n,value:i,condition:!0}];await U(e.join(t,`.env`),a)}}if(i.includes(`native`)){let t=e.join(u,`apps/native`);if(await h.pathExists(t)){let n=`EXPO_PUBLIC_SERVER_URL`,i=`http://localhost:3000`;r===`convex`&&(n=`EXPO_PUBLIC_CONVEX_URL`,i=`https://<YOUR_CONVEX_URL>`);let a=[{key:n,value:i,condition:!0}];await U(e.join(t,`.env`),a)}}if(r===`convex`)return;let b=e.join(u,`apps/server`);if(!await h.pathExists(b))return;let x=e.join(b,`.env`),S=`http://localhost:3001`;(d||_)&&(S=`http://localhost:5173`);let C=null,w=l===`turso`||l===`prisma-postgres`||l===`mongodb-atlas`||l===`neon`;if(a!==`none`&&!w)switch(a){case`postgres`:C=`postgresql://postgres:postgres@localhost:5432/mydb?schema=public`;break;case`mysql`:C=`mysql://root:password@localhost:3306/mydb`;break;case`mongodb`:C=`mongodb://localhost:27017/mydatabase`;break;case`sqlite`:C=`file:./local.db`;break}let T=[{key:`CORS_ORIGIN`,value:S,condition:!0},{key:`BETTER_AUTH_SECRET`,value:le(),condition:!!s},{key:`BETTER_AUTH_URL`,value:`http://localhost:3000`,condition:!!s},{key:`DATABASE_URL`,value:C,condition:a!==`none`&&!w},{key:`GOOGLE_GENERATIVE_AI_API_KEY`,value:``,condition:c?.includes(`ai`)||!1}];await U(x,T)}async function Le(t){let{projectName:n,examples:r,frontend:i,backend:a,projectDir:o}=t;if(!(a===`convex`||!r||r.length===0||r[0]===`none`)&&r.includes(`ai`)){let t=e.join(o,`apps/web`),n=e.join(o,`apps/server`),r=await h.pathExists(t),
|
|
156
|
+
`),r+=a),i&&await h.writeFile(t,r.trimEnd())}async function Ie(t){let{projectName:n,backend:r,frontend:i,database:a,orm:o,auth:s,examples:c,dbSetup:l,projectDir:u}=t,d=i.includes(`react-router`),f=i.includes(`tanstack-router`),p=i.includes(`tanstack-start`),m=i.includes(`next`),g=i.includes(`nuxt`),_=i.includes(`svelte`),v=i.includes(`solid`),y=d||f||p||m||g||v||_;if(y){let t=e.join(u,`apps/web`);if(await h.pathExists(t)){let n=`VITE_SERVER_URL`,i=`http://localhost:3000`;m?n=`NEXT_PUBLIC_SERVER_URL`:g?n=`NUXT_PUBLIC_SERVER_URL`:_&&(n=`PUBLIC_SERVER_URL`),r===`convex`&&(n=m?`NEXT_PUBLIC_CONVEX_URL`:g?`NUXT_PUBLIC_CONVEX_URL`:_?`PUBLIC_CONVEX_URL`:`VITE_CONVEX_URL`,i=`https://<YOUR_CONVEX_URL>`);let a=[{key:n,value:i,condition:!0}];await U(e.join(t,`.env`),a)}}if(i.includes(`native-nativewind`)||i.includes(`native-unistyles`)){let t=e.join(u,`apps/native`);if(await h.pathExists(t)){let n=`EXPO_PUBLIC_SERVER_URL`,i=`http://localhost:3000`;r===`convex`&&(n=`EXPO_PUBLIC_CONVEX_URL`,i=`https://<YOUR_CONVEX_URL>`);let a=[{key:n,value:i,condition:!0}];await U(e.join(t,`.env`),a)}}if(r===`convex`)return;let b=e.join(u,`apps/server`);if(!await h.pathExists(b))return;let x=e.join(b,`.env`),S=`http://localhost:3001`;(d||_)&&(S=`http://localhost:5173`);let C=null,w=l===`turso`||l===`prisma-postgres`||l===`mongodb-atlas`||l===`neon`;if(a!==`none`&&!w)switch(a){case`postgres`:C=`postgresql://postgres:postgres@localhost:5432/mydb?schema=public`;break;case`mysql`:C=`mysql://root:password@localhost:3306/mydb`;break;case`mongodb`:C=`mongodb://localhost:27017/mydatabase`;break;case`sqlite`:C=`file:./local.db`;break}let T=[{key:`CORS_ORIGIN`,value:S,condition:!0},{key:`BETTER_AUTH_SECRET`,value:le(),condition:!!s},{key:`BETTER_AUTH_URL`,value:`http://localhost:3000`,condition:!!s},{key:`DATABASE_URL`,value:C,condition:a!==`none`&&!w},{key:`GOOGLE_GENERATIVE_AI_API_KEY`,value:``,condition:c?.includes(`ai`)||!1}];await U(x,T)}async function Le(t){let{projectName:n,examples:r,frontend:i,backend:a,projectDir:o}=t;if(!(a===`convex`||!r||r.length===0||r[0]===`none`)&&r.includes(`ai`)){let t=e.join(o,`apps/web`),n=e.join(o,`apps/server`),r=await h.pathExists(t),s=await h.pathExists(n),c=i.includes(`nuxt`),l=i.includes(`svelte`);if(r){let e=[`ai`];c?e.push(`@ai-sdk/vue`):l&&e.push(`@ai-sdk/svelte`),await M({dependencies:e,projectDir:t})}s&&a!==`none`&&await M({dependencies:[`ai`,`@ai-sdk/google`],projectDir:n})}}async function Re({projectDir:e,packageManager:t,addons:n=[]}){let r=d();try{r.start(`Running ${t} install...`),await b({cwd:e,stderr:`inherit`})`${t} install`,r.stop(`Dependencies installed successfully`),(n.includes(`biome`)||n.includes(`husky`))&&await ze(e,t)}catch(e){r.stop(g.red(`Failed to install dependencies`)),e instanceof Error&&p.error(g.red(`Installation error: ${e.message}`))}}async function ze(e,t){let n=d();try{n.start(`Running Biome format check...`),await b({cwd:e,stderr:`inherit`})`${t} biome check --write .`,n.stop(`Biome check completed successfully`)}catch{n.stop(g.yellow(`Biome check encountered issues`)),o.warn(g.yellow(`Some files may need manual formatting`))}}function Be(e){let{database:t,projectName:n,relativePath:r,packageManager:i,depsInstalled:a,orm:o,addons:s,runtime:c,frontend:l,backend:u}=e,d=u===`convex`,f=i===`npm`?`npm run`:i,p=`cd ${r}`,h=s?.includes(`husky`)||s?.includes(`biome`),_=!d&&t!==`none`?Ue(t,o,f,c):``,v=s?.includes(`tauri`)?We(f):``,y=h?He(f):``,b=l?.includes(`native-nativewind`)||l?.includes(`native-unistyles`)?Ve(d):``,x=s?.includes(`pwa`)&&(l?.includes(`react-router`)||l?.includes(`tanstack-router`))?Ge():``,S=s?.includes(`starlight`)?Ke(f):``,C=l?.some(e=>[`tanstack-router`,`react-router`,`next`,`tanstack-start`,`nuxt`,`svelte`,`solid`].includes(e)),w=l?.includes(`native-nativewind`)||l?.includes(`native-unistyles`),T=i===`bun`&&w&&C?Je():``,E=!d&&t!==`none`&&o===`none`?qe():``,D=l?.includes(`react-router`),O=l?.includes(`svelte`),k=D||O?`5173`:`3001`,A=N(i,`taze -r`),j=`${g.bold(`Next steps`)}\n${g.cyan(`1.`)} ${p}\n`,M=2;a||(j+=`${g.cyan(`${M++}.`)} ${i} install\n`),d&&(j+=`${g.cyan(`${M++}.`)} ${f} dev:setup ${g.dim(`(this will guide you through Convex project setup)`)}\n`),j+=`${g.cyan(`${M++}.`)} ${f} dev\n\n`,j+=`${g.bold(`Your project will be available at:`)}\n`,C?j+=`${g.cyan(`•`)} Frontend: http://localhost:${k}\n`:!w&&!s?.includes(`starlight`)&&(j+=`${g.yellow(`NOTE:`)} You are creating a backend-only app (no frontend selected)\n`),d||(j+=`${g.cyan(`•`)} Backend API: http://localhost:3000\n`),s?.includes(`starlight`)&&(j+=`${g.cyan(`•`)} Docs: http://localhost:4321\n`),b&&(j+=`\n${b.trim()}\n`),_&&(j+=`\n${_.trim()}\n`),v&&(j+=`\n${v.trim()}\n`),y&&(j+=`\n${y.trim()}\n`),x&&(j+=`\n${x.trim()}\n`),S&&(j+=`\n${S.trim()}\n`),E&&(j+=`\n${E.trim()}\n`),T&&(j+=`\n${T.trim()}\n`),j+=`\n${g.bold(`Update all dependencies:
|
|
157
157
|
`)}${g.cyan(A)}\n\n`,j+=`${g.bold(`Like Better-T Stack?`)} Please consider giving us a star on GitHub:\n`,j+=g.cyan(`https://github.com/AmanVarshney01/create-better-t-stack`),m.box(j)}function Ve(e){let t=e?`EXPO_PUBLIC_CONVEX_URL`:`EXPO_PUBLIC_SERVER_URL`,n=e?`https://<YOUR_CONVEX_URL>`:`http://<YOUR_LOCAL_IP>:3000`,r=`.env`,i=e?`your Convex deployment URL (find after running 'dev:setup')`:`your local IP address`;return`${g.yellow(`NOTE:`)} For Expo connectivity issues, update apps/native/${r} \nwith ${i}:\n${`${t}=${n}`}\n`}function He(e){return`${g.bold(`Linting and formatting:`)}\n${g.cyan(`•`)} Format and lint fix: ${`${e} check`}\n`}function Ue(e,t,n,r){let i=[];return t===`prisma`?(e===`sqlite`&&i.push(`${g.yellow(`NOTE:`)} Turso support with Prisma is in Early Access and requires additional setup.`,`Learn more at: https://www.prisma.io/docs/orm/overview/databases/turso`),r===`bun`&&i.push(`${g.yellow(`NOTE:`)} Prisma with Bun may require additional configuration. If you encounter errors,\nfollow the guidance provided in the error messages`),i.push(`${g.cyan(`•`)} Apply schema: ${`${n} db:push`}`),i.push(`${g.cyan(`•`)} Database UI: ${`${n} db:studio`}`)):t===`drizzle`?(i.push(`${g.cyan(`•`)} Apply schema: ${`${n} db:push`}`),i.push(`${g.cyan(`•`)} Database UI: ${`${n} db:studio`}`),e===`sqlite`&&i.push(`${g.cyan(`•`)} Start local DB (if needed): ${`cd apps/server && ${n} db:local`}`)):t===`none`&&i.push(`${g.yellow(`NOTE:`)} Manual database schema setup required.`),i.length?`${g.bold(`Database commands:`)}\n${i.join(`
|
|
158
|
-
`)}`:``}function We(e){return`\n${g.bold(`Desktop app with Tauri:`)}\n${g.cyan(`•`)} Start desktop app: ${`cd apps/web && ${e} desktop:dev`}\n${g.cyan(`•`)} Build desktop app: ${`cd apps/web && ${e} desktop:build`}\n${g.yellow(`NOTE:`)} Tauri requires Rust and platform-specific dependencies.\nSee: https://v2.tauri.app/start/prerequisites/`}function Ge(){return`\n${g.bold(`PWA with React Router v7:`)}\n${g.yellow(`NOTE:`)} There is a known compatibility issue between VitePWA and React Router v7.\nSee: https://github.com/vite-pwa/vite-plugin-pwa/issues/809`}function Ke(e){return`\n${g.bold(`Documentation with Starlight:`)}\n${g.cyan(`•`)} Start docs site: ${`cd apps/docs && ${e} dev`}\n${g.cyan(`•`)} Build docs site: ${`cd apps/docs && ${e} build`}`}function qe(){return`\n${g.yellow(`WARNING:`)} Database selected without an ORM. Features requiring database access (e.g., examples, auth) need manual setup.`}function Je(){return`\n${g.yellow(`WARNING:`)} 'bun' might cause issues with web + native apps in a monorepo. Use 'pnpm' if problems arise.`}async function Ye(e,t){await Xe(e,t),t.backend===`convex`?await Qe(e,t):await Ze(e,t)}async function Xe(t,n){let r=e.join(t,`package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.name=n.projectName,i.scripts||={};let a=i.scripts,s=n.backend===`convex`?`@${n.projectName}/backend`:`server`,c=``;n.addons.includes(`turborepo`)?c=`turbo -F ${s} dev`:n.packageManager===`bun`?c=`bun run --filter ${s} dev`:n.packageManager===`pnpm`?c=`pnpm --filter ${s} dev`:n.packageManager===`npm`&&(c=`npm run dev --workspace ${s}`);let l=``;n.packageManager===`pnpm`?l=`pnpm -r dev`:n.packageManager===`npm`?l=`npm run dev --workspaces`:n.packageManager===`bun`&&(l=`bun run --filter '*' dev`);let u=n.backend!==`convex`&&n.database!==`none`&&n.orm!==`none`&&n.orm!==`mongoose`;n.addons.includes(`turborepo`)?(a.dev=`turbo dev`,a.build=`turbo build`,a[`check-types`]=`turbo check-types`,a[`dev:native`]=`turbo -F native dev`,a[`dev:web`]=`turbo -F web dev`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`turbo -F ${s} setup`),u&&(a[`db:push`]=`turbo -F ${s} db:push`,a[`db:studio`]=`turbo -F ${s} db:studio`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`turbo -F ${s} db:generate`,a[`db:migrate`]=`turbo -F ${s} db:migrate`))):n.packageManager===`pnpm`?(a.dev=l,a.build=`pnpm -r build`,a[`check-types`]=`pnpm -r check-types`,a[`dev:native`]=`pnpm --filter native dev`,a[`dev:web`]=`pnpm --filter web dev`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`pnpm --filter ${s} setup`),u&&(a[`db:push`]=`pnpm --filter ${s} db:push`,a[`db:studio`]=`pnpm --filter ${s} db:studio`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`pnpm --filter ${s} db:generate`,a[`db:migrate`]=`pnpm --filter ${s} db:migrate`))):n.packageManager===`npm`?(a.dev=l,a.build=`npm run build --workspaces`,a[`check-types`]=`npm run check-types --workspaces`,a[`dev:native`]=`npm run dev --workspace native`,a[`dev:web`]=`npm run dev --workspace web`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`npm run setup --workspace ${s}`),u&&(a[`db:push`]=`npm run db:push --workspace ${s}`,a[`db:studio`]=`npm run db:studio --workspace ${s}`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`npm run db:generate --workspace ${s}`,a[`db:migrate`]=`npm run db:migrate --workspace ${s}`))):n.packageManager===`bun`&&(a.dev=l,a.build=`bun run --filter '*' build`,a[`check-types`]=`bun run --filter '*' check-types`,a[`dev:native`]=`bun run --filter native dev`,a[`dev:web`]=`bun run --filter web dev`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`bun run --filter ${s} setup`),u&&(a[`db:push`]=`bun run --filter ${s} db:push`,a[`db:studio`]=`bun run --filter ${s} db:studio`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`bun run --filter ${s} db:generate`,a[`db:migrate`]=`bun run --filter ${s} db:migrate`))),n.addons.includes(`biome`)&&(a.check=`biome check --write .`),n.addons.includes(`husky`)&&(a.prepare=`husky`,i[`lint-staged`]={"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":[`biome check --write .`]});try{let{stdout:e}=await x(n.packageManager,[`-v`],{cwd:t});i.packageManager=`${n.packageManager}@${e.trim()}`}catch{o.warn(`Could not determine ${n.packageManager} version.`)}i.workspaces||=[];let d=i.workspaces;if(n.backend===`convex`){d.includes(`packages/*`)||d.push(`packages/*`);let e=n.frontend.length>0||n.addons.includes(`starlight`);e&&!d.includes(`apps/*`)&&d.push(`apps/*`)}else d.includes(`apps/*`)||d.push(`apps/*`),d.includes(`packages/*`)||d.push(`packages/*`);await h.writeJson(r,i,{spaces:2})}async function Ze(t,n){let r=e.join(t,`apps/server/package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.scripts||={};let a=i.scripts;n.database!==`none`&&(n.database===`sqlite`&&n.orm===`drizzle`&&(a[`db:local`]=`turso dev --db-file local.db`),n.orm===`prisma`?(a[`db:push`]=`prisma db push --schema ./prisma/schema`,a[`db:studio`]=`prisma studio`,a[`db:generate`]=`prisma generate --schema ./prisma/schema`,a[`db:migrate`]=`prisma migrate dev`):n.orm===`drizzle`&&(a[`db:push`]=`drizzle-kit push`,a[`db:studio`]=`drizzle-kit studio`,a[`db:generate`]=`drizzle-kit generate`,a[`db:migrate`]=`drizzle-kit migrate`)),await h.writeJson(r,i,{spaces:2})}async function Qe(t,n){let r=e.join(t,`packages/backend/package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.name=`@${n.projectName}/backend`,i.scripts||={},await h.writeJson(r,i,{spaces:2})}async function $e(e,t){if(!t)return;let n=await b({cwd:e,reject:!1,stderr:`pipe`})`git --version`;if(n.exitCode!==0){o.warn(g.yellow(`Git is not installed`));return}let r=await b({cwd:e,reject:!1,stderr:`pipe`})`git init`;if(r.exitCode!==0)throw Error(`Git initialization failed: ${r.stderr}`)}async function et(t){let{projectName:n,runtime:r,backend:i,projectDir:a}=t;if(i===`convex`||i===`next`||r===`none`)return;let o=e.join(a,`apps/server`);await h.pathExists(o)&&(r===`bun`?await tt(o,i):r===`node`&&await nt(o,i))}async function tt(t,n){let r=e.join(t,`package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.scripts={...i.scripts,dev:`bun run --hot src/index.ts`,start:`bun run dist/src/index.js`},await h.writeJson(r,i,{spaces:2}),await M({devDependencies:[`@types/bun`],projectDir:t})}async function nt(t,n){let r=e.join(t,`package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.scripts={...i.scripts,dev:`tsx watch src/index.ts`,start:`node dist/src/index.js`},await h.writeJson(r,i,{spaces:2}),await M({devDependencies:[`tsx`,`@types/node`],projectDir:t}),n===`hono`?await M({dependencies:[`@hono/node-server`],projectDir:t}):n===`elysia`&&await M({dependencies:[`@elysiajs/node`],projectDir:t})}async function W(t,n,r){try{let i=await h.readFile(t,`utf-8`),a=w.compile(i),o=a(r);await h.ensureDir(e.dirname(n)),await h.writeFile(n,o)}catch(e){throw p.error(`Error processing template ${t}:`,e),Error(`Failed to process template ${t}`)}}w.registerHelper(`or`,(e,t)=>e||t),w.registerHelper(`eq`,(e,t)=>e===t),w.registerHelper(`includes`,(e,t)=>Array.isArray(e)&&e.includes(t));async function G(t,n,r,i,a=!0){let o=await C(t,{cwd:n,dot:!0,onlyFiles:!0,absolute:!1});for(let t of o){let o=e.join(n,t),s=t;t.endsWith(`.hbs`)&&(s=t.slice(0,-4));let c=e.basename(t);c===`_gitignore`?s=e.join(e.dirname(t),`.gitignore`):c===`_npmrc`&&(s=e.join(e.dirname(t),`.npmrc`));let l=e.join(r,s);try{if(await h.ensureDir(e.dirname(l)),!a&&await h.pathExists(l))continue;o.endsWith(`.hbs`)?await W(o,l,i):await h.copy(o,l,{overwrite:!0})}catch{}}}async function rt(t,n){let r=e.join(k,`templates/base`);await G([`**/*`],r,t,n),await h.ensureDir(e.join(t,`packages`))}async function it(t,n){let r=n.frontend.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),i=n.frontend.includes(`nuxt`),a=n.frontend.includes(`svelte`),o=n.frontend.includes(`solid`),s=n.frontend.includes(`native`),c=n.backend===`convex`;if(r||i||a||o){let s=e.join(t,`apps/web`);if(await h.ensureDir(s),r){let t=e.join(k,`templates/frontend/react/web-base`);await h.pathExists(t)&&await G(`**/*`,t,s,n);let r=n.frontend.find(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e));if(r){let t=e.join(k,`templates/frontend/react/${r}`);if(await h.pathExists(t)&&await G(`**/*`,t,s,n),!c&&n.api!==`none`){let t=e.join(k,`templates/api/${n.api}/web/react/base`);await h.pathExists(t)&&await G(`**/*`,t,s,n)}}}else if(i){let t=e.join(k,`templates/frontend/nuxt`);if(await h.pathExists(t)&&await G(`**/*`,t,s,n),!c&&n.api===`orpc`){let t=e.join(k,`templates/api/${n.api}/web/nuxt`);await h.pathExists(t)&&await G(`**/*`,t,s,n)}}else if(a){let t=e.join(k,`templates/frontend/svelte`);if(await h.pathExists(t)&&await G(`**/*`,t,s,n),!c&&n.api===`orpc`){let t=e.join(k,`templates/api/${n.api}/web/svelte`);await h.pathExists(t)&&await G(`**/*`,t,s,n)}}else if(o){let t=e.join(k,`templates/frontend/solid`);if(await h.pathExists(t)&&await G(`**/*`,t,s,n),!c&&n.api===`orpc`){let t=e.join(k,`templates/api/${n.api}/web/solid`);await h.pathExists(t)&&await G(`**/*`,t,s,n)}}}if(s){let r=e.join(t,`apps/native`);await h.ensureDir(r);let i=e.join(k,`templates/frontend/native`);if(await h.pathExists(i)&&await G(`**/*`,i,r,n),!c&&(n.api===`trpc`||n.api===`orpc`)){let t=e.join(k,`templates/api/${n.api}/native`);await h.pathExists(t)&&await G(`**/*`,t,r,n)}}}async function at(t,n){if(n.backend===`convex`){let r=e.join(t,`packages/backend`),i=e.join(k,`templates/backend/convex/packages/backend`);await h.ensureDir(r),await h.pathExists(i)&&await G(`**/*`,i,r,n);let a=e.join(t,`apps/server`);await h.pathExists(a)&&await h.remove(a);return}let r=e.join(t,`apps/server`);await h.ensureDir(r);let i=e.join(k,`templates/backend/server/server-base`);await h.pathExists(i)&&await G(`**/*`,i,r,n);let a=e.join(k,`templates/backend/server/${n.backend}`);if(await h.pathExists(a)&&await G(`**/*`,a,r,n,!0),n.api!==`none`){let t=e.join(k,`templates/api/${n.api}/server/base`);await h.pathExists(t)&&await G(`**/*`,t,r,n,!0);let i=e.join(k,`templates/api/${n.api}/server/${n.backend}`);await h.pathExists(i)&&await G(`**/*`,i,r,n,!0)}}async function ot(t,n){if(n.backend===`convex`||n.orm===`none`||n.database===`none`)return;let r=e.join(t,`apps/server`);await h.ensureDir(r);let i=e.join(k,`templates/db/${n.orm}/${n.database}`);await h.pathExists(i)&&await G(`**/*`,i,r,n)}async function st(t,n){if(n.backend===`convex`||!n.auth)return;let r=e.join(t,`apps/server`),i=e.join(t,`apps/web`),a=e.join(t,`apps/native`),o=await h.pathExists(r),s=await h.pathExists(i),c=await h.pathExists(a),l=n.frontend.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),u=n.frontend.includes(`nuxt`),d=n.frontend.includes(`svelte`),f=n.frontend.includes(`solid`),p=n.frontend.includes(`native`);if(o){let t=e.join(k,`templates/auth/server/base`);if(await h.pathExists(t)&&await G(`**/*`,t,r,n),n.backend===`next`){let t=e.join(k,`templates/auth/server/next`);await h.pathExists(t)&&await G(`**/*`,t,r,n)}if(n.orm!==`none`&&n.database!==`none`){let t=n.orm,i=n.database,a=``;t===`drizzle`?a=e.join(k,`templates/auth/server/db/drizzle/${i}`):t===`prisma`?a=e.join(k,`templates/auth/server/db/prisma/${i}`):t===`mongoose`&&(a=e.join(k,`templates/auth/server/db/mongoose/${i}`)),a&&await h.pathExists(a)&&await G(`**/*`,a,r,n)}}if((l||u||d||f)&&s){if(l){let t=e.join(k,`templates/auth/web/react/base`);await h.pathExists(t)&&await G(`**/*`,t,i,n);let r=n.frontend.find(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e));if(r){let t=e.join(k,`templates/auth/web/react/${r}`);await h.pathExists(t)&&await G(`**/*`,t,i,n)}}else if(u){let t=e.join(k,`templates/auth/web/nuxt`);await h.pathExists(t)&&await G(`**/*`,t,i,n)}else if(d){if(n.api===`orpc`){let t=e.join(k,`templates/auth/web/svelte`);await h.pathExists(t)&&await G(`**/*`,t,i,n)}}else if(f&&n.api===`orpc`){let t=e.join(k,`templates/auth/web/solid`);await h.pathExists(t)&&await G(`**/*`,t,i,n)}}if(p&&c){let t=e.join(k,`templates/auth/native`);await h.pathExists(t)&&await G(`**/*`,t,a,n)}}async function ct(t,n){if(!(!n.addons||n.addons.length===0))for(let r of n.addons){if(r===`none`)continue;let i=e.join(k,`templates/addons/${r}`),a=t;if(r===`pwa`&&(i=e.join(k,`templates/addons/pwa/apps/web`),a=e.join(t,`apps/web`),!await h.pathExists(a)))continue;await h.pathExists(i)&&await G(`**/*`,i,a,n)}}async function lt(t,n){if(!n.examples||n.examples.length===0||n.examples[0]===`none`)return;let r=e.join(t,`apps/server`),i=e.join(t,`apps/web`),a=await h.pathExists(r),o=await h.pathExists(i),s=n.frontend.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),c=n.frontend.includes(`nuxt`),l=n.frontend.includes(`svelte`),u=n.frontend.includes(`solid`);for(let t of n.examples){if(t===`none`)continue;let d=e.join(k,`templates/examples/${t}`);if(a&&n.backend!==`convex`){let i=e.join(d,`server`);if(t===`ai`&&n.backend===`next`){let t=e.join(i,`next`);await h.pathExists(t)&&await G(`**/*`,t,r,n,!1)}if(n.orm!==`none`&&n.database!==`none`){let t=e.join(i,n.orm,`base`);await h.pathExists(t)&&await G(`**/*`,t,r,n,!1);let a=e.join(i,n.orm,n.database);await h.pathExists(a)&&await G(`**/*`,a,r,n,!1)}let a=[`${n.orm}/**`];t===`ai`&&n.backend===`next`&&a.push(`next/**`);let o=await C([`**/*.ts`,`**/*.hbs`],{cwd:i,onlyFiles:!0,deep:1,ignore:a});for(let t of o){let a=e.join(i,t),o=e.join(r,t.replace(`.hbs`,``));try{a.endsWith(`.hbs`)?await W(a,o,n):await h.pathExists(o)||await h.copy(a,o,{overwrite:!1})}catch{}}}if(o){if(s){let t=e.join(d,`web/react`);if(await h.pathExists(t)){let r=n.frontend.find(e=>[`next`,`react-router`,`tanstack-router`,`tanstack-start`].includes(e));if(r){let a=e.join(t,r);await h.pathExists(a)&&await G(`**/*`,a,i,n,!1)}}}else if(c){let t=e.join(d,`web/nuxt`);await h.pathExists(t)&&await G(`**/*`,t,i,n,!1)}else if(l){let t=e.join(d,`web/svelte`);await h.pathExists(t)&&await G(`**/*`,t,i,n,!1)}else if(u){let t=e.join(d,`web/solid`);await h.pathExists(t)&&await G(`**/*`,t,i,n,!1)}}}}async function ut(t,n){let r=e.join(k,`templates/extras`);if(n.packageManager===`pnpm`){let n=e.join(r,`pnpm-workspace.yaml`),i=e.join(t,`pnpm-workspace.yaml`);await h.pathExists(n)&&await h.copy(n,i)}if(n.packageManager===`pnpm`&&(n.frontend.includes(`native`)||n.frontend.includes(`nuxt`))){let i=e.join(r,`_npmrc.hbs`),a=e.join(t,`.npmrc`);await h.pathExists(i)&&await W(i,a,n)}}async function dt(e){let n=e.projectDir,r=e.backend===`convex`;try{return await h.ensureDir(n),await rt(n,e),await it(n,e),await at(n,e),r||(await ot(n,e),await st(n,e)),e.examples.length>0&&e.examples[0]!==`none`&&await lt(n,e),await ct(n,e),await se(e),r||(await ue(e),await Fe(e),await et(e),e.examples.length>0&&e.examples[0]!==`none`&&await Le(e)),e.addons.length>0&&e.addons[0]!==`none`&&await ne(e),!r&&e.auth&&await ce(e),await ut(n,e),await Ie(e),await Ye(n,e),await de(n,e),await $e(n,e.git),o.success(`Project template successfully scaffolded!`),e.install&&await Re({projectDir:n,packageManager:e.packageManager,addons:e.addons}),Be({...e,depsInstalled:e.install}),n}catch(e){e instanceof Error?(t(g.red(`Error during project creation: ${e.message}`)),console.error(e.stack),process.exit(1)):(t(g.red(`An unexpected error occurred: ${String(e)}`)),console.error(e),process.exit(1))}}async function ft(e,n){if(e!==void 0)return e;let r=n?.includes(`react-router`)||n?.includes(`tanstack-router`)||n?.includes(`solid`),i=n?.includes(`react-router`)||n?.includes(`tanstack-router`)||n?.includes(`nuxt`)||n?.includes(`svelte`)||n?.includes(`solid`),o=[{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)`},{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`}],c=o.filter(e=>e.value===`pwa`?r:e.value===`tauri`?i:!0),l=A.addons.filter(e=>c.some(t=>t.value===e)),u=await s({message:`Select addons`,options:c,initialValues:l,required:!1});return a(u)&&(t(g.red(`Operation cancelled`)),process.exit(0)),u.includes(`husky`)&&!u.includes(`biome`)&&u.push(`biome`),u}async function pt(e,n,r){if(r===`convex`)return`none`;if(e)return e;let i=n?.includes(`nuxt`),o=n?.includes(`svelte`),s=n?.includes(`solid`),c=[{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`}];(i||o||s)&&(c=[{value:`orpc`,label:`oRPC`,hint:`End-to-end type-safe APIs (Required for ${i?`Nuxt`:o?`Svelte`:`Solid`} frontend)`}]);let l=await u({message:`Select API type`,options:c,initialValue:i||o||s?`orpc`:A.api});return a(l)&&(t(g.red(`Operation cancelled`)),process.exit(0)),(i||o||s)&&l!==`orpc`?`orpc`:l}async function mt(e,r,i){if(i===`convex`||!r)return!1;if(e!==void 0)return e;let o=await n({message:`Add authentication with Better-Auth?`,initialValue:A.auth});return a(o)&&(t(g.red(`Operation cancelled`)),process.exit(0)),o}async function ht(e,n){if(e!==void 0)return e;let r=n?.some(e=>e===`nuxt`||e===`solid`),i=[{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`}];r||i.push({value:`convex`,label:`Convex`,hint:`Reactive backend-as-a-service platform`});let o=A.backend;r&&o===`convex`&&(o=`hono`);let s=await u({message:`Select backend framework`,options:i,initialValue:o});return a(s)&&(t(g.red(`Operation cancelled`)),process.exit(0)),s}async function gt(e,n){if(n===`convex`)return`none`;if(e!==void 0)return e;let r=await u({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:A.database});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function _t(e,n,r,i){if(i===`convex`)return`none`;if(n!==void 0)return n;if(e===`none`||e===`sqlite`&&r===`prisma`)return`none`;let o=[];if(e===`sqlite`)o=[{value:`turso`,label:`Turso`,hint:`SQLite for Production. Powered by libSQL`},{value:`none`,label:`None`,hint:`Manual setup`}];else if(e===`postgres`)o=[{value:`neon`,label:`Neon Postgres`,hint:`Serverless Postgres with branching capability`},...r===`prisma`?[{value:`prisma-postgres`,label:`Prisma Postgres`,hint:`Instant Postgres for Global Applications`}]:[],{value:`none`,label:`None`,hint:`Manual setup`}];else if(e===`mongodb`)o=[{value:`mongodb-atlas`,label:`MongoDB Atlas`,hint:`The most effective way to deploy MongoDB`},{value:`none`,label:`None`,hint:`Manual setup`}];else return`none`;let s=await u({message:`Select ${e} setup option`,options:o,initialValue:`none`});return a(s)&&(t(g.red(`Operation cancelled`)),process.exit(0)),s}async function vt(e,n,r,i){if(e!==void 0)return e;if(i===`convex`)return[`todo`];if(n===`none`)return[];let o=r&&r.length===1&&r[0]===`native`;if(o)return[];let c=r?.some(e=>[`react-router`,`tanstack-router`,`tanstack-start`,`next`,`nuxt`,`svelte`,`solid`].includes(e))??!1,l=!r||r.length===0;if(!c&&!l)return[];let u=[],d=[{value:`todo`,label:`Todo App`,hint:`A simple CRUD example app`}];return i!==`elysia`&&!r?.includes(`solid`)&&d.push({value:`ai`,label:`AI Chat`,hint:`A simple AI chat interface using AI SDK`}),u=await s({message:`Include examples`,options:d,required:!1,initialValues:A.examples}),a(u)&&(t(g.red(`Operation cancelled`)),process.exit(0)),u}async function yt(e,n){if(e!==void 0)return e;let r=await s({message:`Select platforms to develop for`,options:[{value:`web`,label:`Web`,hint:`React, Vue or Svelte Web Application`},{value:`native`,label:`Native`,hint:`Create a React Native/Expo app`}],required:!1,initialValues:[`web`]});a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0));let i=[];if(r.includes(`web`)){let e=[{value:`tanstack-router`,label:`TanStack Router`,hint:`Modern and scalable routing for React Applications`},{value:`react-router`,label:`React Router`,hint:`A user‑obsessed, standards‑focused, multi‑strategy router`},{value:`next`,label:`Next.js`,hint:`The React Framework for the Web`},{value:`nuxt`,label:`Nuxt`,hint:`The Progressive Web Framework for Vue.js`},{value:`svelte`,label:`Svelte`,hint:`web development for the rest of us`},{value:`solid`,label:`Solid`,hint:`Simple and performant reactivity for building user interfaces`},{value:`tanstack-start`,label:`TanStack Start (beta)`,hint:`SSR, Server Functions, API Routes and more with TanStack Router`}],r=e.filter(e=>n===`convex`?e.value!==`nuxt`&&e.value!==`solid`:!0),o=await u({message:`Choose frontend framework`,options:r,initialValue:A.frontend[0]});a(o)&&(t(g.red(`Operation cancelled`)),process.exit(0)),i.push(o)}return r.includes(`native`)&&i.push(`native`),i}async function bt(e){if(e!==void 0)return e;let r=await n({message:`Initialize git repository?`,initialValue:A.git});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function xt(e){if(e!==void 0)return e;let r=await n({message:`Install dependencies?`,initialValue:A.install});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}const K={prisma:{value:`prisma`,label:`Prisma`,hint:`Powerful, feature-rich ORM`},mongoose:{value:`mongoose`,label:`Mongoose`,hint:`Elegant object modeling tool`},drizzle:{value:`drizzle`,label:`Drizzle`,hint:`Lightweight and performant TypeScript ORM`}};async function St(e,n,r,i){if(i===`convex`||!n)return`none`;if(e!==void 0)return e;let o=[...r===`mongodb`?[K.prisma,K.mongoose]:[K.drizzle,K.prisma]],s=await u({message:`Select ORM`,options:o,initialValue:r===`mongodb`?`prisma`:A.orm});return a(s)&&(t(g.red(`Operation cancelled`)),process.exit(0)),s}async function Ct(e){if(e!==void 0)return e;let n=E(),r=await u({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:n});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function wt(e,n){if(n===`convex`)return`none`;if(e!==void 0)return e;if(n===`next`)return`node`;let r=await u({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:A.runtime});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function Tt(e,n,i,a){let o=await r({frontend:({results:t})=>yt(e.frontend,e.backend),backend:({results:t})=>ht(e.backend,t.frontend),runtime:({results:t})=>wt(e.runtime,t.backend),database:({results:t})=>gt(e.database,t.backend),orm:({results:t})=>St(e.orm,t.database!==`none`,t.database,t.backend),api:({results:t})=>pt(e.api,t.frontend,t.backend),auth:({results:t})=>mt(e.auth,t.database!==`none`,t.backend),addons:({results:t})=>ft(e.addons,t.frontend),examples:({results:t})=>vt(e.examples,t.database,t.frontend,t.backend),dbSetup:({results:t})=>_t(t.database??`none`,e.dbSetup,t.orm,t.backend),git:()=>bt(e.git),packageManager:()=>Ct(e.packageManager),install:()=>xt(e.install)},{onCancel:()=>{t(g.red(`Operation cancelled`)),process.exit(0)}});return o.backend===`convex`&&(o.runtime=`none`,o.database=`none`,o.orm=`none`,o.api=`none`,o.auth=!1,o.dbSetup=`none`),{projectName:n,projectDir:i,relativePath:a,frontend:o.frontend,backend:o.backend,runtime:o.runtime,database:o.database,orm:o.orm,auth:o.auth,addons:o.addons,examples:o.examples,git:o.git,packageManager:o.packageManager,install:o.install,dbSetup:o.dbSetup,api:o.api}}const Et=[`<`,`>`,`:`,`"`,`|`,`?`,`*`],q=255;function J(e){if(e!==`.`){if(!e)return`Project name cannot be empty`;if(e.length>q)return`Project name must be less than ${q} characters`;if(Et.some(t=>e.includes(t)))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 Y(n){if(n){if(n===`.`)return n;let t=e.basename(n),r=J(t);if(!r)return n}let r=!1,i=``,o=A.projectName,s=1;for(;h.pathExistsSync(e.resolve(process.cwd(),o))&&h.readdirSync(e.resolve(process.cwd(),o)).length>0;)o=`${A.projectName}-${s}`,s++;for(;!r;){let s=await f({message:`Enter your project name or path (relative to current directory)`,placeholder:o,initialValue:n,defaultValue:o,validate:t=>{let n=t.trim()||o,r=e.basename(n),i=J(r);if(i)return i;if(n!==`.`){let t=e.resolve(process.cwd(),n);if(!t.startsWith(process.cwd()))return`Project path must be within current directory`}}});a(s)&&(t(g.red(`Operation cancelled.`)),process.exit(0)),i=s||o,r=!0}return i}function X(e){let t=[];if(e.projectName&&t.push(`${g.blue(`Project Name:`)} ${e.projectName}`),e.frontend!==void 0){let n=Array.isArray(e.frontend)?e.frontend:[e.frontend],r=n.length>0&&n[0]!==void 0?n.join(`, `):`none`;t.push(`${g.blue(`Frontend:`)} ${r}`)}if(e.backend!==void 0&&t.push(`${g.blue(`Backend Framework:`)} ${String(e.backend)}`),e.runtime!==void 0&&t.push(`${g.blue(`Runtime:`)} ${String(e.runtime)}`),e.api!==void 0&&t.push(`${g.blue(`API:`)} ${String(e.api)}`),e.database!==void 0&&t.push(`${g.blue(`Database:`)} ${String(e.database)}`),e.orm!==void 0&&t.push(`${g.blue(`ORM:`)} ${String(e.orm)}`),e.auth!==void 0){let n=typeof e.auth==`boolean`?e.auth?`Yes`:`No`:String(e.auth);t.push(`${g.blue(`Authentication:`)} ${n}`)}if(e.addons!==void 0){let n=Array.isArray(e.addons)?e.addons:[e.addons],r=n.length>0&&n[0]!==void 0?n.join(`, `):`none`;t.push(`${g.blue(`Addons:`)} ${r}`)}if(e.examples!==void 0){let n=Array.isArray(e.examples)?e.examples:[e.examples],r=n.length>0&&n[0]!==void 0?n.join(`, `):`none`;t.push(`${g.blue(`Examples:`)} ${r}`)}if(e.git!==void 0){let n=typeof e.git==`boolean`?e.git?`Yes`:`No`:String(e.git);t.push(`${g.blue(`Git Init:`)} ${n}`)}if(e.packageManager!==void 0&&t.push(`${g.blue(`Package Manager:`)} ${String(e.packageManager)}`),e.install!==void 0){let n=typeof e.install==`boolean`?e.install?`Yes`:`No`:String(e.install);t.push(`${g.blue(`Install Dependencies:`)} ${n}`)}return e.dbSetup!==void 0&&t.push(`${g.blue(`Database Setup:`)} ${String(e.dbSetup)}`),t.length===0?g.yellow(`No configuration selected.`):t.join(`
|
|
158
|
+
`)}`:``}function We(e){return`\n${g.bold(`Desktop app with Tauri:`)}\n${g.cyan(`•`)} Start desktop app: ${`cd apps/web && ${e} desktop:dev`}\n${g.cyan(`•`)} Build desktop app: ${`cd apps/web && ${e} desktop:build`}\n${g.yellow(`NOTE:`)} Tauri requires Rust and platform-specific dependencies.\nSee: https://v2.tauri.app/start/prerequisites/`}function Ge(){return`\n${g.bold(`PWA with React Router v7:`)}\n${g.yellow(`NOTE:`)} There is a known compatibility issue between VitePWA and React Router v7.\nSee: https://github.com/vite-pwa/vite-plugin-pwa/issues/809`}function Ke(e){return`\n${g.bold(`Documentation with Starlight:`)}\n${g.cyan(`•`)} Start docs site: ${`cd apps/docs && ${e} dev`}\n${g.cyan(`•`)} Build docs site: ${`cd apps/docs && ${e} build`}`}function qe(){return`\n${g.yellow(`WARNING:`)} Database selected without an ORM. Features requiring database access (e.g., examples, auth) need manual setup.`}function Je(){return`\n${g.yellow(`WARNING:`)} 'bun' might cause issues with web + native apps in a monorepo. Use 'pnpm' if problems arise.`}async function Ye(e,t){await Xe(e,t),t.backend===`convex`?await Qe(e,t):await Ze(e,t)}async function Xe(t,n){let r=e.join(t,`package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.name=n.projectName,i.scripts||={};let a=i.scripts,s=n.backend===`convex`?`@${n.projectName}/backend`:`server`,c=``;n.addons.includes(`turborepo`)?c=`turbo -F ${s} dev`:n.packageManager===`bun`?c=`bun run --filter ${s} dev`:n.packageManager===`pnpm`?c=`pnpm --filter ${s} dev`:n.packageManager===`npm`&&(c=`npm run dev --workspace ${s}`);let l=``;n.packageManager===`pnpm`?l=`pnpm -r dev`:n.packageManager===`npm`?l=`npm run dev --workspaces`:n.packageManager===`bun`&&(l=`bun run --filter '*' dev`);let u=n.backend!==`convex`&&n.database!==`none`&&n.orm!==`none`&&n.orm!==`mongoose`;n.addons.includes(`turborepo`)?(a.dev=`turbo dev`,a.build=`turbo build`,a[`check-types`]=`turbo check-types`,a[`dev:native`]=`turbo -F native dev`,a[`dev:web`]=`turbo -F web dev`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`turbo -F ${s} setup`),u&&(a[`db:push`]=`turbo -F ${s} db:push`,a[`db:studio`]=`turbo -F ${s} db:studio`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`turbo -F ${s} db:generate`,a[`db:migrate`]=`turbo -F ${s} db:migrate`))):n.packageManager===`pnpm`?(a.dev=l,a.build=`pnpm -r build`,a[`check-types`]=`pnpm -r check-types`,a[`dev:native`]=`pnpm --filter native dev`,a[`dev:web`]=`pnpm --filter web dev`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`pnpm --filter ${s} setup`),u&&(a[`db:push`]=`pnpm --filter ${s} db:push`,a[`db:studio`]=`pnpm --filter ${s} db:studio`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`pnpm --filter ${s} db:generate`,a[`db:migrate`]=`pnpm --filter ${s} db:migrate`))):n.packageManager===`npm`?(a.dev=l,a.build=`npm run build --workspaces`,a[`check-types`]=`npm run check-types --workspaces`,a[`dev:native`]=`npm run dev --workspace native`,a[`dev:web`]=`npm run dev --workspace web`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`npm run setup --workspace ${s}`),u&&(a[`db:push`]=`npm run db:push --workspace ${s}`,a[`db:studio`]=`npm run db:studio --workspace ${s}`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`npm run db:generate --workspace ${s}`,a[`db:migrate`]=`npm run db:migrate --workspace ${s}`))):n.packageManager===`bun`&&(a.dev=l,a.build=`bun run --filter '*' build`,a[`check-types`]=`bun run --filter '*' check-types`,a[`dev:native`]=`bun run --filter native dev`,a[`dev:web`]=`bun run --filter web dev`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`bun run --filter ${s} setup`),u&&(a[`db:push`]=`bun run --filter ${s} db:push`,a[`db:studio`]=`bun run --filter ${s} db:studio`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`bun run --filter ${s} db:generate`,a[`db:migrate`]=`bun run --filter ${s} db:migrate`))),n.addons.includes(`biome`)&&(a.check=`biome check --write .`),n.addons.includes(`husky`)&&(a.prepare=`husky`,i[`lint-staged`]={"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":[`biome check --write .`]});try{let{stdout:e}=await x(n.packageManager,[`-v`],{cwd:t});i.packageManager=`${n.packageManager}@${e.trim()}`}catch{o.warn(`Could not determine ${n.packageManager} version.`)}i.workspaces||=[];let d=i.workspaces;if(n.backend===`convex`){d.includes(`packages/*`)||d.push(`packages/*`);let e=n.frontend.length>0||n.addons.includes(`starlight`);e&&!d.includes(`apps/*`)&&d.push(`apps/*`)}else d.includes(`apps/*`)||d.push(`apps/*`),d.includes(`packages/*`)||d.push(`packages/*`);await h.writeJson(r,i,{spaces:2})}async function Ze(t,n){let r=e.join(t,`apps/server/package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.scripts||={};let a=i.scripts;n.database!==`none`&&(n.database===`sqlite`&&n.orm===`drizzle`&&(a[`db:local`]=`turso dev --db-file local.db`),n.orm===`prisma`?(a[`db:push`]=`prisma db push --schema ./prisma/schema`,a[`db:studio`]=`prisma studio`,a[`db:generate`]=`prisma generate --schema ./prisma/schema`,a[`db:migrate`]=`prisma migrate dev`):n.orm===`drizzle`&&(a[`db:push`]=`drizzle-kit push`,a[`db:studio`]=`drizzle-kit studio`,a[`db:generate`]=`drizzle-kit generate`,a[`db:migrate`]=`drizzle-kit migrate`)),await h.writeJson(r,i,{spaces:2})}async function Qe(t,n){let r=e.join(t,`packages/backend/package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.name=`@${n.projectName}/backend`,i.scripts||={},await h.writeJson(r,i,{spaces:2})}async function $e(e,t){if(!t)return;let n=await b({cwd:e,reject:!1,stderr:`pipe`})`git --version`;if(n.exitCode!==0){o.warn(g.yellow(`Git is not installed`));return}let r=await b({cwd:e,reject:!1,stderr:`pipe`})`git init`;if(r.exitCode!==0)throw Error(`Git initialization failed: ${r.stderr}`)}async function et(t){let{projectName:n,runtime:r,backend:i,projectDir:a}=t;if(i===`convex`||i===`next`||r===`none`)return;let o=e.join(a,`apps/server`);await h.pathExists(o)&&(r===`bun`?await tt(o,i):r===`node`&&await nt(o,i))}async function tt(t,n){let r=e.join(t,`package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.scripts={...i.scripts,dev:`bun run --hot src/index.ts`,start:`bun run dist/src/index.js`},await h.writeJson(r,i,{spaces:2}),await M({devDependencies:[`@types/bun`],projectDir:t})}async function nt(t,n){let r=e.join(t,`package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.scripts={...i.scripts,dev:`tsx watch src/index.ts`,start:`node dist/src/index.js`},await h.writeJson(r,i,{spaces:2}),await M({devDependencies:[`tsx`,`@types/node`],projectDir:t}),n===`hono`?await M({dependencies:[`@hono/node-server`],projectDir:t}):n===`elysia`&&await M({dependencies:[`@elysiajs/node`],projectDir:t})}async function W(t,n,r){try{let i=await h.readFile(t,`utf-8`),a=w.compile(i),o=a(r);await h.ensureDir(e.dirname(n)),await h.writeFile(n,o)}catch(e){throw p.error(`Error processing template ${t}:`,e),Error(`Failed to process template ${t}`)}}w.registerHelper(`or`,(e,t)=>e||t),w.registerHelper(`eq`,(e,t)=>e===t),w.registerHelper(`includes`,(e,t)=>Array.isArray(e)&&e.includes(t));async function G(t,n,r,i,a=!0){let o=await C(t,{cwd:n,dot:!0,onlyFiles:!0,absolute:!1});for(let t of o){let o=e.join(n,t),s=t;t.endsWith(`.hbs`)&&(s=t.slice(0,-4));let c=e.basename(t);c===`_gitignore`?s=e.join(e.dirname(t),`.gitignore`):c===`_npmrc`&&(s=e.join(e.dirname(t),`.npmrc`));let l=e.join(r,s);try{if(await h.ensureDir(e.dirname(l)),!a&&await h.pathExists(l))continue;o.endsWith(`.hbs`)?await W(o,l,i):await h.copy(o,l,{overwrite:!0})}catch{}}}async function rt(t,n){let r=e.join(k,`templates/base`);await G([`**/*`],r,t,n),await h.ensureDir(e.join(t,`packages`))}async function it(t,n){let r=n.frontend.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),i=n.frontend.includes(`nuxt`),a=n.frontend.includes(`svelte`),o=n.frontend.includes(`solid`),s=n.frontend.includes(`native-nativewind`),c=n.frontend.includes(`native-unistyles`),l=s||c,u=n.backend===`convex`;if(r||i||a||o){let s=e.join(t,`apps/web`);if(await h.ensureDir(s),r){let t=e.join(k,`templates/frontend/react/web-base`);await h.pathExists(t)&&await G(`**/*`,t,s,n);let r=n.frontend.find(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e));if(r){let t=e.join(k,`templates/frontend/react/${r}`);if(await h.pathExists(t)&&await G(`**/*`,t,s,n),!u&&n.api!==`none`){let t=e.join(k,`templates/api/${n.api}/web/react/base`);await h.pathExists(t)&&await G(`**/*`,t,s,n)}}}else if(i){let t=e.join(k,`templates/frontend/nuxt`);if(await h.pathExists(t)&&await G(`**/*`,t,s,n),!u&&n.api===`orpc`){let t=e.join(k,`templates/api/${n.api}/web/nuxt`);await h.pathExists(t)&&await G(`**/*`,t,s,n)}}else if(a){let t=e.join(k,`templates/frontend/svelte`);if(await h.pathExists(t)&&await G(`**/*`,t,s,n),!u&&n.api===`orpc`){let t=e.join(k,`templates/api/${n.api}/web/svelte`);await h.pathExists(t)&&await G(`**/*`,t,s,n)}}else if(o){let t=e.join(k,`templates/frontend/solid`);if(await h.pathExists(t)&&await G(`**/*`,t,s,n),!u&&n.api===`orpc`){let t=e.join(k,`templates/api/${n.api}/web/solid`);await h.pathExists(t)&&await G(`**/*`,t,s,n)}}}if(s||c){let r=e.join(t,`apps/native`);await h.ensureDir(r);let i=e.join(k,`templates/frontend/native/native-base`);await h.pathExists(i)&&await G(`**/*`,i,r,n);let a=``;s?a=`nativewind`:c&&(a=`unistyles`);let o=e.join(k,`templates/frontend/native/${a}`);if(await h.pathExists(o)&&await G(`**/*`,o,r,n,!0),!u&&(n.api===`trpc`||n.api===`orpc`)){let t=e.join(k,`templates/api/${n.api}/native`);await h.pathExists(t)&&await G(`**/*`,t,r,n)}}}async function at(t,n){if(n.backend===`none`)return;let r=e.join(t,`apps/server`);if(n.backend===`convex`){await h.pathExists(r)&&await h.remove(r);let i=e.join(t,`packages/backend`),a=e.join(k,`templates/backend/convex/packages/backend`);await h.ensureDir(i),await h.pathExists(a)&&await G(`**/*`,a,i,n);return}await h.ensureDir(r);let i=e.join(k,`templates/backend/server/server-base`);await h.pathExists(i)&&await G(`**/*`,i,r,n);let a=e.join(k,`templates/backend/server/${n.backend}`);if(await h.pathExists(a)&&await G(`**/*`,a,r,n,!0),n.api!==`none`){let t=e.join(k,`templates/api/${n.api}/server/base`);await h.pathExists(t)&&await G(`**/*`,t,r,n,!0);let i=e.join(k,`templates/api/${n.api}/server/${n.backend}`);await h.pathExists(i)&&await G(`**/*`,i,r,n,!0)}}async function ot(t,n){if(n.backend===`convex`||n.orm===`none`||n.database===`none`)return;let r=e.join(t,`apps/server`);await h.ensureDir(r);let i=e.join(k,`templates/db/${n.orm}/${n.database}`);await h.pathExists(i)&&await G(`**/*`,i,r,n)}async function st(t,n){if(n.backend===`convex`||!n.auth)return;let r=e.join(t,`apps/server`),i=e.join(t,`apps/web`),a=e.join(t,`apps/native`),o=await h.pathExists(r),s=await h.pathExists(i),c=await h.pathExists(a),l=n.frontend.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),u=n.frontend.includes(`nuxt`),d=n.frontend.includes(`svelte`),f=n.frontend.includes(`solid`),p=n.frontend.includes(`native-nativewind`),m=n.frontend.includes(`native-unistyles`),g=p||m;if(o){let t=e.join(k,`templates/auth/server/base`);if(await h.pathExists(t)&&await G(`**/*`,t,r,n),n.backend===`next`){let t=e.join(k,`templates/auth/server/next`);await h.pathExists(t)&&await G(`**/*`,t,r,n)}if(n.orm!==`none`&&n.database!==`none`){let t=n.orm,i=n.database,a=``;t===`drizzle`?a=e.join(k,`templates/auth/server/db/drizzle/${i}`):t===`prisma`?a=e.join(k,`templates/auth/server/db/prisma/${i}`):t===`mongoose`&&(a=e.join(k,`templates/auth/server/db/mongoose/${i}`)),a&&await h.pathExists(a)&&await G(`**/*`,a,r,n)}}if((l||u||d||f)&&s){if(l){let t=e.join(k,`templates/auth/web/react/base`);await h.pathExists(t)&&await G(`**/*`,t,i,n);let r=n.frontend.find(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e));if(r){let t=e.join(k,`templates/auth/web/react/${r}`);await h.pathExists(t)&&await G(`**/*`,t,i,n)}}else if(u){let t=e.join(k,`templates/auth/web/nuxt`);await h.pathExists(t)&&await G(`**/*`,t,i,n)}else if(d){if(n.api===`orpc`){let t=e.join(k,`templates/auth/web/svelte`);await h.pathExists(t)&&await G(`**/*`,t,i,n)}}else if(f&&n.api===`orpc`){let t=e.join(k,`templates/auth/web/solid`);await h.pathExists(t)&&await G(`**/*`,t,i,n)}}if(g&&c){let t=e.join(k,`templates/auth/native/native-base`);await h.pathExists(t)&&await G(`**/*`,t,a,n);let r=``;if(p?r=`nativewind`:m&&(r=`unistyles`),r){let t=e.join(k,`templates/auth/native/${r}`);await h.pathExists(t)&&await G(`**/*`,t,a,n)}}}async function ct(t,n){if(!(!n.addons||n.addons.length===0))for(let r of n.addons){if(r===`none`)continue;let i=e.join(k,`templates/addons/${r}`),a=t;if(r===`pwa`&&(i=e.join(k,`templates/addons/pwa/apps/web`),a=e.join(t,`apps/web`),!await h.pathExists(a)))continue;await h.pathExists(i)&&await G(`**/*`,i,a,n)}}async function lt(t,n){if(!n.examples||n.examples.length===0||n.examples[0]===`none`)return;let r=e.join(t,`apps/server`),i=e.join(t,`apps/web`),a=await h.pathExists(r),o=await h.pathExists(i),s=n.frontend.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),c=n.frontend.includes(`nuxt`),l=n.frontend.includes(`svelte`),u=n.frontend.includes(`solid`);for(let t of n.examples){if(t===`none`)continue;let d=e.join(k,`templates/examples/${t}`);if(a&&n.backend!==`convex`&&n.backend!==`none`){let i=e.join(d,`server`);if(t===`ai`&&n.backend===`next`){let t=e.join(i,`next`);await h.pathExists(t)&&await G(`**/*`,t,r,n,!1)}if(n.orm!==`none`&&n.database!==`none`){let t=e.join(i,n.orm,`base`);await h.pathExists(t)&&await G(`**/*`,t,r,n,!1);let a=e.join(i,n.orm,n.database);await h.pathExists(a)&&await G(`**/*`,a,r,n,!1)}let a=[`${n.orm}/**`];t===`ai`&&n.backend===`next`&&a.push(`next/**`);let o=await C([`**/*.ts`,`**/*.hbs`],{cwd:i,onlyFiles:!0,deep:1,ignore:a});for(let t of o){let a=e.join(i,t),o=e.join(r,t.replace(`.hbs`,``));try{a.endsWith(`.hbs`)?await W(a,o,n):await h.pathExists(o)||await h.copy(a,o,{overwrite:!1})}catch{}}}if(o){if(s){let t=e.join(d,`web/react`);if(await h.pathExists(t)){let r=n.frontend.find(e=>[`next`,`react-router`,`tanstack-router`,`tanstack-start`].includes(e));if(r){let a=e.join(t,r);await h.pathExists(a)&&await G(`**/*`,a,i,n,!1)}}}else if(c){let t=e.join(d,`web/nuxt`);await h.pathExists(t)&&await G(`**/*`,t,i,n,!1)}else if(l){let t=e.join(d,`web/svelte`);await h.pathExists(t)&&await G(`**/*`,t,i,n,!1)}else if(u){let t=e.join(d,`web/solid`);await h.pathExists(t)&&await G(`**/*`,t,i,n,!1)}}}}async function ut(t,n){let r=e.join(k,`templates/extras`),i=n.frontend.includes(`native-nativewind`),a=n.frontend.includes(`native-unistyles`),o=i||a;if(n.packageManager===`pnpm`){let n=e.join(r,`pnpm-workspace.yaml`),i=e.join(t,`pnpm-workspace.yaml`);await h.pathExists(n)&&await h.copy(n,i)}if(n.packageManager===`pnpm`&&(o||n.frontend.includes(`nuxt`))){let i=e.join(r,`_npmrc.hbs`),a=e.join(t,`.npmrc`);await h.pathExists(i)&&await W(i,a,n)}}async function dt(e){let n=e.projectDir,r=e.backend===`convex`;try{return await h.ensureDir(n),await rt(n,e),await it(n,e),await at(n,e),r||(await ot(n,e),await st(n,e)),e.examples.length>0&&e.examples[0]!==`none`&&await lt(n,e),await ct(n,e),await se(e),r||(await ue(e),await Fe(e),await et(e),e.examples.length>0&&e.examples[0]!==`none`&&await Le(e)),e.addons.length>0&&e.addons[0]!==`none`&&await ne(e),!r&&e.auth&&await ce(e),await ut(n,e),await Ie(e),await Ye(n,e),await de(n,e),await $e(n,e.git),o.success(`Project template successfully scaffolded!`),e.install&&await Re({projectDir:n,packageManager:e.packageManager,addons:e.addons}),Be({...e,depsInstalled:e.install}),n}catch(e){e instanceof Error?(t(g.red(`Error during project creation: ${e.message}`)),console.error(e.stack),process.exit(1)):(t(g.red(`An unexpected error occurred: ${String(e)}`)),console.error(e),process.exit(1))}}async function ft(e,n){if(e!==void 0)return e;let r=n?.includes(`react-router`)||n?.includes(`tanstack-router`)||n?.includes(`solid`),i=n?.includes(`react-router`)||n?.includes(`tanstack-router`)||n?.includes(`nuxt`)||n?.includes(`svelte`)||n?.includes(`solid`),o=[{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)`},{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`}],c=o.filter(e=>e.value===`pwa`?r:e.value===`tauri`?i:!0),l=A.addons.filter(e=>c.some(t=>t.value===e)),u=await s({message:`Select addons`,options:c,initialValues:l,required:!1});return a(u)&&(t(g.red(`Operation cancelled`)),process.exit(0)),u.includes(`husky`)&&!u.includes(`biome`)&&u.push(`biome`),u}async function pt(e,n,r){if(r===`convex`||r===`none`)return`none`;if(e)return e;let i=n?.includes(`nuxt`),o=n?.includes(`svelte`),s=n?.includes(`solid`),c=[{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`}];(i||o||s)&&(c=[{value:`orpc`,label:`oRPC`,hint:`End-to-end type-safe APIs (Required for ${i?`Nuxt`:o?`Svelte`:`Solid`} frontend)`}]);let l=await u({message:`Select API type`,options:c,initialValue:i||o||s?`orpc`:A.api});return a(l)&&(t(g.red(`Operation cancelled`)),process.exit(0)),(i||o||s)&&l!==`orpc`?`orpc`:l}async function mt(e,r,i){if(i===`convex`||!r)return!1;if(e!==void 0)return e;let o=await n({message:`Add authentication with Better-Auth?`,initialValue:A.auth});return a(o)&&(t(g.red(`Operation cancelled`)),process.exit(0)),o}async function ht(e,n){if(e!==void 0)return e;let r=n?.some(e=>e===`nuxt`||e===`solid`),i=[{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`}];r||i.push({value:`convex`,label:`Convex`,hint:`Reactive backend-as-a-service platform`}),i.push({value:`none`,label:`None`,hint:`No backend server (e.g., for a static site or client-only app)`});let o=A.backend;r&&o===`convex`&&(o=`hono`);let s=await u({message:`Select backend framework`,options:i,initialValue:o});return a(s)&&(t(g.red(`Operation cancelled`)),process.exit(0)),s}async function gt(e,n){if(n===`convex`||n===`none`)return`none`;if(e!==void 0)return e;let r=await u({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:A.database});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function _t(e,n,r,i){if(i===`convex`)return`none`;if(n!==void 0)return n;if(e===`none`||e===`sqlite`&&r===`prisma`)return`none`;let o=[];if(e===`sqlite`)o=[{value:`turso`,label:`Turso`,hint:`SQLite for Production. Powered by libSQL`},{value:`none`,label:`None`,hint:`Manual setup`}];else if(e===`postgres`)o=[{value:`neon`,label:`Neon Postgres`,hint:`Serverless Postgres with branching capability`},...r===`prisma`?[{value:`prisma-postgres`,label:`Prisma Postgres`,hint:`Instant Postgres for Global Applications`}]:[],{value:`none`,label:`None`,hint:`Manual setup`}];else if(e===`mongodb`)o=[{value:`mongodb-atlas`,label:`MongoDB Atlas`,hint:`The most effective way to deploy MongoDB`},{value:`none`,label:`None`,hint:`Manual setup`}];else return`none`;let s=await u({message:`Select ${e} setup option`,options:o,initialValue:`none`});return a(s)&&(t(g.red(`Operation cancelled`)),process.exit(0)),s}async function vt(e,n,r,i){if(e!==void 0)return e;if(i===`convex`)return[`todo`];if(i===`none`||n===`none`)return[];let o=r&&r.length===1&&(r[0]===`native-nativewind`||r[0]===`native-unistyles`);if(o)return[];let c=r?.some(e=>[`react-router`,`tanstack-router`,`tanstack-start`,`next`,`nuxt`,`svelte`,`solid`].includes(e))??!1,l=!r||r.length===0;if(!c&&!l)return[];let u=[],d=[{value:`todo`,label:`Todo App`,hint:`A simple CRUD example app`}];return i!==`elysia`&&!r?.includes(`solid`)&&d.push({value:`ai`,label:`AI Chat`,hint:`A simple AI chat interface using AI SDK`}),u=await s({message:`Include examples`,options:d,required:!1,initialValues:A.examples}),a(u)&&(t(g.red(`Operation cancelled`)),process.exit(0)),u}async function yt(e,n){if(e!==void 0)return e;let r=await s({message:`Select platforms to develop for`,options:[{value:`web`,label:`Web`,hint:`React, Vue or Svelte Web Application`},{value:`native`,label:`Native`,hint:`Create a React Native/Expo app`}],required:!1,initialValues:[`web`]});a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0));let i=[];if(r.includes(`web`)){let e=[{value:`tanstack-router`,label:`TanStack Router`,hint:`Modern and scalable routing for React Applications`},{value:`react-router`,label:`React Router`,hint:`A user‑obsessed, standards‑focused, multi‑strategy router`},{value:`next`,label:`Next.js`,hint:`The React Framework for the Web`},{value:`nuxt`,label:`Nuxt`,hint:`The Progressive Web Framework for Vue.js`},{value:`svelte`,label:`Svelte`,hint:`web development for the rest of us`},{value:`solid`,label:`Solid`,hint:`Simple and performant reactivity for building user interfaces`},{value:`tanstack-start`,label:`TanStack Start (beta)`,hint:`SSR, Server Functions, API Routes and more with TanStack Router`}],r=e.filter(e=>n===`convex`?e.value!==`nuxt`&&e.value!==`solid`:!0),o=await u({message:`Choose frontend framework`,options:r,initialValue:A.frontend[0]});a(o)&&(t(g.red(`Operation cancelled`)),process.exit(0)),i.push(o)}if(r.includes(`native`)){let e=await u({message:`Choose native framework`,options:[{value:`native-nativewind`,label:`NativeWind`,hint:`Use Tailwind CSS for React Native`},{value:`native-unistyles`,label:`Unistyles`,hint:`Consistent styling for React Native`}],initialValue:`native-nativewind`});a(e)&&(t(g.red(`Operation cancelled`)),process.exit(0)),i.push(e)}return i}async function bt(e){if(e!==void 0)return e;let r=await n({message:`Initialize git repository?`,initialValue:A.git});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function xt(e){if(e!==void 0)return e;let r=await n({message:`Install dependencies?`,initialValue:A.install});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}const K={prisma:{value:`prisma`,label:`Prisma`,hint:`Powerful, feature-rich ORM`},mongoose:{value:`mongoose`,label:`Mongoose`,hint:`Elegant object modeling tool`},drizzle:{value:`drizzle`,label:`Drizzle`,hint:`Lightweight and performant TypeScript ORM`}};async function St(e,n,r,i){if(i===`convex`||!n)return`none`;if(e!==void 0)return e;let o=[...r===`mongodb`?[K.prisma,K.mongoose]:[K.drizzle,K.prisma]],s=await u({message:`Select ORM`,options:o,initialValue:r===`mongodb`?`prisma`:A.orm});return a(s)&&(t(g.red(`Operation cancelled`)),process.exit(0)),s}async function Ct(e){if(e!==void 0)return e;let n=E(),r=await u({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:n});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function wt(e,n){if(n===`convex`||n===`none`)return`none`;if(e!==void 0)return e;if(n===`next`)return`node`;let r=await u({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:A.runtime});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function Tt(e,n,i,a){let o=await r({frontend:({results:t})=>yt(e.frontend,e.backend),backend:({results:t})=>ht(e.backend,t.frontend),runtime:({results:t})=>wt(e.runtime,t.backend),database:({results:t})=>gt(e.database,t.backend),orm:({results:t})=>St(e.orm,t.database!==`none`,t.database,t.backend),api:({results:t})=>pt(e.api,t.frontend,t.backend),auth:({results:t})=>mt(e.auth,t.database!==`none`,t.backend),addons:({results:t})=>ft(e.addons,t.frontend),examples:({results:t})=>vt(e.examples,t.database,t.frontend,t.backend),dbSetup:({results:t})=>_t(t.database??`none`,e.dbSetup,t.orm,t.backend),git:()=>bt(e.git),packageManager:()=>Ct(e.packageManager),install:()=>xt(e.install)},{onCancel:()=>{t(g.red(`Operation cancelled`)),process.exit(0)}});return o.backend===`convex`&&(o.runtime=`none`,o.database=`none`,o.orm=`none`,o.api=`none`,o.auth=!1,o.dbSetup=`none`),o.backend===`none`&&(o.runtime=`none`,o.database=`none`,o.orm=`none`,o.api=`none`,o.auth=!1,o.dbSetup=`none`,o.examples=[]),{projectName:n,projectDir:i,relativePath:a,frontend:o.frontend,backend:o.backend,runtime:o.runtime,database:o.database,orm:o.orm,auth:o.auth,addons:o.addons,examples:o.examples,git:o.git,packageManager:o.packageManager,install:o.install,dbSetup:o.dbSetup,api:o.api}}const Et=[`<`,`>`,`:`,`"`,`|`,`?`,`*`],q=255;function J(e){if(e!==`.`){if(!e)return`Project name cannot be empty`;if(e.length>q)return`Project name must be less than ${q} characters`;if(Et.some(t=>e.includes(t)))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 Y(n){if(n){if(n===`.`)return n;let t=e.basename(n),r=J(t);if(!r)return n}let r=!1,i=``,o=A.projectName,s=1;for(;h.pathExistsSync(e.resolve(process.cwd(),o))&&h.readdirSync(e.resolve(process.cwd(),o)).length>0;)o=`${A.projectName}-${s}`,s++;for(;!r;){let s=await f({message:`Enter your project name or path (relative to current directory)`,placeholder:o,initialValue:n,defaultValue:o,validate:t=>{let n=t.trim()||o,r=e.basename(n),i=J(r);if(i)return i;if(n!==`.`){let t=e.resolve(process.cwd(),n);if(!t.startsWith(process.cwd()))return`Project path must be within current directory`}}});a(s)&&(t(g.red(`Operation cancelled.`)),process.exit(0)),i=s||o,r=!0}return i}function X(e){let t=[];if(e.projectName&&t.push(`${g.blue(`Project Name:`)} ${e.projectName}`),e.frontend!==void 0){let n=Array.isArray(e.frontend)?e.frontend:[e.frontend],r=n.length>0&&n[0]!==void 0?n.join(`, `):`none`;t.push(`${g.blue(`Frontend:`)} ${r}`)}if(e.backend!==void 0&&t.push(`${g.blue(`Backend Framework:`)} ${String(e.backend)}`),e.runtime!==void 0&&t.push(`${g.blue(`Runtime:`)} ${String(e.runtime)}`),e.api!==void 0&&t.push(`${g.blue(`API:`)} ${String(e.api)}`),e.database!==void 0&&t.push(`${g.blue(`Database:`)} ${String(e.database)}`),e.orm!==void 0&&t.push(`${g.blue(`ORM:`)} ${String(e.orm)}`),e.auth!==void 0){let n=typeof e.auth==`boolean`?e.auth?`Yes`:`No`:String(e.auth);t.push(`${g.blue(`Authentication:`)} ${n}`)}if(e.addons!==void 0){let n=Array.isArray(e.addons)?e.addons:[e.addons],r=n.length>0&&n[0]!==void 0?n.join(`, `):`none`;t.push(`${g.blue(`Addons:`)} ${r}`)}if(e.examples!==void 0){let n=Array.isArray(e.examples)?e.examples:[e.examples],r=n.length>0&&n[0]!==void 0?n.join(`, `):`none`;t.push(`${g.blue(`Examples:`)} ${r}`)}if(e.git!==void 0){let n=typeof e.git==`boolean`?e.git?`Yes`:`No`:String(e.git);t.push(`${g.blue(`Git Init:`)} ${n}`)}if(e.packageManager!==void 0&&t.push(`${g.blue(`Package Manager:`)} ${String(e.packageManager)}`),e.install!==void 0){let n=typeof e.install==`boolean`?e.install?`Yes`:`No`:String(e.install);t.push(`${g.blue(`Install Dependencies:`)} ${n}`)}return e.dbSetup!==void 0&&t.push(`${g.blue(`Database Setup:`)} ${String(e.dbSetup)}`),t.length===0?g.yellow(`No configuration selected.`):t.join(`
|
|
159
159
|
`)}function Dt(e){let t=[];e.database===`none`?t.push(`--database none`):(t.push(`--database ${e.database}`),e.orm&&t.push(`--orm ${e.orm}`),e.dbSetup&&t.push(`--db-setup ${e.dbSetup}`)),e.api&&t.push(`--api ${e.api}`),t.push(e.auth?`--auth`:`--no-auth`),t.push(e.git?`--git`:`--no-git`),t.push(e.install?`--install`:`--no-install`),e.runtime&&t.push(`--runtime ${e.runtime}`),e.backend&&t.push(`--backend ${e.backend}`),e.frontend&&e.frontend.length>0&&t.push(`--frontend ${e.frontend.join(` `)}`),e.addons&&e.addons.length>0?t.push(`--addons ${e.addons.join(` `)}`):t.push(`--addons none`),e.examples&&e.examples.length>0?t.push(`--examples ${e.examples.join(` `)}`):t.push(`--examples none`),e.packageManager&&t.push(`--package-manager ${e.packageManager}`);let n=``,r=e.packageManager;r===`npm`?n=`npx create-better-t-stack@latest`:r===`pnpm`?n=`pnpm create better-t-stack@latest`:r===`bun`&&(n=`bun create better-t-stack@latest`);let i=e.relativePath?` ${e.relativePath}`:``;return`${n}${i} ${t.join(` `)}`}const Ot=()=>{let t=e.join(k,`package.json`),n=h.readJSONSync(t);return n.version??`1.0.0`},Z=`
|
|
160
160
|
██████╗ ███████╗████████╗████████╗███████╗██████╗
|
|
161
161
|
██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗
|
|
@@ -175,4 +175,4 @@ DATABASE_URL="your_connection_string"`)}async function Pe(n){let{projectName:r,p
|
|
|
175
175
|
╔══════════════════╗
|
|
176
176
|
║ Better T-Stack ║
|
|
177
177
|
╚══════════════════╝
|
|
178
|
-
`;console.log(T(Object.values(Q)).multiline(e))}else console.log(T(Object.values(Q)).multiline(Z))},$=()=>process.exit(0);process.on(`SIGINT`,$),process.on(`SIGTERM`,$);async function At(){let n=Date.now();try{let r=await _(v(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`,`mongoose`,`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`,`nuxt`,`native`,`svelte`,`solid`,`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`,`convex`]}).option(`runtime`,{type:`string`,describe:`Runtime`,choices:[`bun`,`node`,`none`]}).option(`api`,{type:`string`,describe:`API type`,choices:[`trpc`,`orpc`,`none`]}).completion().recommendCommands().version(Ot()).alias(`version`,`v`).help().alias(`help`,`h`).strict().wrap(null).parse(),s=r,l=s.projectDirectory;kt(),i(g.magenta(`Creating a new Better-T-Stack project`));let f,p,y,b,x=!1;if(s.yes&&l)f=l;else if(s.yes){let t=A.relativePath,n=1;for(;h.pathExistsSync(e.resolve(process.cwd(),t))&&h.readdirSync(e.resolve(process.cwd(),t)).length>0;)t=`${A.projectName}-${n}`,n++;f=t}else f=await Y(l);for(;;){let n=e.resolve(process.cwd(),f),r=h.pathExistsSync(n),i=r&&h.readdirSync(n).length>0;if(!i){p=f,x=!1;break}o.warn(`Directory "${g.yellow(f)}" already exists and is not empty.`);let s=await u({message:`What would you like to do?`,options:[{value:`overwrite`,label:`Overwrite`,hint:`Empty the directory and create the project`},{value:`merge`,label:`Merge`,hint:`Create project files inside, potentially overwriting conflicts`},{value:`rename`,label:`Choose a different name/path`,hint:`Keep the existing directory and create a new one`},{value:`cancel`,label:`Cancel`,hint:`Abort the process`}],initialValue:`rename`});if(a(s)&&(t(g.red(`Operation cancelled.`)),process.exit(0)),s===`overwrite`){p=f,x=!0;break}if(s===`merge`){p=f,x=!1,o.info(`Proceeding into existing directory "${g.yellow(f)}". Files may be overwritten.`);break}s===`rename`?(o.info(`Please choose a different project name or path.`),f=await Y(void 0)):s===`cancel`&&(t(g.red(`Operation cancelled.`)),process.exit(0))}if(p===`.`?(y=process.cwd(),b=e.basename(y)):(y=e.resolve(process.cwd(),p),b=e.basename(y)),x){let e=d();e.start(`Clearing directory "${y}"...`);try{await h.emptyDir(y),e.stop(`Directory "${y}" cleared.`)}catch(t){e.stop(g.red(`Failed to clear directory "${y}".`)),m.error(t),process.exit(1)}}else await h.ensureDir(y);let S=jt(s,b),{projectName:C,...w}=S;!s.yes&&Object.keys(w).length>0&&(o.info(g.yellow(`Using these pre-selected options:`)),o.message(X(w)),o.message(``));let T;s.yes?(T={...A,...S,projectName:b,projectDir:y,relativePath:p},T.backend===`convex`?(T.auth=!1,T.database=`none`,T.orm=`none`,T.api=`none`,T.runtime=`none`,T.dbSetup=`none`,T.examples=[`todo`]):T.database===`none`&&(T.orm=`none`,T.auth=!1,T.dbSetup=`none`),o.info(g.yellow(`Using default/flag options (config prompts skipped):`)),o.message(X(T)),o.message(``)):T=await Tt(S,b,y,p),await dt(T),o.success(g.blue(`You can reproduce this setup with the following command:\n${Dt(T)}`));let E=((Date.now()-n)/1e3).toFixed(2);c(g.magenta(`Project created successfully in ${g.bold(E)} seconds!`))}catch(e){e instanceof Error?(e.name===`YError`?t(g.red(`Invalid arguments: ${e.message}`)):(m.error(`An unexpected error occurred: ${e.message}`),e.message.includes(`is only supported with`)||m.error(e.stack)),process.exit(1)):(m.error(`An unexpected error occurred.`),console.error(e),process.exit(1))}}function jt(t,n){let r={},i=new Set(Object.keys(t).filter(e=>e!==`_`&&e!==`$0`));if(t.api&&(r.api=t.api,t.api===`none`&&(t.backend&&t.backend!==`convex`&&(m.fatal(`'--api none' is only supported with '--backend convex'. Please choose a different API setting or use '--backend convex'.`),process.exit(1)),r.backend=`convex`)),t.backend&&(r.backend=t.backend),i.has(`backend`)&&r.backend&&r.backend!==`convex`&&(i.has(`api`)&&t.api===`none`&&(m.fatal(`'--api none' is only supported with '--backend convex'. Please choose 'trpc', 'orpc', or remove the --api flag.`),process.exit(1)),i.has(`runtime`)&&t.runtime===`none`&&(m.fatal(`'--runtime none' is only supported with '--backend convex'. Please choose 'bun', 'node', or remove the --runtime flag.`),process.exit(1))),t.database&&(r.database=t.database),t.orm&&(r.orm=t.orm),t.auth!==void 0&&(r.auth=t.auth),t.git!==void 0&&(r.git=t.git),t.install!==void 0&&(r.install=t.install),t.runtime&&(r.runtime=t.runtime),t.dbSetup&&(r.dbSetup=t.dbSetup),t.packageManager&&(r.packageManager=t.packageManager),n?r.projectName=n:t.projectDirectory&&(r.projectName=e.basename(e.resolve(process.cwd(),t.projectDirectory))),t.frontend&&t.frontend.length>0)if(t.frontend.includes(`none`))t.frontend.length>1&&(m.fatal(`Cannot combine 'none' with other frontend options.`),process.exit(1)),r.frontend=[];else{let e=t.frontend.filter(e=>e!==`none`),n=e.filter(e=>e===`tanstack-router`||e===`react-router`||e===`tanstack-start`||e===`next`||e===`nuxt`||e===`svelte`||e===`solid`);n.length>1&&(m.fatal(`Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next, nuxt, svelte, solid`),process.exit(1)),r.frontend=e}if(t.addons&&t.addons.length>0&&(t.addons.includes(`none`)?(t.addons.length>1&&(m.fatal(`Cannot combine 'none' with other addons.`),process.exit(1)),r.addons=[]):r.addons=t.addons.filter(e=>e!==`none`)),t.examples&&t.examples.length>0&&(t.examples.includes(`none`)?(t.examples.length>1&&(m.fatal(`Cannot combine 'none' with other examples.`),process.exit(1)),r.examples=[]):(r.examples=t.examples.filter(e=>e!==`none`),t.examples.includes(`none`)&&r.backend!==`convex`&&(r.examples=[]))),r.backend===`convex`){let e=[];if(i.has(`auth`)&&t.auth===!0&&e.push(`--auth`),i.has(`database`)&&t.database!==`none`&&e.push(`--database ${t.database}`),i.has(`orm`)&&t.orm!==`none`&&e.push(`--orm ${t.orm}`),i.has(`api`)&&t.api!==`none`&&e.push(`--api ${t.api}`),i.has(`runtime`)&&t.runtime!==`none`&&e.push(`--runtime ${t.runtime}`),i.has(`dbSetup`)&&t.dbSetup!==`none`&&e.push(`--db-setup ${t.dbSetup}`),e.length>0&&(m.fatal(`The following flags are incompatible with '--backend convex': ${e.join(`, `)}. Please remove them.`),process.exit(1)),i.has(`frontend`)&&t.frontend){let e=t.frontend.filter(e=>e===`nuxt`||e===`solid`);e.length>0&&(m.fatal(`The following frontends are not compatible with '--backend convex': ${e.join(`, `)}. Please choose a different frontend or backend.`),process.exit(1))}r.auth=!1,r.database=`none`,r.orm=`none`,r.api=`none`,r.runtime=`none`,r.dbSetup=`none`,r.examples=[`todo`]}else{let e=r.database??(t.yes?A.database:void 0),n=r.orm??(t.yes?A.orm:void 0),a=r.auth??(t.yes?A.auth:void 0),o=r.dbSetup??(t.yes?A.dbSetup:void 0),s=r.examples??(t.yes?A.examples:void 0),c=r.frontend??(t.yes?A.frontend:void 0),l=r.api??(t.yes?A.api:void 0),u=r.backend??(t.yes?A.backend:void 0);if(e===`none`&&(i.has(`orm`)&&t.orm!==`none`&&(m.fatal(`Cannot use ORM '--orm ${t.orm}' when database is 'none'.`),process.exit(1)),r.orm=`none`,i.has(`auth`)&&t.auth===!0&&(m.fatal(`Authentication requires a database. Cannot use --auth when database is 'none'.`),process.exit(1)),r.auth=!1,i.has(`dbSetup`)&&t.dbSetup!==`none`&&(m.fatal(`Database setup '--db-setup ${t.dbSetup}' requires a database. Cannot use when database is 'none'.`),process.exit(1)),r.dbSetup=`none`),r.orm===`mongoose`&&!i.has(`database`)&&(e&&e!==`mongodb`&&(m.fatal(`Mongoose ORM requires MongoDB. Cannot use --orm mongoose with --database ${e}.`),process.exit(1)),r.database=`mongodb`),e===`mongodb`&&n===`drizzle`&&(m.fatal(`Drizzle ORM is not compatible with MongoDB. Please use --orm prisma or --orm mongoose.`),process.exit(1)),n===`mongoose`&&e&&e!==`mongodb`&&(m.fatal(`Mongoose ORM requires MongoDB. Cannot use --orm mongoose with --database ${e}.`),process.exit(1)),r.dbSetup&&r.dbSetup!==`none`){let t=r.dbSetup;(!e||e===`none`)&&(m.fatal(`Database setup '--db-setup ${t}' requires a database. Cannot use when database is 'none'.`),process.exit(1)),t===`turso`?(e&&e!==`sqlite`&&(m.fatal(`Turso setup requires SQLite. Cannot use --db-setup turso with --database ${e}`),process.exit(1)),n!==`drizzle`&&(m.fatal(`Turso setup requires Drizzle ORM. Cannot use --db-setup turso with --orm ${n??`none`}.`),process.exit(1))):t===`prisma-postgres`?(e!==`postgres`&&(m.fatal(`Prisma PostgreSQL setup requires PostgreSQL. Cannot use --db-setup prisma-postgres with --database ${e}.`),process.exit(1)),n!==`prisma`&&(m.fatal(`Prisma PostgreSQL setup requires Prisma ORM. Cannot use --db-setup prisma-postgres with --orm ${n}.`),process.exit(1))):t===`mongodb-atlas`?(e!==`mongodb`&&(m.fatal(`MongoDB Atlas setup requires MongoDB. Cannot use --db-setup mongodb-atlas with --database ${e}.`),process.exit(1)),n!==`prisma`&&n!==`mongoose`&&(m.fatal(`MongoDB Atlas setup requires Prisma or Mongoose ORM. Cannot use --db-setup mongodb-atlas with --orm ${n}.`),process.exit(1))):t===`neon`&&e!==`postgres`&&(m.fatal(`Neon PostgreSQL setup requires PostgreSQL. Cannot use --db-setup neon with --database ${e}.`),process.exit(1))}let d=c?.includes(`nuxt`),f=c?.includes(`svelte`),p=c?.includes(`solid`);if((d||f||p)&&l===`trpc`&&(m.fatal(`tRPC API is not supported with '${d?`nuxt`:f?`svelte`:`solid`}' frontend. Please use --api orpc or remove '${d?`nuxt`:f?`svelte`:`solid`}' from --frontend.`),process.exit(1)),(d||f||p)&&l!==`orpc`&&(!t.api||t.yes&&t.api===`trpc`)&&r.api!==`none`&&(r.api=`orpc`),r.addons&&r.addons.length>0){let e=[`pwa`,`tauri`],t=r.addons.some(t=>e.includes(t)),n=c?.some(e=>{let t=e===`tanstack-router`||e===`react-router`||e===`solid`,n=e===`tanstack-router`||e===`react-router`||e===`nuxt`||e===`svelte`||e===`solid`;return r.addons?.includes(`pwa`)&&r.addons?.includes(`tauri`)?t&&n:r.addons?.includes(`pwa`)?t:r.addons?.includes(`tauri`)?n:!0});if(t&&!n){let e=`Selected frontend is not compatible.`;r.addons.includes(`pwa`)&&(e=`PWA requires tanstack-router, react-router, or solid.`),r.addons.includes(`tauri`)&&(e=`Tauri requires tanstack-router, react-router, nuxt, svelte, or solid.`),m.fatal(`Incompatible addon/frontend combination: ${e}`),process.exit(1)}r.addons.includes(`husky`)&&!r.addons.includes(`biome`)&&m.warn(`Husky addon is recommended to be used with Biome for lint-staged configuration.`),r.addons=[...new Set(r.addons)]}let h=c&&c.length===1&&c[0]===`native`;h&&r.examples&&r.examples.length>0&&!r.examples.includes(`none`)&&(m.fatal(`Examples are not supported when only the 'native' frontend is selected.`),process.exit(1)),r.examples&&r.examples.length>0&&!r.examples.includes(`none`)&&(r.examples.includes(`todo`)&&u!==`convex`&&e===`none`&&(m.fatal(`The 'todo' example requires a database (unless using Convex). Cannot use --examples todo when database is 'none'.`),process.exit(1)),r.examples.includes(`ai`)&&u===`elysia`&&(m.fatal(`The 'ai' example is not compatible with the Elysia backend.`),process.exit(1)),r.examples.includes(`ai`)&&p&&(m.fatal(`The 'ai' example is not compatible with the Solid frontend.`),process.exit(1)))}return r}At().catch(e=>{m.error(`Aborting installation due to unexpected error...`),e instanceof Error?!e.message.includes(`is only supported with`)&&!e.message.includes(`incompatible with`)&&!e.message.includes(`requires`)&&!e.message.includes(`Cannot use`)&&!e.message.includes(`Cannot select multiple`)&&!e.message.includes(`Cannot combine`)&&!e.message.includes(`not supported`)&&(m.error(e.message),m.error(e.stack)):console.error(e),process.exit(1)});
|
|
178
|
+
`;console.log(T(Object.values(Q)).multiline(e))}else console.log(T(Object.values(Q)).multiline(Z))},$=()=>process.exit(0);process.on(`SIGINT`,$),process.on(`SIGTERM`,$);async function At(){let n=Date.now();try{let r=await _(v(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`,`mongoose`,`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`,`nuxt`,`native-nativewind`,`native-unistyles`,`svelte`,`solid`,`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`,`convex`,`none`]}).option(`runtime`,{type:`string`,describe:`Runtime`,choices:[`bun`,`node`,`none`]}).option(`api`,{type:`string`,describe:`API type`,choices:[`trpc`,`orpc`,`none`]}).completion().recommendCommands().version(Ot()).alias(`version`,`v`).help().alias(`help`,`h`).strict().wrap(null).parse(),s=r,l=s.projectDirectory;kt(),i(g.magenta(`Creating a new Better-T-Stack project`));let f,p,y,b,x=!1;if(s.yes&&l)f=l;else if(s.yes){let t=A.relativePath,n=1;for(;h.pathExistsSync(e.resolve(process.cwd(),t))&&h.readdirSync(e.resolve(process.cwd(),t)).length>0;)t=`${A.projectName}-${n}`,n++;f=t}else f=await Y(l);for(;;){let n=e.resolve(process.cwd(),f),r=h.pathExistsSync(n),i=r&&h.readdirSync(n).length>0;if(!i){p=f,x=!1;break}o.warn(`Directory "${g.yellow(f)}" already exists and is not empty.`);let s=await u({message:`What would you like to do?`,options:[{value:`overwrite`,label:`Overwrite`,hint:`Empty the directory and create the project`},{value:`merge`,label:`Merge`,hint:`Create project files inside, potentially overwriting conflicts`},{value:`rename`,label:`Choose a different name/path`,hint:`Keep the existing directory and create a new one`},{value:`cancel`,label:`Cancel`,hint:`Abort the process`}],initialValue:`rename`});if(a(s)&&(t(g.red(`Operation cancelled.`)),process.exit(0)),s===`overwrite`){p=f,x=!0;break}if(s===`merge`){p=f,x=!1,o.info(`Proceeding into existing directory "${g.yellow(f)}". Files may be overwritten.`);break}s===`rename`?(o.info(`Please choose a different project name or path.`),f=await Y(void 0)):s===`cancel`&&(t(g.red(`Operation cancelled.`)),process.exit(0))}if(p===`.`?(y=process.cwd(),b=e.basename(y)):(y=e.resolve(process.cwd(),p),b=e.basename(y)),x){let e=d();e.start(`Clearing directory "${y}"...`);try{await h.emptyDir(y),e.stop(`Directory "${y}" cleared.`)}catch(t){e.stop(g.red(`Failed to clear directory "${y}".`)),m.error(t),process.exit(1)}}else await h.ensureDir(y);let S=jt(s,b),{projectName:C,...w}=S;!s.yes&&Object.keys(w).length>0&&(o.info(g.yellow(`Using these pre-selected options:`)),o.message(X(w)),o.message(``));let T;s.yes?(T={...A,...S,projectName:b,projectDir:y,relativePath:p},T.backend===`convex`?(T.auth=!1,T.database=`none`,T.orm=`none`,T.api=`none`,T.runtime=`none`,T.dbSetup=`none`,T.examples=[`todo`],o.info(`Due to '--backend convex' flag, the following options have been automatically set: auth=false, database=none, orm=none, api=none, runtime=none, dbSetup=none, examples=todo`)):T.backend===`none`?(T.auth=!1,T.database=`none`,T.orm=`none`,T.api=`none`,T.runtime=`none`,T.dbSetup=`none`,T.examples=[],o.info(`Due to '--backend none', the following options have been automatically set: --auth=false, --database=none, --orm=none, --api=none, --runtime=none, --db-setup=none, --examples=none`)):T.database===`none`&&(T.orm=`none`,o.info(`Due to '--database none', '--orm' has been automatically set to 'none'.`),T.auth=!1,o.info(`Due to '--database none', '--auth' has been automatically set to 'false'.`),T.dbSetup=`none`,o.info(`Due to '--database none', '--db-setup' has been automatically set to 'none'.`)),o.info(g.yellow(`Using default/flag options (config prompts skipped):`)),o.message(X(T)),o.message(``)):T=await Tt(S,b,y,p),await dt(T),o.success(g.blue(`You can reproduce this setup with the following command:\n${Dt(T)}`));let E=((Date.now()-n)/1e3).toFixed(2);c(g.magenta(`Project created successfully in ${g.bold(E)} seconds!`))}catch(e){e instanceof Error?(e.name===`YError`?t(g.red(`Invalid arguments: ${e.message}`)):(m.error(`An unexpected error occurred: ${e.message}`),e.message.includes(`is only supported with`)||m.error(e.stack)),process.exit(1)):(m.error(`An unexpected error occurred.`),console.error(e),process.exit(1))}}function jt(t,n){let r={},i=new Set(Object.keys(t).filter(e=>e!==`_`&&e!==`$0`));if(t.api&&(r.api=t.api,t.api===`none`&&t.backend&&t.backend!==`convex`&&t.backend!==`none`&&(m.fatal(`'--api none' is only supported with '--backend convex' or '--backend none'. Please choose a different API setting or use '--backend convex' or '--backend none'.`),process.exit(1))),t.backend&&(r.backend=t.backend),i.has(`backend`)&&r.backend&&r.backend!==`convex`&&r.backend!==`none`&&(i.has(`api`)&&t.api===`none`&&(m.fatal(`'--api none' is only supported with '--backend convex' or '--backend none'. Please choose 'trpc', 'orpc', or remove the --api flag.`),process.exit(1)),i.has(`runtime`)&&t.runtime===`none`&&(m.fatal(`'--runtime none' is only supported with '--backend convex' or '--backend none'. Please choose 'bun', 'node', or remove the --runtime flag.`),process.exit(1))),t.database&&(r.database=t.database),t.orm&&(r.orm=t.orm),t.auth!==void 0&&(r.auth=t.auth),t.git!==void 0&&(r.git=t.git),t.install!==void 0&&(r.install=t.install),t.runtime&&(r.runtime=t.runtime),t.dbSetup&&(r.dbSetup=t.dbSetup),t.packageManager&&(r.packageManager=t.packageManager),n?r.projectName=n:t.projectDirectory&&(r.projectName=e.basename(e.resolve(process.cwd(),t.projectDirectory))),t.frontend&&t.frontend.length>0)if(t.frontend.includes(`none`))t.frontend.length>1&&(m.fatal(`Cannot combine 'none' with other frontend options.`),process.exit(1)),r.frontend=[];else{let e=t.frontend.filter(e=>e!==`none`),n=e.filter(e=>e===`tanstack-router`||e===`react-router`||e===`tanstack-start`||e===`next`||e===`nuxt`||e===`svelte`||e===`solid`),i=e.filter(e=>e===`native-nativewind`||e===`native-unistyles`);n.length>1&&(m.fatal(`Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next, nuxt, svelte, solid`),process.exit(1)),i.length>1&&(m.fatal(`Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles`),process.exit(1)),r.frontend=e}if(t.addons&&t.addons.length>0&&(t.addons.includes(`none`)?(t.addons.length>1&&(m.fatal(`Cannot combine 'none' with other addons.`),process.exit(1)),r.addons=[]):r.addons=t.addons.filter(e=>e!==`none`)),t.examples&&t.examples.length>0&&(t.examples.includes(`none`)?(t.examples.length>1&&(m.fatal(`Cannot combine 'none' with other examples.`),process.exit(1)),r.examples=[]):(r.examples=t.examples.filter(e=>e!==`none`),t.examples.includes(`none`)&&r.backend!==`convex`&&(r.examples=[]))),r.backend===`convex`){let e=[];if(i.has(`auth`)&&t.auth===!0&&e.push(`--auth`),i.has(`database`)&&t.database!==`none`&&e.push(`--database ${t.database}`),i.has(`orm`)&&t.orm!==`none`&&e.push(`--orm ${t.orm}`),i.has(`api`)&&t.api!==`none`&&e.push(`--api ${t.api}`),i.has(`runtime`)&&t.runtime!==`none`&&e.push(`--runtime ${t.runtime}`),i.has(`dbSetup`)&&t.dbSetup!==`none`&&e.push(`--db-setup ${t.dbSetup}`),e.length>0&&(m.fatal(`The following flags are incompatible with '--backend convex': ${e.join(`, `)}. Please remove them.`),process.exit(1)),i.has(`frontend`)&&t.frontend){let e=t.frontend.filter(e=>e===`nuxt`||e===`solid`);e.length>0&&(m.fatal(`The following frontends are not compatible with '--backend convex': ${e.join(`, `)}. Please choose a different frontend or backend.`),process.exit(1))}r.auth=!1,r.database=`none`,r.orm=`none`,r.api=`none`,r.runtime=`none`,r.dbSetup=`none`,r.examples=[`todo`]}else if(r.backend===`none`){let e=[];i.has(`auth`)&&t.auth===!0&&e.push(`--auth`),i.has(`database`)&&t.database!==`none`&&e.push(`--database ${t.database}`),i.has(`orm`)&&t.orm!==`none`&&e.push(`--orm ${t.orm}`),i.has(`api`)&&t.api!==`none`&&e.push(`--api ${t.api}`),i.has(`runtime`)&&t.runtime!==`none`&&e.push(`--runtime ${t.runtime}`),i.has(`dbSetup`)&&t.dbSetup!==`none`&&e.push(`--db-setup ${t.dbSetup}`),e.length>0&&(m.fatal(`The following flags are incompatible with '--backend none': ${e.join(`, `)}. Please remove them.`),process.exit(1)),r.auth=!1,r.database=`none`,r.orm=`none`,r.api=`none`,r.runtime=`none`,r.dbSetup=`none`,t.examples&&!t.examples.includes(`none`)&&t.examples.length>0&&(m.fatal(`Cannot select examples when backend is 'none'. Please remove the --examples flag or set --examples none.`),process.exit(1)),r.examples=[],o.info(`Due to '--backend none', the following options have been automatically set: --auth=false, --database=none, --orm=none, --api=none, --runtime=none, --db-setup=none, --examples=none`)}else{let e=r.database??(t.yes?A.database:void 0),n=r.orm??(t.yes?A.orm:void 0),a=r.auth??(t.yes?A.auth:void 0),s=r.dbSetup??(t.yes?A.dbSetup:void 0),c=r.examples??(t.yes?A.examples:void 0),l=r.frontend??(t.yes?A.frontend:void 0),u=r.api??(t.yes?A.api:void 0),d=r.backend??(t.yes?A.backend:void 0);if(e===`none`&&(i.has(`orm`)&&t.orm!==`none`&&(m.fatal(`Cannot use ORM '--orm ${t.orm}' when database is 'none'.`),process.exit(1)),r.orm=`none`,o.info(`Due to '--database none', '--orm' has been automatically set to 'none'.`),i.has(`auth`)&&t.auth===!0&&(m.fatal(`Authentication requires a database. Cannot use --auth when database is 'none'.`),process.exit(1)),r.auth=!1,o.info(`Due to '--database none', '--auth' has been automatically set to 'false'.`),i.has(`dbSetup`)&&t.dbSetup!==`none`&&(m.fatal(`Database setup '--db-setup ${t.dbSetup}' requires a database. Cannot use when database is 'none'.`),process.exit(1)),r.dbSetup=`none`,o.info(`Due to '--database none', '--db-setup' has been automatically set to 'none'.`)),r.orm===`mongoose`&&!i.has(`database`)&&(e&&e!==`mongodb`&&(m.fatal(`Mongoose ORM requires MongoDB. Cannot use --orm mongoose with --database ${e}.`),process.exit(1)),r.database=`mongodb`),e===`mongodb`&&n===`drizzle`&&(m.fatal(`Drizzle ORM is not compatible with MongoDB. Please use --orm prisma or --orm mongoose.`),process.exit(1)),n===`mongoose`&&e&&e!==`mongodb`&&(m.fatal(`Mongoose ORM requires MongoDB. Cannot use --orm mongoose with --database ${e}.`),process.exit(1)),r.dbSetup&&r.dbSetup!==`none`){let t=r.dbSetup;(!e||e===`none`)&&(m.fatal(`Database setup '--db-setup ${t}' requires a database. Cannot use when database is 'none'.`),process.exit(1)),t===`turso`?(e&&e!==`sqlite`&&(m.fatal(`Turso setup requires SQLite. Cannot use --db-setup turso with --database ${e}`),process.exit(1)),n!==`drizzle`&&(m.fatal(`Turso setup requires Drizzle ORM. Cannot use --db-setup turso with --orm ${n??`none`}.`),process.exit(1))):t===`prisma-postgres`?(e!==`postgres`&&(m.fatal(`Prisma PostgreSQL setup requires PostgreSQL. Cannot use --db-setup prisma-postgres with --database ${e}.`),process.exit(1)),n!==`prisma`&&(m.fatal(`Prisma PostgreSQL setup requires Prisma ORM. Cannot use --db-setup prisma-postgres with --orm ${n}.`),process.exit(1))):t===`mongodb-atlas`?(e!==`mongodb`&&(m.fatal(`MongoDB Atlas setup requires MongoDB. Cannot use --db-setup mongodb-atlas with --database ${e}.`),process.exit(1)),n!==`prisma`&&n!==`mongoose`&&(m.fatal(`MongoDB Atlas setup requires Prisma or Mongoose ORM. Cannot use --db-setup mongodb-atlas with --orm ${n}.`),process.exit(1))):t===`neon`&&e!==`postgres`&&(m.fatal(`Neon PostgreSQL setup requires PostgreSQL. Cannot use --db-setup neon with --database ${e}.`),process.exit(1))}let f=l?.includes(`nuxt`),p=l?.includes(`svelte`),h=l?.includes(`solid`);if((f||p||h)&&u===`trpc`&&(m.fatal(`tRPC API is not supported with '${f?`nuxt`:p?`svelte`:`solid`}' frontend. Please use --api orpc or remove '${f?`nuxt`:p?`svelte`:`solid`}' from --frontend.`),process.exit(1)),(f||p||h)&&u!==`orpc`&&(!t.api||t.yes&&t.api===`trpc`)&&r.api!==`none`&&(r.api=`orpc`,o.info(`Due to frontend selection, API has been set to 'orpc'. tRPC is not compatible with Nuxt, Svelte, or Solid Framework`)),r.addons&&r.addons.length>0){let e=[`pwa`,`tauri`],t=r.addons.some(t=>e.includes(t)),n=l?.some(e=>{let t=e===`tanstack-router`||e===`react-router`||e===`solid`,n=e===`tanstack-router`||e===`react-router`||e===`nuxt`||e===`svelte`||e===`solid`;return r.addons?.includes(`pwa`)&&r.addons?.includes(`tauri`)?t&&n:r.addons?.includes(`pwa`)?t:r.addons?.includes(`tauri`)?n:!0});if(t&&!n){let e=`Selected frontend is not compatible.`;r.addons.includes(`pwa`)&&(e=`PWA requires tanstack-router, react-router, or solid.`),r.addons.includes(`tauri`)&&(e=`Tauri requires tanstack-router, react-router, nuxt, svelte, or solid.`),m.fatal(`Incompatible addon/frontend combination: ${e}`),process.exit(1)}r.addons.includes(`husky`)&&!r.addons.includes(`biome`)&&m.warn(`Husky addon is recommended to be used with Biome for lint-staged configuration.`),r.addons=[...new Set(r.addons)]}let g=l&&l.length===1&&(l[0]===`native-nativewind`||l[0]===`native-unistyles`);g&&r.examples&&r.examples.length>0&&!r.examples.includes(`none`)&&(m.fatal(`Examples are not supported when only a native frontend (NativeWind or Unistyles) is selected.`),process.exit(1)),r.examples&&r.examples.length>0&&!r.examples.includes(`none`)&&(r.examples.includes(`todo`)&&d!==`convex`&&d!==`none`&&e===`none`&&(m.fatal(`The 'todo' example requires a database if a backend (other than Convex) is present. Cannot use --examples todo when database is 'none' and a backend is selected.`),process.exit(1)),r.examples.includes(`ai`)&&d===`elysia`&&(m.fatal(`The 'ai' example is not compatible with the Elysia backend.`),process.exit(1)),r.examples.includes(`ai`)&&h&&(m.fatal(`The 'ai' example is not compatible with the Solid frontend.`),process.exit(1)))}return r}At().catch(e=>{m.error(`Aborting installation due to unexpected error...`),e instanceof Error?!e.message.includes(`is only supported with`)&&!e.message.includes(`incompatible with`)&&!e.message.includes(`requires`)&&!e.message.includes(`Cannot use`)&&!e.message.includes(`Cannot select multiple`)&&!e.message.includes(`Cannot combine`)&&!e.message.includes(`not supported`)&&(m.error(e.message),m.error(e.stack)):console.error(e),process.exit(1)});
|
package/package.json
CHANGED
|
@@ -10,7 +10,12 @@ const requireAuth = o.middleware(async ({ context, next }) => {
|
|
|
10
10
|
if (!context.session?.user) {
|
|
11
11
|
throw new ORPCError("UNAUTHORIZED");
|
|
12
12
|
}
|
|
13
|
-
return next({
|
|
13
|
+
return next({
|
|
14
|
+
context: {
|
|
15
|
+
...context,
|
|
16
|
+
session: context.session,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
14
19
|
});
|
|
15
20
|
|
|
16
21
|
export const protectedProcedure = publicProcedure.use(requireAuth);
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { authClient } from "@/lib/auth-client";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { ScrollView, Text, TouchableOpacity, View } from "react-native";
|
|
4
|
+
import { StyleSheet } from "react-native-unistyles";
|
|
5
|
+
|
|
6
|
+
import { Container } from "@/components/container";
|
|
7
|
+
import { SignIn } from "@/components/sign-in";
|
|
8
|
+
import { SignUp } from "@/components/sign-up";
|
|
9
|
+
{{#if (eq api "orpc")}}
|
|
10
|
+
import { queryClient, orpc } from "@/utils/orpc";
|
|
11
|
+
{{/if}}
|
|
12
|
+
{{#if (eq api "trpc")}}
|
|
13
|
+
import { queryClient, trpc } from "@/utils/trpc";
|
|
14
|
+
{{/if}}
|
|
15
|
+
|
|
16
|
+
export default function Home() {
|
|
17
|
+
{{#if (eq api "orpc")}}
|
|
18
|
+
const healthCheck = useQuery(orpc.healthCheck.queryOptions());
|
|
19
|
+
const privateData = useQuery(orpc.privateData.queryOptions());
|
|
20
|
+
{{/if}}
|
|
21
|
+
{{#if (eq api "trpc")}}
|
|
22
|
+
const healthCheck = useQuery(trpc.healthCheck.queryOptions());
|
|
23
|
+
const privateData = useQuery(trpc.privateData.queryOptions());
|
|
24
|
+
{{/if}}
|
|
25
|
+
const { data: session } = authClient.useSession();
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Container>
|
|
29
|
+
<ScrollView>
|
|
30
|
+
<View style={styles.pageContainer}>
|
|
31
|
+
<Text style={styles.headerTitle}>BETTER T STACK</Text>
|
|
32
|
+
{session?.user ? (
|
|
33
|
+
<View style={styles.sessionInfoCard}>
|
|
34
|
+
<View style={styles.sessionUserRow}>
|
|
35
|
+
<Text style={styles.welcomeText}>
|
|
36
|
+
Welcome,{" "}
|
|
37
|
+
<Text style={styles.userNameText}>{session.user.name}</Text>
|
|
38
|
+
</Text>
|
|
39
|
+
</View>
|
|
40
|
+
<Text style={styles.emailText}>{session.user.email}</Text>
|
|
41
|
+
|
|
42
|
+
<TouchableOpacity
|
|
43
|
+
style={styles.signOutButton}
|
|
44
|
+
onPress={() => {
|
|
45
|
+
authClient.signOut();
|
|
46
|
+
queryClient.invalidateQueries();
|
|
47
|
+
}}
|
|
48
|
+
>
|
|
49
|
+
<Text style={styles.signOutButtonText}>Sign Out</Text>
|
|
50
|
+
</TouchableOpacity>
|
|
51
|
+
</View>
|
|
52
|
+
) : null}
|
|
53
|
+
<View style={styles.apiStatusCard}>
|
|
54
|
+
<Text style={styles.cardTitle}>API Status</Text>
|
|
55
|
+
<View style={styles.apiStatusRow}>
|
|
56
|
+
<View
|
|
57
|
+
style={[
|
|
58
|
+
styles.statusIndicatorDot,
|
|
59
|
+
healthCheck.data
|
|
60
|
+
? styles.statusIndicatorGreen
|
|
61
|
+
: styles.statusIndicatorRed,
|
|
62
|
+
]}
|
|
63
|
+
/>
|
|
64
|
+
<Text style={styles.mutedText}>
|
|
65
|
+
{healthCheck.isLoading
|
|
66
|
+
? "Checking..."
|
|
67
|
+
: healthCheck.data
|
|
68
|
+
? "Connected to API"
|
|
69
|
+
: "API Disconnected"}
|
|
70
|
+
</Text>
|
|
71
|
+
</View>
|
|
72
|
+
</View>
|
|
73
|
+
<View style={styles.privateDataCard}>
|
|
74
|
+
<Text style={styles.cardTitle}>Private Data</Text>
|
|
75
|
+
{privateData && (
|
|
76
|
+
<View>
|
|
77
|
+
<Text style={styles.mutedText}>
|
|
78
|
+
{privateData.data?.message}
|
|
79
|
+
</Text>
|
|
80
|
+
</View>
|
|
81
|
+
)}
|
|
82
|
+
</View>
|
|
83
|
+
{!session?.user && (
|
|
84
|
+
<>
|
|
85
|
+
<SignIn />
|
|
86
|
+
<SignUp />
|
|
87
|
+
</>
|
|
88
|
+
)}
|
|
89
|
+
</View>
|
|
90
|
+
</ScrollView>
|
|
91
|
+
</Container>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const styles = StyleSheet.create((theme) => ({
|
|
96
|
+
pageContainer: {
|
|
97
|
+
paddingHorizontal: 8,
|
|
98
|
+
},
|
|
99
|
+
headerTitle: {
|
|
100
|
+
color: theme?.colors?.typography,
|
|
101
|
+
fontSize: 30,
|
|
102
|
+
fontWeight: "bold",
|
|
103
|
+
marginBottom: 16,
|
|
104
|
+
},
|
|
105
|
+
sessionInfoCard: {
|
|
106
|
+
marginBottom: 24,
|
|
107
|
+
padding: 16,
|
|
108
|
+
borderRadius: 8,
|
|
109
|
+
borderWidth: 1,
|
|
110
|
+
borderColor: theme?.colors?.border,
|
|
111
|
+
},
|
|
112
|
+
sessionUserRow: {
|
|
113
|
+
flexDirection: "row",
|
|
114
|
+
justifyContent: "space-between",
|
|
115
|
+
alignItems: "center",
|
|
116
|
+
marginBottom: 8,
|
|
117
|
+
},
|
|
118
|
+
welcomeText: {
|
|
119
|
+
color: theme?.colors?.typography,
|
|
120
|
+
fontSize: 16,
|
|
121
|
+
},
|
|
122
|
+
userNameText: {
|
|
123
|
+
fontWeight: "500",
|
|
124
|
+
color: theme?.colors?.typography,
|
|
125
|
+
},
|
|
126
|
+
emailText: {
|
|
127
|
+
color: theme?.colors?.typography,
|
|
128
|
+
fontSize: 14,
|
|
129
|
+
marginBottom: 16,
|
|
130
|
+
},
|
|
131
|
+
signOutButton: {
|
|
132
|
+
backgroundColor: theme?.colors?.destructive,
|
|
133
|
+
paddingVertical: 8,
|
|
134
|
+
paddingHorizontal: 16,
|
|
135
|
+
borderRadius: 6,
|
|
136
|
+
alignSelf: "flex-start",
|
|
137
|
+
},
|
|
138
|
+
signOutButtonText: {
|
|
139
|
+
fontWeight: "500",
|
|
140
|
+
},
|
|
141
|
+
apiStatusCard: {
|
|
142
|
+
marginBottom: 24,
|
|
143
|
+
borderRadius: 8,
|
|
144
|
+
borderWidth: 1,
|
|
145
|
+
borderColor: theme?.colors?.border,
|
|
146
|
+
padding: 16,
|
|
147
|
+
},
|
|
148
|
+
cardTitle: {
|
|
149
|
+
marginBottom: 12,
|
|
150
|
+
fontWeight: "500",
|
|
151
|
+
color: theme?.colors?.typography,
|
|
152
|
+
},
|
|
153
|
+
apiStatusRow: {
|
|
154
|
+
flexDirection: "row",
|
|
155
|
+
alignItems: "center",
|
|
156
|
+
gap: 8,
|
|
157
|
+
},
|
|
158
|
+
statusIndicatorDot: {
|
|
159
|
+
height: 12,
|
|
160
|
+
width: 12,
|
|
161
|
+
borderRadius: 9999,
|
|
162
|
+
},
|
|
163
|
+
statusIndicatorGreen: {
|
|
164
|
+
backgroundColor: theme.colors.success,
|
|
165
|
+
},
|
|
166
|
+
statusIndicatorRed: {
|
|
167
|
+
backgroundColor: theme.colors.destructive,
|
|
168
|
+
},
|
|
169
|
+
mutedText: {
|
|
170
|
+
color: theme?.colors?.typography,
|
|
171
|
+
},
|
|
172
|
+
privateDataCard: {
|
|
173
|
+
marginBottom: 24,
|
|
174
|
+
borderRadius: 8,
|
|
175
|
+
borderWidth: 1,
|
|
176
|
+
borderColor: theme?.colors?.border,
|
|
177
|
+
padding: 16,
|
|
178
|
+
},
|
|
179
|
+
}));
|