import { z } from 'zod';
import { InvalidInputError } from '../errors.js';
import { estimateTokenCount } from '../tokens.js';
import { abortIfNeeded, createYielder } from '../utils.js';
import { normalizePath } from './languages.js';
import { Utf8SourceIndex } from './source-span.js';
import { AST_GREP_ENGINE, parseWithAstGrep } from './ast-grep.js';
import { openTreeSitterParseCache, } from './tree-sitter.js';
import { compactSourceText, filesForLanguage, indexJoinNodesByPath, joinSpanToNode, matchesLanguagePath, matchesScope, normalizeScopePrefixes, resolveStructuralLanguage, structuralLanguages, } from './structural.js';
/**
 * Pattern/rule matching over the vendored ast-grep engine.
 *
 * This is an exploration surface in the same family as `occ code tsquery`:
 * read-only, bounded, honesty-annotated, and joined to indexed node ids by
 * span containment. It matches on the same web-tree-sitter parse trees the
 * rest of OCC uses. It is not evidence: nothing here participates in entity
 * lineage, CST proofs, or any persisted receipt.
 */
const RULE_KEYS = [
    'pattern',
    'kind',
    'regex',
    'nthChild',
    'range',
    'inside',
    'has',
    'precedes',
    'follows',
    'all',
    'any',
    'not',
    'matches',
];
const DEFAULT_RULE_LIMIT = 50;
const DEFAULT_MAP_TOKENS = 4_096;
const DEFAULT_MAX_RULE_BYTES = 65_536;
const DEFAULT_MAX_SOURCE_BYTES = 2_097_152;
const MAX_CONFIGURABLE_SOURCE_BYTES = 16_777_216;
const DEFAULT_MAX_MATCHES_PER_FILE = 10_000;
const MAX_MATCH_SCAN = 100_000;
const DEFAULT_MAX_DIAGNOSTICS = 100;
const DEFAULT_MAX_JOIN_CANDIDATES = 20;
const DEFAULT_MAX_JOIN_NODES = 10_000;
const MAX_META_VARIABLE_NAMES = 16;
function positiveInteger(value, fallback, label, maximum) {
    const resolved = value ?? fallback;
    if (!Number.isInteger(resolved) || resolved <= 0) {
        throw new InvalidInputError(`${label} must be a positive integer.`);
    }
    if (maximum !== undefined && resolved > maximum) {
        throw new InvalidInputError(`${label} must not exceed ${maximum}.`);
    }
    return resolved;
}
function validateRuleObject(rule, label) {
    if (rule === null || typeof rule !== 'object' || Array.isArray(rule)) {
        throw new InvalidInputError(`${label} must be a JSON object.`);
    }
    const record = rule;
    const keys = Object.keys(record);
    if (keys.length === 0)
        throw new InvalidInputError(`${label} must contain at least one rule key.`);
    const unknownKeys = keys.filter(key => !RULE_KEYS.includes(key));
    if (unknownKeys.length > 0) {
        throw new InvalidInputError(`${label} has unknown key(s): ${unknownKeys.join(', ')}. Expected: ${RULE_KEYS.join(', ')}.`);
    }
    return record;
}
/**
 * True when any `pattern` clause anywhere in the value — top level, nested
 * relational/composite clauses, constraints, or utils — is a plain string.
 */
function containsStringPatternClause(value) {
    if (Array.isArray(value))
        return value.some(containsStringPatternClause);
    if (value === null || typeof value !== 'object')
        return false;
    const record = value;
    if (typeof record.pattern === 'string')
        return true;
    return Object.values(record).some(containsStringPatternClause);
}
/**
 * Offset translation for engine positions.
 *
 * The engine's `Pos.index` values are the underlying web-tree-sitter node
 * indices, which for string-backed parses are UTF-16 code units — verified
 * empirically against astral characters, where UTF-16 and code-point
 * interpretations diverge. They slot straight into String#slice; canonical
 * index spans are UTF-8 bytes and are converted exactly once here. Lines and
 * columns are recomputed from the same offsets so every reported coordinate
 * shares one provenance.
 */
