Skip to content

@btravstack/config


@btravstack/config ​

Classes ​

ConfigFieldInvalid ​

Defined in: packages/config/src/config.ts:194

Why one variable's value is not the value a field wanted — the message ConfigInvalid reports against it.

Extends ​

  • TaggedErrorInstance<"ConfigFieldInvalid", { reason: string; }>

Constructors ​

Constructor ​
ts
new ConfigFieldInvalid(args): ConfigFieldInvalid;

Defined in: node_modules/.pnpm/unthrown@5.12.1/node_modules/unthrown/dist/index.d.mts:2386

Parameters ​
ParameterType
argsobject & object
Returns ​

ConfigFieldInvalid

Inherited from ​

TaggedError("ConfigFieldInvalid")<{ readonly reason: string; }>.constructor

Properties ​

PropertyModifierTypeOverridesInherited fromDefined in
_tagreadonly"ConfigFieldInvalid"-TaggedError("ConfigFieldInvalid")._tagnode_modules/.pnpm/unthrown@5.12.1/node_modules/unthrown/dist/index.d.mts:2363
cause?publicunknown-TaggedError("ConfigFieldInvalid").causenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.error.d.ts:24
messagepublicstringTaggedError("ConfigFieldInvalid").message-packages/config/src/config.ts:197
namepublicstring-TaggedError("ConfigFieldInvalid").namenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1074
reasonreadonlystring-TaggedError("ConfigFieldInvalid").reasonpackages/config/src/config.ts:195
stack?publicstring-TaggedError("ConfigFieldInvalid").stacknode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1076

ConfigInvalid ​

Defined in: packages/config/src/config.ts:153

A configuration port could not be bound: the environment is wrong, not the code. runMain maps it to sysexits(3)'s EX_CONFIG (78) rather than the generic startup 1, and its message names every offending variable.

Extends ​

  • TaggedErrorInstance<"ConfigInvalid", { issues: readonly ConfigIssue[]; port: string; }>

Constructors ​

Constructor ​
ts
new ConfigInvalid(args): ConfigInvalid;

Defined in: node_modules/.pnpm/unthrown@5.12.1/node_modules/unthrown/dist/index.d.mts:2386

Parameters ​
ParameterType
argsobject & object
Returns ​

ConfigInvalid

Inherited from ​

TaggedError("ConfigInvalid")<{ readonly port: string; readonly issues: readonly ConfigIssue[]; }>.constructor

Properties ​

PropertyModifierTypeOverridesInherited fromDefined in
_tagreadonly"ConfigInvalid"-TaggedError("ConfigInvalid")._tagnode_modules/.pnpm/unthrown@5.12.1/node_modules/unthrown/dist/index.d.mts:2363
cause?publicunknown-TaggedError("ConfigInvalid").causenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.error.d.ts:24
issuesreadonlyreadonly ConfigIssue[]-TaggedError("ConfigInvalid").issuespackages/config/src/config.ts:155
messagepublicstringTaggedError("ConfigInvalid").message-packages/config/src/config.ts:157
namepublicstring-TaggedError("ConfigInvalid").namenode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1074
portreadonlystring-TaggedError("ConfigInvalid").portpackages/config/src/config.ts:154
stack?publicstring-TaggedError("ConfigInvalid").stacknode_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts:1076

Env ​

Defined in: packages/config/src/config.ts:29

The environment, as a service. The kernel provides this port to every graph it boots — process.env by default, StartOptions.env for a test — so nothing else in an application touches process.env.

Extends ​

  • PortInstance<"Env", Readonly<Record<string, string | undefined>>>

Constructors ​

Constructor ​
ts
new Env(): Env;

Defined in: packages/di/dist/index.d.mts:15

Returns ​

Env

Inherited from ​

Port("Env")<Environment>.constructor

Properties ​

PropertyModifierTypeInherited fromDefined in
[ID]readonly"Env"Port("Env").[ID]packages/di/dist/index.d.mts:11
[SERVICE]readonlyServicePort("Env").[SERVICE]packages/di/dist/index.d.mts:12
portIdstatic readonly"Env"Port("Env").portIdpackages/di/dist/index.d.mts:16

Type Aliases ​

ConfigField ​

ts
type ConfigField<T, V, Need> = object;

Defined in: packages/config/src/config.ts:210

One environment variable, read into a value: parse receives the raw string (or undefined when unset) and answers the value or the reason it is not one. Compose them with Config.object.

