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

@epicdm/flowstate-obs-browser

v1.0.0

Published

Browser SDK for Epic Flow Observability Platform

Readme

@epic-flow/obs-browser

Browser SDK for the Epic Flow Observability Platform. Capture errors, logs, and user interactions in real-time with automatic error tracking and WebSocket-based event streaming.

Features

  • Automatic Error Capture: Global error and unhandled rejection handlers
  • Manual Error Reporting: Programmatic error capture with context
  • Structured Logging: Multiple log levels (debug, info, warn, error)
  • Breadcrumb Tracking: Track user actions leading to errors
  • Context Management: Attach custom data and tags to all events
  • User Tracking: Associate events with user sessions
  • WebSocket Connection: Low-latency real-time event streaming
  • Auto Reconnection: Automatic reconnection with exponential backoff
  • Message Buffering: Queue events during disconnection
  • TypeScript Support: Full type definitions included
  • Lightweight: ~15KB minified, ~5KB gzipped

Installation

NPM

npm install @epic-flow/obs-browser

Yarn

yarn add @epic-flow/obs-browser

CDN (UMD build)

<script src="https://unpkg.com/@epic-flow/obs-browser@latest/dist/index.js"></script>
<script>
  const client = new ObservabilityPlatform.ObservabilityClient({
    url: 'ws://your-server.com/ws',
    apiKey: 'your-api-key',
    projectId: 'your-project-id'
  });
  client.init();
</script>

Quick Start

import { ObservabilityClient } from '@epic-flow/obs-browser';

// Initialize the client
const client = new ObservabilityClient({
  url: 'ws://localhost:8080/ws',
  apiKey: 'your-api-key-123',
  projectId: 'my-app',
  environment: 'production',
  release: '1.0.0',
  autoCapture: true, // Automatically capture errors
});

// Start observability
client.init();

// That's it! Errors are now automatically captured
// Add custom logging as needed
client.info('Application started', { userId: '123' });

API Reference

Constructor

new ObservabilityClient(config: ObservabilityConfig)

Creates a new observability client instance.

Parameters:

| Name | Type | Required | Description | |------|------|----------|-------------| | config.url | string | Yes | WebSocket server URL (e.g., ws://localhost:8080/ws) | | config.apiKey | string | Yes | Project API key for authentication | | config.projectId | string | Yes | Unique project identifier | | config.environment | string | No | Environment name (e.g., production, staging) | | config.release | string | No | Release version (e.g., 1.0.0) | | config.userId | string | No | Initial user ID to associate with events | | config.autoCapture | boolean | No | Enable automatic error capture (default: true) |

Example:

const client = new ObservabilityClient({
  url: 'ws://localhost:8080/ws',
  apiKey: 'your-api-key-123',
  projectId: 'my-app',
  environment: 'production',
  release: '1.0.0',
  userId: 'user-456',
  autoCapture: true,
});

Methods

init(): void

Initializes the client, connects to the server, and installs error handlers.

Example:

client.init();

shutdown(): void

Disconnects from the server and removes error handlers. Call this when your application is shutting down.

Example:

// On app cleanup or navigation
client.shutdown();

captureError(error: Error, context?: Record<string, any>): void

Manually capture an error with optional additional context.

Parameters:

  • error (Error): The error object to capture
  • context (Record<string, any>, optional): Additional context data

Example:

try {
  // Your code
  throw new Error('Something went wrong');
} catch (error) {
  client.captureError(error, {
    action: 'checkout',
    orderId: '12345',
    paymentMethod: 'credit-card'
  });
}

captureException(error: Error, context?: Record<string, any>): void

Alias for captureError(). Provided for compatibility with other error tracking SDKs.

Example:

client.captureException(new Error('Payment failed'), {
  userId: '123',
  amount: 99.99
});

log(level: 'debug' | 'info' | 'warn' | 'error', message: string, data?: Record<string, any>): void

Generic logging method for structured logs.

Parameters:

  • level: Log severity level
  • message: Log message
  • data (optional): Additional structured data

Example:

client.log('info', 'User action', {
  action: 'button_click',
  buttonId: 'submit',
  timestamp: Date.now()
});

debug(message: string, data?: any): void

Log a debug message.

Example:

client.debug('Component rendered', { componentName: 'Header', props: {...} });

info(message: string, data?: any): void

Log an info message.

Example:

client.info('User logged in', { userId: '123', method: 'oauth' });

warn(message: string, data?: any): void

Log a warning message.

Example:

client.warn('API response slow', { endpoint: '/api/users', duration: 5000 });

error(message: string, data?: any): void

Log an error message (not an exception).

Example:

client.error('Validation failed', { field: 'email', value: 'invalid' });

addBreadcrumb(breadcrumb: Breadcrumb): void

Add a breadcrumb to track user actions and events. Breadcrumbs are included with error events to provide context.

Parameters:

interface Breadcrumb {
  timestamp: number;
  category: 'navigation' | 'click' | 'console' | 'http' | 'error';
  message: string;
  data?: Record<string, any>;
}

Example:

// Track navigation
client.addBreadcrumb({
  timestamp: Date.now(),
  category: 'navigation',
  message: 'User navigated to checkout',
  data: { from: '/cart', to: '/checkout' }
});

// Track user interaction
client.addBreadcrumb({
  timestamp: Date.now(),
  category: 'click',
  message: 'Clicked submit button',
  data: { buttonId: 'checkout-submit' }
});

// Track HTTP request
client.addBreadcrumb({
  timestamp: Date.now(),
  category: 'http',
  message: 'POST /api/orders',
  data: { status: 200, duration: 245 }
});

Note: The SDK maintains a maximum of 30 breadcrumbs. Older breadcrumbs are automatically removed when the limit is exceeded.

setUser(userId: string, userData?: Record<string, any>): void

Set the current user context. All subsequent events will include this user ID.

Parameters:

  • userId: Unique user identifier
  • userData (optional): Additional user data (reserved for future use)

Example:

client.setUser('user-123', {
  email: '[email protected]',
  subscription: 'premium'
});

clearUser(): void

Clear the current user context.

Example:

// On logout
client.clearUser();

setContext(key: string, value: any): void

Set custom context data that will be included with all events.

Parameters:

  • key: Context key
  • value: Context value (any JSON-serializable data)

Example:

client.setContext('theme', 'dark');
client.setContext('experiment', { variant: 'A', id: 'exp-123' });
client.setContext('feature-flags', {
  newCheckout: true,
  betaFeatures: false
});

clearContext(key?: string): void

Clear custom context. If key is provided, clears that specific context. If omitted, clears all context.

Example:

// Clear specific context
client.clearContext('theme');

// Clear all context
client.clearContext();

setTag(key: string, value: string): void

Set a tag for filtering and grouping events. Tags are key-value string pairs.

Parameters:

  • key: Tag key
  • value: Tag value (must be string)

Example:

client.setTag('server-region', 'us-east-1');
client.setTag('customer-tier', 'enterprise');
client.setTag('feature', 'checkout-v2');

Configuration Reference

ObservabilityConfig

Complete configuration interface:

interface ObservabilityConfig {
  // Required fields
  url: string;           // WebSocket server URL
  apiKey: string;        // Project API key
  projectId: string;     // Project identifier

  // Optional fields
  environment?: string;  // Environment (e.g., 'production', 'staging', 'development')
  release?: string;      // Release version (e.g., '1.0.0', 'v2.3.1-beta')
  userId?: string;       // Initial user ID
  autoCapture?: boolean; // Auto-capture errors (default: true)
}

Event Types

ErrorEvent

interface ErrorEvent {
  id: string;                     // Unique event ID
  type: 'error';
  projectId: string;              // Project identifier
  timestamp: number;              // Unix timestamp (ms)
  level: 'error' | 'fatal';       // Error severity
  message: string;                // Error message
  stackTrace: string[];           // Parsed stack trace lines
  stack?: string;                 // Raw stack trace
  environment: {
    platform: 'browser' | 'node' | 'react-native' | 'electron';
    userAgent?: string;           // Browser user agent
    os?: string;                  // Operating system
    appVersion?: string;          // Application version
  };
  release?: string;               // Release version
  userId?: string;                // User identifier
  url?: string;                   // Page URL where error occurred
  breadcrumbs?: Breadcrumb[];     // User action trail
  context?: Record<string, any>;  // Custom context data
  tags?: Record<string, string>;  // Event tags
  metadata: Record<string, any>;  // Additional metadata
}

LogEvent

interface LogEvent {
  id: string;                     // Unique event ID
  type: 'log';
  projectId: string;              // Project identifier
  timestamp: number;              // Unix timestamp (ms)
  level: 'debug' | 'info' | 'warn' | 'error';
  message: string;                // Log message
  environment?: string;           // Environment name
  release?: string;               // Release version
  userId?: string;                // User identifier
  data?: Record<string, any>;     // Custom log data
  context?: Record<string, any>;  // Custom context
  tags?: Record<string, string>;  // Event tags
}

Breadcrumb

interface Breadcrumb {
  timestamp: number;              // Unix timestamp (ms)
  category: 'navigation' | 'click' | 'console' | 'http' | 'error';
  message: string;                // Breadcrumb description
  data?: Record<string, any>;     // Additional data
}

Usage Examples

React Integration

import { ObservabilityClient } from '@epic-flow/obs-browser';
import { useEffect } from 'react';

// Create client instance (outside component)
const observability = new ObservabilityClient({
  url: 'ws://localhost:8080/ws',
  apiKey: process.env.REACT_APP_OBS_API_KEY,
  projectId: 'my-react-app',
  environment: process.env.NODE_ENV,
  release: process.env.REACT_APP_VERSION,
});

// Initialize in app root
function App() {
  useEffect(() => {
    observability.init();

    return () => {
      observability.shutdown();
    };
  }, []);

  return <YourApp />;
}

// Use in components
function CheckoutButton() {
  const handleClick = async () => {
    observability.addBreadcrumb({
      timestamp: Date.now(),
      category: 'click',
      message: 'Checkout button clicked'
    });

    try {
      await processCheckout();
      observability.info('Checkout completed', { orderId: '123' });
    } catch (error) {
      observability.captureError(error, { step: 'checkout' });
    }
  };

  return <button onClick={handleClick}>Checkout</button>;
}

Vue Integration

import { ObservabilityClient } from '@epic-flow/obs-browser';

// Create plugin
const ObservabilityPlugin = {
  install(app, options) {
    const client = new ObservabilityClient(options);
    client.init();

    // Add to global properties
    app.config.globalProperties.$obs = client;

    // Error handler
    app.config.errorHandler = (error, instance, info) => {
      client.captureError(error, { component: instance?.$options.name, info });
    };
  }
};

// Use in main.js
import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);

