Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude/commands/validate-auth-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ Check that the Drizzle schema in `db/schema/` still satisfies what Better Auth e
bun run db/scripts/generate-auth-schema.ts
```

The script builds a real auth instance from `apps/api/lib/auth.ts` with fixed placeholder credentials, every optional integration switched on, so the output is the full set of tables rather than the subset your environment happens to enable. Per field it carries the `type`, the `required` and `unique` flags, and the reference target — nothing else.
The script builds a real auth instance from `apps/api/lib/auth.ts` with fixed placeholder credentials, every optional integration switched on, so the output is the full set of tables rather than the subset your environment happens to enable. Per field it carries the `type`, the `required`, `unique` and `index` flags, any literal `defaultValue`, and the reference target with its `onDelete`. Per table it carries `indexes`: the composite indexes a plugin declares, which is how the account identity key `(issuer, accountId)` arrives.

Compare that output against `db/schema/` and report:

- Tables Better Auth expects that are missing, and tables no plugin in `auth.ts` needs any more.
- Field-level drift: missing or extra columns, mismatched types, and `required` or `unique` flags that disagree.
- Foreign keys pointing at the wrong table or column.
- Defaults, `onDelete`, and indexes are **not** in the generated output — they are this project's decisions. Check those against the conventions in `db/AGENTS.md` instead, and do not report them as Better Auth drift.
- A `true` `index` flag, a table-level entry in `indexes`, a literal `defaultValue`, or an `onDelete` in the output is Better Auth asking for it: report a schema that lacks one. Indexes, defaults and cascades the project adds beyond that list are its own decisions — check those against the conventions in `db/AGENTS.md` and do not report them as drift.
- Local naming that must stay mapped, not renamed away: Better Auth's `account` is our `identity` table.
- Project-specific columns, indexes, and constraints are expected and fine on their own. Flag any that constrain a write Better Auth performs: a `NOT NULL` column with no database default breaks every insert into that table, and an extra unique or check constraint can reject a row Better Auth considers valid. Fields that must participate in its models belong in `additionalFields` rather than added to the table behind its back.

Expand Down
2 changes: 1 addition & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Before starting work on a significant change, open an issue to discuss your prop

### Prerequisites

- [Bun](https://bun.sh) >= 1.3.0
- [Bun](https://bun.sh) >= 1.4.0
- [Node.js](https://nodejs.org) >= 20 (for some tooling)
- [Git](https://git-scm.com)

Expand Down
22 changes: 11 additions & 11 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ jobs:
# `apps/email/dist` is not deployed directly – the API worker imports
# `@repo/email`, whose exports point there, so `worker.ts` fails to bundle
# without it.
#
# `scripts/deploy.ts` checks the same three, and this stays anyway: that
# check runs after the migration, and a restore that silently dropped a
# `dist` should fail before the point of no return, not after it.
- name: Verify build artifacts
run: |
test -d apps/email/dist
Expand Down Expand Up @@ -168,14 +172,10 @@ jobs:
CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
DEPLOY_ENV: ${{ inputs.environment }}
run: |
# Recomputed rather than carried from the preflight: an empty `--env`
# selects production, so a value that went missing would deploy it.
if [[ "$DEPLOY_ENV" == "production" ]]; then
env_args=(--env "")
else
env_args=(--env "$DEPLOY_ENV")
fi
bun wrangler deploy --config apps/api/wrangler.jsonc "${env_args[@]}"
bun wrangler deploy --config apps/app/wrangler.jsonc "${env_args[@]}"
bun wrangler deploy --config apps/web/wrangler.jsonc "${env_args[@]}"
# `bun deploy:{staging,production}` runs the same script locally, so a
# release from a laptop and one from CI cannot drift in order or in
# environment selection. It maps production to Wrangler's empty `--env`
# itself and rejects any other environment name outright, so a value
# lost in transit fails the run rather than deploying production.
# `--skip-build` because the restored artifact is the verified build.
run: bun scripts/deploy.ts "$DEPLOY_ENV" --skip-build
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

## Tech Stack

- **Toolchain:** Bun >=1.3.0 (package manager, scripts, local API server), TypeScript 6.0, ESM (`"type": "module"`)
- **Toolchain:** Bun >=1.4.0 (package manager, scripts, local API server), TypeScript 6.0, ESM (`"type": "module"`)
- **Production runtime:** Cloudflare Workers (workerd) – not Bun. Code that ships must run on Workers APIs.
- **Frontend:** React 19, TanStack Router, TanStack Query, Jotai, shadcn/ui (new-york), Tailwind CSS v4
- **Backend:** Hono, tRPC 11, Better Auth (email OTP, passkey, Google OAuth, organizations)
Expand All @@ -31,11 +31,12 @@ bun lint # ESLint with cache
bun typecheck # tsc --build (builds apps/email for its types)
bun infra:check # Terraform fmt + validate, no credentials or state
bun ui:add <component> # Add shadcn/ui component to packages/ui
bun deploy:{staging,production} # Build and deploy api → app → web; no migrations

# Per-app: bun {web,app,api}:{dev,build,deploy}; test for app/api, check for web
# Database: bun db:{push,generate,migrate,studio,seed,export}
# :staging / :production on migrate, studio, export; seed stops at :staging;
# push and generate are local-only
# generate is local-only, and push refuses a non-local database
```

