basant307/AI_Governance_Project
048
1/**2 * @license3 * Copyright 2025 Google LLC4 * SPDX-License-Identifier: Apache-2.05 */6 7/**8 * Resolves environment variables in a string.9 * Replaces $VAR_NAME and ${VAR_NAME} with their corresponding environment variable values.10 * If the environment variable is not defined, the original placeholder is preserved.11 *12 * @param value - The string that may contain environment variable placeholders13 * @returns The string with environment variables resolved14 *15 * @example16 * resolveEnvVarsInString("Token: $API_KEY") // Returns "Token: secret-123"17 * resolveEnvVarsInString("URL: ${BASE_URL}/api") // Returns "URL: https://api.example.com/api"18 * resolveEnvVarsInString("Missing: $UNDEFINED_VAR") // Returns "Missing: $UNDEFINED_VAR"19 */20export function resolveEnvVarsInString(21 value: string,22 customEnv?: Record<string, string>,23): string {24 const envVarRegex = /\$(?:(\w+)|{([^}]+)})/g; // Find $VAR_NAME or ${VAR_NAME}25 return value.replace(envVarRegex, (match, varName1, varName2) => {26 const varName = varName1 || varName2;27 if (customEnv && typeof customEnv[varName] === 'string') {28 return customEnv[varName];29 }30 if (process && process.env && typeof process.env[varName] === 'string') {31 return process.env[varName]!;32 }33 return match;34 });35}36 37/**38 * Recursively resolves environment variables in an object of any type.39 * Handles strings, arrays, nested objects, and preserves other primitive types.40 * Protected against circular references using a WeakSet to track visited objects.41 *42 * @param obj - The object to process for environment variable resolution43 * @returns A new object with environment variables resolved44 *45 * @example46 * const config = {47 * server: {48 * host: "$HOST",49 * port: "${PORT}",50 * enabled: true,51 * tags: ["$ENV", "api"]52 * }53 * };54 * const resolved = resolveEnvVarsInObject(config);55 */56export function resolveEnvVarsInObject<T>(57 obj: T,58 customEnv?: Record<string, string>,59): T {60 return resolveEnvVarsInObjectInternal(obj, new WeakSet(), customEnv);61}62 63/**64 * Internal implementation of resolveEnvVarsInObject with circular reference protection.65 *66 * @param obj - The object to process67 * @param visited - WeakSet to track visited objects and prevent circular references68 * @returns A new object with environment variables resolved69 */70function resolveEnvVarsInObjectInternal<T>(71 obj: T,72 visited: WeakSet<object>,73 customEnv?: Record<string, string>,74): T {75 if (76 obj === null ||77 obj === undefined ||78 typeof obj === 'boolean' ||79 typeof obj === 'number'80 ) {81 return obj;82 }83 84 if (typeof obj === 'string') {85 return resolveEnvVarsInString(obj, customEnv) as unknown as T;86 }87 88 if (Array.isArray(obj)) {89 // Check for circular reference90 if (visited.has(obj)) {91 // Return a shallow copy to break the cycle92 return [...obj] as unknown as T;93 }94 95 visited.add(obj);96 const result = obj.map((item) =>97 resolveEnvVarsInObjectInternal(item, visited, customEnv),98 ) as unknown as T;99 visited.delete(obj);100 return result;101 }102 103 if (typeof obj === 'object') {104 // Check for circular reference105 if (visited.has(obj as object)) {106 // Return a shallow copy to break the cycle107 return { ...obj } as T;108 }109 110 visited.add(obj as object);111 const newObj = { ...obj } as T;112 for (const key in newObj) {113 if (Object.prototype.hasOwnProperty.call(newObj, key)) {114 newObj[key] = resolveEnvVarsInObjectInternal(115 newObj[key],116 visited,117 customEnv,118 );119 }120 }121 visited.delete(obj as object);122 return newObj;123 }124 125 return obj;126}127 