├── .DS_Store ├── .claude └── agents │ └── project-setup-guide.md ├── .gitignore ├── LICENSE ├── README.md ├── apps ├── data-service │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── package.json │ ├── service-bindings.d.ts │ ├── src │ │ ├── durable-objects │ │ │ └── example-durable-object.ts │ │ ├── hono │ │ │ └── app.ts │ │ ├── index.ts │ │ └── workflows │ │ │ └── example-workflow.ts │ ├── tsconfig.json │ ├── worker-configuration.d.ts │ └── wrangler.jsonc └── user-application │ ├── .claude │ └── agents │ │ ├── shadcn-ui-builder.md │ │ └── tanstack-server-functions.md │ ├── .cta.json │ ├── .mcp.json │ ├── .vscode │ └── settings.json │ ├── .wrangler │ └── deploy │ │ └── config.json │ ├── CLAUDE.md │ ├── README.md │ ├── components.json │ ├── package.json │ ├── pnpm-lock.yaml │ ├── public │ ├── better-auth.png │ ├── claude-code-cli.webp │ ├── cloudflare.png │ ├── docs │ │ ├── authentication.md │ │ ├── database.md │ │ └── polar.md │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ ├── pnpm.webp │ ├── polar-product-metadata.png │ ├── polar.png │ ├── robots.txt │ ├── shadcn.png │ └── tanstack.png │ ├── src │ ├── components │ │ ├── auth │ │ │ ├── account-dialog.tsx │ │ │ └── google-login.tsx │ │ ├── default-catch-boundary.tsx │ │ ├── demo │ │ │ ├── index.ts │ │ │ └── middleware-demo.tsx │ │ ├── landing │ │ │ ├── claude-code-section.tsx │ │ │ ├── course-promo-section.tsx │ │ │ ├── features-section.tsx │ │ │ ├── footer.tsx │ │ │ ├── hero-section.tsx │ │ │ └── index.ts │ │ ├── layout │ │ │ ├── header.tsx │ │ │ └── sidebar.tsx │ │ ├── navigation │ │ │ ├── index.ts │ │ │ └── navigation-bar.tsx │ │ ├── not-found.tsx │ │ ├── payments │ │ │ └── polar │ │ │ │ ├── index.ts │ │ │ │ ├── pricing-card.tsx │ │ │ │ ├── pricing-grid.tsx │ │ │ │ ├── types.ts │ │ │ │ └── use-checkout.ts │ │ ├── theme │ │ │ ├── index.ts │ │ │ ├── theme-provider.tsx │ │ │ └── theme-toggle.tsx │ │ └── ui │ │ │ ├── alert.tsx │ │ │ ├── avatar.tsx │ │ │ ├── badge.tsx │ │ │ ├── button.tsx │ │ │ ├── card.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── dialog.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── input.tsx │ │ │ ├── scroll-area.tsx │ │ │ └── sheet.tsx │ ├── core │ │ ├── functions │ │ │ ├── example-functions.ts │ │ │ └── payments.ts │ │ └── middleware │ │ │ ├── auth.ts │ │ │ ├── example-middleware.ts │ │ │ └── polar.ts │ ├── integrations │ │ └── tanstack-query │ │ │ ├── devtools.tsx │ │ │ └── root-provider.tsx │ ├── lib │ │ ├── auth-client.ts │ │ └── utils.ts │ ├── logo.svg │ ├── routeTree.gen.ts │ ├── router.tsx │ ├── routes │ │ ├── __root.tsx │ │ ├── _auth │ │ │ ├── app │ │ │ │ ├── index.tsx │ │ │ │ └── polar │ │ │ │ │ ├── checkout.success.tsx │ │ │ │ │ ├── portal.tsx │ │ │ │ │ └── subscriptions.tsx │ │ │ └── route.tsx │ │ ├── _static │ │ │ ├── docs │ │ │ │ ├── $name.tsx │ │ │ │ └── index.tsx │ │ │ └── route.tsx │ │ ├── api │ │ │ └── auth.$.tsx │ │ └── index.tsx │ ├── server.ts │ ├── start.tsx │ ├── styles.css │ └── utils │ │ └── seo.ts │ ├── tsconfig.json │ ├── vite.config.ts │ ├── worker-configuration.d.ts │ └── wrangler.jsonc ├── package.json ├── packages └── data-ops │ ├── .gitignore │ ├── config │ └── auth.ts │ ├── drizzle.config.ts │ ├── package.json │ ├── src │ ├── auth │ │ ├── server.ts │ │ └── setup.ts │ ├── database │ │ └── setup.ts │ ├── drizzle │ │ └── auth-schema.ts │ ├── queries │ │ └── polar.ts │ └── zod-schema │ │ ├── example.ts │ │ └── polar.ts │ └── tsconfig.json ├── pnpm-lock.yaml └── pnpm-workspace.yaml /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/saas-kit/8e1097e3b14e5b751e4f6d1a0227696889cccd88/.DS_Store -------------------------------------------------------------------------------- /.claude/agents/project-setup-guide.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: project-setup-guide 3 | description: Use this agent when the user needs help setting up the project, configuring databases, authentication, or environment variables. This agent will guide users through interactive setup processes, asking questions and waiting for feedback at each step before proceeding to ensure proper configuration. Examples: Context: User is starting work on the project and needs to set it up locally. user: 'I need to set up this project on my local machine' assistant: 'I'll use the project-setup-guide agent to help you get the project configured properly' The user needs project setup assistance, so use the project-setup-guide agent to walk them through the setup process. Context: User is having issues with database configuration. user: 'My database isn't connecting properly, can you help me configure it?' assistant: 'Let me use the project-setup-guide agent to help you with database setup' Database configuration issues fall under project setup, so use the project-setup-guide agent. Context: User mentions environment variables or .env issues. user: 'I'm getting errors about missing environment variables' assistant: 'I'll use the project-setup-guide agent to check your environment configuration' Environment variable issues are part of project setup, so use the project-setup-guide agent. 4 | 5 | model: sonnet 6 | color: green 7 | --- 8 | 9 | You are a Project Setup Specialist, an expert in guiding developers through complex project initialization and configuration processes. Your primary responsibility is to help users set up the project by betting the database and authentication setup. 10 | 11 | ### Step One: Ask the user what database they will be using. We support the following providers: 12 | - **[PlanetScale](https://planetscale.com/docs/vitess/tutorials/planetscale-serverless-driver)** for MySQL and PostgreSQL with their serverless driver 13 | - **[Supabase](https://supabase.com/docs/guides/database/connecting-to-postgres#supavisor-transaction-mode)** with Supavisor transaction mode for PostgreSQL 14 | - **[Neon](https://neon.com/)** for PostgreSQL with built-in connection pooling 15 | - **[Cloudflare D1](https://developers.cloudflare.com/d1/)** for SQLite with edge-native architecture 16 | 17 | ## Step Two: If the user provides info as to what database they are using, make sure they have the ENV variables set up correctly. 18 | 19 | ### PostgreSQL Configuration 20 | 21 | ```bash 22 | # packages/data-ops/.env 23 | # PostgreSQL Configuration (Supabase, Neon, etc.) 24 | DATABASE_HOST="hostname.com/database-name" 25 | DATABASE_USERNAME="username" 26 | DATABASE_PASSWORD="password" 27 | ``` 28 | 29 | ### MySQL Configuration 30 | 31 | ```bash 32 | # packages/data-ops/.env 33 | # MySQL Configuration (PlanetScale, etc.) 34 | DATABASE_HOST="hostname.com/database-name" 35 | DATABASE_USERNAME="username" 36 | DATABASE_PASSWORD="password" 37 | ``` 38 | 39 | ### Cloudflare D1 Configuration 40 | 41 | ```bash 42 | # packages/data-ops/.env 43 | # Cloudflare D1 Configuration 44 | CLOUDFLARE_DATABASE_ID="" 45 | CLOUDFLARE_ACCOUNT_ID="" 46 | CLOUDFLARE_D1_TOKEN="" 47 | ``` 48 | 49 | Ask the user to set them up opposed to reading the .env file. 50 | Not, you should only be looking in the project packages/data-ops/ 51 | 52 | ## Step Three: Update the Drizzle Config to match the database. 53 | Schemas and data will be managed by Drizzle ORM. 54 | 55 | You'll update this file `packages/data-ops/drizzle.config.ts` 56 | 57 | with one of these configs: 58 | 59 | ### PostgreSQL Drizzle Configuration 60 | 61 | ```typescript 62 | // packages/data-ops/drizzle.config.ts 63 | import type { Config } from "drizzle-kit"; 64 | const config: Config = { 65 | out: "./src/drizzle", 66 | schema: ["./src/drizzle/auth-schema.ts"], 67 | dialect: "postgresql", 68 | dbCredentials: { 69 | url: `postgresql://${process.env.DATABASE_USERNAME}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}`, 70 | }, 71 | tablesFilter: ["!_cf_KV", "!auth_*"], 72 | }; 73 | 74 | export default config satisfies Config; 75 | ``` 76 | 77 | ### MySQL Drizzle Configuration 78 | 79 | ```typescript 80 | // packages/data-ops/drizzle.config.ts 81 | import type { Config } from "drizzle-kit"; 82 | const config: Config = { 83 | out: "./src/drizzle", 84 | schema: ["./src/drizzle/auth-schema.ts"], 85 | dialect: "mysql", 86 | dbCredentials: { 87 | url: `mysql://${process.env.DATABASE_USERNAME}:${process.env.DATABASE_PASSWORD}@${process.env.DATABASE_HOST}`, 88 | }, 89 | tablesFilter: ["!_cf_KV", "!auth_*"], 90 | }; 91 | 92 | export default config satisfies Config; 93 | ``` 94 | 95 | ### Cloudflare D1 Drizzle Configuration 96 | 97 | ```typescript 98 | // packages/data-ops/drizzle.config.ts 99 | import type { Config } from "drizzle-kit"; 100 | const config: Config = { 101 | out: "./src/drizzle", 102 | schema: ["./src/drizzle/auth-schema.ts"], 103 | dialect: "sqlite", 104 | driver: "d1-http", 105 | dbCredentials: { 106 | accountId: process.env.CLOUDFLARE_ACCOUNT_ID!, 107 | databaseId: process.env.CLOUDFLARE_DATABASE_ID!, 108 | token: process.env.CLOUDFLARE_D1_TOKEN!, 109 | }, 110 | tablesFilter: ["!_cf_KV", "!auth_*"], 111 | }; 112 | 113 | export default config satisfies Config; 114 | ``` 115 | 116 | Once updated, you can run the following command to pull schemas from the database to ensure connectivity: 117 | 118 | ```bash 119 | pnpm run pull-drizzle-schema 120 | ``` 121 | Run this from the root of the pnpm workspace. This should pull the schemas with no errors in the terminal logs. 122 | 123 | ## Step Four: Setup Auth with Better Auth 124 | The user will need the following environment variables: 125 | 126 | Generate a secure secret key using: `openssl rand -base64 32` 127 | 128 | ```bash 129 | # packages/data-ops/.env 130 | # Auth Environment Variables 131 | BETTER_AUTH_SECRET="your-secret-key-here" 132 | 133 | # Google OAuth (optional) 134 | GOOGLE_CLIENT_ID="your-google-client-id" 135 | GOOGLE_CLIENT_SECRET="your-google-client-secret" 136 | ``` 137 | 138 | 139 | ## Step Five: Update auth config with the correct database helper in packages/data-ops/config/auth.ts 140 | 141 | Update your `packages/data-ops/config/auth.ts` file based on your database provider. This instance is used exclusively by the Better Auth CLI and should not be used in your application runtime. 142 | 143 | ### PostgreSQL CLI Configuration 144 | 145 | ```typescript 146 | // packages/data-ops/config/auth.ts 147 | import { createBetterAuth } from "../src/auth/setup"; 148 | import { initDatabase } from "../src/database/setup"; 149 | import { drizzleAdapter } from "better-auth/adapters/drizzle"; 150 | 151 | export const auth = createBetterAuth({ 152 | database: drizzleAdapter( 153 | initDatabase({ 154 | password: process.env.DATABASE_PASSWORD!, 155 | host: process.env.DATABASE_HOST!, 156 | username: process.env.DATABASE_USERNAME!, 157 | }), 158 | { 159 | provider: "pg", 160 | }, 161 | ), 162 | }); 163 | ``` 164 | 165 | ### MySQL CLI Configuration 166 | 167 | ```typescript 168 | // packages/data-ops/config/auth.ts 169 | import { createBetterAuth } from "../src/auth/setup"; 170 | import { initDatabase } from "../src/database/setup"; 171 | import { drizzleAdapter } from "better-auth/adapters/drizzle"; 172 | 173 | export const auth = createBetterAuth({ 174 | database: drizzleAdapter( 175 | initDatabase({ 176 | password: process.env.DATABASE_PASSWORD!, 177 | host: process.env.DATABASE_HOST!, 178 | username: process.env.DATABASE_USERNAME!, 179 | }), 180 | { 181 | provider: "mysql", 182 | }, 183 | ), 184 | }); 185 | ``` 186 | 187 | ### Cloudflare D1 CLI Configuration 188 | 189 | ```typescript 190 | // packages/data-ops/config/auth.ts 191 | import { createBetterAuth } from "../src/auth/setup"; 192 | import Database from "better-sqlite3"; 193 | import { drizzleAdapter } from "better-auth/adapters/drizzle"; 194 | 195 | // For CLI use - uses dummy SQLite database 196 | export const auth = createBetterAuth({ 197 | database: drizzleAdapter(new Database("./config/test.sqlite"), { 198 | provider: "sqlite", 199 | }), 200 | }); 201 | ``` 202 | 203 | Once updated run `pnpm run build:data-ops` from the root of the pnpm workspace. 204 | if there are any errors with dependencies you can install them in the packages/data-ops project 205 | 206 | 207 | ## Step Six: Generate the Auth Schemas and Database DDL 208 | 209 | Run `pnpm run generate-auth-drizzle-schema` from the root of the pnpm workspace. 210 | This should create a new output in the packages/data-ops/src/drizzle/auth-schema.ts file. 211 | 212 | Check this file and make sure it matches the users database provider. 213 | 214 | Then run `pnpm run generate-drizzle-sql-output` from the root of the pnpm workspace. 215 | 216 | This should generate a new .sql file in the packages/data-ops/src/drizzle/* with the create table statements. 217 | 218 | If it is not there then delete the metadata and .sql files in the packages/data-ops/src/drizzle/* directory and run `pnpm run generate-drizzle-sql-output` again. 219 | 220 | After this instruct the user they can manually run the SQL queries in the generated .sql file in their own SQL editor, or they can run 221 | `pnpm run drizzle:migrate` inside the packages/data-ops project. 222 | 223 | NOTE YOU AS THE AGENT DON'T RUN THE MIGRATE COMMAND 224 | 225 | ## Step Seven: Check if the auth server file is okay, and build the package 226 | 227 | In the `packages/data-ops/src/auth/server.ts` file, check to make sure the correct /drizzle/auth-schema are being imported and used in the drizzleAdapter. 228 | 229 | Once, done run `pnpm run build:data-ops` from the root of the pnpm workspace. 230 | 231 | 232 | ## Step Eight: instruct the user to setup env for user-application and test 233 | Tell the user then need the same env variables in the user-application as they have in the data-ops package. 234 | 235 | Once this is done, they should be able to run the user application and test auth. 236 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional stylelint cache 57 | .stylelintcache 58 | 59 | # Optional REPL history 60 | .node_repl_history 61 | 62 | # Output of 'npm pack' 63 | *.tgz 64 | 65 | # Yarn Integrity file 66 | .yarn-integrity 67 | 68 | # dotenv environment variable files 69 | .env 70 | .env.* 71 | !.env.example 72 | 73 | # parcel-bundler cache (https://parceljs.org/) 74 | .cache 75 | .parcel-cache 76 | 77 | # Next.js build output 78 | .next 79 | out 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and not Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # vuepress v2.x temp and cache directory 95 | .temp 96 | .cache 97 | 98 | # Sveltekit cache directory 99 | .svelte-kit/ 100 | 101 | # vitepress build output 102 | **/.vitepress/dist 103 | 104 | # vitepress cache directory 105 | **/.vitepress/cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # Firebase cache directory 120 | .firebase/ 121 | 122 | # TernJS port file 123 | .tern-port 124 | 125 | # Stores VSCode versions used for testing VSCode extensions 126 | .vscode-test 127 | 128 | # yarn v3 129 | .pnp.* 130 | .yarn/* 131 | !.yarn/patches 132 | !.yarn/plugins 133 | !.yarn/releases 134 | !.yarn/sdks 135 | !.yarn/versions 136 | 137 | # Vite logs files 138 | vite.config.js.timestamp-* 139 | vite.config.ts.timestamp-* 140 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 backpine 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # saas-kit 2 | 3 | [![Watch the video](https://img.youtube.com/vi/TWQv_tr5ABI/maxresdefault.jpg)](https://www.youtube.com/watch?v=TWQv_tr5ABI&t=1s) 4 | 5 | A monorepo SaaS application with user-facing frontend and data service backend. 6 | 7 | ## Setup 8 | 9 | ```bash 10 | pnpm run setup 11 | ``` 12 | 13 | This installs all dependencies and builds required packages. 14 | 15 | ## Development 16 | 17 | ### User Application 18 | ```bash 19 | pnpm run dev:user-application 20 | ``` 21 | 22 | ### Data Service 23 | ```bash 24 | pnpm run dev:data-service 25 | ``` 26 | 27 | ## Deployment 28 | 29 | ### User Application (Cloudflare) 30 | ```bash 31 | pnpm run deploy:user-application 32 | ``` 33 | 34 | ### Data Service 35 | ```bash 36 | pnpm run deploy:data-service 37 | ``` 38 | 39 | ## Working with Individual Apps 40 | 41 | You can also navigate into any sub-application directory and work with it independently in your IDE: 42 | 43 | ```bash 44 | cd packages/user-application 45 | # Open in your preferred IDE 46 | ``` 47 | -------------------------------------------------------------------------------- /apps/data-service/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | logs 4 | _.log 5 | npm-debug.log_ 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | 13 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 14 | 15 | # Runtime data 16 | 17 | pids 18 | _.pid 19 | _.seed 20 | \*.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | 28 | coverage 29 | \*.lcov 30 | 31 | # nyc test coverage 32 | 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | 41 | bower_components 42 | 43 | # node-waf configuration 44 | 45 | .lock-wscript 46 | 47 | # Compiled binary addons (https://nodejs.org/api/addons.html) 48 | 49 | build/Release 50 | 51 | # Dependency directories 52 | 53 | node_modules/ 54 | jspm_packages/ 55 | 56 | # Snowpack dependency directory (https://snowpack.dev/) 57 | 58 | web_modules/ 59 | 60 | # TypeScript cache 61 | 62 | \*.tsbuildinfo 63 | 64 | # Optional npm cache directory 65 | 66 | .npm 67 | 68 | # Optional eslint cache 69 | 70 | .eslintcache 71 | 72 | # Optional stylelint cache 73 | 74 | .stylelintcache 75 | 76 | # Microbundle cache 77 | 78 | .rpt2_cache/ 79 | .rts2_cache_cjs/ 80 | .rts2_cache_es/ 81 | .rts2_cache_umd/ 82 | 83 | # Optional REPL history 84 | 85 | .node_repl_history 86 | 87 | # Output of 'npm pack' 88 | 89 | \*.tgz 90 | 91 | # Yarn Integrity file 92 | 93 | .yarn-integrity 94 | 95 | # dotenv environment variable files 96 | 97 | .env 98 | .env.development.local 99 | .env.test.local 100 | .env.production.local 101 | .env.local 102 | 103 | # parcel-bundler cache (https://parceljs.org/) 104 | 105 | .cache 106 | .parcel-cache 107 | 108 | # Next.js build output 109 | 110 | .next 111 | out 112 | 113 | # Nuxt.js build / generate output 114 | 115 | .nuxt 116 | dist 117 | 118 | # Gatsby files 119 | 120 | .cache/ 121 | 122 | # Comment in the public line in if your project uses Gatsby and not Next.js 123 | 124 | # https://nextjs.org/blog/next-9-1#public-directory-support 125 | 126 | # public 127 | 128 | # vuepress build output 129 | 130 | .vuepress/dist 131 | 132 | # vuepress v2.x temp and cache directory 133 | 134 | .temp 135 | .cache 136 | 137 | # Docusaurus cache and generated files 138 | 139 | .docusaurus 140 | 141 | # Serverless directories 142 | 143 | .serverless/ 144 | 145 | # FuseBox cache 146 | 147 | .fusebox/ 148 | 149 | # DynamoDB Local files 150 | 151 | .dynamodb/ 152 | 153 | # TernJS port file 154 | 155 | .tern-port 156 | 157 | # Stores VSCode versions used for testing VSCode extensions 158 | 159 | .vscode-test 160 | 161 | # yarn v2 162 | 163 | .yarn/cache 164 | .yarn/unplugged 165 | .yarn/build-state.yml 166 | .yarn/install-state.gz 167 | .pnp.\* 168 | 169 | # wrangler project 170 | 171 | .dev.vars 172 | .wrangler/ 173 | -------------------------------------------------------------------------------- /apps/data-service/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "wrangler.json": "jsonc" 4 | } 5 | } -------------------------------------------------------------------------------- /apps/data-service/README.md: -------------------------------------------------------------------------------- 1 | # Worker Publisher 2 | 3 | [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/cloudflare/templates/tree/main/worker-publisher-template) 4 | 5 | 6 | 7 | A Cloudflare Worker that creates and deploys Workers to a Dispatch Namespace via the Cloudflare SDK. 8 | 9 | ## How it works 10 | 11 | - Automatically creates a Workers for Platforms dispatch namespace 12 | - Uses Cloudflare SDK to deploy Workers to the namespace 13 | - Each deployed Worker gets its own /{worker-name} path 14 | - Main Worker acts as a router, forwarding requests to deployed Workers 15 | - Each deployed Worker runs in its own isolated environment 16 | 17 | You can modify this and use it to deploy static sites or full stack applications at scale, build a vibe coding platform, deploy personalized AI agents ... the possibilities are endless! 18 | 19 | 20 | 21 | ## Setup 22 | 23 | After you click "Deploy to Cloudflare", you'll be prompted for: 24 | 25 | - `CLOUDFLARE_ACCOUNT_ID` - Your Cloudflare account ID 26 | - `CLOUDFLARE_API_TOKEN` - Your Cloudflare API token with Workers:Edit permission 27 | -------------------------------------------------------------------------------- /apps/data-service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "data-service", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "deploy": "wrangler deploy", 7 | "dev": "wrangler dev --x-remote-bindings", 8 | "start": "wrangler dev", 9 | "test": "vitest", 10 | "cf-typegen": "wrangler types --env-interface BaseEnv" 11 | }, 12 | "devDependencies": { 13 | "@cloudflare/vitest-pool-workers": "^0.8.19", 14 | "@types/node": "^22.15.19", 15 | "typescript": "^5.5.2", 16 | "vitest": "~3.2.0", 17 | "wrangler": "^4.24.3" 18 | }, 19 | "dependencies": { 20 | "@repo/data-ops": "workspace:*", 21 | "hono": "^4.8.3", 22 | "zod": "^3.25.67" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apps/data-service/service-bindings.d.ts: -------------------------------------------------------------------------------- 1 | interface ExampleWorkflowParmas { 2 | dataToPassIn; 3 | } 4 | 5 | interface Env extends Cloudflare.Env {} 6 | -------------------------------------------------------------------------------- /apps/data-service/src/durable-objects/example-durable-object.ts: -------------------------------------------------------------------------------- 1 | import { DurableObject } from "cloudflare:workers"; 2 | 3 | export class ExampleDurableObject extends DurableObject { 4 | savedData: string | undefined; 5 | 6 | constructor(ctx: DurableObjectState, env: Env) { 7 | super(ctx, env); 8 | ctx.blockConcurrencyWhile(async () => { 9 | const [savedData] = await Promise.all([ 10 | ctx.storage.get("savedData"), 11 | ]); 12 | this.savedData = savedData; 13 | }); 14 | } 15 | 16 | async saveData(data: string) { 17 | await this.ctx.storage.put("savedData", data); 18 | this.savedData = data; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apps/data-service/src/hono/app.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "hono"; 2 | 3 | export const app = new Hono<{ Bindings: Env }>(); 4 | 5 | app.get("/", (c) => { 6 | return c.text("Hello World"); 7 | }); 8 | -------------------------------------------------------------------------------- /apps/data-service/src/index.ts: -------------------------------------------------------------------------------- 1 | import { WorkerEntrypoint } from "cloudflare:workers"; 2 | import { app } from "@/hono/app"; 3 | 4 | export default class DataService extends WorkerEntrypoint { 5 | fetch(request: Request) { 6 | return app.fetch(request, this.env, this.ctx); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/data-service/src/workflows/example-workflow.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WorkflowEntrypoint, 3 | WorkflowEvent, 4 | WorkflowStep, 5 | } from "cloudflare:workers"; 6 | 7 | export class ExampleWorkflow extends WorkflowEntrypoint< 8 | Env, 9 | ExampleWorkflowParmas 10 | > { 11 | async run( 12 | event: Readonly>, 13 | step: WorkflowStep, 14 | ) { 15 | const randomNumber = await step.do("Get random number", async () => { 16 | return Math.floor(Math.random() * 10) + 1; 17 | }); 18 | 19 | await step.sleep( 20 | "Wait for random number of seconds", 21 | `${randomNumber} seconds`, 22 | ); 23 | 24 | await step.do("Log data in payload", async () => { 25 | console.log(event.payload); 26 | }); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /apps/data-service/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noUncheckedIndexedAccess": true, 4 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 5 | 6 | /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 7 | "target": "es2021", 8 | /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 9 | "lib": ["es2021"], 10 | /* Specify what JSX code is generated. */ 11 | "jsx": "react-jsx", 12 | 13 | /* Specify what module code is generated. */ 14 | "module": "es2022", 15 | /* Specify how TypeScript looks up a file from a given module specifier. */ 16 | "moduleResolution": "Bundler", 17 | /* Enable importing .json files */ 18 | "resolveJsonModule": true, 19 | 20 | /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ 21 | "allowJs": true, 22 | /* Enable error reporting in type-checked JavaScript files. */ 23 | "checkJs": false, 24 | 25 | /* Disable emitting files from a compilation. */ 26 | "noEmit": true, 27 | 28 | /* Ensure that each file can be safely transpiled without relying on other imports. */ 29 | "isolatedModules": true, 30 | /* Allow 'import x from y' when a module doesn't have a default export. */ 31 | "allowSyntheticDefaultImports": true, 32 | /* Ensure that casing is correct in imports. */ 33 | "forceConsistentCasingInFileNames": true, 34 | 35 | /* Enable all strict type-checking options. */ 36 | "strict": true, 37 | 38 | /* Skip type checking all .d.ts files. */ 39 | "skipLibCheck": true, 40 | "types": ["./worker-configuration.d.ts", "./service-bindings.d.ts"], 41 | "paths": { 42 | "@/*": ["./src/*"], 43 | "@/bindings": ["./service-bindings.d.ts"] 44 | } 45 | }, 46 | "exclude": ["test"], 47 | "include": [ 48 | "worker-configuration.d.ts", 49 | "service-bindings.d.ts", 50 | "src/**/*.ts" 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /apps/data-service/wrangler.jsonc: -------------------------------------------------------------------------------- 1 | /** 2 | * For more details on how to configure Wrangler, refer to: 3 | * https://developers.cloudflare.com/workers/wrangler/configuration/ 4 | */ 5 | { 6 | "$schema": "./node_modules/wrangler/config-schema.json", 7 | "name": "saas-kit-data-service", 8 | "main": "./src/index.ts", 9 | "compatibility_date": "2025-04-01", 10 | "compatibility_flags": ["nodejs_compat"], 11 | 12 | "observability": { 13 | "enabled": true, 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /apps/user-application/.claude/agents/shadcn-ui-builder.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: shadcn-ui-builder 3 | description: Use this agent when you need to create, modify, or enhance UI components using Shadcn/UI with proper design patterns and file organization. Examples: Context: User wants to create a new dashboard component with cards and charts. user: 'Create a dashboard component with metric cards and a chart section' assistant: 'I'll use the shadcn-ui-builder agent to create a well-structured dashboard component following our design system.' The user needs UI components built, so use the shadcn-ui-builder agent to create components with proper Shadcn patterns, theme colors, and file organization. Context: User needs to improve the UX of an existing form component. user: 'This login form needs better validation feedback and loading states' assistant: 'Let me use the shadcn-ui-builder agent to enhance the form with better UX patterns.' Since this involves UI/UX improvements using Shadcn components, use the shadcn-ui-builder agent. 4 | model: sonnet 5 | color: green 6 | --- 7 | 8 | You are a Senior UI/UX Engineer specializing in Shadcn/UI component development with deep expertise in modern React patterns, accessibility, and design systems. You excel at creating beautiful, functional, and accessible user interfaces that follow best practices. 9 | 10 | Your core responsibilities: 11 | 1. **Component Architecture**: Design and build React components using Shadcn/UI primitives with proper composition patterns 12 | 2. **Design System Adherence**: Ensure all components follow consistent design patterns and use theme-based styling 13 | 3. **File Organization**: Structure components logically within the src/components folder with appropriate subfolders 14 | 4. **Accessibility First**: Build components that are accessible by default with proper ARIA attributes and keyboard navigation 15 | 5. **Performance Optimization**: Create efficient components with proper memoization and lazy loading where appropriate 16 | 17 | **Critical Rules You Must Follow:** 18 | - NEVER hardcode Tailwind colors - always use CSS variables and theme tokens (e.g., `bg-background`, `text-foreground`, `border-border`) 19 | - Use lowercase kebab-case for all file names (e.g., `user-profile.tsx`, not `UserProfile.tsx`) 20 | - Organize components in logical subfolders within `src/components/` (e.g., `src/components/forms/`, `src/components/layout/`) 21 | - Use the Shadcn MCP server to add new components when needed with `pnpx shadcn@latest add ` 22 | - Follow the project's Tailwind CSS v4 setup with CSS variables enabled 23 | 24 | **Component Development Process:** 25 | 1. **Analyze Requirements**: Understand the component's purpose, user interactions, and data flow 26 | 2. **Plan Structure**: Determine component hierarchy, props interface, and folder organization 27 | 3. **Design Patterns**: Choose appropriate Shadcn primitives and composition patterns 28 | 4. **Theme Integration**: Use semantic color tokens and spacing from the design system 29 | 5. **Accessibility**: Implement proper ARIA labels, keyboard navigation, and screen reader support 30 | 6. **Responsive Design**: Ensure components work across all device sizes 31 | 7. **Error Handling**: Include proper loading states, error boundaries, and fallbacks 32 | 33 | **Code Quality Standards:** 34 | - Use TypeScript with strict typing for all props and state 35 | - Implement proper error boundaries and loading states 36 | - Follow React 19 patterns including concurrent features when appropriate 37 | - Use semantic HTML elements and proper heading hierarchy 38 | - Implement proper focus management and keyboard navigation 39 | - Include hover, focus, and active states for interactive elements 40 | 41 | **UX Principles:** 42 | - Prioritize user feedback with clear loading, success, and error states 43 | - Implement progressive disclosure for complex interfaces 44 | - Use consistent spacing, typography, and interaction patterns 45 | - Provide clear visual hierarchy and scannable layouts 46 | - Ensure fast perceived performance with skeleton loaders and optimistic updates 47 | 48 | **File Organization Examples:** 49 | - Forms: `src/components/forms/login-form.tsx`, `src/components/forms/contact-form.tsx` 50 | - Layout: `src/components/layout/header.tsx`, `src/components/layout/sidebar.tsx` 51 | - UI Elements: `src/components/ui/custom-button.tsx`, `src/components/ui/data-table.tsx` 52 | 53 | When creating components, always consider the complete user journey, provide clear feedback for all interactions, and ensure the component integrates seamlessly with the existing TanStack Start application architecture. Ask for clarification if requirements are ambiguous, and suggest UX improvements when you identify opportunities to enhance the user experience. 54 | -------------------------------------------------------------------------------- /apps/user-application/.claude/agents/tanstack-server-functions.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: tanstack-server-functions 3 | description: Expert agent for TanStack Start server functions and middleware patterns. Use this agent when working with server-side logic, middleware composition, data fetching, validation, authentication, and the full-stack TanStack Start architecture. Examples: Context: User wants to create authenticated API endpoints. user: 'Create server functions with authentication middleware' assistant: 'I'll use the tanstack-server-functions agent to create secure server endpoints with proper middleware composition.' This involves server functions and middleware patterns, so use the tanstack-server-functions agent. Context: User needs data validation and error handling. user: 'Set up form submission with server validation using Zod' assistant: 'Let me use the tanstack-server-functions agent to create a robust form handler with validation.' Server-side validation and form handling requires server functions expertise. 4 | model: sonnet 5 | color: blue 6 | --- 7 | 8 | You are a Senior Full-Stack Engineer specializing in TanStack Start server functions and middleware architecture. You have deep expertise in server-side patterns, middleware composition, data validation, authentication, and full-stack type safety. 9 | 10 | Your core responsibilities: 11 | 1. **Server Function Architecture**: Design and implement server functions with proper input validation, error handling, and type safety 12 | 2. **Middleware Composition**: Create composable middleware chains for authentication, logging, validation, and context management 13 | 3. **Full-Stack Integration**: Seamlessly integrate server functions with client-side TanStack Query mutations and queries 14 | 4. **Security Patterns**: Implement secure server-side logic with proper validation and sanitization 15 | 5. **Performance Optimization**: Create efficient server functions with proper caching and optimization strategies 16 | 17 | **Critical Rules You Must Follow:** 18 | - ALWAYS use TypeScript with strict typing for server functions and middleware 19 | - Use Zod or similar schema validation for all server function inputs 20 | - ALWAYS define explicit types using `z.infer` and pass to inputValidator for proper TypeScript inference 21 | - Follow the established pattern: `src/core/middleware/` for middleware, `src/core/functions/` for server functions 22 | - Create composable middleware chains using the base function pattern 23 | - Implement proper error handling and logging for debugging 24 | - Use kebab-case for file names (e.g., `auth-middleware.ts`, `user-functions.ts`) 25 | 26 | **TanStack Start Server Functions Fundamentals:** 27 | 28 | Server functions in TanStack Start are server-only logic that can be called from anywhere in your application while maintaining full type safety across network boundaries. 29 | 30 | **Basic Pattern:** 31 | ```typescript 32 | import { createServerFn } from '@tanstack/react-start' 33 | import { z } from 'zod' 34 | 35 | const InputSchema = z.object({ 36 | data: z.string().min(1), 37 | }) 38 | 39 | type InputType = z.infer 40 | 41 | export const myServerFunction = createServerFn() 42 | .inputValidator((data: InputType) => InputSchema.parse(data)) 43 | .handler(async (ctx) => { 44 | // Server-only logic here 45 | return 'Response data' 46 | }) 47 | ``` 48 | 49 | **Middleware Composition Pattern:** 50 | ```typescript 51 | // Middleware 52 | import { createMiddleware } from '@tanstack/react-start' 53 | 54 | export const authMiddleware = createMiddleware({ 55 | type: 'function' 56 | }).server(async ({ next }) => { 57 | // Authentication logic 58 | return next({ 59 | context: { 60 | user: authenticatedUser 61 | } 62 | }) 63 | }) 64 | 65 | // Base function with middleware 66 | const baseFunction = createServerFn().middleware([ 67 | authMiddleware, 68 | ]) 69 | 70 | // Server function using base 71 | type InputType = z.infer 72 | 73 | export const protectedFunction = baseFunction 74 | .inputValidator((data: InputType) => Schema.parse(data)) 75 | .handler(async (ctx) => { 76 | // Access ctx.context.user from middleware 77 | return result 78 | }) 79 | ``` 80 | 81 | **Key Capabilities:** 82 | 83 | 1. **Input Validation** 84 | - Always validate server function inputs with schemas 85 | - Use Zod for runtime type checking 86 | - Provide clear error messages for invalid inputs 87 | 88 | 2. **Middleware Types** 89 | - **Request Middleware**: Applies to all server routes and functions 90 | - **Function Middleware**: More granular control for specific server functions 91 | - **Composable Chains**: Middleware can depend on other middleware 92 | 93 | 3. **Error Handling** 94 | - Throw errors that serialize properly to client 95 | - Support for redirects and "not found" responses 96 | - Automatic error handling in route lifecycles 97 | 98 | 4. **Advanced Features** 99 | - Access request headers and environment variables 100 | - Handle form submissions and file uploads 101 | - Support for streaming responses 102 | - Request cancellation support 103 | 104 | **Established Patterns in This Project:** 105 | 106 | 1. **File Organization:** 107 | ``` 108 | src/core/ 109 | ├── middleware/ 110 | │ ├── auth-middleware.ts 111 | │ ├── logging-middleware.ts 112 | │ └── example-middleware.ts 113 | └── functions/ 114 | ├── user-functions.ts 115 | ├── auth-functions.ts 116 | └── example-functions.ts 117 | ``` 118 | 119 | 2. **Middleware Pattern:** 120 | ```typescript 121 | export const exampleMiddleware = createMiddleware({ 122 | type: 'function' 123 | }).server(async ({ next }) => { 124 | console.log('Middleware executing') 125 | return next({ 126 | context: { 127 | data: 'Middleware context' 128 | } 129 | }) 130 | }) 131 | ``` 132 | 133 | 3. **Base Function Pattern:** 134 | ```typescript 135 | const baseFunction = createServerFn().middleware([ 136 | exampleMiddleware, 137 | ]) 138 | 139 | type InputType = z.infer 140 | 141 | export const myFunction = baseFunction 142 | .inputValidator((data: InputType) => Schema.parse(data)) 143 | .handler(async (ctx) => { 144 | // Access ctx.data (validated input) 145 | // Access ctx.context (from middleware) 146 | return response 147 | }) 148 | ``` 149 | 150 | 4. **Client Integration with TanStack Query:** 151 | ```typescript 152 | const mutation = useMutation({ 153 | mutationFn: myServerFunction, 154 | onSuccess: (data) => { 155 | console.log('Server function executed:', data) 156 | } 157 | }) 158 | ``` 159 | 160 | **Best Practices:** 161 | 162 | 1. **Security First** 163 | - Always validate inputs on the server 164 | - Never trust client-side data 165 | - Implement proper authentication and authorization 166 | - Log server-side execution for debugging 167 | 168 | 2. **Error Handling** 169 | - Use try-catch blocks for external API calls 170 | - Return meaningful error messages 171 | - Log errors for debugging 172 | - Handle edge cases gracefully 173 | 174 | 3. **Performance** 175 | - Use efficient database queries 176 | - Implement proper caching strategies 177 | - Consider rate limiting for public endpoints 178 | - Optimize for Cloudflare Workers environment 179 | 180 | 4. **Type Safety** 181 | - Use strict TypeScript throughout 182 | - Define clear input/output types 183 | - Leverage Zod for runtime validation 184 | - Maintain type safety across client-server boundary 185 | 186 | **Common Use Cases:** 187 | 188 | 1. **Authentication & Authorization** 189 | - User login/logout functions 190 | - JWT token validation 191 | - Role-based access control 192 | - Session management 193 | 194 | 2. **Data Operations** 195 | - CRUD operations with validation 196 | - Database queries with proper error handling 197 | - File upload and processing 198 | - External API integrations 199 | 200 | 3. **Form Processing** 201 | - Contact form submissions 202 | - User registration/profile updates 203 | - File uploads with validation 204 | - Multi-step form handling 205 | 206 | 4. **Background Tasks** 207 | - Email sending 208 | - Data processing 209 | - Scheduled tasks 210 | - Webhook handling 211 | 212 | **Environment-Specific Considerations:** 213 | 214 | This project runs on Cloudflare Workers, which provides: 215 | - Edge computing capabilities 216 | - KV storage for caching 217 | - Durable Objects for stateful logic 218 | - R2 for object storage 219 | - Access to `env` variables via `cloudflare:workers` 220 | 221 | **Debugging Tips:** 222 | 223 | 1. **Server Logs**: Always log middleware and function execution 224 | 2. **Client Errors**: Use proper error handling in TanStack Query 225 | 3. **Network Inspector**: Monitor requests in browser dev tools 226 | 4. **Type Checking**: Leverage TypeScript for compile-time validation 227 | 5. **Environment Variables**: Use `console.log(env)` to debug Cloudflare environment 228 | 229 | When implementing server functions and middleware, always consider security, performance, and developer experience. Create composable, reusable patterns that can be easily extended and maintained. Focus on type safety and proper error handling throughout the full-stack data flow. -------------------------------------------------------------------------------- /apps/user-application/.cta.json: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": ".", 3 | "mode": "file-router", 4 | "typescript": true, 5 | "tailwind": true, 6 | "packageManager": "pnpm", 7 | "git": true, 8 | "version": 1, 9 | "framework": "react-cra", 10 | "chosenAddOns": [ 11 | "start", 12 | "shadcn", 13 | "tanstack-query" 14 | ] 15 | } -------------------------------------------------------------------------------- /apps/user-application/.mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcpServers": { 3 | "shadcn": { 4 | "command": "npx", 5 | "args": ["shadcn@latest", "mcp"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/user-application/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.watcherExclude": { 3 | "**/routeTree.gen.ts": true 4 | }, 5 | "search.exclude": { 6 | "**/routeTree.gen.ts": true 7 | }, 8 | "files.readonlyInclude": { 9 | "**/routeTree.gen.ts": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /apps/user-application/.wrangler/deploy/config.json: -------------------------------------------------------------------------------- 1 | {"configPath":"../../dist/server/wrangler.json","auxiliaryWorkers":[]} -------------------------------------------------------------------------------- /apps/user-application/CLAUDE.md: -------------------------------------------------------------------------------- 1 | # CLAUDE.md 2 | 3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 | 5 | ## Commands 6 | 7 | ### Development 8 | - `pnpm dev` - Start development server on port 3000 9 | - `pnpm build` - Build for production 10 | - `pnpm serve` - Preview production build 11 | - `pnpm test` - Run tests with Vitest 12 | 13 | ### Shadcn Components 14 | - `pnpx shadcn@latest add ` - Add new Shadcn components (use latest version) 15 | 16 | ## Architecture 17 | 18 | This is a TanStack Start application - a type-safe, client-first, full-stack React framework built on top of: 19 | 20 | ### Core Stack 21 | - **TanStack Router**: File-based routing with type-safe navigation 22 | - **TanStack Query**: Server state management with SSR integration 23 | - **React 19**: Latest React with concurrent features 24 | - **Vite**: Build tool and dev server 25 | - **TypeScript**: Strict type checking enabled 26 | - **Tailwind CSS v4**: Utility-first styling with CSS variables 27 | 28 | ### Project Structure 29 | - `src/routes/` - File-based routes (auto-generates `routeTree.gen.ts`) 30 | - `src/components/` - Reusable React components 31 | - `src/integrations/tanstack-query/` - Query client setup and providers 32 | - `src/lib/utils.ts` - Utility functions (includes clsx/tailwind-merge) 33 | - `src/utils/seo.ts` - SEO helper functions 34 | - Path aliases: `@/*` maps to `src/*` 35 | 36 | ### Key Architecture Patterns 37 | 38 | **Router Setup**: The router is created via `getRouter()` in `src/router.tsx` which integrates TanStack Query context and SSR. Routes are auto-generated from the file system. 39 | 40 | **Query Integration**: TanStack Query is pre-configured with SSR support through `setupRouterSsrQueryIntegration`. The query client is accessible in route contexts. 41 | 42 | **Root Layout**: `src/routes/__root.tsx` defines the HTML document structure, includes devtools, and provides navigation links. It uses `createRootRouteWithContext` for type-safe context passing. 43 | 44 | **Styling**: Uses Tailwind CSS v4 with the Vite plugin. Shadcn components are configured with "new-york" style, Zinc base color, and CSS variables enabled. 45 | 46 | **TypeScript**: Strict mode with additional linting rules (`noUnusedLocals`, `noUnusedParameters`, etc.). Uses modern ESNext module resolution. 47 | 48 | ### Development Notes 49 | - Demo files (prefixed with `demo`) can be safely deleted 50 | - The project uses pnpm as the package manager 51 | - Devtools are included for both Router and Query in development 52 | - Routes support loaders, error boundaries, and not-found components 53 | - File-based routing automatically generates type-safe route definitions -------------------------------------------------------------------------------- /apps/user-application/README.md: -------------------------------------------------------------------------------- 1 | # TanStack Start on Cloudflare 2 | 3 | A modern, full-stack React application built with TanStack Start and deployed on Cloudflare Workers. This template showcases server functions, middleware, type-safe data fetching, and seamless integration with Cloudflare's edge computing platform. 4 | 5 | ## 🚀 Quick Start 6 | 7 | ```bash 8 | # Install dependencies 9 | pnpm install 10 | 11 | # Start development server 12 | pnpm dev 13 | 14 | # Build for production 15 | pnpm build 16 | 17 | # Deploy to Cloudflare 18 | pnpm deploy 19 | ``` 20 | 21 | ## 📦 Development Workflow 22 | 23 | This project provides a comprehensive development workflow with the following scripts: 24 | 25 | - **`pnpm dev`** - Start development server on port 3000 26 | - **`pnpm build`** - Build the application for production 27 | - **`pnpm deploy`** - Build and deploy to Cloudflare Workers 28 | - **`pnpm serve`** - Preview production build locally 29 | - **`pnpm cf-typegen`** - Generate TypeScript types for Cloudflare environment 30 | 31 | ## 🌩️ Cloudflare Integration 32 | 33 | ### Environment Variables & Type Generation 34 | 35 | This project includes full TypeScript support for Cloudflare Workers environment variables: 36 | 37 | ```bash 38 | # Generate types for Cloudflare environment 39 | pnpm cf-typegen 40 | ``` 41 | 42 | This creates type definitions allowing you to safely import and use Cloudflare environment variables: 43 | 44 | ```typescript 45 | import { env } from "cloudflare:workers"; 46 | 47 | // Now env is fully typed with your Wrangler configuration 48 | console.log(env.MY_VAR); // TypeScript knows this exists 49 | ``` 50 | 51 | ### Wrangler Configuration 52 | 53 | The `wrangler.jsonc` file configures your Cloudflare deployment: 54 | 55 | ```jsonc 56 | { 57 | "$schema": "node_modules/wrangler/config-schema.json", 58 | "name": "tanstack-start-app", 59 | "compatibility_date": "2025-09-02", 60 | "compatibility_flags": ["nodejs_compat"], 61 | "main": "./src/server.ts", // Custom server entry point 62 | "vars": { 63 | "MY_VAR": "Hello from Cloudflare" 64 | } 65 | } 66 | ``` 67 | 68 | ### Custom Server Entry (`src/server.ts`) 69 | 70 | The `src/server.ts` file is your custom Cloudflare Workers entry point where you can add additional Cloudflare features: 71 | 72 | ```typescript 73 | import handler from "@tanstack/react-start/server-entry"; 74 | 75 | export default { 76 | fetch(request: Request) { 77 | return handler.fetch(request, { 78 | context: { 79 | fromFetch: true, 80 | }, 81 | }); 82 | }, 83 | 84 | // Add other Cloudflare Workers features: 85 | // - Queue consumers: queue(batch, env) { ... } 86 | // - Scheduled events: scheduled(event, env) { ... } 87 | // - Durable Object handlers 88 | // - etc. 89 | }; 90 | ``` 91 | 92 | ## 🎨 Styling & Components 93 | 94 | ### Tailwind CSS v4 95 | This project uses the latest Tailwind CSS v4 with CSS variables for theming: 96 | 97 | ```bash 98 | # Tailwind is pre-configured with the @tailwindcss/vite plugin 99 | # CSS variables are enabled for theme customization 100 | ``` 101 | 102 | ### Shadcn/UI Components 103 | Add beautiful, accessible components using Shadcn/UI: 104 | 105 | ```bash 106 | # Add individual components 107 | pnpx shadcn@latest add button 108 | pnpx shadcn@latest add card 109 | pnpx shadcn@latest add form 110 | 111 | # Components use semantic color tokens and CSS variables 112 | # Perfect for light/dark theme support 113 | ``` 114 | 115 | 116 | 117 | ## 🗂️ File-Based Routing 118 | 119 | This project uses [TanStack Router](https://tanstack.com/router/latest) with file-based routing. Routes are automatically generated from files in the `src/routes` directory: 120 | 121 | ### Adding A Route 122 | 123 | To add a new route to your application just add another a new file in the `./src/routes` directory. 124 | 125 | TanStack will automatically generate the content of the route file for you. 126 | 127 | Now that you have two routes you can use a `Link` component to navigate between them. 128 | 129 | ### Adding Links 130 | 131 | To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`. 132 | 133 | ```tsx 134 | import { Link } from "@tanstack/react-router"; 135 | ``` 136 | 137 | Then anywhere in your JSX you can use it like so: 138 | 139 | ```tsx 140 | About 141 | ``` 142 | 143 | This will create a link that will navigate to the `/about` route. 144 | 145 | More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent). 146 | 147 | ### Using A Layout 148 | 149 | In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you use the `` component. 150 | 151 | Here is an example layout that includes a header: 152 | 153 | ```tsx 154 | import { Outlet, createRootRoute } from '@tanstack/react-router' 155 | import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' 156 | 157 | import { Link } from "@tanstack/react-router"; 158 | 159 | export const Route = createRootRoute({ 160 | component: () => ( 161 | <> 162 |
163 | 167 |
168 | 169 | 170 | 171 | ), 172 | }) 173 | ``` 174 | 175 | The `` component is not required so you can remove it if you don't want it in your layout. 176 | 177 | More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts). 178 | 179 | 180 | ## 🔄 Data Fetching & Server Functions 181 | 182 | This template demonstrates modern server-side patterns with TanStack Start's server functions, middleware, and seamless client integration. 183 | 184 | ### Server Functions with Middleware 185 | 186 | Server functions run exclusively on the server and maintain type safety across network boundaries: 187 | 188 | ```typescript 189 | // src/core/middleware/example-middleware.ts 190 | export const exampleMiddleware = createMiddleware({ 191 | type: 'function' 192 | }).server(async ({ next }) => { 193 | console.log('Middleware executing on server'); 194 | return next({ 195 | context: { 196 | data: 'Context from middleware' 197 | } 198 | }); 199 | }); 200 | 201 | // src/core/functions/example-functions.ts 202 | const ExampleInputSchema = z.object({ 203 | exampleKey: z.string().min(1), 204 | }); 205 | 206 | type ExampleInput = z.infer; 207 | 208 | const baseFunction = createServerFn().middleware([ 209 | exampleMiddleware, 210 | ]); 211 | 212 | export const exampleFunction = baseFunction 213 | .inputValidator((data: ExampleInput) => ExampleInputSchema.parse(data)) 214 | .handler(async (ctx) => { 215 | // Access validated input: ctx.data 216 | // Access middleware context: ctx.context 217 | // Access Cloudflare env: env.MY_VAR 218 | return 'Server response'; 219 | }); 220 | ``` 221 | 222 | ### Client Integration with TanStack Query 223 | 224 | Server functions integrate seamlessly with TanStack Query for optimal UX: 225 | 226 | ```tsx 227 | import { useMutation } from '@tanstack/react-query'; 228 | import { exampleFunction } from '@/core/functions/example-functions'; 229 | 230 | function MyComponent() { 231 | const mutation = useMutation({ 232 | mutationFn: exampleFunction, 233 | onSuccess: (data) => console.log('Success:', data), 234 | onError: (error) => console.error('Error:', error), 235 | }); 236 | 237 | return ( 238 | 244 | ); 245 | } 246 | ``` 247 | 248 | ### Key Benefits 249 | 250 | - **🔒 Type-Safe**: Full TypeScript support with Zod validation 251 | - **🚀 Server-First**: Secure server-side logic with client convenience 252 | - **⚡ Edge Computing**: Runs on Cloudflare's global edge network 253 | - **🔄 Seamless Integration**: Works perfectly with TanStack Query 254 | - **🧩 Composable**: Middleware chains for auth, logging, validation 255 | 256 | ### Interactive Demo 257 | 258 | This template includes a live demo showcasing the middleware and server function patterns. Check your server logs when running the demo to see the execution flow! 259 | 260 | ## 🧪 Testing 261 | 262 | This project uses [Vitest](https://vitest.dev/) for fast unit and integration testing: 263 | 264 | ```bash 265 | # Run tests 266 | pnpm test 267 | 268 | # Test configuration is in vite.config.ts 269 | # Uses jsdom environment for DOM testing 270 | # Includes @testing-library/react for component testing 271 | ``` 272 | 273 | ## 📋 Tech Stack 274 | 275 | This template includes the latest and greatest from the React ecosystem: 276 | 277 | ### **Core Framework** 278 | - **TanStack Start** - Full-stack React framework with SSR 279 | - **React 19** - Latest React with concurrent features 280 | - **TypeScript** - Strict type checking enabled 281 | 282 | ### **Routing & Data** 283 | - **TanStack Router** - Type-safe, file-based routing 284 | - **TanStack Query** - Server state management with SSR integration 285 | 286 | ### **Styling & UI** 287 | - **Tailwind CSS v4** - Utility-first CSS with CSS variables 288 | - **Shadcn/UI** - Beautiful, accessible component library 289 | - **Lucide React** - Consistent icon set 290 | 291 | ### **Development Tools** 292 | - **Vite** - Lightning-fast build tool and dev server 293 | - **Vitest** - Unit testing with jsdom 294 | - **TypeScript** - Full type safety across client and server 295 | 296 | ### **Deployment** 297 | - **Cloudflare Workers** - Edge computing platform 298 | - **Wrangler** - Cloudflare deployment and development CLI 299 | 300 | ## 🚀 Learn More 301 | 302 | - **[TanStack Start](https://tanstack.com/start)** - Full-stack React framework 303 | - **[TanStack Router](https://tanstack.com/router)** - Type-safe routing 304 | - **[TanStack Query](https://tanstack.com/query)** - Server state management 305 | - **[Cloudflare Workers](https://workers.cloudflare.com/)** - Edge computing platform 306 | - **[Shadcn/UI](https://ui.shadcn.com/)** - Component library 307 | - **[Tailwind CSS](https://tailwindcss.com/)** - Utility-first CSS 308 | 309 | ## 📄 License 310 | 311 | This template is open source and available under the [MIT License](LICENSE). 312 | -------------------------------------------------------------------------------- /apps/user-application/components.json: -------------------------------------------------------------------------------- 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": "src/styles.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /apps/user-application/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "user-application", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite dev --port 3000", 7 | "build": "vite build", 8 | "deploy": "pnpm run build && wrangler deploy", 9 | "serve": "vite preview", 10 | "cf-typegen": "wrangler types --env-interface Env" 11 | }, 12 | "dependencies": { 13 | "@polar-sh/sdk": "^0.34.17", 14 | "@polar-sh/tanstack-start": "^0.1.12", 15 | "@radix-ui/react-avatar": "^1.1.10", 16 | "@radix-ui/react-collapsible": "^1.1.12", 17 | "@radix-ui/react-dialog": "^1.1.15", 18 | "@radix-ui/react-dropdown-menu": "^2.1.16", 19 | "@radix-ui/react-scroll-area": "^1.2.10", 20 | "@radix-ui/react-slot": "^1.2.3", 21 | "@repo/data-ops": "workspace:^", 22 | "@tailwindcss/typography": "^0.5.19", 23 | "@tailwindcss/vite": "^4.1.15", 24 | "@tanstack/react-query": "^5.90.5", 25 | "@tanstack/react-query-devtools": "^5.90.2", 26 | "@tanstack/react-router": "^1.133.22", 27 | "@tanstack/react-router-devtools": "^1.133.22", 28 | "@tanstack/react-router-ssr-query": "^1.133.22", 29 | "@tanstack/react-start": "^1.133.22", 30 | "better-auth": "^1.3.29", 31 | "class-variance-authority": "^0.7.1", 32 | "clsx": "^2.1.1", 33 | "highlight.js": "^11.11.1", 34 | "lucide-react": "^0.476.0", 35 | "react": "^19.2.0", 36 | "react-dom": "^19.2.0", 37 | "react-icons": "^5.5.0", 38 | "react-markdown": "^10.1.0", 39 | "rehype-highlight": "^7.0.2", 40 | "remark-breaks": "^4.0.0", 41 | "remark-gfm": "^4.0.1", 42 | "tailwind-merge": "^3.3.1", 43 | "tailwindcss": "^4.1.15", 44 | "tw-animate-css": "^1.4.0", 45 | "vite-tsconfig-paths": "^5.1.4", 46 | "zod": "^4.1.12" 47 | }, 48 | "devDependencies": { 49 | "@cloudflare/vite-plugin": "^1.13.14", 50 | "@testing-library/dom": "^10.4.1", 51 | "@testing-library/react": "^16.3.0", 52 | "@types/node": "^22.18.12", 53 | "@types/react": "^19.2.2", 54 | "@types/react-dom": "^19.2.2", 55 | "@types/remark-prism": "^1.3.7", 56 | "@vitejs/plugin-react": "^4.7.0", 57 | "jsdom": "^26.1.0", 58 | "typescript": "^5.9.3", 59 | "vite": "7.1.2", 60 | "vitest": "^3.2.4", 61 | "web-vitals": "^4.2.4", 62 | "wrangler": "^4.44.0" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /apps/user-application/public/better-auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/saas-kit/8e1097e3b14e5b751e4f6d1a0227696889cccd88/apps/user-application/public/better-auth.png -------------------------------------------------------------------------------- /apps/user-application/public/claude-code-cli.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/saas-kit/8e1097e3b14e5b751e4f6d1a0227696889cccd88/apps/user-application/public/claude-code-cli.webp -------------------------------------------------------------------------------- /apps/user-application/public/cloudflare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/saas-kit/8e1097e3b14e5b751e4f6d1a0227696889cccd88/apps/user-application/public/cloudflare.png -------------------------------------------------------------------------------- /apps/user-application/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/saas-kit/8e1097e3b14e5b751e4f6d1a0227696889cccd88/apps/user-application/public/favicon.ico -------------------------------------------------------------------------------- /apps/user-application/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/saas-kit/8e1097e3b14e5b751e4f6d1a0227696889cccd88/apps/user-application/public/logo192.png -------------------------------------------------------------------------------- /apps/user-application/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/saas-kit/8e1097e3b14e5b751e4f6d1a0227696889cccd88/apps/user-application/public/logo512.png -------------------------------------------------------------------------------- /apps/user-application/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "TanStack App", 3 | "name": "Create TanStack App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /apps/user-application/public/pnpm.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/saas-kit/8e1097e3b14e5b751e4f6d1a0227696889cccd88/apps/user-application/public/pnpm.webp -------------------------------------------------------------------------------- /apps/user-application/public/polar-product-metadata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/saas-kit/8e1097e3b14e5b751e4f6d1a0227696889cccd88/apps/user-application/public/polar-product-metadata.png -------------------------------------------------------------------------------- /apps/user-application/public/polar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/saas-kit/8e1097e3b14e5b751e4f6d1a0227696889cccd88/apps/user-application/public/polar.png -------------------------------------------------------------------------------- /apps/user-application/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /apps/user-application/public/shadcn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/saas-kit/8e1097e3b14e5b751e4f6d1a0227696889cccd88/apps/user-application/public/shadcn.png -------------------------------------------------------------------------------- /apps/user-application/public/tanstack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/backpine/saas-kit/8e1097e3b14e5b751e4f6d1a0227696889cccd88/apps/user-application/public/tanstack.png -------------------------------------------------------------------------------- /apps/user-application/src/components/auth/account-dialog.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@/components/ui/button"; 2 | import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; 3 | import { 4 | Dialog, 5 | DialogContent, 6 | DialogHeader, 7 | DialogTitle, 8 | DialogTrigger, 9 | } from "@/components/ui/dialog"; 10 | import { authClient } from "@/lib/auth-client"; 11 | import { LogOut, Palette } from "lucide-react"; 12 | import { ThemeToggle } from "@/components/theme/theme-toggle"; 13 | 14 | interface AccountDialogProps { 15 | children: React.ReactNode; 16 | } 17 | 18 | export function AccountDialog({ children }: AccountDialogProps) { 19 | const { data: session } = authClient.useSession(); 20 | 21 | const signOut = async () => { 22 | await authClient.signOut(); 23 | }; 24 | 25 | if (!session) { 26 | return null; 27 | } 28 | 29 | const user = session.user; 30 | const fallbackText = user.name 31 | ? user.name.charAt(0).toUpperCase() 32 | : user.email?.charAt(0).toUpperCase() || "U"; 33 | 34 | return ( 35 | 36 | 37 | {children} 38 | 39 | 40 | 41 | Account 42 | 43 |
44 | 45 | 49 | 50 | {fallbackText} 51 | 52 | 53 |
54 | {user.name && ( 55 |
{user.name}
56 | )} 57 | {user.email && ( 58 |
{user.email}
59 | )} 60 |
61 |
62 |
63 | 64 | 65 | Theme 66 | 67 | 68 |
69 | 78 |
79 |
80 |
81 |
82 | ); 83 | } -------------------------------------------------------------------------------- /apps/user-application/src/components/auth/google-login.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@/components/ui/button"; 2 | import { 3 | Card, 4 | CardContent, 5 | CardDescription, 6 | CardHeader, 7 | CardTitle, 8 | } from "@/components/ui/card"; 9 | import { authClient } from "@/lib/auth-client"; 10 | 11 | export function GoogleLogin() { 12 | const handleGoogleSignIn = async () => { 13 | await authClient.signIn.social({ 14 | provider: "google", 15 | callbackURL: "/app", 16 | }); 17 | }; 18 | 19 | return ( 20 |
21 | 22 | 23 | Welcome back 24 | Sign in to your account to continue 25 | 26 | 27 | 40 | 41 | 42 |
43 | ); 44 | } -------------------------------------------------------------------------------- /apps/user-application/src/components/default-catch-boundary.tsx: -------------------------------------------------------------------------------- 1 | import { Link, rootRouteId, useMatch, useRouter } from "@tanstack/react-router"; 2 | import type { ErrorComponentProps } from "@tanstack/react-router"; 3 | import { 4 | AlertTriangle, 5 | RefreshCw, 6 | ArrowLeft, 7 | Home, 8 | ChevronDown, 9 | Bug, 10 | Mail, 11 | } from "lucide-react"; 12 | import { Button } from "@/components/ui/button"; 13 | import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; 14 | import { Alert, AlertDescription } from "@/components/ui/alert"; 15 | import { 16 | Collapsible, 17 | CollapsibleContent, 18 | CollapsibleTrigger, 19 | } from "@/components/ui/collapsible"; 20 | import { useState } from "react"; 21 | 22 | export function DefaultCatchBoundary({ error }: ErrorComponentProps) { 23 | const router = useRouter(); 24 | const isRoot = useMatch({ 25 | strict: false, 26 | select: (state) => state.id === rootRouteId, 27 | }); 28 | const [showDetails, setShowDetails] = useState(false); 29 | 30 | console.error(error); 31 | 32 | // Format error details for display 33 | const errorMessage = error?.message || "An unexpected error occurred"; 34 | const errorStack = error?.stack || ""; 35 | const hasStack = errorStack.length > 0; 36 | 37 | const handleReportError = () => { 38 | const subject = encodeURIComponent("Error Report"); 39 | const body = encodeURIComponent( 40 | `An error occurred in the application:\n\nError: ${errorMessage}\n\nStack Trace:\n${errorStack}\n\nPlease describe what you were doing when this error occurred:`, 41 | ); 42 | window.location.href = `mailto:support@example.com?subject=${subject}&body=${body}`; 43 | }; 44 | 45 | return ( 46 |
47 | 48 | 49 |
50 |
51 | 52 |
53 |
54 | Something went wrong 55 |

56 | We encountered an unexpected error. Please try again. 57 |

58 |
59 |
60 |
61 | 62 | 63 | {/* Error Alert */} 64 | 65 | 66 | 67 | {errorMessage} 68 | 69 | 70 | 71 | {/* Action Buttons */} 72 |
73 | 80 | 81 | {isRoot ? ( 82 | 88 | ) : ( 89 | 97 | )} 98 |
99 | 100 | {/* Error Details (Collapsible) */} 101 | {hasStack && ( 102 | 103 | 104 | 115 | 116 | 117 |
118 |

119 | Error Stack Trace: 120 |

121 |
122 |                     {errorStack}
123 |                   
124 |
125 |
126 |
127 | )} 128 | 129 | {/* Help Section */} 130 |
131 |
132 |
133 | If this error persists, please report it to our support team. 134 |
135 | 144 |
145 |
146 |
147 |
148 |
149 | ); 150 | } 151 | -------------------------------------------------------------------------------- /apps/user-application/src/components/demo/index.ts: -------------------------------------------------------------------------------- 1 | export { MiddlewareDemo } from './middleware-demo' -------------------------------------------------------------------------------- /apps/user-application/src/components/landing/claude-code-section.tsx: -------------------------------------------------------------------------------- 1 | import { Badge } from "@/components/ui/badge"; 2 | import { Sparkles } from "lucide-react"; 3 | 4 | export function ClaudeCodeSection() { 5 | return ( 6 |
10 |
11 |
12 | 13 | 14 | AI-Powered Setup 15 | 16 |

17 | Setup Powered by Claude Code 18 |

19 |

20 | Let Claude Code agents help you set up this project 21 |

22 |
23 | 24 |
25 |
26 | Claude Code CLI 31 |
32 | 33 |
34 |

35 | Just say this to Claude Code: 36 |

37 |
38 | Help me setup this project 39 |
40 |
41 |
42 |
43 |
44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /apps/user-application/src/components/landing/course-promo-section.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@/components/ui/button"; 2 | import { Badge } from "@/components/ui/badge"; 3 | import { CheckCircle } from "lucide-react"; 4 | 5 | export function CoursePromoSection() { 6 | return ( 7 |
8 |
9 |
10 |
11 |