check is the same field's rule applied to a value that is ALREADY a T — a pin, or a default — so the two routes into a configuration cannot disagree about what is valid. It is optional: a hand-written field keeps compiling and simply accepts whatever it is handed.

Type Parameters ​

Type ParameterDefault type
T-
V extends stringstring
Need extends "required" | "optional""required" | "optional"

Properties ​

PropertyModifierTypeDescriptionDefined in
~need?readonlyNeedType-only: whether the variable must be set — "optional" for a field with a default or a pin. Never present at runtime.packages/config/src/config.ts:222
check?readonly(value) => Result<T, ConfigFieldInvalid>-packages/config/src/config.ts:217
parsereadonly(raw) => Result<T, ConfigFieldInvalid>-packages/config/src/config.ts:216
variablereadonlyV-packages/config/src/config.ts:215

ConfigIssue ​

ts
type ConfigIssue = object;

Defined in: packages/config/src/config.ts:143

The one issue shape a configuration schema reports: Standard Schema's Issue, restated structurally so this package depends on nothing.

Properties ​

PropertyModifierTypeDefined in
messagereadonlystringpackages/config/src/config.ts:144
path?readonlyReadonlyArray< | PropertyKey | { key: PropertyKey; }>packages/config/src/config.ts:145

ConfigSchema ​

ts
type ConfigSchema<Input, Output> = object;

Defined in: packages/config/src/config.ts:176

The slice of Standard Schema (v1) this package speaks — structurally, so a zod, valibot or arktype schema is accepted with no adapter, and so is anything Config.object builds.

Type Parameters ​

Type Parameter
Input
Output

Properties ​

PropertyModifierTypeDefined in
~standardreadonlyobjectpackages/config/src/config.ts:177
~standard.types?readonlyobjectpackages/config/src/config.ts:189
~standard.types.inputreadonlyInputpackages/config/src/config.ts:189
~standard.types.outputreadonlyOutputpackages/config/src/config.ts:189
~standard.validatereadonly(value) => | { issues?: undefined; value: Output; } | { issues: readonly ConfigIssue[]; } | Promise< | { issues?: undefined; value: Output; } | { issues: readonly ConfigIssue[]; }>packages/config/src/config.ts:180
~standard.vendorreadonlystringpackages/config/src/config.ts:179
~standard.versionreadonly1packages/config/src/config.ts:178

Environment ​

ts
type Environment = Readonly<Record<string, string | undefined>>;

Defined in: packages/config/src/config.ts:22

The process environment as it actually arrives: flat, and every value a string or absent.


EnvironmentFor ​

ts
type EnvironmentFor<N> = [Unnamed<N>] extends [never] ? { readonly [K in RequiredOf<N> & string]: string } & { readonly [K in Exclude<OptionalOf<N>, RequiredOf<N>> & string]?: string } : Environment;

Defined in: packages/config/src/config.ts:133

The environment a graph with needs N is configured from: every variable a reader requires, every one it can do without, and nothing else — or any record at all when one of its readers named nothing.

A variable one reader requires and another defaults is required.

Type Parameters ​

Type Parameter
N

EnvNeed ​

ts
type EnvNeed<I> = string extends keyof I ? Env : [I extends unknown ? keyof I : never] extends [keyof I] ? EnvReading<{ [K in keyof I]-?: Record<never, never> extends Pick<I, K> ? never : K }[keyof I] & string, { [K in keyof I]-?: Record<never, never> extends Pick<I, K> ? K : never }[keyof I] & string> : Env;

Defined in: packages/config/src/config.ts:50

What reading I costs a provider: the variables I names, or plain Env when I is an open record and nothing can be said about them.

The key computations are INLINE rather than named helpers: declaration emit prints a named alias unreduced, and a consumer exporting a module would then name an alias this package does not export.

Type Parameters ​

Type Parameter
I

EnvPortFor ​

ts
type EnvPortFor<I> = object & (abstract new () => EnvNeed<I>);

Defined in: packages/config/src/config.ts:118

The Env port class, as a need for EnvNeed<I> — what Config.env answers.

Type Parameters ​

Type Parameter
I

Type Declaration ​

NameTypeDefined in
portId"Env"packages/config/src/config.ts:118

EnvReading ​

ts
type EnvReading<Required, Optional> = Env & object;

Defined in: packages/config/src/config.ts:38

Env, as a need that names what its reader reads: the variables it requires and the ones it can do without. A module's needs union them, and EnvironmentFor turns that union into the env its boot accepts.

A string key rather than a symbol, so declaration emit can always name it.

