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

runtime-reporter

v0.4.7

Published

Replace ad-hoc logging with structured, code-based messaging

Readme

Runtime Reporter

Replace ad-hoc logging with structured, code-based messaging.

Runtime Reporter provides centralized, type-safe reporting that is convenient in development and secure in production.

// ./src/runtime-reporter.ts

import { createReporter } from "runtime-reporter";

export const reporter = createReporter({
    ERR01: "Something went wrong",
});

// ./src/MyComponent.ts

import { reporter } from "./runtime-reporter";

export function MyComponent() {
    useEffect(() => {
        reporter.error("ERR01");
    }, []);
}

Why?

Runtime Reporter solves these problems:

  • duplicated log messages
  • inconsistent error wording
  • fragile test assertions
  • accidental exposure of sensitive data

by introducing these features:

  • centralized and standardized error messaging
  • stable error codes for debugging and error tracking
  • test assertions without string duplication
  • safer production output (no sensitive data exposure)

in a lightweight, self-contained package (less than 1 KB gzipped).

Who is this for?

  • Front-end frameworks and libraries
  • Projects that need stable error codes
  • Teams replacing custom logging systems
  • Projects that want safer production output

Installation

npm install runtime-reporter

Quick start

A full, copy-and-paste example of how to use Runtime Reporter in your project:

import { createReporter, type RuntimeReporterMessages } from "runtime-reporter";

const messages: RuntimeReporterMessages<
    | {
          code: "ERR01";
          template: "{{ componentName }} failed to mount";
          tokens: "componentName";
      }
    | {
          code: "ERR02";
          template: "Failed to load configuration";
      }
    | {
          code: "ERR03";
          template: "Failed to fetch {{ resource }} from {{ url }}";
          tokens: "resource" | "url";
      }
> = {
    ERR01: "{{ componentName }} failed to mount",
    ERR02: "Failed to load configuration",
    ERR03: "Failed to fetch {{ resource }} from {{ url }}",
};

/** The runtime reporter for <project-name> */
const reporter = createReporter(messages);

export default reporter;

Features

If you are new to Runtime Reporter, take a moment to explore its core features.

1. Code-based messaging

Replace inline strings with centralized, code-based identifiers.

// Without runtime-reporter
console.log("Something went wrong");
// ❌ Logs: "Something went wrong"

// With runtime-reporter
reporter.log("ERR01");
// ✅ Logs: "Something went wrong (ERR01)"

2. Dynamic messages

Inject runtime data into your messages via message templates and tokenized variables.

const reporter = createReporter({
    ERR01: "{{ componentName }} failed to mount",
});

reporter.error("ERR01", { componentName: "MyComponent" });
// Logs: "MyComponent failed to mount (ERR01)"

3. Development vs. production

Pass an empty object to the createReporter function in production environments for better security and a smaller bundle size.

const reporter = createReporter(
    process.env.NODE_ENV === "production" ? ({} as typeof messages) : messages
);

Development environments get detailed messaging, while production environments get as little as possible.

reporter.error("ERR01");
// In development, it logs: "Something went wrong (ERR01)"
// In production, it does not log

reporter.fail("ERR01");
// In development, it throws: "Something went wrong (ERR01)"
// In production, it throws: "An error occurred (ERR01)"

4. Type safety

Annotate your messages to get autocomplete and compile-time validation for message codes and token names.

const messages: RuntimeReporterMessages<{
    code: "ERR01";
    template: "{{ componentName }} failed to mount";
    tokens: "componentName";
}> = {
    ERR01: "{{ componentName }} failed to mount",
};

const reporter = createReporter(messages);

// ✅ Autocomplete
reporter.error("ERR01", { componentName: "MyComponent" });

// ❌ TypeScript Error: "ERR02" is not a valid message code
reporter.error("ERR02", { componentName: "MyComponent" });

// ❌ TypeScript Error: "componentName" token is required
reporter.error("ERR01");

