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

@interfere/vite

v11.0.3

Published

Vite build plugin and client + server initialization for Interfere.

Readme

Getting Started

Prerequisites

  • Vite >=5
  • React >=19

Installation

npm install @interfere/vite

Quick Start (vanilla Vite SPA)

1. Add the Vite plugin

// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { interfere } from "@interfere/vite/plugin";

export default defineConfig({
  plugins: [react(), interfere()],
});

2. Initialize the SDK

// src/main.tsx
import { init } from "@interfere/vite/init";

import { createRoot } from "react-dom/client";
import { App } from "./app";

init();

createRoot(document.getElementById("root")!).render(<App />);

3. Add the provider

// src/app.tsx
import { InterfereProvider } from "@interfere/vite/provider";

export function App() {
  return (
    <InterfereProvider>
      <YourApp />
    </InterfereProvider>
  );
}

That's it. The provider auto-resolves the kernel created by init(); you don't have to wire useSyncExternalStore yourself.

Quick Start (TanStack Start + Nitro)

Browser events post directly to Interfere; the server's own telemetry is registered via @interfere/vite/server. Call init() (browser) and register() (server) from the file that boots your router, and put <InterfereProvider> in __root.tsx:

// src/router.tsx
import { init } from "@interfere/vite/init";
import { register } from "@interfere/vite/server";
import { createRouter } from "@tanstack/react-router";
import { routeTree } from "./routeTree.gen";

if (typeof window === "undefined") {
  register().catch(() => {});
} else {
  init();
}

export const getRouter = () =>
  createRouter({ routeTree, scrollRestoration: true });
// src/routes/__root.tsx
import { InterfereProvider } from "@interfere/vite/provider";
import { Outlet } from "@tanstack/react-router";

function RootComponent() {
  return (
    <InterfereProvider>
      <Outlet />
    </InterfereProvider>
  );
}

The browser guard matters for TanStack Start because prerender can evaluate the router import graph on the server, while the Interfere kernel uses browser-only APIs. For an ad-blocker-resistant setup, set interfere({ apiHost: "https://in.yourapp.com" }) — a subdomain of your own site that resolves to Interfere via a DNS record.

First-Time Setup