Type Parameters ​

Type Parameter
Required extends string
Optional extends string

Type Declaration ​

NameTypeDefined in
~envobjectpackages/config/src/config.ts:39
~env.optionalOptionalpackages/config/src/config.ts:39
~env.requiredRequiredpackages/config/src/config.ts:39

MaybePinned ​

ts
type MaybePinned<O, K, V> = [PinState<O, K>] extends ["absent"] ? never : [PinState<O, K>] extends ["set"] ? never : V;

Defined in: packages/config/src/config.ts:96

V, when O may or may not set K: a variable that may be set.

Type Parameters ​

Type Parameter
O
K extends PropertyKey
V extends string

OptionsAs ​

ts
type OptionsAs<O, T> = { readonly [K in keyof O]: K extends keyof T ? T[K] : never };

Defined in: packages/config/src/config.ts:110

O, checked key by key against the options type T: a key T does not know is never, so a misspelt option is an error at its own key. A starter takes its options as O & OptionsAs<O, T> with O extends T — O naked, so it is inferred as written, a union or an annotated type included.

Type Parameters ​

Type Parameter
O
T

Unpinned ​

ts
type Unpinned<O, K, V> = [PinState<O, K>] extends ["absent"] ? V : never;

Defined in: packages/config/src/config.ts:89

V, when the options O certainly leave K — the option that could pin it — unset: a variable that must be set. For a starter spelling its needs from its options' type, which over generic options its schema cannot resolve.

Type Parameters ​

Type Parameter
O
K extends PropertyKey
V extends string

Variables ​

Config ​

ts
const Config: object;

Defined in: packages/config/src/config.ts:378

Configuration, the twelve-factor way: typed values bound from the environment, validated once as the graph is built, and injected like any other service.

Config.object({...}) describes a slice of the environment as a schema — any Standard Schema does as well — and Config.provider(Port)(schema) turns it into a provider that reads Env and answers ConfigInvalid.

Type Declaration ​

NameTypeDefault valueDescriptionDefined in
boolean()<V, O>(variable, options?) => ConfigField<boolean, V, NeedOf<O>>-A flag: true/false, 1/0, yes/no or on/off, case-insensitive. Anything else is an error rather than a falsy reading — a deployment that wrote HTTP_COMPRESSION=enabled meant to turn it on.packages/config/src/config.ts:423
env()<I>(_schema) => EnvPortFor<I>-Env, typed as reading exactly what schema reads — the same port at run time. Inject it where a provider parses the environment itself rather than through Config.provider, so its needs name the variables and a boot's env is typed by them.packages/config/src/config.ts:615
integer()<V, O>(variable, options?) => ConfigField<number, V, NeedOf<O>>-A whole number, optionally bounded (both bounds inclusive).packages/config/src/config.ts:400
list()<V, O>(variable, given?) => ConfigField<readonly string[], V, NeedOf<O>>-A comma-separated list, each entry trimmed and empty entries dropped, so "a, b," is ["a", "b"]. min is the floor a shorter list is named against — a variable that lists nothing is a deployment mistake, not an empty list. Remarks min defaults to 1, unless a default is given — then 0. A default is the composition root saying what it wants when nobody sets the variable, and { default: [] } is a legitimate thing to want; under the flat 1 it was always refused, with a message blaming a variable nobody had set. An explicit min still applies to the default too, so the "a bound the environment route would refuse is not one a default gets to smuggle past" rule survives wherever a caller states one. A pin is checked against min whatever it is, which is why Config.pinned([], Config.list("HTTP_SESSION_KEYS")) is still refused: that field has no default, so its floor is 1, and a session codec with no key to seal with is broken rather than empty.packages/config/src/config.ts:464
object()<F>(fields) => ConfigSchema<EnvironmentOf<F>, { readonly [K in string | number | symbol]: F[K] extends ConfigField<T, string, "required" | "optional"> ? T : never }>-A record of fields, as a Standard Schema over the environment. Every field is read, so one validation names every offending variable at once — an operator fixes the deployment in one round trip.packages/config/src/config.ts:534
parse()<Output>(port, schema) => (env) => AsyncResult<Output, ConfigInvalid>-Validates env against schema; the issues become one ConfigInvalid naming port. This is the step Config.provider performs, on its own — for a piece that is already its own provider and has no second port to hang a Config.provider on, such as a starter binding its options inside a make arm.packages/config/src/config.ts:588
pinned()<T, V, N, X>(value, field) => ConfigField<T, undefined extends X ? V : never, [X] extends [undefined] ? N : "optional">-field, unless value is given — then a field answering value and reading nothing. Explicit beats environment beats default, per field: http({ port: 0 }) still reads HOST. The pin is CHECKED against the field's own rule, so a value the deployment route would refuse is refused here too, with the same message. Without that, a pinned NaN body limit turned a trust boundary off in silence — size > NaN is false — and the composition root was the one input to the configuration system nothing validated.packages/config/src/config.ts:513
port()<V, O>(variable, options?) => ConfigField<number, V, NeedOf<O>>-A TCP port: a whole number the OS will accept, 0 (an ephemeral bind) included.packages/config/src/config.ts:490
provider(){ <P>(port): <I>(schema) => Provider<InstanceType<P>, ConfigInvalid, EnvNeed<I>> & object; <Name>(name): <I, Output>(schema) => Provider<PortInstance<Name, Output>, ConfigInvalid, EnvNeed<I>> & object; }configProviderA provider binding a port from the environment through schema. Reads Env, so the port is built with the rest of the graph and a bad environment is a modeled startup Err rather than a silently wrong value. Two forms of the first call. Config.provider(Port)(schema) binds a port you declared — for a port that is public API another package names. Config.provider("RelayConfig")(schema) mints the port and hands back the provider carrying it, for a slice that is one application's own.packages/config/src/config.ts:607
string()<V, O>(variable, options?) => ConfigField<string, V, NeedOf<O>>-A non-empty string — a rule about the RAW value, deliberately not a check: "set but empty" is a deployment mistake, where a pinned "" is a decision. http({ cors: false }) pins exactly that, and an off switch spelled as the empty string must not be refused as a blank variable. Remarks The bound value is trimmed. X=" abc " binds "abc", because every field parses the trimmed string — it is what makes a whitespace-only variable "set but empty" rather than a value, and what stops a stray space from turning a port into a parse error. A secret whose whitespace is significant is therefore the one value this field cannot carry: pin it through the composition root instead, where Config.pinned hands the value over untouched.packages/config/src/config.ts:394
url()<V, O>(variable, options?) => ConfigField<string, V, NeedOf<O>>-A URL, kept as the string it was written as. The value is what a consumer hands to new URL, and this is what stops that construction from throwing: a malformed one is a ConfigInvalid naming the variable, at graph build, rather than a Defect from wherever the URL is finally needed.packages/config/src/config.ts:438

