config API
Everything the package exports, by the path you import it from. Open an export for its signature and options.
@evolve-framework/config
BaseConfig, the envfield field types, the sources (envSource, fileSource, documentSource), createIndexedSource for writing a source of your own, and the key transformers that match a store's key spelling to a property name
Classes
BaseConfigA service's configuration: extend it, declare each setting as a property whose initializer is its default, and describe how to read each one in config().
import { BaseConfig } from "@evolve-framework/config";new BaseConfig()| Method | Description |
|---|---|
load(options?: LoadOptions | undefined): Promise<void> | Fills every field declared in config() from the sources, then coerces and validates it. |
Functions
anyKeysReads any conventional spelling, including redisUrl and RedisUrl.
import { anyKeys } from "@evolve-framework/config";Deliberately one-directional. Camel and Pascal do not round-trip: MyAPIKey
folds to MY_API_KEY, which spells back as MyApiKey. A reverse mapping that
is wrong on exactly the keys containing an acronym would pass every test and
then miss one key in production, so those spellings are read and never
written.
function anyKeys(): KeyTransformerReturns KeyTransformer
createIndexedSourceBuilds a source from a way to list a namespace and a way to read one key.
import { createIndexedSource } from "@evolve-framework/config";Every source shares the same prefix fallback, the same key folding and the same collision reporting, so a cloud adapter only has to say how to talk to its store.
function createIndexedSource(options: IndexedSourceOptions): ConfigSource| Option | Type | Description |
|---|---|---|
name | string | |
namespace? | string | Describes the namespace when it turns out to hold nothing. |
keys? | KeyTransformer | |
prefix? | string | |
prefixFallback? | boolean | |
lookup? | "index" | "direct" |
|
list? | (() => Promise<NativeEntry[]>) | Lists the namespace. Required for |
read? | ((native: string) => Promise<string | undefined>) | Reads one native key. Required for |
remove? | ((native: string) => void) | Drops a native key, backing |
Returns ConfigSource
documentSourceReads one file holding the entire configuration.
import { documentSource } from "@evolve-framework/config";The cloud-agnostic option, and the reason it earns a place beside the others:
mount a config.json into a container and every key is present, with no SDK,
no credentials, no network call and no cloud to be on. The same file works on
Docker, Kubernetes, ECS and a plain virtual machine. It also covers a
ConfigMap mounted as a single config.yaml, which is at least as common as
the key-per-file form.
function documentSource(options: DocumentSourceOptions): ConfigSource| Option | Type | Description |
|---|---|---|
path | string | The file holding the whole configuration. |
parse? | ((raw: string) => unknown) | Parses the file. JSON by default; pass |
prefix? | string | |
prefixFallback? | boolean | |
keys? | KeyTransformer |
Returns ConfigSource
dottedKeysKeys are lowercase dotted: redis.url.
import { dottedKeys } from "@evolve-framework/config";function dottedKeys(): KeyTransformerReturns KeyTransformer
envSourceReads process.env.
import { envSource } from "@evolve-framework/config";Exact matching rather than folding: environment variables are uppercase by
convention, so there is nothing to reconcile, and PATH against Path on
Windows is a trap. Reading one variable is free, so there is nothing to index.
function envSource(options?: EnvSourceOptions | undefined): ConfigSource| Option | Type | Description |
|---|---|---|
prefix? | string | Tried before the bare name, so |
prefixFallback? | boolean | When false, only the prefixed name is tried. Sealing a service into its own namespace stops a generic name in a shared environment from leaking into a service that meant to read its own. |
keys? | KeyTransformer |
Returns ConfigSource
exactKeysKeys are spelled exactly as the field declares them: REDIS_URL.
import { exactKeys } from "@evolve-framework/config";function exactKeys(): KeyTransformerReturns KeyTransformer
fileSourceReads a directory holding one file per key.
import { fileSource } from "@evolve-framework/config";This is the shape of Docker and Compose secrets under /run/secrets, of a
Kubernetes Secret or ConfigMap mounted as a volume, and of a projected volume.
function fileSource(options: FileSourceOptions): ConfigSource| Option | Type | Description |
|---|---|---|
path | string | The directory holding one file per key. |
prefix? | string | |
prefixFallback? | boolean | |
keys? | KeyTransformer | |
lookup? | "index" | "direct" | |
trim? | boolean | Strips one trailing newline, which is what |
Returns ConfigSource
foldKeyThe permissive fold: separators become underscores and camel humps become boundaries, so every conventional spelling of one key lands on one name.
import { foldKey } from "@evolve-framework/config";function foldKey(key: string): stringReturns string
indexDocumentTurns a parsed document into entries, one per key at every depth.
import { indexDocument } from "@evolve-framework/config";Every level is indexed rather than only the leaves or only the top, because
both readings are legitimate: a field may want the whole subtree as an
envfield.object(), or one value inside it. The document is already in
memory, so offering both costs nothing.
{ "store-config": { "evolve": { "domain": "https://example.test" } } }
store-config -> {"evolve":{"domain":"https://example.test"}} store-config.evolve -> {"domain":"https://example.test"} store-config.evolve.domain -> https://example.test
Segments are joined with a dot so that a folding transformer reduces the path and the segments together: the last of those resolves the field STORE_CONFIG_EVOLVE_DOMAIN.
This only ever runs in this direction. Nothing rebuilds a document from an index, because configuration here is read-only.
function indexDocument(document: unknown): NativeEntry[]Returns NativeEntry[]
kebabKeysKeys are lowercase kebab-case: redis-url.
import { kebabKeys } from "@evolve-framework/config";function kebabKeys(): KeyTransformerReturns KeyTransformer
prefixedKeyNormalizes a lookup prefix to exactly one trailing underscore, so that NL, NL_, nl/ and nl: all describe the same lookup.
import { prefixedKey } from "@evolve-framework/config";function prefixedKey(prefix: string, key: string): stringReturns string
snakeKeysKeys are lowercase snake_case: redis_url.
import { snakeKeys } from "@evolve-framework/config";function snakeKeys(): KeyTransformerReturns KeyTransformer
Constants
envfield
import { envfield } from "@evolve-framework/config";const envfield: { number: (options?: FieldOptions | undefined) => PropertyOptions; string: (options?: FieldOptions | undefined) => PropertyOptions; object: (options?: FieldOpti…silentLoggerThe default: a library nobody asked to be loud stays silent.
import { silentLogger } from "@evolve-framework/config";const silentLogger: ConfigLoggerTypes
ConfigLoggerThe logging surface config needs, kept structural so that a pino logger satisfies it without this package depending on pino — or on core, which is loaded after config and therefore cannot be loaded before it.
import { type ConfigLogger } from "@evolve-framework/config";trace is optional so that a bare console also satisfies the type.
export type ConfigLogger = { debug: LogFn; warn: LogFn; trace?: LogFn;};ConfigSource
import { type ConfigSource } from "@evolve-framework/config";export type ConfigSource = { readonly name: string; get(key: string): Promise<string | undefined>; prefetch?(): Promise<void>; unset?(key: string): void; describe?(key: string): string; applyLoadOptions?(options: SourceLoadOptions): void;};EnviConfig
import { type EnviConfig } from "@evolve-framework/config";export type EnviConfig = EnvVariableDict;EnvVariableDict
import { type EnvVariableDict } from "@evolve-framework/config";export type EnvVariableDict = Record<string, PropertyOptions>;FieldOptions
import { type FieldOptions } from "@evolve-framework/config";export type FieldOptions = { optional?: boolean; validator?: Validator; unset?: boolean; envName?: string; envSeparator?: string;};KeyTransformerReconciles the name a config class declares with the name one store uses.
import { type KeyTransformer } from "@evolve-framework/config";The two directions are not equally knowable, which is why only one of them is
required. Folding redis-url onto REDIS_URL is deterministic. Going back is
a guess unless the store's convention has been declared: it could as easily be
redis_url, RedisUrl, or REDIS_URL unchanged.
export type KeyTransformer = { readonly name: string; toCanonical(native: string): string | undefined; toNative?(key: string): string;};KnownTypes
import { type KnownTypes } from "@evolve-framework/config";export type KnownTypes = "string" | "number" | "boolean" | "object";LoadOptions
import { type LoadOptions } from "@evolve-framework/config";export type LoadOptions = { prefix?: string; prefixFallback?: boolean; disableUnset?: boolean; sources?: ConfigSource[]; logger?: ConfigLogger;};NativeEntryOne entry as the store spells it, with a way to read its value.
import { type NativeEntry } from "@evolve-framework/config";export type NativeEntry = { native: string; read(): Promise<string | undefined>;};PropertyOptions
import { type PropertyOptions } from "@evolve-framework/config";export type PropertyOptions = { type: KnownTypes;} & FieldOptions;SourceLoadOptionsDefaults the load call hands to every source it was given.
import { type SourceLoadOptions } from "@evolve-framework/config";export type SourceLoadOptions = { prefix?: string; prefixFallback?: boolean; logger: ConfigLogger;};@evolve-framework/config/testing
memorySource, a plain record as a source, for testing a config class without the environment or a cloud
Functions
memorySourceA plain record as a source, so a config class can be exercised across the overlay without a cloud, a filesystem or the process environment.
import { memorySource } from "@evolve-framework/config/testing";Keys are written the way the store being stood in for would write them, so
{ "redis-url": "..." } exercises the same folding a real store would.
function memorySource( values: Record<string, string>, options?: MemorySourceOptions,): ConfigSource| Option | Type | Description |
|---|---|---|
name? | string | Names the source in errors and log lines. |
namespace? | string | Stands in for a real store's namespace in errors and log lines. |
prefix? | string | |
prefixFallback? | boolean | |
keys? | KeyTransformer |
Returns ConfigSource
Not listed: 5 options types. They appear as the tables under the constructors and functions they type.

