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

@adtivity/adtivity-sdk

v1.8.0

Published

Adtivity SDK for web and mobile applications (React Native) to track user behavior and analytics.

Readme

Adtivity SDK

Lightweight analytics SDK for web and React Native apps — with built-in Web3/blockchain support.

Installation

npm install @adtivity/adtivity-sdk

For React Native, also install the async storage peer dependency:

npm install @react-native-async-storage/async-storage

Quick Start

import { init, trackEvent } from '@adtivity/adtivity-sdk'

init({ apiKey: 'your-api-key' })

trackEvent('Page Loaded', { section: 'home' })

Entry points

The SDK ships as three separate bundles. Import only what you use — session replay and error tracking are not included in the main bundle, so they cost you nothing unless you ask for them.

| Import | What it gives you | Size (gzipped) | |---|---|---| | @adtivity/adtivity-sdk | Events, click/page/location tracking, identity, Web3 | ~8.7 KB | | @adtivity/adtivity-sdk/session-replay | DOM recording and playback | ~6.6 KB | | @adtivity/adtivity-sdk/errors | Error tracking + the end-user report widget | ~5.5 KB |

All three share the same session and anonymous IDs, so a recording, an error, and an event from the same visit line up in your dashboard automatically.


Session Replay

Records the DOM so you can watch a session back — mouse, clicks, scrolls, input, navigation and errors.

import { SessionReplay } from '@adtivity/adtivity-sdk/session-replay'

const replay = new SessionReplay({ apiKey: 'your-api-key' })
replay.start()

You don't need to set an endpoint: replay sends to the same API base URL init() already resolved.

Masking. Password and credit-card inputs are masked automatically. Add your own selectors for anything else, or mark elements in your markup with data-adtv-mask (masks text) or data-adtv-block (replaces the element).

new SessionReplay({
  apiKey: 'your-api-key',
  maskSelectors: ['input[type=email]', '[data-sensitive]'],
  blockSelectors: ['.customer-details'],
  sampleRate: 0.25,        // record 25% of sessions
  captureNetwork: false,   // fetch/XHR method, url, status, duration
})

| Option | Default | Description | |---|---|---| | sampleRate | 1 | Fraction of sessions to record | | maskSelectors | [] | Extra selectors whose text is masked | | blockSelectors | [] | Selectors replaced with a placeholder | | captureNetwork | false | Record fetch/XHR results | | captureErrors | true | Mark JS errors on the replay timeline | | idleTimeout | 5 min | End the session after this much inactivity | | maxSessionDuration | 30 min | Hard cap on one recording |

replay.pause() / replay.resume() / replay.stop() control it at runtime.

Don't record screens showing other people's data. If your app has an admin area displaying your own customers' information, don't start the recorder there. Masking is for fields; whole screens are better handled by not recording them.


Error Tracking

One call captures uncaught exceptions and unhandled promise rejections, then groups them into issues in your dashboard.

import { initErrorTracking } from '@adtivity/adtivity-sdk/errors'

initErrorTracking({ apiKey: 'your-api-key' })

Rejected promises never reach window.onerror — every failed await and rejected fetch lands somewhere else entirely. On most modern apps those are the majority of real errors, so they're captured separately.

Grouping. Raw errors are a firehose; one broken component can throw thousands of times. Errors are collapsed into issues by normalizing variable data out of the message (User 4821 not found and User 9134 not found are one issue) and by preferring the first stack frame that is your own code (the same generic message thrown from two different places stays two issues). Deploy-hashed filenames group stably across releases.

Identical errors are also collapsed client-side before sending — a render loop throwing 5,000 times sends one entry.

initErrorTracking({
  apiKey: 'your-api-key',
  release: '2.4.1',
  beforeSend: (error) => !error.message.includes('chrome-extension'),
})

| Option | Default | Description | |---|---|---| | widget | true | Show the end-user report widget on error | | release | — | App version, to attribute issues to a deploy | | maxErrorsPerPage | 25 | Hard cap per page load | | beforeSend | — | Return false to drop an error | | flushInterval | 4000 | How often queued errors are sent (ms) |

Report caught errors yourself with captureError:

import { captureError } from '@adtivity/adtivity-sdk/errors'

try {
  await checkout()
} catch (err) {
  captureError(err, { step: 'payment' })
}

Support Widget

When an error occurs, a small panel appears bottom-right letting the user say what happened. The report lands in your Support Inbox linked to both the error and the session replay of what they were doing.

