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

@yraylabs/boring-analytics

v1.0.2

Published

Official SDK for Boring Analytics — product analytics and error monitoring

Readme

boring-analytics

Official SDK for Boring Analytics — lightweight product analytics and error monitoring.

  • Minimal dependencies — native fetch/crypto; optional web-vitals for Core Web Vitals
  • Works everywhere — browser, Node.js, Edge runtimes
  • TypeScript-first — full type definitions included
  • Tiny footprint — tree-shakeable ESM + CJS builds
  • Auto-batching — queues events and flushes efficiently
  • Error monitoring — captures unhandled errors automatically

Installation

npm install boring-analytics

Quick Start

import { BoringAnalytics } from 'boring-analytics';

const analytics = new BoringAnalytics({
  apiKey: 'your-api-key',
});

// Track events
analytics.track('button_clicked', {
  properties: { buttonId: 'signup', page: '/home' },
});

// Identify users
analytics.identify('user-123', {
  traits: { email: '[email protected]', name: 'Jane' },
});

// Track page views
analytics.page('Home');

Configuration

const analytics = new BoringAnalytics({
  // Required
  apiKey: 'your-api-key',

  // Optional
  endpoint: 'https://api.boringanalytics.io',  // API endpoint
  flushAt: 20,            // Flush after N queued items (default: 20)
  flushInterval: 5000,    // Flush every N ms (default: 5000)
  maxRetries: 3,          // Retry failed requests (default: 3)
  debug: false,           // Log debug info to console

  // Auto-capture
  autoCapture: {
    errors: true,          // Capture unhandled errors (default: true)
    pageViews: false,      // Auto-track SPA navigation (default: false)
    webVitals: false,     // Send LCP, FID, INP, CLS, TTFB (default: false)
    scrollDepth: false,    // Send scroll_depth at 25%, 50%, 75%, 100% (default: false)
    outboundClicks: false, // Track external link and download clicks (default: false)
  },

  // Merged into every event
  defaultProperties: {
    appVersion: '1.2.0',
    environment: 'production',
  },

  // Called on transport errors
  onError: (err) => console.error('Analytics error:', err),
});

API

track(name, options?)

Track a named event.

analytics.track('purchase_completed', {
  properties: {
    orderId: 'order-456',
    amount: 99.99,
    currency: 'USD',
  },
});

identify(userId, options?)

Identify the current user. Sets the userId for all subsequent calls.

analytics.identify('user-123', {
  traits: {
    email: '[email protected]',
    name: 'Jane Doe',
    plan: 'pro',
  },
});

page(name?, options?)

Track a page view.

analytics.page('Pricing');
analytics.page('Product', {
  properties: { productId: 'abc-123' },
});

screen(name, options?)

Track a screen view (mobile apps).

analytics.screen('Settings');

group(groupId, options?)

Associate the user with a group/organization.

analytics.group('org-456', {
  traits: { name: 'Acme Inc', plan: 'enterprise' },
});

alias(userId, previousId)

Link two user identities.

analytics.alias('user-123', 'anon-789');

trackRevenue(options)

Track revenue (e.g. purchase, subscription). Sends a purchase event with amount and currency.

analytics.trackRevenue({
  amount: 29.99,
  currency: 'USD',
  properties: { orderId: 'ord_123', productId: 'sku_456' },
});

trackExposure(options)

Track feature-flag or A–B test exposure.

analytics.trackExposure({
  flag: 'pricing_test',
  variant: 'treatment',
  properties: { page: '/pricing' },
});

captureError(error, options?)

Manually capture an error.

try {
  await riskyOperation();
} catch (err) {
  analytics.captureError(err, {
    level: 'ERROR',             // DEBUG | INFO | WARNING | ERROR | FATAL
    tags: { component: 'checkout' },
    metadata: { orderId: '123' },
    release: 'v1.2.3',
    handled: true,
  });
}

flush()

Immediately send all queued events.

await analytics.flush();

reset()

Clear the current user identity. Call on logout.

analytics.reset();

shutdown()

Gracefully shut down — flushes remaining events and removes global error handlers.

await analytics.shutdown();

Auto-Capture: Web Vitals, Scroll Depth, Outbound Clicks

Enable optional automatic tracking (browser only):

const analytics = new BoringAnalytics({
  apiKey: 'your-api-key',
  autoCapture: {
    pageViews: true,     // SPA route changes (pushState/replaceState/popstate)
    webVitals: true,     // LCP, FID, INP, CLS, TTFB → event "web_vitals"
    scrollDepth: true,   // 25%, 50%, 75%, 100% → event "scroll_depth"
    outboundClicks: true, // External links & downloads → event "outbound_click"
  },
});
  • webVitals: Sends one event per metric with metric, value, rating, delta, id.
  • scrollDepth: Sends one event per threshold (25, 50, 75, 100) with depth.
  • outboundClicks: Sends href, domain, and link_type (external or download).

Auto Page View Tracking

Enable automatic page view tracking for SPAs:

const analytics = new BoringAnalytics({
  apiKey: 'your-api-key',
  autoCapture: {
    pageViews: true,  // Tracks pushState, replaceState, and popstate
  },
});

Error Monitoring

Unhandled errors are captured automatically by default. You can also capture errors manually:

// Automatic (enabled by default)
// window.onerror and unhandledrejection are captured

// Manual capture
analytics.captureError(new Error('Something went wrong'), {
  level: 'WARNING',
  tags: { feature: 'checkout' },
});

Context Enrichment

The SDK automatically captures context in the browser:

  • Browser: name, version
  • OS: name, version
  • Device: type (desktop/mobile/tablet)
  • Page: URL, referrer, title, path
  • User Agent and Locale

You can override or extend context per-call:

analytics.track('event', {
  context: {
    location: { country: 'US', city: 'San Francisco' },
  },
});

Server-Side Usage (Node.js)

import { BoringAnalytics } from 'boring-analytics';

const analytics = new BoringAnalytics({
  apiKey: 'your-server-api-key',
  autoCapture: { errors: false },
});

// Track server-side events
analytics.identify('user-123');
analytics.track('api_request', {
  properties: { endpoint: '/users', method: 'GET', duration: 42 },
});

// Flush before process exits
process.on('SIGTERM', async () => {
  await analytics.shutdown();
  process.exit(0);
});

License

MIT