varcor
varcor
is a tool for streamlined management of config values, offering normalization and type enforcement.
Installation
To get started with varcor, install the package via npm:
npm install varcor
Result
The Result
type is used to return values or errors throught varcor.
type ResultSuccess<T> = { success: true; value: T; };
type ResultFailure<F> = { success: false; error: F; };
type Result<T, F> = ResultSuccess<T> | ResultFailure<F>;
const Result = Object.seal({
success: <T>(value: T): ResultSuccess<T> => ({ success: true, value }),
failure: <F>(error: F): ResultFailure<F> => ({ success: false, error })
} as const);
Variables
The Variable
class is used to create an instance of a Variable validator/transformer.
Common Functionalities
Parsing Values
The primary method of the class is parse
. This will take an optional string and return a Result of T
or a list of error strings.
parse(value?: string | undefined): Result<T, string[]>
Variables are Immutable - each of the following methods will create a new Variable:
Making Variables Optional
Marks a variable as optional. Type of variable becomes T | undefined
.optional(): Variable<T | undefined>
const optionalVar = v.string().optional();
console.log(optionalVar.isOptional);
Setting Default Values
Sets a default value for the variable if value is undefined.
.default(value: T | (() => T) ): Variable<T>
const defaultedVar = v.string().default('defaultValue');
console.log(defaultedVar.defaultTo);
Setting Variable Names
Sets the name of the variable
.from(name: string): Variable<T>
const namedVar = v.string().from('NAME');
console.log(namedVar.name)
Variable Unions with Else
Allows variable type unions
.else<S>(variable: Variable<S>): Variable<T | S>
const stringOrNumber = v.string().else(v.number());
Applying Transformations
Applies a custom transformation function to the variable's value. An optional targetType can be provided for documentation purposes.
type Transformer<I, O> = (value: I) => Result<O, string[]>
.transform<S>(transform: Transformer<T, S>, type?: string): Variable<S>
const isOdd = v.number().optional().transform(
value => Result.success((Math.round(value || 0) % 2) === 1)
);
Transformations are particularly powerful, allowing for value conversion, additional validation and type conversion if necessary.
Typed Variables and Helper Functions
varcor provides a series of helper functions designed to define and enforce the types and constraints of your environment variables:
Boolean Variables
Define boolean variables, interpreting various string values ('true'
, 'false'
, '1'
, '0'
) as booleans.
import { v } from 'varcor';
const DEBUG = v.boolean();
Number Variables
Define numeric environment variables, with support for minimum and maximum constraints.
import { v } from 'varcor';
const PORT = v.number().min(3000).max(9000);
Integer Variables
Similar to number variables, but specifically for integer values.
import { v } from 'varcor';
const RETRY_LIMIT = v.integer().min(1).max(5);
String Variables
Define string variables, with optional validators or regex pattern matching.
import { v } from 'varcor';
const DATABASE_URL = v.string().regex(/mongodb:\/\/.+/, 'mongodb Url');
const MAIN_URL = v.string().url();
const EMAIL = v.string().email();
const UUID = v.string().uuid();
const PASSWORD = v.string().validate(value =>
value.length > 10 && value.length < 20
? Result.success(value)
: Result.failure(['must be between 10 and 20 characters.'])
)
Enum Variables
Define enumerated string variables, ensuring the value matches one of the predefined options.
import { v } from 'varcor';
const ENVIRONMENT = v.enum().value('development').value('production').value('test');
You can use a single value enum - the v.literal(value: string)
helper function:
const RED = v.literal('RED');
Date and Time Variables
Define variables for date and time, supporting a generic DateTime object, and both JavaScript Date
objects and luxon
DateTime objects.
Types:
type DateType = 'date' | 'time' | 'datetime' | 'timeTz' | 'datetimeTz';
type DateObject = {
year: number;
month: number;
day: number;
hour: number;
minute: number;
second: number;
ms: number;
tz: TimeZone;
};
v.dateobj:(from?: DateType): Variable<DateObject>
v.jsdate:(from?: DateType): Variable<Date>
v.luxdate:(from?: DateType): Variable<DateTime>
Usage:
import { v } from 'varcor';
const OBJ_DATE = v.dateobj('datetimeTz')
const JS_DATE = v.jsdate('date');
const LUX_DATE = v.luxdate('time');
JSON Variables
Parse and validate JSON formatted string variables.
Types:
type JsonValidator<T> = (data: any) => Result<T, string[]>;
v.json<T = any>(validator?: JsonValidator<T>): Variable<T>
Usage:
import { v } from 'varcor';
const CONFIG = v.json().validate();
Zod Schema Validation
Leverage zod
schemas for complex JSON object validation.
import { z } from 'zod';
import { v } from 'varcor';
const MY_SCHEMA = z.object({ key: z.string() });
const CONFIG = v.tson(MY_SCHEMA);
DataObject and DataObjectBuilder Usage
DataObject
and DataObjectBuilder
are utilities for managing the configuration data within your applications. Here's how to use them effectively:
DataObject
DataObject
is a simple key-value mapping where values are either strings or undefined
.
type DataObject = { [key: string]: string | undefined; };
DataObjectBuilder
The DataObjectBuilder
class provides an interface to incrementally build a DataObject
. It supports adding data from environment variables, JSON strings, .env
files, and more.
DataObjectBuilder is immutable, so each method creates a new DataObjectBuilder.
Basic Usage
-
Creating a New DataObjectBuilder Instance
To start building a new DataObject
, simply instantiate DataObjectBuilder
:
import { DataObjectBuilder } from './DataObjectBuilder';
let builder = new DataObjectBuilder()
-
Adding DataObjects or DataObjectBuilders
Incorporate other DataObjects or DataObjectBuilders.
Signature
.addData(data: DataObject | DataObjectBuilder):
Example
const dataObject: DataObject = {
PORT: '8080',
NAME: 'MyApp'
};
const dataBuilder = v.data.new().addDataObject({
TYPE: 'Open',
DATE: '2024-01-01'
})
builder = builder.data(appConfig)
builder = builder.data(dataBuilder);
-
Adding Environment Variables
Easily include all current environment variables into your data object:
builder = builder.env();
-
Adding Data from an Object
Incorporate configuration data from a regular JavaScript object. Non-string values are automatically converted to JSON strings:
const appConfig = {
port: 8080,
name: "MyApp",
features: { logging: true, debugMode: false },
};
builder = builder.object(appConfig);
-
Adding Data from json
string
const jsonString = '{"apiUrl": "https://api.example.com", "timeout": 5000}';
builder = builder.json(jsonString);
-
Adding Data from dotenv
string
If you have a dotenv
formatted string containing environment variables, you can parse and add those variables:
const envFormat = `
# Variables
API_URL=http://api.example.com;
TIMEOUT="5000";
`
builder = builder.dotenv(envFormat);
-
File Helpers
DataObjectBuilder
has two methods that allow importing from json
and dotenv
files.
Each method takes the FileOptions type
.jsonFile(path: string, options: FileOptions);
.dotenvFile(path: string, options: FileOptions);
type FileOptions = {
when?: boolean;
optional?: boolean;
fileExists?: (path: string) => boolean;
readFile?: (path: string) => string;
};
builder = builder.dotenvFile('./production.env', { when: process.env.NODE_ENV === 'production' });
builder = builder.dotenvFile('./development.env', { when: process.env.NODE_ENV === 'development' });
-
Chaining
As DataObjectBuilder
methods return a new builder instance, allowing for method chaining:
const finalDataObject = new DataObjectBuilder()
.env()
.object(appConfig)
.dotenvFile('./.env')
.data({});
console.log(finalDataObject);
-
Helper Methods
All methods on DataObjectBuilder are available in v.data, to allow easy initializing
v.data.new();
v.data.env();
v.data.json(...);
v.data.jsonFile(...);
-
Finalizing and Retrieving the DataObject
Once you've added all your data sources, finalize the builder to get your DataObject
:
const finalDataObject: DataObject = builder.toDataObject();
console.log(finalDataObject);
Type Inference and Parsing