Skip to content

Repository files navigation

flame

@cuped-io/flame on npm@cuped-io/flame-react on npm@cuped-io/flame-edge on npmLicense: MIT

Client-side SDKs for cuped.io — A/B testing with CUPED variance reduction.

This repo is a pnpm workspace with three packages:

PackagenpmSourceRole
@cuped-io/flamenpmjs.com./packages/flameCore SDK. IIFE for <script> tag use; ESM/CJS for npm consumers.
@cuped-io/flame-reactnpmjs.com./packages/flame-reactReact bindings: <CupedProvider>, useExperiment, <Experiment>, useTrack.
@cuped-io/flame-edgenpmjs.com./packages/flame-edgeEdge resolver + signed cookie utilities for zero-flash SSR. Web Crypto only.

What it does

  1. Fetches active experiments for a project
  2. Assigns visitors to variants
  3. Applies variant changes to the DOM (8 change types: text, html, attribute, class, style, css, visibility, redirect)
  4. Auto-detects e-commerce events (add to cart, checkout, etc.)
  5. Tracks events with experiment assignments for server-side goal matching

Quick start

You'll need a DSN. Sign up at cuped.io, create a project, and copy the DSN from Settings → Install snippet (it looks like https://YOUR_KEY@api.cuped.io).

Option 1: Script tag (no-code DOM mutations)

Drop this in your <head> and define variants in the dashboard. Pin to a version and guard it with Subresource Integrity so a bad deploy can't reach your site (crossorigin is required for SRI to work on the cross-origin script):

<scriptsrc="https://cdn.cuped.io/flame@0.5.0.js"
integrity="sha384-EyZs8bbbc37P0vhn5CdDiwF5Ph/dgG7+92yeULGePA4XIUw52Wx2qWQvs+hsdrLk"
crossorigin="anonymous"
data-dsn="https://YOUR_KEY@api.cuped.io"
></script>

The SRI hash for every published version is listed at cdn.cuped.io/flame.sri.json (a versions map plus a latest pointer). Pinned URLs are write-once and never removed, so your pin keeps resolving byte-for-byte across later releases.

Prefer auto-updates over pinning?https://cdn.cuped.io/flame.js is a floating "latest" that always serves the newest release. It can't be pinned with SRI (the bytes change on every release), so you trade integrity verification and rollback safety for automatic upgrades.

Option 2: React (CSR)

pnpm add @cuped-io/flame @cuped-io/flame-react
import{CupedProvider,useExperiment}from'@cuped-io/flame-react';functionApp(){return(<CupedProviderdsn="https://YOUR_KEY@api.cuped.io"><Hero/></CupedProvider>);}functionHero(){const{ variant }=useExperiment('hero-cta');return<button>{variant?.name==='treatment' ? 'Buy now' : 'Get started'}</button>;}

Option 3: Next.js App Router with zero-flash SSR

The recommended setup for Next.js. Variants are resolved at the edge before the first byte renders, so server HTML matches the assigned variant from request #1.

pnpm add @cuped-io/flame @cuped-io/flame-react @cuped-io/flame-edge

.env.local:

CUPED_DSN=https://YOUR_KEY@api.cuped.io
NEXT_PUBLIC_CUPED_DSN=https://YOUR_KEY@api.cuped.io
CUPED_COOKIE_SECRET=<generate with: openssl rand -base64 32>

middleware.ts:

import{createCupedMiddleware}from'@cuped-io/flame-edge/next';exportdefaultcreateCupedMiddleware({dsn: process.env.CUPED_DSN!,secret: process.env.CUPED_COOKIE_SECRET!,});exportconstconfig={matcher: ['/((?!_next/static|_next/image|api/|favicon.ico).*)'],};

app/providers.tsx:

'use client';import{CupedProvider}from'@cuped-io/flame-react';importtype{PrehydratedState}from'@cuped-io/flame';exportfunctionProviders({
children,
prehydrated,}: {children: React.ReactNode;prehydrated?: PrehydratedState;}){return(<CupedProviderdsn={process.env.NEXT_PUBLIC_CUPED_DSN!}prehydrated={prehydrated}>{children}</CupedProvider>);}

app/layout.tsx:

import{cookies}from'next/headers';import{readPrehydratedForServerComponent}from'@cuped-io/flame-edge/next';import{Providers}from'./providers';exportdefaultasyncfunctionRootLayout({ children }: {children: React.ReactNode}){constprehydrated=awaitreadPrehydratedForServerComponent(awaitcookies(),process.env.CUPED_COOKIE_SECRET!,);return(<html><body><Providersprehydrated={prehydrated??undefined}>{children}</Providers></body></html>);}

That's it — useExperiment and <Experiment> work the same way as Option 2.

Examples

  • examples/script-tag — Static HTML page exercising all 8 change types.
  • examples/next-app — Next.js App Router app with edge middleware + signed prehydrated cookie. Includes verification steps.

Documentation

Development

pnpm install
pnpm test# unit tests, all packages
pnpm typecheck
pnpm lint
pnpm build # IIFE + ESM/CJS across packages
pnpm test:e2e:install # one-time: fetch the Chromium browser
pnpm test:e2e # Playwright browser tests against both examples

The Playwright suite (e2e/) drives the real script-tag and Next.js examples in a browser against a mock cuped API — assignment application, zero-flash SSR, and event delivery. See e2e/README.md.

Releases are managed by changesets and published to npm via OIDC trusted publishing with provenance attestations.

License

MIT — see LICENSE.

About

Client-side script for cuped.io

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages