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

bulk-release

v2.21.0

Published

zx-based alternative for multi-semantic-release

Readme

zx-bulk-release

zx-based alternative for multi-semantic-release

CI Maintainability Test Coverage npm (tag)

Key features

  • Conventional commits trigger semantic releases.
  • Automated cross-pkg version bumping.
  • Predictable toposort-driven flow.
  • No default branch blocking (no release commits).
  • Pkg changelogs go to changelog branch (configurable).
  • Docs are published to gh-pages branch (configurable).
  • No extra builds. The required deps are fetched from the pkg registry (npmFetch config opt).

Roadmap

  • [x] Store release metrics to meta.
  • [ ] ~~Self-repair. Restore broken/missing metadata from external registries (npm, pypi, m2)~~. Tags should be the only source of truth
  • [ ] Multistack. Add support for java/kt/py.
  • [ ] Semaphore. Let several release agents to serve the monorepo at the same time.

Requirements

  • macOS / linux
  • Node.js >= 16.0.0
  • npm >=7 / yarn >= 3
  • ~~wget~~
  • tar
  • git

Usage

Install

yarn add zx-bulk-release

CLI

GH_TOKEN=ghtoken GH_USER=username NPM_TOKEN=npmtoken npx zx-bulk-release [opts]

| Flag | Description | Default | |------------------------------|---------------------------------------------------------------------------------------------------------------------------|------------------| | --ignore | Packages to ignore: a, b | | | --include-private | Include private packages | false | | --concurrency | build/publish threads limit | os.cpus.length | | --no-build | Skip buildCmd invoke | | | --no-test | Disable testCmd run | | | --no-npm-fetch | Disable npm artifacts fetching | |
| --only-workspace-deps | Recognize only workspace: deps as graph edges | | | --dry-run / --no-publish | Disable any publish logic | | | --report | Persist release state to file | | | --snapshot | Disable any publishing steps except of npm and publishCmd (if defined), then push packages to the snapshot channel | |
| --recover | Remove orphan git tags (tag exists but npm publish failed) and exit. Re-run without this flag to release. | | | --debug | Enable zx verbose mode | | | --version / -v | Print own version | |

JS API

import { run } from 'zx-bulk-release'

const cwd = '/foo/bar'
const env = {GH_TOKEN: 'foo', NPM_TOKEN: 'bar'}
const flags = {dryRun: true}

await run({
  cwd,    // Defaults to process.cwd()
  flags,  // Defaults to process.env
  env     // Defaults to minimist-parsed `process.argv.slice(2)`
})

Config

cosmiconfig

Any cosmiconfig compliant format: .releaserc, .release.json, .release.yaml, etc in the package root or in the repo root dir.

{
  "buildCmd": "yarn && yarn build",
  "testCmd": "yarn test",
  "npmFetch": true,
  "changelog": "changelog",
  "ghPages": "gh-pages",
  "diffTagUrl": "${repoPublicUrl}/compare/${prevTag}...${newTag}",
  "diffCommitUrl": "${repoPublicUrl}/commit/${hash}"
}

Command templating

buildCmd, testCmd and publishCmd support ${{ variable }} interpolation. The template context includes all pkg fields and the release context (flags, git, env, etc):

{
  "buildCmd": "yarn build --pkg=${{name}} --ver=${{version}}",
  "testCmd": "yarn test --scope=${{name}}",
  "publishCmd": "echo releasing ${{name}}@${{version}}"
}

Available variables include: name, version, absPath, relPath, and anything from pkg.ctx (e.g. git.sha, git.root, flags.*).

Changelog diff URLs

By default, changelog entries link to GitHub compare/commit pages. Override diffTagUrl and diffCommitUrl to customize for other platforms (e.g. Gerrit):

{
  "diffTagUrl": "https://gerrit.foo.com/plugins/gitiles/${repoName}/+/refs/tags/${newTag}",
  "diffCommitUrl": "https://gerrit.foo.com/plugins/gitiles/${repoName}/+/${hash}%5E%21"
}

Available variables: repoName, repoPublicUrl, prevTag, newTag, name, version, hash, short.

GitHub Enterprise

Set ghUrl to point to your GHE instance. API URL (ghApiUrl) is derived automatically.

{
  "ghUrl": "https://ghe.corp.com"
}

Or via env: GH_URL=https://ghe.corp.com / GITHUB_URL=https://ghe.corp.com.

env vars

