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

@behindthescenes/cart

v0.0.16

Published

Browser cart SDK for BTS external-site checkout links.

Readme

@behindthescenes/cart

Browser SDK for BTS external-site cart state and checkout-link handoff. Use it to persist selected products, validate availability with the BTS API, and send shoppers to static or dynamic BTS checkout links.

Overview

The BTS Cart SDK provides:

  • Persistent cart state - Selected products and quantities are stored in browser localStorage by default.
  • Dynamic checkout links - Generates BTS /c/:checkoutLinkId?cart=... URLs from { lineItemId, quantity, discount } cart items.
  • Static checkout links - Supports one-click static checkout links through the same SDK API.
  • Multiple checkout links - Configure named checkout links and choose which one to use at checkout.
  • Site-key protection - Cart URL generation is validated by the BTS website cart API using the same public site key and verified-domain model as @behindthescenes/analytics.
  • Availability validation - The API validates checkout link status, products/access tiers, requested quantity, and discounts before returning a checkout URL.
  • Quantity - The SDK only normalizes quantities to integers ≥ 1. It does not cap counts to per-line inventory or API max; those rules apply on checkout-url only.

Installation

npm/yarn/pnpm/bun

npm install @behindthescenes/cart
pnpm add @behindthescenes/cart
yarn add @behindthescenes/cart
bun add @behindthescenes/cart

Browser Bundle (Hosted)

<script type="module">
  import { createBTSCart } from "https://behindthescenes.com/sdk/@behindthescenes/cart/latest/browser/browser.js";

  window.btsCart = createBTSCart({
    siteKey: "your-public-site-key",
    checkoutLinks: {
      tickets: {
        checkoutLinkId: "your-checkout-link-id",
        mode: "dynamic"
      }
    },
    defaultCheckoutLinkKey: "tickets"
  });
</script>

Quick Start

import { createBTSCart } from "@behindthescenes/cart";

const cart = createBTSCart({
  siteKey: "your-public-site-key",
  checkoutLinks: {
    tickets: {
      checkoutLinkId: "https://dev.behindthescenes.com/c/1a9d4f91-c1dd-455d-aa4c-851b296aff75",
      mode: "dynamic",
    },
  },
  defaultCheckoutLinkKey: "tickets",
});

cart.addItem({
  id: "garnet",
  accessTierId: "2dd3b989-616e-4e2f-9c47-21e1288d3823",
  checkoutLinkKey: "tickets",
  name: "Garnet",
  unitPrice: 395,
  fullPrice: 550,
  quantity: 1,
  discount: "GARNET-EARLYBIRD",
});

await cart.checkout("tickets");

Setup in BTS

Before using the SDK, configure the destination site in BTS:

  1. In BTS, go to the relevant space.
  2. Configure the external website integration and copy the public site key.
  3. Verify the external site domain for production use.
  4. Create a checkout link.
  5. For dynamic checkout links, use either the checkout link ID or the full checkout link URL plus the relevant product/access-tier line item IDs.

Local development can use localhost origins without verified-domain matching when the API runs in a local/development/test environment.

Dynamic Checkout Links

Dynamic checkout links use the BTS checkout cart query parameter:

encodeURIComponent(
  JSON.stringify([
    {
      lineItemId: "2dd3b989-616e-4e2f-9c47-21e1288d3823",
      quantity: 1,
      discount: "c16cfeea-c604-4009-851c-05e6dcdfbcea",
    },
  ]),
);

The SDK sends that cart payload to the BTS API for validation and receives a final checkout URL. The returned URL is equivalent to:

https://behindthescenes.com/c/<checkoutLinkId>?cart=<encoded-json>

For dynamic checkout links, set accessTierId or productId to the BTS catalog UUID (recommended). The SDK sends that value as lineItemId on checkout-url, matching how BTS checkout and webhooks resolve lines.

When the link has no saved checkout-link line items, BTS synthesizes the catalog from the space’s public products and tiers — use the tier or product UUID, not a site-local slug in id.

You may still pass lineItemId alone (persisted spaces_checkout_link_items.id, or tier/product UUID). If both a slug and a catalog UUID are present, the SDK prefers accessTierId / productId for checkout.

discount can be either the internal promo-code ID or the customer-facing promo code. The cart API resolves customer-facing codes to the internal IDs required by the BTS checkout page before returning the final URL.

Checkout link config also accepts full BTS checkout URLs:

const cart = createBTSCart({
  siteKey: "your-public-site-key",
  checkoutLinks: {
    tickets: {
      checkoutLinkId: "https://behindthescenes.com/c/1a9d4f91-c1dd-455d-aa4c-851b296aff75?discountCode=EARLYBIRD",
      mode: "dynamic",
    },
  },
});

The SDK extracts the checkout link ID and default discountCode from the URL.

Static Checkout Links

const cart = createBTSCart({
  siteKey: "your-public-site-key",
  checkoutLinks: {
    vip: {
      checkoutLinkId: "static-checkout-link-id",
      mode: "static",
      discountCode: "VIP-EARLYBIRD",
    },
  },
});

await cart.checkout("vip");

Static links ignore cart items and validate the configured checkout link before redirecting.

Multiple Checkout Links

