@amirulabu/create-recurring-rabbit-app 0.2.25 → 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 (29) hide show
  1. package/README.md +3 -3
  2. package/dist/index.js +30 -30
  3. package/package.json +1 -1
  4. package/templates/default/.nvmrc +1 -0
  5. package/templates/default/README.md +3 -3
  6. package/templates/default/src/{app/globals.css → globals.css} +3 -2
  7. package/templates/default/src/{app → routes}/__root.tsx +1 -3
  8. package/templates/default/src/{app → routes}/api/auth/get-session.ts +12 -10
  9. package/templates/default/src/routes/api/trpc.ts +19 -0
  10. package/templates/default/src/server/api/routers/user.ts +44 -0
  11. package/templates/default/tsconfig.json +2 -7
  12. package/templates/default/vite.config.ts +23 -0
  13. package/templates/default/app.config.ts +0 -29
  14. package/templates/default/src/app/api/trpc.server.ts +0 -12
  15. package/templates/default/src/app/client.tsx +0 -9
  16. package/templates/default/src/app/ssr.tsx +0 -9
  17. package/templates/default/tailwind.config.js +0 -45
  18. /package/templates/default/src/{app/routeTree.gen.ts → routeTree.gen.ts} +0 -0
  19. /package/templates/default/src/{app/router.ts → router.ts} +0 -0
  20. /package/templates/default/src/{app → routes}/api/auth/$.ts +0 -0
  21. /package/templates/default/src/{app → routes}/api/health.ts +0 -0
  22. /package/templates/default/src/{app → routes}/auth/forgot-password.tsx +0 -0
  23. /package/templates/default/src/{app → routes}/auth/login.tsx +0 -0
  24. /package/templates/default/src/{app → routes}/auth/register.tsx +0 -0
  25. /package/templates/default/src/{app → routes}/auth/reset-password.tsx +0 -0
  26. /package/templates/default/src/{app → routes}/auth/verify-email.tsx +0 -0
  27. /package/templates/default/src/{app → routes}/dashboard/index.tsx +0 -0
  28. /package/templates/default/src/{app → routes}/dashboard/settings.tsx +0 -0
  29. /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,8 +50,7 @@ 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
  },
@@ -63,9 +62,9 @@ async function generatePackageJson(targetDir, config) {
63
62
  "@paralleldrive/cuid2": "^2.2.0",
64
63
  "@radix-ui/react-slot": "^1.0.2",
65
64
  "@tanstack/react-query": "^5.0.0",
66
- "@tanstack/react-router": "~1.120.0",
65
+ "@tanstack/react-router": "~1.121.0",
67
66
  "@tanstack/react-query-devtools": "^5.0.0",
68
- "@tanstack/start": "^1.120.0",
67
+ "@tanstack/react-start": "~1.121.0",
69
68
  "@trpc/client": "^11.8.1",
70
69
  "@trpc/react-query": "^11.8.1",
71
70
  "@trpc/server": "^11.8.1",
@@ -81,7 +80,7 @@ async function generatePackageJson(targetDir, config) {
81
80
  "react-dom": "^18.2.0",
82
81
  resend: "^3.2.0",
83
82
  "tailwind-merge": "^2.2.0",
84
- zod: "^4.3.6",
83
+ zod: "^3.22.4",
85
84
  ...config.dependencies
86
85
  },
87
86
  devDependencies: {
@@ -106,31 +105,32 @@ async function generatePackageJson(targetDir, config) {
106
105
  tailwindcss: "^4.1.18",
107
106
  tsx: "^4.7.0",
108
107
  typescript: "^5.3.3",
109
- vinxi: "^0.3.0",
108
+ vite: "6",
109
+ "vite-tsconfig-paths": "^4.0.0",
110
110
  vitest: "^4.0.18",
111
111
  ...config.devDependencies
112
112
  },
113
113
  pnpm: {
114
114
  overrides: {
115
- zod: "^4.3.6",
116
- "@tanstack/react-router": "~1.120.0",
117
- "@tanstack/react-start-client": "~1.120.0",
118
- "@tanstack/react-start-plugin": "~1.120.0",
119
- "@tanstack/react-start-server": "~1.120.0",
120
- "@tanstack/router-core": "~1.120.0",
121
- "@tanstack/router-generator": "~1.120.0",
122
- "@tanstack/router-plugin": "~1.120.0",
123
- "@tanstack/start-client-core": "~1.120.0",
124
- "@tanstack/start-plugin-core": "~1.120.0",
125
- "@tanstack/start-server-core": "~1.120.0",
126
- "@tanstack/server-functions-plugin": "~1.120.0",
127
- "@tanstack/directive-functions-plugin": "~1.120.0",
128
- "@tanstack/start-api-routes": "~1.120.0",
129
- "@tanstack/start-server-functions-client": "~1.120.0",
130
- "@tanstack/start-server-functions-fetcher": "~1.120.0",
131
- "@tanstack/start-server-functions-handler": "~1.120.0",
132
- "@tanstack/router-utils": "~1.120.0",
133
- "@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"
134
134
  }
135
135
  }
136
136
  };
@@ -215,10 +215,10 @@ function validateNodeVersion() {
215
215
  const nodeVersion = process.version.replace("v", "");
216
216
  const versionParts = nodeVersion.split(".");
217
217
  const majorVersion = parseInt(versionParts[0] ?? "0", 10);
218
- const validVersions = [18, 20, 22];
218
+ const validVersions = [22, 24];
219
219
  if (!validVersions.includes(majorVersion)) {
220
220
  throw new Error(
221
- `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}`
222
222
  );
223
223
  }
224
224
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amirulabu/create-recurring-rabbit-app",
3
- "version": "0.2.25",
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": {
@@ -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
 
@@ -45,9 +45,10 @@
45
45
 
46
46
  @layer base {
47
47
  * {
48
- @apply border-border;
48
+ border-color: hsl(var(--border));
49
49
  }
50
50
  body {
51
- @apply bg-background text-foreground;
51
+ background-color: hsl(var(--background));
52
+ color: hsl(var(--foreground));
52
53
  }
53
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({
@@ -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,45 +0,0 @@
1
- /** @type {import('tailwindcss').Config} */
2
- export default {
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
- }
File without changes