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

@nielspeter/eess

v0.8.0

Published

eess kernel — the dialect-independent rule engine (RuleBuilder, Predicate, Condition, baseline, formatters). Specifications you can run.

Readme

@nielspeter/eess

The dialect-independent kernel of the eess family.

This package is the engine every eess dialect runs on. It knows nothing about TypeScript, Mermaid, or any specific artifact format; it is generic over the element type being validated.

What's in here

  • RuleBuilder<T, P> — the fluent .that().should().check() chain, generic over element type T and project handle P
  • Predicate<T>, Condition<T>, Matcher<V> — the interfaces dialects implement
  • ArchViolation — the violation model; each dialect supplies its own element→violation adapter
  • Baseline mode, diff-aware filtering, exclusions, and the terminal/GitHub/JSON formatters
  • definePredicate / defineCondition — extension points for advanced users

Two entry points

@nielspeter/eess publishes two:

| Specifier | What it is | | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | @nielspeter/eess | The public API. Everything below is documented here, and a change to it is a versioned change. | | @nielspeter/eess/internal | Family plumbing the sibling dialects share — cache registries, stderr guards, suppression counters, identity hashing, glob-tree internals. Not API. |

You want the root. /internal exists so the dialects can share one engine without each name they touch becoming a public commitment; it is published because npm has no way to ship a package-private module, not because it is for you. Nothing there is documented, and no dialect re-exports it as its own API. Two names — DiskSet and OnDisk — are also on @nielspeter/eess-ts's root, where they are eess-ts's own types that happen to share a name; that is the whole of the overlap.

Breaking something here is still a versioned change and still needs a changeset (ADR-011 clause 4). What you are not owed is migration guidance, because it is not API. See ADR-011.

If a symbol you used disappeared from the root, it moved here — the import path is the only change.

Kernel API reference

The fluent surface (RuleBuilder, Predicate, Condition, the combinators, the formatters) is covered in the docs site. This section covers the supporting types you will meet in signatures.

Reporting and severity

A preset never emits on its own — the caller owns emission (ADR-008).

  • ReportMode — 'throw' | 'return' | 'warn'. What a finisher does with findings: raise an ArchRuleError, hand them back, or print them as advisory and continue.
  • ReportOptions — the emission controls: format (terminal/json/github) and reason, the rationale threaded into the output.
  • RuleSeverity — 'error' | 'warn' | 'off', the value of a per-rule override.
  • PresetBaseOptions — what every preset accepts: an overrides map of rule id → RuleSeverity, plus the reporting controls. Extend it when you write a preset so { report: 'return' } works without your own plumbing.
  • RuleBuilderLike — the one-method shape (violations()) a preset needs from anything it dispatches, so a preset is not coupled to a dialect's builder class.
  • Dispatchable — what dispatchRule accepts: a builder that can produce violations and name itself, so the dispatcher can apply a per-rule override to it. Strictly more than RuleBuilderLike; that one is the narrower shape used where only the findings matter.
  • isArchRuleError(err) — use this instead of instanceof ArchRuleError when catching. instanceof compares class identity, so it returns false whenever two copies of the package are on disk — a nested node_modules, or a rule file loaded through a separate module registry. The check is structural (the stamped name plus the violations array), so it survives that. The thrown shape itself — violations: ArchViolation[] — is public either way.
  • ArchConfigError / isArchConfigError(err) — thrown when a rule is misconfigured (bad arguments to a condition, a malformed rule file), as opposed to ArchRuleError, which means the architecture is wrong. Two different faults with two different audiences: one tells a rule author they mistyped something, the other tells a team their code drifted. Carries subject — the condition, predicate or file at fault — so a caller can route the failure without parsing the message. isArchConfigError is structural for the same two-copies-on-disk reason as isArchRuleError.
  • ArchFix — a machine-applicable edit on a violation: file, a [start, end) character span, and the replacement text. This is what --fix applies.

The JSON report

--format json emits an ArchJsonReport: a summary (total, errors, warnings, reason), then ArchJsonViolation entries, plus ArchJsonSuppression for findings silenced by an eess-exclude comment and ArchJsonUntestedAllowlist for allowlists nothing exercised. Import these to type a tool that consumes eess output rather than re-declaring the shape.

Filtering a run

  • BaselineFilter — decides whether an already-accepted finding is suppressed by the baseline file.
  • DiffFilterLike — decides whether a finding falls inside the changed region, for --changed runs.

Both are interfaces so a host can supply its own; the kernel ships implementations.

Correspondence

correspondence() binds two Selections and checks they agree in both directions.

  • CorrespondenceOptions — its argument: the left and right selections, a join strategy, and optional per-side suggest callbacks for the two-sided message.
  • KeyBy — the fast join: a key extracted per element, giving an O(n+m) match. Prefer it. The fallback matchBy predicate is O(n×m).
  • RelationSpec — what preserveRelations() takes: how to read edges from each side, so the check covers relationships and not just membership.
  • ElementInfo — the name / file / line a dialect reports for one element, which is what the message and the violation's location are built from.

Declaring globs (for custom predicates)

A predicate written with definePredicate should declare the globs it matches on. Without that it is opaque, so a vacuity diagnosis cannot tell "matched nothing because the glob is dead" from "matched nothing because nothing was there" — and any or() containing it becomes undiagnosable too.

  • globNode(leaf) — wrap a single declared glob into a tree. The common case.
  • globAnyOf(globs, kind, base?) — declare several alternatives at once.
  • DeclaredGlob — one glob with its kind and base. GlobKind is what the glob matches against ('file-path' | 'parent-dir' | 'import-target' | 'specifier' | 'literal') and GlobBase is what it is resolved against ('absolute' | 'tsconfig-relative' | 'normalized').
  • GlobTree<L> — the and/any tree the declarations form; DeclaredGlobs is that tree over DeclaredGlob, and GlobLeaf<L> is one position in it.
  • OpaqueGlob — the explicit "this branch cannot be described" marker. Honest, and it is what keeps an undeclared predicate from silently reading as a dead one.
  • GlobSite — a declared glob plus where it was written; GlobPosition is that where ('selector' | 'discovery' | 'condition' | 'exclusion'), which is what lets a diagnosis point at the glob you actually got wrong.
  • GlobNode — the tree over GlobSite, i.e. a fully-stamped declaration ready to diagnose.

Coverage evidence

  • EdgeCoverage — one rule's evidence: how many subjects it saw, how many edges it actually tested, and a reason when that number is zero.
  • UntestedReason — why zero: 'no-edges' (nothing to test, usually correct), 'all-filtered' (edges existed and every one was filtered out), 'none-matched' (edges existed and none matched the allowlist). The distinction is the point — a rule that tested nothing should say which of the three it was, per ADR-010.

Who uses it

You normally don't install this directly — you install a dialect:

Install @nielspeter/eess directly only when authoring a new dialect or a cross-dialect tool on top of the kernel.

If that is you, /internal is your surface, not a wall. The two sentences above look like they contradict each other — "not for you", then "install it if you are writing a dialect" — so to be explicit: a dialect author is exactly who /internal exists for, and the five in this repo import from it. What the root alone gives you is the rule-authoring vocabulary, not the engine. Nothing there is documented and it changes without migration notes, so treat it as a contract you re-check on each kernel bump; if you are building a dialect you intend to publish, open an issue and we will work out what belongs on the root.

License

MIT