const cart = createBTSCart({
  siteKey: "your-public-site-key",
  checkoutLinks: {
    tickets: {
      checkoutLinkId: "dynamic-ticket-link-id",
      mode: "dynamic",
    },
    merch: {
      checkoutLinkId: "dynamic-merch-link-id",
      mode: "dynamic",
    },
  },
  defaultCheckoutLinkKey: "tickets",
});

cart.addItem({
  id: "shirt",
  lineItemId: "product-line-item-id",
  checkoutLinkKey: "merch",
  name: "Event Shirt",
  unitPrice: 49,
});

await cart.checkout("merch");

Persistence

The SDK persists cart state to localStorage by default.

const cart = createBTSCart({
  siteKey: "your-public-site-key",
  storageKey: "my-site-cart",
  persist: true,
});

Disable persistence:

const cart = createBTSCart({
  siteKey: "your-public-site-key",
  persist: false,
});

Provide a custom storage adapter:

const cart = createBTSCart({
  siteKey: "your-public-site-key",
  storage: {
    getItem: (key) => sessionStorage.getItem(key),
    setItem: (key, value) => sessionStorage.setItem(key, value),
    removeItem: (key) => sessionStorage.removeItem(key),
  },
});

State API

cart.addItem(item);
cart.removeItem("garnet");
cart.setQuantity("garnet", 2);
cart.increment("garnet");
cart.decrement("garnet");
cart.clear();

const items = cart.getItems();
const itemCount = cart.getItemCount();
const subtotal = cart.getSubtotal();
const total = cart.getTotal();

Subscribe to changes:

const unsubscribe = cart.subscribe((state) => {
  console.log(state.items);
});

unsubscribe();

Checkout API

Return a checkout URL without redirecting:

const url = await cart.resolveCheckoutUrl("tickets");

Validate and redirect:

await cart.checkout("tickets");

React Integration

import { createBTSCart, type BTSCart, type BTSCartItem } from "@behindthescenes/cart";
import { createContext, useContext, useEffect, useMemo, useState } from "react";

const CartContext = createContext<BTSCart | null>(null);

export function CartProvider({ children }: { children: React.ReactNode }) {
  const cart = useMemo(
    () =>
      createBTSCart({
        siteKey: "your-public-site-key",
        checkoutLinks: {
          tickets: {
            checkoutLinkId: "dynamic-ticket-link-id",
            mode: "dynamic",
          },
        },
        defaultCheckoutLinkKey: "tickets",
      }),
    [],
  );

  return <CartContext.Provider value={cart}>{children}</CartContext.Provider>;
}

export function useCartItems(): BTSCartItem[] {
  const cart = useContext(CartContext);
  if (!cart) throw new Error("useCartItems must be used inside CartProvider");

  const [items, setItems] = useState(() => cart.getItems());

  useEffect(() => cart.subscribe((state) => setItems(state.items)), [cart]);

  return items;
}

Configuration

type BTSCartInit = {
  siteKey: string;
  endpoint?: string;
  checkoutLinks?: Record<string, BTSCartCheckoutLinkConfig>;
  defaultCheckoutLinkKey?: string;
  debug?: boolean;
  persist?: boolean;
  storageKey?: string;
  storage?: BTSCartStorage;
  requestHeaders?: HeadersInit | ((request: BTSCartRequestContext) => HeadersInit | Promise<HeadersInit>);
};

Defaults:

  • endpoint: https://api.bts.it.com/v2/website/cart
  • persist: true
  • storageKey: bts-cart
  • defaultCheckoutLinkKey: first configured checkout link key, or default

Browser Global API

The hosted browser bundle installs:

  • window.BTSCart
  • window.createBTSCart
  • window.btsCart
  • window.btsCartDataLayer
  • window.btsCartCommand

Queued command example:

<script>
  window.btsCartDataLayer = window.btsCartDataLayer || [];
  function btsCartCommand(){window.btsCartDataLayer.push(arguments);}

  btsCartCommand("config", {
    siteKey: "your-public-site-key",
    checkoutLinks: {
      tickets: {
        checkoutLinkId: "dynamic-ticket-link-id",
        mode: "dynamic"
      }
    }
  });
</script>

Development

bun install
bun run type-check
bun test
bun run build

Build artifacts:

  • dist/esm/index.js
  • dist/cjs/index.js
  • dist/browser/browser.js
  • dist/types/index.d.ts
  • dist/manifest.json

Hosted Bundle

Sync the hosted bundle into apps/bts-web/public/sdk/@behindthescenes/cart:

bun run build:hosted

Force a rebuild before syncing:

bun run build:hosted:force

Hosted layout matches @behindthescenes/analytics:

  • latest
  • one major-version directory, for example 0 or 1

Release

The cart SDK uses the same release pipeline as @behindthescenes/analytics.

Manual release from GitHub Actions:

  1. Open Cart SDK Release.
  2. Choose patch, minor, or major.
  3. Run the workflow from main.

The workflow:

  • installs dependencies with Bun
  • bumps the package version
  • runs bun run release:check
  • publishes with npm publish --provenance --access public
  • commits the version bump back to main

Tag release:

git tag cart-sdk-v0.0.2
git push origin cart-sdk-v0.0.2

The tag version is applied to packages/cart/package.json before publishing.