Workspace

  • Package manager: pnpm 10 (packageManager: pnpm@10.28.2). Node 20 or newer. CI uses Node 24.
  • Workspaces in pnpm-workspace.yaml: apps/*, packages/*, tooling/*.
  • Dependency versions are pinned once in the catalog: block of pnpm-workspace.yaml and referenced as "catalog:" in each package.json. Change a version there, not per package.
  • Add dependencies with pnpm add, for example pnpm --filter saas add some-package. Do not edit package.json by hand.
  • Turborepo (turbo.json) runs tasks. globalEnv lists the variables that affect the build cache: NODE_ENV, NEXT_PUBLIC_SITE_URL, NEXT_PUBLIC_SAAS_URL, AUTH_TRUSTED_ORIGINS, CORE_API_URL, CORE_API_SERVICE_ID, SERVICE_AUTH_SECRET and two Next flags.

Root scripts

Run from frontend/. dotenv -c loads .env and its cascade (.env.local and so on) from the repo root before running Turborepo. There are no db:* scripts. The Prisma package is gone from this repo.

App scripts

Production builds use webpack (--webpack). next.config.ts also sets a turbopack.root, which applies to next dev. Each package (ui, auth, i18n, payments, logs, notifications, utils) has its own type-check script (tsc --noEmit), but the root pnpm type-check filters to saas. Package code is still checked there, because saas imports it as source.

Type checking

next typegen generates the route types first, so typed params and links are current before tsc runs. Run pnpm type-check before every push. It is the slowest check, so run it once, not in parallel with other heavy jobs. tooling/typescript (@repo/tsconfig) holds three presets: noUnusedLocals and noUnusedParameters are off. oxlint reports unused code instead.

Linting

oxlint, configured in .oxlintrc.json:
Type aware rules run through oxlint-tsgolint, a root dependency. There is no ESLint in this repo. Lint one file while working: pnpm exec oxlint path/to/file.tsx. Disable comments use the oxlint form, for example // oxlint-disable-line eslint-plugin-react-hooks/exhaustive-deps.

Formatting

oxfmt, configured in .oxfmtrc.json:
  • 100 columns, double quotes, semicolons, parentheses around arrow parameters.
  • Imports are sorted into groups: builtin, external, internal and subpath, then parent, sibling and index, then styles.
  • Tailwind classes are sorted in className attributes and inside cn(...), using tooling/tailwind/theme.css as the stylesheet.
  • package.json keys are sorted.
  • JSON files get no trailing commas.
Source files use tabs.

Git hooks

Husky, installed by the prepare script.

lint-staged

.lintstagedrc.json:

pre-push

The script collects the files that differ between the upstream and HEAD (falling back to the last commit), keeps script files, and skips any file that also has uncommitted changes. Then:
  1. node tools/check-theme-tokens.mjs on everything. A failure stops the push.
  2. pnpm exec oxlint on the collected files, if any.
frontend/docs/CI_AND_HOOKS.md says pre-push runs format:check, lint, type-check and check:core-api. The hook in the repo does not. Type-check and the core API boundary check run only in CI.

Commit messages

commitlint.config.cjs extends the conventional config:
  • Type is one of feat, fix, docs, refactor, perf, test, build, ci, chore, revert.
  • Subject is required, in lower case or sentence case.
  • Header at most 100 characters. Body lines over 120 characters produce a warning.

CI

.github/workflows/ci.yml runs on pull requests and on pushes to any branch except dev and main. The job sets placeholder values for DATABASE_URL and BETTER_AUTH_SECRET. No database is reached. format:check, check:theme and Playwright are not CI steps.
The first step looks certain to fail on the current tree. The boundary script allows CORE_API_URL only in modules/shared/lib/core-api.ts, and nine other files read it today. See Talking to the core API. This was read from the script and the code, not run.
Deploy workflows are described in Build and deploy.

Guard scripts in tools/

The workspace root (one level above frontend) has tools/ensure-core-api.sh, used by pnpm dev. With --start it launches the backend in the background, writes its log to a temp file and waits up to 60 seconds for the health endpoint. Without the flag it prints how to start the API and exits non zero.

Shared Tailwind config

tooling/tailwind is the package @repo/tailwind-config. It ships two CSS files and no JavaScript config. Tailwind 4 is configured in CSS. apps/saas/postcss.config.cjs wires @tailwindcss/postcss.

Other configs

Before you push

Warnings are acceptable. Errors are not.