MCPcopy
hub / github.com/vercel-labs/just-bash / UserRegex

Class UserRegex

packages/just-bash/src/regex/user-regex.ts:73–499  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

71 * Uses RE2 for linear-time matching, providing ReDoS protection.
72 */
73export class UserRegex implements RegexLike {
74 private readonly _re2: RE2JS;
75 private readonly _pattern: string;
76 private readonly _flags: string;
77 private readonly _global: boolean;
78 private readonly _ignoreCase: boolean;
79 private readonly _multiline: boolean;
80 private _lastIndex = 0;
81 // Cache native RegExp for compatibility - created lazily
82 private _nativeRegex: RegExp | null = null;
83 // Reusable RE2 Matcher to avoid per-call allocation in tight grep loops.
84 // Matcher allocation dominates regex.test/exec cost when called once per line
85 // across thousands of lines. We mutate charSequence in-place (not resetMatcherInput,
86 // which is broken in re2js 1.2.1 — see acquireMatcher).
87 private _matcher: ReturnType<RE2JS["matcher"]> | null = null;
88 private _matcherInput: string | null = null;
89
90 private acquireMatcher(input: string): ReturnType<RE2JS["matcher"]> {
91 if (this._matcher === null) {
92 this._matcher = this._re2.matcher(input);
93 this._matcherInput = input;
94 return this._matcher;
95 }
96 if (this._matcherInput !== input) {
97 // Swap the cached Utf16MatcherInput's charSequence in-place to avoid
98 // allocating a new Matcher per call. RE2JS's resetMatcherInput is not
99 // safe with raw strings (the constructor wraps strings via
100 // MatcherInput.utf16, but resetMatcherInput assigns its argument
101 // directly and then calls .length() as a method, which throws on a
102 // raw string). MatcherInput is not exported, so we mutate the existing
103 // wrapper's charSequence field — Matcher.reset() reads matcherInput.length()
104 // afterwards, so the new length is picked up correctly.
105 // biome-ignore lint/suspicious/noExplicitAny: reaching into re2js internals
106 (this._matcher as any).matcherInput.charSequence = input;
107 this._matcherInput = input;
108 }
109 this._matcher.reset();
110 return this._matcher;
111 }
112
113 constructor(pattern: string, flags = "") {
114 this._pattern = pattern;
115 this._flags = flags;
116 this._global = flags.includes("g");
117 this._ignoreCase = flags.includes("i");
118 this._multiline = flags.includes("m");
119
120 try {
121 const translatedPattern = translatePattern(pattern);
122 const re2Flags = convertFlags(flags);
123 this._re2 = RE2JS.compile(translatedPattern, re2Flags);
124 } catch (e) {
125 if (e instanceof RE2JSSyntaxException) {
126 // Provide helpful error messages for unsupported RE2 features
127 const msg = e.message || "";
128 let explanation = "";
129
130 if (

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…