## Verification
Expand Down
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Each app deploys independently to Cloudflare Workers. The web worker routes `/ap

## Prerequisites

- [Bun](https://bun.sh/) v1.3+ (replaces Node.js and npm)
- [Bun](https://bun.sh/) v1.4+ (replaces Node.js and npm)
- [VS Code](https://code.visualstudio.com/) with our [recommended extensions](.vscode/extensions.json)
- [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en) browser extension (recommended)
- [Cloudflare account](https://dash.cloudflare.com/sign-up) for deployment
Expand Down Expand Up @@ -172,15 +172,13 @@ bun db:migrate:production # reads .env.production.local, and only that file
### 4. Build and Deploy

```bash
# Build every deployable workspace; Bun orders workspace dependencies
bun run build # Build all deployable workspaces

# Deploy service-binding targets before the web router
bun api:deploy --env=""
bun app:deploy --env=""
bun web:deploy --env=""
# Builds, then deploys api -> app -> web (service-binding targets first)
bun deploy:staging
bun deploy:production
```

CI runs the same script, so a release from your machine and one from GitHub Actions cannot drift.

## Backers

<a href="https://reactstarter.com/b/1"><img src="https://reactstarter.com/b/1.png" height="60" /></a>&nbsp;&nbsp;<a href="https://reactstarter.com/b/2"><img src="https://reactstarter.com/b/2.png" height="60" /></a>&nbsp;&nbsp;<a href="https://reactstarter.com/b/3"><img src="https://reactstarter.com/b/3.png" height="60" /></a>&nbsp;&nbsp;<a href="https://reactstarter.com/b/4"><img src="https://reactstarter.com/b/4.png" height="60" /></a>&nbsp;&nbsp;<a href="https://reactstarter.com/b/5"><img src="https://reactstarter.com/b/5.png" height="60" /></a>&nbsp;&nbsp;<a href="https://reactstarter.com/b/6"><img src="https://reactstarter.com/b/6.png" height="60" /></a>&nbsp;&nbsp;<a href="https://reactstarter.com/b/7"><img src="https://reactstarter.com/b/7.png" height="60" /></a>&nbsp;&nbsp;<a href="https://reactstarter.com/b/8"><img src="https://reactstarter.com/b/8.png" height="60" /></a>
Expand Down
22 changes: 11 additions & 11 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,36 @@
"predev": "bun --filter @repo/email build",
"dev": "bun run --watch --env-file ../../.env --env-file ../../.env.local ./dev.ts",
"build": "bun build index.ts --outdir dist --target bun",
"test": "vitest",
"test": "bun --bun vitest",
"typecheck": "tsc --noEmit",
"deploy": "wrangler deploy --env-file ../../.env --env-file ../../.env.local",
"logs": "wrangler tail --env-file ../../.env --env-file ../../.env.local"
},
"dependencies": {
"@better-auth/passkey": "^1.6.29",
"@better-auth/stripe": "^1.6.29",
"@better-auth/passkey": "^1.7.1",
"@better-auth/stripe": "^1.7.1",
"@repo/core": "workspace:*",
"@repo/db": "workspace:*",
"@repo/email": "workspace:*",
"@trpc/server": "^11.18.0",
"better-auth": "^1.6.29",
"better-auth": "^1.7.1",
"drizzle-orm": "^0.45.2",
"postgres": "^3.4.9",
"resend": "^6.20.0",
"resend": "^6.22.0",
"stripe": "^22.5.0"
},
"peerDependencies": {
"hono": "^4.13.2",
"hono": "^4.13.3",
"zod": "^4.4.3"
},
"devDependencies": {
"@cloudflare/workers-types": "^5.20260816.1",
"@cloudflare/workers-types": "^5.20260823.1",
"@repo/typescript-config": "workspace:*",
"@types/bun": "^1.3.14",
"hono": "^4.13.2",
"@types/bun": "^1.4.0",
"hono": "^4.13.3",
"typescript": "~6.0.3",
"vitest": "~4.1.10",
"wrangler": "^4.123.0",
"vitest": "~4.1.11",
"wrangler": "^4.125.0",
"zod": "^4.4.3"
}
}
35 changes: 17 additions & 18 deletions apps/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,21 @@
"dev": "vite serve",
"build": "vite build",
"preview": "vite preview",
"test": "vitest",
"coverage": "vitest --coverage",
"test": "bun --bun vitest",
"typecheck": "tsc --noEmit",
"deploy": "wrangler deploy --env-file ../../.env --env-file ../../.env.local",
"logs": "wrangler tail --env-file ../../.env --env-file ../../.env.local"
},
"dependencies": {
"@better-auth/passkey": "^1.6.29",
"@better-auth/stripe": "^1.6.29",
"@better-auth/passkey": "^1.7.1",
"@better-auth/stripe": "^1.7.1",
"@repo/ui": "workspace:*",
"@tanstack/react-query": "^5.101.4",
"@tanstack/react-router": "^1.170.29",
"@tanstack/react-query": "^5.102.2",
"@tanstack/react-router": "^1.170.32",
"@trpc/client": "^11.18.0",
"better-auth": "^1.6.29",
"better-auth": "^1.7.1",
"jotai": "^2.20.2",
"lucide-react": "^1.31.0",
"lucide-react": "^1.33.0",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-error-boundary": "^6.1.3",
Expand All @@ -32,24 +31,24 @@
"@repo/api": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@tailwindcss/vite": "^4.3.3",
"@tanstack/react-devtools": "^0.10.10",
"@tanstack/react-query-devtools": "^5.101.4",
"@tanstack/react-devtools": "^0.10.12",
"@tanstack/react-query-devtools": "^5.102.2",
"@tanstack/react-router-devtools": "^1.167.1",
"@tanstack/router-plugin": "^1.168.32",
"@tanstack/router-plugin": "^1.168.35",
"@testing-library/jest-dom": "^7.0.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.4",
"@types/bun": "^1.3.14",
"@testing-library/user-event": "^14.6.6",
"@types/bun": "^1.4.0",
"@types/node": "^26.2.0",
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.4",
"@vitejs/plugin-react": "^6.0.5",
"happy-dom": "^20.11.2",
"@vitejs/plugin-react": "^6.1.0",
"happy-dom": "^20.11.6",
"tailwindcss": "^4.3.3",
"tw-animate-css": "^1.4.0",
"typescript": "~6.0.3",
"vite": "~8.2.1",
"vitest": "~4.1.10",
"wrangler": "^4.123.0"
"vite": "~8.2.2",
"vitest": "~4.1.11",
"wrangler": "^4.125.0"
}
}
10 changes: 5 additions & 5 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@
"logs": "wrangler tail --env-file ../../.env --env-file ../../.env.local"
},
"dependencies": {
"@astrojs/react": "^6.0.2",
"@astrojs/react": "^6.0.4",
"@repo/ui": "workspace:*",
"astro": "^7.2.2",
"hono": "^4.13.2",
"astro": "^7.2.4",
"hono": "^4.13.3",
"react": "^19.2.8",
"react-dom": "^19.2.8"
},
"devDependencies": {
"@astrojs/check": "^0.9.10",
"@cloudflare/workers-types": "^5.20260816.1",
"@cloudflare/workers-types": "^5.20260823.1",
"@repo/typescript-config": "workspace:*",
"@tailwindcss/vite": "^4.3.3",
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.4",
"tailwindcss": "^4.3.3",
"typescript": "~6.0.3",
"wrangler": "^4.123.0"
"wrangler": "^4.125.0"
}
}
Loading