kofi-stack-template-generator 2.0.23 → 2.1.3
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/.turbo/turbo-build.log +5 -5
- package/.turbo/turbo-typecheck.log +4 -0
- package/dist/index.js +38 -9
- package/package.json +2 -2
- package/src/templates.generated.ts +9 -9
- package/templates/convex/convex/auth.config.ts.hbs +6 -0
- package/templates/convex/convex/auth.ts.hbs +22 -9
- package/templates/convex/convex/http.ts.hbs +2 -2
- package/templates/convex/convex/users.ts.hbs +3 -7
- package/templates/packages/ui/package.json.hbs +4 -1
- package/templates/web/components.json.hbs +4 -3
- package/templates/web/package.json.hbs +6 -2
- package/templates/web/tsconfig.json.hbs +3 -1
- package/templates/packages/ui/components.json.hbs +0 -20
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
> kofi-stack-template-generator@2.
|
|
2
|
+
> kofi-stack-template-generator@2.1.3 build /Users/theodenanyoh/Documents/Krumalabs/create-kofi-stack-v2/packages/template-generator
|
|
3
3
|
> pnpm run prebuild && tsup src/index.ts --format esm --dts
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> kofi-stack-template-generator@2.
|
|
6
|
+
> kofi-stack-template-generator@2.1.3 prebuild /Users/theodenanyoh/Documents/Krumalabs/create-kofi-stack-v2/packages/template-generator
|
|
7
7
|
> node scripts/generate-templates.js
|
|
8
8
|
|
|
9
9
|
Generating templates.generated.ts...
|
|
@@ -13,8 +13,8 @@ CLI Using tsconfig: tsconfig.json
|
|
|
13
13
|
CLI tsup v8.5.1
|
|
14
14
|
CLI Target: es2022
|
|
15
15
|
ESM Build start
|
|
16
|
-
ESM dist/index.js
|
|
17
|
-
ESM ⚡️ Build success in
|
|
16
|
+
ESM dist/index.js 100.05 KB
|
|
17
|
+
ESM ⚡️ Build success in 29ms
|
|
18
18
|
DTS Build start
|
|
19
|
-
DTS ⚡️ Build success in
|
|
19
|
+
DTS ⚡️ Build success in 443ms
|
|
20
20
|
DTS dist/index.d.ts 2.96 KB
|
package/dist/index.js
CHANGED
|
@@ -244,11 +244,12 @@ var EMBEDDED_TEMPLATES = {
|
|
|
244
244
|
"base/_gitignore.hbs": "# Dependencies\nnode_modules\n.pnpm-store\n\n# Build outputs\n.next\ndist\n.turbo\nout\n\n# Testing\ncoverage\nplaywright-report\ntest-results\n\n# Environment\n.env\n.env.local\n.env.*.local\n\n# IDE\n.idea\n.vscode\n*.swp\n*.swo\n.DS_Store\n\n# Convex\n.convex\n\n# Vercel\n.vercel\n\n# Debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.pnpm-debug.log*\n\n# TypeScript\n*.tsbuildinfo\n\n# Misc\n*.pem\n.cache\n",
|
|
245
245
|
"base/biome.json.hbs": '{\n "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",\n "organizeImports": {\n "enabled": true\n },\n "linter": {\n "enabled": true,\n "rules": {\n "recommended": true\n }\n },\n "formatter": {\n "enabled": true,\n "indentStyle": "space",\n "indentWidth": 2\n },\n "javascript": {\n "formatter": {\n "quoteStyle": "single",\n "semicolons": "asNeeded"\n }\n },\n "files": {\n "ignore": [\n "node_modules",\n ".next",\n "dist",\n ".turbo",\n "coverage",\n ".vercel",\n "_generated"\n ]\n }\n}\n',
|
|
246
246
|
"convex/_env.local.hbs": "# Convex\nCONVEX_DEPLOYMENT=\nNEXT_PUBLIC_CONVEX_URL=\nNEXT_PUBLIC_CONVEX_SITE_URL=\n\n# Site URL (used for auth redirects)\nSITE_URL=http://localhost:3000\nNEXT_PUBLIC_SITE_URL=http://localhost:3000\n\n# Better Auth Secret (generate with: openssl rand -base64 32)\nBETTER_AUTH_SECRET=\n\n# Auth - GitHub OAuth\nGITHUB_CLIENT_ID=\nGITHUB_CLIENT_SECRET=\n\n# Auth - Google OAuth\nGOOGLE_CLIENT_ID=\nGOOGLE_CLIENT_SECRET=\n\n# Email (Resend) - https://resend.com\nRESEND_API_KEY=\nRESEND_FROM_EMAIL=\n{{#if (eq integrations.analytics 'posthog')}}\n\n# PostHog\nNEXT_PUBLIC_POSTHOG_KEY=\nNEXT_PUBLIC_POSTHOG_HOST=https://app.posthog.com\n{{/if}}\n{{#if (eq integrations.uploads 'convex-fs')}}\n\n# Convex FS - Built-in file storage (no additional config needed)\n{{/if}}\n{{#if (eq integrations.uploads 'r2')}}\n\n# Cloudflare R2\nR2_ACCESS_KEY_ID=\nR2_SECRET_ACCESS_KEY=\nR2_BUCKET=\nR2_ENDPOINT=\n{{/if}}\n{{#if (eq integrations.uploads 'uploadthing')}}\n\n# UploadThing\nUPLOADTHING_TOKEN=\n{{/if}}\n{{#if (eq integrations.uploads 's3')}}\n\n# AWS S3\nAWS_ACCESS_KEY_ID=\nAWS_SECRET_ACCESS_KEY=\nAWS_REGION=\nAWS_S3_BUCKET=\n{{/if}}\n{{#if (eq integrations.uploads 'vercel-blob')}}\n\n# Vercel Blob\nBLOB_READ_WRITE_TOKEN=\n{{/if}}\n{{#if (eq integrations.payments 'stripe')}}\n\n# Stripe\nSTRIPE_SECRET_KEY=\nSTRIPE_WEBHOOK_SECRET=\nNEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=\n{{/if}}\n{{#if (eq integrations.payments 'polar')}}\n\n# Polar\nPOLAR_ACCESS_TOKEN=\nPOLAR_WEBHOOK_SECRET=\nPOLAR_ORGANIZATION_ID=\n{{/if}}\n{{#if (includes addons 'rate-limiting')}}\n\n# Convex Rate Limiter - No additional config needed (uses Convex backend)\n{{/if}}\n{{#if (includes addons 'monitoring')}}\n\n# Sentry\nSENTRY_DSN=\nSENTRY_AUTH_TOKEN=\n{{/if}}\n",
|
|
247
|
-
"convex/convex/auth.ts.hbs": "import {
|
|
247
|
+
"convex/convex/auth.config.ts.hbs": "import { getAuthConfigProvider } from '@convex-dev/better-auth/auth-config'\nimport type { AuthConfig } from 'convex/server'\n\nexport default {\n providers: [getAuthConfigProvider()],\n} satisfies AuthConfig\n",
|
|
248
|
+
"convex/convex/auth.ts.hbs": "import { createClient, type GenericCtx } from '@convex-dev/better-auth'\nimport { convex } from '@convex-dev/better-auth/plugins'\nimport { betterAuth } from 'better-auth/minimal'\nimport { components } from './_generated/api'\nimport type { DataModel } from './_generated/dataModel'\nimport { query } from './_generated/server'\nimport authConfig from './auth.config'\n\nconst siteUrl = process.env.SITE_URL!\n\nexport const authComponent = createClient<DataModel>(components.betterAuth)\n\nexport const createAuth = (ctx: GenericCtx<DataModel>) => {\n return betterAuth({\n baseURL: siteUrl,\n database: authComponent.adapter(ctx),\n emailAndPassword: {\n enabled: true,\n requireEmailVerification: false,\n },\n socialProviders: {\n github: {\n clientId: process.env.GITHUB_CLIENT_ID!,\n clientSecret: process.env.GITHUB_CLIENT_SECRET!,\n },\n google: {\n clientId: process.env.GOOGLE_CLIENT_ID!,\n clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n },\n },\n plugins: [convex({ authConfig })],\n })\n}\n\nexport const getCurrentUser = query({\n args: {},\n handler: async (ctx) => {\n return authComponent.getAuthUser(ctx)\n },\n})\n",
|
|
248
249
|
"convex/convex/convex.config.ts.hbs": "import { defineApp } from 'convex/server'\nimport betterAuth from '@convex-dev/better-auth/convex.config'\n\nconst app = defineApp()\napp.use(betterAuth)\n\nexport default app\n",
|
|
249
|
-
"convex/convex/http.ts.hbs": "import { httpRouter } from 'convex/server'\nimport {
|
|
250
|
+
"convex/convex/http.ts.hbs": "import { httpRouter } from 'convex/server'\nimport { authComponent, createAuth } from './auth'\n\nconst http = httpRouter()\n\n// Register Better Auth routes\nauthComponent.registerRoutes(http, createAuth)\n\nexport default http\n",
|
|
250
251
|
"convex/convex/schema.ts.hbs": "import { defineSchema, defineTable } from 'convex/server'\nimport { v } from 'convex/values'\n\n// Better Auth manages its own tables via the betterAuth component\n// Add your custom application tables here\nexport default defineSchema({\n // Example:\n // posts: defineTable({\n // title: v.string(),\n // content: v.string(),\n // userId: v.string(), // Better Auth user ID\n // createdAt: v.number(),\n // }).index('by_user', ['userId']),\n})\n",
|
|
251
|
-
"convex/convex/users.ts.hbs": "import { query } from './_generated/server'\nimport {
|
|
252
|
+
"convex/convex/users.ts.hbs": "import { query } from './_generated/server'\nimport { authComponent } from './auth'\n\n// Get current user from Better Auth session\nexport const current = query({\n args: {},\n handler: async (ctx) => {\n return authComponent.getAuthUser(ctx)\n },\n})\n\n// Alias for current user - used by dashboard components\nexport const viewer = query({\n args: {},\n handler: async (ctx) => {\n return authComponent.getAuthUser(ctx)\n },\n})\n",
|
|
252
253
|
"convex/package.json.hbs": `{{#if (eq structure 'monorepo')}}{
|
|
253
254
|
"name": "@repo/backend",
|
|
254
255
|
"version": "0.1.0",
|
|
@@ -580,12 +581,11 @@ export default function RootLayout({
|
|
|
580
581
|
"packages/config-typescript/base.json.hbs": '{\n "compilerOptions": {\n "target": "ES2020",\n "lib": ["dom", "dom.iterable", "esnext"],\n "allowJs": true,\n "skipLibCheck": true,\n "strict": true,\n "noEmit": true,\n "esModuleInterop": true,\n "module": "esnext",\n "moduleResolution": "bundler",\n "resolveJsonModule": true,\n "isolatedModules": true,\n "incremental": true\n },\n "exclude": ["node_modules"]\n}\n',
|
|
581
582
|
"packages/config-typescript/nextjs.json.hbs": '{\n "extends": "./base.json",\n "compilerOptions": {\n "jsx": "react-jsx",\n "plugins": [{ "name": "next" }]\n }\n}\n',
|
|
582
583
|
"packages/config-typescript/package.json.hbs": '{\n "name": "@repo/config-typescript",\n "version": "0.1.0",\n "private": true,\n "exports": {\n "./base.json": "./base.json",\n "./nextjs.json": "./nextjs.json"\n },\n "files": ["base.json", "nextjs.json"]\n}\n',
|
|
583
|
-
"packages/ui/
|
|
584
|
-
"packages/ui/package.json.hbs": '{\n "name": "@repo/ui",\n "version": "0.1.0",\n "private": true,\n "main": "./src/index.ts",\n "types": "./src/index.ts",\n "exports": {\n ".": "./src/index.ts",\n "./components/*": "./src/components/*.tsx",\n "./lib/*": "./src/lib/*.ts"\n },\n "scripts": {\n "lint": "biome check .",\n "lint:fix": "biome check --write .",\n "typecheck": "tsc --noEmit"\n },\n "dependencies": {\n "@hugeicons/react": "^0.3.0",\n "class-variance-authority": "^0.7.0",\n "clsx": "^2.1.0",\n "tailwind-merge": "^2.5.0"\n },\n "devDependencies": {\n "@repo/config-typescript": "workspace:*",\n "@types/react": "^19.0.0",\n "@types/react-dom": "^19.0.0",\n "react": "^19.0.0",\n "tailwindcss": "^4.0.0",\n "typescript": "^5.0.0"\n },\n "peerDependencies": {\n "react": "^19.0.0"\n }\n}\n',
|
|
584
|
+
"packages/ui/package.json.hbs": '{\n "name": "@repo/ui",\n "version": "0.1.0",\n "private": true,\n "main": "./src/index.ts",\n "types": "./src/index.ts",\n "exports": {\n ".": "./src/index.ts",\n "./components/*": "./src/components/*.tsx",\n "./lib/*": "./src/lib/*.ts"\n },\n "scripts": {\n "lint": "biome check .",\n "lint:fix": "biome check --write .",\n "typecheck": "tsc --noEmit"\n },\n "dependencies": {\n {{#if (eq shadcn.iconLibrary "hugeicons")}}"@hugeicons/react": "^0.3.0",{{/if}}\n {{#if (eq shadcn.iconLibrary "lucide")}}"lucide-react": "^0.469.0",{{/if}}\n {{#if (eq shadcn.iconLibrary "tabler")}}"@tabler/icons-react": "^3.31.0",{{/if}}\n {{#if (eq shadcn.iconLibrary "phosphor")}}"@phosphor-icons/react": "^2.1.7",{{/if}}\n "class-variance-authority": "^0.7.0",\n "clsx": "^2.1.0",\n "tailwind-merge": "^2.5.0"\n },\n "devDependencies": {\n "@repo/config-typescript": "workspace:*",\n "@types/react": "^19.0.0",\n "@types/react-dom": "^19.0.0",\n "react": "^19.0.0",\n "tailwindcss": "^4.0.0",\n "typescript": "^5.0.0"\n },\n "peerDependencies": {\n "react": "^19.0.0"\n }\n}\n',
|
|
585
585
|
"packages/ui/src/index.ts.hbs": "export { cn } from './lib/utils'\n// Export components as they are added\n// export * from './components/ui/button'\n",
|
|
586
586
|
"packages/ui/src/lib/utils.ts.hbs": "import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n",
|
|
587
587
|
"packages/ui/tsconfig.json.hbs": '{\n "compilerOptions": {\n "target": "ES2020",\n "lib": ["dom", "dom.iterable", "esnext"],\n "allowJs": true,\n "skipLibCheck": true,\n "strict": true,\n "noEmit": true,\n "esModuleInterop": true,\n "module": "esnext",\n "moduleResolution": "bundler",\n "resolveJsonModule": true,\n "isolatedModules": true,\n "jsx": "react-jsx",\n "incremental": true,\n "paths": {\n "@/*": ["./src/*"]\n }\n },\n "include": ["src/**/*"],\n "exclude": ["node_modules"]\n}\n',
|
|
588
|
-
"web/components.json.hbs": '{\n "$schema": "https://ui.shadcn.com/schema.json",\n "style": "new-york",\n "rsc": true,\n "tsx": true,\n "tailwind": {\n "config": "",\n "css": "src/app/globals.css",\n "baseColor": "{{shadcn.baseColor}}",\n "cssVariables": true
|
|
588
|
+
"web/components.json.hbs": '{\n "$schema": "https://ui.shadcn.com/schema.json",\n "style": "new-york",\n "rsc": true,\n "tsx": true,\n "tailwind": {\n "config": "",\n "css": "src/app/globals.css",\n "baseColor": "{{shadcn.baseColor}}",\n "cssVariables": true,\n "prefix": ""\n },\n "aliases": {\n "components": "@/components",\n "utils": "@/lib/utils",\n "ui": "@/components/ui",\n "lib": "@/lib",\n "hooks": "@/hooks"\n },\n "iconLibrary": "{{shadcn.iconLibrary}}"\n}\n',
|
|
589
589
|
"web/next.config.ts.hbs": "import type { NextConfig } from 'next'\n\nconst nextConfig: NextConfig = {\n{{#if (eq structure 'monorepo')}}\n transpilePackages: ['@repo/ui', '@repo/backend'],\n{{/if}}\n}\n\nexport default nextConfig\n",
|
|
590
590
|
"web/package.json.hbs": `{
|
|
591
591
|
"name": "{{#if (eq structure 'monorepo')}}@repo/web{{else}}{{projectName}}{{/if}}",
|
|
@@ -607,14 +607,18 @@ export default function RootLayout({
|
|
|
607
607
|
},
|
|
608
608
|
"dependencies": {
|
|
609
609
|
{{#if (eq structure 'monorepo')}} "@repo/backend": "workspace:*",
|
|
610
|
+
"@repo/ui": "workspace:*",
|
|
610
611
|
{{/if}} "next": "^16.0.0",
|
|
611
612
|
"react": "^19.0.0",
|
|
612
613
|
"react-dom": "^19.0.0",
|
|
613
614
|
"convex": "^1.25.0",
|
|
614
615
|
"@convex-dev/better-auth": "^0.10.0",
|
|
615
616
|
"better-auth": "1.4.9",
|
|
616
|
-
"@hugeicons/react": "^0.3.0",
|
|
617
|
-
"
|
|
617
|
+
{{#unless (eq structure 'monorepo')}}{{#if (eq shadcn.iconLibrary "hugeicons")}} "@hugeicons/react": "^0.3.0",
|
|
618
|
+
{{/if}}{{#if (eq shadcn.iconLibrary "lucide")}} "lucide-react": "^0.469.0",
|
|
619
|
+
{{/if}}{{#if (eq shadcn.iconLibrary "tabler")}} "@tabler/icons-react": "^3.31.0",
|
|
620
|
+
{{/if}}{{#if (eq shadcn.iconLibrary "phosphor")}} "@phosphor-icons/react": "^2.1.7",
|
|
621
|
+
{{/if}}{{/unless}} "class-variance-authority": "^0.7.0",
|
|
618
622
|
"clsx": "^2.1.0",
|
|
619
623
|
"tailwind-merge": "^2.5.0",
|
|
620
624
|
"tw-animate-css": "^1.3.0",
|
|
@@ -1265,7 +1269,32 @@ export function DashboardLayout({ children, title = 'Dashboard' }: DashboardLayo
|
|
|
1265
1269
|
"web/src/lib/auth.ts.hbs": "'use client'\n\nimport { createAuthClient } from 'better-auth/react'\nimport { convexClient } from '@convex-dev/better-auth/client/plugins'\n\nexport const authClient = createAuthClient({\n plugins: [convexClient()],\n})\n\nexport const {\n signIn,\n signUp,\n signOut,\n useSession,\n getSession,\n} = authClient\n",
|
|
1266
1270
|
"web/src/lib/utils.ts.hbs": "import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n",
|
|
1267
1271
|
"web/src/proxy.ts.hbs": "import { NextResponse } from 'next/server'\nimport type { NextRequest } from 'next/server'\nimport { isAuthenticated } from '@/lib/auth-server'\n\nconst publicRoutes = ['/sign-in', '/sign-up', '/api/auth']\n\nfunction isPublicRoute(pathname: string) {\n return publicRoutes.some(route => pathname.startsWith(route))\n}\n\nexport async function proxy(request: NextRequest) {\n const { pathname } = request.nextUrl\n\n // Allow public routes\n if (isPublicRoute(pathname)) {\n return NextResponse.next()\n }\n\n // Check authentication\n const authenticated = await isAuthenticated()\n\n // Redirect unauthenticated users to /sign-up\n if (!authenticated) {\n return NextResponse.redirect(new URL('/sign-up', request.url))\n }\n\n return NextResponse.next()\n}\n\nexport const config = {\n matcher: ['/((?!.*\\\\..*|_next).*)', '/', '/(api|trpc)(.*)'],\n}\n",
|
|
1268
|
-
"web/tsconfig.json.hbs":
|
|
1272
|
+
"web/tsconfig.json.hbs": `{
|
|
1273
|
+
"compilerOptions": {
|
|
1274
|
+
"target": "ES2017",
|
|
1275
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
1276
|
+
"allowJs": true,
|
|
1277
|
+
"skipLibCheck": true,
|
|
1278
|
+
"strict": true,
|
|
1279
|
+
"noEmit": true,
|
|
1280
|
+
"esModuleInterop": true,
|
|
1281
|
+
"module": "esnext",
|
|
1282
|
+
"moduleResolution": "bundler",
|
|
1283
|
+
"resolveJsonModule": true,
|
|
1284
|
+
"isolatedModules": true,
|
|
1285
|
+
"jsx": "react-jsx",
|
|
1286
|
+
"incremental": true,
|
|
1287
|
+
"plugins": [{ "name": "next" }],
|
|
1288
|
+
"paths": {
|
|
1289
|
+
{{#if (eq structure 'monorepo')}} "@/components/ui/*": ["../../packages/ui/src/components/ui/*"],
|
|
1290
|
+
"@/lib/utils": ["../../packages/ui/src/lib/utils"],
|
|
1291
|
+
{{/if}} "@/*": ["./src/*"]
|
|
1292
|
+
}
|
|
1293
|
+
},
|
|
1294
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", ".next/dev/types/**/*.ts"],
|
|
1295
|
+
"exclude": ["node_modules"]
|
|
1296
|
+
}
|
|
1297
|
+
`
|
|
1269
1298
|
};
|
|
1270
1299
|
|
|
1271
1300
|
// src/generator.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kofi-stack-template-generator",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"handlebars": "^4.7.8",
|
|
15
15
|
"memfs": "^4.9.0",
|
|
16
|
-
"kofi-stack-types": "^2.0
|
|
16
|
+
"kofi-stack-types": "^2.1.0"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/node": "^20.0.0",
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
// Auto-generated file. Do not edit manually.
|
|
2
2
|
// Run 'pnpm prebuild' to regenerate.
|
|
3
|
-
// Generated: 2026-01-
|
|
3
|
+
// Generated: 2026-01-14T03:49:45.053Z
|
|
4
4
|
// Template count: 89
|
|
5
5
|
|
|
6
6
|
export const EMBEDDED_TEMPLATES: Record<string, string> = {
|
|
7
7
|
"base/_gitignore.hbs": "# Dependencies\nnode_modules\n.pnpm-store\n\n# Build outputs\n.next\ndist\n.turbo\nout\n\n# Testing\ncoverage\nplaywright-report\ntest-results\n\n# Environment\n.env\n.env.local\n.env.*.local\n\n# IDE\n.idea\n.vscode\n*.swp\n*.swo\n.DS_Store\n\n# Convex\n.convex\n\n# Vercel\n.vercel\n\n# Debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.pnpm-debug.log*\n\n# TypeScript\n*.tsbuildinfo\n\n# Misc\n*.pem\n.cache\n",
|
|
8
8
|
"base/biome.json.hbs": "{\n \"$schema\": \"https://biomejs.dev/schemas/1.9.4/schema.json\",\n \"organizeImports\": {\n \"enabled\": true\n },\n \"linter\": {\n \"enabled\": true,\n \"rules\": {\n \"recommended\": true\n }\n },\n \"formatter\": {\n \"enabled\": true,\n \"indentStyle\": \"space\",\n \"indentWidth\": 2\n },\n \"javascript\": {\n \"formatter\": {\n \"quoteStyle\": \"single\",\n \"semicolons\": \"asNeeded\"\n }\n },\n \"files\": {\n \"ignore\": [\n \"node_modules\",\n \".next\",\n \"dist\",\n \".turbo\",\n \"coverage\",\n \".vercel\",\n \"_generated\"\n ]\n }\n}\n",
|
|
9
9
|
"convex/_env.local.hbs": "# Convex\nCONVEX_DEPLOYMENT=\nNEXT_PUBLIC_CONVEX_URL=\nNEXT_PUBLIC_CONVEX_SITE_URL=\n\n# Site URL (used for auth redirects)\nSITE_URL=http://localhost:3000\nNEXT_PUBLIC_SITE_URL=http://localhost:3000\n\n# Better Auth Secret (generate with: openssl rand -base64 32)\nBETTER_AUTH_SECRET=\n\n# Auth - GitHub OAuth\nGITHUB_CLIENT_ID=\nGITHUB_CLIENT_SECRET=\n\n# Auth - Google OAuth\nGOOGLE_CLIENT_ID=\nGOOGLE_CLIENT_SECRET=\n\n# Email (Resend) - https://resend.com\nRESEND_API_KEY=\nRESEND_FROM_EMAIL=\n{{#if (eq integrations.analytics 'posthog')}}\n\n# PostHog\nNEXT_PUBLIC_POSTHOG_KEY=\nNEXT_PUBLIC_POSTHOG_HOST=https://app.posthog.com\n{{/if}}\n{{#if (eq integrations.uploads 'convex-fs')}}\n\n# Convex FS - Built-in file storage (no additional config needed)\n{{/if}}\n{{#if (eq integrations.uploads 'r2')}}\n\n# Cloudflare R2\nR2_ACCESS_KEY_ID=\nR2_SECRET_ACCESS_KEY=\nR2_BUCKET=\nR2_ENDPOINT=\n{{/if}}\n{{#if (eq integrations.uploads 'uploadthing')}}\n\n# UploadThing\nUPLOADTHING_TOKEN=\n{{/if}}\n{{#if (eq integrations.uploads 's3')}}\n\n# AWS S3\nAWS_ACCESS_KEY_ID=\nAWS_SECRET_ACCESS_KEY=\nAWS_REGION=\nAWS_S3_BUCKET=\n{{/if}}\n{{#if (eq integrations.uploads 'vercel-blob')}}\n\n# Vercel Blob\nBLOB_READ_WRITE_TOKEN=\n{{/if}}\n{{#if (eq integrations.payments 'stripe')}}\n\n# Stripe\nSTRIPE_SECRET_KEY=\nSTRIPE_WEBHOOK_SECRET=\nNEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=\n{{/if}}\n{{#if (eq integrations.payments 'polar')}}\n\n# Polar\nPOLAR_ACCESS_TOKEN=\nPOLAR_WEBHOOK_SECRET=\nPOLAR_ORGANIZATION_ID=\n{{/if}}\n{{#if (includes addons 'rate-limiting')}}\n\n# Convex Rate Limiter - No additional config needed (uses Convex backend)\n{{/if}}\n{{#if (includes addons 'monitoring')}}\n\n# Sentry\nSENTRY_DSN=\nSENTRY_AUTH_TOKEN=\n{{/if}}\n",
|
|
10
|
-
"convex/convex/auth.ts.hbs": "import {
|
|
10
|
+
"convex/convex/auth.config.ts.hbs": "import { getAuthConfigProvider } from '@convex-dev/better-auth/auth-config'\nimport type { AuthConfig } from 'convex/server'\n\nexport default {\n providers: [getAuthConfigProvider()],\n} satisfies AuthConfig\n",
|
|
11
|
+
"convex/convex/auth.ts.hbs": "import { createClient, type GenericCtx } from '@convex-dev/better-auth'\nimport { convex } from '@convex-dev/better-auth/plugins'\nimport { betterAuth } from 'better-auth/minimal'\nimport { components } from './_generated/api'\nimport type { DataModel } from './_generated/dataModel'\nimport { query } from './_generated/server'\nimport authConfig from './auth.config'\n\nconst siteUrl = process.env.SITE_URL!\n\nexport const authComponent = createClient<DataModel>(components.betterAuth)\n\nexport const createAuth = (ctx: GenericCtx<DataModel>) => {\n return betterAuth({\n baseURL: siteUrl,\n database: authComponent.adapter(ctx),\n emailAndPassword: {\n enabled: true,\n requireEmailVerification: false,\n },\n socialProviders: {\n github: {\n clientId: process.env.GITHUB_CLIENT_ID!,\n clientSecret: process.env.GITHUB_CLIENT_SECRET!,\n },\n google: {\n clientId: process.env.GOOGLE_CLIENT_ID!,\n clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n },\n },\n plugins: [convex({ authConfig })],\n })\n}\n\nexport const getCurrentUser = query({\n args: {},\n handler: async (ctx) => {\n return authComponent.getAuthUser(ctx)\n },\n})\n",
|
|
11
12
|
"convex/convex/convex.config.ts.hbs": "import { defineApp } from 'convex/server'\nimport betterAuth from '@convex-dev/better-auth/convex.config'\n\nconst app = defineApp()\napp.use(betterAuth)\n\nexport default app\n",
|
|
12
|
-
"convex/convex/http.ts.hbs": "import { httpRouter } from 'convex/server'\nimport {
|
|
13
|
+
"convex/convex/http.ts.hbs": "import { httpRouter } from 'convex/server'\nimport { authComponent, createAuth } from './auth'\n\nconst http = httpRouter()\n\n// Register Better Auth routes\nauthComponent.registerRoutes(http, createAuth)\n\nexport default http\n",
|
|
13
14
|
"convex/convex/schema.ts.hbs": "import { defineSchema, defineTable } from 'convex/server'\nimport { v } from 'convex/values'\n\n// Better Auth manages its own tables via the betterAuth component\n// Add your custom application tables here\nexport default defineSchema({\n // Example:\n // posts: defineTable({\n // title: v.string(),\n // content: v.string(),\n // userId: v.string(), // Better Auth user ID\n // createdAt: v.number(),\n // }).index('by_user', ['userId']),\n})\n",
|
|
14
|
-
"convex/convex/users.ts.hbs": "import { query } from './_generated/server'\nimport {
|
|
15
|
+
"convex/convex/users.ts.hbs": "import { query } from './_generated/server'\nimport { authComponent } from './auth'\n\n// Get current user from Better Auth session\nexport const current = query({\n args: {},\n handler: async (ctx) => {\n return authComponent.getAuthUser(ctx)\n },\n})\n\n// Alias for current user - used by dashboard components\nexport const viewer = query({\n args: {},\n handler: async (ctx) => {\n return authComponent.getAuthUser(ctx)\n },\n})\n",
|
|
15
16
|
"convex/package.json.hbs": "{{#if (eq structure 'monorepo')}}{\n \"name\": \"@repo/backend\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"type\": \"module\",\n \"main\": \"./convex/_generated/api.js\",\n \"types\": \"./convex/_generated/api.d.ts\",\n \"exports\": {\n \".\": {\n \"import\": \"./convex/_generated/api.js\",\n \"types\": \"./convex/_generated/api.d.ts\"\n }\n },\n \"scripts\": {\n \"dev\": \"convex dev\",\n \"dev:setup\": \"convex dev --configure --until-success\",\n \"deploy\": \"convex deploy\"\n },\n \"dependencies\": {\n \"convex\": \"^1.25.0\",\n \"@convex-dev/better-auth\": \"^0.10.0\",\n \"better-auth\": \"1.4.9\",\n \"@convex-dev/resend\": \"^0.2.0\"{{#if (eq integrations.uploads 'convex-fs')}},\n \"convex-fs\": \"^0.2.0\"{{/if}}{{#if (eq integrations.uploads 'r2')}},\n \"@convex-dev/r2\": \"^0.8.0\"{{/if}}{{#if (eq integrations.payments 'stripe')}},\n \"@convex-dev/stripe\": \"^0.1.0\"{{/if}}{{#if (eq integrations.payments 'polar')}},\n \"@convex-dev/polar\": \"^0.7.0\"{{/if}}{{#if (includes addons 'rate-limiting')}},\n \"@convex-dev/rate-limiter\": \"^0.3.0\"{{/if}}\n },\n \"devDependencies\": {\n \"typescript\": \"^5.0.0\"\n }\n}{{/if}}\n",
|
|
16
17
|
"convex/tsconfig.json.hbs": "{{#if (eq structure 'monorepo')}}{\n \"compilerOptions\": {\n \"target\": \"ESNext\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"bundler\",\n \"strict\": true,\n \"esModuleInterop\": true,\n \"skipLibCheck\": true,\n \"noEmit\": true,\n \"outDir\": \"dist\"\n },\n \"include\": [\"convex/**/*.ts\"],\n \"exclude\": [\"node_modules\"]\n}{{/if}}\n",
|
|
17
18
|
"integrations/posthog/src/components/providers/posthog-provider.tsx.hbs": "'use client'\n\nimport posthog from 'posthog-js'\nimport { PostHogProvider as PHProvider } from 'posthog-js/react'\nimport { useEffect } from 'react'\n\nexport function PostHogProvider({ children }: { children: React.ReactNode }) {\n useEffect(() => {\n posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {\n api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://app.posthog.com',\n person_profiles: 'identified_only',\n capture_pageview: false, // We capture pageviews manually\n })\n }, [])\n\n return <PHProvider client={posthog}>{children}</PHProvider>\n}\n",
|
|
@@ -67,14 +68,13 @@ export const EMBEDDED_TEMPLATES: Record<string, string> = {
|
|
|
67
68
|
"packages/config-typescript/base.json.hbs": "{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"skipLibCheck\": true,\n \"strict\": true,\n \"noEmit\": true,\n \"esModuleInterop\": true,\n \"module\": \"esnext\",\n \"moduleResolution\": \"bundler\",\n \"resolveJsonModule\": true,\n \"isolatedModules\": true,\n \"incremental\": true\n },\n \"exclude\": [\"node_modules\"]\n}\n",
|
|
68
69
|
"packages/config-typescript/nextjs.json.hbs": "{\n \"extends\": \"./base.json\",\n \"compilerOptions\": {\n \"jsx\": \"react-jsx\",\n \"plugins\": [{ \"name\": \"next\" }]\n }\n}\n",
|
|
69
70
|
"packages/config-typescript/package.json.hbs": "{\n \"name\": \"@repo/config-typescript\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"exports\": {\n \"./base.json\": \"./base.json\",\n \"./nextjs.json\": \"./nextjs.json\"\n },\n \"files\": [\"base.json\", \"nextjs.json\"]\n}\n",
|
|
70
|
-
"packages/ui/
|
|
71
|
-
"packages/ui/package.json.hbs": "{\n \"name\": \"@repo/ui\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"main\": \"./src/index.ts\",\n \"types\": \"./src/index.ts\",\n \"exports\": {\n \".\": \"./src/index.ts\",\n \"./components/*\": \"./src/components/*.tsx\",\n \"./lib/*\": \"./src/lib/*.ts\"\n },\n \"scripts\": {\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"dependencies\": {\n \"@hugeicons/react\": \"^0.3.0\",\n \"class-variance-authority\": \"^0.7.0\",\n \"clsx\": \"^2.1.0\",\n \"tailwind-merge\": \"^2.5.0\"\n },\n \"devDependencies\": {\n \"@repo/config-typescript\": \"workspace:*\",\n \"@types/react\": \"^19.0.0\",\n \"@types/react-dom\": \"^19.0.0\",\n \"react\": \"^19.0.0\",\n \"tailwindcss\": \"^4.0.0\",\n \"typescript\": \"^5.0.0\"\n },\n \"peerDependencies\": {\n \"react\": \"^19.0.0\"\n }\n}\n",
|
|
71
|
+
"packages/ui/package.json.hbs": "{\n \"name\": \"@repo/ui\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"main\": \"./src/index.ts\",\n \"types\": \"./src/index.ts\",\n \"exports\": {\n \".\": \"./src/index.ts\",\n \"./components/*\": \"./src/components/*.tsx\",\n \"./lib/*\": \"./src/lib/*.ts\"\n },\n \"scripts\": {\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"dependencies\": {\n {{#if (eq shadcn.iconLibrary \"hugeicons\")}}\"@hugeicons/react\": \"^0.3.0\",{{/if}}\n {{#if (eq shadcn.iconLibrary \"lucide\")}}\"lucide-react\": \"^0.469.0\",{{/if}}\n {{#if (eq shadcn.iconLibrary \"tabler\")}}\"@tabler/icons-react\": \"^3.31.0\",{{/if}}\n {{#if (eq shadcn.iconLibrary \"phosphor\")}}\"@phosphor-icons/react\": \"^2.1.7\",{{/if}}\n \"class-variance-authority\": \"^0.7.0\",\n \"clsx\": \"^2.1.0\",\n \"tailwind-merge\": \"^2.5.0\"\n },\n \"devDependencies\": {\n \"@repo/config-typescript\": \"workspace:*\",\n \"@types/react\": \"^19.0.0\",\n \"@types/react-dom\": \"^19.0.0\",\n \"react\": \"^19.0.0\",\n \"tailwindcss\": \"^4.0.0\",\n \"typescript\": \"^5.0.0\"\n },\n \"peerDependencies\": {\n \"react\": \"^19.0.0\"\n }\n}\n",
|
|
72
72
|
"packages/ui/src/index.ts.hbs": "export { cn } from './lib/utils'\n// Export components as they are added\n// export * from './components/ui/button'\n",
|
|
73
73
|
"packages/ui/src/lib/utils.ts.hbs": "import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n",
|
|
74
74
|
"packages/ui/tsconfig.json.hbs": "{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"skipLibCheck\": true,\n \"strict\": true,\n \"noEmit\": true,\n \"esModuleInterop\": true,\n \"module\": \"esnext\",\n \"moduleResolution\": \"bundler\",\n \"resolveJsonModule\": true,\n \"isolatedModules\": true,\n \"jsx\": \"react-jsx\",\n \"incremental\": true,\n \"paths\": {\n \"@/*\": [\"./src/*\"]\n }\n },\n \"include\": [\"src/**/*\"],\n \"exclude\": [\"node_modules\"]\n}\n",
|
|
75
|
-
"web/components.json.hbs": "{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"new-york\",\n \"rsc\": true,\n \"tsx\": true,\n \"tailwind\": {\n \"config\": \"\",\n \"css\": \"src/app/globals.css\",\n \"baseColor\": \"{{shadcn.baseColor}}\",\n \"cssVariables\": true
|
|
75
|
+
"web/components.json.hbs": "{\n \"$schema\": \"https://ui.shadcn.com/schema.json\",\n \"style\": \"new-york\",\n \"rsc\": true,\n \"tsx\": true,\n \"tailwind\": {\n \"config\": \"\",\n \"css\": \"src/app/globals.css\",\n \"baseColor\": \"{{shadcn.baseColor}}\",\n \"cssVariables\": true,\n \"prefix\": \"\"\n },\n \"aliases\": {\n \"components\": \"@/components\",\n \"utils\": \"@/lib/utils\",\n \"ui\": \"@/components/ui\",\n \"lib\": \"@/lib\",\n \"hooks\": \"@/hooks\"\n },\n \"iconLibrary\": \"{{shadcn.iconLibrary}}\"\n}\n",
|
|
76
76
|
"web/next.config.ts.hbs": "import type { NextConfig } from 'next'\n\nconst nextConfig: NextConfig = {\n{{#if (eq structure 'monorepo')}}\n transpilePackages: ['@repo/ui', '@repo/backend'],\n{{/if}}\n}\n\nexport default nextConfig\n",
|
|
77
|
-
"web/package.json.hbs": "{\n \"name\": \"{{#if (eq structure 'monorepo')}}@repo/web{{else}}{{projectName}}{{/if}}\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"dev\": \"{{#if (eq structure 'monorepo')}}next dev --turbopack{{else}}node scripts/dev.mjs{{/if}}\",\n \"dev:next\": \"next dev --turbopack\",\n{{#unless (eq structure 'monorepo')}} \"dev:setup\": \"npx convex dev --configure --until-success\",\n{{/unless}} \"build\": \"next build\",\n \"start\": \"next start\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:e2e\": \"playwright test\"\n },\n \"dependencies\": {\n{{#if (eq structure 'monorepo')}} \"@repo/backend\": \"workspace:*\",\n{{/if}} \"next\": \"^16.0.0\",\n \"react\": \"^19.0.0\",\n \"react-dom\": \"^19.0.0\",\n \"convex\": \"^1.25.0\",\n \"@convex-dev/better-auth\": \"^0.10.0\",\n \"better-auth\": \"1.4.9\",\n \"@hugeicons/react\": \"^0.3.0\",\n \"class-variance-authority\": \"^0.7.0\",\n \"clsx\": \"^2.1.0\",\n \"tailwind-merge\": \"^2.5.0\",\n \"tw-animate-css\": \"^1.3.0\",\n \"resend\": \"^4.0.0\",\n \"react-email\": \"^3.0.0\",\n \"@react-email/components\": \"^0.0.36\"{{#if (eq integrations.analytics 'posthog')}},\n \"posthog-js\": \"^1.200.0\",\n \"posthog-node\": \"^5.0.0\"{{/if}}{{#if (eq integrations.analytics 'vercel')}},\n \"@vercel/analytics\": \"^1.4.0\",\n \"@vercel/speed-insights\": \"^1.1.0\"{{/if}}{{#if (eq integrations.uploads 'uploadthing')}},\n \"uploadthing\": \"^7.0.0\",\n \"@uploadthing/react\": \"^7.0.0\"{{/if}}{{#if (eq integrations.uploads 's3')}},\n \"@aws-sdk/client-s3\": \"^3.700.0\",\n \"@aws-sdk/s3-request-presigner\": \"^3.700.0\"{{/if}}{{#if (eq integrations.uploads 'vercel-blob')}},\n \"@vercel/blob\": \"^2.0.0\"{{/if}}{{#if (includes addons 'rate-limiting')}},\n \"@arcjet/next\": \"^1.0.0-beta.16\"{{/if}}{{#if (includes addons 'monitoring')}},\n \"@sentry/nextjs\": \"^8.0.0\"{{/if}}\n },\n \"devDependencies\": {\n{{#if (eq structure 'monorepo')}} \"@repo/config-typescript\": \"workspace:*\",\n{{/if}} \"@types/node\": \"^20.0.0\",\n \"@types/react\": \"^19.0.0\",\n \"@types/react-dom\": \"^19.0.0\",\n \"tailwindcss\": \"^4.0.0\",\n \"@tailwindcss/postcss\": \"^4.0.0\",\n \"postcss\": \"^8.4.0\",\n \"typescript\": \"^5.0.0\",\n \"vitest\": \"^3.0.0\",\n \"@vitejs/plugin-react\": \"^4.3.0\",\n \"@testing-library/react\": \"^16.0.0\",\n \"jsdom\": \"^26.0.0\",\n \"playwright\": \"^1.50.0\",\n \"@playwright/test\": \"^1.50.0\"\n }\n}\n",
|
|
77
|
+
"web/package.json.hbs": "{\n \"name\": \"{{#if (eq structure 'monorepo')}}@repo/web{{else}}{{projectName}}{{/if}}\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"dev\": \"{{#if (eq structure 'monorepo')}}next dev --turbopack{{else}}node scripts/dev.mjs{{/if}}\",\n \"dev:next\": \"next dev --turbopack\",\n{{#unless (eq structure 'monorepo')}} \"dev:setup\": \"npx convex dev --configure --until-success\",\n{{/unless}} \"build\": \"next build\",\n \"start\": \"next start\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:e2e\": \"playwright test\"\n },\n \"dependencies\": {\n{{#if (eq structure 'monorepo')}} \"@repo/backend\": \"workspace:*\",\n \"@repo/ui\": \"workspace:*\",\n{{/if}} \"next\": \"^16.0.0\",\n \"react\": \"^19.0.0\",\n \"react-dom\": \"^19.0.0\",\n \"convex\": \"^1.25.0\",\n \"@convex-dev/better-auth\": \"^0.10.0\",\n \"better-auth\": \"1.4.9\",\n{{#unless (eq structure 'monorepo')}}{{#if (eq shadcn.iconLibrary \"hugeicons\")}} \"@hugeicons/react\": \"^0.3.0\",\n{{/if}}{{#if (eq shadcn.iconLibrary \"lucide\")}} \"lucide-react\": \"^0.469.0\",\n{{/if}}{{#if (eq shadcn.iconLibrary \"tabler\")}} \"@tabler/icons-react\": \"^3.31.0\",\n{{/if}}{{#if (eq shadcn.iconLibrary \"phosphor\")}} \"@phosphor-icons/react\": \"^2.1.7\",\n{{/if}}{{/unless}} \"class-variance-authority\": \"^0.7.0\",\n \"clsx\": \"^2.1.0\",\n \"tailwind-merge\": \"^2.5.0\",\n \"tw-animate-css\": \"^1.3.0\",\n \"resend\": \"^4.0.0\",\n \"react-email\": \"^3.0.0\",\n \"@react-email/components\": \"^0.0.36\"{{#if (eq integrations.analytics 'posthog')}},\n \"posthog-js\": \"^1.200.0\",\n \"posthog-node\": \"^5.0.0\"{{/if}}{{#if (eq integrations.analytics 'vercel')}},\n \"@vercel/analytics\": \"^1.4.0\",\n \"@vercel/speed-insights\": \"^1.1.0\"{{/if}}{{#if (eq integrations.uploads 'uploadthing')}},\n \"uploadthing\": \"^7.0.0\",\n \"@uploadthing/react\": \"^7.0.0\"{{/if}}{{#if (eq integrations.uploads 's3')}},\n \"@aws-sdk/client-s3\": \"^3.700.0\",\n \"@aws-sdk/s3-request-presigner\": \"^3.700.0\"{{/if}}{{#if (eq integrations.uploads 'vercel-blob')}},\n \"@vercel/blob\": \"^2.0.0\"{{/if}}{{#if (includes addons 'rate-limiting')}},\n \"@arcjet/next\": \"^1.0.0-beta.16\"{{/if}}{{#if (includes addons 'monitoring')}},\n \"@sentry/nextjs\": \"^8.0.0\"{{/if}}\n },\n \"devDependencies\": {\n{{#if (eq structure 'monorepo')}} \"@repo/config-typescript\": \"workspace:*\",\n{{/if}} \"@types/node\": \"^20.0.0\",\n \"@types/react\": \"^19.0.0\",\n \"@types/react-dom\": \"^19.0.0\",\n \"tailwindcss\": \"^4.0.0\",\n \"@tailwindcss/postcss\": \"^4.0.0\",\n \"postcss\": \"^8.4.0\",\n \"typescript\": \"^5.0.0\",\n \"vitest\": \"^3.0.0\",\n \"@vitejs/plugin-react\": \"^4.3.0\",\n \"@testing-library/react\": \"^16.0.0\",\n \"jsdom\": \"^26.0.0\",\n \"playwright\": \"^1.50.0\",\n \"@playwright/test\": \"^1.50.0\"\n }\n}\n",
|
|
78
78
|
"web/postcss.config.mjs.hbs": "export default {\n plugins: {\n '@tailwindcss/postcss': {},\n },\n}\n",
|
|
79
79
|
"web/src/app/(auth)/layout.tsx.hbs": "export default function AuthLayout({\n children,\n}: {\n children: React.ReactNode\n}) {\n return (\n <div className=\"min-h-screen flex items-center justify-center bg-muted/50 p-4\">\n <div className=\"w-full max-w-md\">\n {children}\n </div>\n </div>\n )\n}\n",
|
|
80
80
|
"web/src/app/(auth)/sign-in/page.tsx.hbs": "import { SignInForm } from '@/components/auth/sign-in-form'\n\nexport default function SignInPage() {\n return <SignInForm />\n}\n",
|
|
@@ -92,5 +92,5 @@ export const EMBEDDED_TEMPLATES: Record<string, string> = {
|
|
|
92
92
|
"web/src/lib/auth.ts.hbs": "'use client'\n\nimport { createAuthClient } from 'better-auth/react'\nimport { convexClient } from '@convex-dev/better-auth/client/plugins'\n\nexport const authClient = createAuthClient({\n plugins: [convexClient()],\n})\n\nexport const {\n signIn,\n signUp,\n signOut,\n useSession,\n getSession,\n} = authClient\n",
|
|
93
93
|
"web/src/lib/utils.ts.hbs": "import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n",
|
|
94
94
|
"web/src/proxy.ts.hbs": "import { NextResponse } from 'next/server'\nimport type { NextRequest } from 'next/server'\nimport { isAuthenticated } from '@/lib/auth-server'\n\nconst publicRoutes = ['/sign-in', '/sign-up', '/api/auth']\n\nfunction isPublicRoute(pathname: string) {\n return publicRoutes.some(route => pathname.startsWith(route))\n}\n\nexport async function proxy(request: NextRequest) {\n const { pathname } = request.nextUrl\n\n // Allow public routes\n if (isPublicRoute(pathname)) {\n return NextResponse.next()\n }\n\n // Check authentication\n const authenticated = await isAuthenticated()\n\n // Redirect unauthenticated users to /sign-up\n if (!authenticated) {\n return NextResponse.redirect(new URL('/sign-up', request.url))\n }\n\n return NextResponse.next()\n}\n\nexport const config = {\n matcher: ['/((?!.*\\\\..*|_next).*)', '/', '/(api|trpc)(.*)'],\n}\n",
|
|
95
|
-
"web/tsconfig.json.hbs": "{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"skipLibCheck\": true,\n \"strict\": true,\n \"noEmit\": true,\n \"esModuleInterop\": true,\n \"module\": \"esnext\",\n \"moduleResolution\": \"bundler\",\n \"resolveJsonModule\": true,\n \"isolatedModules\": true,\n \"jsx\": \"react-jsx\",\n \"incremental\": true,\n \"plugins\": [{ \"name\": \"next\" }],\n \"paths\": {\n \"@/*\": [\"./src/*\"]\n }\n },\n \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\", \".next/dev/types/**/*.ts\"],\n \"exclude\": [\"node_modules\"]\n}\n"
|
|
95
|
+
"web/tsconfig.json.hbs": "{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"skipLibCheck\": true,\n \"strict\": true,\n \"noEmit\": true,\n \"esModuleInterop\": true,\n \"module\": \"esnext\",\n \"moduleResolution\": \"bundler\",\n \"resolveJsonModule\": true,\n \"isolatedModules\": true,\n \"jsx\": \"react-jsx\",\n \"incremental\": true,\n \"plugins\": [{ \"name\": \"next\" }],\n \"paths\": {\n{{#if (eq structure 'monorepo')}} \"@/components/ui/*\": [\"../../packages/ui/src/components/ui/*\"],\n \"@/lib/utils\": [\"../../packages/ui/src/lib/utils\"],\n{{/if}} \"@/*\": [\"./src/*\"]\n }\n },\n \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\", \".next/dev/types/**/*.ts\"],\n \"exclude\": [\"node_modules\"]\n}\n"
|
|
96
96
|
}
|
|
@@ -1,15 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { createClient, type GenericCtx } from '@convex-dev/better-auth'
|
|
2
|
+
import { convex } from '@convex-dev/better-auth/plugins'
|
|
3
|
+
import { betterAuth } from 'better-auth/minimal'
|
|
3
4
|
import { components } from './_generated/api'
|
|
5
|
+
import type { DataModel } from './_generated/dataModel'
|
|
6
|
+
import { query } from './_generated/server'
|
|
7
|
+
import authConfig from './auth.config'
|
|
4
8
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
const siteUrl = process.env.SITE_URL!
|
|
10
|
+
|
|
11
|
+
export const authComponent = createClient<DataModel>(components.betterAuth)
|
|
8
12
|
|
|
9
|
-
export const
|
|
10
|
-
betterAuth({
|
|
13
|
+
export const createAuth = (ctx: GenericCtx<DataModel>) => {
|
|
14
|
+
return betterAuth({
|
|
15
|
+
baseURL: siteUrl,
|
|
16
|
+
database: authComponent.adapter(ctx),
|
|
11
17
|
emailAndPassword: {
|
|
12
18
|
enabled: true,
|
|
19
|
+
requireEmailVerification: false,
|
|
13
20
|
},
|
|
14
21
|
socialProviders: {
|
|
15
22
|
github: {
|
|
@@ -21,7 +28,13 @@ export const { signIn, signUp, signOut, isAuthenticated, getSession } = auth.cre
|
|
|
21
28
|
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
22
29
|
},
|
|
23
30
|
},
|
|
31
|
+
plugins: [convex({ authConfig })],
|
|
24
32
|
})
|
|
25
|
-
|
|
33
|
+
}
|
|
26
34
|
|
|
27
|
-
export
|
|
35
|
+
export const getCurrentUser = query({
|
|
36
|
+
args: {},
|
|
37
|
+
handler: async (ctx) => {
|
|
38
|
+
return authComponent.getAuthUser(ctx)
|
|
39
|
+
},
|
|
40
|
+
})
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { httpRouter } from 'convex/server'
|
|
2
|
-
import {
|
|
2
|
+
import { authComponent, createAuth } from './auth'
|
|
3
3
|
|
|
4
4
|
const http = httpRouter()
|
|
5
5
|
|
|
6
6
|
// Register Better Auth routes
|
|
7
|
-
|
|
7
|
+
authComponent.registerRoutes(http, createAuth)
|
|
8
8
|
|
|
9
9
|
export default http
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { query } from './_generated/server'
|
|
2
|
-
import {
|
|
2
|
+
import { authComponent } from './auth'
|
|
3
3
|
|
|
4
4
|
// Get current user from Better Auth session
|
|
5
5
|
export const current = query({
|
|
6
6
|
args: {},
|
|
7
7
|
handler: async (ctx) => {
|
|
8
|
-
|
|
9
|
-
if (!session) return null
|
|
10
|
-
return session.user
|
|
8
|
+
return authComponent.getAuthUser(ctx)
|
|
11
9
|
},
|
|
12
10
|
})
|
|
13
11
|
|
|
@@ -15,8 +13,6 @@ export const current = query({
|
|
|
15
13
|
export const viewer = query({
|
|
16
14
|
args: {},
|
|
17
15
|
handler: async (ctx) => {
|
|
18
|
-
|
|
19
|
-
if (!session) return null
|
|
20
|
-
return session.user
|
|
16
|
+
return authComponent.getAuthUser(ctx)
|
|
21
17
|
},
|
|
22
18
|
})
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
"typecheck": "tsc --noEmit"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@hugeicons/react": "^0.3.0",
|
|
18
|
+
{{#if (eq shadcn.iconLibrary "hugeicons")}}"@hugeicons/react": "^0.3.0",{{/if}}
|
|
19
|
+
{{#if (eq shadcn.iconLibrary "lucide")}}"lucide-react": "^0.469.0",{{/if}}
|
|
20
|
+
{{#if (eq shadcn.iconLibrary "tabler")}}"@tabler/icons-react": "^3.31.0",{{/if}}
|
|
21
|
+
{{#if (eq shadcn.iconLibrary "phosphor")}}"@phosphor-icons/react": "^2.1.7",{{/if}}
|
|
19
22
|
"class-variance-authority": "^0.7.0",
|
|
20
23
|
"clsx": "^2.1.0",
|
|
21
24
|
"tailwind-merge": "^2.5.0"
|
|
@@ -7,14 +7,15 @@
|
|
|
7
7
|
"config": "",
|
|
8
8
|
"css": "src/app/globals.css",
|
|
9
9
|
"baseColor": "{{shadcn.baseColor}}",
|
|
10
|
-
"cssVariables": true
|
|
10
|
+
"cssVariables": true,
|
|
11
|
+
"prefix": ""
|
|
11
12
|
},
|
|
12
|
-
"iconLibrary": "{{shadcn.iconLibrary}}",
|
|
13
13
|
"aliases": {
|
|
14
14
|
"components": "@/components",
|
|
15
15
|
"utils": "@/lib/utils",
|
|
16
16
|
"ui": "@/components/ui",
|
|
17
17
|
"lib": "@/lib",
|
|
18
18
|
"hooks": "@/hooks"
|
|
19
|
-
}
|
|
19
|
+
},
|
|
20
|
+
"iconLibrary": "{{shadcn.iconLibrary}}"
|
|
20
21
|
}
|
|
@@ -18,14 +18,18 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
{{#if (eq structure 'monorepo')}} "@repo/backend": "workspace:*",
|
|
21
|
+
"@repo/ui": "workspace:*",
|
|
21
22
|
{{/if}} "next": "^16.0.0",
|
|
22
23
|
"react": "^19.0.0",
|
|
23
24
|
"react-dom": "^19.0.0",
|
|
24
25
|
"convex": "^1.25.0",
|
|
25
26
|
"@convex-dev/better-auth": "^0.10.0",
|
|
26
27
|
"better-auth": "1.4.9",
|
|
27
|
-
"@hugeicons/react": "^0.3.0",
|
|
28
|
-
"
|
|
28
|
+
{{#unless (eq structure 'monorepo')}}{{#if (eq shadcn.iconLibrary "hugeicons")}} "@hugeicons/react": "^0.3.0",
|
|
29
|
+
{{/if}}{{#if (eq shadcn.iconLibrary "lucide")}} "lucide-react": "^0.469.0",
|
|
30
|
+
{{/if}}{{#if (eq shadcn.iconLibrary "tabler")}} "@tabler/icons-react": "^3.31.0",
|
|
31
|
+
{{/if}}{{#if (eq shadcn.iconLibrary "phosphor")}} "@phosphor-icons/react": "^2.1.7",
|
|
32
|
+
{{/if}}{{/unless}} "class-variance-authority": "^0.7.0",
|
|
29
33
|
"clsx": "^2.1.0",
|
|
30
34
|
"tailwind-merge": "^2.5.0",
|
|
31
35
|
"tw-animate-css": "^1.3.0",
|
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
"incremental": true,
|
|
16
16
|
"plugins": [{ "name": "next" }],
|
|
17
17
|
"paths": {
|
|
18
|
-
"
|
|
18
|
+
{{#if (eq structure 'monorepo')}} "@/components/ui/*": ["../../packages/ui/src/components/ui/*"],
|
|
19
|
+
"@/lib/utils": ["../../packages/ui/src/lib/utils"],
|
|
20
|
+
{{/if}} "@/*": ["./src/*"]
|
|
19
21
|
}
|
|
20
22
|
},
|
|
21
23
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", ".next/dev/types/**/*.ts"],
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
-
"style": "new-york",
|
|
4
|
-
"rsc": false,
|
|
5
|
-
"tsx": true,
|
|
6
|
-
"tailwind": {
|
|
7
|
-
"config": "",
|
|
8
|
-
"css": "",
|
|
9
|
-
"baseColor": "{{shadcn.baseColor}}",
|
|
10
|
-
"cssVariables": true
|
|
11
|
-
},
|
|
12
|
-
"iconLibrary": "{{shadcn.iconLibrary}}",
|
|
13
|
-
"aliases": {
|
|
14
|
-
"components": "@/components",
|
|
15
|
-
"utils": "@/lib/utils",
|
|
16
|
-
"ui": "@/components/ui",
|
|
17
|
-
"lib": "@/lib",
|
|
18
|
-
"hooks": "@/hooks"
|
|
19
|
-
}
|
|
20
|
-
}
|