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

@flagpool/sdk

v0.4.4

Published

Official Flagpool SDK for TypeScript/JavaScript - feature flags with local evaluation, deterministic rollouts, and encrypted target lists

Readme

Flagpool SDK for TypeScript / JavaScript

Official TypeScript/JavaScript SDK for Flagpool - the modern feature flag platform for teams who want control without complexity.

npm version npm downloads

Installation

npm install @flagpool/sdk

Quick Start

import { FlagpoolClient } from '@flagpool/sdk';

const client = new FlagpoolClient({
  projectId: 'your-project-uuid',     // Get from Flagpool dashboard
  apiKey: 'fp_production_xxx',        // Environment-specific API key
  decryptionKey: 'fp_dec_xxx',        // For target list decryption & CDN URL
  context: {
    userId: 'user-123',
    email: '[email protected]',
    plan: 'pro',
    country: 'US'
  }
});

await client.init();

// Boolean flag
if (client.isEnabled('new-dashboard')) {
  showNewDashboard();
}

// String flag (A/B test)
const buttonColor = client.getValue('cta-button-color');
// 'blue' | 'green' | 'orange'

// Number flag
const maxUpload = client.getValue('max-upload-size-mb');
// 10 | 100 | 1000

// JSON flag
const config = client.getValue('checkout-config');
// { showCoupons: true, maxItems: 50, paymentMethods: [...] }

// Clean up when done
client.close();

Features

  • Local evaluation - No server roundtrip per flag check
  • Deterministic rollouts - Same user always gets same variation
  • Multiple flag types - Boolean, string, number, JSON
  • Advanced targeting - 8 operators including target lists
  • Real-time updates - Automatic polling for flag changes
  • Offline support - Works with cached flags when offline
  • Zero dependencies - Lightweight, no external dependencies
  • Analytics - Track flag evaluations (paid plans only)

Configuration

const client = new FlagpoolClient({
  // Required
  projectId: 'your-project-uuid',     // Project ID from dashboard
  apiKey: 'fp_production_xxx',        // Environment-specific API key
  decryptionKey: 'fp_dec_xxx',        // For CDN URL hash & target list decryption

  // Optional
  context: {                          // User context for targeting
    userId: 'user-123',
    email: '[email protected]',
    plan: 'pro',
    // Add any attributes for targeting
  },
  pollingInterval: 30000,             // Auto-refresh interval (ms), default: 30000
  urlOverride: undefined,             // Complete URL override (for self-hosted/testing)
  analytics: {                        // Analytics (opt-in, paid plans only)
    enabled: true,                    // Default: false
  },
});

API Reference

Methods

| Method | Description | |--------|-------------| | init() | Initialize client and fetch flags (required before evaluation) | | isEnabled(key) | Check if a boolean flag is enabled | | getValue(key) | Get flag value (any type) | | getVariation(key) | Alias for getValue | | updateContext(ctx) | Update user context and re-evaluate flags | | onChange(callback) | Subscribe to flag value changes | | getAnalyticsState() | Get current analytics state (for debugging) | | getAllFlagsWithState() | Get all flags with evaluation status | | flushAnalytics() | Manually flush analytics buffer | | close() | Clean up resources (stop polling, flush analytics) |

Flag Types

Boolean Flags

if (client.isEnabled('feature-flag')) {
  // Feature is enabled for this user
}

String Flags

Perfect for A/B tests and feature variants:

const variant = client.getValue('button-color');
// Returns: 'blue' | 'green' | 'orange'

Number Flags

Great for limits, thresholds, and configurations:

const limit = client.getValue('rate-limit');
// Returns: 100 | 1000 | 10000

JSON Flags

For complex configurations:

const config = client.getValue('checkout-config');
// Returns: { showCoupons: true, maxItems: 50, ... }

Targeting Rules

Flagpool supports powerful targeting with 8 operators:

| Operator | Description | Example | |----------|-------------|---------| | eq | Equals | plan == "enterprise" | | neq | Not equals | plan != "free" | | in | In list | country in ["US", "CA"] | | nin | Not in list | country not in ["CN", "RU"] | | contains | String contains | email contains "@company.com" | | startsWith | String starts with | userId startsWith "admin-" | | inTargetList | In target list | userId in beta-testers | | notInTargetList | Not in target list | userId not in blocked-users |

Dynamic Context Updates

Update user context on the fly - flags re-evaluate automatically:

