bhcto.com

lib/env.js

// Tiny .env reader (the repo hand-parses .env; no dotenv dependency).
const fs = require("fs");
const path = require("path");

// .env lives at the repo root (shared with other scripts) and may sit several
// directories above this module — walk up from __dirname until we find it.
function findEnvFile() {
  let dir = __dirname;
  for (let i = 0; i < 10; i++) {
    const p = path.join(dir, ".env");
    if (fs.existsSync(p)) return p;
    const parent = path.dirname(dir);
    if (parent === dir) break;
    dir = parent;
  }
  return path.join(__dirname, "..", ".env"); // fallback
}

let cache = null;
function loadEnv() {
  if (cache) return cache;
  cache = {};
  let text = "";
  try {
    text = fs.readFileSync(findEnvFile(), "utf8");
  } catch {
    return cache;
  }
  for (const line of text.split(/\r?\n/)) {
    // Allow mixed-case keys (e.g. Brightdata_Username).
    const m = line.match(/^\s*([A-Za-z0-9_]+)\s*=\s*(.*)\s*$/);
    if (m) cache[m[1]] = m[2].replace(/^["']|["']$/g, "").trim();
  }
  return cache;
}

// Case-insensitive lookup against process.env then the .env file.
function lookup(key) {
  if (process.env[key]) return process.env[key];
  const file = loadEnv();
  if (file[key]) return file[key];
  const lc = key.toLowerCase();
  for (const k of Object.keys(file)) if (k.toLowerCase() === lc) return file[k];
  for (const k of Object.keys(process.env))
    if (k.toLowerCase() === lc && process.env[k]) return process.env[k];
  return null;
}

function env(key, { required = false } = {}) {
  const v = lookup(key);
  if (required && !v) {
    throw new Error(
      `Missing required env var ${key}. Add it to .env (or export it).`,
    );
  }
  return v;
}

// First non-empty value among several candidate key names.
function envAny(names, { required = false } = {}) {
  for (const n of names) {
    const v = lookup(n);
    if (v) return v;
  }
  if (required) throw new Error(`Missing env var (tried: ${names.join(", ")}).`);
  return null;
}

module.exports = { env, envAny, loadEnv };