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

analytiics

v0.9.0

Published

Analytiics CLI: set up analytics, scaffold a tracking manifest, and compile it into a typed SDK.

Readme

analytiics

Turns analytics.yaml — a tracking manifest checked into your repo — into a typed SDK. Instrumentation becomes "call the right typed function" instead of "remember the right event name."

npx analytiics start

That is the whole of setup. It signs you in, creates the project, issues a server write key and writes it to .env, scaffolds analytics.yaml, and installs the instrument agent skill — then tells you the two things it cannot do for you: add the tracker snippet, and ask your agent to instrument the code.

Every step is idempotent, so running it again is a status check rather than a second setup. A project is its site's domain; it is read from homepage in package.json, or you pass it: npx analytiics start example.com.

Sign-in is the device flow — a short code, a URL, and a browser you are already signed into. The CLI never sees a password. The write key is refused rather than written when .env is not covered by .gitignore, because a key committed to a repo is a key you have to rotate.

The individual commands

start is those steps in order; each is also its own command.

npx analytiics login             # authorize this machine
npx analytiics link example.com  # create/attach the project, write a key
npx analytiics init example.com  # scaffold analytics.yaml
npx analytiics codegen           # write analytics.gen.ts, and push the manifest
npx analytiics push              # send analytics.yaml to your dashboard
npx analytiics validate          # check the manifest
npx analytiics check             # compare the manifest against the code
npx analytiics whoami            # who you are signed in as

The drift check

A file is only the source of truth if something notices when the code stops agreeing with it.

npx analytiics check             # warn, and exit 0
npx analytiics check --strict    # exit 1 on anything unambiguously wrong
npx analytiics check src apps    # only search these paths

check reads the manifest, finds every call site in the repo, and reports four kinds of drift: an event sent but never declared, an event declared but never sent, a call whose props diverge from the declaration, and an analytics.gen.ts built from a manifest that has since moved on.

Anything ambiguous is reported as nothing at all. Props that are a variable or a spread cannot be read at rest, and a false alarm would cost the credibility of every warning after it — so the check stays quiet rather than guessing. It is a text scan and not a compiler, so it runs in a repo whose build is broken, and it needs neither sign-in nor network: the manifest and the code are both in the checkout.

It warns by default and that default is the point — a check that fails a build the first time it runs gets deleted. Put it on pull requests, let it describe the drift for a few weeks, then turn on --strict once the list is empty.

# .github/workflows/analytics.yml
name: analytics
on: pull_request
jobs:
  drift:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npx analytiics check

--strict never fails on an unused event: wiring a call site is a decision a person has to make, and an unfinished tracking plan is not a reason to block a release.

A call in a test is a call site like any other, which is usually what you want. Where it isn't, name the paths to search: analytiics check src.

Pushing the manifest

Your dashboard draws a card per declared event, breaks down enum props, and groups events by tag. It can only do that from a manifest it has been given — analytics.yaml lives in your repository, and the dashboard renders on ours.

codegen pushes it for you, because regenerating the typed SDK is the moment the manifest changed. --no-push skips that, and push does it on its own for anyone who never runs codegen — a Python or Ruby backend has a tracking plan and no TypeScript to generate.

The file stays the source of truth. Nothing edits it from the dashboard, and what the server keeps is a copy: to change what your charts show, change the file and push again.

A push that cannot happen never fails codegen. Offline or signed out, it says which manifest your dashboard is still showing and carries on — a generated file is correct whether or not the network was reachable while it was written.

Agent skills

npx analytiics skills list
npx analytiics skills install instrument
npx analytiics skills install explore

Installs into .claude/skills/. The instrument skill teaches an agent to audit a codebase, propose a tracking plan, and wire the typed calls — including the parts that are easy to get wrong, like revenue events being server-side only. Skills ship inside this package, so one can never disagree with the CLI that generated the code it describes.

The explore skill reads the owner-only analytics API using your CLI session — traffic, breakdowns, custom events, funnels, People and journeys — and prefers the MCP tools below when the client has them. It can trace an identified customer's retained history or an anonymous visitor's current UTC day. Search uses your application's exact opaque user ID.

MCP server

npx analytiics mcp install   # registers the server in this repo's .mcp.json
npx analytiics mcp           # what the client runs: tools over stdio

The same owner API, as tools an agent can call: list_projects, get_metrics, get_breakdown, list_events, get_funnel, list_people, get_journey and get_manifest. Every one of them reads; none can change a project. They answer with the numbers the dashboard shows, under the same query budget.

The server runs on your machine, started by the agent's client, and reads the session analytiics login wrote to your home directory. Nothing is copied into a client config, so the .mcp.json it writes is safe to commit: it names a command, and each teammate's own login supplies their credentials. Claude Code reads .mcp.json on its own; any other client that speaks stdio can run npx analytiics mcp. A self-hosted ANALYTIICS_HOST travels with the entry.

Ships inside this package, like the skills, so the tools can never disagree with the CLI that installed them. The explore skill says how to read what they return.

The manifest

version: 1
project: thiings

events:
  purchase:
    description: Customer bought an icon pack
    tags: [conversion]
    revenue: true
    props:
      type:
        type: string
        enum: [personal_indie, studio_agency, credits]

Unknown keys are rejected rather than ignored — a silently dropped field means a silently missing chart. Validation also catches what a schema can't express: funnel steps referencing events you never declared, enum on a non-string prop, and event names that would collide once camelCased.

The generated SDK

import { createTrack } from "./analytics.gen.js";
import { createClient, toAnalytiicsClient } from "@analytiics/sdk-node";

// Inside a server request handler:
const client = createClient({ site, writeKey });
const track = createTrack(toAnalytiicsClient(client, { userId }));

track.purchase(
  { type: "personal_indie" },
  { userId, revenueCents: 4900, currency: "USD" },
);

revenue: true makes the amount part of the signature, so recording a conversion while losing the money is a compile error rather than a silent zero. enum props become union types. Descriptions become doc comments.

In the browser the generated calls find the tracker on window.analytiics with no setup. On a server, create typed helpers for each request:

import { createClient, toAnalytiicsClient } from "@analytiics/sdk-node";
import { createTrack } from "./analytics.gen.js";
const track = createTrack(toAnalytiicsClient(createClient({ site, writeKey }), { userId }));

Generated calls are fail-open: a missing client is a no-op, never a thrown error in someone's checkout flow. Keep request identities in createTrack helpers; the legacy configure function changes a shared browser convenience client.

Options

--manifest <path>   default: analytics.yaml
--out <path>        default: analytics.gen.ts
--env <path>        where `link` writes the key (default: .env)
--strict            `check`: exit non-zero on drift, instead of warning

ANALYTIICS_HOST points the CLI at a different server (default https://www.analytiics.co). Credentials live in ~/.analytiics/config.json, mode 0600, keyed by host — so being signed in to the hosted service and a self-hosted instance at once is the normal case rather than a conflict.

codegen is idempotent — an unchanged manifest prints "already up to date" and leaves the file's mtime alone, so it's safe to run on every build. Its push is idempotent too: the same manifest sent twice replaces itself with itself.