npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

treffer

v0.5.0

Published

Tiny, bounded RFC 9485 I-Regexp matcher backed by a Thompson NFA.

Readme

treffer

A tiny, bounded RFC 9485 I-Regexp matcher for JavaScript. ~2KB min+gzip, one tiny runtime dependency.

NPM version Build Status NPM downloads Apache-2.0 license

Treffer is Dutch for a hit or match. It parses I-Regexp patterns into Thompson NFAs and evaluates all active states together. Matching never backtracks, so patterns such as (a+)+ have predictable runtime.

Install

npm install treffer

Node.js 22 or newer, ESM only.

Usage

import { compile, match, search } from "treffer";

const isbn = compile("[0-9]{13}");

isbn.match("9780131103627"); // true
isbn.match("ISBN 9780131103627"); // false
isbn.search("ISBN 9780131103627"); // true

match("a|b", "a"); // true
search("\\p{Lu}+", "price: EUR"); // true

API

compile(pattern, options?)

Checks and compiles a pattern once. The returned object has two methods:

  • match(subject) tests the whole subject.
  • search(subject) tests whether any substring matches.
const words = compile("[\\p{L}-]+");

words.match("naïve"); // true
words.search("42 naïve"); // true

match(pattern, subject, options?)

Compiles the pattern and tests the whole subject.

search(pattern, subject, options?)

Compiles the pattern and tests every possible start position in one forward pass.

Use compile() when a pattern will run more than once.

Syntax

Treffer is a checking RFC 9485 implementation. It supports:

  • alternation, concatenation, and groups;
  • ., character classes, ranges, and negated classes;
  • Unicode general categories such as \p{Lu} and \P{N};
  • *, +, ?, and {m,n} quantifiers.

JavaScript-only syntax such as \d, \w, lookarounds, backreferences, and lazy quantifiers is rejected. Use [0-9] instead of \d.

RFC 9485 treats ^ and $ as ordinary characters. Pass { anchors: true } to use them as subject anchors:

const line = compile("^item-[0-9]+$", { anchors: true });
line.search("item-42"); // true
line.search("x item-42"); // false

Errors and limits

Errors produced by Treffer keep their SyntaxError, TypeError, or RangeError class. Syntax and resource errors expose machine-readable properties:

  • code: a stable category;
  • start: the zero-based offset into the pattern, for syntax errors;
  • end: the exclusive offset into the pattern, for syntax errors;
  • limit: the fixed resource limit, for resource errors;
  • actual: the observed value, when it can be determined without weakening early rejection.

Spans are UTF-16 string offsets into the pattern you passed in, so pattern.slice(start, end) is the offending text. They cover the character that made the pattern invalid — the whole quantifier for an impossible bound like a{2,1}, the property name for an unknown \\p{...}, and an empty span at the end of the pattern when it ends early. A resource limit is not a position in the pattern, so those diagnostics carry limit and actual instead and have no span. Their message names the budget and the number it passed — group depth limit of 64 exceeded. Branch on code; the message is for a human reading a stack.

The codes are TREFFER_SYNTAX, TREFFER_MAX_PATTERN_SCALARS, TREFFER_MAX_GROUP_DEPTH, TREFFER_MAX_QUANTIFIER_DIGITS, TREFFER_MAX_REPETITIONS, TREFFER_MAX_NFA_STATES, TREFFER_MAX_SUBJECT_SCALARS, and TREFFER_MAX_TRANSITIONS. API TypeErrors have no code.

Errors thrown by caller-provided option accessors are host errors. Treffer passes them through unchanged and does not attach diagnostic fields.

Use isDiagnostic(error) when a host needs to distinguish those errors. It returns true only for errors created by the same Treffer module instance. Copying documented code, start, end, limit, and actual properties onto another error does not authenticate it. A diagnostic from another installed copy or module instance also returns false.

Relocating a diagnostic

An embedder that compiles patterns out of a larger document — a filter selector in a JSONPath query, a validation rule in a schema — reports the fault in its own coordinates, not the pattern's. relocate(diagnostic, { prefix, offset }) returns the copy to re-throw:

import { compile, isDiagnostic, relocate } from "treffer";

try {
  compile(pattern);
} catch (error) {
  if (!isDiagnostic(error)) throw error;
  // where the pattern literal starts inside the surrounding query
  throw relocate(error, { prefix: "$.a[?match(@.b, ...)]: ", offset: 16 });
}

The copy keeps the original's class, prepends prefix to the message verbatim, moves the span when there is one, and carries every other field across. It is registered exactly as the original was, so it passes isDiagnostic. The original is left untouched. Relocation belongs here rather than in the embedder because authentication is by identity: a copy an embedder builds itself cannot be authenticated, and a field added to a diagnostic here would be a field the embedder's copy silently drops. Passing anything but a Treffer diagnostic throws a TypeError.

offset is right whenever the pattern was a verbatim slice of your text. It is wrong when your text was decoded first — a pattern read out of a JSON string literal, where \\d is three characters standing for two and every later offset slides. Pass span instead and name the region the pattern came from:

// The pattern reached Treffer after JSON unescaping, so no shift can reach the
// offending character. Point at the literal that carried it.
throw relocate(error, { prefix: "$.a[?match(@.b, ...)]: ", span: [16, 24] });

span replaces the span outright and wins when both are given. Neither option adds a span to a diagnostic that had none.

The fixed safety limits are:

  • 4,096 Unicode scalar values per pattern;
  • 64 nested groups;
  • 4,096 NFA states;
  • 1,024 repetitions in a range quantifier;
  • six digits per quantifier bound;
  • one million Unicode scalar values per subject;
  • one million state transitions per match.

Runtime is bounded by the subject length times the number of active NFA states. Character-class checks count toward the transition budget. Treffer validates Unicode scalar values and rejects lone surrogates.

Content Security Policy

Treffer parses patterns into data structures and closures. It generates no JavaScript source and works under a strict Content Security Policy.

Environments

Node.js 22 and newer, ESM only. Browser use is supported through a standards-based ESM bundler in environments supporting ES2024. Direct <script> globals, UMD, and CommonJS builds are not provided.

Shipping CommonJS alongside ESM would put two copies of the core in any process that mixed require and import. Each copy would have its own diagnostic identity, so isDiagnostic would return false across the seam.

Contributing

git clone https://github.com/getquario/treffer.git
cd treffer
npm install
npm run check

npm run check is the local gate. Conventions for this repo live in AGENTS.md.

License

Copyright 2026 Robin van der Vleuten

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.