{"_id":"@a5i/eregex","name":"@a5i/eregex","dist-tags":{"latest":"0.1.5"},"versions":{"0.1.5":{"name":"@a5i/eregex","version":"0.1.5","description":"Node.js bindings for eregex, an advanced regular expression engine inspired by mrab-regex","repository":{"type":"git","url":"git+https://github.com/a5i/eregex.git","directory":"crates/eregex-node"},"main":"index.js","types":"index.d.ts","license":"Apache-2.0","keywords":["regex","regular-expression","mrab-regex","eregex","text","pattern","native"],"napi":{"binaryName":"eregex"},"scripts":{"build":"napi build --release --platform","build:debug":"napi build --platform","prepublishOnly":"napi prepublish -t npm","test":"node test/smoke.js"},"devDependencies":{"@napi-rs/cli":"^3.0.0"},"engines":{"node":">=10"},"optionalDependencies":{},"_id":"@a5i/eregex@0.1.5","gitHead":"79b0b3789998a90efc7fff9a0abde33a9b358176","bugs":{"url":"https://github.com/a5i/eregex/issues"},"homepage":"https://github.com/a5i/eregex#readme","_nodeVersion":"22.22.3","_npmVersion":"10.9.8","dist":{"integrity":"sha512-n71TJOBlzTnizL3UgjJGUnoZqgFCu2xqzw/j/NvWXiPjbYJd8d5Tp3Y3/dY888aAN9kmtreCRe4YEiMYmGdHrw==","shasum":"56b76da5a74cf84335345dc8e7c372a7c51e5074","tarball":"https://registry.npmjs.org/@a5i/eregex/-/eregex-0.1.5.tgz","fileCount":8,"unpackedSize":2828776,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@a5i%2feregex@0.1.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC96f+9x16g2Bm0Nekhk+93BJfyqlPVOXZLJb9JdVnT5wIgUqGwflBq/ox+GUmuSjX+RB/1+N3GIOer6w9svkzlo+I="}]},"_npmUser":{"name":"alexpav","email":"alexey.pavlyukov@gmail.com"},"directories":{},"maintainers":[{"name":"alexpav","email":"alexey.pavlyukov@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/eregex_0.1.5_1782082520157_0.5078988907257713"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-21T22:55:19.961Z","0.1.5":"2026-06-21T22:55:20.321Z","modified":"2026-06-21T22:55:20.687Z"},"maintainers":[{"name":"alexpav","email":"alexey.pavlyukov@gmail.com"}],"description":"Node.js bindings for eregex, an advanced regular expression engine inspired by mrab-regex","homepage":"https://github.com/a5i/eregex#readme","keywords":["regex","regular-expression","mrab-regex","eregex","text","pattern","native"],"repository":{"type":"git","url":"git+https://github.com/a5i/eregex.git","directory":"crates/eregex-node"},"bugs":{"url":"https://github.com/a5i/eregex/issues"},"license":"Apache-2.0","readme":"# @a5i/eregex (Node.js bindings)\n\nNative Node.js bindings for [`eregex`](https://github.com/a5i/eregex) —\nan advanced regular expression engine for Rust inspired by mrab-regex (the\nPython `regex` module).\n\nThis package exposes eregex's full API to JavaScript / TypeScript via\n[napi-rs](https://napi.rs). All matching logic runs in compiled Rust; the\nJavaScript layer is a thin adapter.\n\n## Features\n\n- Named groups, duplicate group names, **repeated captures**\n- Greedy / lazy / possessive quantifiers, atomic groups `(?>...)`\n- Variable-length lookbehind / lookahead\n- Inline scoped flags `(?i)`, `(?i-m:...)`\n- Backreferences `\\1`, `\\g<name>`, `(?P=name)`\n- **Partial / end-anchored matching** (`findPartial`)\n- `find`, `matchAtStart` (Python `re.match`), `fullMatch` (`re.fullmatch`)\n- `replace`, `replaceAll` with `$1` / `${name}` / `$$` templates\n- `split`, `escape`, and more\n\n## Build\n\nThe native addon is built locally from the Rust core:\n\n```bash\ncd crates/eregex-node\nnpm install\nnpm run build        # release build → index.js + eregex.<platform>.node\n# or: npm run build:debug\n```\n\n`index.js`, `index.d.ts` and the `.node` binary are generated by the build;\nthey are not checked in.\n\n## Quick start\n\n```js\nconst { Regex, IGNORECASE, parseFlags } = require('@a5i/eregex');\n\nconst re = new Regex(String.raw`(\\w+)\\s+(\\w+)`);\nconst m = re.find('hello world');\nconsole.log(m.matched);      // 'hello world'\nconsole.log(m.group(1));     // 'hello'\nconsole.log(m.group(2));     // 'world'\n\n// Flags: pass a bitset of the exported constants, or parse a string.\nnew Regex('hello', IGNORECASE).isMatch('HELLO');         // true\nnew Regex('hello', parseFlags('i')).isMatch('HELLO');    // true\n\n// Repeated captures (signature mrab-regex feature).\nnew Regex(String.raw`(\\w)+`).find('abc').captures(1);    // ['a', 'b', 'c']\n\n// Replace with named groups.\nnew Regex(String.raw`(?P<a>\\d)(?P<b>\\d)`).replaceAll('12 34', '${b}${a}'); // '21 43'\n```\n\n## `Regex`\n\n```ts\nclass Regex {\n  constructor(pattern: string, flags?: number)\n  get pattern(): string\n  get flags(): number         // resolved flags (defaults UNICODE + VERSION1 are added)\n  get captureCount(): number  // capturing groups (group 0 excluded)\n  groupNames(): string[]\n  groupIndex(name: string): number | null\n\n  isMatch(haystack: string): boolean\n  find(haystack: string): Match | null\n  findAt(haystack: string, start: number): Match | null\n  matchAtStart(haystack: string): Match | null   // like re.match\n  fullMatch(haystack: string): Match | null       // like re.fullmatch\n  findAll(haystack: string): Match[]\n  findPartial(haystack: string): PartialMatch | null\n\n  replace(haystack: string, repl: string): string\n  replaceAll(haystack: string, repl: string): string\n  split(haystack: string): string[]\n  dump(): string                                  // parsed AST (debug aid)\n}\n```\n\n`flags` is a bitwise OR of the exported constants: `IGNORECASE`, `MULTILINE`,\n`DOTALL`, `UNICODE`, `ASCII`, `VERBOSE`, `FULLCASE`, `WORD`, `LOCALE`,\n`VERSION0`, `VERSION1`. `parseFlags(\"ims\")` parses a flag string for\n`RegExp`-familiar ergonomics.\n\n## `Match`\n\n```ts\nclass Match {\n  get matched(): string       // whole match (group 0)\n  get input(): string         // original haystack\n  get start(): number         // byte offset\n  get end(): number\n  get span(): { start: number; end: number }\n  get captureCount(): number\n  get groups(): (string | null)[]             // current text, group 0 first\n  get namedGroups(): Record<string, string>\n  get allCaptures(): (string | null)[][]      // repeated-capture history\n  get capturesDict(): Record<string, (string | null)[]>\n\n  group(index: number): string | null\n  namedGroup(name: string): string | null\n  captures(index: number): (string | null)[]\n  capturesByName(name: string): (string | null)[]\n  spanOf(index: number): { start: number; end: number } | null\n}\n```\n\nAll offsets are **byte offsets** (UTF-8), matching Python's `re` and the Rust\ncore. `null` is returned for groups that did not participate.\n\n## Partial matching\n\n`findPartial` is an **end-anchored** search: it asks whether the haystack,\ntaken up to its end, could be the start of a full match. Use it when\nvalidating input as the user types, parsing an incomplete stream, or asking\n\"could more input turn this into a match?\"\n\nIt returns one of three outcomes:\n\n| result                   | meaning                                                        |\n| ------------------------ | ------------------------------------------------------------- |\n| `PartialMatch` (partial) | a valid prefix so far — more input could complete it           |\n| `PartialMatch` (full)    | the input already fully matches (and consumes it to its end)  |\n| `null`                   | a hard mismatch: no possible continuation could match         |\n\nEach capturing group in a partial match is itself in one of three states,\nreported by `groupState(i)`: `'matched'` (fully matched), `'partial'` (entered\nbut not yet completed), or `'none'` (never participated — `group(i)` is `null`).\n\n```ts\nclass PartialMatch {\n  get status(): 'full' | 'partial'\n  get isFull(): boolean\n  get isPartial(): boolean\n  get matched(): string\n  get start(): number          // byte offset where the match starts\n  get end(): number            // byte offset of the input end (always haystack.length)\n  get captureCount(): number\n\n  group(index: number): string | null\n  namedGroup(name: string): string | null\n  groupState(index: number): 'matched' | 'partial' | 'none'\n}\n```\n\nIncremental typing graduates `partial` → `full` → `null`:\n\n```js\nconst re = new Regex(String.raw`abc`);\nre.findPartial('');     // null      (nothing started yet)\nre.findPartial('a');    // partial   .status === 'partial'\nre.findPartial('ab');   // partial\nre.findPartial('abc');  // full      .isFull === true\nre.findPartial('abcd'); // null      ('d' rules out any continuation)\n```\n\nGroup states as a match fills in. With `token=([a-z]+)([0-9]+)([A-Z]+)`:\n\n```js\nconst re = new Regex(String.raw`token=([a-z]+)([0-9]+)([A-Z]+)`);\nconst p = re.findPartial('x token=abc');\n\np.isPartial;            // true\np.matched;              // 'token=abc'\np.start;                // 2    (byte offset of the match)\np.end;                  // 11   (end of input — always, since end-anchored)\np.captureCount;         // 3\n\np.group(1);             // 'abc'   p.groupState(1); // 'matched'\np.group(2);             // ''      p.groupState(2); // 'partial'  (entered, empty so far)\np.group(3);             // null    p.groupState(3); // 'none'     (never entered)\n\nre.findPartial('token=abc123XYZ');  // group 3 -> 'matched', status 'full'\nre.findPartial('x token=abc!');     // null   ('!' rules out any continuation)\n```\n\nNamed groups work the same way:\n\n```js\nconst re = new Regex(String.raw`token=(?P<word>[a-z]+)(?P<num>[0-9]+)`);\nconst p = re.findPartial('token=ab');\np.namedGroup('word');   // 'ab'   (matched)\np.namedGroup('num');    // ''     (partial — empty so far)\n```\n\n## Module-level helpers\n\n```ts\nescape(s: string): string\nescapeSpecialOnly(s: string): string\nescapeLiteralSpaces(s: string): string\nisMatch(pattern: string, haystack: string): boolean  // compiles pattern once\nparseFlags(flagStr: string): number\n```\n\n## Testing\n\n```bash\nnpm test    # runs test/smoke.js\n```\n\n## Layout\n\nThis is one half of eregex's binding story. The same Rust core (`eregex`)\nalso ships Python bindings via `pyo3` + `maturin`. See the project root for\nthe core crate and its feature matrix.\n\n## License\n\nApache-2.0, matching the upstream `mrab-regex` project.\n","readmeFilename":"README.md","_rev":"1-131f6a7b073900e54db239007af10d59"}