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

@eventra_dev/eventra-sdk

v2.0.4

Published

Production-grade analytics SDK for feature usage and product behavior — batching, retries, circuit breaker, runtime-aware (browser, Node, Edge, serverless).

Readme

Eventra SDK

Production-grade analytics SDK for tracking feature usage, product behavior, and backend activity.

Eventra helps you:

  • Track feature adoption
  • Detect unused features
  • Understand user behavior
  • Monitor backend usage
  • Analyze product growth

Why Eventra

Eventra SDK is:

  • Lightweight (~minimal overhead)
  • Runtime-aware (Browser, Node, Edge, Serverless)
  • Resilient (batching + retry + circuit breaker)
  • Durable (browser persistence)
  • Consistent (same delivery model across all environments)
  • TypeScript-first

Installation

npm i @eventra_dev/eventra-sdk
pnpm add @eventra_dev/eventra-sdk
yarn add @eventra_dev/eventra-sdk

Quick Start

import { Eventra } from "@eventra_dev/eventra-sdk";

const tracker = new Eventra({
  apiKey: "YOUR_PROJECT_API_KEY",
});

tracker.track("checkout.completed", {
  userId: "user_123",
});

That's it.

By default, events are sent to https://api.eventra.dev/api/v1/ingest/batch.

The SDK automatically handles:

  • batching
  • retries
  • queueing
  • flushing
  • runtime adaptation

Runtime Behavior

Eventra SDK adapts automatically:

| Environment | Behavior | | ----------- | -------------------------------------- | | Browser | batching + persistence + retry | | Node.js | batching + retry | | Serverless | immediate flush + retry | | Edge | batching + retry | | Anything else (e.g. Cloudflare Workers) | not distinctly detected — falls back to the same batching + retry path as Node.js |

No config needed.


Event Properties

You can attach any JSON data:

tracker.track("checkout.completed", {
  userId: "user_123",
  properties: {
    plan: "pro",
    price: 29,
    currency: "USD"
  }
});

Minimal:

tracker.track("app.loaded");

Event names are trimmed to 64 characters, userId to 120 (API limits). track() never throws: an empty name, or properties that exceed the depth/size limits, are dropped silently and reported via onEventsDropped instead.


Common Examples

Feature Usage

tracker.track("feature.used", {
  userId: "user_123",
  properties: {
    feature: "dashboard"
  }
});

Page View

tracker.track("page.viewed", {
  properties: {
    path: window.location.pathname
  }
});

API Usage

tracker.track("api.request", {
  properties: {
    endpoint: "/checkout",
    method: "POST",
    status: 200
  }
});

Error Tracking

tracker.track("error.occurred", {
  properties: {
    message: "Payment failed",
    code: "PAYMENT_ERROR"
  }
});

Where You Can Use It

  • Browser apps
  • React / Next.js
  • Node.js backends
  • NestJS services
  • Express APIs
  • Edge runtimes
  • Serverless (AWS / Vercel)
  • Bun / Deno

Usage by Environment

Browser

const tracker = new Eventra({
  apiKey: "...",
});

tracker.track("page.viewed");
  • batching
  • retry
  • persistence (localStorage)
  • flush on tab close (fetch with keepalive)

Optional — single sender across tabs:

const tracker = new Eventra({
  apiKey: "...",
  multiTabMode: "leader",
});

Privacy note: persistence stores the pending queue — including any userId/properties you pass to track() — as plaintext JSON in localStorage so it survives reloads. If that's a concern (e.g. properties may carry PII on a shared/public device), disable it:

const tracker = new Eventra({
  apiKey: "...",
  persistQueue: false,
});