const client = new FlagpoolClient({
  projectId: 'your-project-uuid',
  apiKey: 'fp_prod_xxx',
  decryptionKey: 'fp_dec_xxx',
  context: { userId: 'user-1', plan: 'free' }
});

await client.init();

// User on free plan
console.log(client.getValue('max-upload-size-mb')); // 10

// User upgrades to pro
client.updateContext({ plan: 'pro' });

// Instantly gets pro limits
console.log(client.getValue('max-upload-size-mb')); // 100

Real-time Updates

Flags automatically refresh in the background:

const client = new FlagpoolClient({
  projectId: 'your-project-uuid',
  apiKey: 'fp_prod_xxx',
  decryptionKey: 'fp_dec_xxx',
  context: { userId: 'user-1' },
  pollingInterval: 30000  // Refresh every 30 seconds
});

await client.init();

// Listen for flag changes
client.onChange((flagKey, newValue) => {
  console.log(`Flag ${flagKey} changed to:`, newValue);
  
  // React to changes (e.g., update UI)
  if (flagKey === 'maintenance-mode' && newValue === true) {
    showMaintenanceBanner();
  }
});

Offline Support

The SDK caches flags locally. If the network is unavailable, it uses cached values:

const client = new FlagpoolClient({
  projectId: 'your-project-uuid',
  apiKey: 'fp_prod_xxx',
  decryptionKey: 'fp_dec_xxx',
  context: { userId: 'user-1' }
});

// Works even if network fails (uses cache from last successful fetch)
await client.init();

// Always returns a value (from cache if offline)
const feature = client.isEnabled('my-feature');

Analytics (Paid Plans Only)

Track flag evaluation counts to understand usage patterns. Analytics is opt-in and disabled by default.

Enabling Analytics

const client = new FlagpoolClient({
  projectId: 'your-project-uuid',
  apiKey: 'fp_prod_xxx',
  decryptionKey: 'fp_dec_xxx',
  analytics: {
    enabled: true,
  }
});

Configuration Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | enabled | boolean | false | Enable/disable analytics | | flushInterval | number | 60000 | Flush interval in ms (minimum: 30000) | | flushThreshold | number | 100 | Flush after N evaluations | | sampleRate | number | 1.0 | Sample rate (0.0 - 1.0) | | syncFlushOnShutdown | boolean | false | Sync flush on process exit |

How It Works

  • Evaluation counts are batched in memory
  • Batches are sent asynchronously (fire-and-forget)
  • Analytics never blocks flag evaluation
  • Data is aggregated daily in your Flagpool dashboard
  • Note: Data is silently discarded for free plan projects

Debugging Analytics

Browser Console:

// Inspect analytics state
console.log(__FLAGPOOL__.state.analytics);
// { enabled: true, buffer: { 'my-flag': 5 }, bufferSize: 5, ... }

// Get all flags with evaluation status
console.log(__FLAGPOOL__.state.flags);
// { 'my-flag': { value: true, evaluated: true }, ... }

// Force flush
__FLAGPOOL__.flushAnalytics();

Programmatic API:

// Get analytics state
const state = client.getAnalyticsState();
console.log(state?.buffer);       // { 'my-flag': 5 }
console.log(state?.bufferSize);   // 5

// Get all flags with evaluation status
const flags = client.getAllFlagsWithState();
// { 'my-flag': { value: true, evaluated: true }, 'other-flag': { value: false, evaluated: false } }

Serverless Environments

For AWS Lambda, Vercel Functions, or similar short-lived environments:

const client = new FlagpoolClient({
  projectId: 'your-project-uuid',
  apiKey: 'fp_prod_xxx',
  decryptionKey: 'fp_dec_xxx',
  analytics: {
    enabled: true,
    flushThreshold: 10,        // Lower threshold
    syncFlushOnShutdown: true, // Flush before exit
  }
});

Privacy

  • Only flag keys and evaluation counts are tracked
  • No user data or context is sent
  • Data is retained for 3 months

Framework Examples

React

import { useEffect, useState } from 'react';
import { FlagpoolClient } from '@flagpool/sdk';

const client = new FlagpoolClient({
  projectId: 'your-project-uuid',
  apiKey: 'fp_prod_xxx',
  decryptionKey: 'fp_dec_xxx',
});

export function useFeatureFlag(key: string) {
  const [value, setValue] = useState<any>(null);

  useEffect(() => {
    client.init().then(() => {
      setValue(client.getValue(key));
    });

    const unsubscribe = client.onChange((changedKey, newValue) => {
      if (changedKey === key) setValue(newValue);
    });

    return () => unsubscribe?.();
  }, [key]);

  return value;
}

