{"version":3,"file":"dotenv.mjs","names":[],"sources":["../../src/util/dotenv.ts"],"sourcesContent":["// ── File resolution ─────────────────────────────────────────────────────\n\n/** Returns ordered list of `.env` file names to load (no fs access). */\nexport function resolveEnvFiles(modes: string[] = [], local = true, base = true): string[] {\n  const files: string[] = [];\n  if (base) {\n    files.push('.env');\n    if (local) files.push('.env.local');\n  }\n  for (const mode of modes) {\n    files.push(`.env.${mode}`);\n    if (local) files.push(`.env.${mode}.local`);\n  }\n  return files;\n}\n\n// ── Parser ──────────────────────────────────────────────────────────────\n\n/** Parse a `.env` file string into key-value pairs. */\nexport function parseEnvFile(content: string): Record<string, string> {\n  const result: Record<string, string> = {};\n  const lines = content.split('\\n');\n  let i = 0;\n\n  while (i < lines.length) {\n    const line = lines[i]!.trim();\n    i++;\n\n    // Skip empty lines and comments\n    if (!line || line.startsWith('#')) continue;\n\n    // Strip optional `export ` prefix\n    const stripped = line.startsWith('export ') ? line.slice(7) : line;\n\n    const eqIndex = stripped.indexOf('=');\n    if (eqIndex === -1) continue;\n\n    const key = stripped.slice(0, eqIndex).trim();\n    let raw = stripped.slice(eqIndex + 1);\n\n    // Detect quoted values\n    const trimmedRaw = raw.trimStart();\n    const quote = trimmedRaw[0];\n\n    if (quote === '\"' || quote === \"'\" || quote === '`') {\n      let value = trimmedRaw.slice(1);\n\n      // Check for closing quote on the same line\n      const closeIndex = findClosingQuote(value, quote);\n      if (closeIndex !== -1) {\n        value = value.slice(0, closeIndex);\n      } else {\n        // Multiline: accumulate until closing quote\n        while (i < lines.length) {\n          const nextLine = lines[i]!;\n          i++;\n          const ci = findClosingQuote(nextLine, quote);\n          if (ci !== -1) {\n            value += `\\n${nextLine.slice(0, ci)}`;\n            break;\n          }\n          value += `\\n${nextLine}`;\n        }\n      }\n\n      if (quote === '\"') value = unescapeDoubleQuoted(value);\n      result[key] = value;\n    } else {\n      // Unquoted: strip inline comments, trim\n      const commentIndex = raw.indexOf(' #');\n      if (commentIndex !== -1) raw = raw.slice(0, commentIndex);\n      result[key] = raw.trim();\n    }\n  }\n\n  return result;\n}\n\nfunction findClosingQuote(s: string, quote: string): number {\n  let i = 0;\n  while (i < s.length) {\n    if (s[i] === '\\\\' && quote === '\"') {\n      i += 2; // skip escaped char\n      continue;\n    }\n    if (s[i] === quote) return i;\n    i++;\n  }\n  return -1;\n}\n\nfunction unescapeDoubleQuoted(s: string): string {\n  return s.replace(/\\\\n/g, '\\n').replace(/\\\\r/g, '\\r').replace(/\\\\t/g, '\\t').replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, '\\\\');\n}\n\n// ── Variable expansion ──────────────────────────────────────────────────\n\n/**\n * Expand `$VAR`, `${VAR}`, `${VAR:-default}`, `${VAR-default}` in a string.\n * Escaped `\\$` produces a literal `$`. Undefined variables resolve to `\"\"`.\n */\nexport function expandVariables(value: string, env: Record<string, string | undefined>): string {\n  let result = '';\n  let i = 0;\n\n  while (i < value.length) {\n    if (value[i] === '\\\\' && value[i + 1] === '$') {\n      result += '$';\n      i += 2;\n      continue;\n    }\n\n    if (value[i] === '$') {\n      i++;\n      if (i >= value.length) {\n        result += '$';\n        break;\n      }\n\n      if (value[i] === '{') {\n        // ${VAR}, ${VAR:-default}, ${VAR-default}\n        i++;\n        const closeIdx = value.indexOf('}', i);\n        if (closeIdx === -1) {\n          result += `\\${${value.slice(i)}`;\n          break;\n        }\n\n        const expr = value.slice(i, closeIdx);\n        i = closeIdx + 1;\n\n        const colonDashIdx = expr.indexOf(':-');\n        const dashIdx = colonDashIdx === -1 ? expr.indexOf('-') : -1;\n\n        if (colonDashIdx !== -1) {\n          // ${VAR:-default} — use default if unset or empty\n          const varName = expr.slice(0, colonDashIdx);\n          const fallback = expr.slice(colonDashIdx + 2);\n          const val = env[varName];\n          result += val ? val : expandVariables(fallback, env);\n        } else if (dashIdx !== -1) {\n          // ${VAR-default} — use default only if unset\n          const varName = expr.slice(0, dashIdx);\n          const fallback = expr.slice(dashIdx + 1);\n          const val = env[varName];\n          result += val !== undefined ? val : expandVariables(fallback, env);\n        } else {\n          result += env[expr] ?? '';\n        }\n      } else {\n        // $VAR — collect word chars\n        let varName = '';\n        while (i < value.length && /[\\w]/.test(value[i]!)) {\n          varName += value[i];\n          i++;\n        }\n        if (varName) {\n          result += env[varName] ?? '';\n        } else {\n          result += '$';\n        }\n      }\n      continue;\n    }\n\n    result += value[i];\n    i++;\n  }\n\n  return result;\n}\n\n// ── File loading ────────────────────────────────────────────────────────\n\nexport type LoadEnvFilesOptions = {\n  dir?: string;\n  modes?: string[];\n  local?: boolean;\n  override?: boolean;\n  base?: boolean;\n};\n\n/**\n * Load and merge `.env` files, returning the combined key-value map.\n * Variable expansion uses the merged file values + process env as lookup.\n *\n * Returns synchronously when `node:fs`/`node:path` are already cached (typical),\n * or a Promise on the very first call.\n */\nexport function loadEnvFiles(\n  options: LoadEnvFilesOptions,\n  processEnv: Record<string, string | undefined>,\n): Record<string, string> | Promise<Record<string, string>> {\n  if (typeof process === 'undefined') return {};\n\n  try {\n    if (_fs && _path) return loadEnvFilesSync(_fs, _path, options, processEnv);\n    return initNodeModules()\n      .then(() => loadEnvFilesSync(_fs!, _path!, options, processEnv))\n      .catch(() => ({}) as Record<string, string>);\n  } catch {\n    return {};\n  }\n}\n\n// ── Internals ───────────────────────────────────────────────────────────\n\nlet _fs: typeof import('node:fs') | undefined;\nlet _path: typeof import('node:path') | undefined;\n\nasync function initNodeModules(): Promise<void> {\n  if (_fs && _path) return;\n  _fs = await import('node:fs');\n  _path = await import('node:path');\n}\n\nif (typeof process !== 'undefined') initNodeModules();\n\nfunction loadEnvFilesSync(\n  fs: typeof import('node:fs'),\n  path: typeof import('node:path'),\n  options: LoadEnvFilesOptions,\n  processEnv: Record<string, string | undefined>,\n): Record<string, string> {\n  const dir = options.dir ?? process.cwd();\n  const fileNames = resolveEnvFiles(options.modes, options.local ?? true, options.base ?? true);\n  const merged: Record<string, string> = {};\n\n  for (const name of fileNames) {\n    const filePath = path.resolve(dir, name);\n    if (!fs.existsSync(filePath)) continue;\n    const content = fs.readFileSync(filePath, 'utf-8');\n    const parsed = parseEnvFile(content);\n    Object.assign(merged, parsed);\n  }\n\n  // Expand variables: file values + processEnv as lookup (processEnv wins in lookup unless override)\n  const lookup = options.override ? { ...processEnv, ...merged } : { ...merged, ...processEnv };\n  for (const key of Object.keys(merged)) {\n    merged[key] = expandVariables(merged[key]!, lookup);\n  }\n\n  return merged;\n}\n"],"mappings":"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"}