import { __name } from './chunk-IBAHCH6O.js';
import fs from 'fs/promises';
import path from 'path';

var DEFAULT_IGNORED_DIRS = [
  ".git",
  "node_modules",
  "dist",
  "build",
  ".next",
  "vendor",
  ".venv"
];
var DEFAULT_MAX_FILE_SIZE_BYTES = 1024 * 1024;
var DEFAULT_CONCURRENCY = 50;
var ENV_KEY_IDENTIFIER_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/;
var LANGUAGE_BY_EXTENSION = {
  ".js": "js-like",
  ".mjs": "js-like",
  ".cjs": "js-like",
  ".jsx": "js-like",
  ".ts": "js-like",
  ".mts": "js-like",
  ".cts": "js-like",
  ".tsx": "js-like",
  ".vue": "js-like",
  ".svelte": "js-like",
  ".astro": "js-like",
  ".mdx": "js-like",
  ".py": "python",
  ".go": "go",
  ".rb": "ruby",
  ".php": "php",
  ".rs": "rust",
  ".java": "java",
  ".cs": "csharp"
};
var PATTERNS_BY_LANGUAGE = {
  "js-like": [
    {
      regex: /\bprocess\.env\.([A-Za-z_][A-Za-z0-9_]*)\b/g,
      syntax: "process.env.member"
    },
    {
      regex: /\bprocess\.env\[\s*['"`]([A-Za-z_][A-Za-z0-9_]*)['"`]\s*\]/g,
      syntax: "process.env.bracket"
    },
    {
      regex: /\bimport\.meta\.env\.([A-Za-z_][A-Za-z0-9_]*)\b/g,
      syntax: "import.meta.env.member"
    },
    {
      regex: /\bimport\.meta\.env\[\s*['"`]([A-Za-z_][A-Za-z0-9_]*)['"`]\s*\]/g,
      syntax: "import.meta.env.bracket"
    },
    {
      regex: /\bENV\.([A-Za-z_][A-Za-z0-9_]*)\b/g,
      syntax: "ENV.member"
    },
    {
      regex: /\bENV\[\s*['"`]([A-Za-z_][A-Za-z0-9_]*)['"`]\s*\]/g,
      syntax: "ENV.bracket"
    }
  ],
  python: [
    {
      regex: /\bos\.environ\[\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]\s*\]/g,
      syntax: "python.environ"
    },
    {
      regex: /\bos\.getenv\(\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]/g,
      syntax: "python.getenv"
    }
  ],
  go: [
    {
      regex: /\bos\.(?:Getenv|LookupEnv)\(\s*"([A-Za-z_][A-Za-z0-9_]*)"\s*\)/g,
      syntax: "go.getenv"
    }
  ],
  ruby: [
    {
      regex: /\bENV\[\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]\s*\]/g,
      syntax: "ruby.env"
    },
    {
      regex: /\bENV\.fetch\(\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]/g,
      syntax: "ruby.fetch"
    }
  ],
  php: [
    {
      regex: /\bgetenv\(\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]/g,
      syntax: "php.getenv"
    },
    {
      regex: /\$_ENV\[\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]\s*\]/g,
      syntax: "php._env"
    },
    {
      regex: /\$_SERVER\[\s*['"]([A-Za-z_][A-Za-z0-9_]*)['"]\s*\]/g,
      syntax: "php._server"
    }
  ],
  rust: [
    {
      regex: /\bstd::env::(?:var|var_os)\(\s*"([A-Za-z_][A-Za-z0-9_]*)"\s*\)/g,
      syntax: "rust.getenv"
    }
  ],
  java: [
    {
      regex: /\bSystem\.getenv\(\s*"([A-Za-z_][A-Za-z0-9_]*)"\s*\)/g,
      syntax: "java.getenv"
    }
  ],
  csharp: [
    {
      regex: /\bEnvironment\.GetEnvironmentVariable\(\s*"([A-Za-z_][A-Za-z0-9_]*)"\s*\)/g,
      syntax: "csharp.getenv"
    }
  ]
};
var JS_DESTRUCTURE_PATTERNS = [
  {
    regex: /\{([^}]*)\}\s*=\s*process\.env\b/g,
    syntax: "process.env.destructure"
  },
  {
    regex: /\{([^}]*)\}\s*=\s*import\.meta\.env\b/g,
    syntax: "import.meta.env.destructure"
  },
  {
    regex: /\{([^}]*)\}\s*=\s*ENV\b/g,
    syntax: "ENV.destructure"
  }
];
async function discoverSourceFiles(cwd, ignoredDirs) {
  const filePaths = [];
  const globExcludes = [...ignoredDirs].flatMap((dirName) => [`**/${dirName}`, `**/${dirName}/**`]);
  for await (const relativePath of fs.glob("**/*", { cwd, exclude: globExcludes })) {
    const normalizedRelativePath = String(relativePath).replaceAll("\\", "/");
    const extension = path.extname(normalizedRelativePath).toLowerCase();
    if (!(extension in LANGUAGE_BY_EXTENSION)) continue;
    filePaths.push(path.resolve(cwd, normalizedRelativePath));
  }
  return filePaths;
}
__name(discoverSourceFiles, "discoverSourceFiles");
function extractDestructuredKeys(body) {
  const found = [];
  const propPattern = /(^|,)\s*([A-Za-z_][A-Za-z0-9_]*)\s*(?::\s*[A-Za-z_][A-Za-z0-9_]*)?\s*(?:=[^,]+)?\s*(?=,|$)/g;
  for (const match of body.matchAll(propPattern)) {
    const key = match[2];
    if (!key) continue;
    const wholeMatch = match[0] || "";
    const keyIndexInWhole = wholeMatch.indexOf(key);
    const relativeIndex = (match.index ?? 0) + (keyIndexInWhole >= 0 ? keyIndexInWhole : 0);
    found.push({ key, relativeIndex });
  }
  return found;
}
__name(extractDestructuredKeys, "extractDestructuredKeys");
function getNewlineIndices(content) {
  const indices = [];
  for (let i = 0; i < content.length; i++) {
    if (content.charCodeAt(i) === 10) indices.push(i);
  }
  return indices;
}
__name(getNewlineIndices, "getNewlineIndices");
function indexToLineAndColumn(content, newlineIndices, index) {
  let lo = 0;
  let hi = newlineIndices.length;
  while (lo < hi) {
    const mid = lo + Math.floor((hi - lo) / 2);
    if (newlineIndices[mid] < index) lo = mid + 1;
    else hi = mid;
  }
  const lineNumber = lo + 1;
  const lineStartIndex = lo === 0 ? 0 : newlineIndices[lo - 1] + 1;
  return {
    lineNumber,
    columnNumber: index - lineStartIndex + 1
  };
}
__name(indexToLineAndColumn, "indexToLineAndColumn");
function buildReference(filePath, content, newlineIndices, index, key, syntax) {
  const { lineNumber, columnNumber } = indexToLineAndColumn(content, newlineIndices, index);
  return {
    filePath,
    key,
    lineNumber,
    columnNumber,
    syntax
  };
}
__name(buildReference, "buildReference");
function skipQuotedWithoutMask(chars, startIndex, quoteChar) {
  let i = startIndex + 1;
  while (i < chars.length) {
    const ch = chars[i];
    if (ch === "\\") {
      i += 2;
      continue;
    }
    if (ch === quoteChar) {
      i++;
      return i;
    }
    i++;
  }
  return i;
}
__name(skipQuotedWithoutMask, "skipQuotedWithoutMask");
function skipTemplateWithoutMask(chars, startIndex) {
  let i = startIndex + 1;
  while (i < chars.length) {
    const ch = chars[i];
    const next = chars[i + 1];
    if (ch === "\\") {
      i += 2;
      continue;
    }
    if (ch === "`") {
      i++;
      return i;
    }
    if (ch === "$" && next === "{") {
      i += 2;
      let depth = 1;
      while (i < chars.length && depth > 0) {
        if (chars[i] === "\\") {
          i += 2;
          continue;
        }
        if (chars[i] === "{") depth++;
        else if (chars[i] === "}") depth--;
        i++;
      }
      continue;
    }
    i++;
  }
  return i;
}
__name(skipTemplateWithoutMask, "skipTemplateWithoutMask");
function skipAndMaskQuotedString(chars, startIndex, quoteChar) {
  let i = startIndex + 1;
  while (i < chars.length) {
    const ch = chars[i];
    if (ch === "\\") {
      i += 2;
      continue;
    }
    if (ch === quoteChar) {
      i++;
      break;
    }
    i++;
  }
  const endExclusive = i;
  const inner = chars.slice(startIndex + 1, Math.max(startIndex + 1, endExclusive - 1)).join("");
  const keepInner = ENV_KEY_IDENTIFIER_REGEX.test(inner);
  if (!keepInner) {
    for (let idx = startIndex + 1; idx < endExclusive - 1; idx++) {
      if (chars[idx] !== "\n") chars[idx] = " ";
    }
  }
  return endExclusive;
}
__name(skipAndMaskQuotedString, "skipAndMaskQuotedString");
function skipAndMaskTemplateLiteral(chars, startIndex) {
  let i = startIndex + 1;
  let segmentStart = i;
  const literalSegments = [];
  let hasInterpolation = false;
  while (i < chars.length) {
    const ch = chars[i];
    const next = chars[i + 1];
    if (ch === "\\") {
      i += 2;
      continue;
    }
    if (ch === "`") {
      literalSegments.push({ start: segmentStart, endExclusive: i });
      i++;
      break;
    }
    if (ch === "$" && next === "{") {
      hasInterpolation = true;
      literalSegments.push({ start: segmentStart, endExclusive: i });
      i += 2;
      let depth = 1;
      while (i < chars.length && depth > 0) {
        const exprCh = chars[i];
        const exprNext = chars[i + 1];
        if (exprCh === "\\") {
          i += 2;
          continue;
        }
        if (exprCh === "'" || exprCh === '"') {
          i = skipQuotedWithoutMask(chars, i, exprCh);
          continue;
        }
        if (exprCh === "`") {
          i = skipTemplateWithoutMask(chars, i);
          continue;
        }
        if (exprCh === "{") depth++;
        else if (exprCh === "}") depth--;
        if (depth === 0) {
          i++;
          break;
        }
        if (exprCh === "/" && exprNext === "/") {
          i += 2;
          while (i < chars.length && chars[i] !== "\n") i++;
          continue;
        }
        if (exprCh === "/" && exprNext === "*") {
          i += 2;
          while (i < chars.length) {
            if (chars[i] === "*" && chars[i + 1] === "/") {
              i += 2;
              break;
            }
            i++;
          }
          continue;
        }
        i++;
      }
      segmentStart = i;
      continue;
    }
    i++;
  }
  const endExclusive = i;
  if (!hasInterpolation) {
    const inner = chars.slice(startIndex + 1, Math.max(startIndex + 1, endExclusive - 1)).join("");
    if (!ENV_KEY_IDENTIFIER_REGEX.test(inner)) {
      for (let idx = startIndex + 1; idx < endExclusive - 1; idx++) {
        if (chars[idx] !== "\n") chars[idx] = " ";
      }
    }
    return endExclusive;
  }
  for (const segment of literalSegments) {
    for (let idx = segment.start; idx < segment.endExclusive; idx++) {
      if (chars[idx] !== "\n") chars[idx] = " ";
    }
  }
  return endExclusive;
}
__name(skipAndMaskTemplateLiteral, "skipAndMaskTemplateLiteral");
function maskCommentsPreserveLayout(content, language) {
  const chars = content.split("");
  const supportsHashComments = language === "python" || language === "ruby" || language === "php";
  const supportsSlashComments = language !== "python" && language !== "ruby";
  let i = 0;
  let inLineComment = false;
  let inBlockComment = false;
  while (i < chars.length) {
    const ch = chars[i];
    const next = chars[i + 1];
    if (inLineComment) {
      if (ch === "\n") {
        inLineComment = false;
      } else {
        chars[i] = " ";
      }
      i++;
      continue;
    }
    if (inBlockComment) {
      if (ch === "*" && next === "/") {
        chars[i] = " ";
        chars[i + 1] = " ";
        inBlockComment = false;
        i += 2;
        continue;
      }
      if (ch !== "\n") chars[i] = " ";
      i++;
      continue;
    }
    if (supportsSlashComments && ch === "/" && next === "/") {
      chars[i] = " ";
      chars[i + 1] = " ";
      inLineComment = true;
      i += 2;
      continue;
    }
    if (supportsSlashComments && ch === "/" && next === "*") {
      chars[i] = " ";
      chars[i + 1] = " ";
      inBlockComment = true;
      i += 2;
      continue;
    }
    if (supportsHashComments && ch === "#") {
      chars[i] = " ";
      inLineComment = true;
      i++;
      continue;
    }
    if (ch === "'") {
      i = skipAndMaskQuotedString(chars, i, "'");
      continue;
    }
    if (ch === '"') {
      i = skipAndMaskQuotedString(chars, i, '"');
      continue;
    }
    if (ch === "`" && (language === "js-like" || language === "go")) {
      i = skipAndMaskTemplateLiteral(chars, i);
      continue;
    }
    i++;
  }
  return chars.join("");
}
__name(maskCommentsPreserveLayout, "maskCommentsPreserveLayout");
async function scanFileForEnvVarReferences(filePath, maxFileSizeBytes) {
  let fileStat;
  try {
    fileStat = await fs.stat(filePath);
  } catch {
    return [];
  }
  if (!fileStat.isFile() || fileStat.size > maxFileSizeBytes) return [];
  let rawContent;
  try {
    rawContent = await fs.readFile(filePath, "utf-8");
  } catch {
    return [];
  }
  if (!rawContent || rawContent.includes("\0")) return [];
  const extension = path.extname(filePath).toLowerCase();
  const language = LANGUAGE_BY_EXTENSION[extension];
  if (!language) return [];
  const scanContent = maskCommentsPreserveLayout(rawContent, language);
  const newlineIndices = getNewlineIndices(scanContent);
  const references = [];
  for (const pattern of PATTERNS_BY_LANGUAGE[language]) {
    for (const match of scanContent.matchAll(pattern.regex)) {
      const key = match[1];
      if (!key) continue;
      references.push(buildReference(filePath, scanContent, newlineIndices, match.index ?? 0, key, pattern.syntax));
    }
  }
  if (language === "js-like") {
    for (const pattern of JS_DESTRUCTURE_PATTERNS) {
      for (const match of scanContent.matchAll(pattern.regex)) {
        const body = match[1];
        if (!body) continue;
        const bodyOffset = (match.index ?? 0) + match[0].indexOf(body);
        for (const destructured of extractDestructuredKeys(body)) {
          references.push(
            buildReference(
              filePath,
              scanContent,
              newlineIndices,
              bodyOffset + destructured.relativeIndex,
              destructured.key,
              pattern.syntax
            )
          );
        }
      }
    }
  }
  return references;
}
__name(scanFileForEnvVarReferences, "scanFileForEnvVarReferences");
async function scanFilesWithLimit(items, concurrency, worker) {
  if (items.length === 0) return [];
  const results = new Array(items.length);
  let nextIndex = 0;
  async function runWorker() {
    while (true) {
      const currentIndex = nextIndex;
      nextIndex++;
      if (currentIndex >= items.length) return;
      results[currentIndex] = await worker(items[currentIndex]);
    }
  }
  __name(runWorker, "runWorker");
  const workerCount = Math.max(1, Math.min(concurrency, items.length));
  const workers = Array.from({ length: workerCount }, async () => {
    await runWorker();
  });
  await Promise.all(workers);
  return results;
}
__name(scanFilesWithLimit, "scanFilesWithLimit");
async function scanCodeForEnvVars(options = {}, additionalExcludeDirs = []) {
  const cwd = options.cwd || process.cwd();
  const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
  const maxFileSizeBytes = options.maxFileSizeBytes ?? DEFAULT_MAX_FILE_SIZE_BYTES;
  const excludeDirs = /* @__PURE__ */ new Set([
    ...DEFAULT_IGNORED_DIRS,
    ...options.ignoredDirs ?? [],
    ...additionalExcludeDirs
  ]);
  const filePaths = await discoverSourceFiles(cwd, excludeDirs);
  const references = await scanFilesWithLimit(filePaths, concurrency, async (filePath) => {
    return scanFileForEnvVarReferences(filePath, maxFileSizeBytes);
  });
  const flattenedReferences = references.flat();
  const uniqueKeys = [...new Set(flattenedReferences.map((r) => r.key))].sort((a, b) => a.localeCompare(b));
  return {
    keys: uniqueKeys,
    references: flattenedReferences,
    scannedFilesCount: filePaths.length
  };
}
__name(scanCodeForEnvVars, "scanCodeForEnvVars");

export { scanCodeForEnvVars };
//# sourceMappingURL=chunk-C5ZGZA3T.js.map
//# sourceMappingURL=chunk-C5ZGZA3T.js.map