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

@lucerna-dev/gates-openfeature-web

v0.0.1-alpha.0

Published

[OpenFeature](https://openfeature.dev) web provider for Lucerna Gates. It plugs Gates into the vendor-neutral `@openfeature/web-sdk` — and into [`@openfeature/react-sdk`](https://openfeature.dev/docs/reference/technologies/client/web/react), which layers

Readme

@lucerna-dev/gates-openfeature-web

OpenFeature web provider for Lucerna Gates. It plugs Gates into the vendor-neutral @openfeature/web-sdk — and into @openfeature/react-sdk, which layers React hooks over any web provider. One identity-resolved decisions snapshot is fetched with the environment's publishable client key (@lucerna-dev/gates-browser underneath), then every read answers synchronously from it. Targeting rules never reach the browser — only decisions do.

Install

pnpm add @lucerna-dev/gates-openfeature-web @lucerna-dev/gates-browser @openfeature/web-sdk

Both @lucerna-dev/gates-browser and @openfeature/web-sdk are peer dependencies. For React, add @openfeature/react-sdk — no Lucerna-specific React package is needed.

Quickstart

import { OpenFeature } from "@openfeature/web-sdk";
import { LucernaWebProvider } from "@lucerna-dev/gates-openfeature-web";

await OpenFeature.setContext({ targetingKey: "u_42", plan: "pro" });
await OpenFeature.setProviderAndWait(new LucernaWebProvider({ clientKey: "ck_client_YOUR_KEY" }));

const client = OpenFeature.getClient();
const showNewBilling = client.getBooleanValue("new_billing", false); // boolean, sync
const variant = client.getStringValue("checkout_test", "control"); // variant name, sync

The key is the environment's publishable client key (ck_client_…) — safe to embed, and it pins the environment. Secret keys (ck_srv_…, ck_key_…) are rejected at construction: a secret in a browser bundle is readable by anyone.

Set the context before registering the provider so the first snapshot is already targeted; reads are synchronous from then on. When the user logs in, out, or changes, call OpenFeature.setContext() again — the provider refetches decisions for the new identity and the SDK signals ContextChanged when the swap is complete.

With React

import { OpenFeatureProvider, useBooleanFlagValue } from "@openfeature/react-sdk";

function App() {
  return (
    <OpenFeatureProvider>
      <Billing />
    </OpenFeatureProvider>
  );
}

function Billing() {
  const newBilling = useBooleanFlagValue("new_billing", false);
  return newBilling ? <NewBilling /> : <ClassicBilling />;
}

Register the provider once at startup (quickstart above); the hooks re-render on ConfigurationChanged and ContextChanged automatically.

How evaluation context maps to Gates

| OpenFeature | Gates | | --------------------------------------------------- | ----------------------------------------------------------------- | | targetingKey | userId — the sticky-bucketing unit for rollouts and experiments | | string / number / boolean attribute | a stringified trait, what targeting rules match against | | Date attribute | an ISO-8601 trait | | null / undefined / nested structures and arrays | dropped — Gates traits are a flat string map |

Per-evaluation context does not exist in the web SDK's static-context paradigm — identity lives in the global context (OpenFeature.setContext), and every read answers for it.

Type mapping

| OpenFeature read | Gates concept | Details | | ----------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------- | | getBooleanValue | feature flag (kill switches folded in) | reason TARGETING_MATCH; a key in the kills snapshot answers the kill state — thrown reads false | | getStringValue | experiment variant | reason SPLIT with variant set, or your default with reason DEFAULT when not in the experiment | | getNumberValue / getObjectValue | — | Gates has no number/object flags: your default with TYPE_MISMATCH |

Options

| Option | Default | What it does | | ------------------ | ---------------------------- | ------------------------------------------------------------------------------------------- | | clientKey | — | required; the environment's publishable client key (ck_client_…) | | baseUrl | https://api.uselucerna.app | override for self-hosted or local development | | requestTimeoutMs | 5000 | per-request timeout | | readyTimeoutMs | 10000 | cap on how long initialize waits for the first snapshot before coming up on safe defaults | | onError | — | tap for bootstrap/refresh failures — resolvers never throw | | fetch | platform fetch | override the transport (tests, custom dispatchers) | | storage | — | optional decisions cache (e.g. localStorage) so the next page load reads instantly | | storageKey | lucerna:gates | cache key when storage is set |

Failure semantics

Resolvers never throw. Before the first snapshot — or when bootstrap fails — boolean reads answer safe false and string reads answer your default, while the error surfaces through onError. A rejected key (401/403) fails initialize loudly (PROVIDER_ERROR); a slow or offline network does not block the app — initialize resolves at readyTimeoutMs and the provider serves safe defaults until the next context change refetches.

Wire onError: without it, a misconfigured setup serves "everything off" silently.

Decisions sent to a browser are UX hints and can be tampered with — gate what users see with them, never what users are allowed to do. Authorization stays on your backend.

With @lucerna-dev/identity

Identity stays the source of truth and feeds OpenFeature one-directionally:

import { createIdentity } from "@lucerna-dev/identity";
import { OpenFeature } from "@openfeature/web-sdk";

const identity = createIdentity({ apiKey: "ck_client_YOUR_KEY" });

identity.onChange((user) => {
  void OpenFeature.setContext({ targetingKey: user.userId, ...user.traits });
});

identify(), trait() and reset() (logout) all flow into a decisions refetch through that one subscription.