"use strict";
/**
 * Inline allowlist annotation gather pass.
 *
 * Walks the source tree looking for `<lineComment> dxkit-allow:<category>`
 * comments and records each occurrence as a `(file, line, category)`
 * tuple. The `stale-allow` producer uses this list together with the
 * current scan's secret/code/config findings to detect orphaned
 * annotations — annotations whose underlying finding is no longer
 * present, which the developer should remove.
 *
 * Architectural posture:
 *
 *   - File walk goes through the canonical `walkSourceFiles` helper
 *     so `.gitignore` + `.dxkit-ignore` + bundled defaults are
 *     honored uniformly. No custom recursion / exclusion logic
 *     (per CLAUDE.md G_v4_7).
 *   - Per-language comment marker comes from each pack's
 *     `LanguageSupport.commentSyntax` via the inline annotation
 *     parser. No hardcoded `'//'` / `'#'` literals in this module
 *     (arch-check rule 2 enforces).
 *   - Annotation parsing reuses `parseAnnotation` from `inline.ts`
 *     — single source of grammar truth.
 *
 * Test files ARE walked (intentional — annotations often live in
 * test fixtures suppressing scanner findings against deliberate
 * placeholder credentials). Auto-generated files are NOT walked
 * (the developer doesn't author annotations in generated code, and
 * `walkSourceFiles` already excludes them by default).
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.gatherInlineAllowlistAnnotations = gatherInlineAllowlistAnnotations;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const languages_1 = require("../languages");
const walk_source_files_1 = require("../analyzers/tools/walk-source-files");
const inline_1 = require("./inline");
/**
 * Walk source files under `cwd` and collect every inline allowlist
 * annotation occurrence. Cheap on small repos; on large ones the
 * `walkSourceFiles` cache amortizes the cost across multiple gather
 * passes within a single baseline-create run.
 *
 * Returns occurrences in stable order (file path lexicographic, then
 * line ascending) so downstream deterministic-output requirements
 * are satisfied automatically.
 */
function gatherInlineAllowlistAnnotations(cwd, opts = {}) {
    const includeTests = opts.includeTests ?? true;
    const files = (0, walk_source_files_1.walkSourceFiles)(cwd, { includeTests });
    const out = [];
    // Build an extension → language lookup once per call. Cheap; the
    // LANGUAGES registry is small (8 packs today).
    const langByExt = buildLanguageByExtension();
    for (const relPath of files) {
        const ext = path.extname(relPath).toLowerCase();
        const lang = langByExt.get(ext);
        if (!lang || !lang.commentSyntax)
            continue;
        const abs = path.join(cwd, relPath);
        let raw;
        try {
            raw = fs.readFileSync(abs, 'utf8');
        }
        catch {
            // File disappeared mid-walk (race) or unreadable — skip.
            continue;
        }
        // Fast path: source has no `dxkit-allow:` substring at all.
        // Avoids per-line regex on the vast majority of files.
        if (!raw.includes('dxkit-allow:'))
            continue;
        const lines = raw.split(/\r?\n/);
        for (let i = 0; i < lines.length; i++) {
            const line = lines[i];
            const parsed = (0, inline_1.parseAnnotation)(line, lang);
            if (!parsed)
                continue;
            // `parseAnnotation` returns the category + reason but not
            // the position. Determine position by inspecting whether the
            // line is standalone (only whitespace + comment marker + body)
            // or has source code preceding the comment.
            const position = (0, inline_1.isStandaloneAnnotationLine)(line, lang)
                ? 'above'
                : 'same-line';
            out.push({
                file: relPath,
                line: i + 1,
                category: parsed.category,
                position,
            });
        }
    }
    return out;
}
// ─── Internals ────────────────────────────────────────────────────────────
function buildLanguageByExtension() {
    const map = new Map();
    for (const lang of languages_1.LANGUAGES) {
        for (const ext of lang.sourceExtensions) {
            const lower = ext.toLowerCase();
            // First pack wins on duplicate extensions (none today, but
            // the deterministic-order guarantee survives a future overlap).
            if (!map.has(lower))
                map.set(lower, lang);
        }
    }
    return map;
}
//# sourceMappingURL=gather.js.map