mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-02 05:12:11 +03:00
v3.8.40 cycle integration → main. All test gates green (Unit/Integration/Coverage/Node-compat/Quality-Ratchet). The only red check, 'PR Test Policy', is the test-masking heuristic firing on the cumulative ~57-commit release diff (legitimate assert consolidations already reviewed per-PR — Gemini CLI removal #5246, retired GPT models #5280, provider catalog refreshes); overridden with --admin per the documented release-PR convention. CodeQL/SonarQube advisory scans non-blocking; #5278's code already passed CodeQL on main. Homologated on VPS 192.168.0.15 (v3.8.40 healthy).
235 lines
7.5 KiB
TypeScript
235 lines
7.5 KiB
TypeScript
export interface PreservedBlock {
|
||
placeholder: string;
|
||
content: string;
|
||
kind: string;
|
||
}
|
||
|
||
export interface PreservationOptions {
|
||
preservePatterns?: Array<string | RegExp>;
|
||
}
|
||
|
||
interface CompiledPattern {
|
||
pattern: RegExp;
|
||
kind: string;
|
||
}
|
||
|
||
const SENTINEL_PREFIX = "\u0000OMNI_CAVEMAN";
|
||
|
||
function randomSentinelSeed(): string {
|
||
const bytes = new Uint8Array(8);
|
||
const cryptoLike = globalThis.crypto;
|
||
if (cryptoLike?.getRandomValues) {
|
||
cryptoLike.getRandomValues(bytes);
|
||
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
||
}
|
||
return Math.random().toString(36).slice(2);
|
||
}
|
||
|
||
function ensureGlobal(pattern: RegExp): RegExp {
|
||
const flags = pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`;
|
||
return new RegExp(pattern.source, flags);
|
||
}
|
||
|
||
function compileUserPatterns(patterns: Array<string | RegExp> | undefined): CompiledPattern[] {
|
||
if (!patterns?.length) return [];
|
||
const compiled: CompiledPattern[] = [];
|
||
for (const pattern of patterns) {
|
||
try {
|
||
compiled.push({
|
||
pattern: typeof pattern === "string" ? new RegExp(pattern, "g") : ensureGlobal(pattern),
|
||
kind: "custom",
|
||
});
|
||
} catch {
|
||
// Invalid user regexes are ignored by the hot path. Validation/preview exposes warnings.
|
||
}
|
||
}
|
||
return compiled;
|
||
}
|
||
|
||
function replacePattern(
|
||
text: string,
|
||
pattern: RegExp,
|
||
kind: string,
|
||
addBlock: (content: string, kind: string) => string
|
||
): string {
|
||
pattern.lastIndex = 0;
|
||
return text.replace(pattern, (match) => {
|
||
if (!match) return match;
|
||
if (match.includes(SENTINEL_PREFIX)) return match;
|
||
return addBlock(match, kind);
|
||
});
|
||
}
|
||
|
||
export function findFencedCodeBlocks(text: string): string[] {
|
||
const blocks: string[] = [];
|
||
extractFencedCodeBlocks(text, (content) => {
|
||
blocks.push(content);
|
||
return content;
|
||
});
|
||
return blocks;
|
||
}
|
||
|
||
export function extractPreservedBlocks(
|
||
text: string,
|
||
options: PreservationOptions = {}
|
||
): { text: string; blocks: PreservedBlock[] } {
|
||
const blocks: PreservedBlock[] = [];
|
||
const seed = randomSentinelSeed();
|
||
let counter = 0;
|
||
|
||
const addBlock = (content: string, kind: string): string => {
|
||
const placeholder = `${SENTINEL_PREFIX}_${seed}_${counter}\u0000`;
|
||
blocks.push({ placeholder, content, kind });
|
||
counter++;
|
||
return placeholder;
|
||
};
|
||
|
||
let result = text;
|
||
|
||
result = extractFrontmatter(result, addBlock);
|
||
result = extractFencedCodeBlocks(result, (content) => addBlock(content, "fenced_code"));
|
||
|
||
const builtIns: CompiledPattern[] = [
|
||
{ pattern: /\$\$[\s\S]*?\$\$/g, kind: "math_block" },
|
||
{ pattern: /\\\[[\s\S]*?\\\]/g, kind: "math_block" },
|
||
// #4795: exclude `\` from the catch-all branch so a backslash is only ever
|
||
// consumed by the escape branch `\\.`. The previous `[^$\n]` overlapped with
|
||
// `\\.`, making a run of consecutive backslashes (e.g. unmatched `$` + a
|
||
// Windows path) exponentially ambiguous → catastrophic backtracking (ReDoS).
|
||
{
|
||
pattern: /(?<!\$)\$(?![\s$\d])(?:\\.|[^$\n\\]){1,160}?(?<!\s)\$(?!\$)/g,
|
||
kind: "math_inline",
|
||
},
|
||
{ pattern: /\\begin\{[A-Za-z*]+\}[\s\S]*?\\end\{[A-Za-z*]+\}/g, kind: "latex_block" },
|
||
{ pattern: /^#{1,6}\s+.+$/gm, kind: "markdown_heading" },
|
||
{ pattern: /^\s*\|.*\|\s*$/gm, kind: "markdown_table" },
|
||
{ pattern: /^\s*\|?\s*:?-{3,}:?\s*(?:\|\s*:?-{3,}:?\s*)+\|?\s*$/gm, kind: "markdown_table" },
|
||
{ pattern: /^\s*#(?:set|show|let|import|include)\b.+$/gm, kind: "typst_directive" },
|
||
{ pattern: /`[^`\n]+`/g, kind: "inline_code" },
|
||
{ pattern: /\[[^\]\n]+\]\([^) \n]+(?:\s+"[^"]*")?\)/g, kind: "markdown_link" },
|
||
{ pattern: /\bhttps?:\/\/[^\s)\]"'>]+/gi, kind: "url" },
|
||
{ pattern: /\b[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+\b/g, kind: "const_case" },
|
||
{ pattern: /\bprocess\.env\.[A-Za-z_][A-Za-z0-9_]*\b/g, kind: "env_var" },
|
||
{ pattern: /\$[A-Z_][A-Z0-9_]*\b/g, kind: "env_var" },
|
||
{ pattern: /\b\d+(?:\.\d+){1,3}(?:[-+][A-Za-z0-9.-]+)?\b/g, kind: "version" },
|
||
{ pattern: /\b[a-zA-Z_$][\w$]*(?:\.[a-zA-Z_$][\w$]*)+\(\)?/g, kind: "dotted_identifier" },
|
||
{ pattern: /\b[A-Za-z_$][\w$]*\s*\([^()\n]*\)/g, kind: "function_call" },
|
||
{
|
||
pattern: /(?:^|\s)(?:\.{0,2}\/[A-Za-z0-9_@./-]+|[A-Za-z]:\\[A-Za-z0-9_.\\/-]+)/g,
|
||
kind: "file_path",
|
||
},
|
||
{
|
||
pattern:
|
||
/\b(?:TypeError|ReferenceError|SyntaxError|RangeError|URIError|EvalError|Error|Exception):[^\n]+/g,
|
||
kind: "error_message",
|
||
},
|
||
];
|
||
|
||
for (const { pattern, kind } of [...builtIns, ...compileUserPatterns(options.preservePatterns)]) {
|
||
result = replacePattern(result, ensureGlobal(pattern), kind, addBlock);
|
||
}
|
||
|
||
return { text: result, blocks };
|
||
}
|
||
|
||
/**
|
||
* Wrap explicit literal spans (by offset) into SENTINEL placeholders, reusing
|
||
* the exact placeholder family that every engine already treats as opaque.
|
||
* Spans must be non-overlapping and pre-sorted ascending. Restored via
|
||
* `restorePreservedBlocks`.
|
||
*/
|
||
export function preserveSpans(
|
||
text: string,
|
||
spans: Array<{ start: number; end: number; kind: string }>
|
||
): { text: string; blocks: PreservedBlock[] } {
|
||
if (!spans.length) return { text, blocks: [] };
|
||
const seed = randomSentinelSeed();
|
||
const blocks: PreservedBlock[] = [];
|
||
let result = "";
|
||
let cursor = 0;
|
||
let counter = 0;
|
||
for (const span of spans) {
|
||
if (span.start < cursor || span.end > text.length || span.start >= span.end) continue;
|
||
const placeholder = `${SENTINEL_PREFIX}_${seed}_${counter} |