- packages/config: add src/startup.ts exposing assertValidConfig (builds on the T01 TypeBox/Ajv schema) and the field-specific startup errors (MissingRequiredSettingError names the missing field; ConfigStartupError names each violating field); re-export from the package boundary - apps/server: validate the startup configuration (including the required EPPP_SESSION_SECRET) before the server binds, so a missing required setting crashes the process at startup naming the field; depends on @personal-blog/config - compose.yaml: provide EPPP_SESSION_SECRET for the app service (dev-only >= 32 char default; override via .env / shell) - Dockerfile: ship the compiled packages/config in the image (build source + runtime dist), matching the server's new workspace dependency - pnpm-lock.yaml: apps/server importer gains @personal-blog/config
50 lines
2.0 KiB
TypeScript
50 lines
2.0 KiB
TypeScript
/**
|
|
* EPPP configuration validation — [E00-S04-T01] TypeBox/Ajv schema.
|
|
*
|
|
* Compiles the TypeBox `configSchema` with Ajv (the golden-tuple validator,
|
|
* Technology-Stack §5.2/§7) and exposes `validateConfig`, the generic
|
|
* schema-validation entry point: given an unknown value it reports whether
|
|
* the value is a valid configuration and the Ajv error messages otherwise.
|
|
*
|
|
* This is deliberately NOT the E00-S04-T02 field-specific startup error:
|
|
* `validateConfig` returns the raw schema-validation outcome (valid or not,
|
|
* with the Ajv messages) and performs no startup wiring — the startup error
|
|
* formatting (E00-S04-T02, `startup.ts`) and the environment adapter
|
|
* (E00-S04-T04) build on this raw outcome in their own modules.
|
|
*/
|
|
|
|
import { Ajv } from 'ajv';
|
|
|
|
import { configSchema } from './schema.js';
|
|
|
|
/** Ajv instance for the config schema — `allErrors` reports every violation. */
|
|
const ajv = new Ajv({ allErrors: true });
|
|
|
|
/** The compiled validator — TypeBox schemas are JSON Schema, so Ajv compiles them directly. */
|
|
const validateConfigValue = ajv.compile(configSchema);
|
|
|
|
/** The outcome of validating a value against the config schema. */
|
|
export interface ConfigValidationResult {
|
|
/** True when the value is a valid configuration. */
|
|
valid: boolean;
|
|
/** Ajv error messages, empty when `valid` is true. */
|
|
errors: string[];
|
|
}
|
|
|
|
/**
|
|
* Validates an unknown value against the config schema.
|
|
*
|
|
* @param value - the value to validate (typically the parsed config object)
|
|
* @returns `{ valid: true, errors: [] }` for a valid configuration, or
|
|
* `{ valid: false, errors }` with the Ajv messages naming each violation
|
|
* (e.g. `"must have required property 'sessionSecret'"`).
|
|
*/
|
|
export function validateConfig(value: unknown): ConfigValidationResult {
|
|
const valid = validateConfigValue(value);
|
|
if (valid) {
|
|
return { valid: true, errors: [] };
|
|
}
|
|
const errors = (validateConfigValue.errors ?? []).map((error) => error.message ?? 'invalid');
|
|
return { valid: false, errors };
|
|
}
|