class EngineSourceOffsets {
    source;
    utf8;
    lineStarts;
    constructor(source) {
        this.source = source;
        this.utf8 = new Utf8SourceIndex(source);
        this.lineStarts = [0];
        for (let offset = 0; offset < source.length; offset += 1) {
            if (source.charCodeAt(offset) === 10)
                this.lineStarts.push(offset + 1);
        }
    }
    utf16(engineIndex) {
        return Math.max(0, Math.min(this.source.length, engineIndex));
    }
    byte(utf16Offset) {
        return this.utf8.byteOffset(utf16Offset);
    }
    /** 1-based line and 1-based UTF-16 column for a UTF-16 offset. */
    position(utf16Offset) {
        let low = 0;
        let high = this.lineStarts.length - 1;
        while (low < high) {
            const middle = (low + high + 1) >> 1;
            if ((this.lineStarts[middle] ?? 0) <= utf16Offset)
                low = middle;
            else
                high = middle - 1;
        }
        return { line: low + 1, column: utf16Offset - (this.lineStarts[low] ?? 0) + 1 };
    }
}
/**
 * Best-effort discovery of capture names so matches can report them: pattern
 * text mentions `$NAME`/`$$$NAME`, and every constraint key names one. Names
 * starting with `_` are non-capturing by ast-grep convention and are skipped.
 */
function collectMetaVariableNames(config) {
    const single = new Set();
    const multi = new Set();
    const serialized = JSON.stringify({ rule: config.rule, utils: config.utils ?? null });
    for (const match of serialized.matchAll(/(\${1,3})([A-Z_][A-Z0-9_]*)/g)) {
        const name = match[2] ?? '';
        if (name.startsWith('_'))
            continue;
        if (match[1] === '$$$')
            multi.add(name);
        else
            single.add(name);
    }
    for (const name of Object.keys(config.constraints ?? {})) {
        if (!name.startsWith('_'))
            single.add(name);
    }
    const clipped = single.size > MAX_META_VARIABLE_NAMES || multi.size > MAX_META_VARIABLE_NAMES;
    return {
        single: [...single].sort().slice(0, MAX_META_VARIABLE_NAMES),
        multi: [...multi].sort().slice(0, MAX_META_VARIABLE_NAMES),
        clipped,
    };
}
function metaVariableFrom(node, offsets) {
    const range = node.range();
    const startUtf16 = offsets.utf16(range.start.index);
    const endUtf16 = offsets.utf16(range.end.index);
    const start = offsets.position(startUtf16);
    const end = offsets.position(endUtf16);
    return {
        text: compactSourceText(offsets.source, startUtf16, endUtf16),
        startLine: start.line,
        startColumn: start.column,
        endLine: end.line,
        endColumn: end.column,
    };
}
function metaVariablesFor(matchNode, names, offsets) {
    const single = {};
    const multi = {};
    for (const name of names.single) {
        const node = matchNode.getMatch(name);
        if (!node)
            continue;
        try {
            single[name] = metaVariableFrom(node, offsets);
        }
        finally {
            node.free();
        }
    }
    for (const name of names.multi) {
        const nodes = matchNode.getMultipleMatches(name);
        if (nodes.length === 0)
            continue;
        const values = [];
        for (const node of nodes) {
            try {
                values.push(metaVariableFrom(node, offsets));
            }
            finally {
                node.free();
            }
        }
        multi[name] = values;
    }
    const hasSingle = Object.keys(single).length > 0;
    const hasMulti = Object.keys(multi).length > 0;
    if (!hasSingle && !hasMulti)
        return undefined;
    return {
        ...(hasSingle ? { single } : {}),
        ...(hasMulti ? { multi } : {}),
    };
}
function engineErrorMessage(error) {
    return error instanceof Error ? error.message : String(error);
}
/**
 * @internal Own and release every handle, including those not yet visited when
 * a callback throws or returns false to stop iteration.
 */
export async function consumeOwnedAstGrepHandles(handles, visit) {
    let nextIndex = 0;
    try {
        while (nextIndex < handles.length) {
            const index = nextIndex;
            const handle = handles[index];
            nextIndex += 1;
            if (!handle)
                continue;
            let shouldContinue = true;
            try {
                shouldContinue = (await visit(handle, index)) !== false;
            }
            finally {
                handle.free();
            }
            if (!shouldContinue)
                break;
        }
    }
    finally {
        while (nextIndex < handles.length) {
            const handle = handles[nextIndex];
            nextIndex += 1;
            handle?.free();
        }
    }
}
/**
 * Compile the rule against an empty document once, so a malformed rule fails
 * with one clear error before any file is parsed.
 */
