Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@bagdock/analytics

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bagdock/analytics - npm Package Compare versions

Comparing version
0.1.5
to
0.2.0
+11
-1
dist/index.d.mts

@@ -30,2 +30,10 @@ type EventType = 'click' | 'lead' | 'sale' | 'signup' | 'embed_render' | 'share' | 'qr_scan' | 'deep_link_open' | 'page_view' | 'reward_redeemed' | 'points_earned' | 'referral_completed';

}
interface UTMParams {
utm_source?: string;
utm_medium?: string;
utm_campaign?: string;
utm_term?: string;
utm_content?: string;
}
declare function parseUTM(url?: string): UTMParams;
declare class BagdockAnalytics {

@@ -37,3 +45,5 @@ private config;

private flushing;
private utm;
constructor(config: BagdockAnalyticsConfig);
getUTM(): UTMParams;
track(event: TrackableEvent): void;

@@ -64,2 +74,2 @@ trackClick(linkId: string, referralCode?: string): void;

export { BagdockAnalytics, type BagdockAnalyticsConfig, type EventType, type TrackableEvent };
export { BagdockAnalytics, type BagdockAnalyticsConfig, type EventType, type TrackableEvent, type UTMParams, parseUTM };

@@ -30,2 +30,10 @@ type EventType = 'click' | 'lead' | 'sale' | 'signup' | 'embed_render' | 'share' | 'qr_scan' | 'deep_link_open' | 'page_view' | 'reward_redeemed' | 'points_earned' | 'referral_completed';

}
interface UTMParams {
utm_source?: string;
utm_medium?: string;
utm_campaign?: string;
utm_term?: string;
utm_content?: string;
}
declare function parseUTM(url?: string): UTMParams;
declare class BagdockAnalytics {

@@ -37,3 +45,5 @@ private config;

private flushing;
private utm;
constructor(config: BagdockAnalyticsConfig);
getUTM(): UTMParams;
track(event: TrackableEvent): void;

@@ -64,2 +74,2 @@ trackClick(linkId: string, referralCode?: string): void;

export { BagdockAnalytics, type BagdockAnalyticsConfig, type EventType, type TrackableEvent };
export { BagdockAnalytics, type BagdockAnalyticsConfig, type EventType, type TrackableEvent, type UTMParams, parseUTM };

@@ -23,5 +23,37 @@ "use strict";

__export(index_exports, {
BagdockAnalytics: () => BagdockAnalytics
BagdockAnalytics: () => BagdockAnalytics,
parseUTM: () => parseUTM
});
module.exports = __toCommonJS(index_exports);
function parseUTM(url) {
if (typeof window === "undefined" && !url) return {};
try {
const params = new URL(url || window.location.href).searchParams;
const utm = {};
for (const key of ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"]) {
const val = params.get(key);
if (val) utm[key] = val;
}
return utm;
} catch {
return {};
}
}
var UTM_STORAGE_KEY = "bagdock_utm";
function persistUTM(utm) {
if (typeof sessionStorage === "undefined") return;
try {
const existing = JSON.parse(sessionStorage.getItem(UTM_STORAGE_KEY) || "{}");
sessionStorage.setItem(UTM_STORAGE_KEY, JSON.stringify({ ...existing, ...utm }));
} catch {
}
}
function getPersistedUTM() {
if (typeof sessionStorage === "undefined") return {};
try {
return JSON.parse(sessionStorage.getItem(UTM_STORAGE_KEY) || "{}");
} catch {
return {};
}
}
var DEFAULT_BASE_URL = "https://loyalty-api.bagdock.com";

@@ -37,2 +69,3 @@ var DEFAULT_FLUSH_INTERVAL = 5e3;

flushing = false;
utm = {};
constructor(config) {

@@ -50,2 +83,9 @@ this.config = {

if (typeof window !== "undefined") {
const freshUTM = parseUTM();
if (Object.keys(freshUTM).length > 0) {
persistUTM(freshUTM);
this.utm = freshUTM;
} else {
this.utm = getPersistedUTM();
}
window.addEventListener("beforeunload", () => this.flush());

@@ -60,2 +100,5 @@ if (typeof document !== "undefined") {

}
getUTM() {
return { ...this.utm };
}
track(event) {

@@ -66,6 +109,9 @@ if (this.isDuplicate(event)) {

}
const hasUTM = Object.keys(this.utm).length > 0;
const metadata = hasUTM ? { ...this.utm, ...event.metadata } : event.metadata;
this.queue.push({
...event,
landingPage: event.landingPage || (typeof window !== "undefined" ? window.location.href : void 0),
referrer: event.referrer || (typeof document !== "undefined" ? document.referrer : void 0)
referrer: event.referrer || (typeof document !== "undefined" ? document.referrer : void 0),
metadata
});

@@ -169,3 +215,4 @@ this.log("Queued:", event.eventType, `(${this.queue.length}/${this.config.batchSize})`);

0 && (module.exports = {
BagdockAnalytics
BagdockAnalytics,
parseUTM
});
// src/index.ts
function parseUTM(url) {
if (typeof window === "undefined" && !url) return {};
try {
const params = new URL(url || window.location.href).searchParams;
const utm = {};
for (const key of ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"]) {
const val = params.get(key);
if (val) utm[key] = val;
}
return utm;
} catch {
return {};
}
}
var UTM_STORAGE_KEY = "bagdock_utm";
function persistUTM(utm) {
if (typeof sessionStorage === "undefined") return;
try {
const existing = JSON.parse(sessionStorage.getItem(UTM_STORAGE_KEY) || "{}");
sessionStorage.setItem(UTM_STORAGE_KEY, JSON.stringify({ ...existing, ...utm }));
} catch {
}
}
function getPersistedUTM() {
if (typeof sessionStorage === "undefined") return {};
try {
return JSON.parse(sessionStorage.getItem(UTM_STORAGE_KEY) || "{}");
} catch {
return {};
}
}
var DEFAULT_BASE_URL = "https://loyalty-api.bagdock.com";

@@ -12,2 +43,3 @@ var DEFAULT_FLUSH_INTERVAL = 5e3;

flushing = false;
utm = {};
constructor(config) {

@@ -25,2 +57,9 @@ this.config = {

if (typeof window !== "undefined") {
const freshUTM = parseUTM();
if (Object.keys(freshUTM).length > 0) {
persistUTM(freshUTM);
this.utm = freshUTM;
} else {
this.utm = getPersistedUTM();
}
window.addEventListener("beforeunload", () => this.flush());

@@ -35,2 +74,5 @@ if (typeof document !== "undefined") {

}
getUTM() {
return { ...this.utm };
}
track(event) {

@@ -41,6 +83,9 @@ if (this.isDuplicate(event)) {

}
const hasUTM = Object.keys(this.utm).length > 0;
const metadata = hasUTM ? { ...this.utm, ...event.metadata } : event.metadata;
this.queue.push({
...event,
landingPage: event.landingPage || (typeof window !== "undefined" ? window.location.href : void 0),
referrer: event.referrer || (typeof document !== "undefined" ? document.referrer : void 0)
referrer: event.referrer || (typeof document !== "undefined" ? document.referrer : void 0),
metadata
});

@@ -143,3 +188,4 @@ this.log("Queued:", event.eventType, `(${this.queue.length}/${this.config.batchSize})`);

export {
BagdockAnalytics
BagdockAnalytics,
parseUTM
};
+1
-1
{
"name": "@bagdock/analytics",
"version": "0.1.5",
"version": "0.2.0",
"description": "Bagdock Analytics SDK — lightweight client-side event tracking with batching and dedup",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

+231
-34

@@ -7,3 +7,3 @@ ```

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

@@ -18,5 +18,6 @@ ---------++--------+++--------+++--------++ -------+++ -------++---++----++

The official Bagdock Analytics SDK — lightweight client-side event tracking with automatic batching and deduplication.
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](https://img.shields.io/npm/v/@bagdock/analytics.svg)](https://www.npmjs.com/package/@bagdock/analytics)
[![Bundle size](https://img.shields.io/bundlephobia/minzip/@bagdock/analytics)](https://bundlephobia.com/package/@bagdock/analytics)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

@@ -28,18 +29,33 @@

npm install @bagdock/analytics
# or
bun add @bagdock/analytics
```
```bash
yarn add @bagdock/analytics
```
## How tracking works
```bash
pnpm add @bagdock/analytics
```
Your app calls `track()` methods. The SDK deduplicates, queues, and flushes events in batches to the Bagdock API over HTTPS.
```bash
bun add @bagdock/analytics
```mermaid
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
```
## Quick start
On `beforeunload` and `visibilitychange`, the SDK flushes with `navigator.sendBeacon` so no events are lost during navigation.
---
## Get started
```typescript

@@ -49,43 +65,224 @@ import { BagdockAnalytics } from '@bagdock/analytics'

const analytics = new BagdockAnalytics({
writeKey: 'ak_live_...',
apiKey: 'YOUR_API_KEY',
autoPageView: true,
})
// Track a custom event
analytics.track('unit_viewed', {
unitId: 'unit_abc123',
unitSize: '10x10',
analytics.trackLead({
operatorId: 'opreg_acme',
referralCode: 'REF123',
metadata: { source: 'pricing_page' },
})
// Track a page view
analytics.page('/facilities/downtown')
analytics.trackSale({
operatorId: 'opreg_acme',
valuePence: 14900,
currency: 'GBP',
})
// Flush immediately (e.g., before page unload)
await analytics.flush()
analytics.destroy()
```
## Configuration
---
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `writeKey` | `string` | — | **Required.** Your Bagdock analytics write key |
| `baseUrl` | `string` | `https://api.bagdock.com` | API base URL |
| `flushInterval` | `number` | `5000` | Flush interval in milliseconds |
| `maxBatchSize` | `number` | `25` | Max events per batch |
## How to track UTM attribution
## API
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.
```typescript
// 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:
```typescript
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:
```tsx
'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:
```tsx
<AnalyticsProvider>
{children}
</AnalyticsProvider>
```
## How to track embed and widget events
Track renders and clicks from embedded widgets on partner sites:
```typescript
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:
```typescript
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, properties?)` | Track a custom event |
| `page(path?, properties?)` | Track a page view |
| `identify(userId, traits?)` | Identify a user |
| `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 |
| `reset()` | Clear user identity and queue |
| `destroy()` | Flush remaining events, clear timers, remove listeners |
## Zero dependencies
## Types
This SDK has no external runtime dependencies. It uses the native `fetch` API and is designed to be as lightweight as possible for client-side use.
### `TrackableEvent`
```typescript
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`
```typescript
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.
```mermaid
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
MIT — see [LICENSE](LICENSE).