Regex.compileWithFlags ran pattern_analyzer.analyzeAndValidate(..., .high) on every compile, before an engine was chosen, and returned error.PatternTooComplex for anything the analyzer called critical. That rejected patterns JavaScript accepts:
/(a*)*/.exec("b") // node: ["", undefined]
/(a+)+/.exec("aaa") // node: ["aaa", "aaa"]
/(?:a*)+b/.exec("aab")All three failed to compile at all, so an embedder implementing ECMAScript could not even construct the RegExp.
The guard is aimed at exponential backtracking, which is a property of the backtracking engine. The Thompson engines this library routes most patterns to (the Pike VM, the lazy DFA, one-pass) are linear in the input whatever the pattern shape, so nested quantifiers cost them nothing. What they can blow up on is size, not time: counted repeats expand into copies of their operand, so (a{1000}){1000} would want about 10^6 states.
Fixed in 30c1fa9: run the analyzer only when the pattern is routed to the backtracker, or when the dialect is not ECMAScript, and cap an ECMAScript NFA at compiler.max_nfa_states (200,000) so a pattern that would expand past it still fails with PatternTooComplex.
Evidence, on the 20,775-case node v24.4.1 differential corpus:
| refused at compile | cases run | mismatches | |
|---|---|---|---|
bc14170 | 1,660 | 19,915 | 308 |
30c1fa9 | 800 | 20,775 | 308 |
860 patterns that never ran now do, and every one agrees with node: the mismatch set is identical case for case (0 fixed, 0 regressed) and is entirely the backtracker backlog in #30. zig build test-core passes.