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

addilytics

v0.0.1

Published

Backend-first analytics with optional client navigation tracking.

Readme

addilytics

Server-first analytics with no cookies. The optional browser entry point fills SPA navigation gaps without exposing the site key. It is bundled by your application; Addilytics does not ship a hosted or CDN-loaded script.

import { createAddilytics } from 'addilytics';

const analytics = createAddilytics({
	endpoint: 'https://addilytics.example',
	siteKey: process.env.ADDILYTICS_KEY!,
	deliveryTimeoutMs: 5_000,
	onError(error, context) {
		console.error('analytics delivery failed', context.operation, error);
	}
});

const startedAt = Date.now();
const response = await render(request);
ctx.waitUntil(analytics.capture({ request, response, startedAt }));

Framework adapters: @addilytics/sveltekit, @addilytics/wintercg, @addilytics/astro, @addilytics/hono, @addilytics/react-router, @addilytics/nextjs, @addilytics/nuxt, @addilytics/express, and @addilytics/fastify.

Collection never throws into the application request. Set onError if you want failed identity lookups, filters, or ingest requests sent to your own logger. Delivery times out after five seconds by default. Set trustProxy: true only when your server or hosting provider overwrites forwarded IP and country headers from the public request.

Identity hashing requires the Web Crypto API. Use Node 20 or newer in Node deployments. If hashing is unavailable, Addilytics reports an identify error and still records the event anonymously.

The client sends only utm_source, utm_medium, utm_campaign, utm_term, and utm_content from the page URL. It removes the path, query, fragment, and credentials from the referrer before delivery.

Server-only counting sees requests that reach application code. Client-side route changes, prerendered files, CDN hits, full-response caches, and browser back-forward cache restores bypass that mode. The browser entry below covers them through a same-origin relay.

Optional browser navigation tracking

Framework adapters can pair their server integration with the side-effect-free addilytics/browser entry point. Importing it does not register listeners or send requests.

import { createBrowserTracker } from 'addilytics/browser';

const tracker = createBrowserTracker({
	mode: 'hybrid',
	endpoint: '/__addilytics'
});

await tracker.pageview(location.href);

hybrid mode skips the first call because the server adapter owns the initial document request. client mode sends the first call as well. Calls for the same URL, including hash-only changes, are deduplicated by default. Pass { force: true, navigationKey: uniqueNavigationId } for a persisted pageshow event. Reusing a navigation key cannot create another view, even with force.

addEventListener('pageshow', (event) => {
	if (event.persisted) {
		void tracker.pageview(location.href, {
			force: true,
			navigationKey: `pageshow:${event.timeStamp}`
		});
	}
});

The browser sends only an event ID, timestamp, path, UTM query fields, and referrer origin. It never receives or sends the site key, IP address, or user agent. Failed requests retry once by default with the same event ID and timestamp. Use onError to observe failures without throwing into navigation.

A committed client navigation has no reliable HTTP response status. Relayed pageviews store status 0 to mean unknown instead of pretending the route returned 200. trackStatuses applies only to server responses and does not filter browser-relayed pageviews.

Server adapters can share the relay built into the core client:

const relay = analytics.createRelay({ mode: 'hybrid', endpoint: '/__addilytics' });

if (relay.matches(request)) {
	return relay.handle({ request, ip: trustedAddress, country: trustedCountry });
}

The relay requires a same-origin JSON POST, limits the body to 4096 bytes while streaming it, and validates the event ID, timestamp, and canonical path. It ignores client-supplied IP and user-agent fields, resolves identity from the real relay request, and forwards a normal authenticated pageview to the ingest API. Adapter server options should omit browser support for server-only counting, use hybrid for server-owned initial views plus client navigation, or use client and skip automatic server document capture.