🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

re2

Package Overview
Dependencies
Maintainers
1
Versions
93
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

re2 - npm Package Compare versions

Comparing version
1.24.0
to
1.24.1
+131
AGENTS.md
# AGENTS.md — node-re2
> `node-re2` provides Node.js bindings for [RE2](https://github.com/google/re2): a fast, safe alternative to backtracking regular expression engines. The npm package name is `re2`. It is a C++ native addon built with `node-gyp` and `nan`.
For project structure, module dependencies, and the architecture overview see [ARCHITECTURE.md](./ARCHITECTURE.md).
For detailed usage docs see the [README](./README.md) and the [wiki](https://github.com/uhop/node-re2/wiki).
## Setup
This project uses git submodules for vendored dependencies (RE2 and Abseil):
```bash
git clone --recursive https://github.com/uhop/node-re2.git
cd node-re2
npm install
```
If the native addon fails to download a prebuilt artifact, it builds locally via `node-gyp`.
## Commands
- **Install:** `npm install` (downloads prebuilt artifact or builds from source)
- **Build (release):** `npm run rebuild` (or `node-gyp -j max rebuild`)
- **Build (debug):** `npm run rebuild:dev` (or `node-gyp -j max rebuild --debug`)
- **Test:** `npm test` (runs `tape6 --flags FO`, worker threads)
- **Test (sequential):** `npm run test:seq`
- **Test (multi-process):** `npm run test:proc`
- **Test (single file):** `node tests/test-<name>.mjs`
- **TypeScript check:** `npm run ts-check`
- **Lint:** `npm run lint` (Prettier check)
- **Lint fix:** `npm run lint:fix` (Prettier write)
- **Verify build:** `npm run verify-build`
## Project structure
```
node-re2/
├── package.json # Package config; "tape6" section configures test discovery
├── binding.gyp # node-gyp build configuration for the C++ addon
├── re2.js # Main entry point: loads native addon, sets up Symbol aliases
├── re2.d.ts # TypeScript declarations for the public API
├── tsconfig.json # TypeScript config (noEmit, strict, types: ["node"])
├── lib/ # C++ source code (native addon)
│ ├── addon.cc # Node.js addon initialization, method registration
│ ├── wrapped_re2.h # WrappedRE2 class definition (core C++ wrapper)
│ ├── wrapped_re2_set.h # WrappedRE2Set class definition (RE2.Set wrapper)
│ ├── isolate_data.h # Per-isolate data struct for thread-safe addon state
│ ├── new.cc # Constructor: parse pattern/flags, create RE2 instance
│ ├── exec.cc # RE2.prototype.exec() implementation
│ ├── test.cc # RE2.prototype.test() implementation
│ ├── match.cc # RE2.prototype.match() implementation
│ ├── replace.cc # RE2.prototype.replace() implementation
│ ├── search.cc # RE2.prototype.search() implementation
│ ├── split.cc # RE2.prototype.split() implementation
│ ├── to_string.cc # RE2.prototype.toString() implementation
│ ├── accessors.cc # Property accessors (source, flags, lastIndex, etc.)
│ ├── pattern.cc # Pattern translation (RegExp → RE2 syntax, Unicode classes)
│ ├── set.cc # RE2.Set implementation (multi-pattern matching)
│ ├── util.cc # Shared utilities (UTF-8/UTF-16 conversion, buffer helpers)
│ ├── util.h # Utility declarations
│ └── pattern.h # Pattern translation declarations
├── scripts/
│ └── verify-build.js # Quick smoke test for the built addon
├── tests/ # Test files (test-*.mjs using tape-six)
├── ts-tests/ # TypeScript type-checking tests
│ └── test-types.ts # Verifies type declarations compile correctly
├── bench/ # Benchmarks
├── vendor/ # Vendored C++ dependencies (git submodules)
│ ├── re2/ # Google RE2 library source
│ └── abseil-cpp/ # Abseil C++ library (RE2 dependency)
└── .github/ # CI workflows, Dependabot config, actions
```
## Code style
- **CommonJS** throughout (`"type": "commonjs"` in package.json).
- **No transpilation** — JavaScript code runs directly.
- **C++ code** uses tabs for indentation, 4-wide. JavaScript uses 2-space indentation.
- **Prettier** for JS/TS formatting (see `.prettierrc`): 80 char width, single quotes, no bracket spacing, no trailing commas, arrow parens "avoid".
- **nan** (Native Abstractions for Node.js) for the C++ addon API.
- Semicolons are enforced by Prettier (default `semi: true`).
- Imports use `require()` syntax in source, `import` in tests (`.mjs`).
## Critical rules
- **Do not modify vendored code.** Never edit files under `vendor/`. They are git submodules.
- **Do not modify or delete test expectations** without understanding why they changed.
- **Do not add comments or remove comments** unless explicitly asked.
- **Keep `re2.js` and `re2.d.ts` in sync.** All public API exposed from `re2.js` must be typed in `re2.d.ts`.
- **The addon must build on all supported platforms:** Linux (x64, arm64, Alpine), macOS (x64, arm64), Windows (x64, arm64).
- **RE2 is always Unicode-mode.** The `u` flag is always added implicitly.
- **Buffer support is a first-class feature.** All methods that accept strings must also accept Buffers, returning Buffers when given Buffer input.
## Architecture
- `re2.js` is the main entry point. It loads the native C++ addon from `build/Release/re2.node` and sets up `Symbol.match`, `Symbol.search`, `Symbol.replace`, `Symbol.split`, and `Symbol.matchAll` on the prototype.
- The C++ addon (`lib/*.cc`) wraps Google's RE2 library via nan. Each RegExp method has its own `.cc` file.
- `lib/new.cc` handles construction: parsing patterns, translating RegExp syntax to RE2 syntax (via `lib/pattern.cc`), and creating the underlying `re2::RE2` instance.
- `lib/pattern.cc` translates JavaScript RegExp features to RE2 equivalents, including Unicode class names (`\p{Letter}` → `\p{L}`, `\p{Script=Latin}` → `\p{Latin}`).
- `lib/set.cc` implements `RE2.Set` for multi-pattern matching using `re2::RE2::Set`.
- `lib/util.cc` provides UTF-8 ↔ UTF-16 conversion helpers and buffer utilities.
- Prebuilt native artifacts are hosted on GitHub Releases and downloaded at install time via `install-artifact-from-github`.
## Writing tests
```js
import test from 'tape-six';
import {RE2} from '../re2.js';
test('example', t => {
const re = new RE2('a(b*)', 'i');
const result = re.exec('aBbC');
t.ok(result);
t.equal(result[0], 'aBb');
t.equal(result[1], 'Bb');
});
```
- Test files use `tape-six`: `.mjs` for runtime tests, `.ts` for TypeScript typing tests.
- Test file naming convention: `test-*.mjs` in `tests/`, `test-*.ts` in `ts-tests/`.
- Tests are configured in `package.json` under the `"tape6"` section.
- Test files should be directly executable: `node tests/test-foo.mjs`.
## Key conventions
- The library is a drop-in replacement for `RegExp` — the `RE2` object emulates the standard `RegExp` API.
- `RE2.Set` provides multi-pattern matching: `new RE2.Set(patterns, flags, options)`.
- Static helpers: `RE2.getUtf8Length(str)`, `RE2.getUtf16Length(buf)`.
- `RE2.unicodeWarningLevel` controls behavior when non-Unicode regexps are created.
- The `install` script tries to download a prebuilt `.node` artifact before falling back to `node-gyp rebuild`.
- All C++ source is in `lib/`, all vendored third-party C++ is in `vendor/`.
# Architecture
`node-re2` provides Node.js bindings for Google's [RE2](https://github.com/google/re2) regular expression engine. It is a C++ native addon built with `node-gyp` and `nan`. The `RE2` object is a drop-in replacement for `RegExp` with guaranteed linear-time matching (no ReDoS).
## Project layout
```
package.json # Package config; "tape6" section configures test discovery
binding.gyp # node-gyp build configuration for the C++ addon
re2.js # Main entry point: loads native addon, sets up Symbol aliases
re2.d.ts # TypeScript declarations for the public API
tsconfig.json # TypeScript config (noEmit, strict, types: ["node"])
lib/ # C++ source code (native addon)
├── addon.cc # Node.js addon initialization, method registration
├── wrapped_re2.h # WrappedRE2 class definition (core C++ wrapper)
├── wrapped_re2_set.h # WrappedRE2Set class definition (RE2.Set wrapper)
├── isolate_data.h # Per-isolate data struct for thread-safe addon state
├── new.cc # Constructor: parse pattern/flags, create RE2 instance
├── exec.cc # RE2.prototype.exec() implementation
├── test.cc # RE2.prototype.test() implementation
├── match.cc # RE2.prototype.match() implementation
├── replace.cc # RE2.prototype.replace() implementation
├── search.cc # RE2.prototype.search() implementation
├── split.cc # RE2.prototype.split() implementation
├── to_string.cc # RE2.prototype.toString() implementation
├── accessors.cc # Property accessors (source, flags, lastIndex, etc.)
├── pattern.cc # Pattern translation (RegExp → RE2 syntax, Unicode classes)
├── pattern.h # Pattern translation declarations
├── set.cc # RE2.Set implementation (multi-pattern matching)
├── util.cc # Shared utilities (UTF-8/UTF-16 conversion, buffer helpers)
└── util.h # Utility declarations
scripts/
└── verify-build.js # Quick smoke test for the built addon
tests/ # Test files (test-*.mjs using tape-six)
ts-tests/ # TypeScript type-checking tests
└── test-types.ts # Verifies type declarations compile correctly
bench/ # Benchmarks
vendor/ # Vendored C++ dependencies (git submodules) — DO NOT MODIFY
├── re2/ # Google RE2 library source
└── abseil-cpp/ # Abseil C++ library (RE2 dependency)
.github/ # CI workflows, Dependabot config, actions
```
## Core concepts
### How the addon works
1. `re2.js` is the entry point. It loads the compiled C++ addon from `build/Release/re2.node`.
2. The addon exposes an `RE2` constructor that wraps `re2::RE2` from Google's RE2 library.
3. `re2.js` adds `Symbol.match`, `Symbol.search`, `Symbol.replace`, `Symbol.split`, and `Symbol.matchAll` to the prototype so `RE2` instances work with ES6 string methods.
4. The `RE2` constructor can be called with or without `new` (factory mode).
### C++ addon structure
Each RegExp method has its own `.cc` file for maintainability:
| File | Purpose |
| --------------- | ---------------------------------------------------------------- |
| `addon.cc` | Node.js module initialization, registers all methods/accessors |
| `isolate_data.h` | Per-isolate data struct (`AddonData`) for thread-safe addon state |
| `wrapped_re2.h` | `WrappedRE2` class: holds `re2::RE2*`, flags, lastIndex, source |
| `new.cc` | Constructor: parses pattern + flags, translates syntax, creates RE2 instance |
| `exec.cc` | `exec()` — find match with capture groups |
| `test.cc` | `test()` — boolean match check |
| `match.cc` | `match()` — String.prototype.match equivalent |
| `replace.cc` | `replace()` — substitution with string or function replacer |
| `search.cc` | `search()` — find index of first match |
| `split.cc` | `split()` — split string by pattern |
| `to_string.cc` | `toString()` — `/pattern/flags` representation |
| `accessors.cc` | Property getters: `source`, `flags`, `lastIndex`, `global`, `ignoreCase`, `multiline`, `dotAll`, `unicode`, `sticky`, `hasIndices`, `internalSource` |
| `pattern.cc` | Translates JS RegExp syntax to RE2 syntax, maps Unicode property names |
| `set.cc` | `RE2.Set` — multi-pattern matching via `re2::RE2::Set` |
| `util.cc` | UTF-8 ↔ UTF-16 conversion, buffer/string helpers |
### Pattern translation (pattern.cc)
JavaScript RegExp features are translated to RE2 equivalents:
- Named groups: `(?<name>...)` syntax is preserved (RE2 supports it natively).
- Unicode classes: long names like `\p{Letter}` are mapped to short names `\p{L}`. Script names like `\p{Script=Latin}` are mapped to `\p{Latin}`.
- Backreferences and lookahead assertions are **not supported** — RE2 throws `SyntaxError`.
### Buffer support
All methods accept both strings and Node.js Buffers:
- Buffer inputs are assumed UTF-8 encoded.
- Buffer inputs produce Buffer outputs (in composite result objects too).
- Offsets and lengths are in bytes (not characters) when using Buffers.
- The `useBuffers` property on replacer functions controls offset reporting in `replace()`.
### RE2.Set (set.cc)
Multi-pattern matching using `re2::RE2::Set`:
- `new RE2.Set(patterns, flags?, options?)` — compile multiple patterns into a single automaton.
- `set.test(str)` — returns `true` if any pattern matches.
- `set.match(str)` — returns array of indices of matching patterns.
- Properties: `size`, `source`, `sources`, `flags`, `anchor`.
### Build system
- `binding.gyp` defines the node-gyp build: compiles all `.cc` files in `lib/` plus vendored RE2 and Abseil sources.
- Platform-specific compiler flags are set for GCC, Clang, and MSVC.
- The `install` npm script first tries to download a prebuilt `re2.node` from GitHub Releases via `install-artifact-from-github`, falling back to a local `node-gyp rebuild`.
- Prebuilt artifacts cover: Linux (x64, arm64, Alpine/musl), macOS (x64, arm64), Windows (x64, arm64).
## Module dependency graph
```
re2.js ──→ build/Release/re2.node (compiled C++ addon)
├── lib/addon.cc (init)
│ ├── lib/new.cc ──→ lib/pattern.cc
│ ├── lib/exec.cc
│ ├── lib/test.cc
│ ├── lib/match.cc
│ ├── lib/replace.cc
│ ├── lib/search.cc
│ ├── lib/split.cc
│ ├── lib/to_string.cc
│ ├── lib/accessors.cc
│ └── lib/set.cc
├── lib/wrapped_re2.h (shared class definition)
├── lib/wrapped_re2_set.h (RE2.Set class)
├── lib/util.cc / lib/util.h (shared utilities)
└── vendor/ (re2 + abseil-cpp)
```
## Testing
- **Framework**: tape-six (`tape6`)
- **Run all**: `npm test` (worker threads via `tape6 --flags FO`)
- **Run sequential**: `npm run test:seq`
- **Run multi-process**: `npm run test:proc`
- **Run single file**: `node tests/test-<name>.mjs`
- **TypeScript check**: `npm run ts-check`
- **Lint**: `npm run lint` (Prettier check)
- **Lint fix**: `npm run lint:fix` (Prettier write)
- **Verify build**: `npm run verify-build` (quick smoke test)
## Import paths
```js
// CommonJS (source, scripts)
const RE2 = require('re2');
// ESM (tests)
import {RE2} from '../re2.js';
```
# node-re2
> Node.js bindings for RE2: a fast, safe alternative to backtracking regular expression engines. Drop-in RegExp replacement that prevents ReDoS (Regular Expression Denial of Service). Works with strings and Buffers. C++ native addon built with node-gyp and nan.
- Drop-in replacement for RegExp with linear-time matching guarantee
- Prevents ReDoS by disallowing backreferences and lookahead assertions
- Full Unicode mode (always on)
- Buffer support for high-performance binary/UTF-8 processing
- Named capture groups
- Symbol-based methods (Symbol.match, Symbol.search, Symbol.replace, Symbol.split, Symbol.matchAll)
- RE2.Set for multi-pattern matching
- Prebuilt binaries for Linux, macOS, Windows (x64 + arm64)
- TypeScript declarations included
## Install
```bash
npm install re2
```
Prebuilt native binaries are downloaded automatically. Falls back to building from source via node-gyp if no prebuilt is available.
## Quick start
```js
const RE2 = require('re2');
// Create and use like RegExp
const re = new RE2('a(b*)', 'i');
const result = re.exec('aBbC');
console.log(result[0]); // "aBb"
console.log(result[1]); // "Bb"
// Works with ES6 string methods
'hello world'.match(new RE2('\\w+', 'g')); // ['hello', 'world']
'hello world'.replace(new RE2('world'), 'RE2'); // 'hello RE2'
```
## Importing
```js
// CommonJS
const RE2 = require('re2');
// ESM
import { RE2 } from 're2';
```
## Construction
`new RE2(pattern[, flags])` or `RE2(pattern[, flags])` (factory mode).
Pattern can be:
- **String**: `new RE2('\\d+')`
- **String with flags**: `new RE2('\\d+', 'gi')`
- **RegExp**: `new RE2(/ab*/ig)` — copies pattern and flags.
- **RE2**: `new RE2(existingRE2)` — copies pattern and flags.
- **Buffer**: `new RE2(Buffer.from('pattern'))` — pattern from UTF-8 buffer.
Supported flags:
- `g` — global (find all matches)
- `i` — ignoreCase
- `m` — multiline (`^`/`$` match line boundaries)
- `s` — dotAll (`.` matches `\n`)
- `u` — unicode (always on, added implicitly)
- `y` — sticky (match at lastIndex only)
- `d` — hasIndices (include index info for capture groups)
Invalid patterns throw `SyntaxError`. Patterns with backreferences or lookahead throw `SyntaxError`.
## Properties
### Instance properties
- `re.source` (string) — the pattern string, escaped for use in `new RE2(re.source)` or `new RegExp(re.source)`.
- `re.flags` (string) — the flags string (e.g., `'giu'`).
- `re.lastIndex` (number) — the index at which to start the next match (used with `g` or `y` flags).
- `re.global` (boolean) — whether the `g` flag is set.
- `re.ignoreCase` (boolean) — whether the `i` flag is set.
- `re.multiline` (boolean) — whether the `m` flag is set.
- `re.dotAll` (boolean) — whether the `s` flag is set.
- `re.unicode` (boolean) — always `true` (RE2 always operates in Unicode mode).
- `re.sticky` (boolean) — whether the `y` flag is set.
- `re.hasIndices` (boolean) — whether the `d` flag is set.
- `re.internalSource` (string) — the RE2-translated pattern (for debugging; may differ from `source`).
### Static properties
- `RE2.unicodeWarningLevel` (string) — controls behavior when a non-Unicode regexp is created:
- `'nothing'` (default) — silently add `u` flag.
- `'warnOnce'` — warn once, then silently add `u`. Assigning resets the one-time flag.
- `'warn'` — warn every time.
- `'throw'` — throw `SyntaxError` every time.
## RegExp methods
### re.exec(str)
Executes a search for a match. Returns a result array or `null`.
```js
const re = new RE2('a(b+)', 'g');
const result = re.exec('abbc abbc');
// result[0] === 'abb'
// result[1] === 'bb'
// result.index === 0
// result.input === 'abbc abbc'
// re.lastIndex === 3
```
With `d` flag (hasIndices), result has `.indices` property with `[start, end]` pairs for each group.
With `g` or `y` flag, advances `lastIndex`. Call repeatedly to iterate matches.
### re.test(str)
Returns `true` if the pattern matches, `false` otherwise.
```js
new RE2('\\d+').test('abc123'); // true
new RE2('\\d+').test('abcdef'); // false
```
With `g` or `y` flag, advances `lastIndex`.
### re.toString()
Returns `'/pattern/flags'` string representation.
```js
new RE2('abc', 'gi').toString(); // '/abc/giu'
```
## String methods (via Symbol)
RE2 instances implement well-known symbols, so they work directly with ES6 string methods:
### str.match(re) / re[Symbol.match](str)
```js
'test 123 test 456'.match(new RE2('\\d+', 'g')); // ['123', '456']
'test 123'.match(new RE2('(\\d+)')); // ['123', '123', index: 5, input: 'test 123']
```
### str.matchAll(re) / re[Symbol.matchAll](str)
Returns an iterator of all matches (requires `g` flag).
```js
const re = new RE2('\\d+', 'g');
for (const m of '1a2b3c'.matchAll(re)) {
console.log(m[0]); // '1', '2', '3'
}
```
### str.search(re) / re[Symbol.search](str)
Returns the index of the first match, or `-1`.
```js
'hello world'.search(new RE2('world')); // 6
```
### str.replace(re, replacement) / re[Symbol.replace](str, replacement)
Returns a new string with matches replaced.
```js
'aabba'.replace(new RE2('b', 'g'), 'c'); // 'aacca'
```
Replacement string supports:
- `$1`, `$2`, ... — numbered capture groups.
- `$<name>` — named capture groups.
- `$&` — the matched substring.
- `` $` `` — portion before the match.
- `$'` — portion after the match.
- `$$` — literal `$`.
Replacement function receives `(match, ...groups, offset, input)`:
```js
'abc'.replace(new RE2('(b)'), (match, g1, offset) => `[${g1}@${offset}]`);
// 'a[b@1]c'
```
### str.split(re[, limit]) / re[Symbol.split](str[, limit])
Splits string by pattern.
```js
'a1b2c3'.split(new RE2('\\d')); // ['a', 'b', 'c', '']
'a1b2c3'.split(new RE2('\\d'), 2); // ['a', 'b']
```
## String methods (direct)
These are convenience methods on the RE2 instance with swapped argument order:
- `re.match(str)` — equivalent to `str.match(re)`.
- `re.search(str)` — equivalent to `str.search(re)`.
- `re.replace(str, replacement)` — equivalent to `str.replace(re, replacement)`.
- `re.split(str[, limit])` — equivalent to `str.split(re, limit)`.
```js
const re = new RE2('\\d+', 'g');
re.match('test 123 test 456'); // ['123', '456']
re.search('test 123'); // 5
re.replace('test 1 and 2', 'N'); // 'test N and N' (global replaces all)
re.split('a1b2c'); // ['a', 'b', 'c']
```
## Buffer support
All methods accept Node.js Buffers (UTF-8) instead of strings. When given Buffer input, they return Buffer output.
```js
const re = new RE2('матч', 'g');
const buf = Buffer.from('тест матч тест');
const result = re.exec(buf);
// result[0] is a Buffer containing 'матч' in UTF-8
// result.index is in bytes (not characters)
```
Differences from string mode:
- All offsets and lengths are in **bytes**, not characters.
- Results contain Buffers instead of strings.
- Use `buf.toString()` to convert results back to strings.
### useBuffers on replacer functions
When using `re.replace(buf, replacerFn)`, the replacer receives string arguments and character offsets by default. Set `replacerFn.useBuffers = true` to receive byte offsets instead:
```js
function replacer(match, offset, input) {
return '<' + offset + ' bytes>';
}
replacer.useBuffers = true;
new RE2('б').replace(Buffer.from('абв'), replacer);
```
## RE2.Set
Multi-pattern matching — compile many patterns into a single automaton and test/match against all of them at once. Faster than testing individual patterns when the number of patterns is large.
### Constructor
```js
new RE2.Set(patterns[, flagsOrOptions][, options])
```
- `patterns` — any iterable of strings, Buffers, RegExp, or RE2 instances.
- `flagsOrOptions` — optional string/Buffer with flags (apply to all patterns), or options object.
- `options.anchor` — `'unanchored'` (default), `'start'`, or `'both'`.
```js
const set = new RE2.Set([
'^/users/\\d+$',
'^/posts/\\d+$',
'^/api/.*$'
], 'i', {anchor: 'start'});
```
### set.test(str)
Returns `true` if any pattern matches, `false` otherwise.
```js
set.test('/users/42'); // true
set.test('/unknown'); // false
```
### set.match(str)
Returns an array of indices of matching patterns, sorted ascending. Empty array if none match.
```js
set.match('/users/42'); // [0]
set.match('/api/users'); // [2]
set.match('/unknown'); // []
```
### Properties
- `set.size` (number) — number of patterns.
- `set.source` (string) — all patterns joined with `|`.
- `set.sources` (string[]) — individual pattern sources.
- `set.flags` (string) — flags string.
- `set.anchor` (string) — anchor mode.
### set.toString()
Returns `'/pattern1|pattern2|.../flags'`.
```js
set.toString(); // '/^/users/\\d+$|^/posts/\\d+$|^/api/.*$/iu'
```
## Static helpers
### RE2.getUtf8Length(str)
Calculate the byte size needed to encode a UTF-16 string as UTF-8.
```js
RE2.getUtf8Length('hello'); // 5
RE2.getUtf8Length('привет'); // 12
```
### RE2.getUtf16Length(buf)
Calculate the character count needed to encode a UTF-8 buffer as a UTF-16 string.
```js
RE2.getUtf16Length(Buffer.from('hello')); // 5
RE2.getUtf16Length(Buffer.from('привет')); // 6
```
## Named groups
Named capture groups are supported:
```js
const re = new RE2('(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})');
const result = re.exec('2024-01-15');
result.groups.year; // '2024'
result.groups.month; // '01'
result.groups.day; // '15'
```
Named backreferences in replacement strings:
```js
'2024-01-15'.replace(
new RE2('(?<y>\\d{4})-(?<m>\\d{2})-(?<d>\\d{2})'),
'$<d>/$<m>/$<y>'
); // '15/01/2024'
```
## Unicode classes
RE2 supports Unicode property escapes. Long names are translated to RE2 short names:
```js
new RE2('\\p{Letter}+'); // same as \p{L}+
new RE2('\\p{Number}+'); // same as \p{N}+
new RE2('\\p{Script=Latin}+'); // same as \p{Latin}+
new RE2('\\p{sc=Cyrillic}+'); // same as \p{Cyrillic}+
new RE2('\\P{Letter}+'); // negated: non-letters
```
Only `\p{name}` form is supported (not `\p{name=value}` in general). Exception: `Script` and `sc` names.
## Limitations
RE2 does **not** support:
- **Backreferences** (`\1`, `\2`, etc.) — throw `SyntaxError`.
- **Lookahead assertions** (`(?=...)`, `(?!...)`) — throw `SyntaxError`.
- **Lookbehind assertions** (`(?<=...)`, `(?<!...)`) — throw `SyntaxError`.
Fallback pattern:
```js
let re = /pattern-with-lookahead(?=foo)/;
try {
re = new RE2(re);
} catch (e) {
// use original RegExp as fallback
}
const result = re.exec(input);
```
## Common patterns
### Drop-in RegExp replacement
```js
const RE2 = require('re2');
// Before (vulnerable to ReDoS):
const re = new RegExp(userInput);
// After (safe):
const re = new RE2(userInput);
```
### Process Buffer data efficiently
```js
const RE2 = require('re2');
const fs = require('fs');
const data = fs.readFileSync('large-file.txt');
const re = new RE2('pattern', 'g');
let match;
while ((match = re.exec(data)) !== null) {
console.log('Found at byte offset:', match.index);
}
```
### Route matching with RE2.Set
```js
const RE2 = require('re2');
const routes = new RE2.Set([
'^/users/\\d+$',
'^/posts/\\d+$',
'^/api/v\\d+/.*$'
], 'i');
function findRoute(path) {
const matches = routes.match(path);
return matches.length > 0 ? matches[0] : -1;
}
findRoute('/users/42'); // 0
findRoute('/posts/7'); // 1
findRoute('/api/v2/foo'); // 2
findRoute('/unknown'); // -1
```
### Validate user-supplied patterns safely
```js
const RE2 = require('re2');
function safeMatch(input, pattern, flags) {
try {
const re = new RE2(pattern, flags);
return re.test(input);
} catch (e) {
return false; // invalid pattern
}
}
```
## TypeScript
```ts
import RE2 from 're2';
const re: RE2 = new RE2('\\d+', 'g');
const result: RegExpExecArray | null = re.exec('test 123');
// Buffer overloads
const bufResult: RE2BufferExecArray | null = re.exec(Buffer.from('test 123'));
// RE2.Set
const set: RE2Set = new RE2.Set(['a', 'b'], 'i');
const matches: number[] = set.match('abc');
```
## Project structure notes
- Entry point: `re2.js` (loads native addon), types: `re2.d.ts`.
- C++ addon source: `lib/*.cc`, `lib/*.h`.
- Tests: `tests/test-*.mjs` (runtime), `ts-tests/test-*.ts` (type-checking).
- Vendored dependencies: `vendor/re2/`, `vendor/abseil-cpp/` (git submodules) — **never modify files under `vendor/`**.
## Links
- Docs: https://github.com/uhop/node-re2/wiki
- npm: https://www.npmjs.com/package/re2
- Repository: https://github.com/uhop/node-re2
- RE2 syntax: https://github.com/google/re2/wiki/Syntax
# node-re2
> Node.js bindings for RE2: a fast, safe alternative to backtracking regular expression engines. Drop-in RegExp replacement that prevents ReDoS. Works with strings and Buffers.
## Install
npm install re2
## Quick start
```js
// CommonJS
const RE2 = require('re2');
// ESM
import {RE2} from 're2';
const re = new RE2('a(b*)', 'i');
const result = re.exec('aBbC');
console.log(result[0]); // "aBb"
console.log(result[1]); // "Bb"
```
## Why use node-re2?
The built-in Node.js RegExp engine can run in exponential time with vulnerable patterns (ReDoS). RE2 guarantees linear-time matching by disallowing backreferences and lookahead assertions.
## API
### Construction
```js
const RE2 = require('re2');
const re1 = new RE2('\\d+'); // from string
const re2 = new RE2('\\d+', 'gi'); // with flags
const re3 = new RE2(/ab*/ig); // from RegExp
const re4 = new RE2(re3); // from another RE2
const re5 = RE2('\\d+'); // factory (no new)
```
Supported flags: `g` (global), `i` (ignoreCase), `m` (multiline), `s` (dotAll), `u` (unicode, always on), `y` (sticky), `d` (hasIndices).
### RegExp methods
- `re.exec(str)` — find match with capture groups.
- `re.test(str)` — boolean match check.
- `re.toString()` — `/pattern/flags` representation.
### String methods (via Symbol)
RE2 instances work with ES6 string methods:
```js
'abc'.match(re);
'abc'.search(re);
'abc'.replace(re, 'x');
'abc'.split(re);
Array.from('abc'.matchAll(re));
```
### String methods (direct)
- `re.match(str)` — equivalent to `str.match(re)`.
- `re.search(str)` — equivalent to `str.search(re)`.
- `re.replace(str, replacement)` — equivalent to `str.replace(re, replacement)`.
- `re.split(str[, limit])` — equivalent to `str.split(re, limit)`.
### Properties
- `re.source` — pattern string.
- `re.flags` — flags string.
- `re.lastIndex` — index for next match (with `g` or `y` flag).
- `re.global`, `re.ignoreCase`, `re.multiline`, `re.dotAll`, `re.unicode`, `re.sticky`, `re.hasIndices` — boolean flag accessors.
- `re.internalSource` — RE2-translated pattern (for debugging).
### Buffer support
All methods accept Buffers (UTF-8) instead of strings. Buffer input produces Buffer output. Offsets are in bytes.
```js
const re = new RE2('матч', 'g');
const buf = Buffer.from('тест матч тест');
const result = re.exec(buf);
// result[0] is a Buffer
```
### RE2.Set
Multi-pattern matching — test a string against many patterns at once.
```js
const set = new RE2.Set(['^/users/\\d+$', '^/posts/\\d+$'], 'i');
set.test('/users/7'); // true
set.match('/posts/42'); // [1]
set.sources; // ['^/users/\\d+$', '^/posts/\\d+$']
```
- `new RE2.Set(patterns[, flags][, options])` — compile patterns.
- `options.anchor`: `'unanchored'` (default), `'start'`, or `'both'`.
- `set.test(str)` — returns `true` if any pattern matches.
- `set.match(str)` — returns array of matching pattern indices.
- Properties: `size`, `source`, `sources`, `flags`, `anchor`.
### Static helpers
- `RE2.getUtf8Length(str)` — byte size of string as UTF-8.
- `RE2.getUtf16Length(buf)` — character count of UTF-8 buffer as UTF-16 string.
- `RE2.unicodeWarningLevel` — `'nothing'` (default), `'warnOnce'`, `'warn'`, or `'throw'`.
## Limitations
RE2 does not support:
- **Backreferences** (`\1`, `\2`, etc.)
- **Lookahead assertions** (`(?=...)`, `(?!...)`)
These throw `SyntaxError`. Use try-catch to fall back to RegExp when needed:
```js
let re = /pattern-with-lookahead/;
try { re = new RE2(re); } catch (e) { /* use original RegExp */ }
```
## Project notes
- C++ addon source is in `lib/`. Vendored deps (`vendor/re2/`, `vendor/abseil-cpp/`) are git submodules — **never modify files under `vendor/`**.
## Links
- Docs: https://github.com/uhop/node-re2/wiki
- npm: https://www.npmjs.com/package/re2
- Full LLM reference: https://github.com/uhop/node-re2/blob/master/llms-full.txt
+2
-2

@@ -43,3 +43,3 @@ #include "./wrapped_re2.h"

auto s = t.ToLocalChecked();
info.GetReturnValue().Set(static_cast<int>(s->Utf8Length(v8::Isolate::GetCurrent())));
info.GetReturnValue().Set(static_cast<int>(utf8Length(s, v8::Isolate::GetCurrent())));
}

@@ -201,3 +201,3 @@

auto s = t.ToLocalChecked();
auto argLength = s->Utf8Length(isolate);
auto argLength = utf8Length(s, isolate);

@@ -204,0 +204,0 @@ auto buffer = node::Buffer::New(isolate, s).ToLocalChecked();

@@ -79,6 +79,6 @@ #include "./wrapped_re2.h"

auto s = t.ToLocalChecked();
size = s->Utf8Length(isolate);
size = utf8Length(s, isolate);
buffer.resize(size + 1);
data = &buffer[0];
s->WriteUtf8(isolate, data, buffer.size());
writeUtf8(s, isolate, data, buffer.size());
buffer[size] = '\0';

@@ -138,6 +138,6 @@ }