app.use(ObservabilityPlugin, {
  url: 'ws://localhost:8080/ws',
  apiKey: import.meta.env.VITE_OBS_API_KEY,
  projectId: 'my-vue-app',
  environment: import.meta.env.MODE,
});

app.mount('#app');

// Use in components
export default {
  methods: {
    async submitForm() {
      try {
        await api.submit(this.formData);
        this.$obs.info('Form submitted', { form: 'contact' });
      } catch (error) {
        this.$obs.captureError(error, { form: 'contact' });
      }
    }
  }
};

Vanilla JavaScript

<!DOCTYPE html>
<html>
<head>
  <title>My App</title>
  <script src="https://unpkg.com/@epic-flow/obs-browser/dist/index.js"></script>
</head>
<body>
  <button id="myButton">Click Me</button>

  <script>
    // Initialize
    const obs = new ObservabilityPlatform.ObservabilityClient({
      url: 'ws://localhost:8080/ws',
      apiKey: 'your-api-key',
      projectId: 'my-app',
      autoCapture: true
    });
    obs.init();

    // Track interactions
    document.getElementById('myButton').addEventListener('click', () => {
      obs.addBreadcrumb({
        timestamp: Date.now(),
        category: 'click',
        message: 'Button clicked',
        data: { buttonId: 'myButton' }
      });

      obs.info('User clicked button');
    });
  </script>
</body>
</html>

With TypeScript

import {
  ObservabilityClient,
  type ObservabilityConfig,
  type Breadcrumb,
  type ErrorEvent,
  type LogEvent
} from '@epic-flow/obs-browser';

