🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

12factor-env

Package Overview
Dependencies
Maintainers
1
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

12factor-env - npm Package Compare versions

Comparing version
1.17.0
to
1.17.1
+7
-26
esm/index.d.ts

@@ -42,26 +42,2 @@ import type { CleanedEnv, CleanOptions, Spec, ValidatorSpec } from 'envalid';

declare const cleanEnv: <S extends Record<string, ValidatorSpec<unknown>>>(environment: Record<string, string | undefined>, specs: S, options?: CleanOptions<S>) => CleanedEnv<S>;
/**
* Get the secrets path lazily to allow ENV_SECRETS_PATH changes at runtime
*/
declare const getSecretsPath: () => string;
/**
* Resolve the full path to a secret file
*/
declare const secretPath: (name: string) => string;
/**
* Read a secret from a file
* @param secret - The secret file name or path
* @returns The secret value or undefined if not found
*/
declare const getSecret: (secret: string | undefined) => string | undefined;
/**
* Read secrets from files based on the secret props configuration
* Supports both direct secret files and _FILE suffix pattern
*/
declare const secretEnv: <T extends Record<string, ValidatorSpec<unknown>>>(inputEnv: Record<string, string | undefined>, secretProps: T) => Record<string, string>;
/**
* Create a validator for a secret file
* @param envFile - The environment variable name for the secret file
*/
declare const secret: (envFile?: string) => ValidatorSpec<string>;
type ValidatorFactory<T> = (spec?: Spec<T>) => ValidatorSpec<T>;

@@ -83,2 +59,7 @@ /** Class 1 — always resolves to `defaultValue` when the var is unset. */

/**
* Parse a comma-separated env value into a trimmed, non-empty string list;
* unset/blank => undefined.
*/
declare const parseEnvList: (val?: string) => string[] | undefined;
/**
* Lenient boolean validator. Unlike envalid's built-in `bool` (which rejects

@@ -92,3 +73,3 @@ * e.g. `TRUE`/`yes`), this accepts `true`/`1`/`yes` case-insensitively. Safe to

/**
* Validate environment variables with secret file support
* Validate environment variables
*

@@ -116,3 +97,3 @@ * @param inputEnv - The environment object (usually process.env)

declare const env: <S extends Specs, V extends Specs>(inputEnv: Record<string, string | undefined>, secrets?: S, vars?: V) => CleanedEnv<S & V>;
export { bool, boolish, cleanEnv, devDefault, email, env, EnvError, EnvMissingError, getSecret, getSecretsPath, host, json, makeValidator, num, parseEnvBoolean, parseEnvNumber, port, required, secret, secretEnv, secretPath, str, testOnly, url, withDefault };
export { bool, boolish, cleanEnv, devDefault, email, env, EnvError, EnvMissingError, host, json, makeValidator, num, parseEnvBoolean, parseEnvList, parseEnvNumber, port, required, str, testOnly, url, withDefault };
export type { CleanedEnv, Spec, ValidatorSpec };
import { bool, cleanEnv as envalidCleanEnv, email, EnvError, EnvMissingError, host, json, makeValidator, num, port, str, testOnly, url } from 'envalid';
import { readFileSync } from 'fs';
import { join, resolve } from 'path';
export const getNodeEnv = (environment = process.env) => {

@@ -48,55 +46,2 @@ const raw = environment.NODE_ENV?.toLowerCase();

};
/**
* Get the secrets path lazily to allow ENV_SECRETS_PATH changes at runtime
*/
const getSecretsPath = () => process.env.ENV_SECRETS_PATH ?? '/run/secrets/';
/**
* Resolve the full path to a secret file
*/
const secretPath = (name) => name.startsWith('/') ? name : resolve(join(getSecretsPath(), name));
/**
* Read a secret from a file
* @param secret - The secret file name or path
* @returns The secret value or undefined if not found
*/
const getSecret = (secret) => {
if (!secret)
return undefined;
try {
const value = readFileSync(secretPath(secret), 'utf-8');
return value.trim();
}
catch {
return undefined;
}
};
/**
* Read secrets from files based on the secret props configuration
* Supports both direct secret files and _FILE suffix pattern
*/
const secretEnv = (inputEnv, secretProps) => {
return Object.keys(secretProps).reduce((m, k) => {
// Try to read secret directly from file
const secret = getSecret(k);
if (secret) {
m[k] = secret;
}
else {
// Try _FILE suffix pattern (e.g., DATABASE_PASSWORD_FILE)
const secretFileKey = `${k}_FILE`;
if (Object.prototype.hasOwnProperty.call(inputEnv, secretFileKey)) {
const secretFileValue = getSecret(inputEnv[secretFileKey]);
if (secretFileValue) {
m[k] = secretFileValue;
}
}
}
return m;
}, {});
};
/**
* Create a validator for a secret file
* @param envFile - The environment variable name for the secret file
*/
const secret = (envFile) => str({ default: getSecret(envFile) });
/** Class 1 — always resolves to `defaultValue` when the var is unset. */

