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

@9apes/rum-sdk

v0.1.1

Published

9apes Real User Monitoring SDK helps you track the real time performance, errors, and analytics of your application's users.

Downloads

228

Readme

9APES RUM SDK

9apes Real User Monitoring SDK helps you track the real time performance, errors, and analytics of your application's users.

Installation

npm install @9apes/rum-sdk
# or
pnpm add @9apes/rum-sdk

Usage

import { initWatchdog } from '@9apes/rum-sdk';

initWatchdog({
  apiKey: 'your-api-key',
  projectId: 'your-project-id',
  organizationId: 'your-organization-id',
  enableProfiling: false, // optional
  userId: 'user-123' // optional
});

Configuration

Required Fields

{
  apiKey: string;        // Your API key
  projectId: string;     // Your project ID
  organizationId: string;
}

Optional Configuration

{
  // User identification
  userId?: string;

  // Batch configuration
  batchSize?: number;              // Default: 25
  flushInterval?: number;          // Default: 15000 (15 seconds)

  // Feature toggles (all true by default)
  enableWebVitals?: boolean;
  enableErrorTracking?: boolean;
  enablePerformanceTracking?: boolean;
  enableBehaviorTracking?: boolean;
  enableBusinessMetrics?: boolean;
  enableSessionRecording?: boolean;

  // Privacy settings
  enablePrivacyMode?: boolean;     // Default: true
  maskAllInputs?: boolean;         // Default: true
  sensitiveSelectors?: string[];   // CSS selectors to mask

  // Session recording
  maxRecordingTime?: number;       // Default: 1800000 (30 min)
  segmentDuration?: number;        // Default: 30000 (30 sec)
  maxSegmentSize?: number;         // Default: 500 events

  // Error handling
  maxStackTraceLength?: number;              // Default: 1000
  maxErrorMessageLength?: number;            // Default: 500
  sendCriticalErrorsImmediately?: boolean;   // Default: true
  criticalErrorDelay?: number;               // Default: 0

  // Debug
  debug?: boolean;                 // Default: false
}

API Reference

Core Functions

initWatchdog(config: WatchdogConfig): void

Initialize Watchdog with your configuration.

initWatchdog({
  apiKey: 'your-api-key',
  projectId: 'your-project',
});

destroyWatchdog(): void

Cleanup and destroy the Watchdog instance.

destroyWatchdog();

Tracking Functions

trackFeatureUsage(featureName: string, context?: object): void

Track feature usage for product analytics.

trackFeatureUsage('export-pdf', {
  format: 'a4',
  pages: 10
});

trackConversion(event: string, value?: number, metadata?: object): void

Track conversion events.

trackConversion('purchase', 99.99, {
  productId: '123',
  quantity: 2
});

addCustomBreadcrumb(message: string, data?: object): void

Add debugging breadcrumbs for error context.

addCustomBreadcrumb('User clicked checkout', {
  cartItems: 3,
  total: 149.99
});

Session Control

pauseRecording(): void

Pause session recording (useful for sensitive screens).

pauseRecording();

resumeRecording(): void

Resume session recording.

resumeRecording();

updateUserId(userId: string): void

Update the user ID after authentication.

updateUserId('user-456');

Utilities

forceFlush(): void

Immediately flush all buffered events.

forceFlush();

getAnalyticsStatus(): object

Get current tracking status.

const status = getAnalyticsStatus();
console.log(status);
// {
//   isRecording: true,
//   sessionId: '...',
//   bufferSize: 5,
//   collectors: ['WebVitalsCollector', 'ErrorCollector', ...]
// }

Architecture

Modular Design

Watchdog uses a modular architecture with clear separation of concerns:

┌─────────────────────────────────────────────────────┐
│                   Public API                         │
└────────────────────┬────────────────────────────────┘
                     │
┌────────────────────▼────────────────────────────────┐
│                 WatchdogCore                         │
│  ┌─────────────────────────────────────────────┐   │
│  │            Event Pipeline                    │   │
│  │  Collectors → EventBus → Middleware → Queue │   │
│  └─────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────┘
         │              │              │
    ┌────▼───┐    ┌────▼────┐    ┌───▼────┐
    │Collect-│    │Middle-  │    │Trans-  │
    │ors     │    │ware     │    │port    │
    └────────┘    └─────────┘    └────────┘

Collectors

Each collector is independent and focuses on a specific tracking concern:

  • WebVitalsCollector: Core Web Vitals (CLS, INP, FCP, LCP, TTFB)
  • ErrorCollector: JavaScript errors, resource errors, promise rejections
  • PerformanceCollector: Navigation timing, resource timing, memory usage
  • BehaviorCollector: Clicks, scrolls, form interactions, rage clicks
  • NetworkCollector: Fetch/XHR tracking
  • SessionRecorder: rrweb-based session replay

Design Patterns

Observer Pattern (EventBus)

Collectors emit events without knowing who consumes them:

// Collector emits
eventBus.emit('event:collected', event);

// Core subscribes
eventBus.subscribe('event:collected', (event) => {
  // Process event
});

Strategy Pattern

Severity Classification
const classifier = new SeverityClassifier();
const severity = classifier.classify({
  type: 'javascript',
  error: new Error('Chunk load failed')
});
// Returns: 'critical'
Transport Selection
// Automatically selects best available transport:
// 1. Fetch (priority: 100)
// 2. Beacon (priority: 50)
// 3. XHR (priority: 10)
await transportManager.send(payload);

Middleware System

Watchdog includes a powerful middleware system with three hooks:

