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

@balvajs/pr-beacon

v1.2.1

Published

DangerJS-inspired GitHub Action tool that posts and updates a single sticky PR comment with CI info and warnings.

Readme

PR Beacon 🗼

npm License: MIT

A Danger.js-inspired GitHub Action that maintains a single sticky PR comment consolidating CI failures, warnings, and messages from across all your workflow jobs.

Preview of PR Beacon comment on a pull request


Overview

PR Beacon solves the noise problem in pull request CI feedback. Instead of each job posting its own comment — leaving a cluttered thread — every job contributes to one persistent beacon comment that is created on first run and updated in place on every subsequent run.

The beacon is structured into two main areas:

  • Tables — Three severity levels displayed as structured HTML tables at the top of the comment: | Section | Default Icon | Meaning | |---|---|---| | Fails | 🚫 | Blocking issues that must be resolved | | Warnings | ⚠️ | Non-blocking issues worth attention | | Messages | 📖 | Informational notes |

  • Markdowns — Free-form markdown sections appended below the tables.

Each job owns its content slice via a content ID (defaulting to workflow/job). On every run a job replaces only its own slice, leaving other jobs' content untouched. Concurrent writes from parallel jobs are handled with optimistic locking and automatic retries.


Features

  • Sticky comment — one comment per PR, updated in place, never duplicated
  • Multi-job safe — parallel CI jobs can all write without clobbering each other
  • Upsert semantics — each job replaces only its own previously written content
  • Markdown support — render markdown inside table cells or in free-form sections
  • Custom icons — override the default icon per row
  • JSON file input — pass a structured payload file for complex multi-row updates
  • SDK — use the @balvajs/pr-beacon NPM package directly in your own scripts

GitHub Action Usage

Minimal — single message

- uses: Balvajs/pr-beacon@1248b5b89c25f915cc9e65b8dcc99c2e7be8f973 # v1.0.0
  with:
    token: ${{ secrets.GITHUB_TOKEN }}
    message: Build completed successfully.

Report a failure

- uses: Balvajs/pr-beacon@1248b5b89c25f915cc9e65b8dcc99c2e7be8f973 # v1.0.0
  with:
    token: ${{ secrets.GITHUB_TOKEN }}
    fail: Tests failed on Node 22. See the [logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}).

Report a warning

- uses: Balvajs/pr-beacon@1248b5b89c25f915cc9e65b8dcc99c2e7be8f973 # v1.0.0
  with:
    token: ${{ secrets.GITHUB_TOKEN }}
    warn: Bundle size increased by 12 kB.
    warn-icon: 📦

Multi-row payload via JSON file

For complex scenarios — multiple rows, mixed severity levels, or markdown sections — write the payload to a JSON file and point the action at it.

beacon-payload.json

{
  "fails": [{ "message": "Coverage dropped below 80%.", "id": "coverage-check" }],
  "warnings": [
    "Dependency `lodash` has a known vulnerability.",
    { "message": "Build took **4 m 12 s** — consider caching.", "icon": "🐢" }
  ],
  "messages": ["Deployed preview to https://preview.example.com"],
  "markdowns": [
    {
      "id": "coverage-report",
      "message": "## Coverage\n\n| File | % |\n|---|---|\n| index.ts | 94% |\n| utils.ts | 78% |"
    }
  ]
}
- name: Generate payload
  run: node scripts/generate-beacon-payload.js > beacon-payload.json

- uses: Balvajs/pr-beacon@1248b5b89c25f915cc9e65b8dcc99c2e7be8f973 # v1.0.0
  with:
    token: ${{ secrets.GITHUB_TOKEN }}
    json-file: beacon-payload.json

Multi-job workflow

Each job writes to its own content slice. The beacon accumulates content from all jobs.

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test
        continue-on-error: true
      - uses: Balvajs/pr-beacon@1248b5b89c25f915cc9e65b8dcc99c2e7be8f973 # v1.0.0
        if: always()
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          fail: Unit tests failed. # written under ID "{workflow} / {job}"

  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm run lint
        continue-on-error: true
      - uses: Balvajs/pr-beacon@1248b5b89c25f915cc9e65b8dcc99c2e7be8f973 # v1.0.0
        if: always()
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          warn: Lint warnings detected. # written under ID "{workflow} / {job}"

Post a markdown section

- uses: Balvajs/pr-beacon@1248b5b89c25f915cc9e65b8dcc99c2e7be8f973 # v1.0.0
  with:
    token: ${{ secrets.GITHUB_TOKEN }}
    markdown-id: coverage-report
    markdown: |
      ## Coverage

      | File | % |
      |---|---|
      | index.ts | 94% |
      | utils.ts | 78% |

Targeted update — replace content from a previous job

Use content-ids-to-update to remove content written by a specific earlier job before adding new content. This is useful in retry or follow-up jobs.

- uses: Balvajs/pr-beacon@1248b5b89c25f915cc9e65b8dcc99c2e7be8f973 # v1.0.0
  with:
    token: ${{ secrets.GITHUB_TOKEN }}
    content-ids-to-update: 'CI / build'
    message: Build retried and passed.

