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

@provable-games/collectables

v0.5.2

Published

SDK for Provable Games collectable NFTs on Starknet — typed API client, metadata generators, and bundled beast artwork

Downloads

984

Readme

@provable-games/collectables

TypeScript SDK for Provable Games collectable NFTs on Starknet — a typed client for the collectables API, on-chain-faithful metadata generators, and all beast artwork bundled in the package.

| Collection | Contract (Starknet mainnet) | |---|---| | Beasts | 0x046da8955829adf2bda310099a0063451923f02e648cf25a1203aac6335cf0e4 | | Golden Token | 0x027838dea749f41c6f8a44fcfa791788e6101080c1b3cd646a361f653ad10e2d | | Karat | 0x030f694df2a04e04cf3e1d3e79dd5aadfdaa77295bb007696222fc60a5e8730d |

npm install @provable-games/collectables

Query collectables

import { CollectablesClient } from "@provable-games/collectables";

const client = new CollectablesClient(); // hosted API by default

// any filter/sort combination on metadata, keyset-paginated
const page = await client.beasts.list({
  shiny: true,
  type: "Magic",
  levelGte: 20,
  sort: "rank",
  order: "asc",
  limit: 50,
  includeOwner: true,
});

const beast = await client.beasts.get(85559);
const golden = await client.goldenTokens.list({ airdropRound: 1 });

Owner collections (single request)

Owner-scoped queries return the full holding in one response — no cursor loop:

const mine = await client.owner("0xYOUR_ADDRESS");   // { beasts, goldenTokens } — everything
const held = await client.beasts.byOwner("0xADDR");  // full page, nextCursor === null

// counts for an instant progress header, before/while the full list loads
const stats = await client.beasts.ownerStats("0xADDR");
// { total, uniqueBeastIds, shiny, animated, genesis }

Grouped gallery

Pre-aggregate beasts into cards — one per species / type / tier / affix — each with its count, shiny/animated tallies, and a representative token. Any beasts.list filter (incl. search and the *In filters) applies before grouping. groups is global (the whole collection); ownerGroups is scoped to one address. Both return Page<GroupAggregate> in a single response (nextCursor is always null).

// global: how many of each affix pair exist, rarest first
const gallery = await client.beasts.groups({
  groupBy: "full_name",   // "beast_id" | "type" | "tier" | "prefix" | "suffix" | "full_name"
  sort: "count",          // "count" | "tier" | "power" | "rank" | "name" (default "count")
  order: "desc",
  tierIn: [1, 2],         // filters apply before grouping
});

// same shape, scoped to one owner's holdings
const mine = await client.beasts.ownerGroups("0xADDR", { groupBy: "type" });

// each item:
// { key, label, beastId?, prefixName?, suffixName?,
//   count, shinyCount, animatedCount, representative /* a Beast */ }

Unnamed beasts collapse into a "__none__" group (label "No Special Name"), always sorted last. The representative is the group's max-power token (tie-break: more shiny+animated, then lowest token id).

Cursor pagination (general lists)

General token lists cap at 100 per page; walk them with the returned cursor:

let cursor: string | undefined;
do {
  const p = await client.beasts.list({ sort: "power", order: "desc", cursor });
  // ...use p.items
  cursor = p.nextCursor ?? undefined;
} while (cursor);

Custom fetch

Pass your own fetch (e.g. for auth headers or a proxy); it is invoked as-is:

new CollectablesClient({ fetch: myFetch });

Images

All 75 beast species ship in the package in four variants (static|animated × regular|shiny, PNG/GIF), the Golden Token SVG, and all 512 Karat SVGs (one unique on-chain image per token).

Recommended: copy them into your static assets once (add to your build or postinstall):

npx collectables copy-images ./public/collectables
import { beastImageUrl, goldenTokenImageUrl, karatImageUrl } from "@provable-games/collectables";

// the beast's shiny/animated flags select the right variant automatically
beastImageUrl(beast, { baseUrl: "/collectables" });
// → "/collectables/beasts/animated/shiny/lich.gif"

karatImageUrl({ tokenId: 1, baseUrl: "/collectables" });
// → "/collectables/karat/1.svg"

Other modes:

// bundler asset imports (advanced)
import { beastImageSpecifier, IMAGE_MANIFEST } from "@provable-games/collectables";

