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

@latimer-woods-tech/attribution

v0.1.0

Published

The attribution spine: Worker-safe, zero-dependency UTM + referrer touch capture, first/last-touch journey helpers, a documented last-non-direct conversion model, compact cookie/KV serialization, a storage-agnostic touch store port, and a bot/human (synth

Readme

@latimer-woods-tech/attribution

The attribution spine. Capture where a visitor came from, keep an ordered touch journey, credit the channel that produced a buyer, and separate real humans from bots and our own probes — so a funnel means something.

Zero runtime dependencies, no Node built-ins — safe inside a Cloudflare Worker.

The problem

The portfolio could not answer the single most important marketing question — "which channel produced this buyer?" There was zero UTM capture anywhere, and the funnels were inflated by ~29 autonomous loops and synthetic monitors hitting the same public endpoints as humans. You cannot spend to acquire when you cannot measure acquisition, and you cannot trust a conversion rate that counts your own robots.

Capture a touch

import { parseAttribution } from '@latimer-woods-tech/attribution';

const touch = parseAttribution(
  'https://selfprime.net/quiz?utm_source=beehiiv&utm_medium=email&utm_campaign=lunation-07',
  request.headers.get('referer'),
  { selfHosts: ['selfprime.net'] },
);
// → { source: 'beehiiv', medium: 'email', campaign: 'lunation-07', term: null,
//     content: null, referrerHost: '…', landingPath: '/quiz', ts: 1_700_000_000_000 }

Rules, in priority order:

  1. UTM parameters win. Any of utm_source / utm_medium / utm_campaign marks an explicitly tagged campaign; the tags are the source of truth.
  2. Otherwise the referrer is classifiedorganic (a search engine), social (a known network), or referral (any other external host).
  3. No usable referrer → direct. A referrer matching selfHosts is internal navigation, also direct — never a bogus self-referral.

Deterministic: inject now to pin the timestamp.

Journey: first touch, last touch, conversion

import { mergeTouch, attributeConversion } from '@latimer-woods-tech/attribution';

let touches = [];
touches = mergeTouch(touches, firstVisit);  // ordered oldest-first, capped
touches = mergeTouch(touches, returnVisit);

attributeConversion(touches);                 // last-non-direct-click (default)
attributeConversion(touches, 'first-touch');  // or first / last touch

mergeTouch maintains an ordered, capped list. When the cap is exceeded the very first touch is always preserved (first-touch attribution must survive trimming), alongside the most-recent window; consecutive same-channel hits collapse into a recency refresh rather than a duplicate hop.

attributeConversion credits one source. The default last-non-direct-click model walks back past a direct return visit to the last channel that actually referred the visitor — a bookmark reopened right before purchase should not steal credit from the newsletter that drove them.

Store the journey

import { InMemoryAttributionStore, recordTouch } from '@latimer-woods-tech/attribution';

const store = new InMemoryAttributionStore();          // reference impl for tests
await recordTouch(store, anonId, parseAttribution(url, ref));

AttributionStore is a narrow get/put port (mirrors @latimer-woods-tech/operator): a KV-, cookie-, or Neon-backed adapter is a drop-in replacement. Or persist the list yourself in a cookie:

import { serializeTouches, deserializeTouches } from '@latimer-woods-tech/attribution';

const cookie = serializeTouches(touches);          // compact base64url, no Buffer
const touches = deserializeTouches(cookie ?? '');   // total — bad input yields []

Bot / human separation

import { classifyTraffic } from '@latimer-woods-tech/attribution';

const { isSynthetic, reason } = classifyTraffic({
  userAgent: request.headers.get('user-agent'),
  // ONLY true after you HMAC-verify the X-Synthetic-Probe header upstream:
  syntheticProbeHeaderPresent: verifiedProbe,
});

Combines user-agent heuristics (known bots, headless markers, HTTP libraries, uptime monitors) with the X-Synthetic-Probe convention (the-calling#45 / HD#1005). This primitive consumes an already-verified probe flag — it never inspects a raw header or runs the HMAC (that stays with the caller that holds the secret). A forged or absent signal counts as real, so a bot cannot label itself synthetic to slip out of the funnel.

De-pollution can make your numbers look worse, and that is correct. When you stop counting your own probes and crawlers, completion rates fall to their true human value. Never act on pre-launch funnel metrics; the decision baseline is the first real human cohort.

What this does not do

This package is pure measurement primitives. It does not mint anon ids, set cookies, run the synthetic-probe HMAC, talk to a database, or decide spend. Those are the caller's job — this is the spine they hang on.

License

MIT © Latimer Woods Tech