@amirulabu/create-recurring-rabbit-app 0.2.19 → 0.2.26

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.
Files changed (34) hide show
  1. package/README.md +3 -3
  2. package/dist/index.js +146 -61
  3. package/package.json +7 -2
  4. package/templates/default/.npmrc +5 -0
  5. package/templates/default/.nvmrc +1 -0
  6. package/templates/default/README.md +3 -3
  7. package/templates/default/postcss.config.js +5 -0
  8. package/templates/default/src/components/ui/button.tsx +1 -1
  9. package/templates/default/src/components/ui/input.tsx +1 -1
  10. package/templates/default/src/{app/globals.css → globals.css} +4 -5
  11. package/templates/default/src/{app → routes}/__root.tsx +1 -3
  12. package/templates/default/src/{app → routes}/api/auth/get-session.ts +12 -10
  13. package/templates/default/src/routes/api/trpc.ts +19 -0
  14. package/templates/default/src/server/api/routers/user.ts +44 -0
  15. package/templates/default/src/server/db/seed.ts +5 -0
  16. package/templates/default/tsconfig.json +2 -7
  17. package/templates/default/vite.config.ts +23 -0
  18. package/templates/default/app.config.ts +0 -29
  19. package/templates/default/src/app/api/trpc.server.ts +0 -12
  20. package/templates/default/src/app/client.tsx +0 -9
  21. package/templates/default/src/app/ssr.tsx +0 -9
  22. package/templates/default/tailwind.config.js +0 -46
  23. /package/templates/default/src/{app/routeTree.gen.ts → routeTree.gen.ts} +0 -0
  24. /package/templates/default/src/{app/router.ts → router.ts} +0 -0
  25. /package/templates/default/src/{app → routes}/api/auth/$.ts +0 -0
  26. /package/templates/default/src/{app → routes}/api/health.ts +0 -0
  27. /package/templates/default/src/{app → routes}/auth/forgot-password.tsx +0 -0
  28. /package/templates/default/src/{app → routes}/auth/login.tsx +0 -0
  29. /package/templates/default/src/{app → routes}/auth/register.tsx +0 -0
  30. /package/templates/default/src/{app → routes}/auth/reset-password.tsx +0 -0
  31. /package/templates/default/src/{app → routes}/auth/verify-email.tsx +0 -0
  32. /package/templates/default/src/{app → routes}/dashboard/index.tsx +0 -0
  33. /package/templates/default/src/{app → routes}/dashboard/settings.tsx +0 -0
  34. /package/templates/default/src/{app → routes}/index.tsx +0 -0
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > A production-ready CLI tool that scaffolds an opinionated micro‑SaaS starter with TanStack Start, tRPC, Drizzle, Better-auth, and Tailwind CSS.
4
4
 
5
- **Current Version**: v0.2.13 | **Status**: ✅ Production Ready - Ready for Public Launch
5
+ **Current Version**: v0.2.34 | **Status**: ✅ Production Ready - Ready for Public Launch
6
6
 
7
7
  ## Quick Start
8
8
 
@@ -29,7 +29,7 @@ The generated starter includes everything you need to build a micro-SaaS:
29
29
  - 🔧 **Developer Tools** - Hot reload, ESLint, Prettier, bundle analyzer, performance monitoring
30
30
  - 📦 **CI/CD** - GitHub Actions workflows for automated testing and validation
31
31
  - 📝 **Documentation** - Comprehensive guides, ADRs, and JSDoc comments
32
- - 🧪 **Testing** - Vitest integration with 21 passing tests
32
+ - 🧪 **Testing** - Vitest integration with 30 passing tests
33
33
 
34
34
  ## CLI Usage
35
35
 
@@ -67,7 +67,7 @@ create-recurring-rabbit-app/
67
67
  │ └── templates/ # App templates
68
68
  │ └── default/ # Default template
69
69
  │ ├── src/
70
- │ │ ├── app/ # TanStack Start routes
70
+ │ │ ├── routes/ # TanStack Start routes
71
71
  │ │ ├── server/ # tRPC + DB + Auth
72
72
  │ │ ├── components/ # UI components
73
73
  │ │ └── lib/ # Shared utilities
