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

@zenmanage/sdk

v3.1.2

Published

Feature flags SDK for JavaScript/TypeScript - works in Node.js and browsers

Downloads

255

Readme

Zenmanage JavaScript SDK

npm version Build Status Codacy Badge TypeScript

Add feature flags to your JavaScript/TypeScript application in minutes. Control feature rollouts, A/B test, and manage configurations without deploying code. Works in both Node.js and browsers!

Entry Points

The SDK ships two entry points:

| Import | Environment | Includes | | --------------------- | --------------------- | -------------------------------------- | | @zenmanage/sdk | Browser + Node.js | Core SDK, InMemoryCache, NullCache | | @zenmanage/sdk/node | Node.js only | Everything above + FileSystemCache |

Use @zenmanage/sdk for browser apps or universal code. Use @zenmanage/sdk/node when you need filesystem caching in a Node.js server.

Why Zenmanage?

  • 🚀 Fast: Rules cached locally - ~1ms evaluation time
  • 🎯 Targeted: Roll out features to specific users, organizations, or segments
  • 🛡️ Safe: Graceful fallbacks and error handling built-in
  • 📊 Insightful: Automatic usage tracking (optional)
  • 🧪 Testable: Easy to mock in tests
  • 🌐 Universal: Works in Node.js (16+) and modern browsers
  • 📘 TypeScript: Full type definitions included

Installation

npm install @zenmanage/sdk

Browser Requirements: Modern browsers with fetch API support (or use a polyfill) Node.js Requirements: Node.js 16+ (requires native fetch API)

Key Compatibility

  • Browser/client runtime: client keys prefixed with cli_
  • Node.js/server runtime: server keys prefixed with srv_
  • Mobile keys (mob_) are rejected by this SDK at initialization

Get Started in 60 Seconds

1. Get your environment token from zenmanage.com

2. Initialize the SDK:

import { Zenmanage, ConfigBuilder } from '@zenmanage/sdk';

const zenmanage = new Zenmanage(
  ConfigBuilder.create().withEnvironmentToken('cli_your_client_key_here').build()
);

3. Check a feature flag:

const flag = await zenmanage.flags().single('new-dashboard');

if (flag.isEnabled()) {
  // Show new dashboard
  showNewDashboard();
} else {
  // Show old dashboard
  showOldDashboard();
}

That's it! 🎉

Common Use Cases

Roll Out a New Feature Gradually

import { Zenmanage, ConfigBuilder, Context } from '@zenmanage/sdk';

const zenmanage = new Zenmanage(
  ConfigBuilder.create().withEnvironmentToken('cli_your_client_key_here').build()
);

// Create context with user information
const context = Context.single('user', userId, userName);

const betaAccess = await zenmanage.flags().withContext(context).single('beta-program');

if (betaAccess.isEnabled()) {
  // User is in beta program
  const features = getBetaFeatures();
}

Note: Call withContext() on the flag manager to ensure context is sent to the API when loading rules.

A/B Testing

import { Context, Attribute } from '@zenmanage/sdk';

const context = new Context('user', user.name, user.id, [
  new Attribute('country', [user.country]),
  new Attribute('plan', [user.subscriptionPlan]),
]);

const variant = await zenmanage.flags().withContext(context).single('checkout-flow');

if (variant.asString() === 'one-page') {
  renderOnePageCheckout();
} else {
  renderMultiPageCheckout();
}

Percentage Rollouts

Gradually roll out features to a percentage of your users. The SDK handles bucketing automatically using a deterministic CRC32B hash — no manual bucket logic needed.

import { Context } from '@zenmanage/sdk';

// Just provide a context with an identifier — the SDK does the rest
const context = Context.single('user', userId);

const flag = await zenmanage.flags().withContext(context).single('new-checkout-flow');

if (flag.isEnabled()) {
  // This user is in the rollout percentage
  renderNewCheckout();
} else {
  // This user is outside the rollout
  renderClassicCheckout();
}

How it works:

  • Configure the rollout percentage (0–100%) and a unique salt in the Zenmanage dashboard
  • The SDK hashes salt:contextIdentifier to deterministically assign each user to a bucket (0–99)
  • Users whose bucket is below the percentage get the rollout value; others get the fallback
  • The same user always gets the same result (deterministic), and increasing the percentage never removes previously included users
  • Rollout rules can further refine targeting within the rollout group (e.g., only US users in the rollout)

Note: A context identifier is required for bucketing. Without one, the user always receives the fallback value.

Feature Toggles by Organization

const orgContext = Context.single('organization', orgId, orgName);