@@ -127,2 +72,14 @@ const withDefault = (validator, defaultValue, spec = {}) => validator({ ...spec, default: defaultValue });

/**
* Parse a comma-separated env value into a trimmed, non-empty string list;
* unset/blank => undefined.
*/
const parseEnvList = (val) => {
if (!val)
return undefined;
return val
.split(',')
.map((s) => s.trim())
.filter(Boolean);
};
/**
* Lenient boolean validator. Unlike envalid's built-in `bool` (which rejects

@@ -140,3 +97,3 @@ * e.g. `TRUE`/`yes`), this accepts `true`/`1`/`yes` case-insensitively. Safe to

/**
* Validate environment variables with secret file support
* Validate environment variables
*

@@ -166,8 +123,3 @@ * @param inputEnv - The environment object (usually process.env)

const varEnv = cleanEnv(inputEnv, vars);
// Read secrets from files
const _secrets = secretEnv(inputEnv, secrets);
// Second pass: validate secrets with file values merged in
// Include inputEnv first so env vars (e.g., Kubernetes secretKeyRef) are available,
// then varEnv overrides, then file-based secrets have highest priority
const mergedEnv = { ...inputEnv, ...varEnv, ..._secrets };
const mergedEnv = { ...inputEnv, ...varEnv };
return cleanEnv(mergedEnv, { ...secrets, ...vars });

@@ -177,6 +129,6 @@ };

// Re-export from envalid
cleanEnv, devDefault, email, env, EnvError, EnvMissingError, getSecret, getSecretsPath, host, json, makeValidator, num,
cleanEnv, devDefault, email, env, EnvError, EnvMissingError, host, json, makeValidator, num,
// Lenient coercion
parseEnvBoolean, parseEnvNumber, port, required, secret, secretEnv, secretPath, str, testOnly, url,
parseEnvBoolean, parseEnvList, parseEnvNumber, port, required, str, testOnly, url,
// Fallback-class wrappers
withDefault };

@@ -42,26 +42,2 @@ import type { CleanedEnv, CleanOptions, Spec, ValidatorSpec } from 'envalid';

declare const cleanEnv: <S extends Record<string, ValidatorSpec<unknown>>>(environment: Record<string, string | undefined>, specs: S, options?: CleanOptions<S>) => CleanedEnv<S>;
/**
* Get the secrets path lazily to allow ENV_SECRETS_PATH changes at runtime
*/
declare const getSecretsPath: () => string;
/**
* Resolve the full path to a secret file
*/
declare const secretPath: (name: string) => string;
/**
* Read a secret from a file
* @param secret - The secret file name or path
* @returns The secret value or undefined if not found
*/
declare const getSecret: (secret: string | undefined) => string | undefined;
/**
* Read secrets from files based on the secret props configuration
* Supports both direct secret files and _FILE suffix pattern
*/
declare const secretEnv: <T extends Record<string, ValidatorSpec<unknown>>>(inputEnv: Record<string, string | undefined>, secretProps: T) => Record<string, string>;
/**
* Create a validator for a secret file
* @param envFile - The environment variable name for the secret file
*/
declare const secret: (envFile?: string) => ValidatorSpec<string>;
type ValidatorFactory<T> = (spec?: Spec<T>) => ValidatorSpec<T>;

@@ -83,2 +59,7 @@ /** Class 1 — always resolves to `defaultValue` when the var is unset. */

