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

@purplle/beacon-web-sdk

v0.1.0

Published

Beacon Web SDK for sending batched clickstream events to the Beacon Event Gateway.

Downloads

147

Readme

Beacon Web SDK

Thin browser SDK for sending batched clickstream events to the Beacon Event Gateway. Events are queued locally, batched, and sent with retries and offline support.


Table of contents


Install

Using npm (once published):

npm install @purplle/beacon-web-sdk

For local development in this repo:

npm install
npm run build

Quick start

import { Beacon } from '@purplle/beacon-web-sdk';
// Prefer env so the SDK picks the right endpoint
Beacon.init({
  env: 'production',
  token: '<platform-token>',
  visitorId: '<anonymous-id>',
  modeDevice: 'desktop',
});

Beacon.track(Beacon.createEvent('add_to_cart', {
  target_entity_id: 1001,
  target_entity_type: 'PRODUCT',
  quantity: 1,
  our_price: 1499,
}), {
  page_name: document.title,
  page_url: window.location.href,
});

Optional: manual flush, monitoring, and queue tuning:

Beacon.init({
  env: 'production',
  token: '<platform-token>',
  batchSize: 20,
  flushIntervalMs: 10_000,
  maxQueueSize: 1000,
  failedBatchTtlMs: 24 * 60 * 60 * 1000,
  onBatchSent: (payload) => { /* monitor */ },
  onBatchFailed: (payload) => { /* alert */ },
});

Beacon.track(Beacon.createEvent('interaction', {
    target_entity_id: 'checkout',
    target_entity_type: 'BUTTON',
  }), { page_url: window.location.href });
await Beacon.flush(); // optional: force send now

API reference

| Method | Description | |--------|-------------| | Beacon.init(config) | Initialize the SDK. Requires token; use env so the SDK resolves the ingestion endpoint. Call once (e.g. at app bootstrap). | | Beacon.createEvent(eventName, payload) | Build a schema-validated event payload from generated types. | | Beacon.track(event, pageData?) | Enqueue a prepared event from createEvent. Non-blocking; events are batched and sent in the background. | | Beacon.flush() | Manually trigger a flush (main queue + failed-batch queue). Returns a Promise<void>. |

Exported types: BeaconInitConfig, BeaconWebSDKConfig, BeaconTrackEvent, BeaconTrackPageData, BeaconTrackEventName, BeaconTrackEventByName, BeaconCreateEventInput, BeaconPreparedEvent, BeaconEvent, BeaconBatchPayload, EventData, PageData, and all generated event interfaces (for example AddToCartEvent).


Configuration

All options under Beacon.init(config).

| Option | Type | Default | Description | |--------|------|---------|-------------| | env | string | 'local' | Environment name; SDK resolves endpoint from this (local, sandbox, test, production or custom). | | token | string | required | Platform token; sent as token header. | | visitorId | string | — | Anonymous visitor id; sent as visitorppl header and used in batch_id. | | modeDevice | string | 'desktop' | Device mode; sent as mode_device header and in context. | | batchSize | number | 20 | Max events per batch (cap 50). Flush is triggered when the queue reaches this size. | | flushIntervalMs | number | 10_000 | Max time (ms) between automatic flushes. | | maxQueueSize | number | 1000 | Max events in the main queue; oldest are dropped (FIFO) when exceeded. | | maxQueueBytes | number | 5 * 1024 * 1024 | Approx max size (bytes) of the main queue; oldest events evicted when exceeded. | | failedBatchTtlMs | number | 24 * 60 * 60 * 1000 (24h) | TTL for failed batches. Batches older than this are removed from the failed-batch queue. | | maxEventAgeMs | number | — | If set, events older than this (ms) are dropped before send. Omit for no age limit. | | schemaVersion | string | '2.3' | Schema version sent in the payload. | | sdkVersion | string | 'web-1.0.0' | SDK version in context. | | appVersion | string \| null | — | App version in context. | | buildNumber | string | '1' | Build number in context. | | debug | boolean | false | When true, logs payloads and skips all network calls. | | onEventDropped | (event) => void | — | Called when an event is dropped (queue full or stale). | | onBatchSent | (payload) => void | — | Called when a batch is successfully sent. | | onBatchFailed | (payload) => void | — | Called when a batch is moved to the failed-batch queue after all retries. |


Event shape and strict typing

Beacon.createEvent(eventName, payload) + Beacon.track(event, pageData?) is strongly typed:

  • eventName must be one of generated event names.
  • payload must match the exact generated schema for that event.
  • track accepts only prepared events from createEvent.
  • pageData is optional and must match PageData.
  • invalid keys or wrong value types are rejected at compile time by TypeScript.

Example:

import { Beacon } from '@purplle/beacon-web-sdk';
const event = Beacon.createEvent('add_to_cart', {
  target_entity_id: 1001,
  target_entity_type: 'PRODUCT',
  quantity: 2,
  our_price: 1499,
});

Beacon.track(event, { page_url: window.location.href });

How it works

  1. EnqueueBeacon.track() appends events to the main queue (in-memory + localStorage).
  2. Flush triggers — A flush runs when:
    • The queue size reaches batchSize, or
    • The periodic timer fires every flushIntervalMs (e.g. 10s), or
    • The app goes online (after being offline), or
    • The tab becomes hidden (visibilitychange), or
    • You call Beacon.flush().
  3. Send — Batches are sent with retries (see Retries and HTTP status handling). On success, the batch is removed from the queue and onBatchSent is called.
  4. On failure after retries — The batch is moved to the failed-batch queue (dead-letter queue), persisted in localStorage, and onBatchFailed is called. The failed-batch queue is retried on the same 10s flush cycle with exponential backoff until success or TTL.

Queues and persistence

| Queue | Storage key | Purpose | |-------|-------------|---------| | Main event queue | beacon_event_queue | Pending events; FIFO; limited by maxQueueSize and maxQueueBytes. | | Failed-batch queue | beacon_failed_batch_queue | Batches that failed after all immediate retries; retried with backoff; evicted after failedBatchTtlMs. |

  • Both use localStorage when available (e.g. browser). Data survives refresh and is sent when the app is back online or on the next flush.
  • There is no time-based TTL on the main queue; only count and size limits. Use maxEventAgeMs to drop very old events before send.

Retries and HTTP status handling

HTTP status behaviour

| Status | Treated as | Behaviour | |--------|------------|-----------| | 2xx | Success | Batch removed from queue; onBatchSent called. | | 500, 502, 503, 429 | Retryable | Retried with exponential backoff; after all retries, batch goes to failed-batch queue. | | 400, 401, 403 | Non-retryable | No retries; batch moved to failed-batch queue immediately. | | Other | Non-retryable | Same as above. |

The SDK honours the Retry-After response header when present for retryable statuses.

Main queue retry (per batch)

  • Each batch is sent through a RetryManager: up to 4 attempts (1 initial + 3 retries).
  • Backoff between attempts: 2s → 4s → 8s (capped at 16s), or Retry-After if provided.
  • If all 4 fail: batch is moved to the failed-batch queue and onBatchFailed is called.

Failed-batch queue retry

  • Failed batches are stored with a failedAt timestamp and retryCount.
  • On each flush cycle (every ~10s), batches that are due (based on backoff) are tried again.
  • Per attempt: same RetryManager (4 attempts, 2s/4s/8s backoff).
  • Between attempts: exponential backoff 30s → 1m → 2m → … (capped at 1h). So the next “due” time is failedAt + min(30s × 2^retryCount, 1h).
  • Batches older than failedBatchTtlMs (default 24h) are removed and no longer retried.

Page lifecycle (visibility & unload)

  • Tab hidden (visibilitychangedocument.visibilityState === 'hidden'): the SDK triggers a normal flush() so pending events are sent with fetch before the user leaves.
  • Page unload (beforeunload): the SDK sends one batch via navigator.sendBeacon() so some events can still be delivered when the tab is closed. Custom headers (e.g. token) are not sent with sendBeacon; your backend may need to accept beacon requests without them or support token in body for that path. If sendBeacon fails, that batch is re-queued and will be sent on the next load.

Environments and endpoints

Pass env to Beacon.init(); the SDK resolves the ingestion URL internally.

| env | Endpoint | |-------|----------| | local | http://localhost:8080/de/events/v1/push | | sandbox | https://sandbox.purplle.com/de/events/v1/push | | test | https://test.purplle.com/de/events/v1/push | | production | https://www.purplle.com/de/events/v1/push |

Unsupported env values throw at init. Extend resolveEndpointFromEnv in the SDK (or use a wrapper) to add more environments.


Example and local development

A minimal demo lives under example-apps/ (e.g. React). To run it:

  1. Build the SDK:

    npm install
    npm run build
  2. Serve the repo over HTTP (from project root):

    npx serve .
  3. Open the example (e.g. http://localhost:3000/beacon-web-sdk/example-apps/react/) and trigger events. Batched requests go to the endpoint for your env (e.g. http://localhost:8080/de/events/v1/push for local).


Summary

  • Init with env and token; track with typed generated event objects and optional pageData.
  • Events are queued in localStorage, batched (size + 10s interval), and sent with retries.
  • Failed batches go to a dead-letter queue and are retried with backoff for up to 24h (configurable).
  • Tab hidden and page unload trigger an extra flush and a best-effort sendBeacon for better delivery.