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

@productcraft/rally

v0.0.5

Published

Waitlist management — public-form signups, variants for A/B/n landing pages, referrals, position + leaderboard, approval workflow with invite-to-app, signed outbound webhooks, CSV export. Generated from the production OpenAPI spec.

Readme

@productcraft/rally

Typed Node.js SDK for ProductCraft Rally — waitlist management: public-form signups, variants for A/B/n landing pages, referrals, position + leaderboard, approval workflow with invite-to-app, signed outbound webhooks, CSV export.

npm install @productcraft/rally

The waitlist-entries endpoint can be called unauthenticated from a marketing-site form. Every other endpoint (admin, approval, analytics, exports, webhooks) requires a PlatformUser cookie or a workspace-scoped PAK (pcft_live_…).

Quick start — accept a signup from a public form

import { Rally } from "@productcraft/rally";

// No auth — public surface
const rally = new Rally();

const { data, error } = await rally.client.POST(
  "/v1/waitlists/{workspace_slug}/{waitlist_slug}/entries",
  {
    params: { path: { workspace_slug: "acme", waitlist_slug: "early-access" } },
    body: {
      email: "[email protected]",
      name: "Alice",
      referrer: "twitter",
      referral_code: "ada-123",     // optional — bumps the referrer's position
      metadata: { plan_interest: "pro" },
    },
  },
);

The response includes the entry's id + assigned position. Round-trip the id into your "thanks" page so the visitor can share their own referral link.

If the waitlist has settings.recaptcha_site_key set, also pass a recaptcha_token from your client side.

Quick start — workspace-admin (authenticated)

import { Rally } from "@productcraft/rally";

const rally = new Rally({
  auth: { type: "apiKey", key: process.env.PCFT_KEY! },
});

// Create a waitlist — body uses `display_name`, not `name`.
const { data } = await rally.client.POST(
  "/v1/workspaces/{workspace_id}/waitlists",
  {
    params: { path: { workspace_id: "<workspace-uuid>" } },
    body: { display_name: "Early Access", slug: "early-access" },
  },
);

{workspace_id} is the workspace UUID returned by @productcraft/platform-auth's introspect endpoint.

Configuration

new Rally({
  // Optional: required for workspace-admin calls. Public submit works without auth.
  auth: { type: "apiKey", key: "pcft_live_..." }
      | { type: "bearer", token: "eyJ..." }
      | { type: "cookie", value: "auth_token=..." },
  baseUrl: "https://api.rally.example.test",   // optional override
  fetch: customFetch,                            // optional
});

Common operations

Public surface

// Read the waitlist's public metadata (name, position-window, active variant, ...)
await rally.client.GET(
  "/v1/waitlists/{workspace_slug}/{waitlist_slug}",
  { params: { ... } },
);

// Read the public leaderboard (when enabled per-waitlist)
await rally.client.GET(
  "/v1/waitlists/{workspace_slug}/{waitlist_slug}/leaderboard",
  { params: { ... } },
);

Entries (workspace-admin)

// List entries
await rally.client.GET(
  "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries",
  { params: { path: { workspace_id, waitlist_id }, query: { limit: 50 } } },
);

// Count
await rally.client.GET(
  "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries/count",
  { ... },
);

// Approve / reject in bulk
await rally.client.POST(
  "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries/bulk",
  { params: { ... }, body: { ids: [...], action: "approve" } },
);

// Send an invite (e.g. into a Heimdall app)
await rally.client.POST(
  "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries/{entry_id}/invite-to-app",
  { params: { ... }, body: { /* app + role + invite_template */ } },
);

// Export to CSV
await rally.client.GET(
  "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/entries/export.csv",
  { ... },
);

Variants (A/B/n landing pages)

Variants split two ways: kind: "ab" for split-traffic A/B/n tests, or kind: "locale" for per-locale copy switching. Pick one; the body shape is { kind, slug, locale? }.

// A/B variant
await rally.client.POST(
  "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/variants",
  {
    params: { ... },
    body: { kind: "ab", slug: "headline-b" },
  },
);

// Locale variant
await rally.client.POST(
  "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/variants",
  {
    params: { ... },
    body: { kind: "locale", slug: "pt-br", locale: "pt-BR" },
  },
);

Front-end picks up the active variant from GET /v1/waitlists/:workspace_slug/:waitlist_slug and round-trips its id back in the entry submission via the variant_id field — Rally computes per-variant conversion without a separate impressions table.

Analytics

// Conversion + per-variant counts
await rally.client.GET(
  "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/analytics",
  { ... },
);

// Timeseries — `since` is an ISO-8601 timestamp. There are no
// `bucket` / `lookback` query params.
await rally.client.GET(
  "/v1/workspaces/{workspace_id}/waitlists/{waitlist_id}/analytics/timeline",
  {
    params: {
      path: { ... },
      query: { since: "2026-05-01T00:00:00Z" },
    },
  },
);

Webhooks

// Subscribe to entry.created / entry.approved / entry.rejected
await rally.client.POST(
  "/v1/workspaces/{workspace_id}/webhooks",
  { params: { ... }, body: { url: "https://yourapp.com/hooks/rally", events: ["entry.created"] } },
);

// Rotate the signing secret without disabling the webhook
await rally.client.POST(
  "/v1/workspaces/{workspace_id}/webhooks/{id}/rotate-secret",
  { ... },
);

// Replay a delivery from history
await rally.client.GET(
  "/v1/workspaces/{workspace_id}/webhooks/{id}/deliveries",
  { ... },
);

How this SDK is built

Generated from the live OpenAPI spec at https://api.rally.productcraft.co/docs-json via openapi-typescript + openapi-fetch. The nightly spec-refresh workflow opens a PR whenever the spec changes.

License

MIT.