// src/detect/adapters/parse-guard.ts
var MAX_AST_FILE_BYTES = 1 * 1024 * 1024;
var MAX_AST_PARSE_DEPTH = 5e3;
var MAX_AST_PARSE_MS = 2e3;
function isParsableSource(source, sizeBytes) {
  const bytes = sizeBytes ?? Buffer.byteLength(source, "utf-8");
  if (bytes > MAX_AST_FILE_BYTES) {
    return {
      reason: "size-cap",
      detail: `${bytes} bytes > ${MAX_AST_FILE_BYTES} cap`
    };
  }
  let depth = 0;
  let maxDepth = 0;
  for (let i = 0; i < source.length; i++) {
    const c = source.charCodeAt(i);
    if (c === 0) {
      return { reason: "control-bytes", detail: "NUL byte at offset " + i };
    }
    if (c === 40 || c === 91 || c === 123) {
      depth++;
      if (depth > maxDepth) maxDepth = depth;
      if (depth > MAX_AST_PARSE_DEPTH) {
        return {
          reason: "depth-cap",
          detail: `nesting depth exceeded ${MAX_AST_PARSE_DEPTH}`
        };
      }
    } else if (c === 41 || c === 93 || c === 125) {
      depth = depth > 0 ? depth - 1 : 0;
    }
  }
  return null;
}
function withParseDeadline(fn, filePath, budgetMs = MAX_AST_PARSE_MS) {
  const start = Date.now();
  const value = fn();
  const elapsedMs = Date.now() - start;
  const overBudget = elapsedMs > budgetMs;
  if (overBudget) {
    process.stderr.write(
      `[massu/ast] WARN: parse of ${filePath} took ${elapsedMs}ms (budget ${budgetMs}ms) \u2014 file may be adversarial. (Phase 3.5 mitigation)
`
    );
  }
  return { value, elapsedMs, overBudget };
}
export {
  MAX_AST_FILE_BYTES,
  MAX_AST_PARSE_DEPTH,
  MAX_AST_PARSE_MS,
  isParsableSource,
  withParseDeadline
};