const config: ObservabilityConfig = {
  url: 'ws://localhost:8080/ws',
  apiKey: 'your-api-key',
  projectId: 'my-app',
  environment: 'production',
  release: '1.0.0',
};

const client = new ObservabilityClient(config);
client.init();

// Type-safe breadcrumb
const breadcrumb: Breadcrumb = {
  timestamp: Date.now(),
  category: 'http',
  message: 'API request',
  data: { endpoint: '/api/users', status: 200 }
};
client.addBreadcrumb(breadcrumb);

Browser Compatibility

| Browser | Minimum Version | |---------|----------------| | Chrome | 90+ | | Firefox | 88+ | | Safari | 14+ | | Edge | 90+ | | iOS Safari | 14+ | | Chrome Mobile | Latest |

Requirements:

  • WebSocket support (required)
  • ES6 support (required)
  • Promise support (required)

Not Supported:

  • Internet Explorer (all versions)
  • Legacy browsers without WebSocket

Performance Considerations

Memory Usage

  • Baseline: ~5MB
  • Per Breadcrumb: ~1KB (max 30 breadcrumbs = 30KB)
  • Per Buffered Event: ~1-2KB
  • Buffer Limit: 100 events during disconnection

Network Usage

  • Average Event Size: 500 bytes (before compression)
  • With gzip: ~200 bytes
  • Heartbeat: Minimal ping/pong every 30 seconds

CPU Impact

  • Error Capture: < 1ms overhead
  • Log Call: < 0.5ms overhead
  • Breadcrumb Addition: < 0.1ms overhead

Best Practices

  1. Use appropriate log levels: Debug logs only in development
  2. Limit breadcrumb data: Keep data payloads small
  3. Batch when possible: Events are automatically batched over WebSocket
  4. Set release version: Helps with debugging and filtering
  5. Clean up on unmount: Always call shutdown() when done

Troubleshooting

Connection Issues

Problem: "WebSocket connection failed"

Solutions:

  • Verify server is running and accessible
  • Check URL uses ws:// (or wss:// for secure)
  • Verify API key is correct
  • Check CORS settings on server
  • Ensure no firewall blocking WebSocket

Events Not Appearing

Problem: Events sent but not visible

Solutions:

  • Check server logs for validation errors
  • Verify projectId matches server configuration
  • Check rate limit (1000 events/min)
  • Ensure WebSocket is connected (check state)
  • Verify event schema matches server requirements

TypeScript Errors

Problem: Type errors when using SDK

Solutions:

// Ensure types are imported
import type { ObservabilityConfig } from '@epic-flow/obs-browser';

// Or use inline types
const config = {
  url: 'ws://...',
  apiKey: 'key',
  projectId: 'id',
} satisfies ObservabilityConfig;

Memory Leaks

Problem: Memory usage grows over time

Solutions:

  • Always call client.shutdown() on cleanup
  • Limit breadcrumb data size
  • Check for reconnection loops (verify server is stable)
  • Monitor buffered message count during disconnections

Advanced Usage

Custom Error Boundaries (React)

import { Component, ReactNode } from 'react';

class ErrorBoundary extends Component {
  componentDidCatch(error: Error, errorInfo: any) {
    observability.captureError(error, {
      errorInfo,
      componentStack: errorInfo.componentStack
    });
  }

  render() {
    return this.props.children;
  }
}

Rate Limiting on Client

// Debounce high-frequency events
let lastLog = 0;
const logRateLimit = 1000; // 1 event per second

function throttledLog(message: string) {
  const now = Date.now();
  if (now - lastLog >= logRateLimit) {
    client.info(message);
    lastLog = now;
  }
}

Environment-Specific Configuration

const config = {
  url: process.env.NODE_ENV === 'production'
    ? 'wss://obs.production.com/ws'
    : 'ws://localhost:8080/ws',
  apiKey: process.env.OBS_API_KEY,
  projectId: 'my-app',
  environment: process.env.NODE_ENV,
  autoCapture: process.env.NODE_ENV === 'production',
};

Contributing

See Contributing Guidelines

License

MIT

Support