0016: Repository layout and toolchain
The packages, apps and tests layout, the pnpm, Turborepo, tsdown, Oxlint and Vitest toolchain, and the TypeScript 5.9/6/7 support matrix.
Status
Accepted, September 2026.
Context
Several of the permix PRs that were merged or closed concerned tooling rather than API: a TS 5.9/6/7 matrix, tsdown, Oxlint/Oxfmt, per-entry size measurement, coverage thresholds, Release Please (#54, #58, #67–#69, #72). Those choices were validated on a real codebase and are carried forward. Numeric gzip budgets are set from a measured baseline after core ships, not guessed in advance (0020). Two platform facts constrain them further: TypeScript 7 ships as a native compiler with no stable programmatic API until 7.1, so codegen must not depend on the compiler API; and Node's type stripping plus Bun and Deno reward source that uses only erasable syntax.
The repository also hosts a docs app and one example app per adapter, which needs a layout that keeps publishable packages separate from apps and from cross-package test suites.
Decision
Layout
/packages:permdock(core plus one folder per subpath adapter,skills/shipped in the package),cli(@permdock/cli, ownscreatePermDockPlugin),testing(@permdock/testing), plus two private, never-published config packages that every workspace consumes withworkspace:*:typescript-config(@permdock/typescript-config, thebase,library,react-libraryandnexttsconfig presets) andox-config(@permdock/ox-config, the shared Oxlint and Oxfmt baselines). The roottsconfig.json,oxlint.config.tsandoxfmt.config.tsonly extend those and add repository-specific ignores./apps:docs(Fumadocs v16 on Next.js 16.3, dogfooding PermDock, withllms.txt,llms-full.txt,.mdper page and a docs MCP route) andexamples/<name>, one per adapter plusmonorepo./tests:e2e(Playwright across examples, including@next/playwrightinstant()),types(TS 5.9, 6 and 7 matrix),integration(Postgres via testcontainers for RLS parity and providers),bundle(per-entry gzip measurements; a regression baseline is recorded after core ships).
Toolchain
- pnpm workspaces with catalogs for shared dependency versions; Turborepo for task orchestration and caching.
- tsdown for builds, emitting
.d.tsfromisolatedDeclarationsso declaration emit does not depend on the TypeScript compiler API. - Oxlint and Oxfmt for linting and formatting, run once from the repository root over every workspace (they are root scripts, not Turborepo tasks); the shared configuration lives in
packages/ox-config, andoptions.typeAwareis set only in the root config. - Vitest for unit tests and type tests; 95% coverage threshold on
packages/permdock; Playwright for e2e; testcontainers for Postgres. publintandarethetypeswrongon every package before publish.- Changesets for changelogs and versioning, with Release Please as the accepted alternative if automated release PRs are preferred; one of the two is chosen when Phase 1 publishes.
TypeScript
- Supported: TypeScript 5.9, 6 and 7, tested in
tests/types. isolatedDeclarationsanderasableSyntaxOnlyare enabled in every package: exported functions have explicit return types; no enums, namespaces, parameter properties orimport x = require. Packages get both from@permdock/typescript-config/library.json; apps extendnext.json, which keepserasableSyntaxOnlybut notisolatedDeclarations.stricteverywhere;moduleResolutionsettings that TS 6 removed are never used.
Consequences
- Public function signatures are always explicit, which also makes generated docs and skills more precise.
- Codegen (
collect,openapi,rls) parses withoxc-parserand reads runtime definitions; it never loads the TypeScript compiler. - Every adapter has an example app, so e2e tests double as living documentation and skills can point at working code.
- The type matrix triples type-test time; Turborepo caching keeps it tolerable.
- Contributors need pnpm 12 and Node 24; the repo does not support npm or Yarn workflows.
Alternatives considered
- tsc for declaration emit. Rejected: slower, and TS 7.0 has no stable API for tooling to hook into.
- ESLint and Prettier. Rejected for speed and configuration surface; Oxlint/Oxfmt were already adopted in the permix work.
- Jest. Rejected: Vitest has first-class type tests and ESM.
- Examples inside
packages/*. Rejected: examples are apps with their own dependency trees and must never be published. - Single
tests/folder inside each package. Unit tests do live next to sources; the top-level/testsis for suites that span packages.
Related
0015: No runtime dependencies in core
Why permdock depends only on @standard-schema/spec, ships ESM-only, and keeps validators, serialisers and OpenTelemetry out of core.
0017: Docs first, in Fumadocs-ready MDX
Why research, design and decisions are written as MDX under apps/docs/content/docs before any code or docs app exists, and the conventions that make that work.