// engine/text-signal-boundaries.ts — Boundary-aware keyword matching for advisory signals.
const NEGATION_WINDOW_WORDS = 5;
const NEGATION_TERMS = new Set([
    'no',
    'not',
    'without',
    'avoid',
    'exclude',
    'excluding',
    'except',
    'skip',
    'remove',
    'disable',
    'disallow',
    'never',
]);
const OUT_OF_SCOPE_RE = /\b(?:out\s+of\s+scope|not\s+in\s+scope|no\s+scope|do\s+not|don't)\b/i;
/** Remove fenced code and quoted examples before keyword-driven inference. */
export function stripNonContractText(text) {
    return text
        .replace(/```[\s\S]*?```/g, ' ')
        .replace(/`[^`\n]+`/g, ' ')
        .replace(/"[^"\n]+"/g, ' ')
        .replace(/'[^'\n]+'/g, ' ');
}
function wordTokensBefore(text, index) {
    return text
        .slice(Math.max(0, index - 160), index)
        .toLowerCase()
        .split(/[^a-z0-9]+/i)
        .filter(Boolean);
}
function isNegatedAt(text, index) {
    const before = wordTokensBefore(text, index);
    const recent = before.slice(-NEGATION_WINDOW_WORDS);
    return recent.some((word) => NEGATION_TERMS.has(word));
}
function isOutOfScopeLine(line, indexInLine) {
    return OUT_OF_SCOPE_RE.test(line.slice(0, Math.max(indexInLine, 0) + 80));
}
/** True only when a regex match appears in contract prose and is not negated/excluded. */
export function hasAffirmedMatch(text, pattern) {
    const cleaned = stripNonContractText(text);
    const flags = pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g`;
    const re = new RegExp(pattern.source, flags);
    let match = re.exec(cleaned);
    while (match) {
        const index = match.index;
        const lineStart = cleaned.lastIndexOf('\n', index) + 1;
        const lineEnd = cleaned.indexOf('\n', index);
        const line = cleaned.slice(lineStart, lineEnd === -1 ? cleaned.length : lineEnd);
        if (!isNegatedAt(cleaned, index) && !isOutOfScopeLine(line, index - lineStart)) {
            return true;
        }
        match = re.exec(cleaned);
    }
    return false;
}
export function hasAnyAffirmedMatch(text, patterns) {
    return patterns.some((pattern) => hasAffirmedMatch(text, pattern));
}
//# sourceMappingURL=text-signal-boundaries.js.map