The SDK splits its credentials by concern: a public key travels with the browser bundle (it's not a secret), and an API key stays server-side (CI / GSM / .env.local).

  1. Install @interfere/vite and add the plugin (above).
  2. Public key — copy the dashboard's regional publishable key (interfere_pub_us_* or interfere_pub_eu_*) into .env.local as VITE_INTERFERE_PUBLIC_KEY=.... Public by design; safe to commit to a public repo.
  3. API key — create an Interfere API key and put the regional secret key (interfere_secret_us_* or interfere_secret_eu_*) in .env.local as INTERFERE_API_KEY=.... This key is what the build pipeline uses to publish release metadata. Server-side; never commit.
  4. First build — run bun run build. The plugin's closeBundle hook publishes release metadata and preflight-confirms the release. After this the commit's slug is accepted at ingest.
  5. Dev cyclebun run dev then exercises the bundle in your browser. Events flow with the commit's preflight-confirmed slug (the build did the work). New commit? Re-run bun run build so the new commit's slug is preflighted.

For Vercel preview / production, set INTERFERE_API_KEY in your .env.local or via GSM (or directly as a Vercel env var). Each preview build runs the same bun run build flow in CI; no local-vs-preview divergence.

Environment Variables

| Variable | Required | Description | | --- | --- | --- | | VITE_INTERFERE_PUBLIC_KEY | Yes | Surface publishable key in the interfere_pub_us_* or interfere_pub_eu_* format. Stamped into the page by the plugin at build time, and reused server-side (register(), request middleware) — the plugin mirrors it into the dev server's process, so no separate server variable is needed. | | INTERFERE_API_KEY | Yes for release metadata | Interfere secret key in the interfere_secret_us_* or interfere_secret_eu_* format. Used by the plugin at build time. Without it, release metadata is not published and the collector rejects events as COLLECTOR_RELEASE_PREFLIGHT_UNCONFIRMED. | | INTERFERE_API_URL | No | Override the collector URL the build pipeline targets. Defaults to https://in.interfere.com. | | VITE_INTERFERE_API_URL | No | Override the collector URL the browser kernel targets in direct mode. Proxy mode uses INTERFERE_API_URL server-side. | | VITE_INTERFERE_COMMIT_SHA | No | Override the auto-detected commit SHA used to derive the release slug. Defaults to git rev-parse HEAD. | | VITE_INTERFERE_FORCE_ENABLE | No | "1" / "true" flips the SDK into force-enable mode so vite dev (which sets NODE_ENV=development) emits events. Production collectors silently 202-drop force-enabled batches; safe to ship by mistake but useless. Use only in dev. |

Plugin Options

interfere({
  commitSha: "abcd0000abcd0000abcd0000abcd0000abcd0000",
});

| Option | Default | Description | | --- | --- | --- | | apiHost | — | The endpoint the browser SDK posts to. Defaults to Interfere's endpoint. Set it to a subdomain of your own site (e.g. "https://in.yourapp.com") that resolves to Interfere via a DNS record to survive ad-blockers, or to a regional endpoint. Add a CNAME pointing at the target shown in your dashboard; Interfere provisions TLS automatically. | | commitSha | VITE_INTERFERE_COMMIT_SHAgit rev-parse HEAD | Commit SHA used to derive the deterministic release slug. | | sourceMaps | auto | false disables build-time release metadata publishing. By default the pipeline runs when INTERFERE_API_KEY and a commit SHA are available. |

Ingest requests never carry cookies. apiHost posts to your own site's subdomain, surviving ad-blockers with no server-side component.

Migrating From 0.x

Upgrade all @interfere/* SDK packages together. Keep the env var names, but replace old int_pub_* / interfere_pk_* values with the dashboard's regional publishable key, and replace old ak_* / interfere_ak_* build keys with the regional Interfere secret key.

Identity Management

Link sessions to your authenticated users with identity.set():

import { useInterfere } from "@interfere/vite/provider";

function useInterfereIdentity() {
  const { identity } = useInterfere();
  const { user } = useAuthProvider();

  useEffect(() => {
    if (user) {
      identity.set({
        identifier: user.id,
        name: user.name,
        email: user.email,
        source: { type: "clerk", name: "Clerk" },
      });
    } else {
      identity.clear();
    }
  }, [user]);

  return null;
}

Parameters

| Field | Required | Description | | --- | --- | --- | | identifier | Yes | Unique user ID (your internal ID, not email) | | source | Yes | Auth source: { type: "clerk", name: "Clerk" }, { type: "auth0", name: "Auth0" }, or { type: "custom", name: "Your Provider" } | | name | No | Display name | | email | No | Email address | | avatar | No | Avatar URL | | traits | No | Arbitrary key-value metadata (Record<string, unknown>) |

API

| Method | Description | | --- | --- | | identity.set(params) | Link the current session to a user. Deduplicated per session — only the first call sends a request. | | identity.clear() | Clears the linked identity and rotates the session. Call on logout. | | identity.get() | Returns the current IdentifyParams, or null if no identity has been set. |

Identity is automatically cleared when the SDK is closed. Call identity.clear() on logout to start a fresh anonymous session.

Consent Management

By default, all SDK features are active. To gate features behind user consent, pass a consent prop to the provider:

import { InterfereProvider } from "@interfere/vite/provider";

function App() {
  return (
    <InterfereProvider consent={{ analytics: true, replay: false }}>
      <YourApp />
    </InterfereProvider>
  );
}

Consent categories

| Category | Plugins | Gated? | | --- | --- | --- | | necessary | Error tracking | Always on | | analytics | Page events, rage clicks, fingerprint | Yes | | replay | Session replay | Yes |

Omitting the consent prop disables gating entirely (all features load). Passing it enables gating — only necessary plugins plus explicitly consented categories will activate.

Imperative API

Use consent.set() and consent.get() from the useInterfere hook:

const { consent } = useInterfere();

consent.set({ analytics: true, replay: true }); // selective
consent.set();                                   // grant all
consent.get();                                   // current state, or null if no gating

Initial consent via init

To set consent before React renders (avoiding any window where non-consented plugins might load), pass it to init() directly instead of using auto:

import { init } from "@interfere/vite/init";

init({ consent: { analytics: false, replay: false } });

The provider's consent prop will then keep it in sync as the user updates their preferences.

What's Included

  • Build metadata injection — automatic Git SHA-derived release slug
  • Error tracking — automatic capture of uncaught errors and unhandled rejections
  • Session replay — full visual playback of user sessions
  • Page analytics — SPA-aware pageviews and UI interaction events
  • Rage click detection — surface frustrated user behavior
  • Consent gating — fine-grained control over which features activate

License

MIT