/** * Shared "find literal text, replace it" matching engine for content-patch * style edits (extensions, slides). Pure and dependency-free so both server * packages and template code can import it without pulling in server code. * * Only an EXACT substring match is ever applied. A whitespace-flexible match * (runs of whitespace, including CRLF/LF, collapsed to a single space before * comparing) is never spliced in — collapsing whitespace before applying an * edit risks silently rewriting semantically significant whitespace * (`
`, `white-space: pre`, embedded JS/CSS string literals). Instead a
* whitespace-flexible hit is reported as the top "not_found" candidate, with
* its exact current bytes, so the caller's next attempt can copy the real
* text verbatim. Cheap token-overlap window candidates fill any remaining
* slots when nothing matches at all, even loosely.
*
* A find that matches more than once is reported as "ambiguous" instead of
* silently applying to the first hit, unless the caller passed `occurrence`
* or `all` to say which one(s) it wants.
*
* Never throws: every outcome is a discriminated result. Callers decide what
* "not found" or "ambiguous" should mean for their op (fail loudly, no-op,
* etc). An `occurrence` that isn't a positive integer is reported as
* "invalid_occurrence" rather than silently coerced to 1. An `occurrence`
* past the number of real matches is reported as "occurrence_out_of_range" —
* a DIFFERENT reason than "not_found" — because matches exist; a caller's
* zero-matches no-op (`expectedMatches: 0` / `required: false`) must not
* treat "found some, just not that many" the same as "found none".
*/
export interface TargetedTextEditOptions {
occurrence?: number;
all?: boolean;
}
export interface TargetedMatch {
index: number;
end: number;
line: number;
text: string;
}
export interface TargetedCandidate {
line: number;
text: string;
similarity: number;
}
export interface TargetedAmbiguousMatch {
line: number;
snippet: string;
}
export type TargetedMatchFailure = {
ok: false;
reason: "not_found";
candidates: TargetedCandidate[];
} | {
ok: false;
reason: "ambiguous";
matches: TargetedAmbiguousMatch[];
} | {
ok: false;
reason: "invalid_occurrence";
occurrence: number;
} | {
ok: false;
reason: "occurrence_out_of_range";
occurrence: number;
matchCount: number;
matches: TargetedMatch[];
};
export type TargetedMatchesResult = {
ok: true;
matches: TargetedMatch[];
} | TargetedMatchFailure;
export type TargetedReplaceResult = {
ok: true;
content: string;
matchedText: string;
index: number;
matchCount: number;
} | TargetedMatchFailure;
export declare function findTargetedMatches(content: string, find: string, opts?: TargetedTextEditOptions): TargetedMatchesResult;
export declare function applyTargetedReplace(content: string, find: string, replacement: string, opts?: TargetedTextEditOptions): TargetedReplaceResult;