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

@bro-code/scribe-web

v2026.1.0

Published

Scribe for the browser - records what the app logs in the same ScribeRecord shape as @bro-code/scribe, mirrors it to the console, and ships batches of records to a service callback (e.g. an HTTP endpoint). Can capture uncaught errors and unhandled rejecti

Readme

@bro-code/scribe-web

npm version license

Scribe for the browser. It has the call signatures of @bro-code/scribe and records in its ScribeRecord shape, but a browser has no stdout to capture, so it builds the text itself, writes it to the console and queues the records for a service callback that ships them in batches (to an HTTP endpoint, say). It can also record uncaught errors and unhandled promise rejections.

@bro-code/logger and @bro-code/scribe read files when they load, so they cannot be bundled for a page. This package imports only scribe's types (@bro-code/logger is a development dependency, for the test that checks the level table) and keeps its own copy of that table.

Installation

npm install @bro-code/scribe-web

Usage

import scribe from '@bro-code/scribe-web';

scribe.setService(async (records, final) => {
  await fetch('/api/logs', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ records }),
    keepalive: final,
  });
});
scribe.captureGlobalErrors();

scribe.info('Loaded');                                  // console only
scribe.error('Could not load the save', 0, null, err);  // console, and queued for the service

CommonJS

const scribe = require('@bro-code/scribe-web').default;

scribe.info('Loaded');

By default only error records are queued for the service; the console shows info and above. Records queued before setService is called wait for it.

API

new BroScribeWeb(options?)

The default export is an instance made with no options.

| Option | Default | | |---|---|---| | level | info | Minimum level written to the console. | | serviceLevel | error | Minimum level queued for the service. silent queues nothing. | | tabSize | 2 | Spaces per indent level. | | flushIntervalMs | 5000 | How long records wait before a batch is sent. | | maxBatch | 20 | Most records per service call; a full batch is sent at once. | | maxPending | 200 | Most records held; the oldest are dropped past it. | | maxOutputLength | 4000 | Longest output; longer is cut and ends with (truncated). | | repeatWindowMs | 30000 | See Repeats. | | errorMessages | keep | drop sends errors to the service without their messages. See Error messages. | | onServiceError | | (err, records) => void, called when the service throws or rejects. |

Logging

log, info, warn, debug, error, json and code, each (message, tab = 0, options?, ...args) and chainable, as in @bro-code/logger. The text is:

  • the label ([INFO], [WARN], [DEBUG], [ERROR]; none for log), then the message indented tab levels
  • each follow-on argument on its own line, one level deeper than the one before; arrays one item per line
  • an Error as its stack (or name: message when it has none), other objects as JSON
  • json records the compact JSON of the data and code the raw source, ignoring follow-on arguments

The options are ScribeWebLogOptions: BroLogOptions and a kind. Of BroLogOptions, label, showLabel, tabSize, bullets, bulletChar, numbered and silent are honoured; colours, borders, padding, timestamps and wrapping are terminal-only and ignored. A record has no trailing newline.

kind is a name of the app's own ('multiplayer', 'uncaught') put on the record queued for the service, so the service can sort or filter by it; the console output is the same with or without it. A record from a call with no kind has no kind field.

scribe.error('Host: the room could not be opened', 0, { kind: 'multiplayer' }, `${err.type}: ${err.message}`);

Console: error goes to console.error, warn to console.warn, debug to console.debug, the rest to console.log.

Error messages

With errorMessages: 'drop', every Error in a record queued for the service, as the message, an argument or an array item, is written as its name and stack only. The message is left out, and so is the copy of it a V8 (Chrome, Edge) stack opens with, even a message over several lines. A Firefox or Safari stack has no such line and is kept as it is, after the name. An error with no stack is its name only. Values that are not errors are written as usual, so text the app builds itself is sent as it is.

The console still gets the whole error. The service's copy is rendered a second time only when the record is going to the service.

const scribe = new BroScribeWeb({ errorMessages: 'drop' });
scribe.error('Could not save', 0, null, new TypeError('Entry "Rent" is invalid'));
// console:  [ERROR] Could not save\n  TypeError: Entry "Rent" is invalid\n      at save (app.js:3:4)
// service:  [ERROR] Could not save\n  TypeError\n      at save (app.js:3:4)

