├── .husky ├── .gitignore └── pre-commit ├── docs ├── public │ └── CNAME ├── adr │ └── 000-template.md ├── .vitepress │ ├── theme │ │ ├── index.ts │ │ └── components │ │ │ └── GitHubStats.vue │ └── config.ts └── index.md ├── packages ├── core │ ├── README.md │ ├── index.ts │ ├── tsconfig.json │ └── package.json ├── ui │ ├── hooks │ │ └── index.ts │ ├── styles.css │ ├── lib │ │ └── utils.ts │ ├── components │ │ ├── skeleton.tsx │ │ ├── textarea.tsx │ │ ├── label.tsx │ │ ├── input.tsx │ │ ├── separator.tsx │ │ ├── checkbox.tsx │ │ ├── switch.tsx │ │ ├── radio-group.tsx │ │ ├── avatar.tsx │ │ ├── scroll-area.tsx │ │ ├── button.tsx │ │ └── card.tsx │ ├── components.json │ ├── tsconfig.json │ ├── index.ts │ ├── scripts │ │ ├── format-utils.ts │ │ ├── add.ts │ │ └── list.ts │ └── package.json ├── typescript-config │ ├── node.jsonc │ ├── cloudflare.jsonc │ ├── react.jsonc │ ├── package.json │ ├── base.jsonc │ └── README.md └── ws-protocol │ ├── index.ts │ ├── tsconfig.json │ ├── package.json │ ├── README.md │ └── messages.ts ├── apps ├── app │ ├── components │ │ ├── index.ts │ │ ├── layout │ │ │ ├── constants.ts │ │ │ ├── sidebar.tsx │ │ │ ├── index.tsx │ │ │ ├── sidebar-nav.tsx │ │ │ └── header.tsx │ │ ├── error.tsx │ │ ├── user-menu.tsx │ │ └── auth │ │ │ └── social-login.tsx │ ├── lib │ │ ├── utils.ts │ │ ├── store.ts │ │ ├── auth.ts │ │ ├── trpc.ts │ │ ├── query.ts │ │ └── auth-config.ts │ ├── public │ │ ├── favicon.ico │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── robots.txt │ │ └── site.manifest │ ├── postcss.config.js │ ├── routes │ │ ├── (app) │ │ │ ├── dashboard.tsx │ │ │ └── route.tsx │ │ ├── __root.tsx │ │ └── (auth) │ │ │ └── login.tsx │ ├── components.json │ ├── tsconfig.json │ ├── global.d.ts │ ├── index.html │ ├── index.tsx │ ├── package.json │ ├── CLAUDE.md │ ├── tailwind.config.css │ └── hooks │ │ └── use-login-form.ts ├── web │ ├── lib │ │ └── utils.ts │ ├── public │ │ ├── favicon.ico │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── robots.txt │ │ └── site.manifest │ ├── postcss.config.js │ ├── astro.config.mjs │ ├── tsconfig.json │ ├── .env.example │ ├── package.json │ ├── _headers │ ├── wrangler.jsonc │ └── tailwind.config.css ├── api │ ├── lib │ │ ├── utils.ts │ │ ├── ai.ts │ │ ├── trpc.ts │ │ ├── db.ts │ │ ├── env.ts │ │ ├── loaders.ts │ │ ├── context.ts │ │ └── app.ts │ ├── tsconfig.json │ ├── CLAUDE.md │ ├── index.ts │ ├── routers │ │ ├── user.ts │ │ └── organization.ts │ ├── worker.ts │ ├── package.json │ ├── Dockerfile │ └── dev.ts └── email │ ├── index.ts │ ├── emails │ ├── otp-sign-in.tsx │ ├── otp-password-reset.tsx │ ├── otp-verification.tsx │ ├── password-reset.tsx │ └── email-verification.tsx │ ├── assets │ └── logo.svg │ ├── tsconfig.json │ ├── utils │ └── render.ts │ ├── package.json │ ├── README.md │ └── templates │ └── email-verification.tsx ├── infra ├── templates │ ├── backend-gcs.example.hcl │ ├── env-roots │ │ └── hybrid │ │ │ ├── main.tf │ │ │ ├── terraform.tfvars.example │ │ │ ├── providers.tf │ │ │ ├── README.md │ │ │ └── variables.tf │ └── backend-r2.example.hcl ├── modules │ ├── cloudflare │ │ ├── dns │ │ │ ├── outputs.tf │ │ │ ├── variables.tf │ │ │ └── main.tf │ │ ├── r2-bucket │ │ │ ├── outputs.tf │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ └── hyperdrive │ │ │ ├── outputs.tf │ │ │ ├── variables.tf │ │ │ └── main.tf │ └── gcp │ │ ├── gcs │ │ ├── outputs.tf │ │ ├── variables.tf │ │ └── main.tf │ │ ├── cloud-run │ │ ├── outputs.tf │ │ ├── variables.tf │ │ └── main.tf │ │ └── cloud-sql │ │ ├── outputs.tf │ │ ├── variables.tf │ │ └── main.tf ├── .gitignore ├── envs │ ├── dev │ │ └── edge │ │ │ ├── terraform.tfvars.example │ │ │ ├── providers.tf │ │ │ ├── variables.tf │ │ │ ├── main.tf │ │ │ └── .terraform.lock.hcl │ ├── prod │ │ └── edge │ │ │ ├── terraform.tfvars.example │ │ │ ├── providers.tf │ │ │ ├── variables.tf │ │ │ ├── main.tf │ │ │ └── .terraform.lock.hcl │ ├── preview │ │ └── edge │ │ │ ├── terraform.tfvars.example │ │ │ ├── providers.tf │ │ │ ├── variables.tf │ │ │ ├── main.tf │ │ │ └── .terraform.lock.hcl │ └── staging │ │ └── edge │ │ ├── terraform.tfvars.example │ │ ├── providers.tf │ │ ├── variables.tf │ │ ├── main.tf │ │ └── .terraform.lock.hcl └── stacks │ ├── hybrid │ ├── outputs.tf │ ├── variables.tf │ └── main.tf │ └── edge │ ├── outputs.tf │ ├── main.tf │ └── variables.tf ├── db ├── backups │ └── .gitignore ├── schema │ ├── index.ts │ ├── passkey.ts │ └── team.ts ├── migrations │ └── meta │ │ └── _journal.json ├── index.ts ├── scripts │ ├── setup-extensions.sql │ ├── seed.ts │ └── generate-auth-schema.ts ├── tsconfig.json ├── seeds │ └── users.ts ├── drizzle.config.ts ├── package.json └── CLAUDE.md ├── .gemini └── settings.json ├── .vscode ├── mcp.json ├── extensions.json └── settings.json ├── .github ├── FUNDING.yml ├── copilot-instructions.md ├── workflows │ ├── conventional-commits.yml │ └── deploy.yml └── dependabot.yml ├── scripts ├── tsconfig.json ├── package.json ├── post-install.ts └── mcp.ts ├── vitest.config.ts ├── .prettierignore ├── tsconfig.json ├── .editorconfig ├── vitest.workspace.ts ├── .gitattributes ├── LICENSE ├── .claude └── commands │ ├── review-better-auth.md │ └── validate-auth-schema.md ├── .gitignore ├── .env └── eslint.config.ts /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /docs/public/CNAME: -------------------------------------------------------------------------------- 1 | reactstarter.com 2 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # Core Package 2 | -------------------------------------------------------------------------------- /apps/app/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./error"; 2 | -------------------------------------------------------------------------------- /apps/app/lib/utils.ts: -------------------------------------------------------------------------------- 1 | export { cn } from "@repo/ui"; 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -e 3 | 4 | bunx lint-staged 5 | -------------------------------------------------------------------------------- /packages/ui/hooks/index.ts: -------------------------------------------------------------------------------- 1 | // Export custom hooks here when they are added 2 | -------------------------------------------------------------------------------- /infra/templates/backend-gcs.example.hcl: -------------------------------------------------------------------------------- 1 | bucket = "tf-state" 2 | prefix = "prod/hybrid" 3 | -------------------------------------------------------------------------------- /apps/web/lib/utils.ts: -------------------------------------------------------------------------------- 1 | // Re-export utils from the UI package 2 | export * from "@repo/ui/lib/utils"; 3 | -------------------------------------------------------------------------------- /apps/app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriasoft/react-starter-kit/HEAD/apps/app/public/favicon.ico -------------------------------------------------------------------------------- /apps/app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriasoft/react-starter-kit/HEAD/apps/app/public/logo192.png -------------------------------------------------------------------------------- /apps/app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriasoft/react-starter-kit/HEAD/apps/app/public/logo512.png -------------------------------------------------------------------------------- /apps/web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriasoft/react-starter-kit/HEAD/apps/web/public/favicon.ico -------------------------------------------------------------------------------- /apps/web/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriasoft/react-starter-kit/HEAD/apps/web/public/logo192.png -------------------------------------------------------------------------------- /apps/web/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kriasoft/react-starter-kit/HEAD/apps/web/public/logo512.png -------------------------------------------------------------------------------- /db/backups/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all backup files 2 | *.sql 3 | 4 | # Keep the directory in git 5 | !.gitignore 6 | -------------------------------------------------------------------------------- /.gemini/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "preferredEditor": "vscode", 3 | "contextFileName": ["CLAUDE.md", "CLAUDE.local.md"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | 3 | # Allow crawling of all content 4 | User-agent: * 5 | Disallow: 6 | -------------------------------------------------------------------------------- /apps/web/public/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | 3 | # Allow crawling of all content 4 | User-agent: * 5 | Disallow: 6 | -------------------------------------------------------------------------------- /apps/app/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/web/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /infra/modules/cloudflare/dns/outputs.tf: -------------------------------------------------------------------------------- 1 | output "hostname" { 2 | description = "The configured hostname" 3 | value = var.hostname 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "servers": { 3 | "github": { 4 | "type": "http", 5 | "url": "https://api.githubcopilot.com/mcp/" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /db/schema/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./invitation"; 2 | export * from "./organization"; 3 | export * from "./passkey"; 4 | export * from "./team"; 5 | export * from "./user"; 6 | -------------------------------------------------------------------------------- /packages/ui/styles.css: -------------------------------------------------------------------------------- 1 | /* shadcn CLI configuration only - actual styles in app globals.css */ 2 | 3 | @import "tailwindcss/preflight"; 4 | @import "tailwindcss/utilities"; 5 | -------------------------------------------------------------------------------- /packages/core/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Core package entrypoint. 3 | * 4 | * Placeholder for shared utilities and WebSocket functionality. 5 | */ 6 | 7 | export default {}; 8 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # GitHub Sponsors configuration for React Starter Kit 2 | # Enables sponsorship options on the repository's main page 3 | 4 | open_collective: react-starter-kit 5 | github: koistya 6 | -------------------------------------------------------------------------------- /apps/api/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /packages/ui/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /packages/typescript-config/node.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "./base.jsonc", 4 | "compilerOptions": { 5 | "lib": ["ESNext"], 6 | "types": ["bun"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/typescript-config/cloudflare.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "./base.jsonc", 4 | "compilerOptions": { 5 | "lib": ["ESNext"], 6 | "types": ["@cloudflare/workers-types"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /infra/.gitignore: -------------------------------------------------------------------------------- 1 | .terraform/ 2 | *.tfstate 3 | *.tfstate.* 4 | *.tfvars 5 | *.tfvars.json 6 | backend.hcl 7 | override.tf 8 | override.tf.json 9 | *_override.tf 10 | *_override.tf.json 11 | .terraform.tfstate.lock.info 12 | crash.log 13 | crash.*.log 14 | -------------------------------------------------------------------------------- /infra/envs/dev/edge/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | cloudflare_api_token = "" 2 | cloudflare_account_id = "" 3 | cloudflare_zone_id = "" 4 | hostname = "" 5 | project_slug = "myapp" 6 | environment = "dev" 7 | neon_database_url = "" 8 | -------------------------------------------------------------------------------- /infra/envs/prod/edge/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | cloudflare_api_token = "" 2 | cloudflare_account_id = "" 3 | cloudflare_zone_id = "" 4 | hostname = "" 5 | project_slug = "myapp" 6 | environment = "prod" 7 | neon_database_url = "" 8 | -------------------------------------------------------------------------------- /infra/envs/preview/edge/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | cloudflare_api_token = "" 2 | cloudflare_account_id = "" 3 | cloudflare_zone_id = "" 4 | hostname = "" 5 | project_slug = "myapp" 6 | environment = "preview" 7 | neon_database_url = "" 8 | -------------------------------------------------------------------------------- /infra/envs/staging/edge/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | cloudflare_api_token = "" 2 | cloudflare_account_id = "" 3 | cloudflare_zone_id = "" 4 | hostname = "" 5 | project_slug = "myapp" 6 | environment = "staging" 7 | neon_database_url = "" 8 | -------------------------------------------------------------------------------- /infra/modules/cloudflare/r2-bucket/outputs.tf: -------------------------------------------------------------------------------- 1 | output "name" { 2 | description = "R2 bucket name" 3 | value = cloudflare_r2_bucket.bucket.name 4 | } 5 | 6 | output "id" { 7 | description = "R2 bucket ID" 8 | value = cloudflare_r2_bucket.bucket.id 9 | } 10 | -------------------------------------------------------------------------------- /apps/email/index.ts: -------------------------------------------------------------------------------- 1 | export { EmailVerification } from "./templates/email-verification.js"; 2 | export { PasswordReset } from "./templates/password-reset.js"; 3 | export { OTPEmail } from "./templates/otp-email.js"; 4 | export { renderEmailToHtml, renderEmailToText } from "./utils/render.js"; 5 | -------------------------------------------------------------------------------- /db/migrations/meta/_journal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "7", 3 | "dialect": "postgresql", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "1", 8 | "when": 1751197781613, 9 | "tag": "0000_init", 10 | "breakpoints": true 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /infra/modules/cloudflare/dns/variables.tf: -------------------------------------------------------------------------------- 1 | variable "zone_id" { 2 | description = "Cloudflare zone ID" 3 | type = string 4 | } 5 | 6 | variable "hostname" { 7 | description = "DNS hostname (e.g., 'example.com' or 'staging.example.com')" 8 | type = string 9 | } 10 | -------------------------------------------------------------------------------- /infra/modules/gcp/gcs/outputs.tf: -------------------------------------------------------------------------------- 1 | output "bucket_name" { 2 | description = "Name of the GCS bucket" 3 | value = google_storage_bucket.bucket.name 4 | } 5 | 6 | output "url" { 7 | description = "URL of the GCS bucket" 8 | value = google_storage_bucket.bucket.url 9 | } 10 | -------------------------------------------------------------------------------- /scripts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../packages/typescript-config/node.jsonc", 3 | "compilerOptions": { 4 | "composite": true, 5 | "noEmit": true, 6 | "tsBuildInfoFile": "../.cache/tsconfig/scripts.tsbuildinfo" 7 | }, 8 | "include": ["**/*.ts", "**/*.js"] 9 | } 10 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | React Starter Kit is a production-ready, edge-first web application template built with Bun, TypeScript, React 19, and Cloudflare Workers. This monorepo delivers end-to-end type safety, global edge deployment, and enterprise-grade patterns from day one. #file:../CLAUDE.md 2 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | /** 4 | * Vitest configuration. 5 | * 6 | * @see https://vitest.dev/config/ 7 | */ 8 | export default defineConfig({ 9 | test: { 10 | cache: { 11 | dir: "./.cache/vitest", 12 | }, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /db/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Database schema exports. 3 | * 4 | * Re-exports Drizzle ORM schemas for users, organizations, and authentication. 5 | */ 6 | 7 | import * as schema from "./schema"; 8 | 9 | export * from "./schema"; 10 | export { schema }; 11 | export type DatabaseSchema = typeof schema; 12 | -------------------------------------------------------------------------------- /infra/modules/gcp/cloud-run/outputs.tf: -------------------------------------------------------------------------------- 1 | output "url" { 2 | description = "URL of the Cloud Run service" 3 | value = google_cloud_run_v2_service.service.uri 4 | } 5 | 6 | output "service_name" { 7 | description = "Name of the Cloud Run service" 8 | value = google_cloud_run_v2_service.service.name 9 | } 10 | -------------------------------------------------------------------------------- /packages/typescript-config/react.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "./base.jsonc", 4 | "compilerOptions": { 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "jsx": "react-jsx", 7 | "jsxImportSource": "react", 8 | "types": ["vite/client"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/app/routes/(app)/dashboard.tsx: -------------------------------------------------------------------------------- 1 | import { createFileRoute, redirect } from "@tanstack/react-router"; 2 | 3 | export const Route = createFileRoute("/(app)/dashboard")({ 4 | beforeLoad: () => { 5 | // Redirect to index which is the main dashboard 6 | throw redirect({ 7 | to: "/", 8 | }); 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /apps/email/emails/otp-sign-in.tsx: -------------------------------------------------------------------------------- 1 | import { OTPEmail } from "../templates/otp-email"; 2 | 3 | export default function OTPSignInPreview() { 4 | return ( 5 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /infra/modules/cloudflare/hyperdrive/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | description = "Hyperdrive configuration ID" 3 | value = cloudflare_hyperdrive_config.hyperdrive.id 4 | } 5 | 6 | output "name" { 7 | description = "Hyperdrive configuration name" 8 | value = cloudflare_hyperdrive_config.hyperdrive.name 9 | } 10 | -------------------------------------------------------------------------------- /infra/modules/cloudflare/r2-bucket/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | cloudflare = { 4 | source = "cloudflare/cloudflare" 5 | } 6 | } 7 | } 8 | 9 | resource "cloudflare_r2_bucket" "bucket" { 10 | account_id = var.account_id 11 | name = var.name 12 | location = var.location 13 | } 14 | -------------------------------------------------------------------------------- /packages/ws-protocol/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * WebSocket protocol definitions for the application. 3 | * 4 | * @example 5 | * ```ts 6 | * import { createAppRouter, Ping, Pong, Echo } from "@repo/ws-protocol"; 7 | * 8 | * const router = createAppRouter(); 9 | * ``` 10 | */ 11 | 12 | export * from "./messages"; 13 | export * from "./router"; 14 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Compiled & generated output 2 | /app/queries/ 3 | /*/dist/ 4 | /**/*.generated.ts 5 | /**/*.gen.ts 6 | 7 | # Cache 8 | /.cache 9 | 10 | # Bun and Node.js modules 11 | /node_modules 12 | /bun.lock 13 | 14 | # TypeScript 15 | /tsconfig.base.json 16 | 17 | # Terraform 18 | .terraform/ 19 | 20 | # Misc 21 | /.husky 22 | *.hbs 23 | -------------------------------------------------------------------------------- /apps/email/emails/otp-password-reset.tsx: -------------------------------------------------------------------------------- 1 | import { OTPEmail } from "../templates/otp-email"; 2 | 3 | export default function OTPPasswordResetPreview() { 4 | return ( 5 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /apps/email/emails/otp-verification.tsx: -------------------------------------------------------------------------------- 1 | import { OTPEmail } from "../templates/otp-email"; 2 | 3 | export default function OTPVerificationPreview() { 4 | return ( 5 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../typescript-config/node.jsonc", 3 | "compilerOptions": { 4 | "composite": true, 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "outDir": "./dist", 8 | "rootDir": "./" 9 | }, 10 | "include": ["**/*.ts"], 11 | "exclude": ["**/node_modules/**/*", "dist"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/ws-protocol/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../typescript-config/node.jsonc", 3 | "compilerOptions": { 4 | "composite": true, 5 | "declaration": true, 6 | "emitDeclarationOnly": true, 7 | "outDir": "./dist", 8 | "rootDir": "./" 9 | }, 10 | "include": ["**/*.ts"], 11 | "exclude": ["**/node_modules/**/*", "dist"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/ui/components/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ); 13 | } 14 | 15 | export { Skeleton }; 16 | -------------------------------------------------------------------------------- /apps/email/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | R 4 | -------------------------------------------------------------------------------- /infra/modules/cloudflare/r2-bucket/variables.tf: -------------------------------------------------------------------------------- 1 | variable "account_id" { 2 | description = "Cloudflare account ID" 3 | type = string 4 | } 5 | 6 | variable "name" { 7 | description = "R2 bucket name" 8 | type = string 9 | } 10 | 11 | variable "location" { 12 | description = "R2 bucket location" 13 | type = string 14 | default = "enam" 15 | } 16 | -------------------------------------------------------------------------------- /infra/stacks/hybrid/outputs.tf: -------------------------------------------------------------------------------- 1 | output "api_url" { 2 | value = module.api.url 3 | description = "Cloud Run service URL" 4 | } 5 | 6 | output "edge_api_url" { 7 | value = var.enable_edge_routing && var.hostname != "" ? "https://${var.hostname}/api" : null 8 | description = "Public API URL via Cloudflare edge (null if edge routing disabled or no hostname)" 9 | } 10 | -------------------------------------------------------------------------------- /apps/email/emails/password-reset.tsx: -------------------------------------------------------------------------------- 1 | import { PasswordReset } from "../templates/password-reset"; 2 | 3 | export default function PasswordResetPreview() { 4 | return ( 5 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /infra/envs/dev/edge/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12, < 2.0" 3 | 4 | required_providers { 5 | cloudflare = { 6 | source = "cloudflare/cloudflare" 7 | version = "~> 5.0" 8 | } 9 | } 10 | } 11 | 12 | provider "cloudflare" { 13 | api_token = var.cloudflare_api_token 14 | } 15 | # See "Provider Versions" in docs/specs/infra-terraform.md 16 | -------------------------------------------------------------------------------- /infra/envs/preview/edge/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12, < 2.0" 3 | 4 | required_providers { 5 | cloudflare = { 6 | source = "cloudflare/cloudflare" 7 | version = "~> 5.0" 8 | } 9 | } 10 | } 11 | 12 | provider "cloudflare" { 13 | api_token = var.cloudflare_api_token 14 | } 15 | # See "Provider Versions" in docs/specs/infra-terraform.md 16 | -------------------------------------------------------------------------------- /infra/envs/prod/edge/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12, < 2.0" 3 | 4 | required_providers { 5 | cloudflare = { 6 | source = "cloudflare/cloudflare" 7 | version = "~> 5.0" 8 | } 9 | } 10 | } 11 | 12 | provider "cloudflare" { 13 | api_token = var.cloudflare_api_token 14 | } 15 | # See "Provider Versions" in docs/specs/infra-terraform.md 16 | -------------------------------------------------------------------------------- /infra/envs/staging/edge/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.12, < 2.0" 3 | 4 | required_providers { 5 | cloudflare = { 6 | source = "cloudflare/cloudflare" 7 | version = "~> 5.0" 8 | } 9 | } 10 | } 11 | 12 | provider "cloudflare" { 13 | api_token = var.cloudflare_api_token 14 | } 15 | # See "Provider Versions" in docs/specs/infra-terraform.md 16 | -------------------------------------------------------------------------------- /db/scripts/setup-extensions.sql: -------------------------------------------------------------------------------- 1 | -- Sets up required PostgreSQL extensions for the database. 2 | -- Ensures pg_uuidv7 is available for UUIDv7 support in Drizzle ORM schemas. 3 | -- Safe to run multiple times; uses IF NOT EXISTS. 4 | -- Note: Native UUIDv7 support is coming in PostgreSQL v18, 5 | -- so this extension may not be needed in future versions. 6 | 7 | CREATE EXTENSION IF NOT EXISTS "pg_uuidv7"; 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { "path": "./apps/api" }, 5 | { "path": "./apps/app" }, 6 | { "path": "./apps/email" }, 7 | { "path": "./apps/web" }, 8 | { "path": "./db" }, 9 | { "path": "./packages/core" }, 10 | { "path": "./packages/ui" }, 11 | { "path": "./packages/ws-protocol" }, 12 | { "path": "./scripts" } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # For more information about the properties used in 2 | # this file, please see the EditorConfig documentation: 3 | # https://editorconfig.org/ 4 | 5 | root = true 6 | 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | indent_size = 2 11 | indent_style = space 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /apps/email/emails/email-verification.tsx: -------------------------------------------------------------------------------- 1 | import { EmailVerification } from "../templates/email-verification"; 2 | 3 | export default function EmailVerificationPreview() { 4 | return ( 5 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /apps/app/components/layout/constants.ts: -------------------------------------------------------------------------------- 1 | import { Activity, FileText, Home, Settings, Users } from "lucide-react"; 2 | 3 | export const sidebarItems = [ 4 | { icon: Home, label: "Dashboard", to: "/" }, 5 | { icon: Activity, label: "Analytics", to: "/analytics" }, 6 | { icon: Users, label: "Users", to: "/users" }, 7 | { icon: FileText, label: "Reports", to: "/reports" }, 8 | { icon: Settings, label: "Settings", to: "/settings" }, 9 | ]; 10 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/core", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "exports": { 7 | ".": "./index.ts", 8 | "./package.json": "./package.json" 9 | }, 10 | "scripts": { 11 | "typecheck": "tsc --noEmit" 12 | }, 13 | "devDependencies": { 14 | "@repo/typescript-config": "workspace:*", 15 | "@types/bun": "^1.3.3", 16 | "typescript": "~5.9.3" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /db/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../packages/typescript-config/node.jsonc", 3 | "compilerOptions": { 4 | "tsBuildInfoFile": "dist/.tsbuildinfo", 5 | "composite": true, 6 | "declaration": true, 7 | "emitDeclarationOnly": true, 8 | "outDir": "./dist", 9 | "types": ["bun", "node", "vite/client"] 10 | }, 11 | "include": ["schema/**/*.ts", "*.ts", "*.json"], 12 | "exclude": ["**/dist/**/*", "**/node_modules/**/*", "scripts/**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/scripts", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "typecheck": "tsc --noEmit" 8 | }, 9 | "dependencies": { 10 | "@modelcontextprotocol/sdk": "^1.24.3", 11 | "execa": "^9.6.1", 12 | "got": "^14.6.5", 13 | "zx": "^8.8.5" 14 | }, 15 | "devDependencies": { 16 | "@repo/typescript-config": "workspace:*", 17 | "typescript": "~5.9.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /infra/modules/gcp/gcs/variables.tf: -------------------------------------------------------------------------------- 1 | variable "project_id" { 2 | description = "GCP project ID" 3 | type = string 4 | } 5 | 6 | variable "location" { 7 | description = "GCS bucket location" 8 | type = string 9 | } 10 | 11 | variable "bucket_name" { 12 | description = "Name of the GCS bucket" 13 | type = string 14 | } 15 | 16 | variable "cors_origins" { 17 | description = "List of CORS origins" 18 | type = list(string) 19 | default = [] 20 | } 21 | -------------------------------------------------------------------------------- /apps/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../packages/typescript-config/node.jsonc", 3 | "compilerOptions": { 4 | "composite": true, 5 | "declaration": true, 6 | "declarationMap": true, 7 | "noEmit": false, 8 | "outDir": "./dist", 9 | "types": ["@cloudflare/workers-types", "bun"] 10 | }, 11 | "include": ["**/*.ts", "**/*.tsx"], 12 | "exclude": ["**/dist/**/*", "**/node_modules/**/*"], 13 | "references": [{ "path": "../../packages/core" }, { "path": "../../db" }] 14 | } 15 | -------------------------------------------------------------------------------- /apps/app/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "css": "styles/globals.css", 8 | "baseColor": "neutral", 9 | "cssVariables": true, 10 | "prefix": "" 11 | }, 12 | "aliases": { 13 | "components": "@/components", 14 | "utils": "@/lib/utils", 15 | "ui": "@repo/ui", 16 | "lib": "@/lib", 17 | "hooks": "@/hooks" 18 | }, 19 | "iconLibrary": "lucide" 20 | } 21 | -------------------------------------------------------------------------------- /apps/app/lib/store.ts: -------------------------------------------------------------------------------- 1 | import { createStore, Provider } from "jotai"; 2 | import type { ReactNode } from "react"; 3 | import { createElement } from "react"; 4 | 5 | /** 6 | * Global state management powered by Jotai. 7 | * @see https://jotai.org/ 8 | */ 9 | export const store = createStore(); 10 | 11 | export function StoreProvider(props: StoreProviderProps) { 12 | return createElement(Provider, { store, ...props }); 13 | } 14 | 15 | export type StoreProviderProps = { 16 | children: ReactNode; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/ui/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "css": "styles.css", 8 | "baseColor": "neutral", 9 | "cssVariables": true, 10 | "prefix": "" 11 | }, 12 | "aliases": { 13 | "components": "@/components", 14 | "utils": "@/lib/utils", 15 | "ui": "@/components", 16 | "lib": "@/lib", 17 | "hooks": "@/hooks" 18 | }, 19 | "iconLibrary": "lucide" 20 | } 21 | -------------------------------------------------------------------------------- /vitest.workspace.ts: -------------------------------------------------------------------------------- 1 | import { defineWorkspace } from "vitest/config"; 2 | import { workspaces } from "./package.json"; 3 | 4 | /** 5 | * Inline Vitest configuration for all workspaces. 6 | * 7 | * @see https://vitest.dev/guide/workspace 8 | */ 9 | export default defineWorkspace( 10 | workspaces 11 | .filter((name) => !["scripts"].includes(name)) 12 | .map((name) => ({ 13 | extends: `./${name}/vite.config.ts`, 14 | test: { 15 | name, 16 | root: `./${name}`, 17 | }, 18 | })), 19 | ); 20 | -------------------------------------------------------------------------------- /infra/stacks/edge/outputs.tf: -------------------------------------------------------------------------------- 1 | # Hyperdrive ID - copy to wrangler.jsonc hyperdrive binding 2 | output "hyperdrive_id" { 3 | value = module.hyperdrive.id 4 | description = "Hyperdrive configuration ID for wrangler.jsonc" 5 | } 6 | 7 | output "hyperdrive_name" { 8 | value = module.hyperdrive.name 9 | description = "Hyperdrive configuration name" 10 | } 11 | 12 | output "hostname" { 13 | value = var.hostname != "" ? var.hostname : null 14 | description = "Configured hostname (null if using workers.dev)" 15 | } 16 | -------------------------------------------------------------------------------- /apps/api/CLAUDE.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | tRPC API and authentication router that can be deployed either as part of the Cloudflare Workers edge layer or as a standalone API server. 4 | 5 | ## Tech Stack 6 | 7 | TypeScript, tRPC, Drizzle ORM, Better Auth. 8 | 9 | ## Commands 10 | 11 | - `bun dev` - Start development server 12 | - `bun build` - Build production server 13 | - `bun test` - Run tests 14 | - `bun typecheck` - Type check the codebase 15 | 16 | ## References 17 | 18 | - https://trpc.io/llms.txt 19 | - https://www.better-auth.com/llms.txt 20 | -------------------------------------------------------------------------------- /apps/web/astro.config.mjs: -------------------------------------------------------------------------------- 1 | import react from "@astrojs/react"; 2 | import { defineConfig } from "astro/config"; 3 | import { loadEnv } from "vite"; 4 | 5 | // Allows sharing root .env variables with Astro build process 6 | loadEnv(process.env.NODE_ENV || "development", "../..", ""); 7 | 8 | export default defineConfig({ 9 | srcDir: ".", 10 | publicDir: "./public", 11 | outDir: "./dist", 12 | output: "static", 13 | integrations: [react()], 14 | vite: { 15 | css: { 16 | postcss: "./postcss.config.js", 17 | }, 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /docs/adr/000-template.md: -------------------------------------------------------------------------------- 1 | # ADR-NNN Title 2 | 3 | **Status:** Proposed | Accepted | Deprecated | Superseded 4 | **Date:** YYYY-MM-DD 5 | **Tags:** tag1, tag2 6 | 7 | ## Problem 8 | 9 | - One or two sentences on the decision trigger or constraint. 10 | 11 | ## Decision 12 | 13 | - The chosen approach in a short paragraph. 14 | 15 | ## Alternatives (brief) 16 | 17 | - Option A — why not. 18 | - Option B — why not. 19 | 20 | ## Impact 21 | 22 | - Positive: 23 | - Negative/Risks: 24 | 25 | ## Links 26 | 27 | - Code/Docs: 28 | - Related ADRs: 29 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../typescript-config/react.jsonc", 3 | "compilerOptions": { 4 | "composite": true, 5 | "declaration": true, 6 | "declarationMap": true, 7 | "emitDeclarationOnly": true, 8 | "outDir": "./dist", 9 | "rootDir": "./", 10 | "tsBuildInfoFile": "../../.cache/tsconfig/ui.tsbuildinfo", 11 | "baseUrl": ".", 12 | "paths": { 13 | "@/*": ["./*"] 14 | } 15 | }, 16 | "include": ["components", "hooks", "lib", "index.ts"], 17 | "exclude": ["dist", "node_modules"] 18 | } 19 | -------------------------------------------------------------------------------- /apps/email/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "allowSyntheticDefaultImports": true, 7 | "esModuleInterop": true, 8 | "skipLibCheck": true, 9 | "strict": true, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "outDir": "dist", 13 | "rootDir": ".", 14 | "jsx": "react-jsx", 15 | "types": ["node"] 16 | }, 17 | "include": ["**/*.ts", "**/*.tsx"], 18 | "exclude": ["dist", "node_modules", ".email"] 19 | } 20 | -------------------------------------------------------------------------------- /infra/modules/cloudflare/dns/main.tf: -------------------------------------------------------------------------------- 1 | # Proxied DNS record for Cloudflare Workers routing. 2 | # Workers and routes are managed via Wrangler, not Terraform. 3 | 4 | terraform { 5 | required_providers { 6 | cloudflare = { 7 | source = "cloudflare/cloudflare" 8 | } 9 | } 10 | } 11 | 12 | resource "cloudflare_dns_record" "record" { 13 | zone_id = var.zone_id 14 | name = var.hostname 15 | type = "AAAA" 16 | content = "100::" 17 | ttl = 1 # Auto (required for proxied records) 18 | proxied = true 19 | comment = "Managed by Terraform" 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "anthropic.claude-code", 4 | "astro-build.vscode-astro", 5 | "bradlc.vscode-tailwindcss", 6 | "dbaeumer.vscode-eslint", 7 | "dbcode.dbcode", 8 | "editorconfig.editorconfig", 9 | "esbenp.prettier-vscode", 10 | "github.copilot", 11 | "github.vscode-github-actions", 12 | "hashicorp.terraform", 13 | "mikestead.dotenv", 14 | "oven.bun-vscode", 15 | "rphlmr.vscode-drizzle-orm", 16 | "streetsidesoftware.code-spell-checker", 17 | "vscode-icons-team.vscode-icons" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /infra/stacks/edge/main.tf: -------------------------------------------------------------------------------- 1 | # Edge stack: Cloudflare infrastructure for Workers deployment. 2 | # Workers are deployed separately via Wrangler. 3 | 4 | module "hyperdrive" { 5 | source = "../../modules/cloudflare/hyperdrive" 6 | 7 | account_id = var.cloudflare_account_id 8 | name = "${var.project_slug}-${var.environment}" 9 | database_url = var.neon_database_url 10 | } 11 | 12 | module "dns" { 13 | count = var.hostname != "" ? 1 : 0 14 | source = "../../modules/cloudflare/dns" 15 | 16 | zone_id = var.cloudflare_zone_id 17 | hostname = var.hostname 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/conventional-commits.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions workflow 2 | # https://help.github.com/actions 3 | # https://www.conventionalcommits.org 4 | 5 | name: "Conventional Commits" 6 | 7 | on: 8 | pull_request_target: 9 | types: 10 | - opened 11 | - edited 12 | - synchronize 13 | 14 | permissions: 15 | pull-requests: read 16 | 17 | jobs: 18 | lint: 19 | name: "Lint PR Title" 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: amannn/action-semantic-pull-request@v5 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | -------------------------------------------------------------------------------- /infra/envs/dev/edge/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cloudflare_api_token" { 2 | type = string 3 | sensitive = true 4 | } 5 | 6 | variable "cloudflare_account_id" { 7 | type = string 8 | } 9 | 10 | variable "cloudflare_zone_id" { 11 | type = string 12 | default = "" 13 | } 14 | 15 | variable "hostname" { 16 | type = string 17 | default = "" 18 | } 19 | 20 | variable "project_slug" { 21 | type = string 22 | } 23 | 24 | variable "environment" { 25 | type = string 26 | } 27 | 28 | variable "neon_database_url" { 29 | type = string 30 | sensitive = true 31 | } 32 | -------------------------------------------------------------------------------- /infra/envs/preview/edge/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cloudflare_api_token" { 2 | type = string 3 | sensitive = true 4 | } 5 | 6 | variable "cloudflare_account_id" { 7 | type = string 8 | } 9 | 10 | variable "cloudflare_zone_id" { 11 | type = string 12 | default = "" 13 | } 14 | 15 | variable "hostname" { 16 | type = string 17 | default = "" 18 | } 19 | 20 | variable "project_slug" { 21 | type = string 22 | } 23 | 24 | variable "environment" { 25 | type = string 26 | } 27 | 28 | variable "neon_database_url" { 29 | type = string 30 | sensitive = true 31 | } 32 | -------------------------------------------------------------------------------- /infra/envs/prod/edge/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cloudflare_api_token" { 2 | type = string 3 | sensitive = true 4 | } 5 | 6 | variable "cloudflare_account_id" { 7 | type = string 8 | } 9 | 10 | variable "cloudflare_zone_id" { 11 | type = string 12 | default = "" 13 | } 14 | 15 | variable "hostname" { 16 | type = string 17 | default = "" 18 | } 19 | 20 | variable "project_slug" { 21 | type = string 22 | } 23 | 24 | variable "environment" { 25 | type = string 26 | } 27 | 28 | variable "neon_database_url" { 29 | type = string 30 | sensitive = true 31 | } 32 | -------------------------------------------------------------------------------- /infra/envs/staging/edge/variables.tf: -------------------------------------------------------------------------------- 1 | variable "cloudflare_api_token" { 2 | type = string 3 | sensitive = true 4 | } 5 | 6 | variable "cloudflare_account_id" { 7 | type = string 8 | } 9 | 10 | variable "cloudflare_zone_id" { 11 | type = string 12 | default = "" 13 | } 14 | 15 | variable "hostname" { 16 | type = string 17 | default = "" 18 | } 19 | 20 | variable "project_slug" { 21 | type = string 22 | } 23 | 24 | variable "environment" { 25 | type = string 26 | } 27 | 28 | variable "neon_database_url" { 29 | type = string 30 | sensitive = true 31 | } 32 | -------------------------------------------------------------------------------- /infra/templates/env-roots/hybrid/main.tf: -------------------------------------------------------------------------------- 1 | module "stack" { 2 | source = "../../../stacks/hybrid" 3 | 4 | gcp_project_id = var.gcp_project_id 5 | gcp_region = var.gcp_region 6 | project_slug = var.project_slug 7 | environment = var.environment 8 | api_image = var.api_image 9 | cloud_sql_tier = var.cloud_sql_tier 10 | 11 | # --- Edge routing (optional) --- 12 | # enable_edge_routing = true 13 | # cloudflare_zone_id = var.cloudflare_zone_id 14 | # hostname = var.hostname 15 | } 16 | 17 | output "api_url" { 18 | value = module.stack.api_url 19 | } 20 | -------------------------------------------------------------------------------- /apps/app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../packages/typescript-config/react.jsonc", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "tsBuildInfoFile": "../../.cache/tsconfig/web.tsbuildinfo", 6 | "baseUrl": ".", 7 | "paths": { 8 | "@/*": ["./*"], 9 | "@repo/ui": ["../../packages/ui"], 10 | "@repo/ui/*": ["../../packages/ui/*"] 11 | } 12 | }, 13 | "include": ["**/*.ts", "**/*.tsx", "**/*.json", "./global.d.ts"], 14 | "exclude": ["**/dist/**/*", "**/node_modules/**/*"], 15 | "references": [{ "path": "../api" }, { "path": "../../packages/ui" }] 16 | } 17 | -------------------------------------------------------------------------------- /packages/typescript-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/typescript-config", 3 | "version": "0.0.0", 4 | "private": true, 5 | "exports": { 6 | "./base": "./base.jsonc", 7 | "./base.jsonc": "./base.jsonc", 8 | "./cloudflare": "./cloudflare.jsonc", 9 | "./cloudflare.jsonc": "./cloudflare.jsonc", 10 | "./node": "./node.jsonc", 11 | "./node.jsonc": "./node.jsonc", 12 | "./react": "./react.jsonc", 13 | "./react.jsonc": "./react.jsonc", 14 | "./package.json": "./package.json" 15 | }, 16 | "files": [ 17 | "*.jsonc", 18 | "package.json" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /infra/templates/backend-r2.example.hcl: -------------------------------------------------------------------------------- 1 | bucket = "tf-state" 2 | key = "prod/edge/terraform.tfstate" 3 | 4 | endpoints = { 5 | s3 = "https://.r2.cloudflarestorage.com" 6 | } 7 | 8 | # Do not hard-code secrets here. Set credentials via environment variables: 9 | # access_key = "..." → AWS_ACCESS_KEY_ID 10 | # secret_key = "..." → AWS_SECRET_ACCESS_KEY 11 | 12 | skip_credentials_validation = true 13 | skip_metadata_api_check = true 14 | skip_region_validation = true 15 | skip_requesting_account_id = true 16 | skip_s3_checksum = true 17 | region = "auto" 18 | -------------------------------------------------------------------------------- /infra/templates/env-roots/hybrid/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | # GCP Configuration 2 | gcp_project_id = "my-gcp-project" 3 | gcp_region = "us-central1" 4 | 5 | # Project Configuration 6 | project_slug = "myapp" 7 | environment = "prod" 8 | 9 | # API Configuration 10 | api_image = "gcr.io/my-gcp-project/api:latest" 11 | cloud_sql_tier = "db-f1-micro" 12 | 13 | # --- Edge routing (optional) --- 14 | # cloudflare_account_id = "your-account-id" 15 | # cloudflare_zone_id = "your-zone-id" 16 | # hostname = "api.example.com" 17 | # TF_VAR_cloudflare_api_token: set via environment variable in CI 18 | -------------------------------------------------------------------------------- /infra/envs/dev/edge/main.tf: -------------------------------------------------------------------------------- 1 | module "stack" { 2 | source = "../../../stacks/edge" 3 | 4 | cloudflare_account_id = var.cloudflare_account_id 5 | cloudflare_zone_id = var.cloudflare_zone_id 6 | hostname = var.hostname 7 | project_slug = var.project_slug 8 | environment = var.environment 9 | neon_database_url = var.neon_database_url 10 | } 11 | 12 | # Copy hyperdrive_id to wrangler.jsonc hyperdrive binding 13 | output "hyperdrive_id" { 14 | value = module.stack.hyperdrive_id 15 | } 16 | 17 | output "hyperdrive_name" { 18 | value = module.stack.hyperdrive_name 19 | } 20 | -------------------------------------------------------------------------------- /infra/envs/prod/edge/main.tf: -------------------------------------------------------------------------------- 1 | module "stack" { 2 | source = "../../../stacks/edge" 3 | 4 | cloudflare_account_id = var.cloudflare_account_id 5 | cloudflare_zone_id = var.cloudflare_zone_id 6 | hostname = var.hostname 7 | project_slug = var.project_slug 8 | environment = var.environment 9 | neon_database_url = var.neon_database_url 10 | } 11 | 12 | # Copy hyperdrive_id to wrangler.jsonc hyperdrive binding 13 | output "hyperdrive_id" { 14 | value = module.stack.hyperdrive_id 15 | } 16 | 17 | output "hyperdrive_name" { 18 | value = module.stack.hyperdrive_name 19 | } 20 | -------------------------------------------------------------------------------- /infra/modules/gcp/gcs/main.tf: -------------------------------------------------------------------------------- 1 | resource "google_storage_bucket" "bucket" { 2 | name = var.bucket_name 3 | location = var.location 4 | force_destroy = false 5 | 6 | uniform_bucket_level_access = true 7 | 8 | dynamic "cors" { 9 | for_each = length(var.cors_origins) > 0 ? [1] : [] 10 | content { 11 | origin = var.cors_origins 12 | method = ["GET", "PUT", "POST", "OPTIONS"] 13 | response_header = [ 14 | "Content-Type", 15 | "Access-Control-Allow-Origin", 16 | "x-goog-resumable" 17 | ] 18 | max_age_seconds = 3600 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /infra/envs/preview/edge/main.tf: -------------------------------------------------------------------------------- 1 | module "stack" { 2 | source = "../../../stacks/edge" 3 | 4 | cloudflare_account_id = var.cloudflare_account_id 5 | cloudflare_zone_id = var.cloudflare_zone_id 6 | hostname = var.hostname 7 | project_slug = var.project_slug 8 | environment = var.environment 9 | neon_database_url = var.neon_database_url 10 | } 11 | 12 | # Copy hyperdrive_id to wrangler.jsonc hyperdrive binding 13 | output "hyperdrive_id" { 14 | value = module.stack.hyperdrive_id 15 | } 16 | 17 | output "hyperdrive_name" { 18 | value = module.stack.hyperdrive_name 19 | } 20 | -------------------------------------------------------------------------------- /infra/envs/staging/edge/main.tf: -------------------------------------------------------------------------------- 1 | module "stack" { 2 | source = "../../../stacks/edge" 3 | 4 | cloudflare_account_id = var.cloudflare_account_id 5 | cloudflare_zone_id = var.cloudflare_zone_id 6 | hostname = var.hostname 7 | project_slug = var.project_slug 8 | environment = var.environment 9 | neon_database_url = var.neon_database_url 10 | } 11 | 12 | # Copy hyperdrive_id to wrangler.jsonc hyperdrive binding 13 | output "hyperdrive_id" { 14 | value = module.stack.hyperdrive_id 15 | } 16 | 17 | output "hyperdrive_name" { 18 | value = module.stack.hyperdrive_name 19 | } 20 | -------------------------------------------------------------------------------- /apps/app/public/site.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "React 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": "/?utm_source=homescreen", 22 | "display": "standalone", 23 | "background_color": "#fafafa", 24 | "theme_color": "#fafafa" 25 | } 26 | -------------------------------------------------------------------------------- /apps/web/public/site.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "React 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": "/?utm_source=homescreen", 22 | "display": "standalone", 23 | "background_color": "#fafafa", 24 | "theme_color": "#fafafa" 25 | } 26 | -------------------------------------------------------------------------------- /apps/app/global.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import "vite/client"; 3 | 4 | interface Window { 5 | dataLayer: unknown[]; 6 | } 7 | 8 | interface ImportMetaEnv { 9 | readonly VITE_APP_NAME: string; 10 | readonly VITE_APP_ORIGIN: string; 11 | readonly VITE_GOOGLE_CLOUD_PROJECT: string; 12 | readonly VITE_GA_MEASUREMENT_ID: string; 13 | } 14 | 15 | declare module "relay-runtime" { 16 | interface PayloadError { 17 | errors?: Record; 18 | } 19 | } 20 | 21 | declare module "*.css"; 22 | 23 | declare module "*.svg" { 24 | const content: React.FC>; 25 | export default content; 26 | } 27 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "compilerOptions": { 4 | "composite": true, 5 | "noEmit": true, 6 | "tsBuildInfoFile": "../../.cache/tsconfig/web.tsbuildinfo", 7 | "baseUrl": ".", 8 | "jsx": "react-jsx", 9 | "jsxImportSource": "react", 10 | "paths": { 11 | "@/*": ["./*"], 12 | "@repo/ui": ["../../packages/ui"], 13 | "@repo/ui/*": ["../../packages/ui/*"] 14 | }, 15 | "types": ["astro/client"] 16 | }, 17 | "include": ["**/*.ts", "**/*.tsx", "**/*.json", "**/*.astro"], 18 | "exclude": ["**/dist/**/*", "**/node_modules/**/*"], 19 | "references": [{ "path": "../api" }, { "path": "../../packages/ui" }] 20 | } 21 | -------------------------------------------------------------------------------- /infra/modules/gcp/cloud-sql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "connection_name" { 2 | description = "Cloud SQL connection name (project:region:instance)" 3 | value = google_sql_database_instance.instance.connection_name 4 | } 5 | 6 | output "connection_string" { 7 | description = "PostgreSQL connection string" 8 | value = "postgresql://${google_sql_user.user.name}:${random_password.password.result}@localhost/${var.database_name}?host=/cloudsql/${google_sql_database_instance.instance.connection_name}" 9 | sensitive = true 10 | } 11 | 12 | output "instance_ip" { 13 | description = "Private IP address of the instance" 14 | value = google_sql_database_instance.instance.private_ip_address 15 | } 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Automatically normalize line endings for all text-based files 2 | # https://git-scm.com/docs/gitattributes#_end_of_line_conversion 3 | 4 | * text=auto 5 | 6 | # For the following file types, normalize line endings to LF on 7 | # checkin and prevent conversion to CRLF when they are checked out 8 | # (this is required in order to prevent newline related issues like, 9 | # for example, after the build script is run) 10 | 11 | .* text eol=lf 12 | *.css text eol=lf 13 | *.html text eol=lf 14 | *.js text eol=lf 15 | *.json text eol=lf 16 | *.md text eol=lf 17 | *.sh text eol=lf 18 | *.ts text eol=lf 19 | *.txt text eol=lf 20 | *.xml text eol=lf 21 | 22 | /.yarn/** linguist-vendored 23 | -------------------------------------------------------------------------------- /apps/app/routes/__root.tsx: -------------------------------------------------------------------------------- 1 | import { AuthErrorBoundary } from "@/components/auth/auth-error-boundary"; 2 | import type { AuthClient } from "@/lib/auth"; 3 | import type { QueryClient } from "@tanstack/react-query"; 4 | import { createRootRouteWithContext, Outlet } from "@tanstack/react-router"; 5 | import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; 6 | 7 | export const Route = createRootRouteWithContext<{ 8 | auth: AuthClient; 9 | queryClient: QueryClient; 10 | }>()({ 11 | component: Root, 12 | }); 13 | 14 | export function Root() { 15 | return ( 16 | 17 | 18 | {import.meta.env.DEV && } 19 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /apps/web/.env.example: -------------------------------------------------------------------------------- 1 | # Astro Environment Variables for Marketing Site 2 | # https://docs.astro.build/en/guides/environment-variables/ 3 | # 4 | # These are specific to the marketing website (apps/web) 5 | # Shared secrets like DATABASE_URL should go in root .env 6 | 7 | # Public variables (exposed to client-side code) 8 | # Must be prefixed with PUBLIC_ for Astro 9 | PUBLIC_APP_NAME="Example" 10 | PUBLIC_APP_ORIGIN="https://example.com" 11 | PUBLIC_GA_MEASUREMENT_ID="" 12 | 13 | # Build-time variables (only available during build) 14 | # Since this is a static site, all env vars are build-time only 15 | NODE_ENV="production" 16 | 17 | # Note: Sensitive variables like API keys should be in root .env 18 | # and referenced during build if needed 19 | -------------------------------------------------------------------------------- /apps/email/utils/render.ts: -------------------------------------------------------------------------------- 1 | import { render } from "@react-email/render"; 2 | import type { ReactElement } from "react"; 3 | 4 | /** 5 | * Render a React email component to HTML string 6 | * @param component React email component 7 | * @returns HTML string 8 | */ 9 | export async function renderEmailToHtml( 10 | component: ReactElement, 11 | ): Promise { 12 | return await render(component, { pretty: true }); 13 | } 14 | 15 | /** 16 | * Render a React email component to plain text string 17 | * @param component React email component 18 | * @returns Plain text string 19 | */ 20 | export async function renderEmailToText( 21 | component: ReactElement, 22 | ): Promise { 23 | return await render(component, { plainText: true }); 24 | } 25 | -------------------------------------------------------------------------------- /apps/api/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Public API surface for the backend package. 3 | * 4 | * Re-exports the Hono app, tRPC router, and core utilities. 5 | */ 6 | 7 | // Core utilities and services 8 | export { getOpenAI } from "./lib/ai.js"; 9 | export { createAuth } from "./lib/auth.js"; 10 | export { createDb } from "./lib/db.js"; 11 | 12 | // Application and router exports 13 | export { default as app, appRouter } from "./lib/app.js"; 14 | 15 | // Type exports 16 | export type { AppRouter } from "./lib/app.js"; 17 | export type { AppContext } from "./lib/context.js"; 18 | // Re-export context type to fix TypeScript portability issues 19 | export type * from "./lib/context.js"; 20 | 21 | // Default export is the core app 22 | export { default } from "./lib/app.js"; 23 | -------------------------------------------------------------------------------- /packages/ui/components/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | const Textarea = React.forwardRef< 6 | HTMLTextAreaElement, 7 | React.ComponentProps<"textarea"> 8 | >(({ className, ...props }, ref) => { 9 | return ( 10 |