Comparing version 5.0.0 to 5.1.0
@@ -0,1 +1,6 @@ | ||
## 5.1.0 (09/09/19) | ||
* Ability to add custom accessors in PR #72 (thanks @todofixthis) | ||
* Improved TypeScript tests | ||
* Fixed warning generated by husky | ||
## 5.0.0 (14/06/19) | ||
@@ -2,0 +7,0 @@ * Return values from `asArray()` are now more intuitive & consitent |
@@ -210,5 +210,5 @@ | ||
declare class EnvVarError extends Error {} | ||
export class EnvVarError extends Error {} | ||
interface IEnv { | ||
interface IEnv<PresentVariable, OptionalVariable> { | ||
/** | ||
@@ -222,3 +222,3 @@ * Returns an object containing all current environment variables | ||
*/ | ||
get (varName: string): IOptionalVariable; | ||
get (varName: string): OptionalVariable; | ||
@@ -228,3 +228,3 @@ /** | ||
*/ | ||
get (varName: string, defaultValue: string): IPresentVariable; | ||
get (varName: string, defaultValue: string): PresentVariable; | ||
@@ -235,3 +235,6 @@ /** | ||
*/ | ||
from(values: NodeJS.ProcessEnv): IEnv; | ||
from<T extends Extensions, K extends keyof T>(values: NodeJS.ProcessEnv, extensions?: T): IEnv< | ||
IPresentVariable & Record<K, (...args: any[]) => ReturnType<T[K]>>, | ||
IOptionalVariable & Record<K, (...args: any[]) => ReturnType<T[K]>|undefined> | ||
>; | ||
@@ -245,3 +248,14 @@ /** | ||
declare const env: IEnv; | ||
export = env; | ||
export type Extensions = { | ||
[key: string]: ExtensionFn<any> | ||
} | ||
export type RaiseErrorFn = (error: string) => void | ||
export type ExtensionFn<T> = (value: string, ...args: any[]) => T | ||
export function get(): {[varName: string]: string} | ||
export function get(varName: string): IOptionalVariable; | ||
export function get(varName: string, defaultValue: string): IPresentVariable; | ||
export function from<T extends Extensions, K extends keyof T>(values: NodeJS.ProcessEnv, extensions?: T): IEnv< | ||
IPresentVariable & Record<K, (...args: any[]) => ReturnType<T[K]>>, | ||
IOptionalVariable & Record<K, (...args: any[]) => ReturnType<T[K]>|undefined> | ||
>; |
@@ -9,5 +9,7 @@ 'use strict' | ||
* @param {Object} container target container to read values from | ||
* @param {Object} extraAccessors additional accessors to attach to the | ||
* resulting object | ||
* @return {Object} a new module instance | ||
*/ | ||
const from = (container) => { | ||
const from = (container, extraAccessors) => { | ||
return { | ||
@@ -18,3 +20,3 @@ from: from, | ||
* This is the Error class used to generate exceptions. Can be used to identify | ||
* exceptions adn handle them appropriatly. | ||
* exceptions and handle them appropriately. | ||
*/ | ||
@@ -34,3 +36,3 @@ EnvVarError: require('./lib/env-error'), | ||
return variable(container, variableName, defaultValue) | ||
return variable(container, variableName, defaultValue, extraAccessors || {}) | ||
} | ||
@@ -37,0 +39,0 @@ } |
@@ -5,3 +5,3 @@ 'use strict' | ||
module.exports = function asArray (raiseError, value, delimiter) { | ||
module.exports = function asArray (value, delimiter) { | ||
delimiter = delimiter || ',' | ||
@@ -12,4 +12,4 @@ | ||
} else { | ||
return asString(raiseError, value).split(delimiter).filter(Boolean) | ||
return asString(value).split(delimiter).filter(Boolean) | ||
} | ||
} |
'use strict' | ||
module.exports = function asBoolStrict (raiseError, value) { | ||
module.exports = function asBoolStrict (value) { | ||
const val = value.toLowerCase() | ||
if ((val !== 'false') && (val !== 'true')) { | ||
raiseError('should be either "true", "false", "TRUE", or "FALSE"') | ||
throw new Error('should be either "true", "false", "TRUE", or "FALSE"') | ||
} | ||
@@ -9,0 +9,0 @@ |
'use strict' | ||
module.exports = function asBool (raiseError, value) { | ||
module.exports = function asBool (value) { | ||
const val = value.toLowerCase() | ||
@@ -14,3 +14,3 @@ | ||
if (allowedValues.indexOf(val) === -1) { | ||
raiseError('should be either "true", "false", "TRUE", "FALSE", 1, or 0') | ||
throw new Error('should be either "true", "false", "TRUE", "FALSE", 1, or 0') | ||
} | ||
@@ -17,0 +17,0 @@ |
@@ -5,7 +5,7 @@ 'use strict' | ||
module.exports = function asEnum (raiseError, value, validValues) { | ||
const valueString = asString(raiseError, value) | ||
module.exports = function asEnum (value, validValues) { | ||
const valueString = asString(value) | ||
if (validValues.indexOf(valueString) < 0) { | ||
raiseError(`should be a one of [${validValues.join(', ')}]`) | ||
throw new Error(`should be one of [${validValues.join(', ')}]`) | ||
} | ||
@@ -12,0 +12,0 @@ |
@@ -5,7 +5,7 @@ 'use strict' | ||
module.exports = function floatNegative (raiseError, value) { | ||
const ret = asFloat(raiseError, value) | ||
module.exports = function floatNegative (value) { | ||
const ret = asFloat(value) | ||
if (ret > 0) { | ||
raiseError('should be a negative float') | ||
throw new Error('should be a negative float') | ||
} | ||
@@ -12,0 +12,0 @@ |
@@ -5,7 +5,7 @@ 'use strict' | ||
module.exports = function floatPositive (raiseError, value) { | ||
const ret = asFloat(raiseError, value) | ||
module.exports = function floatPositive (value) { | ||
const ret = asFloat(value) | ||
if (ret < 0) { | ||
raiseError('should be a positive float') | ||
throw new Error('should be a positive float') | ||
} | ||
@@ -12,0 +12,0 @@ |
'use strict' | ||
module.exports = function asFloat (raiseError, value) { | ||
module.exports = function asFloat (value) { | ||
const n = parseFloat(value) | ||
if (isNaN(n)) { | ||
raiseError('should be a valid float') | ||
throw new Error('should be a valid float') | ||
} | ||
@@ -9,0 +9,0 @@ |
@@ -5,7 +5,7 @@ 'use strict' | ||
module.exports = function intNegative (raiseError, value) { | ||
const ret = asInt(raiseError, value) | ||
module.exports = function intNegative (value) { | ||
const ret = asInt(value) | ||
if (ret > 0) { | ||
raiseError('should be a negative integer') | ||
throw new Error('should be a negative integer') | ||
} | ||
@@ -12,0 +12,0 @@ |
@@ -5,7 +5,7 @@ 'use strict' | ||
module.exports = function intPositive (raiseError, value) { | ||
const ret = asInt(raiseError, value) | ||
module.exports = function intPositive (value) { | ||
const ret = asInt(value) | ||
if (ret < 0) { | ||
raiseError('should be a positive integer') | ||
throw new Error('should be a positive integer') | ||
} | ||
@@ -12,0 +12,0 @@ |
'use strict' | ||
module.exports = function asInt (raiseError, value) { | ||
module.exports = function asInt (value) { | ||
const n = parseInt(value, 10) | ||
if (isNaN(n) || value.toString().indexOf('.') !== -1) { | ||
raiseError('should be a valid integer') | ||
throw new Error('should be a valid integer') | ||
} | ||
@@ -9,0 +9,0 @@ |
@@ -5,7 +5,7 @@ 'use strict' | ||
module.exports = function asJsonArray (raiseError, value) { | ||
var ret = asJson(raiseError, value) | ||
module.exports = function asJsonArray (value) { | ||
var ret = asJson(value) | ||
if (!Array.isArray(ret)) { | ||
raiseError('should be a parseable JSON Array') | ||
throw new Error('should be a parseable JSON Array') | ||
} | ||
@@ -12,0 +12,0 @@ |
@@ -5,7 +5,7 @@ 'use strict' | ||
module.exports = function asJsonArray (raiseError, value) { | ||
var ret = asJson(raiseError, value) | ||
module.exports = function asJsonArray (value) { | ||
var ret = asJson(value) | ||
if (Array.isArray(ret)) { | ||
raiseError('should be a parseable JSON Object') | ||
throw new Error('should be a parseable JSON Object') | ||
} | ||
@@ -12,0 +12,0 @@ |
'use strict' | ||
module.exports = function asJson (raiseError, value) { | ||
module.exports = function asJson (value) { | ||
try { | ||
return JSON.parse(value) | ||
} catch (e) { | ||
raiseError('should be valid (parseable) JSON') | ||
throw new Error('should be valid (parseable) JSON') | ||
} | ||
} |
@@ -5,7 +5,7 @@ 'use strict' | ||
module.exports = function asPortNumber (raiseError, value) { | ||
var ret = asIntPositive(raiseError, value) | ||
module.exports = function asPortNumber (value) { | ||
var ret = asIntPositive(value) | ||
if (ret > 65535) { | ||
raiseError('cannot assign a port number greater than 65535') | ||
throw new Error('cannot assign a port number greater than 65535') | ||
} | ||
@@ -12,0 +12,0 @@ |
'use strict' | ||
module.exports = function (raiseException, value) { | ||
module.exports = function (value) { | ||
return value | ||
} |
@@ -6,4 +6,4 @@ 'use strict' | ||
module.exports = function asUrlObject (raiseError, value) { | ||
const ret = asString(raiseError, value) | ||
module.exports = function asUrlObject (value) { | ||
const ret = asString(value) | ||
@@ -13,4 +13,4 @@ try { | ||
} catch (e) { | ||
raiseError('should be a valid URL') | ||
throw new Error('should be a valid URL') | ||
} | ||
} |
@@ -5,4 +5,4 @@ 'use strict' | ||
module.exports = function (raiseError, value) { | ||
return urlObject(raiseError, value).toString() | ||
module.exports = function (value) { | ||
return urlObject(value).toString() | ||
} |
@@ -6,3 +6,3 @@ 'use strict' | ||
/** | ||
* Creates a cutom error class that can be used to identify errors generated | ||
* Creates a custom error class that can be used to identify errors generated | ||
* by the module | ||
@@ -9,0 +9,0 @@ */ |
@@ -9,15 +9,17 @@ 'use strict' | ||
* the variable you wish to have returned | ||
* @param {Object} container Encapsulated container (e.g., `process.env`). | ||
* @param {String} varName Name of the requested property from `container`. | ||
* @param {*} defValue Default value to return if `varName` is invalid. | ||
* @param {Object} extraAccessors Extra accessors to install. | ||
* @return {Object} | ||
*/ | ||
module.exports = function getVariableAccessors (container, varName, defValue) { | ||
module.exports = function getVariableAccessors (container, varName, defValue, extraAccessors) { | ||
let isBase64 = false | ||
/** | ||
* Generate a function to throw an error | ||
* Throw an error with a consistent type/format. | ||
* @param {String} value | ||
*/ | ||
function generateRaiseError (value) { | ||
return function _raiseError (msg) { | ||
throw new EnvVarError(`"${varName}" ${msg}, but was "${value}"`) | ||
} | ||
function raiseError (value, msg) { | ||
throw new EnvVarError(`"${varName}" ${msg}, but was "${value}"`) | ||
} | ||
@@ -47,3 +49,3 @@ | ||
if (!value.match(base64Regex)) { | ||
generateRaiseError(value)('should be a valid base64 string if using convertFromBase64') | ||
raiseError(value, 'should be a valid base64 string if using convertFromBase64') | ||
} | ||
@@ -54,11 +56,12 @@ | ||
const args = [ | ||
generateRaiseError(value), | ||
value | ||
].concat(Array.prototype.slice.call(arguments)) | ||
const args = [value].concat(Array.prototype.slice.call(arguments)) | ||
return accessor.apply( | ||
accessor, | ||
args | ||
) | ||
try { | ||
return accessor.apply( | ||
accessor, | ||
args | ||
) | ||
} catch (error) { | ||
raiseError(value, error.message) | ||
} | ||
} | ||
@@ -118,3 +121,8 @@ } | ||
// Attach extra accessors, if provided. | ||
Object.entries(extraAccessors).forEach(([name, accessor]) => { | ||
accessors[name] = generateAccessor(accessor) | ||
}) | ||
return accessors | ||
} |
{ | ||
"name": "env-var", | ||
"version": "5.0.0", | ||
"version": "5.1.0", | ||
"description": "Verification, sanatization, and type coercion for environment variables in Node.js", | ||
@@ -12,7 +12,11 @@ "main": "env-var.js", | ||
"unit": "mocha test/", | ||
"precommit": "npm run lint && npm run unit", | ||
"lint": "standard example/*.js *.js \"lib/**/*.js\" test/*.js --fix", | ||
"test": "npm run unit && npm run coverage && npm run check-coverage && npm run lint && npm run ts-verify", | ||
"ts-verify": "tsc test/index.ts --noEmit --target es5" | ||
"ts-verify": "tsc && node test/types/index.js" | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "npm run lint && npm run unit" | ||
} | ||
}, | ||
"repository": { | ||
@@ -51,16 +55,16 @@ "type": "git", | ||
"devDependencies": { | ||
"@types/node": "~12.0.0", | ||
"@types/node": "~12.7.0", | ||
"bluebird": "~3.5.1", | ||
"chai": "~4.2.0", | ||
"coveralls": "~3.0.0", | ||
"husky": "~2.4.0", | ||
"mocha": "~6.1.2", | ||
"husky": "~3.0.0", | ||
"mocha": "~6.2.0", | ||
"mocha-lcov-reporter": "~1.3.0", | ||
"nyc": "~14.1.0", | ||
"standard": "~12.0.1", | ||
"standard": "~14.1.0", | ||
"typescript": "~3.1.3" | ||
}, | ||
"engines": { | ||
"node": ">=6.0" | ||
"node": ">=8" | ||
} | ||
} |
136
README.md
@@ -16,6 +16,7 @@ # env-var | ||
Verification, sanatization, and type coercion for environment variables in | ||
Verification, sanitization, and type coercion for environment variables in | ||
Node.js. Particularly useful in TypeScript environments. | ||
## Install | ||
**Note:** env-var requires Node version 8 or later. | ||
@@ -77,3 +78,3 @@ ### npm | ||
* [EnvVarError()](#envvarerror) | ||
* [from()](#fromvalues) | ||
* [from()](#fromvalues-extraaccessors) | ||
* [get()](#getvarname-default) | ||
@@ -84,3 +85,3 @@ * [variable](#variable) | ||
* [asArray()](#asarraydelimiter-string) | ||
* [asBoolStrict()](#asBoolStrict) | ||
* [asBoolStrict()](#asboolstrict) | ||
* [asBool()](#asbool) | ||
@@ -125,3 +126,3 @@ * [asPortNumer()](#asportnumber) | ||
### from(values) | ||
### from(values, extraAccessors) | ||
This function is useful if you're not in a typical Node.js environment, or for | ||
@@ -140,2 +141,118 @@ testing. It allows you to generate an env-var instance that reads from the | ||
#### extraAccessors | ||
When calling `from()` you can also pass an optional parameter containing | ||
additional accessors that will be attached to any variables gotten by that | ||
env-var instance. | ||
Accessor functions must accept at least one argument: | ||
- `{*} value`: The value that the accessor should process. | ||
**Important:** Do not assume that `value` is a string! | ||
Example: | ||
```js | ||
const { from } = require('env-var') | ||
// Environment variable that we will use for this example: | ||
process.env.ADMIN = 'admin@example.com' | ||
// Add an accessor named 'checkEmail' that verifies that the value is a | ||
// valid-looking email address. | ||
const env = from(process.env, { | ||
checkEmail: (value) => { | ||
const split = String(value).split('@') | ||
// Validating email addresses is hard. | ||
if (split.length !== 2) { | ||
throw new Error('must contain exactly one "@"') | ||
} | ||
return value | ||
} | ||
}) | ||
// We specified 'checkEmail' as the name for the accessor above, so now | ||
// we can call `checkEmail()` like any other accessor. | ||
let validEmail = env.get('ADMIN').checkEmail() | ||
``` | ||
The accessor function may accept additional arguments if desired; these must be | ||
provided explicitly when the accessor is invoked. | ||
For example, we can modify the `checkEmail()` accessor from above so that it | ||
optionally verifies the domain of the email address: | ||
```js | ||
const { from } = require('env-var') | ||
// Environment variable that we will use for this example: | ||
process.env.ADMIN = 'admin@example.com' | ||
// Add an accessor named 'checkEmail' that verifies that the value is a | ||
// valid-looking email address. | ||
// | ||
// Note that the accessor function also accepts an optional second | ||
// parameter `requiredDomain` which can be provided when the accessor is | ||
// invoked (see below). | ||
const env = from(process.env, { | ||
checkEmail: (value, requiredDomain) => { | ||
const split = String(value).split('@') | ||
// Validating email addresses is hard. | ||
if (split.length !== 2) { | ||
throw new Error('must contain exactly one "@"') | ||
} | ||
if (requiredDomain && (split[1] !== requiredDomain)) { | ||
throw new Error(`must end with @${requiredDomain}`) | ||
} | ||
return value | ||
} | ||
}) | ||
// We specified 'checkEmail' as the name for the accessor above, so now | ||
// we can call `checkEmail()` like any other accessor. | ||
// | ||
// `env-var` will provide the first argument for the accessor function | ||
// (`value`), but we declared a second argument `requiredDomain`, which | ||
// we can provide when we invoke the accessor. | ||
// Calling the accessor without additional parameters accepts an email | ||
// address with any domain. | ||
let validEmail = env.get('ADMIN').checkEmail() | ||
// If we specify a parameter, then the email address must end with the | ||
// domain we specified. | ||
let invalidEmail = env.get('ADMIN').checkEmail('github.com') | ||
``` | ||
This feature is also available for TypeScript users. The `ExtensionFn` type is | ||
expoed to help in the creation of these new accessors. | ||
```ts | ||
import { from, ExtensionFn, EnvVarError } from 'env-var' | ||
// Environment variable that we will use for this example: | ||
process.env.ADMIN = 'admin@example.com' | ||
const checkEmail: ExtensionFn<string> = (value) => { | ||
const split = String(value).split('@') | ||
// Validating email addresses is hard. | ||
if (split.length !== 2) { | ||
throw new Error('must contain exactly one "@"') | ||
} | ||
return value | ||
} | ||
const env = from(process.env, { | ||
checkEmail | ||
}) | ||
// Returns the email string if it's valid, otherwise it will throw | ||
env.get('ADMIN').checkEmail() | ||
``` | ||
### get([varname, [default]]) | ||
@@ -322,5 +439,5 @@ You can call this function 3 different ways: | ||
If you want to add a new type it's easy. Add a file to `lib/accessors`, | ||
with the name of the type e.g add a file named `number-zero.js` into that folder | ||
and populate it with code following this structure: | ||
If you want to add a new global accessor, it's easy. Add a file to | ||
`lib/accessors`, with the name of the type e.g add a file named `number-zero.js` | ||
into that folder and populate it with code following this structure: | ||
@@ -330,6 +447,5 @@ ```js | ||
* Validate that the environment value is an integer and equals zero. | ||
* @param {Function} raiseError use this to raise a cleanly formatted error | ||
* @param {String} environmentValue this is the string from process.env | ||
*/ | ||
module.exports = function numberZero (raiseError, environmentValue) { | ||
module.exports = function numberZero (environmentValue) { | ||
@@ -343,3 +459,3 @@ // Your custom code should go here...below code is an example | ||
} else { | ||
raiseError('should be zero') | ||
throw new Error('should be zero') | ||
} | ||
@@ -346,0 +462,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
39548
502
482