digitaltwin-core
Version:
Minimalist framework to collect and handle data in a Digital Twin project
177 lines • 6.7 kB
JavaScript
/**
* @fileoverview Environment variable validation and configuration management
*
* This utility class provides type-safe environment variable parsing with
* validation rules for string, number, boolean, and enum types.
*/
/**
* Environment variable validation and configuration utility.
*
* The Env class provides a schema-based approach to validating and parsing
* environment variables with type safety and format validation.
*
* @example
* ```typescript
* const config = Env.validate({
* PORT: Env.schema.number({ optional: true, default: 3000 }),
* API_URL: Env.schema.string({ format: 'url' }),
* DEBUG: Env.schema.boolean({ optional: true, default: false }),
* NODE_ENV: Env.schema.enum(['development', 'production', 'test'])
* });
*
* // config is now type-safe and validated
* console.log(config.PORT); // number
* console.log(config.API_URL); // validated URL string
* ```
*/
export class Env {
/**
* Schema builders for different environment variable types.
*
* Provides factory methods for creating validation rules for different
* data types that can be parsed from environment variables.
*/
static { this.schema = {
/**
* Creates a string validation rule.
*
* @param opts - Optional configuration for string validation
* @param opts.optional - Whether the environment variable is optional
* @param opts.format - Format validation ('url' or 'email')
* @returns String validation rule object
*/
string: (opts) => ({
type: 'string',
...opts
}),
/**
* Creates a number validation rule.
*
* @param opts - Optional configuration for number validation
* @param opts.optional - Whether the environment variable is optional
* @returns Number validation rule object
*/
number: (opts) => ({
type: 'number',
...opts
}),
/**
* Creates a boolean validation rule.
*
* Accepts 'true'/'false' or '1'/'0' as valid boolean values.
*
* @param opts - Optional configuration for boolean validation
* @param opts.optional - Whether the environment variable is optional
* @param opts.default - Default value if the variable is missing
* @returns Boolean validation rule object
*/
boolean: (opts) => ({
type: 'boolean',
...opts
}),
/**
* Creates an enum validation rule.
*
* @template T - Array of allowed string values
* @param values - Array of allowed values for this environment variable
* @returns Enum validation rule object
*/
enum: (values) => ({
type: 'enum',
values
})
}; }
/**
* Stores the last validated configuration.
*
* This static property holds the most recently validated environment
* configuration for reference by other parts of the application.
*/
static { this.config = {}; }
/**
* Validates environment variables against a schema definition.
*
* Parses and validates environment variables according to the provided
* schema, returning a type-safe configuration object.
*
* @template T - The expected type of the returned configuration object
* @param schema - Object mapping environment variable names to validation rules
* @param rawEnv - Environment variables object (defaults to process.env)
* @returns Validated and parsed configuration object
*
* @throws {Error} When required environment variables are missing
* @throws {Error} When environment variables fail format validation
*
* @example
* ```typescript
* interface Config {
* DATABASE_URL: string;
* PORT: number;
* DEBUG: boolean;
* NODE_ENV: 'development' | 'production';
* }
*
* const config: Config = Env.validate({
* DATABASE_URL: Env.schema.string({ format: 'url' }),
* PORT: Env.schema.number({ optional: true }),
* DEBUG: Env.schema.boolean({ optional: true, default: false }),
* NODE_ENV: Env.schema.enum(['development', 'production'])
* });
* ```
*/
static validate(schema, rawEnv = process.env) {
const config = {};
for (const [key, rules] of Object.entries(schema)) {
const value = rawEnv[key];
if (value === undefined || value === '') {
if (rules.optional) {
// Use default value if provided
if (rules.default !== undefined) {
config[key] = rules.default;
}
continue;
}
throw new Error(`Missing environment variable: ${key}`);
}
switch (rules.type) {
case 'string':
if (rules.format === 'url' && !/^https?:\/\/.+$/.test(value)) {
throw new Error(`Invalid URL format for ${key}`);
}
if (rules.format === 'email' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
throw new Error(`Invalid email format for ${key}`);
}
config[key] = value;
break;
case 'number':
const parsed = Number(value);
if (isNaN(parsed)) {
throw new Error(`Invalid number format for ${key}`);
}
config[key] = parsed;
break;
case 'boolean':
const lowerValue = value.toLowerCase();
if (lowerValue === 'true' || lowerValue === '1') {
config[key] = true;
}
else if (lowerValue === 'false' || lowerValue === '0') {
config[key] = false;
}
else {
throw new Error(`Invalid boolean format for ${key}, expected true/false or 1/0`);
}
break;
case 'enum':
if (!rules.values.includes(value)) {
throw new Error(`Invalid value for ${key}, expected one of ${rules.values.join(', ')}`);
}
config[key] = value;
break;
}
}
this.config = config;
return config;
}
}
//# sourceMappingURL=env.js.map