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

blinker-sdk

v2.2.1

Published

Universal JavaScript SDK for error tracking and event capture - works with any framework

Readme

Blinker SDK

Universal JavaScript SDK for error tracking and event capture. Works with any framework - React, Vue, Angular, Next.js, or Vanilla JavaScript.

Features

  • Zero dependencies - Lightweight and fast
  • Universal - Works in any JavaScript environment
  • Automatic error capture - Catches global errors and unhandled rejections
  • Smart filtering - Ignores common non-bugs (401, 403, ResizeObserver) by default
  • Event batching - Efficient sending with configurable batch size
  • Offline support - Persists events when offline, sends when back online
  • Deduplication - Prevents flood of identical errors
  • Rate limiting - Protects against excessive event sending
  • Sampling - Sample events for high-traffic apps
  • Session tracking - Automatic session management with pageview counting
  • User context - Identify users and attach custom context
  • Feedback widget - Interactive avatar with server-driven questions for user feedback
  • Multiple formats - ESM, CommonJS, and UMD (CDN)
  • Type-safe - Full TypeScript support

Installation

npm / yarn / pnpm

npm install blinker-sdk
# or
yarn add blinker-sdk
# or
pnpm add blinker-sdk

CDN

<script src="https://unpkg.com/blinker-sdk/dist/blinker.min.js"></script>
<script>
  const { blinker } = BlinkerSDK;
  blinker.init({ token: 'your-token' });
</script>

Quick Start

import { blinker } from 'blinker-sdk';

blinker.init({ token: 'your-blinker-token' });

// That's it! Errors are now automatically captured.
// Batches events for efficient sending
// Works offline with localStorage persistence
// Deduplicates repeated errors
// Ignores 401/403 errors by default
// Filters out common browser noise

Usage

Initialization

import { blinker } from 'blinker-sdk';

// Minimal setup
blinker.init({ token: 'your-blinker-token' });

// Full options
blinker.init({
  token: 'your-blinker-token',
  captureErrors: true,
  captureRejections: true,
  enableBatching: true,
  batchSize: 10,
  flushInterval: 5000,
  enableOfflineStorage: true,
  maxStoredEvents: 100,
  enableDeduplication: true,
  deduplicationWindow: 60000,
  maxEventsPerMinute: 100,
  sampleRate: 1.0,
  filters: {
    ignoreHttpCodes: [401, 403],
    captureAll: false
  },
  widget: {
    enabled: true,
    position: 'bottom-right',
    delay: 2000,
    maxShowsPerSession: 2,
    cooldownMinutes: 30
  },
  debug: false
});

User Identification

blinker.identify('user-123');

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

blinker.clearContext();

Custom Context

blinker.setContext('environment', 'production');
blinker.setContext('appVersion', '2.1.0');

const context = blinker.getContext();

blinker.clearContext();

Track Custom Events

blinker.track('click', 'Button clicked');

blinker.track('purchase', 'User made a purchase', {
  productId: '12345',
  amount: 99.99
});

blinker.trackPageView();
blinker.trackPageView('Home Page', { section: 'hero' });

Manual Error Capture

try {
  // Your code
} catch (error) {
  blinker.captureError(error, { context: 'checkout' });
}

Error Filters

The SDK ignores common non-bug errors by default:

  • 401 and 403 HTTP errors
  • ResizeObserver loop errors
  • Script error (cross-origin)
// Capture everything
blinker.init({
  token: 'your-token',
  filters: { captureAll: true }
});

// Custom HTTP code filters
blinker.init({
  token: 'your-token',
  filters: { ignoreHttpCodes: [401, 403, 404, 429] }
});

// Ignore specific error types
blinker.init({
  token: 'your-token',
  filters: { ignoreErrorTypes: ['TypeError', 'SyntaxError'] }
});

// Ignore message patterns
blinker.init({
  token: 'your-token',
  filters: {
    ignoreMessagePatterns: ['ChunkLoadError', 'Network request failed']
  }
});

Session Information

const sessionId = blinker.getSessionId();

const session = blinker.getSession();
// { sessionId, startTime, pageViews, duration }

Queue Control

const pending = blinker.getQueueSize();
await blinker.flush();

Check SDK Status