With persistQueue: false, nothing is written to localStorage; the queue lives in memory only and is lost if the page closes before delivery (live cross-tab dedup via BroadcastChannel still works, since that's never written to disk).


Node.js

const tracker = new Eventra({
  apiKey: "...",
});

tracker.track("invoice.created");
  • batching
  • retry
  • auto flush (interval)
  • graceful shutdown support

Serverless (IMPORTANT)

export default async function handler(req, res) {
  const tracker = new Eventra({
    apiKey: "...",
  });

  tracker.track("function.called");

  await tracker.flush();

  res.status(200).end();
}
  • immediate flush
  • retry
  • optimized for short-lived environments

Configuration

const tracker = new Eventra({
  apiKey: "YOUR_PROJECT_API_KEY",
  flushInterval: 2000,
  maxBatchSize: 50,
  maxQueueSize: 10000,
  maxRetries: 3,
  retryBaseDelayMs: 300,
  maxPayloadBytes: 60000,
  onEventsDropped: (count) => {
    console.warn(`Dropped ${count} event(s) — queue full`);
  },
  onDeliveryFailed: ({ status, events }) => {
    console.error(`Ingest rejected batch (${status})`, events.length);
  },
});

endpoint is optional — omit it to use the production ingest URL.

For self-hosted or local development:

const tracker = new Eventra({
  apiKey: "...",
  endpoint: "...",
});

Options

| option | description | | ------------- | --------------------------- | | apiKey | Project API key (required) | | endpoint | Ingest batch URL | | flushInterval | Flush interval (ms) | | maxBatchSize | Events per batch | | maxQueueSize | Max buffer size | | maxRetries | Total delivery attempts per batch (default: 3) | | retryBaseDelayMs | Base delay for exponential backoff (ms) | | maxPayloadBytes | Max serialized batch size (default: 60000) | | fetchImpl | Custom fetch (Node without native fetch, tests) | | autoFlushOnExit | Flush on process exit (Node / serverless, default: true) | | disableTimer | Disable periodic flush timer | | onEventsDropped | Callback when queue is full | | onDeliveryFailed | Callback on permanent ingest errors (4xx except 429) | | multiTabMode | "independent" (default) or "leader" (browser only) | | persistQueue | Persist the queue to localStorage in the browser (browser only, default: true) — false keeps it in-memory only, avoiding writing userId/properties to disk |


Manual Flush

await tracker.flush();

Shutdown

Graceful shutdown — flush first, then cleanup:

await tracker.shutdown();

For Node / serverless, prefer explicit shutdown over relying on process signals:

await tracker.flush();
await tracker.shutdown();

destroy() stops timers and listeners immediately without flushing.


Reliability Features

Eventra SDK includes:

  • Idempotency (UUID v4 per event, stable across retries)
  • Retry with exponential backoff + jitter (capped)
  • Circuit breaker with half-open recovery
  • Queue-based delivery (all runtimes)
  • Safe dequeue (events removed only after successful ingest)
  • Queue persistence (browser) with merge + cross-tab sync — opt out via persistQueue: false if localStorage isn't acceptable for your data
  • Multi-tab leader election with re-election
  • fetch + keepalive on tab close (with x-api-key, size-checked)
  • pagehide + visibilitychange flush hooks
  • Property validation at track() (depth + size) — fails soft via onEventsDropped, track() never throws
  • Payload byte limits per batch
  • Permanent error handling via onDeliveryFailed (401, 422, etc.)
  • Automatic requeue on network errors and 429 / 5xx

Test Coverage

100% statement/branch/function/line coverage (v8 provider, pnpm test:coverage).

80 vitest tests across 12 suites cover the entire delivery pipeline:

  • track() validation — name length, userId truncation, properties depth/size, idempotency keys
  • Batching — auto-flush at maxBatchSize, periodic flush via timer, queue overflow drop
  • Retry & backoff — 429, 5xx, network errors, no-retry on 4xx, fetch timeout/abort
  • Circuit breaker — opens after 5 consecutive failures, cools down, resets on success
  • Payload guards — multi-batch splitting, oversize event dropped via onDeliveryFailed(413)
  • Shutdown & destroy — flush ordering, timer teardown, idempotent shutdown
  • Runtime detection — Node, Edge (EdgeRuntime), Serverless (AWS Lambda), unknown fallback, Node process exit handling
  • Browser mode — persistence, leader election (including multi-tab lease races), BroadcastChannel sync, fetch keepalive, pagehide
  • Storage edge cases — corrupted/blocked localStorage, quota exceeded, queue trimming
  • Internal fallbacks — crypto.getRandomValues, Buffer/string-length byte counting

Run locally:

pnpm --filter @eventra_dev/eventra-sdk test
pnpm --filter @eventra_dev/eventra-sdk test:coverage

Node version note: the SDK itself supports Node 18+ (see engines), but vitest@4 depends on node:util's styleText, which requires Node ≥ 20.12 — running the test suite on an older Node 20.x patch (or Node 18) fails to start. CI runs the matrix on Node 20 and 22; locally, use the version pinned in the repo's .nvmrc.


Event Format

{
  "sentAt": "2026-03-12T10:00:00Z",
  "sdk": {
    "name": "@eventra_dev/eventra-sdk",
    "version": "<sdk-version>",
    "runtime": "browser"
  },
  "events": [
    {
      "idempotencyKey": "550e8400-e29b-41d4-a716-446655440000",
      "name": "user_signup",
      "userId": "user_123",
      "timestamp": "2026-03-12T10:00:00Z",
      "properties": {}
    }
  ]
}

idempotencyKey is generated once per event and stays identical across every retry attempt of that event — delivery is at-least-once, not exactly-once. A retried batch resends the same events with the same keys, and under a slow network a request can be fully received and processed by the ingestion endpoint even after the SDK's own fetch timed out client-side and moved on to a retry — the backend seeing the same idempotencyKey more than once is expected, not a sign of a bug, and should be deduplicated on that key rather than on request count.

Limits enforced by the SDK at track() time:

| Field | Limit | |-------|-------| | name | trimmed, ≤ 64 chars | | userId | ≤ 120 chars | | properties (JSON serialized) | ≤ 32,000 bytes per event | | Property nesting depth | ≤ 8 | | Batch payload | ≤ maxPayloadBytes (default 60,000) | | Fetch timeout | 5,000 ms | | Circuit breaker | opens after 5 consecutive failures, cools down 5,000 ms |


Docs

https://eventra.dev/docs


License

MIT