5. Test friendly

Assert against resolved messages without duplicating message text in your test environment.

it("should log error if component fails to mount", () => {
    vi.spyOn(console, "error").mockImplementation(() => {});

    render(<MyComponent />);

    expect(console.error).toHaveBeenCalledWith(
        reporter.message("ERR01", { componentName: "MyComponent" })
    );
});

API

createReporter(RuntimeReporterMessages, options?: RuntimeReporterOptions): RuntimeReporter

Takes a messages object, an optional set of configuration options, and returns a reporter object.

RuntimeReporter

| Method | Type | Description | | ------- | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | message | (code: string, tokens:? RuntimeReporterTokens) => string | Returns the resolved message (no logging); useful for testing environments. | | warn | (code: string, tokens:? RuntimeReporterTokens) => void | Logs via console.warn only if the template exists in messages. | | error | (code: string, tokens:? RuntimeReporterTokens) => void | Logs via console.error only if the template exists in messages. | | log | (code: string, tokens:? RuntimeReporterTokens) => void | Logs via console.log only if the template exists in messages. | | fail | (code: string, tokens:? RuntimeReporterTokens) => never | Throws new Error(resolvedMessage) in all environments. Uses the defaultTemplate when the template does not exist in messages. |

RuntimeReporterOptions

| Property | Type | Required | Description | | --------------- | ------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | formatMessage | (message: string, code: string) => string | No | Customize the final output of every message. By default, messages are in the format: "<message> (<code>)". | | defaultTemplate | string | No | Fallback message text used when the code does not exist in messages. Defaults to "An error occurred". This is mostly relevant for fail() in production when you pass an empty message set. |

RuntimeReporterTokens

| Property | Type | Description | | -------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | string | RuntimeReporterToken | A record of token names along with their replacement value. Supported types include: string, number, boolean, Error. |

Examples

Custom formatting

You can customize the format of the message by providing a custom formatMessage function.

const reporter = createReporter(messages, {
    formatMessage: (msg, code) => `[${code}] ${msg}`,
});

reporter.message("ERR01", { componentName: "MyComponent" });
// "[ERR01] MyComponent failed to mount"

Calling fail() in production

When the createReporter function provided an empty message set in production, the fail method will use the customizable defaultTemplate option which defaults to "An error occurred". This message is intended to be generic so that it does not reveal sensitive information about the system, while still providing a code for debugging purposes.

const reporter = createReporter(
    process.env.NODE_ENV === "production" ? ({} as typeof messages) : messages
);

reporter.fail("ERR01", { componentName: "Router" });
// throws: "An error occurred (ERR01)"

Using message() in tests

The message method returns the resolved string without side effects, allowing you to validate precise messaging without duplicating text.

it("should log error if component fails to mount", () => {
    vi.spyOn(console, "error").mockImplementation(() => {});

    render(<MyComponent />);

    expect(console.error).toHaveBeenCalledWith(
        reporter.message("ERR01", { componentName: "MyComponent" })
    );
});

Type safety without TypeScript

You can still get the same benefits as TypeScript by using JSDoc-style type annotations.

/**
 * @type {import("runtime-reporter").RuntimeReporterMessages<{
 *     code: "ERR01";
 *     template: "{{ componentName }} failed to mount";
 *     tokens: "componentName";
 * } | {
 *     code: "ERR02";
 *     template: "Failed to load configuration";
 * } | {
 *     code: "ERR03";
 *     template: "Failed to fetch {{ resource }} from {{ url }}";
 *     tokens: "resource" | "url";
 * }>}
 */
const messages = {
    ERR01: "{{ componentName }} failed to mount",
    ERR02: "Failed to load configuration",
    ERR03: "Failed to fetch {{ resource }} from {{ url }}",
};

CommonJS support

If needed, Common JS imports are also available.

const { createReporter } = require("runtime-reporter");