async function preflightRule(language, config) {
    const probe = await parseWithAstGrep(language, '');
    const tree = probe.getInnerTree();
    try {
        const root = probe.root();
        try {
            for (const node of root.findAll(config))
                node.free();
        }
        finally {
            root.free();
        }
    }
    catch (error) {
        throw new InvalidInputError(`ast-grep rejected the rule: ${engineErrorMessage(error)}`, { cause: error });
    }
    finally {
        probe.free();
        tree.delete();
    }
}
export async function queryCodePattern(index, options, runtime = {}) {
    const treeCache = openTreeSitterParseCache(index);
    try {
        return await queryCodePatternWithCache(index, options, runtime, treeCache);
    }
    finally {
        treeCache.close();
    }
}
async function queryCodePatternWithCache(index, options, runtime, treeCache) {
    const language = resolveStructuralLanguage(options.language);
    if (!language) {
        throw new InvalidInputError(`Unsupported pattern language "${options.language}". Expected one of: ${structuralLanguages()
            .map(item => item.name)
            .join(', ')}`);
    }
    if ((options.pattern !== undefined) === (options.rule !== undefined)) {
        throw new InvalidInputError('Provide exactly one matcher: pattern or rule.');
    }
    if (options.pattern !== undefined && options.pattern.trim().length === 0) {
        throw new InvalidInputError('Pattern must not be empty.');
    }
    const rule = options.rule !== undefined ? validateRuleObject(options.rule, 'Rule') : { pattern: options.pattern };
    const limit = positiveInteger(options.limit, DEFAULT_RULE_LIMIT, 'Pattern limit');
    const mapTokens = positiveInteger(options.mapTokens, DEFAULT_MAP_TOKENS, 'Pattern mapTokens');
    const maxRuleBytes = positiveInteger(options.maxRuleBytes, DEFAULT_MAX_RULE_BYTES, 'Pattern maxRuleBytes');
    const maxSourceBytes = positiveInteger(options.maxSourceBytes, DEFAULT_MAX_SOURCE_BYTES, 'Pattern maxSourceBytes', MAX_CONFIGURABLE_SOURCE_BYTES);
    const maxMatchesPerFile = positiveInteger(options.maxMatchesPerFile, DEFAULT_MAX_MATCHES_PER_FILE, 'Pattern maxMatchesPerFile');
    const maxDiagnostics = positiveInteger(options.maxDiagnostics, DEFAULT_MAX_DIAGNOSTICS, 'Pattern maxDiagnostics');
    const maxJoinCandidates = positiveInteger(options.maxJoinCandidates, DEFAULT_MAX_JOIN_CANDIDATES, 'Pattern maxJoinCandidates');
    const maxJoinNodes = positiveInteger(options.maxJoinNodes, DEFAULT_MAX_JOIN_NODES, 'Pattern maxJoinNodes');
    const scopePrefixes = normalizeScopePrefixes(options.files);
    const config = {
        rule,
        ...(options.constraints ? { constraints: options.constraints } : {}),
        ...(options.utils ? { utils: options.utils } : {}),
    };
    if (language.name === 'php' && containsStringPatternClause(config)) {
        // The vendored php grammar parses a standalone pattern as inline HTML
        // because it expects an opening <?php tag, so a string pattern compiles
        // cleanly yet can never match — an empty result would be a fabricated
        // "verified absent" claim. Refuse instead of reporting full coverage.
        throw new InvalidInputError('PHP string patterns cannot match: the php grammar parses bare pattern text as inline HTML. ' +
            'Use an object pattern {"context": "<?php ...", "selector": "<node-kind>"} or kind/regex/relational clauses instead.');
    }
    let serializedConfig;
    try {
        serializedConfig = JSON.stringify(config);
    }
    catch (error) {
        throw new InvalidInputError('Rule, constraints, and utils must be JSON-serializable.', { cause: error });
    }
    const ruleBytes = Buffer.byteLength(serializedConfig, 'utf8');
    if (ruleBytes > maxRuleBytes) {
        throw new InvalidInputError(`Rule exceeds the ${maxRuleBytes}-byte rule limit.`);
    }
    if (index.contentMode !== 'full') {
        throw new InvalidInputError('Pattern queries require a full-content code index.');
    }
    abortIfNeeded(options.signal);
    await preflightRule(language, config);
    abortIfNeeded(options.signal);
    const files = filesForLanguage(index, language).filter(file => matchesScope(file.relativePath, scopePrefixes));
    const eligibleSkipped = index.skippedFiles.filter(file => matchesLanguagePath(file.relativePath, language) && matchesScope(file.relativePath, scopePrefixes)).length;
    const nodesByPath = indexJoinNodesByPath(index);
    const metaNames = collectMetaVariableNames(config);
    const countTokens = options.countTokens ?? estimateTokenCount;
    const matches = [];
    const diagnostics = [];
    let diagnosticsOmitted = 0;
    const addDiagnostic = (message) => {
        if (diagnostics.length < maxDiagnostics)
            diagnostics.push(message);
        else
            diagnosticsOmitted += 1;
    };
    const yielder = createYielder(options.cooperative, options.signal);
    let totalMatches = 0;
    let tokensUsed = 0;
    let filesParsed = 0;
    let filesQueried = 0;
    let filesVisited = 0;
    let runtimeFilesSkipped = 0;
    let scanBoundHit = false;
    let budgetEarlyExitHit = false;
    let unjoinedMatches = 0;
    let ambiguousSpanJoins = 0;
    let syntaxErrorFiles = 0;
    let parserCoveragePartialFiles = 0;
    let joinSearchTruncated = 0;
    let fileMatchLimitFiles = 0;
    if (metaNames.clipped) {
        addDiagnostic(`Meta-variable reporting is capped at ${MAX_META_VARIABLE_NAMES} names; extra captures are omitted.`);
    }
    if (index.truncated) {
        addDiagnostic(`The code index was truncated by ${index.truncated.reason}; ${index.truncated.droppedFiles} of ` +
            `${index.truncated.totalFiles} discovered file(s) were not indexed, so absence claims are incomplete.`);
    }
    if (eligibleSkipped > 0) {
        addDiagnostic(`${eligibleSkipped} matching file(s) were skipped while the code index was built.`);
    }
    for (const file of files) {
        if (scanBoundHit || budgetEarlyExitHit)
            break;
        filesVisited += 1;
        abortIfNeeded(options.signal);
        await yielder.tick();
        if (file.content === undefined) {
            runtimeFilesSkipped += 1;
            addDiagnostic(`${file.relativePath} has no retained source content and was not matched.`);
            continue;
        }
        const content = file.content;
        const sourceBytes = Buffer.byteLength(content, 'utf8');
        if (sourceBytes > maxSourceBytes) {
            runtimeFilesSkipped += 1;
            addDiagnostic(`${file.relativePath} exceeds the ${maxSourceBytes}-byte pattern source limit.`);
            continue;
        }
        let sgRoot;
        try {
            sgRoot = await parseWithAstGrep(language, content);
        }
        catch (error) {
            runtimeFilesSkipped += 1;
            addDiagnostic(`${file.relativePath} could not be parsed by the ast-grep engine: ${engineErrorMessage(error)}`);
            continue;
        }
        const innerTree = sgRoot.getInnerTree();
        let treeLease;
        try {
            treeLease = await treeCache.adopt(language.grammar, content, innerTree);
            const provenanceRecovered = file.provenance?.syntax.status === 'recovered';
            if (innerTree.rootNode.hasError || provenanceRecovered) {
                syntaxErrorFiles += 1;
                addDiagnostic(`${file.relativePath} contains syntax recovery; matches may be incomplete.`);
            }
            const parserCoverageIncomplete = file.provenance !== undefined &&
                (file.provenance.capabilities.definitions !== 'full' || file.provenance.capabilities.exactSpans !== 'full');
            if (parserCoverageIncomplete) {
                parserCoveragePartialFiles += 1;
                addDiagnostic(`${file.relativePath} has partial index-parser definition/span coverage; match joins may be incomplete.`);
            }
            const rootNode = sgRoot.root();
            try {
                // The rule already compiled in preflight, so a per-file engine failure
                // here is unexpected — but the engine is a WASM sandbox and one
                // pathological file must not take down the whole query. Degrade to a
                // diagnostic and partial coverage, matching per-file parse failures.
                let fileMatches;
                try {
                    fileMatches = runtime.findAll ? runtime.findAll(rootNode, config) : rootNode.findAll(config);
                }
                catch (error) {
                    runtimeFilesSkipped += 1;
                    addDiagnostic(`${file.relativePath} failed inside the ast-grep engine: ${engineErrorMessage(error)}`);
                    continue;
                }
                filesParsed += 1;
                filesQueried += 1;
                const offsets = new EngineSourceOffsets(content);
                const indexedNodes = nodesByPath.get(normalizePath(file.relativePath)) ?? [];
                let processedInFile = 0;
                let fileMatchLimitHit = false;
                await consumeOwnedAstGrepHandles(fileMatches, async (matchNode) => {
                    if (scanBoundHit || budgetEarlyExitHit)
                        return false;
                    if (processedInFile >= maxMatchesPerFile) {
                        fileMatchLimitHit = true;
                        return true;
                    }
                    if (totalMatches >= MAX_MATCH_SCAN) {
                        scanBoundHit = true;
                        addDiagnostic(`Match scan stopped at the ${MAX_MATCH_SCAN}-match global scan bound; omitted is a lower bound.`);
                        return false;
                    }
                    if ((totalMatches & 255) === 0) {
                        abortIfNeeded(options.signal);
                        await yielder.tick();
                    }
                    processedInFile += 1;
                    totalMatches += 1;
                    const range = matchNode.range();
                    const startUtf16 = offsets.utf16(range.start.index);
                    const endUtf16 = offsets.utf16(range.end.index);
                    const start = offsets.position(startUtf16);
                    const end = offsets.position(endUtf16);
                    const span = {
                        startByte: offsets.byte(startUtf16),
                        endByte: offsets.byte(endUtf16),
                        startLine: start.line,
                        startColumn: start.column,
                        endLine: end.line,
                        endColumn: end.column,
                    };
                    const joined = joinSpanToNode(indexedNodes, span, maxJoinNodes, maxJoinCandidates);
                    if (joined.searchTruncated)
                        joinSearchTruncated += 1;
                    if (!joined.node && !joined.ambiguous)
                        unjoinedMatches += 1;
                    if (joined.ambiguous)
                        ambiguousSpanJoins += 1;
                    if (matches.length >= limit)
                        return true;
                    const match = {
                        relativePath: normalizePath(file.relativePath),
                        kind: matchNode.kind(),
                        text: compactSourceText(content, startUtf16, endUtf16),
                        startLine: start.line,
                        startColumn: start.column,
                        endLine: end.line,
                        endColumn: end.column,
                        metaVariables: metaVariablesFor(matchNode, metaNames, offsets),
                        nodeId: joined.node?.id,
                        symbol: joined.node?.name,
                        joinStatus: joined.node ? 'resolved' : joined.ambiguous ? 'ambiguous' : 'unresolved',
                        joinBasis: joined.basis,
                        nodeCandidates: joined.ambiguous ? joined.candidates : undefined,
                    };
                    const tokenCost = countTokens(JSON.stringify(match));
                    if (!Number.isFinite(tokenCost) || tokenCost < 0) {
                        throw new Error('Pattern token counter returned an invalid value.');
                    }
                    if (tokensUsed + tokenCost > mapTokens) {
                        if (options.budgetEarlyExit) {
                            budgetEarlyExitHit = true;
                            addDiagnostic('Budget early exit stopped at the first match that did not fit; later smaller matches were not considered, so totalMatches and omitted are lower bounds.');
                            return false;
                        }
                        return true;
                    }
                    matches.push(match);
                    tokensUsed += tokenCost;
                    return true;
                });
                if (fileMatchLimitHit) {
                    fileMatchLimitFiles += 1;
                    addDiagnostic(`${file.relativePath} hit the ${maxMatchesPerFile}-match per-file limit; its match count is a lower bound.`);
                }
            }
            finally {
                rootNode.free();
            }
        }
        finally {
            // web-tree-sitter 0.26 has no finalizer: the engine's inner tree must be
            // released only after the engine-side handle is freed. The invocation
            // cache now owns deletion and may retain it for another bounded borrow.
            sgRoot.free();
            if (treeLease)
                treeLease.release();
            else
                innerTree.delete();
        }
    }
    const unvisitedFiles = scanBoundHit || budgetEarlyExitHit ? Math.max(0, files.length - filesVisited) : 0;
    const filesSkipped = eligibleSkipped + runtimeFilesSkipped + unvisitedFiles;
    const omitted = Math.max(0, totalMatches - matches.length);
    const incomplete = scanBoundHit ||
        budgetEarlyExitHit ||
        filesSkipped > 0 ||
        syntaxErrorFiles > 0 ||
        parserCoveragePartialFiles > 0 ||
        joinSearchTruncated > 0 ||
        fileMatchLimitFiles > 0 ||
        diagnosticsOmitted > 0 ||
        index.truncated !== undefined;
    const truncated = omitted > 0 || scanBoundHit || budgetEarlyExitHit || fileMatchLimitFiles > 0 || unvisitedFiles > 0;
    return {
        status: 'resolved',
        language: language.name,
        grammar: language.grammar,
        engine: { name: AST_GREP_ENGINE.name, version: AST_GREP_ENGINE.version },
        rule,
        ...(scopePrefixes ? { scope: { files: scopePrefixes } } : {}),
        matches,
        totalMatches,
        omitted,
        truncated,
        filesParsed,
        filesSkipped,
        mapTokens,
        tokensUsed,
        tokenizer: options.tokenizer ?? 'heuristic',
        diagnostics,
        honesty: {
            freshness: 'unverified',
            stalenessReason: 'CodebaseIndex does not carry a filesystem freshness marker.',
            coverage: incomplete ? 'partial' : 'full',
            truncated,
            omitted,
            filesSkipped,
            unjoinedMatches,
            ambiguousSpanJoins,
            eligibleFiles: files.length + eligibleSkipped,
            filesQueried,
            syntaxErrorFiles,
            parserCoveragePartialFiles,
            joinSearchTruncated,
            diagnosticsOmitted,
            matchScanLimitHit: scanBoundHit,
            fileMatchLimitFiles,
            ...(budgetEarlyExitHit ? { budgetEarlyExitHit: true } : {}),
        },
    };
}
/** One user-defined fact predicate: an ast-grep rule with an id and a message. */
export const CustomCodeFactRuleSchema = z
    .object({
    id: z
        .string()
        .min(1)
        .max(64)
        .regex(/^[a-z0-9][a-z0-9-]*$/, 'rule ids are lowercase slugs'),
    language: z.string().min(1),
    message: z.string().max(1024).optional(),
    files: z.array(z.string().min(1)).max(16).optional(),
    rule: z.record(z.string(), z.unknown()),
    constraints: z.record(z.string(), z.unknown()).optional(),
    utils: z.record(z.string(), z.unknown()).optional(),
})
    .strict();