const enterpriseFeatures = await zenmanage
  .flags()
  .withContext(orgContext)
  .single('enterprise-analytics');

if (enterpriseFeatures.isEnabled()) {
  showEnterpriseAnalytics();
}

Using Default Values

// Inline default (highest priority)
const flag = await zenmanage.flags().single('feature-flag', true);

console.log(flag.isEnabled()); // Returns true if flag not found

// Or use DefaultsCollection for multiple defaults
import { DefaultsCollection } from '@zenmanage/sdk';

const defaults = DefaultsCollection.fromObject({
  'new-ui': true,
  'api-version': 'v2',
  'max-items': 100,
});

const flagManager = zenmanage.flags().withDefaults(defaults);
const flag = await flagManager.single('new-ui');

Get All Flags

const allFlags = await zenmanage.flags().all();

for (const flag of allFlags) {
  console.log(`${flag.getKey()}: ${flag.getValue()}`);
}

Configuration Options

const config = ConfigBuilder.create()
  .withEnvironmentToken('cli_your_client_key_here') // Browser/client runtime
  // For Node.js/server runtime use: .withEnvironmentToken('srv_your_server_key_here')
  .withCacheTtl(3600) // Cache TTL in seconds (default: 3600)
  .withCacheBackend('memory') // 'memory' or 'null' (default: 'memory')
  .withCache(customCacheInstance) // Custom Cache implementation (overrides cacheBackend)
  .withUsageReporting(true) // Enable usage tracking (default: true)
  .withApiEndpoint('https://api.zenmanage.com') // Custom API endpoint (default: api.zenmanage.com)
  .withLogger(customLogger) // Custom logger instance
  .build();

const zenmanage = new Zenmanage(config);

Configuration from Environment Variables (Node.js only)

// Reads from environment variables:
// - ZENMANAGE_ENVIRONMENT_TOKEN
// - ZENMANAGE_CACHE_TTL
// - ZENMANAGE_CACHE_BACKEND
// - ZENMANAGE_CACHE_DIR
// - ZENMANAGE_ENABLE_USAGE_REPORTING
// - ZENMANAGE_API_ENDPOINT

const config = ConfigBuilder.fromEnvironment().build();
const zenmanage = new Zenmanage(config);

Cache Backends

Memory Cache (Default)

Best for: Most applications, serverless functions, browsers

ConfigBuilder.create()
  .withEnvironmentToken('cli_your_client_key_here')
  .withCacheBackend('memory')
  .build();

Data is cached in memory for the lifetime of the application. Fastest option but data is lost on restart. Works everywhere (Node.js and browsers).

Filesystem Cache (Node.js only)

Best for: Long-running Node.js servers

The filesystem cache is available from the @zenmanage/sdk/node entry point:

import { Zenmanage, ConfigBuilder, FileSystemCache } from '@zenmanage/sdk/node';

const config = ConfigBuilder.create()
  .withEnvironmentToken('srv_your_server_key_here')
  .withCache(new FileSystemCache('/tmp/zenmanage-cache'))
  .withCacheTtl(7200)
  .build();

Data persists across application restarts. Only available in Node.js — it is not included in the default @zenmanage/sdk import to keep the browser bundle free of Node.js dependencies.

Custom Cache

You can provide any object that implements the Cache interface via .withCache():

import type { Cache } from '@zenmanage/sdk';

class RedisCache implements Cache {
  async get(key: string): Promise<string | null> {
    /* ... */
  }
  async set(key: string, value: string, ttl?: number): Promise<void> {
    /* ... */
  }
  async has(key: string): Promise<boolean> {
    /* ... */
  }
  async delete(key: string): Promise<void> {
    /* ... */
  }
  async clear(): Promise<void> {
    /* ... */
  }
}

const config = ConfigBuilder.create()
  .withEnvironmentToken('cli_your_client_key_here')
  .withCache(new RedisCache())
  .build();

Null Cache (No Caching)

Best for: Testing, debugging

ConfigBuilder.create()
  .withEnvironmentToken('cli_your_client_key_here')
  .withCacheBackend('null')
  .build();

Fetches rules from API on every request. Useful for testing but not recommended for production.

Context

Context allows you to pass information about the user, organization, or other entities that influence flag evaluation based on server-side rules.

Simple Context

import { Context } from '@zenmanage/sdk';

// Context with just identifier
const context = Context.single('user', 'user-123');

// Context with identifier and name
const context = Context.single('user', 'user-123', 'John Doe');

Context with Attributes

import { Context, Attribute } from '@zenmanage/sdk';