/**
* Parse a comma-separated env value into a trimmed, non-empty string list;
* unset/blank => undefined.
*/
declare const parseEnvList: (val?: string) => string[] | undefined;
/**
* Lenient boolean validator. Unlike envalid's built-in `bool` (which rejects

@@ -92,3 +73,3 @@ * e.g. `TRUE`/`yes`), this accepts `true`/`1`/`yes` case-insensitively. Safe to

/**
* Validate environment variables with secret file support
* Validate environment variables
*

@@ -116,3 +97,3 @@ * @param inputEnv - The environment object (usually process.env)

declare const env: <S extends Specs, V extends Specs>(inputEnv: Record<string, string | undefined>, secrets?: S, vars?: V) => CleanedEnv<S & V>;
export { bool, boolish, cleanEnv, devDefault, email, env, EnvError, EnvMissingError, getSecret, getSecretsPath, host, json, makeValidator, num, parseEnvBoolean, parseEnvNumber, port, required, secret, secretEnv, secretPath, str, testOnly, url, withDefault };
export { bool, boolish, cleanEnv, devDefault, email, env, EnvError, EnvMissingError, host, json, makeValidator, num, parseEnvBoolean, parseEnvList, parseEnvNumber, port, required, str, testOnly, url, withDefault };
export type { CleanedEnv, Spec, ValidatorSpec };
+16
-68
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.withDefault = exports.url = exports.testOnly = exports.str = exports.secretPath = exports.secretEnv = exports.secret = exports.required = exports.port = exports.parseEnvNumber = exports.parseEnvBoolean = exports.num = exports.makeValidator = exports.json = exports.host = exports.getSecretsPath = exports.getSecret = exports.EnvMissingError = exports.EnvError = exports.env = exports.email = exports.devDefault = exports.cleanEnv = exports.boolish = exports.bool = exports.getStrictEnvMode = exports.isDevelopment = exports.isTest = exports.isProduction = exports.getNodeEnv = void 0;
exports.withDefault = exports.url = exports.testOnly = exports.str = exports.required = exports.port = exports.parseEnvNumber = exports.parseEnvList = exports.parseEnvBoolean = exports.num = exports.makeValidator = exports.json = exports.host = exports.EnvMissingError = exports.EnvError = exports.env = exports.email = exports.devDefault = exports.cleanEnv = exports.boolish = exports.bool = exports.getStrictEnvMode = exports.isDevelopment = exports.isTest = exports.isProduction = exports.getNodeEnv = void 0;
const envalid_1 = require("envalid");

@@ -17,4 +17,2 @@ Object.defineProperty(exports, "bool", { enumerable: true, get: function () { return envalid_1.bool; } });

Object.defineProperty(exports, "url", { enumerable: true, get: function () { return envalid_1.url; } });
const fs_1 = require("fs");
const path_1 = require("path");
const getNodeEnv = (environment = process.env) => {

@@ -70,60 +68,2 @@ const raw = environment.NODE_ENV?.toLowerCase();

exports.cleanEnv = cleanEnv;
/**
* Get the secrets path lazily to allow ENV_SECRETS_PATH changes at runtime
*/
const getSecretsPath = () => process.env.ENV_SECRETS_PATH ?? '/run/secrets/';
exports.getSecretsPath = getSecretsPath;
/**
* Resolve the full path to a secret file
*/
const secretPath = (name) => name.startsWith('/') ? name : (0, path_1.resolve)((0, path_1.join)(getSecretsPath(), name));
exports.secretPath = secretPath;
/**
* Read a secret from a file
* @param secret - The secret file name or path
* @returns The secret value or undefined if not found
*/
const getSecret = (secret) => {
if (!secret)
return undefined;
try {
const value = (0, fs_1.readFileSync)(secretPath(secret), 'utf-8');
return value.trim();
}
catch {
return undefined;
}
};
exports.getSecret = getSecret;
/**
* Read secrets from files based on the secret props configuration
* Supports both direct secret files and _FILE suffix pattern
*/
const secretEnv = (inputEnv, secretProps) => {
return Object.keys(secretProps).reduce((m, k) => {
// Try to read secret directly from file
const secret = getSecret(k);
if (secret) {
m[k] = secret;
}
else {
// Try _FILE suffix pattern (e.g., DATABASE_PASSWORD_FILE)
const secretFileKey = `${k}_FILE`;
if (Object.prototype.hasOwnProperty.call(inputEnv, secretFileKey)) {
const secretFileValue = getSecret(inputEnv[secretFileKey]);
if (secretFileValue) {
m[k] = secretFileValue;
}
}
}
return m;
}, {});
};
exports.secretEnv = secretEnv;
/**
* Create a validator for a secret file
* @param envFile - The environment variable name for the secret file
*/
const secret = (envFile) => (0, envalid_1.str)({ default: getSecret(envFile) });
exports.secret = secret;
/** Class 1 — always resolves to `defaultValue` when the var is unset. */

