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

@bagdock/analytics

v0.2.0

Published

Bagdock Analytics SDK — lightweight client-side event tracking with batching and dedup

Readme

  ----++                                ----++                    ---+++     
  ---+++                                ---++                     ---++      
 ----+---     -----     ---------  --------++ ------     -----   ----++----- 
 ---------+ --------++----------++--------+++--------+ --------++---++---++++
 ---+++---++ ++++---++---+++---++---+++---++---+++---++---++---++------++++  
----++ ---++--------++---++----++---+++---++---++ ---+---++     -------++    
----+----+---+++---++---++----++---++----++---++---+++--++ --------+---++   
---------++--------+++--------+++--------++ -------+++ -------++---++----++  
 +++++++++   +++++++++- +++---++   ++++++++    ++++++    ++++++  ++++  ++++  
                     --------+++                                             
                       +++++++                                               

@bagdock/analytics

Track events, attribute conversions, and measure engagement across Bagdock-powered self-storage apps. The SDK batches events client-side, deduplicates within a configurable window, captures UTM attribution automatically, and flushes gracefully on page unload.

npm version Bundle size License: MIT

Install

npm install @bagdock/analytics
# or
bun add @bagdock/analytics

How tracking works

Your app calls track() methods. The SDK deduplicates, queues, and flushes events in batches to the Bagdock API over HTTPS.

sequenceDiagram
  participant App
  participant SDK as @bagdock/analytics
  participant API as Bagdock API

  App->>SDK: track({ eventType: 'lead' })
  SDK->>SDK: Dedup check
  SDK->>SDK: Attach UTM metadata
  SDK->>SDK: Queue event

  Note over SDK: Flush on timer (5 s) or batch full (25)

  SDK->>API: POST /api/loyalty/events
  API-->>SDK: 200 OK

On beforeunload and visibilitychange, the SDK flushes with navigator.sendBeacon so no events are lost during navigation.


Get started

import { BagdockAnalytics } from '@bagdock/analytics'

const analytics = new BagdockAnalytics({
  apiKey: 'YOUR_API_KEY',
  autoPageView: true,
})

analytics.trackLead({
  operatorId: 'opreg_acme',
  referralCode: 'REF123',
  metadata: { source: 'pricing_page' },
})

analytics.trackSale({
  operatorId: 'opreg_acme',
  valuePence: 14900,
  currency: 'GBP',
})

await analytics.flush()
analytics.destroy()

How to track UTM attribution

The SDK captures UTM parameters from the URL on init, persists them in sessionStorage for the tab's lifetime, and attaches them as metadata to every event.

// User lands on: https://example.com?utm_source=google&utm_medium=cpc&utm_campaign=spring

const analytics = new BagdockAnalytics({ apiKey: 'YOUR_API_KEY' })

analytics.getUTM()
// → { utm_source: 'google', utm_medium: 'cpc', utm_campaign: 'spring' }

analytics.trackLead({ operatorId: 'opreg_acme' })
// → event metadata includes utm_source, utm_medium, utm_campaign

Parse UTM parameters from any URL:

import { parseUTM } from '@bagdock/analytics'

parseUTM('https://example.com?utm_source=partner&utm_campaign=launch')
// → { utm_source: 'partner', utm_campaign: 'launch' }

How to use with Next.js App Router

Create a provider component that initializes the SDK once and tracks route changes:

'use client'

import { useEffect, useRef } from 'react'
import { usePathname, useSearchParams } from 'next/navigation'
import { BagdockAnalytics } from '@bagdock/analytics'

export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
  const pathname = usePathname()
  const searchParams = useSearchParams()
  const ref = useRef<BagdockAnalytics | null>(null)
  const prevPath = useRef('')

  useEffect(() => {
    if (!ref.current) {
      ref.current = new BagdockAnalytics({
        apiKey: process.env.NEXT_PUBLIC_ANALYTICS_API_KEY!,
        autoPageView: true,
      })
    }
    return () => { ref.current?.destroy(); ref.current = null }
  }, [])

  useEffect(() => {
    const path = pathname + (searchParams?.toString() ? `?${searchParams}` : '')
    if (path !== prevPath.current) {
      prevPath.current = path
      ref.current?.trackPageView()
    }
  }, [pathname, searchParams])

  return <>{children}</>
}