const context = new Context(
  'user', // type
  'John Doe', // name (optional)
  'user-123', // identifier (optional)
  [
    new Attribute('country', ['US']),
    new Attribute('plan', ['premium', 'annual']),
    new Attribute('signup_date', ['2024-01-15']),
  ]
);

const flag = await zenmanage.flags().withContext(context).single('premium-feature');

Context Types

  • user: Individual users
  • organization: Companies or teams
  • session: Browser or app sessions
  • device: Physical devices
  • custom: Any custom type you define

Rule Targeting With Context Types

When rules target context or segment, the SDK compares the context identifier and, if present, the type. If a rule value omits type (or sets it to null), only the identifier is matched.

const rules = [
  {
    clauses: [
      {
        attribute: 'context',
        operator: 'equals',
        value: { identifier: 'user-123', type: 'user' },
      },
    ],
    value: { value: { boolean: true } },
  },
  {
    clauses: [
      {
        attribute: 'segment',
        operator: 'equals',
        value: { identifier: 'shared-id', type: null },
      },
    ],
    value: { value: { boolean: true } },
  },
];

Flag Types

Boolean Flags

const flag = await zenmanage.flags().single('feature-enabled');

if (flag.isEnabled()) {
  // Feature is enabled
}

// Or use asBool()
const enabled = flag.asBool();

String Flags

const flag = await zenmanage.flags().single('theme');

const theme = flag.asString();
console.log(theme); // 'dark', 'light', etc.

Number Flags

const flag = await zenmanage.flags().single('max-items');

const maxItems = flag.asNumber();
console.log(maxItems); // 100, 250, etc.

Type-Safe Access

All flags have multiple accessor methods:

  • isEnabled(): Boolean check (only true for boolean flags with value true)
  • asBool(): Get value as boolean
  • asString(): Get value as string
  • asNumber(): Get value as number
  • getValue(): Get raw value

Advanced Features

Custom Logger

import type { Logger } from '@zenmanage/sdk';

const customLogger: Logger = {
  debug: (msg, meta) => console.debug(msg, meta),
  info: (msg, meta) => console.info(msg, meta),
  warn: (msg, meta) => console.warn(msg, meta),
  error: (msg, meta) => console.error(msg, meta),
};

const config = ConfigBuilder.create()
  .withEnvironmentToken('srv_your_server_key_here')
  .withLogger(customLogger)
  .build();

Force Refresh Rules

// Force refresh from API (bypasses cache)
await zenmanage.flags().refreshRules();

Manual Usage Reporting

const context = Context.single('user', 'user-123');

// Report that a flag was evaluated
await zenmanage.flags().reportUsage('feature-flag', context);

TypeScript Support

The SDK is written in TypeScript and includes full type definitions:

import type {
  Config,
  Logger,
  Cache,
  FlagType,
  FlagValue,
  ContextData,
  RulesResponse,
} from '@zenmanage/sdk';

const config: Config = {
  environmentToken: 'srv_test',
  cacheTtl: 3600,
  cacheBackend: 'memory',
};

Browser Usage

The default @zenmanage/sdk entry point is fully browser-safe — it contains no Node.js built-ins (fs, path, util), so it works with any bundler (Webpack, Vite, Rollup, esbuild, etc.) and from a CDN.

<!DOCTYPE html>
<html>
  <head>
    <script type="module">
      import { Zenmanage, ConfigBuilder } from 'https://cdn.skypack.dev/@zenmanage/sdk';

      const zenmanage = new Zenmanage(
        ConfigBuilder.create().withEnvironmentToken('srv_your_server_key_here').build()
      );

      const flag = await zenmanage.flags().single('new-ui');
      if (flag.isEnabled()) {
        document.body.classList.add('new-ui');
      }
    </script>
  </head>
  <body>
    <!-- Your content -->
  </body>
</html>

With a bundler (Webpack, Vite, etc.):

import { Zenmanage, ConfigBuilder } from '@zenmanage/sdk';

const zenmanage = new Zenmanage(
  ConfigBuilder.create().withEnvironmentToken('srv_your_server_key_here').build()
);

Note: FileSystemCache is only available from @zenmanage/sdk/node. In the browser, use the default in-memory cache or provide a custom Cache implementation.

Error Handling

import { EvaluationError, FetchRulesError } from '@zenmanage/sdk';

try {
  const flag = await zenmanage.flags().single('unknown-flag');
} catch (error) {
  if (error instanceof EvaluationError) {
    // Flag not found
    console.error('Flag not found:', error.message);
  } else if (error instanceof FetchRulesError) {
    // API error
    console.error('Failed to fetch rules:', error.message);
  }
}

