UNPKG

@strattadb/environment

Version:

Environment variable configuration for Node.js made easy.

240 lines (184 loc) 5.39 kB
import validator from 'validator'; import EnvironmentVariableError from './EnvironmentVariableError'; export type Parser<TReturn = any> = (serializedValue: string) => TReturn; /** * Parses a string. */ export const string: Parser<string> = (serializedValue) => serializedValue; /** * Parses a boolean. parsed values are case insensitive. * Truthy values: true, 1, yes. * Falsy values: false, 0, no. */ export const boolean: Parser<boolean> = (serializedValue) => { const truthyValues = ['true', '1', 'yes', 'on']; const falsyValues = ['false', '0', 'no', 'off']; const lowercaseSerializedValue = serializedValue.toLowerCase(); if (truthyValues.includes(lowercaseSerializedValue)) { return true; } if (falsyValues.includes(lowercaseSerializedValue)) { return false; } const validValuesString = [...truthyValues, ...falsyValues].join(' '); throw new EnvironmentVariableError( `value is not valid. Valid values: ${validValuesString}`, ); }; /** * Parses an integer. */ export const integer: Parser<number> = (serializedValue) => { if (!validator.isInt(serializedValue)) { throw new EnvironmentVariableError('value is not an integer'); } const value = Number.parseInt(serializedValue, 10); return value; }; /** * Parses a float. */ export const float: Parser<number> = (serializedValue) => { const value = Number.parseFloat(serializedValue); if (Number.isNaN(value)) { throw new EnvironmentVariableError('value is not a number'); } return value; }; /** * Parses an email. */ export const email: Parser<string> = (serializedValue) => { const value = serializedValue; if (!validator.isEmail(value)) { throw new EnvironmentVariableError('value is not an email'); } return value; }; /** * Parses a URL. */ export const url: Parser<string> = (serializedValue) => { const value = serializedValue; if (!validator.isURL(value)) { throw new EnvironmentVariableError('value is not an URL'); } return value; }; /** * Parses an IP address. */ export const ipAddress: Parser<string> = (serializedValue) => { const value = serializedValue; if (!validator.isIP(value)) { throw new EnvironmentVariableError('value is not an IP address'); } return value; }; /** * Parses a port number. */ export const port: Parser<number> = (serializedValue) => { if (!validator.isPort(serializedValue)) { throw new EnvironmentVariableError('value is not a port'); } const value = Number.parseInt(serializedValue, 10); return value; }; /** * Returns a parser that parses a value from * a list of whitelisted values. */ export function whitelist<TValue extends string>( whitelistedValues: readonly TValue[], ): Parser<TValue> { const whitelistParser: Parser<TValue> = (serializedValue) => { const value = serializedValue; if (!(whitelistedValues as readonly string[]).includes(value)) { const whitelistedValuesString = whitelistedValues .map((whitelistedValue) => `'${whitelistedValue}'`) .join(', '); throw new EnvironmentVariableError( `value is not in the whitelist. Valid values are ${whitelistedValuesString}`, ); } return value as TValue; }; return whitelistParser; } /** * Returns a parser that parses a value matching a regular expression. */ export function regex(pattern: RegExp): Parser<string> { const regexParser: Parser<string> = (serializedValue) => { const value = serializedValue; if (!pattern.test(value)) { throw new EnvironmentVariableError( `value does not match regex ${pattern.toString()}`, ); } return value; }; return regexParser; } export type ArrayParserArgs<TType> = Readonly<{ parser: Parser<TType>; separator?: string; }>; const defaultArraySeparator = ','; /** * Returns a parser that parses a list of values of a type. */ export function array<TType>( args: ArrayParserArgs<TType>, ): Parser<readonly TType[]> { const separator = args.separator || defaultArraySeparator; const arrayParser: Parser<readonly TType[]> = (serializedArray) => { const serializedValues = serializedArray.split(separator); const values = serializedValues.map((serializedValue) => args.parser(serializedValue), ); return values; }; return arrayParser; } /** * Parses a positive integer. */ export const positiveInteger: Parser<number> = (serializedValue) => { const value = integer(serializedValue); if (value <= 0) { throw new EnvironmentVariableError('value is not positive'); } return value; }; /** * Parses a non-positive integer. */ export const nonPositiveInteger: Parser<number> = (serializedValue) => { const value = integer(serializedValue); if (value > 0) { throw new EnvironmentVariableError('value is positive'); } return value; }; /** * Parses a negative integer. */ export const negativeInteger: Parser<number> = (serializedValue) => { const value = integer(serializedValue); if (value >= 0) { throw new EnvironmentVariableError('value is not negative'); } return value; }; /** * Parses a non-negative integer. */ export const nonNegativeInteger: Parser<number> = (serializedValue) => { const value = integer(serializedValue); if (value < 0) { throw new EnvironmentVariableError('value is negative'); } return value; };