Built-in Middleware

  1. BreadcrumbEnricher: Attaches breadcrumbs to error events
  2. SessionEnricher: Adds session-level metrics
  3. ViewportEnricher: Adds device category and derived viewport data
  4. ConnectionEnricher: Adds network quality classification

Custom Middleware

Register your own middleware to enrich or transform events:

import { registerMiddleware } from '@9apes/rum-sdk';

registerMiddleware({
  name: 'my-enricher',
  
  // Filter events before collection
  onPreCollect(event) {
    if (event.type === 'internal-debug') {
      return null; // Filter out
    }
    return event;
  },
  
  // Enrich events after collection
  onPostCollect(event) {
    return {
      ...event,
      customField: 'value',
      timestamp: Date.now()
    };
  },
  
  // Transform before sending
  onPreSend(batch) {
    return {
      ...batch,
      metadata: {
        buildVersion: '1.2.3'
      }
    };
  }
});

ClickHouse Schema

Watchdog uses a flat, ClickHouse-optimized schema for efficient analytics.

Event Metadata (All Events)

CREATE TABLE events (
  -- Event identification
  event_id String,              -- UUIDv7 (time-ordered)
  event_type LowCardinality(String),
  event_category LowCardinality(String),
  timestamp DateTime64(3),
  
  -- Session and user
  session_id LowCardinality(String),
  user_id Nullable(String),
  
  -- Page context
  page_url String,
  page_path String,
  page_title Nullable(String),
  referrer Nullable(String),
  
  -- Device and browser
  user_agent LowCardinality(String),
  viewport_width UInt16,
  viewport_height UInt16,
  screen_width UInt16,
  screen_height UInt16,
  device_pixel_ratio Float32,
  
  -- Platform
  platform LowCardinality(String),
  language LowCardinality(String),
  timezone LowCardinality(String),
  
  -- Network
  connection_type Nullable(LowCardinality(String)),
  connection_downlink Nullable(Float32),
  connection_rtt Nullable(UInt16),
  
  -- Event-specific fields (nullable)
  -- See specific event types below
  
  -- Backend enrichment (add these in your pipeline)
  country LowCardinality(String),
  city Nullable(String),
  -- ... other enrichment fields
  
) ENGINE = MergeTree()
ORDER BY (event_category, event_type, timestamp);

Error Events

-- Additional fields for error events
error_message String,
error_stack Nullable(String),
error_severity LowCardinality(String), -- 'low' | 'medium' | 'high' | 'critical'
error_filename Nullable(String),
error_line Nullable(UInt32),
error_column Nullable(UInt32),
breadcrumbs_json String,  -- JSON array
resource_url Nullable(String),
resource_type Nullable(LowCardinality(String)),
http_status Nullable(UInt16),
http_status_text Nullable(String)

Web Vital Events

-- Additional fields for web vital events
metric_name LowCardinality(String),  -- 'CLS' | 'INP' | 'FCP' | 'LCP' | 'TTFB'
metric_value Float64,
metric_rating LowCardinality(String),  -- 'good' | 'needs-improvement' | 'poor'
metric_delta Float64,
metric_id String

See CLICKHOUSE_SCHEMA.md for complete schema documentation.

Examples

React Integration

import { useEffect } from 'react';
import { initWatchdog, updateUserId } from '@9apes/rum-sdk';

function App() {
  useEffect(() => {
    initWatchdog({
      apiKey: process.env.REACT_APP_WATCHDOG_API_KEY!,
      projectId: 'my-react-app',
      endpoint: 'https://analytics.example.com/events'
    });
  }, []);
  
  const handleLogin = (user) => {
    updateUserId(user.id);
  };
  
  return <YourApp />;
}

Next.js Integration

// pages/_app.tsx
import { useEffect } from 'react';
import type { AppProps } from 'next/app';
import { initWatchdog } from '@9apes/rum-sdk';

function MyApp({ Component, pageProps }: AppProps) {
  useEffect(() => {
    initWatchdog({
      apiKey: process.env.NEXT_PUBLIC_WATCHDOG_API_KEY!,
      projectId: 'my-nextjs-app',
      endpoint: '/api/analytics'
    });
  }, []);
  
  return <Component {...pageProps} />;
}

export default MyApp;

Vue Integration

// main.ts
import { createApp } from 'vue';
import { initWatchdog } from '@9apes/rum-sdk';
import App from './App.vue';

initWatchdog({
  apiKey: import.meta.env.VITE_WATCHDOG_API_KEY,
  projectId: 'my-vue-app',
  endpoint: 'https://analytics.example.com/events'
});

createApp(App).mount('#app');

Adding Custom Collectors

Adding a new collector is straightforward:

// collectors/BfCacheCollector.ts
import { BaseCollector } from '@9apes/rum-sdk/collectors/BaseCollector';
import type { EventBus } from '@9apes/rum-sdk/core/EventBus';

export class BfCacheCollector extends BaseCollector {
  constructor(eventBus: EventBus, sessionId: string, userId: string | null) {
    super('BfCacheCollector', eventBus, sessionId, userId);
  }

  initialize(): void {
    window.addEventListener('pageshow', (event) => {
      if (event.persisted) {
        const baseMetadata = this.getBaseMetadata();
        this.emit('event:collected', {
          ...baseMetadata,
          event_type: 'bfcache_restore',
          event_category: 'performance'
        });
      }
    });
    
    this.isInitialized = true;
  }

  destroy(): void {
    // Cleanup
    this.isInitialized = false;
  }
}

License

MIT

Support

For issues and questions, please open an issue on GitHub.