// Or use default values to avoid errors
const flag = await zenmanage.flags().single('unknown-flag', false);
console.log(flag.isEnabled()); // false

Testing

The SDK is designed to be easily testable:

import { Zenmanage, ConfigBuilder } from '@zenmanage/sdk';
import { vi } from 'vitest'; // or jest

// Mock the API client
vi.mock('@zenmanage/sdk', async () => {
  const actual = await vi.importActual('@zenmanage/sdk');
  return {
    ...actual,
    Zenmanage: vi.fn().mockImplementation(() => ({
      flags: () => ({
        single: vi.fn().mockResolvedValue({
          isEnabled: () => true,
          asString: () => 'test-value',
          asNumber: () => 42,
        }),
      }),
    })),
  };
});

test('feature flag enabled', async () => {
  const zenmanage = new Zenmanage(ConfigBuilder.create().withEnvironmentToken('srv_test').build());

  const flag = await zenmanage.flags().single('test-flag');
  expect(flag.isEnabled()).toBe(true);
});

Best Practices

1. Initialize Once

Create a single Zenmanage instance and reuse it throughout your application:

// zenmanage.ts (Node.js)
import { Zenmanage, ConfigBuilder } from '@zenmanage/sdk/node';

export const zenmanage = new Zenmanage(
  ConfigBuilder.create().withEnvironmentToken(process.env.ZENMANAGE_TOKEN!).build()
);

// zenmanage.ts (Browser)
import { Zenmanage, ConfigBuilder } from '@zenmanage/sdk';

export const zenmanage = new Zenmanage(
  ConfigBuilder.create().withEnvironmentToken('srv_your_server_key_here').build()
);

// other-file.ts
import { zenmanage } from './zenmanage';

const flag = await zenmanage.flags().single('feature');

2. Use Default Values

Always provide default values to ensure your application works even if the API is unavailable:

const flag = await zenmanage.flags().single('feature', false);

3. Context Best Practices

  • Always include context when evaluating flags that have targeting rules
  • Use consistent attribute names across your application
  • Keep attribute values simple (strings, numbers)

4. Cache Configuration

  • Use memory cache for most applications, serverless, and browsers (default)
  • Use filesystem cache for long-running Node.js servers (import from @zenmanage/sdk/node)
  • Use custom cache (.withCache()) for Redis, IndexedDB, or other backends
  • Use null cache only for testing/debugging

5. Error Handling

Always handle errors gracefully with try-catch or default values:

try {
  const flag = await zenmanage.flags().single('feature');
  if (flag.isEnabled()) {
    // Enable feature
  }
} catch (error) {
  // Fallback to safe default
  console.error('Flag evaluation failed:', error);
}

API Reference

Zenmanage

Main SDK class.

Methods:

  • flags(): Returns the FlagManager instance

ConfigBuilder

Fluent builder for creating configuration.

Methods:

  • create(): Create new builder
  • fromEnvironment(): Create builder from environment variables (Node.js only)
  • withEnvironmentToken(token): Set environment token (required)
  • withCacheTtl(seconds): Set cache TTL
  • withCacheBackend(backend): Set cache backend ('memory' or 'null')
  • withCacheDirectory(path): Set cache directory (used with filesystem cache)
  • withCache(cache): Set a custom Cache instance (overrides cacheBackend)
  • withUsageReporting(enabled): Enable or disable usage tracking
  • withApiEndpoint(url): Set custom API endpoint
  • withLogger(logger): Set custom logger
  • build(): Build the configuration

FlagManager

Manages flag evaluation.

Methods:

  • single(key, defaultValue?): Get a single flag by key
  • all(): Get all flags
  • withContext(context): Create new manager with context
  • withDefaults(defaults): Create new manager with defaults
  • refreshRules(): Force refresh rules from API
  • reportUsage(key, context?): Manually report flag usage

Context

Represents evaluation context.

Methods:

  • single(type, identifier, name?): Create simple context
  • fromObject(data): Create from plain object
  • addAttribute(attribute): Add an attribute
  • getAttribute(key): Get an attribute
  • hasAttribute(key): Check if attribute exists
  • getAttributes(): Get all attributes

Flag

Represents a feature flag.

Methods:

  • isEnabled(): Check if boolean flag is enabled
  • asBool(): Get value as boolean
  • asString(): Get value as string
  • asNumber(): Get value as number
  • getValue(): Get raw value
  • getKey(): Get flag key
  • getName(): Get flag name
  • getType(): Get flag type

Examples

See the examples directory for more examples:

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

License

MIT License - see LICENSE for details.

Support

Changelog

See CHANGELOG.md for release history and changes.