Inputs

| Input | Required | Default | Description | | ----------------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------------ | | token | Yes | — | GitHub token (GITHUB_TOKEN) used to post PR comments. Requires issues: write and pull-requests: write permissions. | | json-file | No | '' | Path to a JSON file defining the full beacon payload (see schema). | | fail | No | '' | A single failure message added to the Fails table. Supports markdown. | | fail-icon | No | '' | Custom icon for the fail entry (e.g. 💥). | | fail-id | No | '' | Content ID for the fail entry, used for targeted upsert. | | warn | No | '' | A single warning message added to the Warnings table. Supports markdown. | | warn-icon | No | '' | Custom icon for the warn entry. | | warn-id | No | '' | Content ID for the warn entry, used for targeted upsert. | | message | No | '' | A single informational message added to the Messages table. Supports markdown. | | message-icon | No | '' | Custom icon for the message entry. | | message-id | No | '' | Content ID for the message entry, used for targeted upsert. | | markdown | No | '' | A single free-form markdown block appended below the tables. | | markdown-id | No | '' | Content ID for the markdown entry, used for targeted upsert. | | content-ids-to-update | No | '' | Comma-separated list of content IDs to remove before adding new content. | | fail-on-fail-message | No | 'false' | When 'true', the action step exits with a non-zero code if any fail message is present. |

JSON file schema

{
  "fails":    [ "string" | { "message": "string", "icon?": "string", "id?": "string" } ],
  "warnings": [ "string" | { "message": "string", "icon?": "string", "id?": "string" } ],
  "messages": [ "string" | { "message": "string", "icon?": "string", "id?": "string" } ],
  "markdowns": [ "string" | { "message": "string", "id?": "string" } ],
  "options": {
    "contentIdsToUpdate?": [ "string" ]
  }
}

Permissions

Add the following permissions to your workflow or job:

permissions:
  issues: write
  pull-requests: write

SDK

The @balvajs/pr-beacon package exposes the same engine used by the GitHub Action, letting you build the beacon programmatically in your own Node.js scripts.

Installation

npm install @balvajs/pr-beacon

Requirements: Node.js ≥ 24, must be run inside a GitHub Actions environment with GITHUB_TOKEN set.

submitPrBeacon(callback, options?)

The primary entry point. Runs your callback, then submits the accumulated content to the PR comment in one atomic operation.

import { submitPrBeacon } from '@balvajs/pr-beacon';

await submitPrBeacon(async (beacon) => {
  beacon.fail('Tests failed on Node 22.');
  beacon.warn('Bundle size increased by 12 kB.', { icon: '📦' });
  beacon.message('Preview deployed to https://preview.example.com');
  beacon.markdown('coverage', '## Coverage\n\n94% overall.');
});

Callback argument — PrBeacon

| Method | Signature | Description | | ----------------- | ----------------------------- | ------------------------------------------------------------------------ | | fail | (message, options?) => void | Add a row to the Fails table. | | warn | (message, options?) => void | Add a row to the Warnings table. | | message | (message, options?) => void | Add a row to the Messages table. | | markdown | (message, options?) => void | Append a free-form markdown section. | | hasFails | () => boolean | Returns true if any fail was added. | | getPrInfo | () => Promise<PrInfo> | Fetch pull request metadata (title, head SHA, base/head branches, etc.). | | getChangedFiles | () => Promise<File[]> | Fetch the full list of files changed in the PR. |

Row methods accept an optional second argument:

type RowOptions = {
  icon?: string; // override the default section icon
  id?: string; // content ID for targeted upsert (default: "workflow/job")
  markdownToHtml?: boolean; // convert markdown syntax in the message to HTML
};

submitPrBeacon options

type Options = {
  githubToken?: string; // defaults to process.env.GITHUB_TOKEN
  contentIdsToUpdate?: string[]; // content IDs to clear before writing (default: ["workflow/job"])
  shouldFailOnFailMessage?: boolean; // call setFailed() when fails are present
};

Advanced — accessing PR info

import { submitPrBeacon } from '@balvajs/pr-beacon';

await submitPrBeacon(async (beacon) => {
  const pr = await beacon.getPrInfo();
  const files = await beacon.getChangedFiles();

  const hasDbMigration = files.some((f) => f.filename.startsWith('db/migrations/'));

  if (hasDbMigration) {
    beacon.warn(
      `This PR modifies **${files.filter((f) => f.filename.startsWith('db/migrations/')).length}** database migration(s). Please review carefully.`,
      { icon: '🗄️', markdownToHtml: true },
    );
  }

  beacon.message(`PR #${pr.number}: _${pr.title}_`, { markdownToHtml: true });
});

Advanced — multi-job with custom content IDs

import { submitPrBeacon } from '@balvajs/pr-beacon';

await submitPrBeacon(
  (beacon) => {
    beacon.fail('E2E tests failed on Chrome.');
  },
  {
    contentIdsToUpdate: ['e2e-chrome'],
    shouldFailOnFailMessage: true,
  },
);

License

MIT © Balvajs