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

@logg/signals

v0.1.1

Published

Universal event tracking SDK for Logg Signals

Readme

@logg/signals

Universal event tracking SDK for Logg Signals. Track events from web, React Native, and Node.js applications.

Features

Universal - Works in browsers, React Native, and Node.js
Type-safe - Full TypeScript support
Automatic batching - Efficient event batching with configurable thresholds
Persistent storage - Uses localStorage, AsyncStorage, or memory as fallback
Retry logic - Exponential backoff for failed requests
Auto metadata - Automatically collects browser/device information
Small bundle - <5KB gzipped

Installation

npm install @logg/signals

For React Native, also install AsyncStorage:

npm install @react-native-async-storage/async-storage

Quick Start

Web (Browser)

import { Signals } from '@logg/signals';

const signals = new Signals({
  apiKey: 'your-api-key',
  endpoint: 'https://signals.yourdomain.com/events',
});

// Track events
signals.event({
  type: 'page_view',
  page: '/dashboard',
  userId: '12345',
});

signals.event({
  type: 'button_click',
  element: 'signup_cta',
  userId: '12345',
});

React Native

import { Signals } from '@logg/signals';
import AsyncStorage from '@react-native-async-storage/async-storage';

const signals = new Signals({
  apiKey: 'your-api-key',
  endpoint: 'https://signals.yourdomain.com/events',
  // AsyncStorage is auto-detected, but you can pass it explicitly
});

// Track events
signals.event({
  type: 'screen_view',
  screen: 'HomeScreen',
  userId: user.id,
});

signals.event({
  type: 'purchase',
  productId: 'premium-plan',
  amount: 29.99,
  userId: user.id,
});

Node.js

import { Signals } from '@logg/signals';

const signals = new Signals({
  apiKey: 'your-api-key',
  endpoint: 'https://signals.yourdomain.com/events',
});

// Track server-side events
await signals.event({
  type: 'api_call',
  endpoint: '/api/users',
  method: 'POST',
  userId: req.user.id,
});

// Make sure to flush before process exit
process.on('beforeExit', async () => {
  await signals.flush();
});

Configuration

const signals = new Signals({
  // Required
  apiKey: 'your-api-key',
  endpoint: 'https://signals.yourdomain.com/events',

  // Optional
  batchSize: 10,           // Send after 10 events (default: 10)
  batchInterval: 5000,     // Or every 5 seconds (default: 5000)
  maxRetries: 3,           // Retry failed requests 3 times (default: 3)
  retryDelay: 1000,        // Initial retry delay in ms (default: 1000)
  debug: false,            // Enable debug logging (default: false)
  sessionId: 'custom-id',  // Custom session ID (auto-generated by default)
  storage: customAdapter,  // Custom storage adapter (auto-detected by default)
});

API Reference

signals.event(eventData)

Track an event. Events are automatically batched and sent based on batchSize and batchInterval config.

await signals.event({
  type: 'event_type',      // Required: event type
  userId: 'user-123',      // Optional: user ID
  // ... any other properties
});

Auto-added fields:

  • event_id - Unique event identifier (UUID v4)
  • timestamp - ISO 8601 timestamp
  • session_id - Session identifier
  • client - Client metadata (type, version, user_agent, screen, locale, timezone)

signals.flush()

Manually flush all pending events immediately.

await signals.flush();

signals.getSessionId()

Get the current session ID.

const sessionId = signals.getSessionId();

signals.getQueueSize()

Get the number of events in the queue.

const queueSize = signals.getQueueSize();

signals.destroy()

Destroy the client, flush remaining events, and cleanup resources.

await signals.destroy();

Event Batching

Events are automatically batched to reduce network requests:

  1. Batch by size: Sends when batchSize events are queued (default: 10)
  2. Batch by time: Sends every batchInterval milliseconds (default: 5000)
  3. Manual flush: Call signals.flush() to send immediately

Batch format sent to backend:

{
  "api_key": "your-api-key",
  "batch_id": "batch-uuid",
  "timestamp": "2025-12-02T10:30:00.000Z",
  "metadata": {
    "type": "web",
    "version": "0.1.0",
    "user_agent": "Mozilla/5.0...",
    "screen": { "width": 1920, "height": 1080 },
    "locale": "en-US",
    "timezone": "America/New_York"
  },
  "events": [
    {
      "event_id": "uuid-1",
      "timestamp": "2025-12-02T10:30:00.000Z",
      "session_id": "session-uuid",
      "type": "page_view",
      "userId": "12345",
      "page": "/dashboard"
    }
  ]
}

