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

@cro-engine/sdk

v0.1.0

Published

Client SDK for the CRO Engine platform. Fetches experiment config from a project's API and evaluates bucketing locally via @cro-engine/assignment-engine — no per-request network call to the platform on the common path.

Readme

@cro-engine/sdk

The client SDK for the CRO Engine platform — install this in your app to run experiments defined in your CRO Engine project. This is the piece that makes the platform something other projects can actually integrate with, the way you'd integrate AB Tasty or GrowthBook, rather than a demo you'd have to fork.

What this is (and isn't)

This is a thin client around two things: fetching your project's experiment config from the platform, and evaluating bucketing locally using @cro-engine/assignment-engine. It is not a reimplementation of the bucketing logic — assign() is called directly from the engine package, so this SDK gets every correctness property that package already has (deterministic hashing, fail-closed targeting, etc.) for free.

Why local evaluation

The naive design for a hosted experimentation platform is "call our API on every page load to ask which variant to show." That's the wrong shape: it adds the platform's network latency to every request, and it makes your app's checkout page depend on the platform's uptime. Real experimentation platforms (GrowthBook, Statsig, LaunchDarkly) don't work that way — their SDKs fetch config periodically, cache it locally, and evaluate bucketing in your own process. Only exposure/conversion events flow back to the platform, asynchronously.

CroEngineClient.assign() follows the same pattern: it fetches (or reuses a cached copy of) your project's config, then calls assign() from @cro-engine/assignment-engine in-process. There's no network call on the common path — only when the cache is cold or has expired.

The cache itself is refreshed lazily, checked inside assign(), not via a background setInterval. A timer doesn't reliably survive between invocations in serverless/edge environments, where your app may get a fresh instance per request. Lazy-refresh-on-use degrades gracefully instead: a cold instance just pays one extra fetch on its first call, and if that fetch fails, a still-cached (if stale) config is served rather than throwing — a transient blip talking to the platform shouldn't break bucketing for a page that was working a moment ago.

Quickstart

npm install @cro-engine/sdk
import { CroEngineClient } from '@cro-engine/sdk';

const client = new CroEngineClient({
  apiKey: process.env.CRO_ENGINE_API_KEY!,
  apiUrl: process.env.CRO_ENGINE_API_URL!, // e.g. your platform deployment's URL
});

// Bucketing — evaluated locally after the first fetch.
const result = await client.assign('checkout-flow-v2', { userId: 'user-123' });
if (result.status === 'assigned') {
  console.log(`variant: ${result.variantKey}`);
}

// Tracking — queued, sent as one batched request on flush().
client.trackExposure('user-123', 'checkout-flow-v2', 'variant');
client.trackConversion('user-123', 'purchase_completed', 49);
await client.flush();

In Next.js, the natural place to call flush() is inside after() (from next/server), so it never blocks the response — see apps/demo-consumer for a full worked example, including sticky-assignment cookies and a flicker-free redirect experiment driven entirely by this SDK.

Keep the API key server-side. Instantiate CroEngineClient in a Server Component, Route Handler, or middleware — never in client-side/browser code. An API key embedded in browser JS is visible to anyone, letting them send arbitrary events under your project's identity. If you need client-triggered tracking (a button click), have the browser call your own server route, which then calls the SDK — the same pattern apps/demo-consumer's /api/convert route uses.

API

  • new CroEngineClient({ apiKey, apiUrl, cacheTtlMs? })cacheTtlMs defaults to 30s.
  • client.assign(experimentKey, ctx) => Promise<AssignmentResult> — fetches/caches config, evaluates locally.
  • client.getConfig(experimentKey) => Promise<ExperimentConfig | undefined> — the raw config, same cache as assign(). Useful when you need the config itself (e.g. to fingerprint it for a sticky-cookie cache, as the demo consumer app does), not just an assignment decision.
  • client.trackExposure(userId, experimentKey, variantKey) — queues, doesn't send.
  • client.trackConversion(userId, eventName, value?) — queues, doesn't send.
  • client.flush() => Promise<void> — sends everything queued as one request. Never throws (best-effort delivery).

Running tests

npm install
npm test

Tests mock fetch directly (this package's own I/O boundary) — no server or database needed. They cover caching (single fetch reused within TTL, concurrent calls coalesced into one request, stale-cache fallback on a failed refresh) and event batching/flush behavior.