export const CodeRulesManifestSchema = z
    .object({
    rules: z.array(CustomCodeFactRuleSchema).min(1).max(32),
})
    .strict();
export function parseCodeRulesManifest(raw) {
    let parsed;
    try {
        parsed = JSON.parse(raw);
    }
    catch (error) {
        throw new InvalidInputError(`Rules manifest is not valid JSON: ${engineErrorMessage(error)}`, { cause: error });
    }
    const manifest = CodeRulesManifestSchema.safeParse(parsed);
    if (!manifest.success) {
        throw new InvalidInputError(`Invalid rules manifest: ${manifest.error.message}`);
    }
    const seen = new Set();
    for (const rule of manifest.data.rules) {
        if (seen.has(rule.id))
            throw new InvalidInputError(`Duplicate rule id "${rule.id}" in rules manifest.`);
        seen.add(rule.id);
    }
    return manifest.data;
}
/**
 * Evaluate user-defined rule predicates against the index, one bounded result
 * per rule. Like `layer-violations`, the user supplies the rules; OCC refuses
 * to infer them.
 */
export async function queryCustomCodeFacts(index, manifest, options = {}) {
    const rules = [];
    for (const rule of manifest.rules) {
        abortIfNeeded(options.signal);
        const result = await queryCodePattern(index, {
            language: rule.language,
            rule: rule.rule,
            constraints: rule.constraints,
            utils: rule.utils,
            files: rule.files,
            limit: options.limit,
            mapTokens: options.mapTokens,
            tokenizer: options.tokenizer,
            countTokens: options.countTokens,
            budgetEarlyExit: options.budgetEarlyExit,
            signal: options.signal,
            cooperative: options.cooperative,
            maxSourceBytes: options.maxSourceBytes,
        });
        rules.push({
            id: rule.id,
            ...(rule.message ? { message: rule.message } : {}),
            result,
        });
    }
    return {
        status: 'resolved',
        engine: { name: AST_GREP_ENGINE.name, version: AST_GREP_ENGINE.version },
        rules,
    };
}
//# sourceMappingURL=rules.js.map