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

@traffical/openfeature-web

v0.2.0

Published

OpenFeature web (static-context) provider backed by the Traffical browser SDK

Readme

@traffical/openfeature-web

OpenFeature web (static-context) provider backed by the Traffical browser SDK (@traffical/js-client).

Resolvers are synchronous and evaluate against a single bound static context (the current user/session). Each resolution runs one Traffical decide() (the intent-to-treat / ITT decision); exposure is a separate, explicit render-time signal — so treatment-on-the-treated (ToT) metrics, the SRM health gate, and bandit optimization measure what the user actually saw.

Install

npm install @traffical/openfeature-web @openfeature/web-sdk @traffical/js-client

@openfeature/web-sdk and @traffical/js-client are peer dependencies.

Register the provider

Construct your Traffical browser client, wrap it in the provider, and set it on OpenFeature with the initial context. The caller owns the client lifecycle.

import { OpenFeature } from "@openfeature/web-sdk";
import { TrafficalClient } from "@traffical/js-client";
import { TrafficalWebProvider } from "@traffical/openfeature-web";

const client = new TrafficalClient({ apiKey: "pk_...", /* ... */ });

await OpenFeature.setContext({ targetingKey: user.id, plan: user.plan });
await OpenFeature.setProviderAndWait(new TrafficalWebProvider(client));

const of = OpenFeature.getClient();

Static context

The provider evaluates against the context bound via OpenFeature.setContext(...) — resolvers ignore any per-call context argument. On a context change (e.g. login/logout) the provider re-binds the context, clears its decision memo (so a decision made under the old identity is never served or exposed under the new one), flows the new identity into the client, and returns — letting the web SDK emit the reconcile lifecycle events.

Resolve a flag

const enabled = of.getBooleanValue("checkout.newFlow", false);
const color = of.getStringValue("ui.color", "blue");

Resolvers are synchronous. The variant, reason (SPLIT when a variant was assigned, else DEFAULT), and scalar traffical.* flagMetadata come from the decision.

Exposure — the $traffical.exposure convention

Exposure (ToT) is explicit: fire it at your render site once the user actually sees the treatment. Use the reserved event name $traffical.exposure (exported as EXPOSURE_EVENT_NAME) and echo the flagKey in the details:

import { EXPOSURE_EVENT_NAME } from "@traffical/openfeature-web";

of.track(EXPOSURE_EVENT_NAME, { flagKey: "checkout.newFlow" });

The provider stitches this to the memoized decision for that flag in the current context and calls the native trackExposure(). It never re-decides in the exposure path — a miss (flag not resolved in this context) warns once and no-ops.

Every other track(name, details) is a business/reward event, forwarded to the client's track() joined on the bound targeting key (falling back to the client's anonymous stable id). A numeric details.value is lifted out as the reward value.

Web Tracking takes no context argument — identity comes from the bound static context.

No-exposure alarm

If the provider records many decisions but zero exposures, it fires a one-shot warning and a non-fatal provider Error event, since ToT/SRM/optimization would be silently empty. Instrument $traffical.exposure, or set exposureOnResolve.

Options

new TrafficalWebProvider(client, options):

| Option | Description | | --- | --- | | exposureOnResolve | When true, the resolver fires trackExposure() on the just-made decision (collapsing ToT toward ITT). | | unitKey | Override the context field the bundle buckets on. Defaults to client.getUnitKeyField() (the bundle's hashing.unitKey). | | exposureEventName | Override the reserved exposure event name. Defaults to $traffical.exposure. | | gatePropensity | Omit traffical.propensity from flagMetadata. Defaults to true on web. |

On web, flagMetadata is visible in browser devtools, so the provider always gates both traffical.propensity and traffical.modelVersion out — bandit selection internals never leak to the client.

targetingKey → unit-key mapping

The bound targetingKey is written under the bundle's actual bucketing field (hashing.unitKey), not a literal "targetingKey" field — otherwise the client's context enrichment would overwrite it with the anonymous stable id and silently mis-bucket the unit. A missing/empty targeting key throws TargetingKeyMissingError, which the OpenFeature SDK maps to the default with reason: ERROR.