Skip to main content
Glama
env-interpolation.ts3.38 kB
/** * Environment Variable Interpolation * Supports ${VAR_NAME} and ${VAR_NAME:-default} syntax * * Matches Wonder Logger's environment variable interpolation pattern */ import { getChildLogger } from './logger.js'; const logger = getChildLogger('config:env-interpolation'); /** * Interpolate environment variables in a string value * * Supports two syntaxes: * - ${VAR_NAME} - Required variable (throws if not set) * - ${VAR_NAME:-default} - Optional variable with default value * * @param value - String value to interpolate * @param throwOnMissing - If true, throw error for required vars that are undefined (default: true) * @returns Interpolated string * * @example * ```typescript * // Required variable * interpolateString("${HOME}/projects") // "/Users/jenova/projects" * interpolateString("${MISSING}") // throws Error * * // Optional variable with default * interpolateString("${PORT:-3000}") // "3000" (if PORT not set) * interpolateString("${NODE_ENV:-development}") // "development" (if NODE_ENV not set) * ``` */ export function interpolateString(value: string, throwOnMissing = true): string { // Pattern matches ${VAR_NAME} or ${VAR_NAME:-default} const pattern = /\$\{([^}:]+)(?::-([ -~]*))?\}/g; return value.replace(pattern, (match, varName, defaultValue) => { const envValue = process.env[varName]; // If env var is set, use it if (envValue !== undefined) { return envValue; } // If default is provided, use it if (defaultValue !== undefined) { logger.debug({ varName, defaultValue }, 'Using default value for env var'); return defaultValue; } // Required variable is missing if (throwOnMissing) { throw new Error( `Environment variable "${varName}" is required but not set. ` + `Set it or provide a default value using \${${varName}:-default} syntax.` ); } // Don't throw, return original match logger.warn({ varName }, 'Environment variable not set, keeping original value'); return match; }); } /** * Recursively interpolate environment variables in an object * * Walks the object tree and interpolates any string values containing ${VAR} syntax. * Arrays and nested objects are handled recursively. * * @param obj - Object to interpolate * @param throwOnMissing - If true, throw error for required vars that are undefined (default: true) * @returns New object with interpolated values (does not mutate input) * * @example * ```typescript * const config = { * port: "${PORT:-3000}", * database: { * host: "${DB_HOST}", * name: "${DB_NAME:-iris}" * } * }; * * const interpolated = interpolateObject(config); * // { port: "3000", database: { host: "localhost", name: "iris" } } * ``` */ export function interpolateObject<T>(obj: T, throwOnMissing = true): T { if (typeof obj === 'string') { return interpolateString(obj, throwOnMissing) as T; } if (Array.isArray(obj)) { return obj.map(item => interpolateObject(item, throwOnMissing)) as T; } if (obj !== null && typeof obj === 'object') { const result: any = {}; for (const [key, value] of Object.entries(obj)) { result[key] = interpolateObject(value, throwOnMissing); } return result as T; } // Primitives (number, boolean, null) pass through return obj; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jenova-marie/iris-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server