Hackorda Docs

Tech Stack

The truth lives in package.json and the config files at the repo root. This page is the human-readable map of it. Versions below are the semver ranges as declared (^x.y.z) — run npm ls <pkg> for the exact resolved version.

🧱 Layers at a glance

LayerWhat we useVersion
FrameworkNext.js — App Router, React Server Components, route handlers under app/api/**^15.5.6
UI runtimeReact + React DOM^19.2.3
StylingTailwindCSS v4 (stable) via @tailwindcss/postcss; @tailwindcss/typography; tailwind-merge, tailwindcss-animate, class-variance-authority, clsx^4.1.18
ComponentsRadix UI primitives + radix-ui + @base-ui-components/react (shadcn/ui pattern); cmdk command palette; @dnd-kit/* drag-and-dropradix-ui ^1.4.3
Iconslucide-react, @heroicons/react, @remixicon/react, react-iconslucide-react ^0.479.0
Toasts / theming / motionsonner, next-themes, motionsonner ^2.0.3
LanguageTypeScriptstrict: true, moduleResolution: bundler, @/* path alias → src/*^5.9.3
DataPostgreSQL (self-managed; Neon planned) via the pg driver + Drizzle ORM; migrations by drizzle-kit; drizzle-seed + @faker-js/faker for seedsdrizzle-orm ^0.45.1 / drizzle-kit ^0.31.9 / pg ^8.14.0
AuthClerk@clerk/nextjs (app), @clerk/testing (e2e); custom role-based access (student/admin)^6.36.6
AIAnthropic SDK — fire-and-forget issue-intake triage (src/lib/ai/)^0.92.0
Object storageDigitalOcean Spaces (S3-compatible) via @aws-sdk/client-s3 + @aws-sdk/s3-request-presigner^3.975.0
Webhookssvix — verifies Clerk webhook signatures (src/app/api/webhooks/route.ts)^1.64.1
ObservabilitySentry@sentry/nextjs (build-time wiring is opt-in via SENTRY_DSN); pino + pino-pretty structured logging (src/lib/logger.ts)@sentry/nextjs ^9.40.0 / pino ^9.6.0
i18nnext-intl[locale] segment + src/i18n/request.ts plugin^4.7.0
Server state / tablesTanStack react-query (+ react-query-devtools), react-table, react-virtualreact-query ^5.90.16
Charts / dates / parsingrecharts; date-fns + dayjs + react-day-picker; csv-parse, gray-matter, remark/remark-gfm/remark-html, nanoid/uuid, zodrecharts ^2.15.4 / zod ^4.2.1
TestsVitest (^4.1.7) for unit logic + Playwright (@playwright/test ^1.59.1) for e2e

⚠️ Tailwind v4 is stable, not alpha. Earlier docs flagged it as a bleeding-edge risk — that was true in early 2025 and is no longer the case. It is wired through @tailwindcss/postcss in production today.

🛠️ Scripts

All scripts run via npm run <name> (see package.json). TypeScript scripts execute through tsx.

Dev / build

ScriptCommandNotes
devnext dev --turbopackLocal dev server on Turbopack
buildnext buildProduction build
startnext startServe the production build

Database — generate / migrate / seed

ScriptCommandNotes
generatedrizzle-kit generateGenerate a migration from src/db/schema.ts
migratedrizzle-kit migrateApply pending migrations (also runs on container start)
seed:enumstsx src/db/seed-enums.tsEnum/lookup tables
seed:userstsx src/db/seed-users.tsUsers
seed:qtsx src/db/seed-questions.tsQuestions
seed:cmltsx src/db/seed-course-modules-lessons.tsCourses / modules / lessons
seed:events-teamstsx src/db/seed-events-teams.tsEvents + teams
seed:institutionstsx src/db/seed-institutions.tsInstitutions
seed:tc-bootstraptsx src/db/seed-test-cycles-bootstrap.tsTest-cycles bootstrap data
seed:tc-onboardingtsx src/db/seed-hackorda-qa-cycle.tsThe "Hackorda Onboarding — QA shake-down" cycle (needs an ADMIN user)
seed:api-keytsx src/db/seed-api-key.tsAPI key

Test

ScriptCommandNotes
testvitest runUnit tests (src/**/*.test.ts) — CI check job
test:watchvitestWatch mode
test:coveragevitest run --coveragev8 coverage over src/lib/**
test:e2eplaywright testBrowser e2e (tests/e2e/**); gated on E2E_ENABLED
test:e2e:uiplaywright test --uiPlaywright UI mode
test:e2e:debugplaywright test --debugSingle test, inspector attached
test:e2e:installplaywright install chromium webkitInstall browser engines

Audit / lint

ScriptCommandNotes
linteslint .Flat config (eslint.config.mjs), next/core-web-vitals + next/typescript
audittsx scripts/audit-frontend.tsHuman-readable file-size report
audit:checktsx scripts/audit-frontend.ts --checkCI mode — exits 1 on regressions
audit:write-baselinetsx scripts/audit-frontend.ts --write-baseline-jsonLock in reductions

🚀 Hosting

Not Vercel. The app is self-hosted on a DigitalOcean droplet running Docker. CI builds the image, pushes it to GHCR, then SSHes to the droplet to pull and recreate the container. Caddy terminates TLS and reverse-proxies to the app. PostgreSQL is self-managed on the same droplet (a move to managed Postgres / Neon is planned). The self-hosted GitHub Actions runner also lives on that box.

Migrations and seeds run automatically on container start (scripts/docker-entrypoint.sh), so a fresh deploy is "push to main and wait."

See Deployment for the pipeline and host-resource runbook, and Database for connection topology, SSL, backups, and the managed-Postgres cutover plan.

🧰 Tooling config

ConcernFileHighlights
Next.jsnext.config.tsnext-intl plugin, opt-in Sentry wrap, serverExternalPackages: ['pg', …], DO Spaces image hosts
TypeScripttsconfig.jsonstrict, moduleResolution: bundler, @/*src/*
ESLinteslint.config.mjsFlat config; ignores docs-site/
Drizzledrizzle.config.tspostgresql dialect, schema src/db/schema.ts, out src/db/migrations
Vitestvitest.config.tsNode env, src/**/*.test.ts, v8 coverage over src/lib/**
Playwrightplaywright.config.tsdesktop-chromium + mobile-chrome + mobile-safari; auto-starts npm run dev

✅ Code-quality gates

  • File-size auditscripts/audit-frontend.ts enforces per-layer line caps (routes 600, components 400, hooks 500, lib 600, db 600). A baseline (scripts/audit-frontend-baseline.json) grandfathers existing offenders; CI fails when a new file exceeds its cap or a baselined file grows. Run npm run audit before opening a PR.
  • Service-layer migration — routes are moving off direct @/db access to a 3-tier route → lib/<domain>/<action> → @/db layout. See Service layer for the canonical pattern.

🔄 Data flow

Client → TanStack Query hook → app/api route handler
       → lib/<domain> service (validation, transactions, biz logic)
       → Drizzle ORM → PostgreSQL
       → type-safe response back to the client

On this page