{"version":3,"sources":["../src/index.ts","../src/parser.ts","../src/loader.ts","../src/errors.ts","../src/validator/zod-adapter.ts","../src/utils.ts","../src/validator/internal.ts","../src/types.ts"],"sourcesContent":["// Public API for smart-env.\n// Orchestrates loading, validation, and error handling.\n\nimport { loadRawEnv, extractSchemaKeys } from \"./loader\";\nimport { validateWithZod } from \"./validator/zod-adapter\";\nimport { validateWithInternal } from \"./validator/internal\";\nimport { SmartEnvError, logValidationErrors } from \"./errors\";\nimport { freezeConfig, scrubKeys } from \"./utils\";\nimport {\n  LoadEnvOptions,\n  InternalSchema,\n  isZodSchema,\n  ValidationDetail,\n} from \"./types\";\n\n// Main function: load and validate environment variables.\n// Accepts Zod schema or internal DSL schema.\n// Returns frozen, type-safe config object.\nexport function loadEnv<T extends Record<string, any>>(\n  schema: any,\n  options: LoadEnvOptions = {}\n): T {\n  // Set defaults for options.\n  const strict = options.strict ?? true;\n  const maskSecrets = options.maskSecrets ?? true;\n  const scrubProcessEnv = options.scrubProcessEnv ?? false;\n\n  // Load raw env from files and process.env.\n  const rawEnv = loadRawEnv(options);\n\n  // Validate using appropriate validator.\n  const result = isZodSchema(schema)\n    ? validateWithZod(schema, rawEnv)\n    : validateWithInternal(schema as InternalSchema, rawEnv);\n\n  // Handle validation failure.\n  if (!result.success) {\n    handleValidationErrors(result.errors, strict, maskSecrets);\n\n    // In warn mode, return empty frozen object.\n    return freezeConfig({}) as T;\n  }\n\n  // Optionally scrub process.env.\n  if (scrubProcessEnv) {\n    const keys = extractSchemaKeys(schema);\n    scrubKeys(keys);\n  }\n\n  // Return frozen config.\n  return freezeConfig(result.data) as T;\n}\n\n// Handle validation errors: throw or log based on strict mode.\nfunction handleValidationErrors(\n  errors: ValidationDetail[],\n  strict: boolean,\n  maskSecrets: boolean\n): void {\n  if (strict) {\n    throw new SmartEnvError(errors);\n  }\n\n  logValidationErrors(errors, maskSecrets);\n}\n\n// Define schema using internal DSL (for non-Zod users).\nexport function defineSchema(schema: InternalSchema): InternalSchema {\n  return schema;\n}\n\n// Re-export types for consumer convenience.\nexport type { LoadEnvOptions, InternalSchema, ValidationDetail };\nexport { SmartEnvError };\n","// Parses .env files into key-value records.\n// Wraps dotenv with a clean, testable interface.\n\nimport { config } from \"dotenv\";\nimport { existsSync } from \"fs\";\nimport { EnvRecord } from \"./types\";\n\n// Parse a single .env file and return its contents.\n// Returns empty object if file doesn't exist or has errors.\nexport function parseEnvFile(filePath: string): EnvRecord {\n  if (!existsSync(filePath)) {\n    return {};\n  }\n\n  const result = config({ path: filePath });\n\n  if (result.error) {\n    console.warn(`smart-env: failed to parse ${filePath}`, result.error);\n    return {};\n  }\n\n  return result.parsed || {};\n}\n\n// Parse multiple .env files in order.\n// Later files override earlier ones.\nexport function parseEnvFiles(filePaths: string[]): EnvRecord {\n  const merged: EnvRecord = {};\n\n  for (const path of filePaths) {\n    const parsed = parseEnvFile(path);\n    Object.assign(merged, parsed);\n  }\n\n  return merged;\n}\n","// Merges .env files with process.env.\n// Pure function: reads files and environment, returns merged result.\n\nimport { parseEnvFiles } from \"./parser\";\nimport { EnvRecord, LoadEnvOptions } from \"./types\";\n\n// Default source files to load.\nconst DEFAULT_SOURCE_FILES = [\".env\"];\n\n// Merge .env file(s) with process.env.\n// process.env values override file values.\nexport function loadRawEnv(options: LoadEnvOptions = {}): EnvRecord {\n  const sourceFiles = options.sourceFiles || DEFAULT_SOURCE_FILES;\n\n  // Parse all source files.\n  const fileEnv = parseEnvFiles(sourceFiles);\n\n  // Merge with process.env (process.env takes priority).\n  const merged: EnvRecord = { ...fileEnv };\n\n  for (const key in process.env) {\n    if (process.env[key] !== undefined) {\n      merged[key] = process.env[key];\n    }\n  }\n\n  return merged;\n}\n\n// Get list of keys that were loaded from schema.\n// Used for scrubbing process.env.\nexport function extractSchemaKeys(schema: unknown): string[] {\n  if (typeof schema !== \"object\" || schema === null) {\n    return [];\n  }\n\n  // Handle internal schema format.\n  if (!(\"_def\" in schema)) {\n    return Object.keys(schema);\n  }\n\n  // Handle Zod schema.\n  // Access shape from _def if available.\n  const def = (schema as any)._def;\n  if (def && def.shape && typeof def.shape === \"function\") {\n    return Object.keys(def.shape());\n  }\n\n  return [];\n}\n","// Structured error types and pretty printing.\n// Provides clear, actionable error messages.\n\nimport { ValidationDetail } from \"./types\";\nimport { maybeeMaskValue } from \"./utils\";\n\n// Custom error class for smart-env validation failures.\nexport class SmartEnvError extends Error {\n  public readonly details: ValidationDetail[];\n  public readonly type: \"Missing\" | \"Invalid\" | \"UnknownKey\";\n\n  constructor(details: ValidationDetail[], type = \"Invalid\" as const) {\n    const message = formatErrorMessage(details);\n    super(message);\n    this.name = \"SmartEnvError\";\n    this.details = details;\n    this.type = type;\n  }\n}\n\n// Format validation details into readable error message.\n// Shows count and bulleted list of problems.\nfunction formatErrorMessage(details: ValidationDetail[]): string {\n  const count = details.length;\n  const header = `smart-env: ${count} problem(s) found`;\n  const items = details.map((d) => `  • ${d.key} — ${d.message}`);\n  return [header, ...items].join(\"\\n\");\n}\n\n// Pretty print error details to console.\n// Respects maskSecrets option.\nexport function logValidationErrors(\n  details: ValidationDetail[],\n  maskSecrets: boolean\n): void {\n  console.error(`\\n❌ smart-env: ${details.length} problem(s) found`);\n\n  for (const detail of details) {\n    const message = maskSecrets\n      ? maskErrorMessage(detail.message)\n      : detail.message;\n    console.error(`  • ${detail.key} — ${message}`);\n  }\n\n  console.error(\"\");\n}\n\n// Mask sensitive values in error message.\n// Simple pattern: replaces quoted strings if they look secret-like.\nfunction maskErrorMessage(message: string): string {\n  return message.replace(/\"([^\"]+)\"/g, (match, value) => {\n    if (value.length > 10 || /[A-Z0-9_]{8,}/.test(value)) {\n      return '\"***\"';\n    }\n    return match;\n  });\n}\n\n// Convert Zod issues to ValidationDetail array.\n// Extracts key and message from Zod error format.\nexport function zodIssuesToDetails(issues: any[]): ValidationDetail[] {\n  return issues.map((issue) => ({\n    key: issue.path.join(\".\") || \"unknown\",\n    message: issue.message,\n  }));\n}\n","// Zod schema adapter.\n// Validates env using Zod and maps results to common contract.\n\nimport { EnvRecord, ValidationDetail } from \"../types\";\nimport { zodIssuesToDetails } from \"../errors\";\n\n// Validate env record against Zod schema.\n// Returns validated data or array of validation errors.\nexport function validateWithZod(\n  schema: any,\n  env: EnvRecord\n):\n  | { success: true; data: any }\n  | { success: false; errors: ValidationDetail[] } {\n  const result = schema.safeParse(env);\n\n  if (result.success) {\n    return { success: true, data: result.data };\n  }\n\n  const errors = zodIssuesToDetails(result.error.issues);\n  return { success: false, errors };\n}\n","// Shared utility functions.\n// Includes coercion, masking, and helper logic.\n\nimport { EnvRecord } from \"./types\";\n\n// Coerce string value to boolean.\n// \"true\", \"1\", \"yes\" => true; \"false\", \"0\", \"no\" => false.\nexport function coerceBoolean(value: string | undefined): boolean {\n  if (!value) return false;\n  const lower = value.toLowerCase();\n  return lower === \"true\" || lower === \"1\" || lower === \"yes\";\n}\n\n// Coerce string value to number.\n// Returns NaN if conversion fails.\nexport function coerceNumber(value: string | undefined): number {\n  if (!value) return NaN;\n  return Number(value);\n}\n\n// Mask a secret value for logging.\n// Shows only first 3 and last 2 chars.\nexport function maskSecret(value: string): string {\n  if (value.length <= 5) return \"***\";\n  return `${value.slice(0, 3)}***${value.slice(-2)}`;\n}\n\n// Detect if a key name looks like a secret.\n// Checks for common patterns: KEY, SECRET, TOKEN, PASSWORD.\nexport function isSecretKey(key: string): boolean {\n  const upper = key.toUpperCase();\n  return (\n    upper.includes(\"KEY\") ||\n    upper.includes(\"SECRET\") ||\n    upper.includes(\"TOKEN\") ||\n    upper.includes(\"PASSWORD\")\n  );\n}\n\n// Mask value if masking enabled and key is secret-like.\nexport function maybeeMaskValue(\n  key: string,\n  value: string,\n  shouldMask: boolean\n): string {\n  if (!shouldMask) return value;\n  if (!isSecretKey(key)) return value;\n  return maskSecret(value);\n}\n\n// Remove specified keys from process.env.\n// Used for scrubProcessEnv option.\nexport function scrubKeys(keys: string[]): void {\n  for (const key of keys) {\n    delete process.env[key];\n  }\n}\n\n// Freeze an object deeply (one level).\n// Returns readonly version of input.\nexport function freezeConfig<T extends Record<string, unknown>>(\n  config: T\n): Readonly<T> {\n  return Object.freeze(config);\n}\n","// Internal DSL validator.\n// Minimal schema validator for users who don't want Zod.\n\nimport { EnvRecord, InternalSchema, ValidationDetail } from \"../types\";\nimport { coerceBoolean, coerceNumber } from \"../utils\";\n\n// Validate env record against internal schema.\n// Returns validated data or array of validation errors.\nexport function validateWithInternal(\n  schema: InternalSchema,\n  env: EnvRecord\n):\n  | { success: true; data: any }\n  | { success: false; errors: ValidationDetail[] } {\n  const errors: ValidationDetail[] = [];\n  const data: Record<string, any> = {};\n\n  for (const key in schema) {\n    const field = schema[key];\n    const rawValue = env[key];\n\n    // Check required fields.\n    if (field.required && !rawValue) {\n      errors.push({\n        key,\n        message: `Required but missing. Add: ${key}=your_value_here`,\n      });\n      continue;\n    }\n\n    // Apply defaults.\n    if (rawValue === undefined && field.default !== undefined) {\n      data[key] = field.default;\n      continue;\n    }\n\n    // Coerce and validate types.\n    if (rawValue !== undefined) {\n      const coerced = coerceValue(rawValue, field.type);\n\n      if (coerced.error) {\n        errors.push({\n          key,\n          message: `Expected ${field.type}, got \"${rawValue}\"`,\n        });\n      } else {\n        data[key] = coerced.value;\n      }\n    }\n  }\n\n  if (errors.length > 0) {\n    return { success: false, errors };\n  }\n\n  return { success: true, data };\n}\n\n// Coerce string value to specified type.\n// Returns coerced value or error.\nfunction coerceValue(\n  value: string,\n  type: \"string\" | \"number\" | \"boolean\"\n): { value: any; error?: string } {\n  if (type === \"string\") {\n    return { value };\n  }\n\n  if (type === \"number\") {\n    const num = coerceNumber(value);\n    if (isNaN(num)) {\n      return { value: null, error: \"not a number\" };\n    }\n    return { value: num };\n  }\n\n  if (type === \"boolean\") {\n    return { value: coerceBoolean(value) };\n  }\n\n  return { value, error: \"unknown type\" };\n}\n","// Central type definitions for smart-env.\n// Keeps type contracts consistent across modules.\n\nexport type EnvValue = string | undefined;\n\nexport type EnvRecord = Record<string, EnvValue>;\n\n// Options passed to loadEnv function.\nexport interface LoadEnvOptions {\n  // Throw errors on validation failure (default: true).\n  strict?: boolean;\n  // Remove keys from process.env after loading (default: false).\n  scrubProcessEnv?: boolean;\n  // Mask secret values in error messages (default: true).\n  maskSecrets?: boolean;\n  // Allow extra env vars not in schema (default: true).\n  allowUnknownKeys?: boolean;\n  // List of .env files to load (default: ['.env']).\n  sourceFiles?: string[];\n}\n\n// Shape of validation error detail.\nexport interface ValidationDetail {\n  key: string;\n  message: string;\n}\n\n// Internal schema field definition for DSL.\nexport interface SchemaField {\n  type: \"string\" | \"number\" | \"boolean\";\n  required?: boolean;\n  default?: string | number | boolean;\n}\n\n// Internal schema definition object.\nexport type InternalSchema = Record<string, SchemaField>;\n\n// Type guard: checks if value is Zod schema.\nexport function isZodSchema(schema: unknown): boolean {\n  return (\n    typeof schema === \"object\" &&\n    schema !== null &&\n    \"_def\" in schema &&\n    \"parse\" in schema\n  );\n}\n"],"mappings":"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","names":["config"]}