import { readFile } from "fs/promises"; import { resolve } from "path"; /** * Configuration for environment variable loading */ export interface EnvLoaderConfig { /** Custom paths to search for .env files (relative to current working directory) */ searchPaths?: string[]; /** Whether to log when environment variables are loaded */ verbose?: boolean; /** Whether to override existing environment variables */ override?: boolean; } /** * Default search paths for .env files */ const DEFAULT_ENV_PATHS = [ "./.env", "../.env", "../../.env", "../plugin/.env", "../../../.env", ]; /** * Load environment variables from .env files * Searches multiple common locations for .env files and loads them into process.env * * @param config Configuration options * @returns Object containing loaded environment variables */ export async function loadEnvVariables( config: EnvLoaderConfig = {}, ): Promise> { const { searchPaths = DEFAULT_ENV_PATHS, verbose = false, override = false, } = config; const loadedVars: Record = {}; for (const envPath of searchPaths) { try { const fullPath = resolve(envPath); const content = await readFile(fullPath, "utf8"); if (verbose) { console.log(`Checking .env file: ${envPath}`); } // Parse .env file content const lines = content.split("\n"); for (const line of lines) { const trimmed = line.trim(); if (trimmed && !trimmed.startsWith("#") && trimmed.includes("=")) { const [key, ...valueParts] = trimmed.split("="); const value = valueParts.join("=").trim(); // Remove quotes if present const cleanValue = value.replace(/^["']|["']$/g, ""); if (key && cleanValue && (override || !process.env[key])) { process.env[key] = cleanValue; loadedVars[key] = cleanValue; if (verbose) { console.log(`Loaded ${key} from ${envPath}`); } } } } } catch (error) { // File doesn't exist or can't be read, continue to next if (verbose) { console.log(`Could not read ${envPath}: ${error.message}`); } } } return loadedVars; } /** * Get a specific environment variable with automatic .env file loading * * @param varName Name of the environment variable * @param config Configuration options * @returns The environment variable value or null if not found */ export async function getEnvVariable( varName: string, config: EnvLoaderConfig = {}, ): Promise { // First check if it's already in the environment let value = process.env[varName]; if (!value) { // Try to load from .env files const loadedVars = await loadEnvVariables(config); value = loadedVars[varName] || process.env[varName]; } return value || null; } /** * Get a required environment variable with automatic .env file loading * Throws an error if the variable is not found * * @param varName Name of the environment variable * @param config Configuration options * @returns The environment variable value * @throws Error if the variable is not found */ export async function getRequiredEnvVariable( varName: string, config: EnvLoaderConfig = {}, ): Promise { const value = await getEnvVariable(varName, config); if (!value) { const searchPaths = config.searchPaths || DEFAULT_ENV_PATHS; throw new Error(`${varName} not found. Please set it in your environment or .env file. To fix this: 1. Add to .env file: ${varName}=your_value_here 2. Or export it: export ${varName}=your_value_here Current working directory: ${process.cwd()} Searched paths: ${searchPaths.join(", ")} Environment variables available: ${ Object.keys(process.env) .filter((k) => k.includes(varName.split("_")[0])) .join(", ") || "none matching" }`); } return value; } /** * Load multiple required environment variables at once * * @param varNames Array of environment variable names * @param config Configuration options * @returns Object with variable names as keys and values as values * @throws Error if any variable is not found */ export async function getRequiredEnvVariables( varNames: string[], config: EnvLoaderConfig = {}, ): Promise> { const result: Record = {}; // Load all .env files first await loadEnvVariables(config); // Check each required variable for (const varName of varNames) { const value = process.env[varName]; if (!value) { throw new Error( `Required environment variable ${varName} not found. Please set it in your environment or .env file.`, ); } result[varName] = value; } return result; } /** * Utility function specifically for API keys * * @param apiKeyName Name of the API key environment variable * @param config Configuration options * @returns The API key value * @throws Error if the API key is not found */ export async function getApiKey( apiKeyName: string, config: EnvLoaderConfig = {}, ): Promise { return getRequiredEnvVariable(apiKeyName, config); }