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

swetrix

v4.4.0

Published

The JavaScript analytics client for Swetrix Analytics

Downloads

18,848

Readme

NPM Package size JSDelivr hits Contributions welcome

Swetrix Tracking Script

Privacy-first, lightweight analytics tracking library for Swetrix. Tracks page views, custom events, errors, feature flags, and A/B experiments — all without cookies or invading user privacy.

Installation

npm / yarn / pnpm

npm install swetrix

CDN

<script src="https://swetrix.org/swetrix.js" defer></script>

Quick Start

ES Modules

import { init, trackViews, trackErrors } from 'swetrix'

init('YOUR_PROJECT_ID')
trackViews()
trackErrors()

CDN / Script Tag

<script src="https://swetrix.org/swetrix.js" defer></script>
<script>
  document.addEventListener('DOMContentLoaded', () => {
    swetrix.init('YOUR_PROJECT_ID')
    swetrix.trackViews()
    swetrix.trackErrors()
  })
</script>

API

init(projectId, options?)

Initialise the library. Must be called before any other method.

init('YOUR_PROJECT_ID', {
  apiURL: 'https://api.swetrix.com/log',
  devMode: false,
  disabled: false,
  respectDNT: false,
  profileId: 'user-123',
  preloadSessionReplay: false,
})

| Option | Description | Default | |---|---|---| | apiURL | API endpoint. Change this if you're self-hosting. | 'https://api.swetrix.com/log' | | devMode | When true, localhost events are sent to the server. | false | | disabled | When true, no data is sent. Useful for dev environments. | false | | respectDNT | When true, disables tracking for users with Do Not Track enabled. | false | | profileId | Profile ID for long-term user tracking (MAU/DAU). | undefined | | preloadSessionReplay | Preload the session replay recorder after init(). Recording only starts after startSessionReplay(). | undefined |

trackViews(options?)

Automatically tracks page views, including navigation changes in SPAs. Returns a Promise<{ stop() }>.

const { stop } = await trackViews({
  hash: false,
  search: false,
  unique: false,
  heartbeatOnBackground: false,
  callback: undefined,
})

// Stop tracking when needed
stop()

| Option | Description | Default | |---|---|---| | hash | Track hash-based routing (e.g. /#/path). | false | | search | Track search/query-based routing (e.g. /path?query). | false | | unique | Only count unique page views per session. | false | | heartbeatOnBackground | Send heartbeat when the tab is not active. | false | | callback | A function to edit or prevent pageview payloads. Return false to block, true to send as-is, or return a modified payload object. | undefined |

track(event)

Track custom events (e.g. button clicks, sign-ups).

track({
  ev: 'signup',
  unique: true,
  meta: { plan: 'pro', source: 'landing' },
  profileId: 'user-123',
})

| Option | Description | Default | |---|---|---| | ev | Event name (max 256 chars). | required | | unique | Only count once per session. | false | | meta | Key-value metadata (max 20 keys, 1000 chars total). | {} | | profileId | Optional profile ID. Overrides the global profileId for this event. | undefined |

trackErrors(options?)

Automatically captures client-side errors. Returns { stop() }.

const { stop } = trackErrors({
  sampleRate: 1,
  callback: undefined,
})

| Option | Description | Default | |---|---|---| | sampleRate | Fraction of errors to send (0 to 1). | 1 | | callback | Edit or prevent error payloads. Return false to block. | undefined |

trackError(payload)

Manually report an error.

trackError({
  name: 'PaymentError',
  message: 'Card declined',
  meta: { gateway: 'stripe' },
})

pageview(options)

Manually track a single page view (useful for custom routing).

pageview({
  payload: { pg: '/checkout', lc: 'en-US' },
  unique: true,
})

Feature Flags

// Get all flags. Results are cached for 5 minutes.
const flags = await getFeatureFlags({ profileId: 'user-123' })

// Force a fresh fetch
const freshFlags = await getFeatureFlags({ profileId: 'user-123' }, true)

// Get a single flag. The third argument is an optional fallback value.
const enabled = await getFeatureFlag('dark-mode', { profileId: 'user-123' })
const enabledWithFallback = await getFeatureFlag('dark-mode', { profileId: 'user-123' }, false)

// Clear the shared feature flag / experiment cache
clearFeatureFlagsCache()

A/B Experiments

// Get all running experiment assignments. Results are cached for 5 minutes.
const experiments = await getExperiments({ profileId: 'user-123' })

// Force a fresh fetch
const freshExperiments = await getExperiments({ profileId: 'user-123' }, true)

// Get a specific experiment variant. The third argument is an optional fallback variant.
const variant = await getExperiment('checkout-redesign-experiment-id', { profileId: 'user-123' })
const variantWithFallback = await getExperiment('checkout-redesign-experiment-id', { profileId: 'user-123' }, 'control')

// Clear the shared feature flag / experiment cache
clearExperimentsCache()

startSessionReplay(options?)

Start recording a session replay. Session replays use total privacy by default, which masks text and inputs and blocks media/canvas capture unless you explicitly choose another mode.

If you use the npm package, rrweb is dynamically imported from your installed dependencies only when the recorder is preloaded or started. If you use the CDN/script-tag build, the standalone replay recorder is loaded with an async script tag.

const replay = await startSessionReplay({
  privacy: 'total',
  maskAllText: true,
  sampleRate: 0.25,
  maxDurationMs: 10 * 60 * 1000,
  idleTimeoutMs: 2 * 60 * 1000,
  maxBytesPerChunk: 512 * 1024,
})

// Stop or flush manually when needed
await replay.flush()
await replay.stop()

| Option | Description | Default | |---|---|---| | privacy | Privacy mode: total, normal, or none. | 'total' | | maskAllText | Mask all non-input text with asterisks. Defaults to true when privacy is total, otherwise false. | privacy-based | | sampleRate | Fraction of sessions to record (0 to 1). | 1 | | maxDurationMs | Stop recording after this duration. | undefined | | idleTimeoutMs | Stop recording after this much visitor inactivity. | undefined | | flushIntervalMs | Upload buffered replay events at this interval. | 5000 | | maxEventsPerChunk | Upload once this many events are buffered. | 100 | | maxBytesPerChunk | Upload once buffered replay events reach this approximate byte size. | 524288 | | maxBytesPerEvent | Drop a single replay event if it is larger than this many bytes. | 5242880 | | recordIframes | Allow iframe elements to be captured. Iframes are blocked by default to reduce replay size and avoid recording embedded third-party content. | false | | rrweb | Additional rrweb record options. | undefined |

To mask text while keeping media less restricted than total privacy, combine normal privacy with maskAllText:

await startSessionReplay({
  privacy: 'normal',
  maskAllText: true,
})

By default, Swetrix blocks iframe elements from replay snapshots. If you own the iframe content and need it in the replay, opt in explicitly:

await startSessionReplay({
  privacy: 'normal',
  recordIframes: true,
})

Cross-origin iframe recording also requires rrweb's cross-origin iframe support and should only be enabled for domains you control:

await startSessionReplay({
  recordIframes: true,
  rrweb: {
    recordCrossOriginIframes: true,
  },
})

Session & Profile IDs

const profileId = await getProfileId()
const sessionId = await getSessionId()

These are useful for revenue attribution with payment providers like Paddle.

Self-Hosting

If you're running a self-hosted Swetrix API instance, point the apiURL to your server:

init('YOUR_PROJECT_ID', {
  apiURL: 'https://your-api.example.com/log',
})

Documentation

Full reference and guides are available at docs.swetrix.com.

Contributing

Contributions are welcome — feel free to open an issue or submit a pull request.

License

MIT