Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,180 @@
# 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
```ts
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
```ts
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
```ts
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
```ts
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
```ts
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
```ts
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 |