Socket
Socket
Sign inDemoInstall

env-verifier

Package Overview
Dependencies
0
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.1.3 to 1.2.0-github-workflows.1

65

dist/index.d.ts

@@ -1,28 +0,51 @@

export interface MappedConfig {
[key: string]: any | string | undefined | MappedConfig;
declare type Cast<X extends any, Y extends any> = X extends Y ? X : Y;
declare type TransformTuple_<U extends any> = (string | ((_: string) => U))[];
declare type MappedConfigElement_<E extends any> = E extends any[] ? E extends TransformTuple_<infer R> ? R : never : E extends InsertValue<infer U> ? U : E extends SecretKey ? Secret : E extends string ? string | undefined : MappedConfig<E>;
declare type MappedConfigElement<E extends any> = MappedConfigElement_<E> extends infer X ? Cast<X, any> : never;
export declare type MappedConfig<T> = {
[P in keyof T]: MappedConfigElement<T[P]>;
};
export declare type TransformTuple<T> = [string, (_: string) => T];
export declare type ConfigWithEnvKeys<T> = {
[P in keyof T]: T[P] extends InsertValue<infer U> ? InsertValue<U> : T[P] extends SecretKey ? SecretKey : T[P] extends string ? string : T[P] extends TransformTuple<infer U> ? TransformTuple<U> : T[P] extends ConfigWithEnvKeys<T[P]> ? ConfigWithEnvKeys<T[P]> : never;
};
declare type MissingValue = {
path: string;
envKey: string;
};
export declare type VerifiedConfig<T> = {
[P in keyof T]: T[P] extends SecretKey ? Secret : T[P] extends TransformTuple_<infer U> ? U : T[P] extends InsertValue<infer U> ? U : T[P] extends string ? string : VerifiedConfig<T[P]>;
};
export declare class InsertValue<T> {
value: T;
constructor(value: T);
}
export interface TransformFn {
(envValue: string): any;
export declare class SecretKey {
secret: string;
constructor(secret: string);
}
export declare type TransformTuple = [string, TransformFn];
export interface ConfigWithEnvKeys {
[key: string]: string | InsertValue | TransformTuple | ConfigWithEnvKeys;
export declare class Secret {
toJSON(): string;
reveal: {
(): string;
};
constructor(secret: string);
}
export interface VerifiedConfig {
[key: string]: any | string | VerifiedConfig;
}
declare class InsertValue {
value: any;
constructor(value: any);
}
export declare function verify(config: ConfigWithEnvKeys, env?: {
[key: string]: string | undefined;
}): {
config: MappedConfig;
export declare type VerifyReturnObject<T> = {
config: MappedConfig<T>;
missingValues: MissingValue[];
missingValueMessages: string[];
/**
* @deprecated please use missingValues or missingValueMessages instead.
*/
errors: string[];
};
export declare function strictVerify(config: ConfigWithEnvKeys, env?: {
export declare function verify<T>(config: T extends ConfigWithEnvKeys<T> ? T : never, env?: {
[key: string]: string | undefined;
}): VerifiedConfig;
export declare function insert(value: any): InsertValue;
}): VerifyReturnObject<T>;
export declare function strictVerify<T>(config: T extends ConfigWithEnvKeys<T> ? T : never, env?: {
[key: string]: string | undefined;
}): VerifiedConfig<T>;
export declare function insert<T>(value: T): InsertValue<T>;
export declare function secret(envKey: string): SecretKey;
export {};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.secret = exports.insert = exports.strictVerify = exports.verify = exports.Secret = exports.SecretKey = exports.InsertValue = void 0;
class InsertValue {

@@ -8,55 +9,96 @@ constructor(value) {

}
const recursiveVerify = ({ config, env, errors = [], path = '' }) => {
const mapConf = (key) => {
const value = config[key];
const subPath = path.length === 0 ? key : `${path}.${key}`;
const getValueFromEnv = (key) => {
const envValue = env[key];
if (envValue === undefined || envValue.length === 0) {
errors.push(new Error(`environment value ${key} is missing from config object at ${subPath}`));
return undefined;
}
return envValue;
};
if (value instanceof InsertValue) {
return { [key]: value.value };
}
else if (Array.isArray(value)) {
const [envKey, transformFn] = value;
const envValue = getValueFromEnv(envKey);
const transforedValue = envValue && transformFn(envValue);
return { [key]: transforedValue };
}
else if (typeof value === 'string') {
const envValue = getValueFromEnv(value);
return { [key]: envValue };
}
else {
const { errors: subErrors, config: subConfig } = recursiveVerify({
config: value,
env,
path: subPath
});
errors = errors.concat(subErrors);
return { [key]: subConfig };
}
};
const reduceConf = (acc, obj) => ({
...acc,
...obj
exports.InsertValue = InsertValue;
class SecretKey {
constructor(secret) {
this.secret = secret;
}
}
exports.SecretKey = SecretKey;
class Secret {
constructor(secret) {
this.reveal = () => secret;
}
toJSON() {
return '[secret]';
}
}
exports.Secret = Secret;
const isNode = () => typeof process === 'object' && process + '' === '[object process]';
const getSecretObject = (secret) => {
const secretObj = new Secret(secret);
if (isNode()) {
const util = require('util');
secretObj[util.inspect.custom] = () => '[secret]';
}
return secretObj;
};
const getEnvValueOrErrorCurried = (env, subPath) => (key) => {
const envValue = env[key];
if (envValue === undefined || envValue.length === 0) {
const error = { envKey: key, path: subPath };
return [undefined, [error]];
}
return [envValue, []];
};
const getMapConfigFunction = ({ config, env, path = '', }) => (key) => {
const value = config[key];
const subPath = path.length === 0 ? key + '' : `${path}.${key}`;
const getEnvValueOrError = getEnvValueOrErrorCurried(env, subPath);
if (value instanceof SecretKey) {
const secretKey = value.secret;
const [secretValue, errors] = getEnvValueOrError(secretKey);
return [
{ [key]: getSecretObject(secretValue) },
errors,
];
}
if (value instanceof InsertValue) {
return [{ [key]: value.value }, []];
}
if (Array.isArray(value)) {
const [envKey, transformFn] = value;
const [envValue, errors] = getEnvValueOrError(envKey);
const transformedValue = envValue && transformFn(envValue);
return [{ [key]: transformedValue }, errors];
}
if (typeof value === 'string') {
const [envValue, errors] = getEnvValueOrError(value);
return [{ [key]: envValue }, errors];
}
const { errors, config: subConfig } = recursiveVerify({
config: value,
env,
path: subPath,
});
const mappedConf = Object.keys(config).map(mapConf);
const newConfig = mappedConf.reduce(reduceConf, {});
return { config: newConfig, env, errors, path };
return [{ [key]: subConfig }, errors];
};
const reduceConf = (acc, [config, errors]) => {
return {
config: Object.assign(Object.assign({}, acc.config), config),
errors: acc.errors.concat(errors),
};
};
const recursiveVerify = (paramCollection) => {
const mapConf = getMapConfigFunction(paramCollection);
const mappedConf = Object.keys(paramCollection.config).map(mapConf);
return mappedConf.reduce(reduceConf, { config: {}, errors: [] });
};
function verify(config, env = process.env) {
const { config: builtConfig, errors } = recursiveVerify({ config, env });
const errorMessages = errors.map(({ message }) => message);
return { config: builtConfig, errors: errorMessages };
const { config: builtConfig, errors } = recursiveVerify({
config,
env,
});
const missingValueMessages = errors.map(({ envKey, path }) => `environment value ${envKey} is missing from config object at ${path}`);
return {
config: builtConfig,
missingValues: errors,
missingValueMessages,
errors: missingValueMessages,
};
}
exports.verify = verify;
function strictVerify(config, env = process.env) {
const { config: builtConfig, errors } = verify(config, env);
if (errors.length > 0) {
throw new Error(`Missing configuration values: ${errors.join('\n')}`);
const { config: builtConfig, missingValueMessages } = verify(config, env);
if (missingValueMessages.length > 0) {
throw new Error(`Missing configuration values: ${missingValueMessages.join('\n')}`);
}

@@ -70,2 +112,6 @@ return builtConfig;

exports.insert = insert;
function secret(envKey) {
return new SecretKey(envKey);
}
exports.secret = secret;
//# sourceMappingURL=index.js.map
{
"name": "env-verifier",
"version": "1.1.3",
"version": "1.2.0-github-workflows.1",
"description": "\"Make sure you have all your env variables!\"",

@@ -11,2 +11,3 @@ "main": "dist/index.js",

"prepack": "tsc",
"prepare": "husky install",
"build": "tsc"

@@ -18,13 +19,20 @@ },

],
"files": [
"dist"
],
"author": "Snugbear",
"license": "MIT",
"devDependencies": {
"@commitlint/cli": "^13.1.0",
"@commitlint/config-conventional": "^13.1.0",
"@semantic-release/git": "^9.0.0",
"@types/jest": "^26.0.10",
"@types/node": "^12.7.1",
"jest": "^24.8.0",
"onchange": "^6.0.0",
"@types/jest": "^24.0.17",
"prettier": "1.18.2",
"ts-jest": "^24.0.2",
"typescript": "^3.5.3"
"husky": "^7.0.1",
"jest": "^26.6.3",
"onchange": "^7.0.2",
"prettier": "2.0.5",
"ts-jest": "^26.5.1",
"typescript": "^4.1.5"
}
}
# env-verifier
Quickly verify that incoming variables from process.env aren't missing.
Verify that your environment variables exist, and build up your config object at the same time!

@@ -9,2 +9,12 @@ [GitHub](https://github.com/pluralsight/env-verifier)

## Package Purpose
Certain types of apps require the use of different variables depending on the environment that the app is run in.
The purpose of this package is to fail early whenever one of those values is missing from the environment object (ie: `process.env`).
Using this package properly will prevent the sometimes cryptic errors that occur when environment variables are missing.
Because every missing environment variable that `env-verifier` encountered is returned (or is displayed in a thrown error), this package can also help with the: `run the app, app crashes because of missing environment variable, add environment variable, repeat` loop that sometimes occurs.
## Getting Started

@@ -48,3 +58,3 @@

const { config, errors } = verify({
const { config, missingValues } = verify({
database: {

@@ -59,3 +69,5 @@ name: 'DB_NAME'

// do custom error logging, possibly throw your own errors
errors.forEach(console.error)
missingValues.forEach(
({ envKey, path }) => console.log(`missing env variable: ${envKey} from config at path: ${path}`)
)

@@ -71,44 +83,120 @@ module.exports = config

- [Function Signatures](#functionSignatures)
- [Arbitrary Value Insertion](#arbitraryValueInsertion)
- [Error Generation and Reporting](#errorGenerationAndReporting)
- [Variable Transformation (TransformTuple)](#variableTransformation)
- [Function Parameters and Return Types](#function-parameters-and-return-types)
- [Processing Missing Values](#processing-missing-values)
- [Arbitrary Value Insertion](#arbitrary-value-insertion)
- [Secret Insertion](#secret-insertion)
- [Error Generation and Reporting](#error-generation-and-reporting)
- [Variable Transformation (TransformTuple)](#variable-transformation)
- [Dynamic Typings](#dynamic-typings)
#### <a name="functionSignatures"><a/> Function signatures
### Function Parameters and Return Types
#### `verify`
```typescript
interface TransformFn {
(envValue: string): any
export function insert<T>(value: T) => Insert<T> // see `Arbitrary Value Insertion` documentation
export function secret(envKey: string) => Secret // see `Secret Insertion` documentation
export type TransformTuple = [string, (envValue: string) => any]
// Type given as example only, the real type is a bit more complex
export type ConfigWithEnvKeys<T> = {
[P in keyof T]: string | TransformTuple | ReturnType<typeof insert> | ReturnType<typeof secret> | ConfigWithEnvKeys<T[P]>
}
//see below
// [envKeyName, TransformFn]
type TransformTuple = [string, TransformFn]
// Type given as example only, the real type is a bit more complex
export type MappedConfig<T> = {
[P in keyof T]: string | null | ReturnType<typeof secret> | MappedConfig<T[P]> | any // ie: return type of `insert` or `TransformTuple` function
}
interface ConfigWithEnvKeys {
[key: string]: string | InsertValue | TransformTuple | ConfigWithEnvKeys
export type MissingValue = {
path: string
envKey: string
}
interface MappedConfig {
[key: string]: any | string | undefined | Config
export function verify<T>(
config: ConfigWithEnvKeys<T>
env: { [key: string]: string | undefined } = process.env
): {
config: MappedConfig<T>,
missingValues: MissingValue[],
missingValueMessages: string[],
/**
* @deprecated Please use missingValueMessages
*/
errors: string[]
}
```
interface VerifiedConfig {
[key: string]: any | string | VerifiedConfig
#### `strictVerify`
```typescript
export function insert<T extends any>(value: T) => Insert<T> // see `Arbitrary Value Insertion` documentation
export function secret(envKey: string) => Secret // see `Secret Insertion` documentation
export type TransformTuple = [string, (envValue: string) => any]
// Type given as example only, the real type is a bit more complex
export type ConfigWithEnvKeys<T> = {
[P in keyof T]: string | TransformTuple | ReturnType<typeof insert> | ReturnType<typeof secret> | ConfigWithEnvKeys<T[P]>
}
interface Env {
[key: string]: string | undefined
// Type given as example only, the real type is a bit more complex
// Similar to MappedConfig<T>, but does not contain nulls (except for as returned by `TransformTuple` functions or `insert` calls)
export type VerifiedConfig<T> = {
[P in keyof T]: string | ReturnType<typeof secret> | VerifiedConfig<T[P]> | any // ie: return type of `insert` or `TransformTuple` function
}
function insert(value: any): InsertValue
export function strictVerify<T>(
config: ConfigWithEnvKeys<T>
env: { [key: string]: string | undefined } = process.env
): VerifiedConfig<T>
```
function verify(config: ConfigWithEnvKeys, env: Env = process.env): { config: MappedConfig, errors: string[] }
### Processing Missing Values
function strictVerify(config: ConfigWithEnvKeys, env: Env = process.env): VerifiedConfig
An array of objects of type `MissingValue` is returned from the `verify` function.
```typescript
type MissingValue = {
path: string
envKey: string
}
```
#### <a name="arbitraryValueInsertion"><a/> Arbitrary Value Insertion
#### `envKey`
The key of the missing `env` value.
#### `path`
The path in the `config` argument to the missing `env` variable.
Example:
```transcript
import { verify } from 'env-verifier'
const config = {
db: {
password: 'DB_PASSWORD'
}
}
const env = {
DB_PASSWORD: undefined
}
const result = verify(config, env)
console.log(result.missingValues)
// results in:
// [{ path: 'db.password', envKey: 'DB_PASSWORD' }]
```
### Arbitrary Value Insertion
You may have values that aren't present on your `env` object, but that you would like to live in your config object, this can be achieved by using the `insert()` function.

@@ -131,18 +219,81 @@

#### <a name="errorGenerationAndReporting"><a/> Error Generation and Reporting
### Secret Insertion
As of env-verifier version `1.2.0`, the obfuscation of env secrets is supported.
by wrapping the env key of the secret in the `secret` function exported by `env-verifier`, the secret will be retrieved and wrapped in a `Secret` object (see function specification above).
Note: support for transforming or inserting secrets is not supported at this time.
To retrieve the secret, the `reveal` function can be called.
What secret obfuscation will do:
- protect secrets from casual logging of the produced config object
- `JSON.stringify` of the config object will replace all secrets with the string `'[secret]'`
What secret obfuscation will not do:
- prevent the actually logging of the revealed secret
- mutate the actual string returned from the `env` object
```javascript
const { verify, secret } = require('env-verifier')
const env = {
PASSWORD: 'superSecretPassword'
}
const { config } = verify({
password: secret('PASSWORD')
... // other env key names
}, env)
module.exports = config
//exports:
{
password: {
reveal(): string
}
... // other env values
}
config.password.reveal()
// returns:
'superSecretPassword'
console.log(config)
// prints:
// {
// if you're using nodejs:
// password: [secret]
// if you're using other JS environments:
// password: Secret { reveal: [Function] }
// ... other env values
// }
JSON.stringify(config)
// returns
// {
// "password": "[secret]"
// ... other env values
// }
```
### Error Generation and Reporting
Error reports are generated when an `env` variable is missing. An `env` variable is considered missing under the following circumstances:
- `undefined` is returned from the `env` object.
- an empty string, `''`, is returned from the `env` object.
- an empty string, `''`, is returned from the `env` object. (useful for development with Docker)
`verify` will always return the errors array, but it will be an empty array if there are no `env` misses.
`verify` will always return an array of `MissingValue`s, which will be empty if there are no `env` misses.
`strictVerify` will not throw an error on the first encountered missing `env` value. Instead it will continue in order to report all missing `env` variables.
`strictVerify` will evaluate the entire `config` object before throwing any errors in order to report all missing `env` variables
#### <a name="variableTransformation"><a/> Variable Transformation (TransformTuple)
### Variable Transformation (TransformTuple)
Since process.env only returns strings, sometimes its necessary to transform those strings into something else (IE: transform the string `"true"` to a boolean `true`)
Since `env-verifier` only takes environment key-value pair objects that have `strings` as the values, its sometimes necessary to transform those strings into something else (IE: transform the string `"true"` to a boolean `true`)
This can be done by passing in an array (TransformTuple) containing the `env` variable name, and the function that you would like to use to transform the `env` variable value like so:
This can be done by passing in an array (called a `TransformTuple` in this context) containing the `env` variable name, and the function that you would like to use to transform the `env` variable value like so:

@@ -160,6 +311,64 @@ ```javascript

### Prerequisites
### Dynamic Typings
This package works best with projects that have centralized config files, IE: You map your `.env` variables to a `config` object in a file, and `import`/`require` that config object wherever you need `.env` values.
**Important**: as of `v1.4.0` `env-verifier` should now be able to correctly and dynamically infer the return types of both `verify` and `strictVerify` without any extra help. the below is only valid for versions that pre-date `v1.4.0`
`env-verifier` tries to give typescript typings for the config object that it returns, but needs a little help to get the correct types
If you are using TypeScript, you can do the following:
```typescript
const config: {
a: 'A',
b: insert([1, 2])
c: {
d: ['A', (envValue) => ([envValue])]
}
}
const verifiedConfig = strictVerify(config)
// pre-v1.4.0 typings:
// typeof verifiedConfig = {
// a: VerifiedConfig<unknown>
// b: VerifiedConfig<unknown>
// c: VerifiedConfig<unknown>
// }
// add typeof config object
const verifiedConfig = strictVerify<typeof config>(config)
// better typings:
// typeof verifiedConfig = {
// a: string,
// b: number[],
// c: {
// d: (string | (envVerify: any) => any)
// }
// }
// cast TransformTuple types correctly
const config = {
a: 'A',
b: insert([1, 2])
c: {
d: ['A', (envValue) => ([envValue])] as TransformTuple<string>
}
}
const verifiedConfig = strictVerify<typeof config>(config)
// best typings:
// typeof verifiedConfig = {
// a: string,
// b: number[],
// c: {
// d: string
// }
// }
```
## Prerequisites
This package is written in TypeScript@4.1.5 and is built/distributed for environments that support the majority of the es2016 specification.
This package also works best with projects that have centralized config files, IE: You map your `.env` variables to a `config` object in a file, and `import`/`require` that config object wherever you need `.env` values.
Other than that, just install the package and get going!

@@ -189,3 +398,3 @@

Please read [CONTRIBUTING.md](CONSTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.

@@ -192,0 +401,0 @@ ## Versioning

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc