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

@vexilapp/sdk

v0.1.2

Published

Vexil JavaScript/TypeScript SDK

Readme

@vexilapp/sdk

Official JavaScript/TypeScript SDK for Vexil. Works in browsers, Node.js, and edge runtimes.

Installation

npm install @vexilapp/sdk
# or
bun add @vexilapp/sdk

Quick start

import { VexilClient } from "@vexilapp/sdk";

const client = new VexilClient({
  apiKey: "your-api-key",
  environment: "production",
  context: { userId: "user_123" },
});

await client.initialize();

if (client.isEnabled("new-checkout-flow")) {
  // show new checkout
}

const variant = client.getVariant("ui-variant", "control");

Configuration

new VexilClient({
  apiKey: string           // required — your Vexil API key
  environment: string      // required — e.g. "production", "staging", "development"
  baseUrl?: string         // default: "https://api.vexil.online"
  pollingInterval?: number // ms between flag refreshes. default: 30_000. set 0 to disable.
  context?: FlagContext    // initial user context for targeting
  localCache?: boolean     // persist to localStorage for offline fallback. default: true
  onChange?: (            // called when a flag value changes after a poll
    key: string,
    next: FlagEvaluation,
    prev: FlagEvaluation | undefined
  ) => void
})

API

initialize(): Promise<void>

Fetches all flag evaluations for the configured environment and user context, then starts polling. Call this once before using any flag methods.

await client.initialize();

isEnabled(key: string, defaultValue?: boolean): boolean

Returns true if the flag is enabled for the current user. Falls back to defaultValue if the flag is not found.

client.isEnabled("dark-mode"); // false
client.isEnabled("dark-mode", true); // true (default if not found)

getVariant<T>(key: string, defaultValue: T): T

Returns the flag's evaluated value. Use for string, number, or JSON flags.

const theme = client.getVariant("ui-theme", "default"); // string
const limit = client.getVariant("rate-limit", 100); // number
const config = client.getVariant("checkout-config", {}); // object

identify(context: FlagContext): Promise<void>

Updates the user context and re-fetches all flag evaluations. Call this after a user logs in or their attributes change.

await client.identify({
  userId: "user_456",
  plan: "pro",
  country: "US",
});

getAllFlags(): Record<string, FlagEvaluation>

Returns a snapshot of all currently evaluated flags.

const flags = client.getAllFlags();
// { "new-checkout-flow": { enabled: true, value: true }, ... }

destroy(): void

Stops polling and cleans up. Call this when the client is no longer needed (e.g. on component unmount or process exit).

client.destroy();

A/B Experiments

When a running experiment is attached to a flag in the current environment, the SDK automatically:

  1. Assigns the user to a variant — deterministically, based on userId and flag key, so the same user always sees the same variant across sessions and devices.
  2. Tracks an impression — fires a background request to record that the user was exposed to the experiment variant. No extra code needed.

You only need to call track() when the goal event occurs.

getExperiment(flagKey: string): ExperimentAssignment | undefined

Returns the experiment assignment for a flag, if one is active for the current user. Use this to read the assigned variant without re-evaluating the flag.

const assignment = client.getExperiment("checkout-redesign");
// { experimentId: 'exp_abc123', variantValue: 'variant-a', variantLabel: 'Variant A' }

if (assignment) {
  console.log("User is in variant:", assignment.variantLabel);
}

track(eventName: string, properties?: Record<string, unknown>, flagKey?: string): void

Records a goal conversion event. Call this when the user performs the action your experiment is measuring.

  • If flagKey is provided, only records a conversion for that flag's active experiment.
  • If flagKey is omitted, fans out to all flags that currently have an active experiment assignment.
// Track 'signup' goal for all active experiments
client.track("signup");

// Track with metadata
client.track("purchase_completed", { revenue: 49.99, currency: "USD" });

// Track for a specific flag's experiment only
client.track("purchase_completed", { revenue: 49.99 }, "checkout-redesign");

Full A/B example

const client = new VexilClient({
  apiKey: "ff_live_...",
  environment: "production",
  context: { userId: "user_123" },
});

await client.initialize();

// 1. Check if the flag (and its experiment) is enabled for this user.
//    Impression is automatically tracked.
if (client.isEnabled("checkout-redesign")) {
  const assignment = client.getExperiment("checkout-redesign");

  if (assignment?.variantValue === "one-page") {
    renderOnePageCheckout();
  } else {
    renderMultiStepCheckout();
  }
}

// 2. When the user converts, track the goal event.
onPurchaseComplete((order) => {
  client.track("purchase_completed", { revenue: order.total });
});

Flag dependencies

Flag dependencies are enforced server-side — when you call isEnabled for a dependent flag, the API already factors in whether its parent flags are enabled. No client-side changes are needed; the SDK always reflects the effective state.

// 'advanced-analytics' requires 'analytics-base' to be enabled.
// If 'analytics-base' is off, this returns false automatically.
client.isEnabled("advanced-analytics");

Offline resilience

The SDK caches flag evaluations in localStorage (browser) after every successful fetch. If the API is unreachable, isEnabled and getVariant return the last cached values instead of the defaults.

To disable caching:

new VexilClient({ ..., localCache: false })

Reacting to flag changes

Use onChange to react when a flag's value changes after a background poll:

const client = new VexilClient({
  apiKey: "...",
  environment: "production",
  onChange: (key, next, prev) => {
    console.log(`[${key}] ${prev?.enabled} → ${next.enabled}`);
  },
});

Types

interface FlagContext {
  userId?: string;
  [key: string]: string | number | boolean | undefined;
}

interface ExperimentAssignment {
  experimentId: string; // ID of the running experiment
  variantValue: unknown; // the value assigned to this user (matches a variant in the experiment)
  variantLabel: string; // human-readable label, e.g. "Control", "Variant A"
}

interface FlagEvaluation {
  enabled: boolean;
  value: FlagValue; // boolean | string | number | Record<string, unknown>
  rolloutPercentage?: number;
  experiment?: ExperimentAssignment; // present when a running experiment is attached
}

Building from source

npm run build   # outputs CJS + ESM + types to dist/
npm run dev     # watch mode