blinker.isInitialized();
blinker.getConfig();
blinker.getFilters();

Cleanup

blinker.destroy();

Feedback Widget

The SDK includes an interactive feedback widget that collects user context when errors occur. An animated avatar appears after an error, and when clicked, opens a dialog with questions configured by your team.

How It Works

  1. Error is captured by the SDK
  2. SDK sends the event to the server (POST /events)
  3. Server evaluates widget rules and returns relevant questions
  4. If widget should show: avatar appears after configurable delay
  5. User clicks the avatar to open the feedback dialog
  6. Questions are displayed based on the error type (text, yes/no, select)
  7. User submits answers, which are sent as feedback (POST /feedback)

Enabling the Widget

blinker.init({
  token: 'your-token',
  widget: {
    enabled: true
  }
});

Widget Configuration

blinker.init({
  token: 'your-token',
  widget: {
    enabled: true,
    position: 'bottom-right',    // 'bottom-right' | 'bottom-left'
    delay: 2000,                 // ms before avatar appears
    maxShowsPerSession: 2,       // max widget shows per session
    cooldownMinutes: 30,         // cooldown between shows
    theme: {
      primary: '#6366f1',
      background: '#ffffff',
      text: '#1f2937',
      accent: '#10b981'
    },
    messages: {
      greeting: 'Oops! Something went wrong.',
      subtext: 'Can you help us understand what happened?',
      thankYou: 'Thanks! We\'re looking into it.',
      buttonSend: 'Send',
      buttonSkip: 'Not now'
    }
  }
});

Server-Driven Questions

Questions are configured in the Blinker dashboard and served dynamically based on the error type. The server matches error characteristics (HTTP codes, message patterns, error types) against rules to determine which questions to show.

Question types:

| Type | Description | Example | |------|-------------|---------| | text | Free-text input | "What were you doing?" | | boolean | Yes/No toggle | "Were you logged in?" | | select | Dropdown options | "Which browser?" (Chrome, Firefox, Safari...) |

Rule matching: Rules are evaluated by priority. The first matching rule determines which questions appear. If no rule matches, the first two questions are shown as fallback.

Dashboard Configuration

In the Blinker dashboard (/widget), you can:

  • Enable/disable the widget (template questions are seeded on first enable)
  • Create, edit, and delete questions
  • Configure rules that match errors to specific questions
  • Set conditions: HTTP codes, HTTP code ranges, message patterns, error types

Widget Lifecycle

Error captured
  -> Event sent to server
  -> Server returns widget decision: { show: true/false, questions: [...] }
  -> If show: true, avatar appears after delay
  -> User clicks avatar -> dialog opens with questions
  -> User submits -> feedback sent to server
  -> Avatar disappears (with cooldown)

Rate limiting:

  • maxShowsPerSession limits how many times the widget appears per session
  • cooldownMinutes enforces minimum time between shows
  • State is persisted in sessionStorage (resets on new tab/window)

Framework Examples

React

import { blinker } from 'blinker-sdk';

blinker.init({
  token: process.env.REACT_APP_BLINKER_TOKEN,
  widget: { enabled: true }
});

function App() {
  useEffect(() => {
    blinker.identify(user.id, { name: user.name, email: user.email });
  }, [user]);

  return <button onClick={() => blinker.track('click', 'CTA clicked')}>Click me</button>;
}

Next.js

'use client';

import { useEffect } from 'react';
import { blinker } from 'blinker-sdk';

export default function RootLayout({ children }) {
  useEffect(() => {
    blinker.init({
      token: process.env.NEXT_PUBLIC_BLINKER_TOKEN,
      widget: { enabled: true }
    });
  }, []);

  return (
    <html>
      <body>{children}</body>
    </html>
  );
}

Vue.js

import { createApp } from 'vue';
import { blinker } from 'blinker-sdk';
import App from './App.vue';

blinker.init({
  token: import.meta.env.VITE_BLINKER_TOKEN,
  widget: { enabled: true }
});

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

Vanilla JavaScript

<script src="https://unpkg.com/blinker-sdk/dist/blinker.min.js"></script>
<script>
  const { blinker } = BlinkerSDK;
  blinker.init({ token: 'your-token', widget: { enabled: true } });
  blinker.identify('user-123', { name: 'John' });
