-
Notifications
You must be signed in to change notification settings - Fork 2
Developer Guide
Day-to-day patterns for working in the Valence codebase.
valence/
├── packages/
│ ├── core/ Telemetry engine, client router, server utilities (256 tests)
│ ├── db/ PostgreSQL pool, config, migrations, error mapping (38 tests)
│ ├── telemetry/ Summary queries, daily aggregation, fleet types (96 tests)
│ ├── ui/ 18 Web Components, OKLCH tokens, Tailwind preset, theme contract (368 tests)
│ └── cms/ Content management engine (270 tests)
└── docs/ Architecture, guides, troubleshooting
Tests use Vitest with happy-dom. No database or network required.
Web Component tests use beforeAll with dynamic import to ensure the component registers in the happy-dom environment:
let MyComponent: typeof import('../components/MyComponent.js').MyComponent
beforeAll(async () => {
const mod = await import('../components/MyComponent.js')
MyComponent = mod.MyComponent
})function createElement (): HTMLElement {
const el = document.createElement('my-component')
document.body.appendChild(el)
return el
}All code changes follow strict TDD with tagged micro-commits:
-
RED: Write a failing test. Run tests. Commit with
REDtag. -
GREEN: Write the minimum implementation. Run tests. Commit with
GREENtag. -
REFACTOR: Clean up while tests stay green. Commit with
REFACTORtag.
test(cms): add rate limiter tests -- RED
feat(cms): implement rate limiter -- GREEN
refactor(cms): extract shared test helpers -- REFACTORThese fail code review. The pre-commit hook enforces lint rules, manual review catches the rest.
| Pattern | Why | Alternative |
|---|---|---|
throw new Error |
No exceptions |
err() / errAsync() from neverthrow |
try { } |
No try/catch | Result monads |
switch ( |
Complexity | Static dictionary maps |
enum Foo |
Banned keyword | const Foo = [...] as const |
.parse( |
Zod footgun |
.safeParse() only |
export default |
Named exports only | export function/class/const |
Record<string, any> |
Loose typing | Explicit interfaces |
Record<string, unknown> |
Loose typing | Explicit interfaces or typed unions |
unknown as property type |
Loose typing | string | number | boolean | null |
localStorage / sessionStorage
|
Fragile state | Server-side state, cookies |
- Named exports only. No default exports.
-
Barrel exports. Every module directory has an
index.ts. -
File naming.
kebab-case.tsfor modules,PascalCase.tsfor Web Component classes. - Explicit return types on all exported/public functions.
- Comments explain WHY, not WHAT.
- Error types. Const union + interface pattern.
-
Commit format.
type(scope): description.
ui (standalone, zero deps)
core (neverthrow)
db (neverthrow, postgres, zod)
cms (core, db, ui, zod, neverthrow, argon2)
telemetry (db, ui, neverthrow, postgres)
Build order (topological): core, db, ui (parallel) > telemetry > cms
Files follow NNN-kebab-description.sql naming. The runner creates a _migrations tracking table. Write idempotent SQL (IF NOT EXISTS, IF EXISTS). Returns Result, not exceptions.
The CMS is the largest package (~270 tests, ~55 source files). See Packages: CMS for the API reference and CMS Guide for the walkthrough.
-
buildCms(config)– main entry point. ReturnsResult<CmsInstance, CmsError>. -
collection()/field.*– schema definition. -
createQueryBuilder(pool, registry)– chainable SQL queries. -
generateCreateTableSql(collection)– schema to DDL.
CMS tests use mock pools from __tests__/test-helpers.ts:
import { makeMockPool, makeErrorPool } from './test-helpers.js'
const pool = makeMockPool([{ id: '1', title: 'Hello' }])Get Started
Guides
- Adding Collections
- Working with the Admin
- Telemetry Guide
- Authentication & Access
- Media Uploads
- Deploying
Architecture
- Architecture
- Architecture: Router
- Architecture: Telemetry
- Architecture: Server
- Architecture: Design Tokens
- Architecture: 14kB Protocol
Reference
- Packages
- Packages: CMS
- Packages: Core
- Packages: DB
- Packages: Telemetry
- Packages: UI
- Field Types Reference
- CLI Reference
- Configuration Options
Contributing