export const parseEnv = (env = process.env) => {
  const {GH_USER, GH_USERNAME, GITHUB_USER, GITHUB_USERNAME, GH_TOKEN, GITHUB_TOKEN, GH_URL, GITHUB_URL, NPM_TOKEN, NPM_REGISTRY, NPMRC, NPM_USERCONFIG, NPM_CONFIG_USERCONFIG, NPM_PROVENANCE, NPM_OIDC, ACTIONS_ID_TOKEN_REQUEST_URL, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL} = env

  return {
    ghUser:             GH_USER || GH_USERNAME || GITHUB_USER || GITHUB_USERNAME || 'x-access-token',
    ghToken:            GH_TOKEN || GITHUB_TOKEN,
    ghUrl:              GH_URL || GITHUB_URL || 'https://github.com',
    npmToken:           NPM_TOKEN,
    // npmConfig suppresses npmToken
    npmConfig:          NPMRC || NPM_USERCONFIG || NPM_CONFIG_USERCONFIG,
    npmRegistry:        NPM_REGISTRY || 'https://registry.npmjs.org',
    npmProvenance:      NPM_PROVENANCE,
    // OIDC trusted publishing: https://docs.npmjs.com/trusted-publishers/
    npmOidc:            NPM_OIDC || (!NPM_TOKEN && ACTIONS_ID_TOKEN_REQUEST_URL),
    gitCommitterName:   GIT_COMMITTER_NAME || 'Semrel Extra Bot',
    gitCommitterEmail:  GIT_COMMITTER_EMAIL || '[email protected]',
  }
}

OIDC Trusted Publishing

npm now supports OIDC trusted publishing as a replacement for long-lived access tokens. To enable:

  1. Configure a trusted publisher for each package on npmjs.com (link your GitHub repo and workflow)
  2. Set NPM_OIDC=true in your workflow environment
  3. Ensure your GitHub Actions workflow has permissions: { id-token: write }
  4. Make sure each package.json includes the repository field matching your GitHub repo URL

OIDC mode is also auto-detected when NPM_TOKEN is not set and ACTIONS_ID_TOKEN_REQUEST_URL is present (GitHub Actions with id-token: write permission).

When OIDC is active, NPM_TOKEN and NPMRC are ignored for publishing and --provenance is enabled automatically.

Selective testing along the change graph

In a monorepo, --dry-run combined with --no-build lets you run tests only for packages affected by the current changes — following the dependency graph, without publishing anything. This gives you a precise CI check scoped to what actually changed:

npx zx-bulk-release --dry-run --no-build

See antongolub/misc for a real-world example of this pattern.

Demo

Implementation notes

Flow

topo ─► contextify ─► analyze ──► build ──► test ──► publish ─► clean
         (per pkg)    (per pkg)   (per pkg)  (per pkg) (per pkg)

@semrel-extra/topo resolves the release queue respecting dependency graphs. The graph allows parallel execution where the dependency tree permits; memoizeBy prevents duplicate work when a package is reached by multiple paths.

By default, packages marked as private are omitted. Override with --include-private.

Steps

Each step has a uniform signature (pkg, ctx):

  • contextify — resolves per-package config, latest release metadata, and git context.
  • analyze — determines semantic changes, release type, and next version.
  • build — runs buildCmd (with dep traversal and optional npm artifact fetch).
  • test — runs testCmd.
  • publish — orchestrates the publisher registry: prepare (serial) → run (parallel) → rollback on failure.
  • clean — restores package.json files and unsets git user config.

Set config.releaseRules to override the default rules preset:

[
  {group: 'Features', releaseType: 'minor', prefixes: ['feat']},
  {group: 'Fixes & improvements', releaseType: 'patch', prefixes: ['fix', 'perf', 'refactor', 'docs', 'patch']},
  {group: 'BREAKING CHANGES', releaseType: 'major', keywords: ['BREAKING CHANGE', 'BREAKING CHANGES']},
]

Publishers

Publish targets are a registry of {name, when, prepare?, run, undo?, snapshot?} objects:

  • meta — pushes release metadata to the meta branch (or as a GH release asset).
  • npm — publishes to the npm registry.
  • gh-release — creates a GitHub release with optional file assets.
  • gh-pages — pushes docs to a gh-pages branch.
  • changelog — pushes a changelog entry to a changelog branch.
  • cmd — runs a custom publishCmd.

Teardown walks the registry in reverse, calling undo() on each publisher for rollback/recovery.

Tags

Lerna tags (like @pkg/[email protected]) are suitable for monorepos, but they don’t follow semver spec. Therefore, we propose another contract:

'2022.6.13-optional-org.pkg-name.v1.0.0-beta.1+sha.1-f0'
// date    name                  version             format

Note, npm-package-name charset is wider than semver, so we need a pinch of base64url magic for some cases.

'2022.6.13-examplecom.v1.0.0.ZXhhbXBsZS5jb20-f1'
// date    name       ver    b64             format

