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

@anveshan/sdk

v0.4.0

Published

A/B testing and remote config for React Native and web — bucketing, variant resolution and conversion tracking.

Readme

@anveshan/sdk

A/B testing and remote config for React Native and web. Deterministic bucketing, variant resolution and conversion tracking, with the same resolver running on the device, in the browser and on the server — so a variant computed anywhere matches everywhere.

npm install @anveshan/sdk

Entry points

| Import | What it is | Needs | | --- | --- | --- | | @anveshan/sdk | Client, bucketing and the pure resolver. No UI framework. | — | | @anveshan/sdk/react | AnveshanProvider, useControl, and the instrumentation helpers. | react | | @anveshan/sdk/native | Screen capture and live mirroring for the visual picker. Android, debug builds only. | react-native | | @anveshan/sdk/babel-plugin | Auto-instruments static JSX so copy and styles are remotely overridable. | @babel/core |

Every peer is optional. A Node service resolving variants for server-rendered pages installs none of them.

Quick start

import { AnveshanProvider, useControl } from "@anveshan/sdk/react";

<AnveshanProvider options={{ accountId: "anv_live", endpoint: "https://…" }}>
  <App />
</AnveshanProvider>;

function Hero() {
  // The second argument is what the shipped binary renders — with no config,
  // no network, or no experiment, the screen still works.
  const headline = useControl("hero.headline", "Cold-pressed, always");
  return <Text>{headline}</Text>;
}

Reading a control is what reports the exposure, so an experiment only counts users who actually reached the screen.

Auto-instrumentation

babel.config.js:

module.exports = {
  presets: ["module:@react-native/babel-preset"],
  overrides: [
    {
      // Transforming node_modules is worse than useless here: RN's LogBox
      // renders outside your provider tree, so instrumenting it replaces the
      // stack trace you needed with a blank screen.
      exclude: /node_modules/,
      plugins: ["@anveshan/sdk/babel-plugin"],
    },
  ],
};

Then generate the element catalogue the dashboard's visual picker reads:

npx anveshan-manifest src anveshan-manifest.json

Ids are keyed on file basename + line, so two same-named files in different folders can collide — rename or scope if that bites.

Pass it to the provider with require, not a static import:

let manifest: unknown[] | undefined;
try {
  manifest = require('./anveshan-manifest.json');
} catch {}

<AnveshanProvider accountId="…" manifest={manifest}>

A static import makes the generated file a hard build dependency — a checkout that has never run the generator dies at Unable to resolve module. React Native's Metro config enables allowOptionalDependencies, so a require inside a try/catch builds either way. Leave it undefined rather than [] when it is missing: any non-nullish manifest is treated as one worth uploading, and an empty array overwrites the dashboard's catalogue with nothing.

The manifest is a dashboard convenience, not a runtime dependency. Without it your controls still resolve and your experiments still run — only the visual picker's element list goes unpopulated.

Live mirroring

Nothing to wire. Install, rebuild, run a dev build — the provider mirrors the screen to the dashboard on its own. There is no button and no touch handler to add, because anything the host has to render is something the host has to remember to render.

<AnveshanProvider accountId="anv_live" endpoint={ENDPOINT} screen={route.name}>

screen is optional and only labels the capture. mirrorCode defaults to "demo" — the pairing code a PM types into the dashboard — and setting it to null turns mirroring off.

Android only. The native module ships in this package and autolinks itself; there is no iOS module yet, so isCaptureAvailable() is false there and mirroring is a no-op. It is also dev-only, and enforced natively rather than by convention: the module refuses to capture unless the host app is debuggable, so a release build cannot take a screenshot even though autolinking registers it everywhere.

What drives it

A native ViewTreeObserver.OnDrawListener, debounced, rather than a JS timer or a touch handler.

A touch handler has to wrap your whole tree and still only sees taps — it misses a scroll settling, an image decoding, a fetch landing, a transition ending. A redraw covers all of them, since those are exactly the things that redraw the screen. The debounce is trailing, so an animation produces one capture when it finishes rather than sixty while it runs.

Frames identical to the last are dropped before upload. An idle screen uploads nothing at all — which matters because the obvious implementation, a 2s setInterval, re-uploads the same JPEG thirty times a minute, and each one is a write on the dashboard's side. That was enough to exhaust a Firestore free tier in about five hours, with the likeliest case being the worst: a picker left open on a screen nobody was looking at.

Driving it yourself

createMirrorSession() gives the same behaviour under manual control, and captureCurrentScreen() / uploadCapture() are exported for anything more bespoke.

const session = createMirrorSession({baseUrl, code: 'demo', screen: route.name});
session.captureNow();
session.activity();   // safe to call on every touch
session.stop();

Development

Source lives in src/, published output is built by tsc:

npm run build -w @anveshan/sdk    # or: npm run sdk:build from the repo root
npm test                          # from the repo root; needs Node >= 22

Imports carry explicit .ts extensions so Node's type stripping and Metro both resolve them directly; rewriteRelativeImportExtensions turns them into .js on the way into dist/.