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

@tomb-stone/eval

v0.1.0

Published

Zero-dependency evaluation engine for Tombstone — WASM-ready, runs in any JS runtime

Readme

@tombstone/eval

Zero-dependency evaluation engine for Tombstone.

Runs in: Node.js, browser, Deno, Bun, Cloudflare Workers, and any WASM-capable JS runtime.

Version: 0.1.0 | Tests: 41/41 passing | Dependencies: none

Why @tombstone/eval?

@tombstone/core depends on murmurhash and eventsource — npm packages that do not run in all JavaScript runtimes. @tombstone/eval eliminates both dependencies by inlining:

  • MurmurHash3 x86 32-bit — exact port of the murmurhash npm v3() method, producing identical bucket assignments to @tombstone/core
  • FNV-32a — used as the building block for hash v2

The result is a single TypeScript file with no external imports that runs everywhere a JS engine runs.

Installation

npm install @tombstone/eval

Usage

import { evaluate, isInRollout } from '@tombstone/eval';
import type { FlagState, EvalContext, EvalResult } from '@tombstone/eval';

// isInRollout — consistent, stateless rollout check
const inCohort = isInRollout('my_flag', 'user_abc123', 50);
// true if the user falls within the 50% rollout for this flag

// evaluate — full 5-step pipeline
const flag: FlagState = {
  flagKey: 'checkout_v2',
  enabled: true,
  rolloutPct: 50,
  safeDefault: 'false',
  hashVersion: 1,
  targetingRules: [
    {
      attribute: 'plan',
      operator: 'IN',
      values: ['enterprise', 'pro'],
      variation: 'true',
      priority: 10,
    },
  ],
};

const context: EvalContext = {
  userId: 'user_abc123',
  plan: 'enterprise',
};

const result: EvalResult = evaluate(flag, context, false);
// result.value  — resolved flag value
// result.reason — 'OFF' | 'FALLTHROUGH' | 'RULE_MATCH' | 'ERROR'

API

evaluate(flag, context, defaultValue)

Runs the full 5-step evaluation pipeline on a single FlagState.

function evaluate(
  flag: FlagState,
  context: EvalContext,
  defaultValue: unknown,
): EvalResult

Returns an EvalResult with value and reason. Never throws — on any internal error returns { value: defaultValue, reason: 'ERROR' }.

isInRollout(flagKey, userId, rolloutPct, hashVersion?)

Returns true if the given userId falls within the rollout percentage for the given flagKey. Consistent — same inputs always produce the same result. Stateless — no external state required.

function isInRollout(
  flagKey: string,
  userId: string,
  rolloutPct: number,
  hashVersion?: 1 | 2,   // default: 1
): boolean

5-Step Evaluation Pipeline

evaluate() implements the same pipeline as @tombstone/core:

  1. Guard — flag must be defined (returns ERROR if missing)
  2. OFF checkflag.enabled must be true (returns OFF if false)
  3. Targeting rules — rules sorted by priority descending (higher number = higher priority in this engine); first match returns RULE_MATCH
  4. Rollout hash checkisInRollout() with flag.hashVersion
  5. Default fallthrough — returns defaultValue with reason FALLTHROUGH

Hash v1 / v2

Both hash algorithms are inlined — no external packages.

| hashVersion | Algorithm | Notes | |---|---|---| | 1 (default) | MurmurHash3 x86 32-bit | Identical to murmurhash npm v3() | | 2 | Double-FNV32a | Better avalanche for short/numeric user IDs |

// Hash v1 (MurmurHash3)
isInRollout('my_flag', 'u123', 50, 1);

// Hash v2 (double-FNV32a)
isInRollout('my_flag', 'u123', 50, 2);

Both hash algorithms produce consistent bucket assignments that match @tombstone/core for the same inputs.

Types

export type EvaluationReason = 'OFF' | 'FALLTHROUGH' | 'RULE_MATCH' | 'ERROR';

export type RuleOperator =
  | 'IN' | 'NOT_IN' | 'EQ' | 'NEQ'
  | 'LT' | 'LTE' | 'GT' | 'GTE'
  | 'CONTAINS' | 'PREFIX' | 'SUFFIX';

export interface TargetingRule {
  attribute: string;
  operator: RuleOperator;
  values: unknown[];
  variation: string;
  priority: number;
}

export interface FlagState {
  flagKey: string;
  enabled: boolean;
  rolloutPct: number;
  safeDefault: string;
  hashVersion?: 1 | 2;
  targetingRules?: TargetingRule[];
}

export interface EvalContext {
  userId?: string;
  [key: string]: unknown;
}

export interface EvalResult {
  value: unknown;
  reason: EvaluationReason;
}

Cloudflare Workers Example

// worker.ts
import { evaluate } from '@tombstone/eval';

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const userId = request.headers.get('X-User-Id') ?? '';

    // Flag state loaded from KV or pre-bundled at build time
    const flag = await env.FLAGS_KV.get('checkout_v2', 'json');

    const result = evaluate(flag, { userId }, false);

    return new Response(JSON.stringify({ enabled: result.value }), {
      headers: { 'Content-Type': 'application/json' },
    });
  },
};

No murmurhash or eventsource — both were incompatible with the Workers runtime. @tombstone/eval has zero external dependencies.

Deno / Bun Example

// deno
import { evaluate, isInRollout } from 'npm:@tombstone/eval';

// bun
import { evaluate, isInRollout } from '@tombstone/eval';

Relationship to @tombstone/core

| Feature | @tombstone/eval | @tombstone/core | |---|---|---| | Dependencies | None | murmurhash, eventsource | | SSE streaming | No | Yes | | Snapshot fetch | No | Yes | | OpenFeature provider | No | Yes | | Browser / Workers / Deno / Bun | Yes | Node.js only | | Bundle size | ~4KB | ~25KB | | Hash v1 (MurmurHash3) | Inlined | npm package | | Hash v2 (FNV32a) | Inlined | Inlined | | Targeting rules | Yes | Yes | | 5-step pipeline | Yes | Yes |

Use @tombstone/eval when you need evaluation without a persistent SSE connection — edge functions, browser bundles, serverless cold starts, or any non-Node runtime. Use @tombstone/core for long-lived server processes that benefit from real-time flag streaming.

Tests

npm test
# 41 passing

Tests cover hash v1/v2 parity with @tombstone/core, all rule operators, rollout edge cases (0%, 100%, boundary), disabled flags, and missing flags.