// Node.js servers (OG images, scripts) — separate subpath, browser-safe main entry
import { beastImageFilePath } from "@provable-games/collectables/node";

Card image

The 250×350 beast "card" — the canonical NFT image the on-chain token_uri emits (beasts/src/beast_svg.cairo) — is available as a self-contained SVG, so apps don't re-implement it. The renderer is a faithful transcription of that on-chain generator: same gold frame, shiny rainbow rim, tier-colored logo, crown/trophy, VT323 typeface and stat tiles. It's a pure, browser-safe function — the client renders the card itself from the lightweight beast data it already has; no server round-trip, no image bytes over the wire beyond the (shared, cached) pixel art.

Drop into <img>

fetchBeastCardImgSrc returns a self-contained data: URI you put straight in an <img src>. It fetches your hosted sprite once (browser-cached) and embeds it, so the card renders in the sandboxed image context with no external requests — no inline SVG, no dangerouslySetInnerHTML, nothing added to your bundle. The fetch in the name flags that it does I/O (and so is async):

import { fetchBeastCardImgSrc } from "@provable-games/collectables";

const src = await fetchBeastCardImgSrc(beast); // uses beast.image; or pass { art: "…url or data URI" }
// <img src={src} width={250} height={350} />

// React:
const [src, setSrc] = useState<string>();
useEffect(() => {
  let alive = true;
  fetchBeastCardImgSrc(beast).then((s) => alive && setSrc(s));
  return () => { alive = false; };
}, [beast.image]);
return src ? <img src={src} style={{ width: "100%", height: "auto" }} /> : null;

The beast needs an image field with your hosted sprite URL (or pass { art }). An already-data: art skips the fetch entirely.

Why <img> needs the embed: a card whose sprite is a URL renders with a blank art box inside <img>, because the browser's sandboxed image context blocks the sprite fetch. fetchBeastCardImgSrc sidesteps that by embedding the sprite up front. If you'd rather not fetch at all, render the SVG inline instead (below).

Render inline (lightest for a gallery)

Skip the embed entirely by inlining the SVG into the DOM — external art URLs load fine when the SVG is real markup rather than an image. Pull a page of beasts as JSON, render each card locally (~microseconds), and let the browser cache the per-species art; a 100-card page costs a few KB of JSON plus the shared art:

import { renderBeastCardSvg, beastImageUrl } from "@provable-games/collectables";

const svg = renderBeastCardSvg(beast, { art: beastImageUrl(beast, { baseUrl: "/collectables" }) });
return <div dangerouslySetInnerHTML={{ __html: svg }} />; // .card > svg { width:100%; height:auto }

Bytes (NFT image, OG, non-SDK)

When you need the card as bytes from the bundled art (server/SSR, an NFT image field, an OG image), use the /node generators:

import { beastCardDataUri, renderBeastCardSvgFromBundle } from "@provable-games/collectables/node";

beastCardDataUri(beast);             // "data:image/svg+xml;base64,…"  (same shape as token_uri)
renderBeastCardSvgFromBundle(beast); // the raw <svg> string with the bundled art inlined

Deliberate deviation from the chain: the art is drawn with <image> rather than <foreignObject>, which does not render when an SVG is shown via an <img> element — exactly how apps consume the card. tierColor() / TIER_COLORS are exported too.

Metadata

The API intentionally stores no descriptions or image URIs — the SDK regenerates them, byte-identical to the on-chain token_uri output:

import {
  beastFullName,      // '"Ghoul Sun" Lich'
  beastTraits,        // the exact on-chain 17-trait array
  beastTokenMetadata, // full ERC721-style metadata JSON
  goldenTokenMetadata,
} from "@provable-games/collectables";

const metadata = beastTokenMetadata(beast, {
  imageUrl: beastImageUrl(beast, { baseUrl: "https://yoursite.com/collectables" }),
});

Constants and helpers are exported too: BEAST_NAMES, ITEM_NAME_PREFIXES, ITEM_NAME_SUFFIXES, beastType(), beastTier(), beastPower(), airdropRound(), normalizeAddress(), and the collection registry (COLLECTIONS).

API

The hosted REST API is public — no key required (cached, rate-limited at 300 req/min/IP). Point the client elsewhere with new CollectablesClient({ baseUrl }). Endpoints and the full filter allowlist are discoverable at GET /collections.

License

MIT