Wrap your root layout:

<AnalyticsProvider>
  {children}
</AnalyticsProvider>

How to track embed and widget events

Track renders and clicks from embedded widgets on partner sites:

const analytics = new BagdockAnalytics({ apiKey: 'YOUR_API_KEY' })

analytics.trackEmbedRender('opreg_acme')
analytics.trackClick('link_abc123', 'REF456')

How to track loyalty program events

Track points, rewards, and referrals:

analytics.track({
  eventType: 'points_earned',
  memberId: 'mem_abc',
  operatorId: 'opreg_acme',
  valuePence: 500,
})

analytics.track({
  eventType: 'reward_redeemed',
  memberId: 'mem_abc',
  metadata: { rewardId: 'rwd_xyz', tier: 'gold' },
})

analytics.track({
  eventType: 'referral_completed',
  referralCode: 'REF123',
  memberId: 'mem_abc',
})

Methods

| Method | Description | |--------|-------------| | track(event) | Track any event with full control over the payload | | trackClick(linkId, referralCode?) | Track a link click with optional referral attribution | | trackLead(params) | Track a lead conversion | | trackSale(params) | Track a completed sale | | trackPageView() | Track a page view (captures URL and referrer automatically) | | trackEmbedRender(operatorId?) | Track when an embedded widget renders | | getUTM() | Return the current UTM attribution context | | flush() | Flush the event queue immediately | | destroy() | Flush remaining events, clear timers, remove listeners |

Types

TrackableEvent

interface TrackableEvent {
  eventType: EventType
  linkId?: string
  memberId?: string
  operatorId?: string
  referralCode?: string
  valuePence?: number
  currency?: string
  landingPage?: string
  referrer?: string
  metadata?: Record<string, unknown>
}

EventType

type EventType =
  | 'click' | 'lead' | 'sale' | 'signup' | 'embed_render'
  | 'share' | 'qr_scan' | 'deep_link_open' | 'page_view'
  | 'reward_redeemed' | 'points_earned' | 'referral_completed'

Exports

| Export | Type | Description | |--------|------|-------------| | BagdockAnalytics | class | Main SDK class | | parseUTM | function | Parse UTM parameters from a URL | | BagdockAnalyticsConfig | interface | Constructor config shape | | TrackableEvent | interface | Event payload shape | | EventType | type | Union of supported event type strings | | UTMParams | interface | UTM parameter shape |

How to configure the SDK

| Option | Type | Default | Description | |--------|------|---------|-------------| | apiKey | string | — | Required. Your Bagdock API key | | baseUrl | string | https://loyalty-api.bagdock.com | API base URL | | flushIntervalMs | number | 5000 | How often to flush queued events (ms) | | batchSize | number | 25 | Max events per flush batch | | dedupWindowMs | number | 500 | Window for dropping duplicate events (ms) | | autoPageView | boolean | false | Track a page view on init | | debug | boolean | false | Log SDK activity to the console |

How batching and deduplication work

Events queue in memory and flush on a timer or when the batch size fills. Duplicate events — defined as matching eventType, linkId, referralCode, and memberId — within the dedup window are dropped silently.

graph TD
  A["track() called"] --> B{"Duplicate?"}
  B -- Yes --> C["Drop"]
  B -- No --> D["Add to queue"]
  D --> E{"Queue full?"}
  E -- Yes --> F["Flush"]
  E -- No --> G["Wait for timer"]
  G --> F

Data privacy and security

  • Outbound only. The SDK sends event data to the Bagdock API. It does not read cookies, fingerprint browsers, or collect PII.
  • Scoped API keys. Use a key scoped to analytics write access. Never embed keys with broader permissions in client-side code.
  • Session-scoped UTM storage. UTM data lives in sessionStorage, scoped to the current tab. It is cleared when the tab closes.
  • HTTPS enforced. All API requests use Authorization: Bearer headers over TLS.
  • Zero dependencies. No third-party runtime code. The SDK uses native fetch and navigator.sendBeacon.

License

MIT — see LICENSE.