Functions ​

cleartext() ​

ts
function cleartext(url, allowed): boolean | "refused";

Defined in: packages/config/src/cleartext.ts:29

Whether this URL may be talked to in cleartext, decided once at boot.

Config.url says a value PARSES, not that it is safe — measured, it accepts javascript:, mailto:, file: and a bare host:port, and it is named for a property it does not check. So the scheme question is asked here, by whoever knows what the URL is for.

https: is always fine, http: on a loopback host is the dev loop, and any other http: sends whatever the URL carries across the wire in the open. That case is "refused" — a ConfigInvalid at boot unless the caller pinned the opt-in — and so is every other scheme, opt-in or not: a file: issuer is a deployment mistake to report at boot, not on the first call, which is where a security posture belongs and is why it is an option rather than a variable: rule 6's own test, that a value whose silent change is a security regression stays in the composition root.

One rule for every starter that is handed a URL carrying a credential — an issuer a client secret is sent to, a JWKS whose keys decide which tokens are accepted. A public key set fetched in cleartext lets anything on the path substitute its own key and mint tokens the process then accepts (RFC 8725 §3), so even a URL that carries no secret is worth attacking.

Parameters ​

ParameterType
urlstring
allowedboolean

Returns ​

boolean | "refused"


cleartextRefused() ​

ts
function cleartextRefused(args): ConfigInvalid;

Defined in: packages/config/src/cleartext.ts:49

The refusal, as the ConfigInvalid a boot reports — one message, so the callers cannot drift into explaining the same rule differently.

port is the configuration port's name and variable the one an operator sets; option is the option they would pin to say they meant it and on the call it is pinned at. The two are separate fields because this builder puts the backticks in — folding the call into option produced `allowInsecureIssuer` on `oidc(): true`, with the : true attached to the wrong half and the backticks unbalanced.

Parameters ​

ParameterType
args{ on: string; option: string; port: string; variable: string; what: string; }
args.onstring
args.optionstring
args.portstring
args.variablestring
args.whatstring

Returns ​

ConfigInvalid

Released under the MIT License.