package/dist/index.js CHANGED
@@ -36,9 +36,9 @@ async function generatePackageJson(targetDir, config) {
36
36
  description: config.description ?? "A micro-SaaS built with TanStack Start, tRPC, and Drizzle",
37
37
  type: "module",
38
38
  scripts: {
39
- dev: "vinxi dev",
40
- build: "vinxi build",
41
- start: "vinxi start",
39
+ dev: "vite dev --port 3000",
40
+ build: "vite build",
41
+ start: "node .output/server/index.mjs",
42
42
  test: "vitest",
43
43
  "test:run": "vitest run",
44
44
  "db:generate": "drizzle-kit generate",
@@ -50,35 +50,37 @@ async function generatePackageJson(targetDir, config) {
50
50
  lint: "eslint .",
51
51
  "lint:fix": "eslint . --fix",
52
52
  format: 'prettier --write "src/**/*.{ts,tsx,json,css}"',
53
- clean: "rm -rf .vinxi dist data/*.db data/*.db-shm data/*.db-wal",
54
- "build:analyze": "ANALYZE=true vinxi build",
53
+ clean: "rm -rf .vinxi dist node_modules/.vite data/*.db data/*.db-shm data/*.db-wal",
55
54
  lighthouse: "lhci autorun",
56
55
  ...config.scripts
57
56
  },
57
+ engines: {
58
+ node: ">=22.0.0",
59
+ pnpm: ">=9.0.0"
60
+ },
58
61
  dependencies: {
59
62
  "@paralleldrive/cuid2": "^2.2.0",
60
63
  "@radix-ui/react-slot": "^1.0.2",
61
64
  "@tanstack/react-query": "^5.0.0",
62
- "@tanstack/react-router": "~1.120.0",
65
+ "@tanstack/react-router": "~1.121.0",
63
66
  "@tanstack/react-query-devtools": "^5.0.0",
64
- "@tanstack/start": "^1.120.0",
65
- "@trpc/client": "^11.0.0",
66
- "@trpc/react-query": "^11.0.0",
67
- "@trpc/server": "^11.0.0",
67
+ "@tanstack/react-start": "~1.121.0",
68
+ "@trpc/client": "^11.8.1",
69
+ "@trpc/react-query": "^11.8.1",
70
+ "@trpc/server": "^11.8.1",
68
71
  "@t3-oss/env-core": "^0.10.0",
69
- "better-auth": "^1.0.0",
72
+ "better-auth": "^1.4.17",
70
73
  "better-sqlite3": "^12.0.0",
71
74
  "class-variance-authority": "^0.7.0",
72
75
  clsx: "^2.1.0",
73
76
  dotenv: "^16.4.0",
74
- "drizzle-orm": "^0.41.0",
77
+ "drizzle-orm": "^0.45.1",
75
78
  postgres: "^3.4.0",
76
79
  react: "^18.2.0",
77
80
  "react-dom": "^18.2.0",
78
81
  resend: "^3.2.0",
79
82
  "tailwind-merge": "^2.2.0",
80
- "tailwindcss-animate": "^1.0.7",
81
- zod: "^3.22.0",
83
+ zod: "^3.22.4",
82
84
  ...config.dependencies
83
85
  },
84
86
  devDependencies: {
@@ -95,38 +97,40 @@ async function generatePackageJson(targetDir, config) {
95
97
  eslint: "^9.39.2",
96
98
  "eslint-plugin-react": "^7.37.0",
97
99
  "eslint-plugin-react-hooks": "^5.0.0",
98
- "drizzle-kit": "^0.31.0",
100
+ "drizzle-kit": "^0.31.8",
99
101
  globals: "^15.0.0",
100
- postcss: "^8.4.0",
102
+ "@tailwindcss/postcss": "^4.0.0",
101
103
  prettier: "^3.2.5",
102
104
  "rollup-plugin-visualizer": "^5.12.0",
103
- tailwindcss: "^3.4.0",
105
+ tailwindcss: "^4.1.18",
104
106
  tsx: "^4.7.0",
105
107
  typescript: "^5.3.3",
106
- vinxi: "^0.3.0",
107
- vitest: "^1.6.0",
108
+ vite: "6",
109
+ "vite-tsconfig-paths": "^4.0.0",
110
+ vitest: "^4.0.18",
108
111
  ...config.devDependencies
109
112
  },
110
113
  pnpm: {
111
114
  overrides: {
112
- "@tanstack/react-router": "~1.120.0",
113
- "@tanstack/react-start-client": "~1.120.0",
114
- "@tanstack/react-start-plugin": "~1.120.0",
115
- "@tanstack/react-start-server": "~1.120.0",
116
- "@tanstack/router-core": "~1.120.0",
117
- "@tanstack/router-generator": "~1.120.0",
118
- "@tanstack/router-plugin": "~1.120.0",
119
- "@tanstack/start-client-core": "~1.120.0",
120
- "@tanstack/start-plugin-core": "~1.120.0",
121
- "@tanstack/start-server-core": "~1.120.0",
122
- "@tanstack/server-functions-plugin": "~1.120.0",
123
- "@tanstack/directive-functions-plugin": "~1.120.0",
124
- "@tanstack/start-api-routes": "~1.120.0",
125
- "@tanstack/start-server-functions-client": "~1.120.0",
126
- "@tanstack/start-server-functions-fetcher": "~1.120.0",
127
- "@tanstack/start-server-functions-handler": "~1.120.0",
128
- "@tanstack/router-utils": "~1.120.0",
129
- "@tanstack/history": "~1.120.0"
115
+ zod: "^3.22.4",
116
+ "@tanstack/react-router": "~1.121.0",
117
+ "@tanstack/react-start-client": "~1.121.0",
118
+ "@tanstack/react-start-plugin": "~1.121.0",
119
+ "@tanstack/react-start-server": "~1.121.0",
120
+ "@tanstack/router-core": "~1.121.0",
121
+ "@tanstack/router-generator": "~1.121.0",
122
+ "@tanstack/router-plugin": "~1.121.0",
123
+ "@tanstack/start-client-core": "~1.121.0",
124
+ "@tanstack/start-plugin-core": "~1.121.0",
125
+ "@tanstack/start-server-core": "~1.121.0",
126
+ "@tanstack/server-functions-plugin": "~1.121.0",
127
+ "@tanstack/directive-functions-plugin": "~1.121.0",
128
+ "@tanstack/start-api-routes": "~1.121.0",
129
+ "@tanstack/start-server-functions-client": "~1.121.0",
130
+ "@tanstack/start-server-functions-fetcher": "~1.121.0",
131
+ "@tanstack/start-server-functions-handler": "~1.121.0",
132
+ "@tanstack/router-utils": "~1.121.0",
133
+ "@tanstack/history": "~1.121.0"
130
134
  }
131
135
  }
132
136
  };
@@ -167,7 +171,7 @@ async function detectPackageManager() {
167
171
  function getInstallCommand(packageManager) {
168
172
  switch (packageManager) {
169
173
  case "pnpm":
170
- return "pnpm install --ignore-workspace";
174
+ return "pnpm install --ignore-workspace --shamefully-hoist=false";
171
175
  case "yarn":
172
176
  return "yarn";
173
177
  case "npm":
@@ -187,29 +191,17 @@ function installDependencies(projectPath, packageManager) {
187
191
  if (packageManager === "pnpm") {
188
192
  const rebuildSpinner = ora("Building native modules...").start();
189
193
  try {
190
- execSync("pnpm rebuild better-sqlite3", {
191
- cwd: projectPath,
194
+ execSync("npx node-gyp rebuild", {
195
+ cwd: `${projectPath}/node_modules/better-sqlite3`,
192
196
  stdio: "inherit"
193
197
  });
194
198
  rebuildSpinner.succeed(chalk2.green("\u2713 Native modules built"));
195
199
  } catch {
196
- rebuildSpinner.warn(
197
- chalk2.yellow("\u26A0 Native module build failed, trying alternative method...")
200
+ rebuildSpinner.fail(chalk2.red("\u2717 Native module build failed"));
201
+ throw new Error(
202
+ `Failed to build native modules. This is required for database functionality.
203
+ Try running manually: cd ${projectPath}/node_modules/better-sqlite3 && npx node-gyp rebuild`
198
204
  );
199
- try {
200
- execSync("npx node-gyp rebuild --directory node_modules/better-sqlite3", {
201
- cwd: projectPath,
202
- stdio: "inherit"
203
- });
204
- rebuildSpinner.succeed(chalk2.green("\u2713 Native modules built"));
205
- } catch {
206
- rebuildSpinner.fail(chalk2.red("\u2717 Native module build failed"));
207
- throw new Error(
208
- `Failed to build native modules. This is required for database functionality.
209
- Try running manually: cd ${projectPath} && pnpm rebuild better-sqlite3
210
- Or use npm instead: cd ${projectPath} && npm install`
211
- );
212
- }
213
205
  }
214
206
  }
215
207
  } catch {
@@ -223,10 +215,10 @@ function validateNodeVersion() {
223
215
  const nodeVersion = process.version.replace("v", "");
224
216
  const versionParts = nodeVersion.split(".");
225
217
  const majorVersion = parseInt(versionParts[0] ?? "0", 10);
226
- const validVersions = [18, 20, 22];
218
+ const validVersions = [22, 24];
227
219
  if (!validVersions.includes(majorVersion)) {
228
220
  throw new Error(
229
- `Node.js v${majorVersion} is not supported. Please use Node.js 18, 20, or 22. Current version: v${nodeVersion}`
221
+ `Node.js v${majorVersion} is not supported. Please use Node.js 22 or 24 (latest LTS versions). Current version: v${nodeVersion}`
230
222
  );
231
223
  }
232
224
  }
@@ -519,6 +511,47 @@ async function cleanupProject(projectPath) {
519
511
  console.warn(`Warning: Could not cleanup ${projectPath}: ${error.message}`);
520
512
  }
521
513
  }
514
+ function startDevServer(options) {
515
+ const { projectPath, packageManager, openBrowser = true } = options;
516
+ const spinner = ora("Starting development server...").start();
517
+ try {
518
+ const devCommand = packageManager === "npm" ? "npm" : packageManager;
519
+ const args = ["run", "dev"];
520
+ const devServer = spawn(devCommand, args, {
521
+ cwd: projectPath,
522
+ stdio: "inherit",
523
+ shell: true
524
+ });
525
+ devServer.stdout?.on("data", (data) => {
526
+ const output = data.toString();
527
+ if (output.includes("Local:") || output.includes("listening")) {
528
+ spinner.succeed(chalk2.green("\u2713 Development server started"));
529
+ console.log("\n" + output.trim());
530
+ if (openBrowser) {
531
+ setTimeout(() => {
532
+ const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
533
+ spawn(openCmd, ["http://localhost:3000"], { stdio: "ignore", shell: true });
534
+ }, 2e3);
535
+ }
536
+ }
537
+ });
538
+ devServer.stderr?.on("data", (data) => {
539
+ console.error(data.toString());
540
+ });
541
+ devServer.on("error", (error) => {
542
+ spinner.fail(chalk2.red("\u2717 Failed to start development server"));
543
+ throw new Error(`Dev server error: ${error.message}`);
544
+ });
545
+ devServer.on("close", (code) => {
546
+ if (code !== 0 && code !== null) {
547
+ spinner.fail(chalk2.red("\u2717 Development server exited with error"));
548
+ }
549
+ });
550
+ } catch (error) {
551
+ spinner.fail(chalk2.red("\u2717 Failed to start development server"));
552
+ throw error;
553
+ }
554
+ }
522
555
 
523
556
  // src/commands/create.ts
524
557
  var getPackageRoot = () => {
@@ -597,6 +630,16 @@ async function initProjectDatabase(projectPath) {
597
630
  throw error;
598
631
  }
599
632
  }
633
+ function startDevelopmentServer(projectPath, packageManager) {
634
+ try {
635
+ startDevServer({ projectPath, packageManager, openBrowser: true });
636
+ } catch (error) {
637
+ console.error("\nFailed to start development server. You can start it manually by running:");
638
+ console.log(` cd ${projectPath}`);
639
+ console.log(` ${packageManager === "npm" ? "npm run dev" : `${packageManager} dev`}`);
640
+ throw error;
641
+ }
642
+ }
600
643
  function displaySuccess(projectName, packageManager) {
601
644
  console.log(getSuccessMessage());
602
645
  }
@@ -606,11 +649,45 @@ function displayError(error) {
606
649
  console.log(getTroubleshootingTips());
607
650
  }
608
651
  }
609
-
610
- // src/index.ts
652
+ var MIN_NODE_VERSION = 22;
653
+ var SUPPORTED_VERSIONS = [22, 24];
654
+ function checkNodeVersion() {
655
+ const nodeVersion = process.version.slice(1);
656
+ const majorVersion = parseInt(nodeVersion.split(".")[0] || "0", 10);
657
+ if (majorVersion < MIN_NODE_VERSION) {
658
+ console.error(
659
+ chalk2.red(
660
+ "\u274C Error: create-recurring-rabbit-stack requires Node.js 22 or higher (LTS versions only)"
661
+ )
662
+ );
663
+ console.error(chalk2.red(`Current version: v${nodeVersion}`));
664
+ console.error(chalk2.yellow("\nPlease upgrade to Node.js 22.x or 24.x (LTS versions)"));
665
+ console.error(chalk2.gray("\nQuick upgrade using nvm:"));
666
+ console.error(chalk2.gray(" nvm install 24"));
667
+ console.error(chalk2.gray(" nvm use 24"));
668
+ console.error(chalk2.gray("\nOr download from: https://nodejs.org/"));
669
+ process.exit(1);
670
+ }
671
+ const isSupported = SUPPORTED_VERSIONS.includes(majorVersion);
672
+ if (!isSupported) {
673
+ console.warn(
674
+ chalk2.yellow(
675
+ `\u26A0\uFE0F Warning: Node.js v${nodeVersion} is not an officially supported LTS version`
676
+ )
677
+ );
678
+ console.warn(chalk2.yellow(`Supported versions: ${SUPPORTED_VERSIONS.join(", ")}`));
679
+ console.warn(chalk2.gray("\nThe CLI may still work, but issues may occur."));
680
+ if (process.env.CI === "true") {
681
+ console.error(chalk2.red("\n\u274C CI/CD requires Node.js 22 or 24"));
682
+ process.exit(1);
683
+ }
684
+ }
685
+ console.log(chalk2.green(`\u2705 Node.js version check passed: v${nodeVersion}`));
686
+ }
687
+ checkNodeVersion();
611
688
  program.name("create-recurring-rabbit-app").description(
612
689
  "Scaffold a production-ready micro-SaaS with TanStack Start, tRPC, Drizzle, and Better-auth"
613
- ).version("0.0.0-alpha").argument("[app-name]", "Name of your application", "my-saas-app").action(async (appName) => {
690
+ ).version("0.0.0-alpha").argument("[app-name]", "Name of your application", "my-saas-app").option("--no-server", "Skip starting development server (useful for testing)").action(async (appName, options) => {
614
691
  let hasError = false;
615
692
  try {
616
693
  const targetPath = process.cwd();
@@ -618,6 +695,14 @@ program.name("create-recurring-rabbit-app").description(
618
695
  await installProjectDependencies(projectPath, packageManager);
619
696
  await initProjectDatabase(projectPath);
620
697
  displaySuccess(appName, packageManager);
698
+ if (options.server !== false) {
699
+ console.log(chalk2.cyan("\nStarting development server..."));
700
+ console.log(chalk2.gray("Press Ctrl+C to stop the server\n"));
701
+ startDevelopmentServer(projectPath, packageManager);
702
+ } else {
703
+ console.log(chalk2.gray("\nSkipping development server start (--no-server flag set)\n"));
704
+ process.exit(0);
705
+ }
621
706
  } catch (error) {
622
707
  hasError = true;
623
708
  if (error instanceof CLIError) {
package/package.json CHANGED
@@ -1,11 +1,15 @@
1
1
  {
2
2
  "name": "@amirulabu/create-recurring-rabbit-app",
3
- "version": "0.2.19",
3
+ "version": "0.2.26",
4
4
  "description": "CLI tool to scaffold micro-SaaS apps with TanStack Start, tRPC, Drizzle, and Better-auth",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "create-recurring-rabbit-app": "./bin/index.js"
8
8
  },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/amirulabu/recurring-rabbit-app.git"
12
+ },
9
13
  "publishConfig": {
10
14
  "access": "public"
11
15
  },
@@ -35,7 +39,8 @@
35
39
  "@vitest/ui": "^1.0.4"
36
40
  },
37
41
  "engines": {
38
- "node": ">=18.0.0"
42
+ "node": ">=22.0.0",
43
+ "pnpm": ">=9.0.0"
39
44
  },
40
45
  "scripts": {
41
46
  "dev": "tsx src/index.ts",
@@ -0,0 +1,5 @@
1
+ enable-pre-post-scripts=true
2
+ enable-scripts=true
3
+ shamefully-hoist=false
4
+ pnpm.approvedBuildScripts=better-sqlite3,esbuild,@parcel/watcher,node-gyp
5
+
@@ -0,0 +1 @@
1
+ 24
@@ -27,7 +27,7 @@ pnpm dev
27
27
 
28
28
  ```
29
29
  src/
30
- ├── app/ # TanStack Start routes
30
+ ├── routes/ # TanStack Start routes
31
31
  ├── server/ # tRPC API and database
32
32
  ├── components/ # Reusable UI components
33
33
  └── lib/ # Shared utilities
@@ -88,7 +88,7 @@ Analyze your bundle size to identify large dependencies and optimize performance
88
88
  pnpm build:analyze
89
89
  ```
90
90
 
91
- This will build your application and generate a `stats.html` file in build output directory (`.vinxi`). Open this file in your browser to explore:
91
+ This will build your application and generate a `stats.html` file in build output directory (`.output`). Open this file in your browser to explore:
92
92
 
93
93
  - **Treemap view** - Visual representation of module sizes
94
94
  - **Gzip/Brotli sizes** - Real-world compression impact
@@ -202,7 +202,7 @@ export const postsRouter = router({
202
202
 
203
203
  **New Pages**
204
204
 
205
- 1. Add route file in `src/app/[route].tsx`
205
+ 1. Add route file in `src/routes/[route].tsx`
206
206
  2. Implement component with TanStack Start conventions
207
207
  3. Add navigation in layout components
208
208
 
@@ -0,0 +1,5 @@
1
+ export default {
2
+ plugins: {
3
+ '@tailwindcss/postcss': {},
4
+ },
5
+ }
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from 'class-variance-authority'
5
5
  import { cn } from '@/lib/utils'
6
6
 
7
7
  const buttonVariants = cva(
8
- 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
8
+ 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
9
9
  {
10
10
  variants: {
11
11
  variant: {
@@ -10,7 +10,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
10
10
  <input
11
11
  type={type}
12
12
  className={cn(
13
- 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
13
+ 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
14
14
  className
15
15
  )}
16
16
  ref={ref}
@@ -1,6 +1,4 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
1
+ @import 'tailwindcss';
4
2
 
5
3
  @layer base {
6
4
  :root {
@@ -47,9 +45,10 @@
47
45
 
48
46
  @layer base {
49
47
  * {
50
- @apply border-border;
48
+ border-color: hsl(var(--border));
51
49
  }
52
50
  body {
53
- @apply bg-background text-foreground;
51
+ background-color: hsl(var(--background));
52
+ color: hsl(var(--foreground));
54
53
  }
55
54
  }
@@ -5,14 +5,12 @@ import { useState } from 'react'
5
5
  import { httpBatchLink } from '@trpc/client'
6
6
  import { TRPCProvider, trpc } from '@/lib/api'
7
7
  import { env } from '@/lib/env'
8
- import '@/app/globals.css'
8
+ import '@/globals.css'
9
9
 
10
10
  export const Route = createRootRoute({
11
11
  component: RootComponent,
12
12
  })
13
13
 
14
- export const rootRoute = Route
15
-
16
14
  function RootComponent() {
17
15
  const [queryClient] = useState(() => new QueryClient())
18
16
  const [trpcClient] = useState(() =>
@@ -11,16 +11,18 @@ import { auth } from '@/server/auth/config'
11
11
  */
12
12
  export const Route = createFileRoute('/api/auth/get-session')({
13
13
  server: {
14
- handler: async ({ request }: { request: Request }) => {
15
- try {
16
- const session = await auth.api.getSession({
17
- headers: request.headers,
18
- })
19
- return Response.json(session)
20
- } catch (error) {
21
- console.error('Failed to get session:', error)
22
- return Response.json({ user: null, session: null }, { status: 401 })
23
- }
14
+ handlers: {
15
+ GET: async ({ request }: { request: Request }) => {
16
+ try {
17
+ const session = await auth.api.getSession({
18
+ headers: request.headers,
19
+ })
20
+ return Response.json(session)
21
+ } catch (error) {
22
+ console.error('Failed to get session:', error)
23
+ return Response.json({ user: null, session: null }, { status: 401 })
24
+ }
25
+ },
24
26
  },
25
27
  },
26
28
  } as any)
@@ -0,0 +1,19 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { fetchRequestHandler } from '@trpc/server/adapters/fetch'
3
+ import { appRouter } from '@/server/api/root'
4
+ import { createTRPCContext } from '@/server/api/trpc'
5
+
6
+ export const Route = createFileRoute('/api/trpc', {
7
+ server: {
8
+ handlers: {
9
+ POST: async ({ request }: { request: Request }) => {
10
+ return await fetchRequestHandler({
11
+ endpoint: '/api/trpc',
12
+ req: request,
13
+ router: appRouter,
14
+ createContext: (opts) => createTRPCContext(opts),
15
+ })
16
+ },
17
+ },
18
+ },
19
+ } as any)
@@ -4,7 +4,30 @@ import { db } from '@/server/db'
4
4
  import { users } from '@/server/db/schema'
5
5
  import { eq } from 'drizzle-orm'
6
6
 
7
+ /**
8
+ * User Router
9
+ *
10
+ * Handles user profile operations including retrieval and updates.
11
+ * All procedures are protected and require authenticated sessions.
12
+ *
13
+ * @module routers/user
14
+ */
15
+
7
16
  export const userRouter = router({
17
+ /**
18
+ * Get Profile
19
+ *
20
+ * Retrieves the authenticated user's profile from the database.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * const profile = await trpc.user.getProfile.query()
25
+ * // Returns: { id: string, name: string, email: string, ... }
26
+ * ```
27
+ *
28
+ * @throws Will throw if user session is invalid or database query fails
29
+ * @returns The user's profile object or null if not found
30
+ */
8
31
  getProfile: protectedProcedure.query(async ({ ctx }) => {
9
32
  const result = await (db as any)
10
33
  .select()
@@ -14,6 +37,27 @@ export const userRouter = router({
14
37
  return result[0] ?? null
15
38
  }),
16
39
 
40
+ /**
41
+ * Update Profile
42
+ *
43
+ * Updates the authenticated user's profile name.
44
+ *
45
+ * @param input.name - The new display name (1-100 characters)
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * const updatedProfile = await trpc.user.updateProfile.mutate({
50
+ * name: 'John Doe'
51
+ * })
52
+ * // Returns: { id: string, name: 'John Doe', email: string, ... }
53
+ * ```
54
+ *
55
+ * @throws Will throw if:
56
+ * - Name validation fails (not 1-100 characters)
57
+ * - User session is invalid
58
+ * - Database update fails
59
+ * @returns The updated user profile object
60
+ */
17
61
  updateProfile: protectedProcedure
18
62
  .input(
19
63
  z.object({
@@ -123,3 +123,8 @@ export async function seedDatabase() {
123
123
  }
124
124
  }
125
125
  }
126
+
127
+ seedDatabase().catch((error) => {
128
+ console.error('Failed to seed database:', error)
129
+ process.exit(1)
130
+ })
@@ -8,7 +8,7 @@
8
8
  "resolveJsonModule": true,
9
9
  "allowJs": true,
10
10
  "checkJs": false,
11
- "outDir": "./.vinxi/out",
11
+ "outDir": "./dist",
12
12
  "rootDir": "./src",
13
13
  "removeComments": true,
14
14
  "noEmit": true,
@@ -27,15 +27,10 @@
27
27
  "noImplicitOverride": true,
28
28
  "esModuleInterop": true,
29
29
  "skipLibCheck": true,
30
- "plugins": [
31
- {
32
- "name": "@tanstack/start/plugin"
33
- }
34
- ],
35
30
  "paths": {
36
31
  "@/*": ["./src/*"]
37
32
  }
38
33
  },
39
34
  "include": ["src/**/*"],
40
- "exclude": ["node_modules", "dist", ".vinxi"]
35
+ "exclude": ["node_modules", "dist", ".vinxi", ".vite"]
41
36
  }
@@ -0,0 +1,23 @@
1
+ import { defineConfig } from 'vite'
2
+ import tsconfigPaths from 'vite-tsconfig-paths'
3
+ import { visualizer } from 'rollup-plugin-visualizer'
4
+ import { tanstackStart } from '@tanstack/react-start/plugin/vite'
5
+
6
+ export default defineConfig({
7
+ plugins: [
8
+ tsconfigPaths(),
9
+ tanstackStart(),
10
+ visualizer({
11
+ filename: 'stats.html',
12
+ open: process.env.ANALYZE === 'true',
13
+ gzipSize: true,
14
+ brotliSize: true,
15
+ template: 'treemap',
16
+ }),
17
+ ],
18
+ resolve: {
19
+ alias: {
20
+ '@': '/src',
21
+ },
22
+ },
23
+ })
@@ -1,29 +0,0 @@
1
- import { defineConfig } from '@tanstack/start/config'
2
- import { visualizer } from 'rollup-plugin-visualizer'
3
- import path from 'path'
4
- import { fileURLToPath } from 'url'
5
-
6
- const __dirname = path.dirname(fileURLToPath(import.meta.url))
7
-
8
- export default defineConfig({
9
- tsr: {
10
- appDirectory: 'src/app',
11
- routesDirectory: 'src/app',
12
- },
13
- vite: {
14
- plugins: [
15
- visualizer({
16
- filename: 'stats.html',
17
- open: process.env.ANALYZE === 'true',
18
- gzipSize: true,
19
- brotliSize: true,
20
- template: 'treemap',
21
- }),
22
- ],
23
- resolve: {
24
- alias: {
25
- '@': path.resolve(__dirname, './src'),
26
- },
27
- },
28
- },
29
- })
@@ -1,12 +0,0 @@
1
- import { fetchRequestHandler } from '@trpc/server/adapters/fetch'
2
- import { appRouter } from '@/server/api/root'
3
- import { createTRPCContext } from '@/server/api/trpc'
4
-
5
- export default async function handler(req: Request) {
6
- return fetchRequestHandler({
7
- endpoint: '/api/trpc',
8
- req,
9
- router: appRouter,
10
- createContext: (opts) => createTRPCContext(opts),
11
- })
12
- }
@@ -1,9 +0,0 @@
1
- import { createFileRoute } from '@tanstack/react-router'
2
-
3
- export const Route = createFileRoute('/_client')({
4
- component: () => null,
5
- })
6
-
7
- export default function handler() {
8
- return new Response('Not implemented')
9
- }
@@ -1,9 +0,0 @@
1
- import { createFileRoute } from '@tanstack/react-router'
2
-
3
- export const Route = createFileRoute('/_ssr')({
4
- component: () => null,
5
- })
6
-
7
- export default function handler() {
8
- return new Response('Not implemented')
9
- }
@@ -1,46 +0,0 @@
1
- /** @type {import('tailwindcss').Config} */
2
- module.exports = {
3
- darkMode: ['class'],
4
- content: ['./src/**/*.{ts,tsx}'],
5
- theme: {
6
- extend: {
7
- colors: {
8
- border: 'hsl(var(--border))',
9
- input: 'hsl(var(--input))',
10
- ring: 'hsl(var(--ring))',
11
- background: 'hsl(var(--background))',
12
- foreground: 'hsl(var(--foreground))',
13
- primary: {
14
- DEFAULT: 'hsl(var(--primary))',
15
- foreground: 'hsl(var(--primary-foreground))',
16
- },
17
- secondary: {
18
- DEFAULT: 'hsl(var(--secondary))',
19
- foreground: 'hsl(var(--secondary-foreground))',
20
- },
21
- destructive: {
22
- DEFAULT: 'hsl(var(--destructive))',
23
- foreground: 'hsl(var(--destructive-foreground))',
24
- },
25
- muted: {
26
- DEFAULT: 'hsl(var(--muted))',
27
- foreground: 'hsl(var(--muted-foreground))',
28
- },
29
- accent: {
30
- DEFAULT: 'hsl(var(--accent))',
31
- foreground: 'hsl(var(--accent-foreground))',
32
- },
33
- card: {
34
- DEFAULT: 'hsl(var(--card))',
35
- foreground: 'hsl(var(--card-foreground))',
36
- },
37
- },
38
- borderRadius: {
39
- lg: 'var(--radius)',
40
- md: 'calc(var(--radius) - 2px)',
41
- sm: 'calc(var(--radius) - 4px)',
42
- },
43
- },
44
- },
45
- plugins: [require('tailwindcss-animate')],
46
- }
File without changes