TS-Rex is designed around four architectural pillars that work together to make regex construction correct by construction: it never manipulates raw strings during the build phase, every operation produces a fresh builder instance, type state is tracked at the TypeScript compiler level with no runtime cost, and a singleDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/fajarnugraha37/ts-rex/llms.txt
Use this file to discover all available pages before exploring further.
.compile() call converts the accumulated tree into a native RegExp. Understanding these pillars helps you reason about why the API is shaped the way it is and what guarantees it provides.
AST generation
Every method you call on aRegexBuilder appends an ASTNode to an internal array called chunks. No string concatenation happens at method-call time. This means that intermediate builder values are inert data structures — they cannot produce a malformed pattern mid-chain.
The ASTNode interface is minimal by design:
prefix and suffix wrappers alongside optional children, which lets composite constructs like groups and quantifiers nest arbitrarily without requiring the pattern builder to understand the shape of its children. For example, .group() appends a node with prefix: '(?:' and suffix: ')', with the inner builder’s chunks passed as children:
.capture() writes the group name directly into the prefix:
Immutability
Every method returns a newRegexBuilder instance rather than mutating the current one. The private _chain method is the single point responsible for this:
_chain spreads this.chunks into a new array, appends the incoming node, and constructs a brand-new RegexBuilder with the result. The original instance is untouched.
This means you can safely branch a base pattern and extend it in multiple directions without one branch contaminating another:
Phantom type state
RegexBuilder carries two generic parameters, TCaptures and TFlags, that track accumulated state purely at the TypeScript type level. They exist nowhere at runtime:
declare keyword means the _ property is type-only — the TypeScript compiler uses it to resolve generics, but the JavaScript runtime never allocates it. Every method signature that changes the capture or flag shape does so by returning a RegexBuilder with updated generics. For instance, .global() shifts TFlags to include { global: true }:
.capture() extends TCaptures with the new group name:
Runtime compilation
Calling.compile() is the only moment the AST collapses into a string. The private _buildPattern method walks the chunks array recursively, concatenating each node’s prefix, value, children, and suffix:
compile() then wraps the pattern in a CompiledRegex that contains the raw pattern string, the native RegExp instance, and an exec wrapper that creates a fresh RegExp on every call to avoid lastIndex state bugs caused by the g and y flags:
The
native property on CompiledRegex is provided for inspection (logging, tooling). Use exec for actual matching — it is the only stateless path.How the pillars interact
You call rx() and chain methods
Each method call invokes
_chain, appending an ASTNode and returning a new RegexBuilder. TypeScript updates TCaptures or TFlags in the return type automatically.Intermediate builders are safe to store and reuse
Because every result is a new instance with an immutable
chunks array, you can assign any intermediate builder to a variable and branch from it any number of times.You call .compile()
_buildPattern traverses the AST and produces the final pattern string. A CompiledRegex is returned with full type information locked in.