// Usage
function MyComponent() {
  const showNewFeature = useFeatureFlag('new-feature');
  
  if (showNewFeature) {
    return <NewFeature />;
  }
  return <OldFeature />;
}

Next.js

// lib/flagpool.ts
import { FlagpoolClient } from '@flagpool/sdk';

let client: FlagpoolClient | null = null;

export async function getFlags(userId: string) {
  if (!client) {
    client = new FlagpoolClient({
      projectId: process.env.FLAGPOOL_PROJECT_ID!,
      apiKey: process.env.FLAGPOOL_API_KEY!,
      decryptionKey: process.env.FLAGPOOL_DECRYPTION_KEY!,
      context: { userId }
    });
    await client.init();
  }
  
  return {
    newDashboard: client.isEnabled('new-dashboard'),
    buttonColor: client.getValue('button-color'),
  };
}

Node.js / Express

import express from 'express';
import { FlagpoolClient } from '@flagpool/sdk';

const app = express();

const flagpool = new FlagpoolClient({
  projectId: process.env.FLAGPOOL_PROJECT_ID!,
  apiKey: process.env.FLAGPOOL_API_KEY!,
  decryptionKey: process.env.FLAGPOOL_DECRYPTION_KEY!,
});

// Initialize on startup
await flagpool.init();

app.get('/api/data', (req, res) => {
  // Update context per-request
  flagpool.updateContext({ userId: req.user.id });
  
  if (flagpool.isEnabled('new-api-response')) {
    return res.json({ version: 'v2', data: newData });
  }
  return res.json({ version: 'v1', data: legacyData });
});

Target List Encryption

By default, target lists (used for inTargetList and notInTargetList operators) are encrypted in SDK exports to protect sensitive user data like emails and user IDs.

Encrypted Target Lists (Default)

If your environment has target list encryption enabled, you must provide a crypto adapter:

import { FlagpoolClient, setCryptoAdapter, CryptoAdapter } from '@flagpool/sdk';

// Create a Web Crypto adapter for browsers
const webCryptoAdapter: CryptoAdapter = {
  async decrypt(ciphertext, key, iv, tag) {
    const keyBase64 = key.startsWith('tlk_') ? key.slice(4) : key;
    const keyBytes = Uint8Array.from(atob(keyBase64), c => c.charCodeAt(0));
    const ivBytes = Uint8Array.from(atob(iv), c => c.charCodeAt(0));
    const ciphertextBytes = Uint8Array.from(atob(ciphertext), c => c.charCodeAt(0));
    const tagBytes = Uint8Array.from(atob(tag), c => c.charCodeAt(0));

    const cryptoKey = await crypto.subtle.importKey(
      'raw', keyBytes, { name: 'AES-GCM' }, false, ['decrypt']
    );

    const combined = new Uint8Array(ciphertextBytes.length + tagBytes.length);
    combined.set(ciphertextBytes, 0);
    combined.set(tagBytes, ciphertextBytes.length);

    const decrypted = await crypto.subtle.decrypt(
      { name: 'AES-GCM', iv: ivBytes }, cryptoKey, combined
    );

    return new TextDecoder().decode(decrypted);
  }
};

// Set up crypto adapter BEFORE creating client
setCryptoAdapter(webCryptoAdapter);

const client = new FlagpoolClient({
  projectId: 'your-project-id',
  apiKey: 'your-api-key',
  decryptionKey: 'tlk_xxx...',  // From Flagpool dashboard
  context: { userId: 'user-123' }
});

Error: "Encrypted target lists received but no crypto adapter configured"

If you see this error, it means:

  1. Your environment has target list encryption enabled (the default)
  2. You haven't set up a crypto adapter

Solutions:

  1. Set up a crypto adapter (recommended for production):

    setCryptoAdapter(yourCryptoAdapter);
  2. Disable encryption (for development/testing):

    • Go to Flagpool Dashboard → Settings → Environments
    • Toggle off "Target List Encryption" for your environment
    • This will export target lists in plaintext (values will be visible)

Plaintext Target Lists

If you disable target list encryption in the dashboard, no crypto adapter is needed. The SDK will use target lists directly without decryption.

⚠️ Security Note: Disabling encryption exposes target list values (emails, user IDs, etc.) in plaintext in the SDK exports. Only disable for development environments or non-sensitive data.

Documentation

For complete documentation, guides, and best practices, visit:

📚 flagpool.io/docs

Support

License

MIT © Flagpool