Skip to main content

Key Features

Reasonable Defaults, "Batteries Included", etc.
The default Configurator setup is a great starting point for quickly providing configuration hooks for typical applications. The schema API is superficially similar to that of most command-line focused libraries, so adoption is easy. Out of the box, you get a command line parser with help text generation, environment variable parsing, configuration file loading, and a library of field validators that cover many common needs.
Extensible Schema System
The Configurator is pre-loaded with basic primitive types and common extensions (string, number, boolean, array, object, date, buffer), as well as many useful field validators (e.g. $positive, $alphanum, $directory, etc.) but developers can also define their own implementations. Custom types and validators can provide arbitrary (potentially asynchronous!) transformations from configuration inputs to validated outputs.
Sequenced Configuration Sources
Central to the Configurator is the concept of a ConfigurationSource. Each source encapsulates the functionality of discovering configuration field assignments from a single origin: ObjectSource understands simple objects, EnvironmentSource reads environment variables, CommandLineSource parses command line arguments, and so forth.
Each ConfigurationSource is loaded in sequence. Field assignments discovered later in the sequence override assignments from earlier in the sequence.
The Configurator provides a default sequence of predefined sources, but this sequence can be changed, and the sources individually provide options to tune their behavior. The sequence can also be extended with additional sources, either fully custom, or provided by optional packages.
Single Source of Truth: Configuration as a Data Model
The structure of the validated output configuration object is intended to mirror an "idealized" config file format for your application. If a schema hierarchy is created to align with the structure of the application and its subsystems, then each subsystem's configured properties will be nested inside a child object. This child object can then be used in isolation to safely initialize that subsystem, without extraneous data leaking in. This reduces the "dig through a random bag of whatever" output generated by many other configuration libraries.
Built to be Embedded
The observant reader may have noticed that the combination of features described above suggests that if subsystems were each built as self-contained modules, each module could internally define its own configuration schema. One could then build a system on top of Configurator that introspects these modules, discovers their internal schemas, and initializes each modular subsystem with a validated object containing only their requested configurables. Furthermore, those modules could be themselves be registered as new schema types, with a type resolver that returns singleton instances of those subsystems, allowing them to be assigned to matching configuration fields in other modules... 🤔
Good news, this exists!
ModuleManager is a separately installed library @versionzero/module-manager, with its own section in the documentation. It still provides all the Configurator capabilities described in this documentation, but with a bias towards serving applications structured as a dependency graph of modular subsystems. As a quick pitch, ModuleManager provides embedded declarative schemas, strongly typed references, dependency injection, and lifecycle management. It enables you to keep your configurable field definitions fully colocated with the modules that consume them, and provides a structured approach to wiring everything together. While the Configurator is a fine library for direct use on its own, it really shines for larger applications when embedded in an active "smart" layer (such as ModuleManager) that understands how your functionality is partitioned.