auto s = t.ToLocalChecked();
size = s->Utf8Length(isolate);
size = utf8Length(s, isolate);
buffer.resize(size + 1);
data = &buffer[0];
s->WriteUtf8(isolate, data, buffer.size());
writeUtf8(s, isolate, data, buffer.size());
buffer[size] = '\0';

@@ -197,6 +197,6 @@

auto s = t.ToLocalChecked();
size = s->Utf8Length(isolate);
size = utf8Length(s, isolate);
buffer.resize(size + 1);
data = &buffer[0];
s->WriteUtf8(isolate, data, buffer.size());
writeUtf8(s, isolate, data, buffer.size());
buffer[size] = '\0';

@@ -203,0 +203,0 @@

@@ -37,5 +37,5 @@ #include "./wrapped_re2_set.h"

auto s = t.ToLocalChecked();
size = s->Utf8Length(isolate);
size = utf8Length(s, isolate);
buffer.resize(size + 1);
s->WriteUtf8(isolate, &buffer[0], buffer.size());
writeUtf8(s, isolate, &buffer[0], buffer.size());
buffer[buffer.size() - 1] = '\0';

@@ -291,6 +291,6 @@ data = &buffer[0];

auto s = t.ToLocalChecked();
auto utf8Length = s->Utf8Length(isolate);
auto len = utf8Length(s, isolate);
auto buffer = node::Buffer::New(isolate, s).ToLocalChecked();
keepAlive = buffer;
str.reset(buffer, node::Buffer::Length(buffer), utf8Length, 0);
str.reset(buffer, node::Buffer::Length(buffer), len, 0);
return true;