Storage Adapters

The SDK automatically detects the best storage adapter:

  1. Web: LocalStorageAdapter (uses localStorage)
  2. React Native: AsyncStorageAdapter (uses @react-native-async-storage/async-storage)
  3. Node.js: MemoryStorageAdapter (in-memory, no persistence)

Custom Storage Adapter

You can provide a custom storage adapter:

import { Signals, StorageAdapter } from '@logg/signals';

class CustomStorageAdapter implements StorageAdapter {
  async getItem(key: string): Promise<string | null> {
    // Your implementation
  }

  async setItem(key: string, value: string): Promise<void> {
    // Your implementation
  }

  async removeItem(key: string): Promise<void> {
    // Your implementation
  }
}

const signals = new Signals({
  apiKey: 'your-api-key',
  endpoint: 'https://signals.yourdomain.com/events',
  storage: new CustomStorageAdapter(),
});

Error Handling

The SDK includes automatic retry logic with exponential backoff:

  • Failed requests are retried up to maxRetries times (default: 3)
  • Retry delay doubles after each attempt (exponential backoff)
  • Events are persisted in storage and retried on next batch
const signals = new Signals({
  apiKey: 'your-api-key',
  endpoint: 'https://signals.yourdomain.com/events',
  maxRetries: 5,      // Retry up to 5 times
  retryDelay: 2000,   // Start with 2 second delay
  debug: true,        // Log retry attempts
});

React Integration

Track Page Views

import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

function App() {
  const location = useLocation();

  useEffect(() => {
    signals.event({
      type: 'page_view',
      page: location.pathname,
      title: document.title,
    });
  }, [location]);

  return <div>...</div>;
}

Track User Actions

function SignupButton() {
  const handleClick = () => {
    signals.event({
      type: 'button_click',
      element: 'signup_cta',
      page: '/landing',
    });

    // Navigate to signup...
  };

  return <button onClick={handleClick}>Sign Up</button>;
}

React Native Integration

import { Signals } from '@logg/signals';
import { useEffect } from 'react';
import { useNavigation } from '@react-navigation/native';

const signals = new Signals({
  apiKey: 'your-api-key',
  endpoint: 'https://signals.yourdomain.com/events',
});

function HomeScreen() {
  const navigation = useNavigation();

  useEffect(() => {
    // Track screen view
    signals.event({
      type: 'screen_view',
      screen: 'HomeScreen',
    });
  }, []);

  return (
    <Button
      title="Buy Premium"
      onPress={() => {
        signals.event({
          type: 'button_press',
          button: 'buy_premium',
          screen: 'HomeScreen',
        });
        navigation.navigate('Checkout');
      }}
    />
  );
}

Best Practices

1. Initialize Once

Create a single instance and reuse it:

// lib/signals.ts
import { Signals } from '@logg/signals';

export const signals = new Signals({
  apiKey: process.env.SIGNALS_API_KEY!,
  endpoint: process.env.SIGNALS_ENDPOINT!,
});

// app.tsx
import { signals } from './lib/signals';

signals.event({ type: 'app_opened' });

2. Flush on Exit

Always flush events before the app closes:

// React Native
useEffect(() => {
  return () => {
    signals.flush();
  };
}, []);

// Node.js
process.on('beforeExit', async () => {
  await signals.flush();
});

3. Type-safe Events

Define your event types for better DX:

type AppEvent =
  | { type: 'page_view'; page: string; title: string }
  | { type: 'button_click'; element: string }
  | { type: 'purchase'; productId: string; amount: number };

const signals = new Signals({...});

function trackEvent(event: AppEvent) {
  signals.event(event);
}

// Now fully type-safe!
trackEvent({ type: 'page_view', page: '/home', title: 'Home' });

4. User Identification

Include user ID in all events:

function trackUserEvent(event: Omit<EventData, 'userId'>) {
  const userId = getCurrentUserId();
  signals.event({ ...event, userId });
}

License

MIT