It's on by default with initErrorTracking(). Pass widget: false for silent collection only.

It never shows technical detail — no message, no stack, no filename. Your users can't act on a TypeError, and showing your internals to anyone who can trigger an error is a leak. That all goes to your dashboard instead.

initErrorTracking({
  apiKey: 'your-api-key',
  widgetTitle: "That didn't work",
  widgetMessage: "Tell us what happened and we'll take a look.",
  widgetSubmitLabel: 'Send report',
  widgetSuccessMessage: "Thanks — we're on it.",
  widgetAccent: '#e05104',
  widgetAskEmail: true,
})

It shows once per page load (a page that throws repeatedly shouldn't nag), renders in a shadow root so your CSS and its CSS can't collide, and confirms to the user even if the upload fails — showing an error inside an error reporter is a bleak experience.


Identifying users

Call identify() when someone signs in. Pass an email — this is the difference between knowing that a user is churning and being able to contact them.

import { identify, resetIdentity } from '@adtivity/adtivity-sdk'

identify('user_5150', {
  email: '[email protected]',
  name: 'Dana Okafor',
  plan: 'pro',              // anything else is kept as a trait
})

// on sign-out
resetIdentity()

email and name are stored against the person rather than only recorded as event properties. Everything else you pass is kept as traits, so it shows up next to a failing session.

Activity captured before sign-in is attributed retroactively. Identification almost always happens mid-session — someone browses anonymously, hits the error that made them give up, and only then signs in. Matching on the anonymous ID means that earlier error is still tied to a real person, so you don't have to call identify() early to benefit from it.

Call resetIdentity() on logout. Identity is persisted to localStorage so it survives a refresh — a logged-in user who reloads shouldn't silently go anonymous. The cost of that is the next person on a shared browser inheriting the last one's identity if you never clear it.

Once identified, the person appears against:

| Surface | What you get | |---|---| | Error Tracking | Every person who hit an issue, so you can email those affected rather than knowing only that "12 users" were | | Session Replay | Recordings listed by person, not anonymous ID | | Support Widget | The reporter's email pre-filled, so a logged-in user who leaves it blank is still reachable |

Identity is scoped per company: two Adtivity customers can each have a user with the same email without colliding.


Everything together

'use client'
import { useEffect } from 'react'
import { init, initClickTracking, initPageTracking, identify } from '@adtivity/adtivity-sdk'
import { SessionReplay } from '@adtivity/adtivity-sdk/session-replay'
import { initErrorTracking } from '@adtivity/adtivity-sdk/errors'

const API_KEY = process.env.NEXT_PUBLIC_ADTIVITY_API_KEY!

export function Analytics() {
  useEffect(() => {
    init({ apiKey: API_KEY })
    initPageTracking()
    initClickTracking({ trackAllButtons: true })

    initErrorTracking({ apiKey: API_KEY })

    // Once you know who the user is — errors and recordings become contactable
    if (user) identify(user.id, { email: user.email, name: user.name })

    const replay = new SessionReplay({ apiKey: API_KEY, sampleRate: 0.5 })
    replay.start()
    return () => replay.stop()
  }, [])

  return null
}

Initialization

import { init } from '@adtivity/adtivity-sdk'

// Simple
init('your-api-key')

// With full config
init({
  apiKey: 'your-api-key',
  apiBaseUrl: 'https://your-backend.com', // optional; has a default
  debug: true,                            // logs to console
  batchSize: 10,                          // flush after N events (default: 10)
  flushInterval: 5000,                    // flush every N ms (default: 5000)
  maxRetries: 3,
  retryDelayMs: 1000,
  collectData: true,                      // set false to start with consent off
})

Tracking

Custom events

import { trackEvent } from '@adtivity/adtivity-sdk'

trackEvent('Purchase Completed', {
  product_id: 'prod_123',
  amount: 49.99,
  currency: 'USD',
})

User identification

import { identify } from '@adtivity/adtivity-sdk'

identify('user_abc', {
  email: '[email protected]',
  plan: 'pro',
})

Sent immediately (not batched). Links all subsequent events to this user ID.


Web3 / Blockchain

Connect a wallet

Call setWallet when the user connects their wallet. The address and chain ID will be automatically attached to every subsequent event.

import { setWallet } from '@adtivity/adtivity-sdk'

// On wallet connect
setWallet('0xabc...def', '1')   // address, chainId (chainId optional)

// On wallet disconnect
setWallet(null)

Track transactions

import { trackTx } from '@adtivity/adtivity-sdk'

trackTx('Token Swap', {
  transaction_hash: '0xdef...789',
  contract_address: '0x123...456',
  amount: '100',
  token: 'USDC',
})

transaction_hash and contract_address are promoted to top-level fields in the API payload. wallet_address and chain_id (set via setWallet) are included automatically.

Full Web3 example

import { init, setWallet, identify, trackTx, trackEvent } from '@adtivity/adtivity-sdk'

init({ apiKey: 'your-api-key' })

// User connects wallet
async function onWalletConnect(address: string, chainId: string) {
  setWallet(address, chainId)
  identify(address)             // treat wallet address as the user ID
  trackEvent('Wallet Connected', { chainId })
}

// User submits a transaction
async function onTransactionSent(hash: string) {
  trackTx('NFT Minted', {
    transaction_hash: hash,
    contract_address: '0x...',
    token_id: '42',
  })
}

// User disconnects
function onWalletDisconnect() {
  setWallet(null)
  trackEvent('Wallet Disconnected')
}

Auto-Trackers

These are opt-in. Call them once after init.

Page views

Tracks initial load and SPA navigation (history API + popstate).

import { initPageTracking } from '@adtivity/adtivity-sdk'

initPageTracking()

Captured fields: title, pathname, query, url, timestamp.

Clicks

Tracks clicks on any element with a data-adtivity-* attribute.

import { initClickTracking } from '@adtivity/adtivity-sdk'

initClickTracking()

| Attribute | Event prefix | Example event name | |---|---|---| | data-adtivity-track | none | "Hero CTA" | | data-adtivity-button-track | Button: | "Button: Sign Up" | | data-adtivity-link-track | Link: | "Link: View Pricing" |

<button data-adtivity-button-track="Sign Up">Sign Up</button>

<a href="/pricing" data-adtivity-link-track="View Pricing">Pricing</a>

<!-- Attach extra properties as JSON -->
<button
  data-adtivity-button-track="Add to Cart"
  data-adtivity-props='{"product_id": "123", "price": 29.99}'>
  Add to Cart
</button>

Geolocation

Fetches country, region, city, and IP via ipapi.co on init.

import { initLocationTracking } from '@adtivity/adtivity-sdk'

initLocationTracking()

Privacy & Consent

import { setConsent } from '@adtivity/adtivity-sdk'

// Disable tracking and clear all queued events
setConsent(false)

// Re-enable after user gives consent
setConsent(true)

Runtime Configuration

import { setApiKey, setBaseUrl } from '@adtivity/adtivity-sdk'

setApiKey('new-api-key')
setBaseUrl('https://new-backend.com')

React Native

The SDK works in React Native out of the box. IDs are loaded from AsyncStorage asynchronously, so wait for the SDK to be ready before tracking:

import { init, waitUntilReady, trackEvent } from '@adtivity/adtivity-sdk'

init({ apiKey: 'your-api-key' })

await waitUntilReady()

trackEvent('App Opened')

Page tracking and click tracking are browser-only and will no-op in React Native.


API Reference

| Export | Description | |---|---| | init(config) | Initialize the SDK | | trackEvent(name, props?) | Track a custom event | | trackTx(name, props?) | Track a blockchain transaction | | identify(userId, props?) | Identify a user | | setWallet(address, chainId?) | Set wallet address and chain for all subsequent events | | initPageTracking() | Enable automatic page view tracking | | initClickTracking() | Enable automatic click tracking | | initLocationTracking() | Enable automatic geolocation tracking | | setConsent(bool) | Enable or disable data collection | | setApiKey(key) | Update the API key at runtime | | setBaseUrl(url) | Update the backend URL at runtime | | waitUntilReady() | Wait for async ID initialization (React Native) |


Event Payload Shape

Every event sent to the server looks like:

{
  "type": "track",
  "eventName": "Token Swap",
  "timestamp": "2025-01-14T12:00:00.000Z",
  "anonymous_id": "uuid-v4",
  "session_id": "uuid-v4",
  "user_id": "user_abc",
  "wallet_address": "0xabc...def",
  "chain_id": "1",
  "transaction_hash": "0xdef...789",
  "contract_address": "0x123...456",
  "country": "US",
  "region": "California",
  "city": "San Francisco",
  "properties": {
    "amount": "100",
    "token": "USDC"
  }
}

License

MIT