UNPKG

schema-env

Version:

Type-safe environment variable validation for Node.js using Zod schemas or custom adapters. Load .env files, expand variables, fetch async secrets, and validate process.env at startup.

1 lines 35 kB
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// File: src/index.ts\n\n// TSDoc comments updated for adapters\n\nimport fs from \"node:fs\"; // <--- Added Node.js fs import\nimport dotenv from \"dotenv\";\nimport { expand } from \"dotenv-expand\";\n// Use z.AnyZodObject for constraints where appropriate\nimport { z, ZodError, ZodObject } from \"zod\";\n\n// --- Type Definitions ---\n\n// Removed DotenvConfigFunction type as it's no longer used\ntype DotenvExpandFunction = (\n config: dotenv.DotenvConfigOutput\n) => dotenv.DotenvConfigOutput;\n\n/** Input for schema validation, potentially holding values from all sources. */\ntype EnvironmentInput = Record<string, unknown>;\n\n/** Function signature for fetching secrets asynchronously. */\nexport type SecretSourceFunction = () => Promise<\n Record<string, string | undefined>\n>;\n\n// --- Validation Adapter Types (as per ADR-009) ---\n\n/** Standardized error format for validation failures. */\nexport interface StandardizedValidationError {\n /** Path to the invalid field. */\n path: (string | number)[];\n /** Description of the validation failure. */\n message: string;\n}\n\n/** Standardized result structure for validation adapters. */\nexport type ValidationResult<TResult> =\n | { success: true; data: TResult }\n | {\n success: false;\n error: { issues: StandardizedValidationError[] };\n };\n\n/**\n * Interface for validation library adapters.\n * Allows plugging in different validation libraries (Zod, Joi, Yup, etc.).\n * @template TResult The expected shape of the validated environment object.\n */\nexport interface ValidatorAdapter<TResult> {\n /**\n * Validates the merged environment data.\n * @param data The raw, merged environment data object.\n * @returns A `ValidationResult` indicating success or failure.\n */\n validate(data: EnvironmentInput): ValidationResult<TResult>;\n}\n\n// --- Options Interfaces (Corrected ZodObject constraint) ---\n\n/**\n * Base options common to both `createEnv` and `createEnvAsync`.\n * You must provide EITHER `schema` (for default Zod validation) OR `validator`.\n *\n * @template TSchema The Zod object schema type if using the default Zod validator.\n * @template TResult The expected type of the validated environment object.\n */\ninterface CreateEnvBaseOptions<\n TSchema extends z.ZodSchema | undefined,\n TResult,\n> {\n /**\n * The Zod schema defining expected environment variables.\n * Required if a custom `validator` is NOT provided.\n * If provided, it MUST be a `z.object({...})`.\n * Mutually exclusive with `validator`.\n */\n schema?: TSchema;\n\n /**\n * A custom validation adapter instance conforming to the `ValidatorAdapter` interface.\n * Required if `schema` is NOT provided.\n * If provided, the return type `TResult` must typically be specified via a\n * generic type argument on `createEnv`/`createEnvAsync` (e.g., `createEnv<undefined, MyType>({...})`).\n * Mutually exclusive with `schema`.\n */\n validator?: ValidatorAdapter<TResult>;\n\n /**\n * Optional: Path or array of paths to .env files to load.\n * - Defaults to './.env' relative to `process.cwd()`.\n * - If an array is provided (e.g., `['.env.base', '.env.local']`), files are loaded sequentially,\n * with variables in later files overriding earlier ones. Non-string entries are ignored with a warning.\n * - Set to `false` to disable loading all .env files (including the environment-specific one).\n *\n * Note on Environment-Specific File: Regardless of whether a single path or an array\n * is provided (unless `dotEnvPath` is `false`), if `process.env.NODE_ENV` is set,\n * an attempt will be made to load an environment-specific file (e.g., `.env.development`)\n * *after* all files specified in `dotEnvPath` have been loaded. This environment-specific\n * file will override variables from the files specified in `dotEnvPath`.\n */\n dotEnvPath?: string | false | string[];\n\n /**\n * Optional: Enable variable expansion using `dotenv-expand`. Defaults to `false`.\n * Expansion is performed on the combined values from all loaded `.env` files\n * (including the environment-specific file if loaded) *before* merging with\n * `process.env` (for `createEnv`) or secrets (for `createEnvAsync`) and validation.\n * Variables in `process.env` or secrets are NOT expanded.\n */\n expandVariables?: boolean;\n\n // Removed _internalDotenvConfig\n /** @internal */\n _internalDotenvExpand?: DotenvExpandFunction;\n}\n\n// --- Specialized Options Interfaces (Corrected ZodObject constraint) ---\n\n/**\n * Options for the synchronous `createEnv` function.\n * Requires either `schema` (a Zod object schema) OR `validator`.\n *\n * @template TSchema The Zod object schema type if using the default Zod validator.\n * @template TResult The expected type of the validated environment object. Inferred if TSchema is provided.\n */\n// Use AnyZodObject for inference constraint\nexport type CreateEnvOptions<\n TSchema extends z.AnyZodObject | undefined,\n TResult = TSchema extends z.AnyZodObject ? z.infer<TSchema> : unknown,\n> = CreateEnvBaseOptions<TSchema, TResult>;\n\n/**\n * Options for the asynchronous `createEnvAsync` function.\n * Requires either `schema` (a Zod object schema) OR `validator`.\n *\n * @template TSchema The Zod object schema type if using the default Zod validator.\n * @template TResult The expected type of the validated environment object. Inferred if TSchema is provided.\n */\n// Use AnyZodObject for inference constraint\nexport interface CreateEnvAsyncOptions<\n TSchema extends z.AnyZodObject | undefined,\n TResult = TSchema extends z.AnyZodObject ? z.infer<TSchema> : unknown,\n> extends CreateEnvBaseOptions<TSchema, TResult> {\n /**\n * Optional: An array of functions that fetch secrets asynchronously.\n * Each function should return a Promise resolving to a `Record<string, string | undefined>`.\n * Secrets fetched here will override variables from `.env` files but be overridden by `process.env`.\n * Fetching errors are logged as warnings, but do not halt execution unless all sources fail.\n *\n * @example\n * ```js\n * const getSecretsFromAWS = async () => {\n * // Your logic to fetch from AWS Secrets Manager\n * return { DB_PASSWORD: 'fetchedPassword' };\n * }\n * const getSecretsFromVault = async () => {\n * // Your logic to fetch from HashiCorp Vault\n * return { API_KEY: 'fetchedApiKey' };\n * }\n *\n * createEnvAsync({\n * schema, // Or validator\n * secretsSources: [getSecretsFromAWS, getSecretsFromVault]\n * })\n * ```\n */\n secretsSources?: SecretSourceFunction[];\n}\n\n// --- Default Zod Adapter Implementation (Corrected ZodObject constraint) ---\n\n/**\n * Default implementation of ValidatorAdapter using Zod.\n * @internal\n */\n// Use AnyZodObject for the constraint here\nclass ZodValidatorAdapter<T extends z.AnyZodObject>\n implements ValidatorAdapter<z.infer<T>>\n{\n constructor(private schema: T) {}\n\n validate(data: EnvironmentInput): ValidationResult<z.infer<T>> {\n const result = this.schema.safeParse(data);\n if (result.success) {\n return { success: true, data: result.data };\n } else {\n return {\n success: false,\n error: {\n // Map Zod errors to standardized format\n issues: result.error.errors.map((zodError) => ({\n path: zodError.path,\n message: zodError.message,\n })),\n },\n };\n }\n }\n}\n\n// --- Internal Helper Functions (Updated) ---\n\n/**\n * Loads and merges environment variables from specified `.env` file paths using fs.\n * Handles default path, single path, array paths, and environment-specific files.\n * Gracefully ignores ENOENT errors but throws on other file access errors.\n * PREVENTS mutation of process.env during loading.\n * @internal\n */\nfunction _loadDotEnvFiles(\n dotEnvPath: string | false | string[] | undefined,\n nodeEnv: string | undefined\n): dotenv.DotenvParseOutput {\n if (dotEnvPath === false) {\n return {}; // Loading disabled\n }\n\n let mergedDotEnvParsed: dotenv.DotenvParseOutput = {};\n\n // Use Node.js 'fs' module to read files directly\n const loadEnvFile = (filePath: string): dotenv.DotenvParseOutput => {\n try {\n // Read the file content\n const fileContent = fs.readFileSync(filePath, { encoding: \"utf8\" });\n // Parse the content using dotenv's parser\n const parsed = dotenv.parse(fileContent);\n // console.log(`[schema-env] Successfully parsed env file: ${filePath}`);\n return parsed;\n } catch (e) {\n const err = e as NodeJS.ErrnoException;\n if (err.code !== \"ENOENT\") {\n // Throw actual file reading errors (permissions, etc.)\n throw new Error(\n `❌ Failed to load environment file from ${filePath}: ${err.message}`\n );\n }\n // console.warn(`[schema-env] Optional env file not found, ignoring: ${filePath}`);\n return {}; // File not found (ENOENT) is ignored\n }\n };\n\n let pathsToLoad: string[] = [];\n\n if (dotEnvPath === undefined) {\n pathsToLoad = [\"./.env\"]; // Default path\n } else if (typeof dotEnvPath === \"string\") {\n pathsToLoad = [dotEnvPath]; // Single path\n } else if (Array.isArray(dotEnvPath)) {\n // Filter out non-string paths from the array\n pathsToLoad = dotEnvPath.filter((path): path is string => {\n if (typeof path !== \"string\") {\n console.warn(\n `⚠️ [schema-env] Warning: Invalid path ignored in dotEnvPath array: ${String(\n path\n )}`\n );\n return false;\n }\n return true;\n });\n }\n\n // Load base files sequentially. Errors (non-ENOENT) will throw and halt here.\n for (const path of pathsToLoad) {\n const parsed = loadEnvFile(path);\n mergedDotEnvParsed = { ...mergedDotEnvParsed, ...parsed }; // Ensure later files override\n }\n\n // Load environment-specific file *after* base files. Errors will throw and halt here.\n // Use the NODE_ENV value passed in (which should reflect process.env)\n if (nodeEnv) {\n const envSpecificPath = `./.env.${nodeEnv}`;\n const envSpecificParsed = loadEnvFile(envSpecificPath);\n mergedDotEnvParsed = { ...mergedDotEnvParsed, ...envSpecificParsed }; // Env-specific overrides base\n }\n\n return mergedDotEnvParsed;\n}\n\n/**\n * Performs variable expansion on the provided dotenv parsed values if enabled.\n * @internal\n */\nfunction _expandDotEnvValues(\n mergedDotEnvParsed: dotenv.DotenvParseOutput,\n expandVariables: boolean | undefined,\n expandDotenv: DotenvExpandFunction\n): dotenv.DotenvParseOutput {\n if (\n !expandVariables ||\n !mergedDotEnvParsed ||\n Object.keys(mergedDotEnvParsed).length === 0\n ) {\n return mergedDotEnvParsed || {};\n }\n\n // dotenv-expand expects a specific input structure and mutates it\n const configToExpand: dotenv.DotenvConfigOutput = {\n parsed: { ...mergedDotEnvParsed },\n };\n\n try {\n const expansionResult = expandDotenv(configToExpand);\n // Use the expanded results if available, fallback to original if expansion failed somehow\n return expansionResult?.parsed || mergedDotEnvParsed || {};\n } catch (e) {\n // Catch potential errors during expansion itself\n console.error(\n `❌ Error during variable expansion: ${\n e instanceof Error ? e.message : String(e)\n }`\n );\n // Return the unexpanded values as a fallback\n return mergedDotEnvParsed || {};\n }\n}\n\n/**\n * Merges values from process.env into a source object.\n * process.env values take precedence over existing values in the source object.\n * @param sourceInput - The object containing values from previous steps (e.g., .env, secrets).\n * @internal\n */\nfunction _mergeProcessEnv(sourceInput: EnvironmentInput): EnvironmentInput {\n // --- Add Debug Log ---\n // console.log(\"DEBUG: [_mergeProcessEnv] sourceInput:\", sourceInput);\n // console.log(\"DEBUG: [_mergeProcessEnv] process.env.TARGET:\", process.env.TARGET);\n // --- End Debug Log ---\n\n // Create a copy to avoid mutating the input object directly\n const sourceWithProcessEnv: EnvironmentInput = { ...sourceInput };\n\n // process.env overrides existing values\n for (const key in process.env) {\n if (Object.prototype.hasOwnProperty.call(process.env, key)) {\n // Ensure value is not undefined before assigning\n const value = process.env[key];\n if (value !== undefined) {\n sourceWithProcessEnv[key] = value;\n }\n }\n }\n // --- Add Debug Log ---\n // console.log(\"DEBUG: [_mergeProcessEnv] result:\", sourceWithProcessEnv);\n // --- End Debug Log ---\n return sourceWithProcessEnv;\n}\n\n/**\n * Formats validation errors from the standardized result into a user-friendly string.\n * Renamed from _formatZodError.\n * @internal\n */\nfunction _formatValidationError(\n error: { issues: StandardizedValidationError[] } | ZodError\n): string {\n let issues: StandardizedValidationError[];\n\n // Check if it's a ZodError or the standardized structure\n if (error instanceof ZodError) {\n // Map Zod errors if necessary (e.g., if called directly with ZodError, though unlikely now)\n issues = error.errors.map((err) => ({\n path: err.path,\n message: err.message,\n }));\n } else if (error && Array.isArray(error.issues)) {\n issues = error.issues;\n } else {\n // Fallback for unexpected error format\n return \"❌ Unknown validation error occurred.\";\n }\n\n const formattedErrors = issues.map(\n (err) => ` - ${err.path.join(\".\") || \"UNKNOWN_PATH\"}: ${err.message}`\n );\n return `❌ Invalid environment variables:\\n${formattedErrors.join(\"\\n\")}`;\n}\n\n/**\n * Validates the prepared environment input using the chosen validation adapter.\n * Replaces the old _validateSchema.\n * @internal\n */\nfunction _validateEnvironment<TResult>(\n adapter: ValidatorAdapter<TResult>,\n sourceForValidation: EnvironmentInput\n): ValidationResult<TResult> {\n // Adapter handles the validation logic internally\n return adapter.validate(sourceForValidation);\n}\n\n/**\n * Fetches secrets from multiple sources concurrently.\n * Logs warnings for failures but doesn't halt unless all fail.\n * @internal\n */\nasync function _fetchSecrets(\n secretsSources: SecretSourceFunction[] | undefined\n): Promise<Record<string, string | undefined>> {\n if (!secretsSources || secretsSources.length === 0) {\n return {};\n }\n\n // Use Promise.allSettled to run all sources even if some fail\n const results = await Promise.allSettled(\n secretsSources.map((sourceFn, index) => {\n try {\n // Ensure the function returns a promise\n const maybePromise = sourceFn();\n if (\n maybePromise &&\n typeof (maybePromise as Promise<unknown>).then === \"function\"\n ) {\n return maybePromise;\n } else {\n // If it's not a promise, reject explicitly\n return Promise.reject(\n new Error(\n `Sync return value from secrets source function at index ${index}. Function must return a Promise.`\n )\n );\n }\n } catch (syncError) {\n // Catch synchronous errors in the source function itself\n return Promise.reject(\n new Error(\n `Sync error in secrets source function at index ${index}: ${\n syncError instanceof Error ? syncError.message : String(syncError)\n }`\n )\n );\n }\n })\n );\n\n let mergedSecrets: Record<string, string | undefined> = {};\n let successfulFetches = 0;\n\n results.forEach((result, index) => {\n if (result.status === \"fulfilled\") {\n successfulFetches++;\n // Merge fulfilled results, later sources override earlier ones\n // Ensure result.value is an object before spreading\n if (result.value && typeof result.value === \"object\") {\n mergedSecrets = { ...mergedSecrets, ...result.value };\n } else if (result.value !== undefined && result.value !== null) {\n // Log a warning if the resolved value isn't an object as expected\n console.warn(\n `⚠️ [schema-env] Warning: Secrets source function at index ${index} resolved with non-object value: ${typeof result.value}. Expected Record<string, string | undefined>.`\n );\n }\n // Ignore null/undefined results silently\n } else {\n // Log warning on rejection (async error or caught sync error)\n console.warn(\n `⚠️ [schema-env] Warning: Secrets source function at index ${index} failed: ${\n result.reason instanceof Error\n ? result.reason.message\n : String(result.reason)\n }`\n );\n }\n });\n\n // Check if all sources failed *after* iterating through results\n if (successfulFetches === 0 && secretsSources.length > 0) {\n // ADR says log warnings and continue if *at least one* succeeds.\n // If *all* fail, we still proceed but log a more prominent warning.\n console.warn(\n `⚠️ [schema-env] Warning: All ${\n secretsSources.length\n } provided secretsSources functions failed to resolve successfully.`\n );\n // We still return an empty object as per ADR (continue validation with other sources)\n return {};\n }\n\n return mergedSecrets;\n}\n\n/**\n * Determines the correct validator adapter based on options.\n * Checks for mutual exclusivity and ensures a valid adapter (either default Zod or custom) is available.\n * @internal\n * @template TSchema The Zod object schema type if provided.\n * @template TResult The expected result type.\n * @param options The base options containing `schema` and/or `validator`.\n * @returns The determined `ValidatorAdapter`.\n * @throws {Error} If both `schema` and `validator` are provided.\n * @throws {Error} If `schema` is provided but is not a `ZodObject`.\n * @throws {Error} If neither `schema` nor `validator` is provided.\n */\nfunction _getValidatorAdapter<\n // Use AnyZodObject here for the constraint\n TSchema extends z.AnyZodObject | undefined,\n TResult = TSchema extends z.AnyZodObject ? z.infer<TSchema> : unknown,\n>(options: CreateEnvBaseOptions<TSchema, TResult>): ValidatorAdapter<TResult> {\n const { schema, validator } = options;\n\n // 1. Check for mutual exclusivity\n if (schema && validator) {\n throw new Error(\"Cannot provide both 'schema' and 'validator' options.\");\n }\n\n // 2. Use custom validator if provided\n if (validator) {\n // Type assertion needed here because TResult is determined by the caller's generic\n return validator as ValidatorAdapter<TResult>;\n }\n\n // 3. Use default Zod adapter if schema is provided and valid\n if (schema) {\n // The runtime check remains instanceof ZodObject\n if (!(schema instanceof ZodObject)) {\n throw new Error(\n \"Invalid 'schema' provided. Expected a ZodObject when 'validator' is not used.\"\n );\n }\n // We know schema is a ZodObject here due to the runtime check\n // Cast needed to align with the broader TResult generic\n return new ZodValidatorAdapter(\n schema\n ) as unknown as ValidatorAdapter<TResult>;\n }\n\n // 4. Throw if neither is provided\n throw new Error(\"Must provide either a 'schema' or a 'validator' option.\");\n}\n\n// --- Public API (Updated Types) ---\n\n/**\n * Validates and parses environment variables synchronously.\n * Supports Zod schema validation (default via `schema` option) or a custom\n * validation library via the `validator` option.\n *\n * Loads variables from `.env` files (specified paths, base, and environment-specific) and `process.env`.\n * Optionally expands variables using `dotenv-expand`.\n * Throws an error if validation fails, ensuring environment safety at startup.\n *\n * Use this for standard synchronous initialization. For fetching secrets from\n * external systems asynchronously, use `createEnvAsync`.\n *\n * You **must** provide either the `schema` option (for default Zod validation)\n * or the `validator` option (for custom validation), but not both.\n *\n * The final precedence order for variables is:\n * 1. `process.env` (Highest priority)\n * 2. Environment-specific file (e.g., `.env.production`) if `NODE_ENV` is set and `dotEnvPath` is not false.\n * 3. Files specified in `dotEnvPath` array (later files override earlier ones) / Single `dotEnvPath` file / Default `./.env` (if `dotEnvPath` is not false).\n * 4. Defaults defined in the validation schema/logic (Lowest priority - applied by Zod or custom adapter during validation).\n *\n * Note: Variable expansion (`expandVariables: true`) happens *after* all `.env` files (2, 3) are merged,\n * but *before* merging with `process.env` (1).\n *\n * @template TSchema - The Zod object schema type (`z.AnyZodObject`) if using default validation. Leave `undefined` if using `validator`.\n * @template TResult - The expected type of the validated environment object. Inferred from TSchema if using Zod, otherwise requires explicit specification (e.g., `createEnv<undefined, MyCustomType>({ validator: ... })`).\n * @param options - Configuration options. Requires either `schema` OR `validator`.\n * @returns {TResult} The validated environment object.\n * @throws {Error} If validation fails, options are invalid (e.g., both `schema` and `validator` provided, or neither), or file loading encounters critical errors.\n */\n// Use AnyZodObject for the TSchema constraint\nexport function createEnv<\n TSchema extends z.AnyZodObject | undefined,\n TResult = TSchema extends z.AnyZodObject ? z.infer<TSchema> : unknown,\n>(options: CreateEnvOptions<TSchema, TResult>): TResult {\n const {\n dotEnvPath,\n expandVariables = false,\n _internalDotenvExpand = expand, // _internalDotenvConfig removed\n } = options;\n\n // 1. Determine validator adapter (throws on invalid option combinations)\n const adapter = _getValidatorAdapter(options);\n\n // 2. Load .env files (respecting NODE_ENV) - Can throw sync\n const mergedDotEnvParsed = _loadDotEnvFiles(\n dotEnvPath,\n process.env.NODE_ENV // Use actual process.env value here for deciding which env-specific file to load\n // _internalDotenvConfig removed from call\n );\n\n // 3. Expand .env values if enabled - Should not throw\n const finalDotEnvValues = _expandDotEnvValues(\n mergedDotEnvParsed,\n expandVariables,\n _internalDotenvExpand\n );\n\n // 4. Merge with process.env\n const sourceForValidation = _mergeProcessEnv(finalDotEnvValues);\n\n // --- Add Debug Log ---\n // console.log(\"--- DEBUG [createEnv] ---\");\n // console.log(\"Incoming process.env.NODE_ENV:\", process.env.NODE_ENV);\n // console.log(\"Incoming process.env.TARGET:\", process.env.TARGET);\n // console.log(\"mergedDotEnvParsed (from files):\", mergedDotEnvParsed);\n // console.log(\"finalDotEnvValues (after expansion):\", finalDotEnvValues);\n // console.log(\n // \"sourceForValidation (final merge before validation):\",\n // sourceForValidation\n // );\n // console.log(\"--- END DEBUG [createEnv] ---\");\n // --- End Debug Log ---\n\n // 5. Validate against schema using the chosen adapter\n const validationResult = _validateEnvironment(adapter, sourceForValidation);\n\n // 6. Handle validation outcome\n if (!validationResult.success) {\n // Use the updated error formatter\n const errorMessage = _formatValidationError(validationResult.error);\n console.error(errorMessage); // Log details\n throw new Error(\"Environment validation failed. Check console output.\");\n }\n\n // Return the strongly typed parsed data\n return validationResult.data;\n}\n\n/**\n * Validates and parses environment variables asynchronously.\n * Supports Zod schema validation (default via `schema` option) or a custom\n * validation library via the `validator` option.\n *\n * Loads variables from `.env` files, optional asynchronous `secretsSources`, and `process.env`.\n * Optionally expands variables from `.env` files using `dotenv-expand`.\n * Returns a Promise that resolves with the validated environment or rejects if validation fails.\n *\n * Use this when you need to fetch secrets from external systems during startup.\n * For purely synchronous validation, use `createEnv`.\n *\n * You **must** provide either the `schema` option (for default Zod validation)\n * or the `validator` option (for custom validation), but not both.\n *\n * The final precedence order for variables is:\n * 1. `process.env` (Highest priority)\n * 2. Variables fetched via `secretsSources` (Later sources override earlier ones).\n * 3. Environment-specific file (e.g., `.env.production`) if `NODE_ENV` is set and `dotEnvPath` is not false.\n * 4. Files specified in `dotEnvPath` array (later files override earlier ones) / Single `dotEnvPath` file / Default `./.env` (if `dotEnvPath` is not false).\n * 5. Defaults defined in the validation schema/logic (Lowest priority - applied by Zod or custom adapter during validation).\n *\n * Note: Variable expansion (`expandVariables: true`) happens *after* all `.env` files (3, 4) are merged,\n * but *before* merging with `secretsSources` (2) and `process.env` (1).\n *\n * @template TSchema - The Zod object schema type (`z.AnyZodObject`) if using default validation. Leave `undefined` if using `validator`.\n * @template TResult - The expected type of the validated environment object. Inferred from TSchema if using Zod, otherwise requires explicit specification (e.g., `createEnvAsync<undefined, MyCustomType>({ validator: ... })`).\n * @param options - Configuration options. Requires either `schema` OR `validator`.\n * @returns {Promise<TResult>} A Promise resolving to the validated environment object.\n * @throws {Error} If options are invalid (e.g., both `schema` and `validator` provided, or neither) (synchronous throw).\n * @throws {Error} If synchronous file loading encounters critical errors (synchronous throw).\n * @rejects {Error} If asynchronous operations or validation fail.\n */\n// Use AnyZodObject for the TSchema constraint\nexport async function createEnvAsync<\n TSchema extends z.AnyZodObject | undefined,\n TResult = TSchema extends z.AnyZodObject ? z.infer<TSchema> : unknown,\n>(options: CreateEnvAsyncOptions<TSchema, TResult>): Promise<TResult> {\n const {\n dotEnvPath,\n expandVariables = false,\n secretsSources,\n _internalDotenvExpand = expand, // _internalDotenvConfig removed\n } = options;\n\n // 1. Determine validator adapter (throws on invalid option combinations)\n // This synchronous check happens before any async operations.\n const adapter = _getValidatorAdapter(options);\n\n // --- Synchronous Operations ---\n // Any synchronous errors thrown here will cause the promise to reject implicitly.\n // 2. Load .env files (respecting NODE_ENV)\n const mergedDotEnvParsed: dotenv.DotenvParseOutput = _loadDotEnvFiles(\n dotEnvPath,\n process.env.NODE_ENV // Use actual process.env value here for deciding which env-specific file to load\n // _internalDotenvConfig removed from call\n );\n // 3. Expand .env values if enabled\n const expandedDotEnvValues: dotenv.DotenvParseOutput = _expandDotEnvValues(\n mergedDotEnvParsed,\n expandVariables,\n _internalDotenvExpand\n );\n\n // Now handle the async part\n try {\n // 4. Fetch secrets asynchronously\n const secretsValues = await _fetchSecrets(secretsSources);\n\n // 5. Merge sources in correct async precedence: .env -> secrets -> process.env\n const sourceBeforeProcessEnv: EnvironmentInput = {\n ...expandedDotEnvValues,\n ...secretsValues,\n };\n const sourceForValidation = _mergeProcessEnv(sourceBeforeProcessEnv);\n\n // --- Add Debug Log ---\n // console.log(\"--- DEBUG [createEnvAsync] ---\");\n // console.log(\"Incoming process.env.NODE_ENV:\", process.env.NODE_ENV);\n // console.log(\"Incoming process.env.TARGET:\", process.env.TARGET);\n // console.log(\"mergedDotEnvParsed (from files):\", mergedDotEnvParsed);\n // console.log(\"expandedDotEnvValues (after expansion):\", expandedDotEnvValues);\n // console.log(\"secretsValues (from sources):\", secretsValues);\n // console.log(\n // \"sourceBeforeProcessEnv (.env+secrets):\",\n // sourceBeforeProcessEnv\n // );\n // console.log(\n // \"sourceForValidation (final merge before validation):\",\n // sourceForValidation\n // );\n // console.log(\"--- END DEBUG [createEnvAsync] ---\");\n // --- End Debug Log ---\n\n // 6. Validate against schema using the chosen adapter\n const validationResult = _validateEnvironment(adapter, sourceForValidation);\n\n // 7. Handle validation outcome\n if (!validationResult.success) {\n const errorMessage = _formatValidationError(validationResult.error);\n console.error(errorMessage); // Log details\n // Throw an error to cause the promise rejection\n throw new Error(\"Environment validation failed. Check console output.\");\n }\n\n // Resolve the promise with the strongly typed parsed data\n return validationResult.data;\n } catch (error) {\n // Catch errors from _fetchSecrets or validation fail above\n if (error instanceof Error) {\n return Promise.reject(error);\n } else {\n // Wrap non-Error throws/rejections\n return Promise.reject(\n new Error(`An unexpected error occurred: ${String(error)}`)\n );\n }\n }\n}\n"],"mappings":"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","names":["fs","dotenv"]}