@@ -159,2 +99,15 @@ const withDefault = (validator, defaultValue, spec = {}) => validator({ ...spec, default: defaultValue });

/**
* Parse a comma-separated env value into a trimmed, non-empty string list;
* unset/blank => undefined.
*/
const parseEnvList = (val) => {
if (!val)
return undefined;
return val
.split(',')
.map((s) => s.trim())
.filter(Boolean);
};
exports.parseEnvList = parseEnvList;
/**
* Lenient boolean validator. Unlike envalid's built-in `bool` (which rejects

@@ -173,3 +126,3 @@ * e.g. `TRUE`/`yes`), this accepts `true`/`1`/`yes` case-insensitively. Safe to

/**
* Validate environment variables with secret file support
* Validate environment variables
*

@@ -199,10 +152,5 @@ * @param inputEnv - The environment object (usually process.env)

const varEnv = cleanEnv(inputEnv, vars);
// Read secrets from files
const _secrets = secretEnv(inputEnv, secrets);
// Second pass: validate secrets with file values merged in
// Include inputEnv first so env vars (e.g., Kubernetes secretKeyRef) are available,
// then varEnv overrides, then file-based secrets have highest priority
const mergedEnv = { ...inputEnv, ...varEnv, ..._secrets };
const mergedEnv = { ...inputEnv, ...varEnv };
return cleanEnv(mergedEnv, { ...secrets, ...vars });
};
exports.env = env;
{
"name": "12factor-env",
"version": "1.17.0",
"version": "1.17.1",
"author": "Constructive <developers@constructive.io>",
"description": "Environment variable validation with secret file support for 12-factor apps",
"description": "Environment variable validation for 12-factor apps",
"main": "index.js",

@@ -44,8 +44,5 @@ "module": "esm/index.js",

"12factor",
"secrets",
"kubernetes",
"docker",
"constructive"
],
"gitHead": "ff6c442b789cbe8803f33036110f6e09124e65df"
"gitHead": "e53a570a4a70987e82dc074bd73501971fd82828"
}

@@ -19,5 +19,5 @@ # 12factor-env

> Environment variable validation with secret file support for 12-factor apps
> Environment variable validation for 12-factor apps
A TypeScript library for validating environment variables with built-in support for Docker/Kubernetes secret files. Built on top of [envalid](https://github.com/af/envalid) with additional features for reading secrets from files.
A TypeScript library for validating environment variables. Built on top of [envalid](https://github.com/af/envalid).

@@ -119,45 +119,2 @@ ## Installation

## Secret File Support
This library supports reading secrets from files, which is useful for Docker secrets and Kubernetes secrets that are mounted as files.
### Direct Secret Files
Secrets can be read from `/run/secrets/` (or a custom path via `ENV_SECRETS_PATH`):
```ts
import { env, str } from '12factor-env';
// If /run/secrets/DATABASE_PASSWORD exists, it will be read automatically
const config = env(process.env, {
DATABASE_PASSWORD: str()
});
```
### _FILE Suffix Pattern
You can also use the `_FILE` suffix pattern commonly used with Docker:
```bash
# Set the path to the secret file
export DATABASE_PASSWORD_FILE=/run/secrets/db-password
```
```ts
import { env, str } from '12factor-env';
// Will read from the file specified in DATABASE_PASSWORD_FILE
const config = env(process.env, {
DATABASE_PASSWORD: str()
});
```
### Custom Secrets Path
Set `ENV_SECRETS_PATH` to change the default secrets directory:
```bash
export ENV_SECRETS_PATH=/custom/secrets/path
```
## Validators

@@ -201,35 +158,2 @@

### `secret(envFile)`
Create a validator for a secret file:
```ts
import { env, secret } from '12factor-env';
const config = env(process.env, {
DB_PASSWORD: secret('DATABASE_PASSWORD')
});
```
### `getSecret(name)`
Read a secret from a file:
```ts
import { getSecret } from '12factor-env';
const password = getSecret('DATABASE_PASSWORD');
```
### `secretPath(name)`
Resolve the full path to a secret file:
```ts
import { secretPath } from '12factor-env';
const path = secretPath('DATABASE_PASSWORD');
// Returns: /run/secrets/DATABASE_PASSWORD
```
## Re-exports from envalid

@@ -236,0 +160,0 @@