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

@facet-smith/growthbook

v0.2.1

Published

Server-resolved GrowthBook assignment adapter for FacetSmith

Readme

@facet-smith/growthbook

Server-resolved GrowthBook assignment for FacetSmith. GrowthBook owns targeting, allocation, rollout, forced values, and sticky storage; FacetSmith keeps source variants authoritative and records exposure only after visible render.

Install

pnpm add @facet-smith/core @facet-smith/next @facet-smith/growthbook @growthbook/growthbook

Next.js server usage

Initialize one GrowthBookClient outside the request path, then pass the adapter to a server experiment:

import { GrowthBookClient } from "@growthbook/growthbook";
import {
  createGrowthBookResolver,
  growthBookIterationKey,
} from "@facet-smith/growthbook";
import { createNextExperiment } from "@facet-smith/next/server";

const growthbook = new GrowthBookClient({
  clientKey: process.env.GROWTHBOOK_CLIENT_KEY,
});
await growthbook.init({ timeout: 2_000 });

const growthBookResolver = createGrowthBookResolver({ client: growthbook });

const Hero = createNextExperiment(
  {
    id: "pricing-hero",
    iteration: "launch-1",
    defaultVariant: "control",
    variants: {
      control: { revision: "1", component: Control },
      concise: { revision: "1", component: Concise },
    },
  },
  growthBookResolver,
);

// Configure this exact feature key in GrowthBook. Its string values must be
// the source variant IDs: "control" and "concise".
growthBookIterationKey("pricing-hero", "launch-1");
// => "facetsmith-12-pricing-hero-8-launch-1"

Call await Hero.resolve(options) or await Hero.render(props, options) during the Server Component render. Pass the returned assignment through initialAssignments if a matching client boundary hydrates below it.

Do not configure a GrowthBook trackingCallback on the client used by this adapter. GrowthBook invokes that callback at evaluation time, while FacetSmith deliberately waits for visible render. Send exposure through the FacetSmith analytics adapter instead.

Identity and sticky bucketing

The adapter generates one GrowthBook feature key per (experiment ID, iteration). GrowthBook sticky documents use experimentKey__bucketVersion; the adapter rejects an experiment rule whose explicit key differs from its generated feature key with FSGB106. This prevents a new FacetSmith iteration from inheriting a stale GrowthBook assignment.

Why each iteration appears separately in GrowthBook

A new FacetSmith iteration is deliberately a new GrowthBook feature and experiment, so it appears as a separate row in the GrowthBook dashboard. For example, pricing-hero iterations launch-1 and launch-2 use different generated keys and must be configured as two experiments in GrowthBook rather than as two phases of one experiment.

That separation is the integrity boundary. A new iteration means that allocation, salt, eligibility, randomization, resolver behavior, or the vendor-key mapping changed. Combining its results with an earlier iteration would pool observations produced by different assignment semantics and contaminate the analysis. The extra dashboard row makes that boundary visible and prevents sticky assignments and results from leaking across runs.

To enable server sticky storage, pass a GrowthBook StickyBucketService. Loading sticky documents makes resolver execution asynchronous, which the Next server factory awaits before rendering:

const growthBookResolver = createGrowthBookResolver({
  client: growthbook,
  stickyBucketService: redisStickyBuckets,
});

Changing the key prefix, subject attribute, GrowthBook rule mapping, resolver, or eligibility semantics requires a new FacetSmith iteration.

Anonymous subjects

FacetSmith remains the identity owner. Supply the long-lived first-party anonymous cookie as subjectId; the adapter writes it to GrowthBook's id attribute by default. To use GrowthBook's documented anonymous attribute instead:

createGrowthBookResolver({
  client: growthbook,
  subjectAttribute: "anonymousId",
});

The adapter never mints an identifier. Additional attributes remain available through FacetSmith assignment attributes, including a secondary anonymous ID used as a GrowthBook sticky fallback attribute.

Non-assignment diagnostics

Only a GrowthBook result with source: "experiment", inExperiment: true, and hashUsed: true is exposure-eligible. Forced, overridden, missing, defaulted, and prerequisite-blocked values render a declared source variant without exposure and carry stable FSGB1xx diagnostics.

GrowthBook 1.7's public result currently reports both targeting misses and coverage exclusion as source: "defaultValue". The adapter therefore emits FSGB101 for that combined state. FacetSmith's resolver contract preserves arbitrary distinct reason codes, so the adapter can separate those populations if a future GrowthBook SDK exposes them without changing core.

This package is intentionally server-first. It performs no feature fetches during resolution and does not provide a React client integration.