setService(fn | null)

fn is (records: ScribeWebRecord[], final: boolean) => void | Promise<void>, where ScribeWebRecord is ScribeRecord with an optional kind. final is true when the page is being hidden or unloaded; use a send that survives that (fetch with keepalive, navigator.sendBeacon). Passing null detaches the service and leaves the queue as it is.

While a service is set:

  • the queue is sent on a flushIntervalMs timer, and at once when maxBatch records wait
  • one call is in flight at a time, and a failed batch is reported through onServiceError and not retried
  • on pagehide, and when visibilitychange turns the page hidden, everything left is sent with final: true, a batch per call and without waiting for a call in flight

The listeners exist only while a service is set. The package does nothing when imported.

flush(final = false)

Sends waiting records now. Resolves when the call has finished; it never rejects. During a call in flight it resolves with that call.

clear()

Empties the queue.

captureGlobalErrors(options?)

Listens on window for error and unhandledrejection and records each as an error: the message, filename:lineno:colno, and the error's stack (or the rejection reason's). They are not written to the console again, since the browser already prints them. Returns a function that removes both listeners. Without a window it does nothing.

options is ScribeWebCaptureOptions, { kind?: string }: the kind given to every record it makes.

Under errorMessages: 'drop', the browser's message is not used:

  • an uncaught error is Uncaught <name>, then filename:lineno:colno and the error as its name and stack. A thrown value that is not an error gives its type in place of the name (Uncaught string), and nothing more; a cross-origin Script error. is Uncaught null.
  • an unhandled rejection is Unhandled promise rejection, then the reason as its name and stack when it is an error, or its type only otherwise (string, object, undefined, null).

level, serviceLevel

Getters and setters for the two levels.

Types

The package exports BroScribeWebOptions, ScribeWebServiceFn, ScribeWebRecord, ScribeWebLogOptions, ScribeWebCaptureOptions, ScribeWebErrorMessages, ScribeRecord, ScribeMethod, LogLevel, and BroLogOptions as types. The scribe record and logger types are re-exported from @bro-code/scribe.

Repeats

An identical record (same method, kind and output) within repeatWindowMs of the last one queued is not queued again; the count held back goes out in the next batch as <output> (repeated N more times), with the same kind. An error thrown every frame costs one record and one count.

Dropped records

When the queue overflows maxPending, the next batch begins with a warn record, -- N record(s) dropped --, for each kind that lost records, carrying that kind (and no kind for records that had none).

License

MIT - see LICENSE.

Changelog

[2026.1.0] - 2026-11-10

Added

  • errorMessages option: drop writes every error in a record queued for the service as its name and stack, without its message (nor the copy a V8 stack opens with), while the console keeps the whole error. Default keep, as before.
  • Under drop, captureGlobalErrors records an uncaught error as Uncaught <name> in place of the browser's message, and a thrown or rejected value that is not an error as its type only.
  • kind on the log options (ScribeWebLogOptions), carried on the queued record (ScribeWebRecord); repeats fold only within a kind, and dropped records are counted per kind, a line for each.
  • captureGlobalErrors({ kind }) gives its records a kind (ScribeWebCaptureOptions).
  • Types ScribeWebRecord, ScribeWebLogOptions, ScribeWebCaptureOptions and ScribeWebErrorMessages.

[2026.0.0] - 2026-10-27

Added

  • Initial release.
  • BroScribeWeb class with @bro-code/scribe's call signatures (log, info, warn, debug, error, json, code) and ScribeRecord as its record.
  • Console output filtered by level; records queued for the service filtered by serviceLevel (default error).
  • setService with batched sending on an interval and on a full batch, one call in flight, a final flush on pagehide and visibilitychange, and onServiceError.
  • A bounded queue (maxPending) that reports dropped records, and folding of identical records into (repeated N more times).
  • captureGlobalErrors for uncaught errors and unhandled rejections.
  • maxOutputLength truncation.