Skip to main content
View as Markdown

Validation & Schema Generation

Two functions cover the offline half of the API: check a config without starting anything, and emit the JSON Schema that describes every config.

validateConfig(config)

Decode an unknown value against AppSchema. It never throws — a bad config comes back as data, so you can report several problems at once instead of dying on the first.

import { validateConfig } from 'sovrium'

const result = validateConfig({ name: 'My App' })

if (result.valid) {
  console.log(result.config.name)
} else {
  result.errors.forEach((error) => console.error(error))
}

ValidateConfigResult

The result is a discriminated union, not a record with optional fields. Narrow on valid before reading anything else — TypeScript will not let you do otherwise.

When Shape
valid: true { valid: true, config: AppConfig }
valid: false { valid: false, errors: readonly string[] }

errors holds strings, one per line of the formatted decode tree — each naming the path that failed and why. On the success branch there is no errors property at all, and on the failure branch there is no config.

import { validateConfig } from 'sovrium'

const untrusted: unknown = JSON.parse(await Bun.file('app.json').text())
const result = validateConfig(untrusted)

if (!result.valid) {
  console.error(`Invalid config:\n${result.errors.join('\n')}`)
  process.exit(1)
}

// `result.config` is an AppConfig here, and only here.
console.log(`Validated ${result.config.name}`)

generateAppJsonSchema()

Produce the JSON Schema (Draft-07) for the app configuration as a plain object — the same document sovrium schema prints.

import { generateAppJsonSchema } from 'sovrium'

const schema = generateAppJsonSchema()

await Bun.write('app.schema.json', JSON.stringify(schema, null, 2))

It takes no arguments and reads nothing from the environment: the schema is derived from AppSchema itself, so the output depends only on the Sovrium version. That makes it safe to regenerate in CI and diff — a change in the file is a change in the schema, never in the machine that ran it.

A common use is pinning the schema alongside the config so editors validate against the exact version you deploy:

// scripts/sync-schema.ts — run after every Sovrium upgrade
import { generateAppJsonSchema } from 'sovrium'

await Bun.write('app.schema.json', JSON.stringify(generateAppJsonSchema(), null, 2))
# app.yaml
# yaml-language-server: $schema=./app.schema.json
name: my-app

Last updated July 27, 2026

This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta — contributions and corrections are welcome.

Built with Sovrium