@strattadb/environment
Version:
Environment variable configuration for Node.js made easy.
240 lines (184 loc) • 5.39 kB
text/typescript
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;
};