Files
2026-05-27 23:29:53 +01:00

4.9 KiB

config

Category: universal
Path: universal/config/config
Archived from: openspec/archive/2026-04-24-enterprise-config/


Purpose

Layered configuration management for the Suaveplan monorepo. Five typed layers (defaults, file, object, env, CLI) are resolved in priority order, deep-merged, optionally validated via a caller-supplied Zod-compatible schema, and exposed through an immutable ConfigStore with hot-reload and subscription support.

Requirements

Constants

LAYER_PRIORITIES: { DEFAULTS: 0; FILE: 10; OBJECT: 20; ENV: 30; CLI: 40 }
MASK_STRING: "****"
TRUTHY_STRINGS: string[]
FALSY_STRINGS: string[]

Error Codes

Code Severity Retryable Trigger
CONFIG_MISSING error false Required config key not present
CONFIG_INVALID_TYPE error false Value has wrong type
CONFIG_PARSE_FAILED error false Layer resolution threw
CONFIG_FILE_NOT_FOUND error false File layer content unavailable
CONFIG_ENV_NOT_SET error false Required env var absent
CONFIG_RELOAD_FAILED error true Reload aborted or layer re-resolution threw
CONFIG_VALIDATION_FAILED fatal false Validator threw

Core API

class ConfigError extends SuaveplanError<ConfigPackageErrorCode>

function createConfig<T extends ConfigData>(options: CreateConfigOptions<T>): ConfigStore<T>

interface ConfigErrorDetail { path?: string; message: string }
interface MergeResult      { data: ConfigData; layers: ResolvedLayer[] }
interface ResolvedLayer    { name: string; priority: number; data: ConfigData }

Store

function createConfigStore<T extends ConfigData>(options: ConfigStoreOptions<T>): ConfigStore<T>

interface ConfigStoreOptions<T extends ConfigData = ConfigData> {
  data: T
  sensitive: string[]
}

interface ConfigStore<T extends ConfigData = ConfigData> {
  get<K extends string>(key: K): unknown
  has(key: string): boolean
  toJSON(): Readonly<T>
  toMasked(): Readonly<T>
  reload(signal?: AbortSignal): Promise<void>
  subscribe(listener: (next: Readonly<T>) => void): () => void
}

Layers

function defaultsLayer<T extends ConfigData>(defaults: T): ConfigLayer
function objectLayer(data: ConfigData, priority?: number): ConfigLayer
function envLayer(options: EnvLayerOptions): ConfigLayer
function fileLayer(options: FileLayerOptions): ConfigLayer
function cliLayer(options: CliLayerOptions): ConfigLayer
function parseArgs(args: string[], mapping: Record<string, string>): Record<string, string>

Types

type ConfigData = Record<string, unknown>

interface ConfigLayer {
  readonly name: string
  readonly priority: number
  resolve(): ConfigData
  teardown?(): void
}

type ConfigValidator<T extends ConfigData = ConfigData> = (data: ConfigData) => T

type EnvironmentOverrides<T extends ConfigData = ConfigData> = Record<string, Partial<T>>

interface CreateConfigOptions<T extends ConfigData = ConfigData> {
  layers: ConfigLayer[]
  validate?: ConfigValidator<T>
  sensitive?: string[]
  environments?: EnvironmentOverrides<T>
  environment?: string
  logger?: LoggerLike
}

interface EnvLayerOptions {
  prefix?: string
  mapping: Record<string, string>
  source?: Record<string, string | undefined>
}

interface CliLayerOptions {
  args: string[]
  mapping: Record<string, string>
}

interface FileLayerOptions {
  content: ConfigData
  watchBus?: EventBusLike
  watchEvent?: string
}

Utils

function deepMerge<T extends ConfigData>(base: T, override: Partial<T>): T
function mergeLayers(layers: Array<{ name: string; data: ConfigData }>): ConfigData
function getByPath(obj: ConfigData, path: string): unknown
function hasByPath(obj: ConfigData, path: string): boolean
function setByPath(obj: ConfigData, path: string, value: unknown): ConfigData
function maskSensitive(data: ConfigData, sensitive: string[], mask?: string): ConfigData
function coerceValue(raw: string): unknown

Zod Schemas

  • EnvLayerOptionsSchema
  • CliLayerOptionsSchema
  • FileLayerOptionsSchema
  • ConfigStoreOptionsSchema
  • CreateConfigOptionsSchema

OTel Instrumentation

Signal Name Attributes
Span config.create config.layer_count, config.environment
Counter config.get_total config.hit (bool)
Counter config.reload_total —
Histogram config.reload_duration_ms —
Histogram config.merge_duration_ms config.layer_count

Dependencies

Package Role
@suaveplan/error SuaveplanError base class, defineCodes
@suaveplan/types LoggerLike, EventBusLike
@suaveplan/events EventBus for file-layer watch mode
@suaveplan/env coerceValue boolean/numeric token reference
@opentelemetry/api OTel spans, counters, histograms
zod Zod schemas for all public option types
tslib TS runtime helpers