Skip to content

Developer Guide

Forrest Blade edited this page Mar 19, 2026 · 1 revision

Developer Guide

Day-to-day patterns for working in the Valence codebase.

Project Structure

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

Testing Patterns

Tests use Vitest with happy-dom. No database or network required.

Dynamic Import Pattern

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
})

createElement / attach Helpers

function createElement (): HTMLElement {
  const el = document.createElement('my-component')
  document.body.appendChild(el)
  return el
}

TDD Commit Convention

All code changes follow strict TDD with tagged micro-commits:

  1. RED: Write a failing test. Run tests. Commit with RED tag.
  2. GREEN: Write the minimum implementation. Run tests. Commit with GREEN tag.
  3. REFACTOR: Clean up while tests stay green. Commit with REFACTOR tag.
test(cms): add rate limiter tests -- RED
feat(cms): implement rate limiter -- GREEN
refactor(cms): extract shared test helpers -- REFACTOR

Banned Patterns

These 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

Code Rules

  • Named exports only. No default exports.
  • Barrel exports. Every module directory has an index.ts.
  • File naming. kebab-case.ts for modules, PascalCase.ts for 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.

Dependency Graph

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

Migration Runner

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.

Working with @valencets/cms

The CMS is the largest package (~270 tests, ~55 source files). See Packages: CMS for the API reference and CMS Guide for the walkthrough.

Key Entry Points

  • buildCms(config) – main entry point. Returns Result<CmsInstance, CmsError>.
  • collection() / field.* – schema definition.
  • createQueryBuilder(pool, registry) – chainable SQL queries.
  • generateCreateTableSql(collection) – schema to DDL.

Testing CMS Code

CMS tests use mock pools from __tests__/test-helpers.ts:

import { makeMockPool, makeErrorPool } from './test-helpers.js'
const pool = makeMockPool([{ id: '1', title: 'Hello' }])

Clone this wiki locally