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

@hearthforge/repo-guards

v0.1.0

Published

Repository guards every HearthForge repo runs in CI — first: no tracked file names the maintainer's personal namespace

Readme

@hearthforge/repo-guards

Repository guards every HearthForge repository runs in CI, published once so each repository judges its tree with the same code instead of a vendored copy.

Security note: a leak guard, not a secret scanner

The namespace guard stops the maintainer's personal namespace (a registry owner, a package scope, a repository path) from landing where the hearthforge organisation's belongs. It does not look for credentials, tokens or keys. For those, use gitleaks and GitHub secret scanning with push protection.

Use

pnpm add -D @hearthforge/repo-guards
{ "scripts": { "test:namespace": "hf-repo-guards namespace" } }

hf-repo-guards namespace runs from anywhere inside the repository and judges every tracked file (git ls-files — what CI checks out, never untracked scratch):

  • every file path, so a file named after the handle is found even when its content is clean;
  • every line of every file the config does not skip, as written and as decoded.

Exit codes: 0 clean, 1 findings (each as file:line: token — excerpt), 2 the guard could not run (not a repository, nothing tracked, a bad config, an unknown argument). A guard that cannot run never reports clean.

What is a hit

The spellings are the HearthForge spec in src/personal-namespace.mjs — the one place that names them. The engine (src/namespace-matcher.mjs) knows no handle of its own:

  • a handle spelling is a hit as its own token — separated by any non-alphanumeric character or a camelCase hump — in any case, with any trailing digits;
  • a surname spelling is a hit anywhere;
  • the maintainer's GitHub login is exempt only as a mention: @<login> or the profile link github.com/<login>, and only where the login ends. Followed by /, \, _, -, or by ., :, @ and a name, it is a path, a host, an image tag or a mailbox, and a hit. A bare login is a hit too: OWNER=<login> is a value some tool builds a path from.
  • the owner's legal name is not a namespace — the CLA, LICENSE and copyright lines must name the rights holder.

Each line is judged as written and again decoded: percent-encoding, HTML character references and JavaScript escapes undone, NFKC-normalised, invisible characters removed and common look-alike letters folded onto Latin. The exact rules — and what is deliberately out of scope, base64 included — are the header of src/namespace-matcher.mjs; this README does not restate them. A symlink is judged by its target string and never followed; a UTF-16 file (with a byte-order mark) is decoded.

Exemptions: repo-guards.config.json

At the repository root (or --config <path>, relative to the root):

{
  "namespace": {
    "skip": [{ "file": "fixtures/blob.bin", "reason": "binary fixture; a UTF-8 read of it is noise" }],
    "allow": [
      {
        "file": ".github/workflows/cla.yml",
        "line": "allowlist: \"<login>,dependabot[bot]\"",
        "reason": "the CLA action matches bare GitHub logins"
      }
    ]
  }
}
  • allow exempts that exact line (compared trimmed) in that file only. Every entry needs a reason.
  • An entry that exempts nothing — the line is gone or is no longer a hit — is itself a finding, and so is a skip naming an untracked file. A stale exemption is a hole waiting for the next line.
  • skip excludes a file's content; its path is still judged. Prefer allow: a skipped file is a file nobody reads.
  • The config file is judged too, raw, line by line: the only hit it may carry is a line that is exactly one "line": "…" member naming an allow entry — so keep it formatted one member per line. Unknown keys, blank reasons and duplicate entries are refused.

API

import { createNamespaceMatcher, findNamespaceFindings, parseNamespaceConfig, personalNamespaceTokens } from "@hearthforge/repo-guards";

createNamespaceMatcher({ tokens, anywhere, logins }) compiles any spec into a pure { tokens(line), matches(line) }; findNamespaceFindings({ files, config, tokens }) is the guard's decision with no I/O.

Changing the spec

Edit src/personal-namespace.mjs and its table (test/personal-namespace.test.mjs) here, add a changeset, release. Consuming repositories pick the new version up as an ordinary dependency bump; nothing is copied.

Releasing the first version

npm Trusted Publishing can only be configured on a package that already exists, so the release workflow cannot publish 0.1.0 of a brand-new name over OIDC. Before the "Version Packages" PR that carries it is merged:

  1. From that PR's head, the owner publishes the one version by hand: pnpm --filter @hearthforge/repo-guards publish --access public, with a passkey.
  2. On npmjs.com, add the Trusted Publisher: repository hearthforge/hearthforge-sdk, workflow publish.yml, environment npm.
  3. Verify it anonymously with npm view @hearthforge/[email protected] dist.tarball, before any repository pins it.

The workflow's pnpm publish -r then skips the already-published version. Check whether the release step still created the @hearthforge/[email protected] tag and GitHub release; if not, create them by hand. Every later version goes through the workflow.