@@ -336,3 +336,3 @@ }

auto context = Nan::GetCurrentContext();
auto isolate = context->GetIsolate();
auto isolate = v8::Isolate::GetCurrent();

@@ -346,3 +346,3 @@ if (!info.IsConstructCall())

}
auto isolate = context->GetIsolate();
auto isolate = v8::Isolate::GetCurrent();
auto addonData = getAddonData(isolate);

@@ -520,5 +520,5 @@ if (!addonData) return;

auto s = t.ToLocalChecked();
size = s->Utf8Length(isolate);
size = utf8Length(s, isolate);
buffer.resize(size + 1);
s->WriteUtf8(isolate, &buffer[0], buffer.size());
writeUtf8(s, isolate, &buffer[0], buffer.size());
buffer[size] = '\0';

@@ -536,5 +536,5 @@ data = &buffer[0];

auto s = t.ToLocalChecked();
size = s->Utf8Length(isolate);
size = utf8Length(s, isolate);
buffer.resize(size + 1);
s->WriteUtf8(isolate, &buffer[0], buffer.size());
writeUtf8(s, isolate, &buffer[0], buffer.size());
buffer[size] = '\0';

@@ -541,0 +541,0 @@ data = &buffer[0];

@@ -228,2 +228,31 @@ #pragma once

