npm package: @linkzly/web-sdk
Description: Full-featured browser SDK for Linkzly — event tracking, attribution, sessions, and deep linking
Latest version: 1.0.3

# @linkzly/web-sdk

[![Platform](https://img.shields.io/badge/platform-Web-blue.svg)](https://developer.mozilla.org)
[![TypeScript](https://img.shields.io/badge/language-TypeScript-blue.svg)](https://www.typescriptlang.org)
[![License](https://img.shields.io/badge/license-MIT-lightgrey.svg)](LICENSE)

Full-featured browser SDK for Linkzly — event tracking, attribution, session management, and deep linking for web applications. Zero dependencies, < 6KB gzipped.

## Features

- **Event Tracking** — Custom events, page views, purchases with automatic batching
- **Session Management** — Automatic visibility-based sessions (30s timeout, matching mobile SDKs)
- **Attribution Tracking** — UTM parameters, referrer capture, smart link handling
- **Deep Link / Smart Link** — Parse and handle Linkzly smart links with SPA support
- **User Identification** — Persistent visitor ID + custom user ID
- **Privacy Controls** — DNT/GPC respect, opt-out controls, GDPR-friendly
- **Offline Queue** — localStorage-backed event queue with retry and sendBeacon on unload
- **Scroll Depth** — Automatic max scroll depth tracking per page
- **Outbound Click Tracking** — Automatic external link click tracking
- **Time on Page** — Automatic session duration tracking
- **SPA Support** — History API hooks for pushState/popstate navigation

## Requirements

- Any modern browser (Chrome 66+, Firefox 60+, Safari 12+, Edge 79+)
- No dependencies

## Installation

### npm / yarn

```bash
npm install @linkzly/web-sdk
# or
yarn add @linkzly/web-sdk
```

### CDN (UMD)

```html
<script src="https://cdn.linkzly.com/web-sdk/latest/index.js"></script>
<script>
  LinkzlySDK.configure('YOUR_SDK_KEY');
</script>
```

## Quick Start

```typescript
import LinkzlySDK from '@linkzly/web-sdk';

// Initialize
LinkzlySDK.configure('YOUR_SDK_KEY');

// Track events
LinkzlySDK.trackEvent('button_click', { button_id: 'signup' });

// Track purchases
LinkzlySDK.trackPurchase({
  order_id: 'ORD-123',
  amount: 99.99,
  currency: 'USD',
});

// Set user ID after login
LinkzlySDK.setUserID('user_12345');
```

### Named Imports (Tree-Shakeable)

```typescript
import { configure, trackEvent, trackPageView, setUserID } from '@linkzly/web-sdk';

configure('YOUR_SDK_KEY');
trackEvent('signup_completed', { method: 'google' });
```

## API Reference

### Configuration

#### `configure(sdkKey, environment?, options?)`

Initialize the SDK. Must be called before any other method.

```typescript
import LinkzlySDK, { Environment } from '@linkzly/web-sdk';

LinkzlySDK.configure('YOUR_SDK_KEY', Environment.PRODUCTION, {
  autoTrackPageViews: false,  // opt-in SPA page view tracking (default: false)
  autoTrackSessions: true,    // visibility-based sessions (default: true)
  autoExtractUTM: true,       // auto-capture UTM params (default: true)
  autoCaptureReferrer: true,  // auto-capture document.referrer (default: true)
  respectDNT: true,           // honor Do Not Track / GPC (default: true)
  batchSize: 20,              // events per batch (default: 20)
  flushIntervalMs: 30000,     // flush timer (default: 30s)
  sessionTimeoutMs: 30000,    // session timeout (default: 30s)
  debug: false,               // console logging (default: false, auto-true in DEVELOPMENT)
});
```

**Environments:**
- `Environment.PRODUCTION` — Production endpoint (default)
- `Environment.STAGING` — Staging endpoint
- `Environment.DEVELOPMENT` — Development endpoint with verbose logging

### Event Tracking

#### `trackEvent(eventName, parameters?)`

Track a custom event.

```typescript
LinkzlySDK.trackEvent('add_to_cart', {
  product_id: 'SKU-123',
  quantity: 2,
  price: 49.99,
});
```

#### `trackPageView(pageUrl?, parameters?)`

Track a page view. Called automatically if `autoTrackPageViews: true`.

```typescript
LinkzlySDK.trackPageView(); // current page
LinkzlySDK.trackPageView('/products/123', { category: 'electronics' });
```

#### `trackPurchase(parameters?)`

Track a purchase event.

```typescript
LinkzlySDK.trackPurchase({
  order_id: 'ORD-98765',
  total: 599.99,
  currency: 'USD',
  items: 3,
});
```

#### `trackEventBatch(events)`

Track multiple events in one call.

```typescript
LinkzlySDK.trackEventBatch([
  { eventName: 'screen_view', parameters: { screen: 'home' } },
  { eventName: 'button_click', parameters: { button: 'signup' } },
]);
```

#### `flushEvents()`

Manually flush all queued events to the server.

```typescript
await LinkzlySDK.flushEvents();
```

#### `getPendingEventCount()`

Get the number of events waiting in the queue.

```typescript
const count = LinkzlySDK.getPendingEventCount();
```

### Deep Link / Smart Link

#### `handleSmartLink(url?)`

Parse a URL into deep link data (matching mobile SDK pattern).

```typescript
const data = LinkzlySDK.handleSmartLink();
// { url, path, parameters, smartLinkId, clickId }

const data = LinkzlySDK.handleSmartLink('https://example.com/product?id=123&slid=abc');
```

#### `addDeepLinkListener(listener)`

Listen for SPA navigation events (requires `autoTrackPageViews: true`).

```typescript
const unsubscribe = LinkzlySDK.addDeepLinkListener((data) => {
  console.log('Navigation:', data.path, data.parameters);
});

// Cleanup
unsubscribe();
```

### User Management

#### `setUserID(userID)` / `getUserID()` / `clearUserID()`

```typescript
LinkzlySDK.setUserID('user_12345');
const id = LinkzlySDK.getUserID(); // 'user_12345'
LinkzlySDK.clearUserID();
```

#### `getVisitorID()` / `resetVisitorID()`

Persistent device/browser identifier (survives across sessions).

```typescript
const visitorId = LinkzlySDK.getVisitorID(); // UUID persisted in localStorage
const newId = LinkzlySDK.resetVisitorID();   // generates new UUID
```

### Session Management

```typescript
const sessionId = LinkzlySDK.getSessionId();
LinkzlySDK.startSession();  // force new session
LinkzlySDK.endSession();    // end current session
```

Sessions auto-manage via the Page Visibility API — a new session starts when the page becomes visible after 30+ seconds of being hidden.

### Privacy Controls

```typescript
LinkzlySDK.setTrackingEnabled(false); // disable all tracking
const enabled = LinkzlySDK.isTrackingEnabled();
```

The SDK respects `navigator.doNotTrack` and `navigator.globalPrivacyControl` by default. Set `respectDNT: false` in config to override.

### Cleanup

```typescript
LinkzlySDK.destroy(); // remove all listeners, flush events, stop timers
```

## Framework Examples

### React / Next.js

```tsx
import { useEffect } from 'react';
import LinkzlySDK, { Environment } from '@linkzly/web-sdk';

export default function App({ children }) {
  useEffect(() => {
    LinkzlySDK.configure('YOUR_SDK_KEY', Environment.PRODUCTION, {
      autoTrackPageViews: true, // enable for Next.js SPA navigation
    });

    return () => LinkzlySDK.destroy();
  }, []);

  return <>{children}</>;
}
```

### Vue 3

```typescript
// main.ts
import { createApp } from 'vue';
import LinkzlySDK from '@linkzly/web-sdk';
import App from './App.vue';

LinkzlySDK.configure('YOUR_SDK_KEY');
createApp(App).mount('#app');
```

### Vanilla JS

```html
<script type="module">
  import LinkzlySDK from '@linkzly/web-sdk';

  LinkzlySDK.configure('YOUR_SDK_KEY');

  document.getElementById('buy-btn').addEventListener('click', () => {
    LinkzlySDK.trackEvent('button_click', { button: 'buy' });
  });
</script>
```

## Auto-Tracked Web Features

These features use the existing event payload structure (`customData` field) — no database changes required.

| Feature | Event Name | Data Collected |
|---------|-----------|----------------|
| **Scroll Depth** | `scroll_depth` | `max_depth_percent`, `page_url` |
| **Time on Page** | `time_on_page` | `duration_seconds`, `page_url` |
| **Outbound Clicks** | `outbound_click` | `destination_url`, `link_text`, `page_url` |

Scroll depth and time on page are sent automatically on page unload via `sendBeacon`. Outbound clicks are tracked in real-time.

## Event Delivery

- **Batching**: Up to 20 events per batch (configurable), max 100 per network request
- **Queue**: Events persisted in localStorage (up to 500, 24h expiry)
- **Flush triggers**: Batch size reached, 30s timer, `flushEvents()`, page unload
- **Unload**: `navigator.sendBeacon()` for reliable delivery on page close
- **Retry**: Exponential backoff (1s, 2s, 4s) with max 3 retries on 5xx/429

## Data Collected

When tracking is enabled, the SDK collects:
- Browser information (user agent, language, screen size, viewport, timezone)
- Page URL and referrer (external only)
- UTM parameters from URL
- Smart link / click attribution IDs
- Custom event data (provided by your application)

**Not collected:**
- Personal user information (unless explicitly provided via `setUserID`)
- Location data (beyond IP-based geo-location server-side)

> **Note:** For affiliate attribution tracking (cookie-based click IDs, checkout form helpers, S2S conversion payloads), use the separate [`@linkzly/affiliate-web`](../linkzly-affiliate-web) package.

## License

MIT

## Support

- Documentation: https://docs.linkzly.com
- Issues: https://github.com/linkzly/linkzly-web-sdk/issues
- Email: support@linkzly.com