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

@ham2k/extension-tools

v0.4.0

Published

Build and package extensions for the Ham2K Logger

Readme

@ham2k/extension-tools

Build and package extensions for the Ham2K Logger.

npm install --save-dev @ham2k/extension-tools esbuild

Starting one

npx h2kext-init w7abc-notes

Writes a whole small extension — manifest, a panel that says hello, the build script, a tsconfig — into the working directory, and refuses rather than overwrite anything already there. An existing package.json is added to, never replaced.

It also writes a .prettierrc.json and the format / format:check scripts, and what it scaffolds is already in that style — so npm run format:check passes on an untouched project, and the code an agent adds to it lands formatted the same way. Configure Prettier already, anywhere it looks, and yours is left in charge: no config is written, and the command says so.

It takes the key rather than defaulting one: a placeholder like n0call-my-extension validates, so it would ship, which is the one outcome the naming convention exists to stop.

The AGENTS.md it writes points at @ham2k/extension-sdk's own — the hook reference, the sandbox's limits, and what to read for what. That matters most if something other than you is writing the code: an agent reads a project's AGENTS.md unprompted, and otherwise never learns the reference is sitting in node_modules.

Building

import { build } from 'esbuild'
import { buildExtension } from '@ham2k/extension-tools'

await buildExtension(build, { dir: import.meta.dirname })

Reads your manifest.json, compiles src/index.ts into build/index.js, and copies the manifest alongside it. The esbuild settings it applies are requirements of the runtime rather than preferences: one IIFE because the sandbox evaluates a script and has no module loader, neutral because there is no Node and no DOM, and es2020 because that is what its engine speaks.

esbuild is yours to bring — the preset takes your build function rather than importing its own, so there is only ever one copy of it.

Everything your manifest's sharedDependencies names becomes a lookup on the library the host already holds, which is worth roughly thirty times the bundle size. Using one without declaring it fails the build rather than quietly shipping a private copy; pass inline: ['liquidjs'] if that copy is what you want, which is how you use a version the host does not carry.

Packaging

npx h2kext-pack build -o my-extension.h2kext

Packages a directory holding a built index.js, a manifest.json and an optional assets/. It packages; it does not compile.

It reports every problem at once, and refuses what would install cleanly and then misbehave where you cannot see it. Extension keys are <callsign>-<name> — the namespace every author of this software already holds, so two people who never met cannot ship the same key.

Publishing to the catalog

export H2K_CATALOG_TOKEN=h2kc_…
npx h2kext-publish build --notes "What changed"

Uploads a bundle to the Ham2K extension catalog, where the app finds it. Sign in there and make a token under /publish; the command reads it from H2K_CATALOG_TOKEN and from nowhere else — there is no flag for it, because a flag lands in your shell history — and stores it nowhere. Publishing needs a grant for your callsign prefix, which the same page is where you ask for.

A directory is packaged first, exactly as h2kext-pack would; a .h2kext you already made is sent as it is. A bundle must be 16 MiB or smaller: the app never downloads more than that, the catalog refuses more than that, and the command refuses it before sending a byte. A version's bytes are frozen the first time a reviewer approves them: from then on the same version with different bytes is refused, so a fix is a new version.

Release notes go up in a second request, once the upload is accepted. If that request is refused, the version is still published — the command says so and exits 1, and you can write the notes on the site, where they can be edited at any time.

The result line reads published w7abc-notes 1.2.3 → prod (pending review). "Pending review" means a Ham2K reviewer has not looked yet — the version is stored, but nobody can install it until they approve it, and your dashboard on the site shows the outcome and any note. Every refusal prints the catalog's reason and exits 1; nothing is retried.

A channel says how finished a release is — stable, unstable, bleeding — not which app edition is asking. They cascade upward, so stable reaches everyone and bleeding reaches only somebody who asked for it. --channel is optional and defaults to stable.