// V8 13.4 introduced Utf8LengthV2 / WriteUtf8V2; V8 14.6 removed the bare
// Utf8Length / WriteUtf8. On older V8 (Node 22) only the bare forms exist.
#if defined(V8_MAJOR_VERSION) && (V8_MAJOR_VERSION > 13 || \
(V8_MAJOR_VERSION == 13 && defined(V8_MINOR_VERSION) && V8_MINOR_VERSION >= 4))
inline size_t utf8Length(v8::Local<v8::String> s, v8::Isolate *isolate)
{
return s->Utf8LengthV2(isolate);
}
inline void writeUtf8(v8::Local<v8::String> s, v8::Isolate *isolate, char *buffer, size_t capacity)
{
s->WriteUtf8V2(isolate, buffer, capacity);
}
#else
inline size_t utf8Length(v8::Local<v8::String> s, v8::Isolate *isolate)
{
return static_cast<size_t>(s->Utf8Length(isolate));
}
inline void writeUtf8(v8::Local<v8::String> s, v8::Isolate *isolate, char *buffer, size_t capacity)
{
s->WriteUtf8(isolate, buffer, static_cast<int>(capacity));
}
#endif
inline size_t getUtf16PositionByCounter(const char *data, size_t from, size_t n)

@@ -230,0 +259,0 @@ {

{
"name": "re2",
"version": "1.24.0",
"version": "1.24.1",
"description": "Bindings for RE2: fast, safe alternative to backtracking regular expression engines.",

@@ -11,4 +11,8 @@ "homepage": "https://github.com/uhop/node-re2",

"files": [
"AGENTS.md",
"ARCHITECTURE.md",
"binding.gyp",
"lib",
"llms-full.txt",
"llms.txt",
"re2.d.ts",

@@ -19,13 +23,13 @@ "scripts/*.js",

"dependencies": {
"install-artifact-from-github": "^1.4.0",
"nan": "^2.26.2",
"node-gyp": "^12.2.0"
"install-artifact-from-github": "^1.6.0",
"nan": "^2.27.0",
"node-gyp": "^12.3.0"
},
"devDependencies": {
"@types/node": "^25.5.0",
"@types/node": "^25.7.0",
"nano-benchmark": "^1.0.15",
"prettier": "^3.8.1",
"tape-six": "^1.7.13",
"tape-six-proc": "^1.2.8",
"typescript": "^6.0.2"
"prettier": "^3.8.3",
"tape-six": "^1.9.0",
"tape-six-proc": "^1.2.9",
"typescript": "^6.0.3"
},

@@ -54,4 +58,7 @@ "scripts": {

"type": "git",
"url": "git://github.com/uhop/node-re2.git"
"url": "git+https://github.com/uhop/node-re2.git"
},
"engines": {
"node": ">=22"
},
"keywords": [

@@ -58,0 +65,0 @@ "RegExp",

+1
-0

@@ -0,1 +1,2 @@

// @ts-self-types="./re2.d.ts"
'use strict';

@@ -2,0 +3,0 @@

@@ -388,2 +388,3 @@ # node-re2 [![NPM version][npm-img]][npm-url]

- 1.24.1 *Support for Node 22, 24, 26 + precompiled binaries.*
- 1.24.0 *Fixed multi-threaded crash in worker threads (#235). Added named import: `import {RE2} from 're2'`. Added CJS test. Updated docs and dependencies.*

@@ -390,0 +391,0 @@ - 1.23.3 *Updated Abseil and dev dependencies.*

@@ -121,3 +121,3 @@ //

#define ABSL_LTS_RELEASE_VERSION 20260107
#define ABSL_LTS_RELEASE_PATCH_LEVEL 0
#define ABSL_LTS_RELEASE_PATCH_LEVEL 1

@@ -124,0 +124,0 @@ // Helper macro to convert a CPP variable to a string literal.

@@ -107,3 +107,6 @@ // Copyright 2018 The Abseil Authors.

#elif defined(_MSC_VER) && !defined(__clang__) && defined(__AVX__)
// 32-bit builds with AVX do not have _mm_crc32_u64, so the _M_X64 condition is
// necessary.
#elif defined(_MSC_VER) && !defined(__clang__) && defined(__AVX__) && \
defined(_M_X64)

@@ -110,0 +113,0 @@ // MSVC AVX (/arch:AVX) implies SSE 4.2.

@@ -736,2 +736,6 @@ // Copyright 2017 The Abseil Authors.

EXPECT_EQ("", bytes); // Results in empty output.
// Ensure there is no sign extension bug on a signed char.
hex.assign("\xC8" "b", 2);
EXPECT_FALSE(absl::HexStringToBytes(hex, &bytes));
}

@@ -738,0 +742,0 @@

@@ -830,3 +830,3 @@ // Copyright 2017 The Abseil Authors.

/* clang-format off */
constexpr std::array<char, 256> kHexValueLenient = {
constexpr std::array<uint8_t, 256> kHexValueLenient = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

@@ -850,3 +850,3 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

constexpr std::array<signed char, 256> kHexValueStrict = {
constexpr std::array<int8_t, 256> kHexValueStrict = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,

@@ -879,3 +879,3 @@ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,

to[i] = static_cast<char>(kHexValueLenient[from[i * 2] & 0xFF] << 4) +
(kHexValueLenient[from[i * 2 + 1] & 0xFF]);
static_cast<char>(kHexValueLenient[from[i * 2 + 1] & 0xFF]);
}

@@ -998,4 +998,6 @@ }

for (size_t i = 0; i < buf_size; ++i) {
int h1 = absl::kHexValueStrict[static_cast<size_t>(*hex_p++)];
int h2 = absl::kHexValueStrict[static_cast<size_t>(*hex_p++)];
int h1 = absl::kHexValueStrict[static_cast<size_t>(
static_cast<uint8_t>(*hex_p++))];
int h2 = absl::kHexValueStrict[static_cast<size_t>(
static_cast<uint8_t>(*hex_p++))];
if (h1 == -1 || h2 == -1) {

@@ -1002,0 +1004,0 @@ return size_t{0};

Sorry, the diff of this file is not supported yet