Anyway, it's still possible to override the default config by tagFormat option:

| tagFormat | Example | |-----------|------------------------------------------------------| | f0 | 2022.6.22-qiwi.pijma-native.v1.0.0-beta.0+foo.bar-f0 | | f1 | 2022.6.13-examplecom.v1.0.0.ZXhhbXBsZS5jb20-f1 | | lerna | @qiwi/[email protected] | | pure | 1.2.3-my.package |

Meta

Each release gathers its own meta. It is recommended to store the data somehow to ensure flow reliability.:

  • Set meta: {type: 'asset'} to persist as gh asset.
  • If set meta: {type: null} the required data will be fetched from the npm artifact.
  • Otherwise, it will be pushed as a regular git commit to the meta branch (default behaviour).

2022-6-26-semrel-extra-zxbr-test-c-1-3-1-f0.json

{
  "META_VERSION": "1",
  "name": "@semrel-extra/zxbr-test-c",
  "hash": "07b7df33f0159f674c940bd7bbb2652cdaef5207",
  "version": "1.3.1",
  "dependencies": {
    "@semrel-extra/zxbr-test-a": "^1.4.0",
    "@semrel-extra/zxbr-test-d": "~1.2.0"
  }
}

Report

Release process state is reported to the console and to a file if --report flag is set to /some/path/release-report.json, for example.

{
  status: 'success',            // 'sucess' | 'failure' | 'pending'
  error: null,                  // null or Error
  queue: ['a', 'b', 'c', 'd']   // release queue
  packages: [{
    name: 'a',
    version: '1.1.0',
    path: '/pkg/abs/path',
    relPath: 'pkg/rel/path',
    config: {                   // pkg config
      changelog: 'changelog',
      npmFetch: true
    },                 
    changes: [{                 // semantic changes
      group: 'Features',
      releaseType: 'minor',
      change: 'feat: add feat',
      subj: 'feat: add feat',
      body: '',
      short: '792512c',
      hash: '792512cccd69c6345d9d32d3d73e2591ea1776b5'
    }],                  
    tag: {
      version: 'v1.1.0',
      name: 'a',
      ref: '2022.6.22-a.v1.1.0-f0'
    },
    releaseType: 'minor',       // 'major' | 'minor' | 'patch'
    prevVersion: '1.0.0'        // previous version or null
  }, {
    name: 'b',
    // ...
  }],
  events: [
    {msg: ['zx-bulk-release'], scope:'~', date: 1665839585488, level: 'info'},
    {msg: ['queue:',['a','b']], scope:'~', date: 1665839585493, level: 'info'},
    {msg: ["run buildCmd 'yarn && yarn build && yarn test'"], scope: 'a', date: 1665839585719, level:'info'},
    // ...
  ]
}

Output

Compact and clear logs

Run npm_config_yes=true npx zx-bulk-release
zx-bulk-release
[@semrel-extra/zxbr-test-a] semantic changes [
  {
    group: 'Fixes & improvements',
    releaseType: 'patch',
    change: 'fix(a): random',
    subj: 'fix(a): random',
    body: '',
    short: '6ff25bd',
    hash: '6ff25bd421755b929ef2b58f35c727670fd93849'
  }
]
[@semrel-extra/zxbr-test-a] run cmd 'yarn && yarn build && yarn test'
[@semrel-extra/zxbr-test-a] push release tag 2022.6.27-semrel-extra.zxbr-test-a.1.8.1-f0
[@semrel-extra/zxbr-test-a] push artifact to branch 'meta'
[@semrel-extra/zxbr-test-a] push changelog
[@semrel-extra/zxbr-test-a] publish npm package @semrel-extra/zxbr-test-a 1.8.1 to https://registry.npmjs.org
[@semrel-extra/zxbr-test-a] create gh release
[@semrel-extra/zxbr-test-b] semantic changes [
  {
    group: 'Dependencies',
    releaseType: 'patch',
    change: 'perf',
    subj: 'perf: @semrel-extra/zxbr-test-a updated to 1.8.1'
  }
]
[@semrel-extra/zxbr-test-b] run cmd 'yarn && yarn build && yarn test'
[@semrel-extra/zxbr-test-b] push release tag 2022.6.27-semrel-extra.zxbr-test-b.1.3.5-f0
[@semrel-extra/zxbr-test-b] push artifact to branch 'meta'
[@semrel-extra/zxbr-test-b] push changelog
[@semrel-extra/zxbr-test-b] publish npm package @semrel-extra/zxbr-test-b 1.3.5 to https://registry.npmjs.org
[@semrel-extra/zxbr-test-b] create gh release
[@semrel-extra/zxbr-test-d] semantic changes [

References

License

MIT