</script>

API Reference

blinker.init(config)

Initialize the SDK.

| Option | Type | Default | Description | |--------|------|---------|-------------| | token | string | required | Your Blinker API token | | captureErrors | boolean | true | Auto-capture window errors | | captureRejections | boolean | true | Auto-capture unhandled rejections | | enableBatching | boolean | true | Enable event batching | | batchSize | number | 10 | Events per batch | | flushInterval | number | 5000 | Batch flush interval (ms) | | enableOfflineStorage | boolean | true | Persist events when offline | | maxStoredEvents | number | 100 | Max offline events | | enableDeduplication | boolean | true | Enable error deduplication | | deduplicationWindow | number | 60000 | Dedup window (ms) | | maxEventsPerMinute | number | 100 | Rate limit (0 = unlimited) | | sampleRate | number | 1.0 | Sample rate (0.0-1.0) | | filters | FilterSettings | see below | Error filter configuration | | widget | WidgetConfig | see below | Feedback widget configuration | | onBeforeSend | function | undefined | Transform or drop events before sending | | debug | boolean | false | Enable debug logging |

FilterSettings

| Option | Type | Default | Description | |--------|------|---------|-------------| | ignoreHttpCodes | number[] | [401, 403] | HTTP codes to ignore | | ignoreErrorTypes | string[] | [] | JS error types to ignore | | ignoreMessagePatterns | string[] | ['ResizeObserver loop', 'Script error'] | Patterns to ignore | | captureAll | boolean | false | Disable all filters |

WidgetConfig

| Option | Type | Default | Description | |--------|------|---------|-------------| | enabled | boolean | false | Enable feedback widget | | position | string | 'bottom-right' | 'bottom-right' or 'bottom-left' | | delay | number | 2000 | Delay before showing avatar (ms) | | maxShowsPerSession | number | 2 | Max shows per session | | cooldownMinutes | number | 30 | Cooldown between shows (min) | | theme | WidgetTheme | see defaults | Widget colors | | messages | WidgetMessages | see defaults | Widget text strings |

blinker.track(type, message, payload?)

Track a custom event. Returns Promise<{ success: boolean, error?: string }>.

blinker.captureError(error, payload?)

Manually capture an error.

blinker.trackPageView(pageName?, payload?)

Track a page view.

blinker.identify(userId, traits?)

Identify the current user.

blinker.setContext(key, value) / blinker.getContext() / blinker.clearContext()

Manage custom context included in all events.

blinker.getSessionId() / blinker.getSession()

Get session tracking data.

blinker.flush()

Force flush all queued events. Returns Promise<void>.

blinker.getQueueSize()

Get number of pending events. Returns number.

blinker.isInitialized() / blinker.getConfig() / blinker.getFilters()

Check SDK state.

blinker.destroy()

Remove error handlers and reset SDK state.

Event Payload

Events sent to the API:

{
  "type": "error",
  "message": "Error message",
  "payload": { "custom": "data" },
  "timestamp": "2026-01-15T12:00:00.000Z",
  "url": "https://example.com/page",
  "userAgent": "Mozilla/5.0...",
  "sessionId": "abc123...",
  "userId": "user-123",
  "userTraits": { "name": "John", "plan": "pro" },
  "context": { "appVersion": "2.1.0" },
  "count": 5
}

TypeScript

Full type definitions included.

import { blinker, BlinkerConfig, FilterSettings, SessionInfo, WidgetConfig } from 'blinker-sdk';

const config: BlinkerConfig = {
  token: 'your-token',
  enableBatching: true,
  sampleRate: 0.5,
  widget: { enabled: true },
  filters: { ignoreHttpCodes: [401, 403, 404] }
};

blinker.init(config);

Multiple Instances

import { Blinker } from 'blinker-sdk';

const frontendErrors = new Blinker();
const backendErrors = new Blinker();

frontendErrors.init({ token: 'frontend-token', widget: { enabled: true } });
backendErrors.init({ token: 'backend-token' });

Browser Support

  • Chrome 63+
  • Firefox 67+
  • Safari 12+
  • Edge 79+

Uses ES2018 features (async/await, fetch).

License

MIT