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

@carverjs/embed-sdk

v0.0.5

Published

Tiny SDK for games embedded in the CarverJS marketplace — typed postMessage to the parent shell without raw postMessage calls

Readme

@carverjs/embed-sdk

npm license Discord

Talk to the CarverJS marketplace shell from inside your game. Dependency-free, ~1 KB.

Games on the marketplace run inside a sandboxed <iframe> on their own origin. The shell (the play page) listens for a small set of typed postMessage signals — this SDK sends them so you never hand-roll postMessage shapes, and it stays in lockstep with the shell's validator.

Beta: CarverJS is under active development. APIs may change between minor versions until 1.0.

Install

npm install @carverjs/embed-sdk

Usage

import { carver } from "@carverjs/embed-sdk";

carver.progress(40);                    // loading bar in the shell (0–100)
carver.ready();                         // hides the shell loader — call on first frame
carver.score(1280, "points");           // feeds player-profile stats
carver.event("level-complete", { level: 3 });
carver.requestFullscreen();             // call from a click / keypress handler
carver.error("asset-load-failed", "texture atlas 404");
carver.exit();                          // game over; hand control back to the shell

// prove which signed-in player this is, to YOUR OWN backend
const id = await carver.getIdentity();
if (id.ok) saveToMyBackend(id.token, id.userId);

// future shell -> game hints (pause / resume, etc.)
const unsubscribe = carver.subscribe((msg) => {
  if (msg.type === "carver:pause") {
    /* pause your loop */
  }
});

Every call is a safe no-op outside an iframe (for example when you open your build locally) and in non-browser environments (SSR, tests) — nothing here ever throws, so you can leave the calls in during development.

API

| Method | When to call it | | --- | --- | | ready() | First frame rendered. Required — the shell loader waits for it. | | progress(percent) | Loading progress, clamped to 0–100. | | error(code, message) | Fatal error — the shell shows an error card. | | event(name, payload?) | Gameplay events for stat aggregation. | | score(value, label?) | Score reporting (finite numbers only). | | requestFullscreen() | Ask the shell to go fullscreen (needs a user gesture). | | exit() | Game finished. | | getIdentity() | Promise — a signed token proving which signed-in player this is, for your own backend. See below. | | subscribe(handler) | Shell to game messages; returns an unsubscribe function. | | configure({ targetOrigin?, parentOrigin? }) | Optional origin pinning. | | isEmbedded() | true when running inside the marketplace shell. |

Player identity (getIdentity)

If your game keeps player data on your own backend, getIdentity() lets that backend know which marketplace user it's talking to — without the game ever handling a marketplace credential.

const id = await carver.getIdentity();
if (id.ok) {
  // id.token     — short-lived signed JWT (RS256)
  // id.userId    — stable, opaque id for THIS player in THIS game
  // id.expiresAt — Unix epoch (ms)
  await fetch("https://my-game-backend.com/save", {
    method: "POST",
    headers: { Authorization: `Bearer ${id.token}` },
    body: JSON.stringify({ progress: 12 }),
  });
} else if (id.reason === "signin-required") {
  // the player isn't signed in to the marketplace — prompt them
}

It always resolves (never throws). On failure you get { ok: false, reason } where reason is one of "signin-required", "not-embedded", "timeout", "rate-limited", or "error".

Verify the token on your server — never trust it in the browser. It's a standard RS256 JWT; verify it against the marketplace JWKS and check the claims:

// any backend / language with a JWT library — example uses `jose`
import { jwtVerify, createRemoteJWKSet } from "jose";

const JWKS = createRemoteJWKSet(
  new URL("https://www.carverjs.dev/.well-known/jwks.json"),
);

const { payload } = await jwtVerify(token, JWKS, {
  issuer: "https://www.carverjs.dev",
  audience: "<your-game-id>", // also present as payload.gameId
});
// payload.sub === id.userId — your stable key for this player

userId (the token's sub) is per-game: the same person gets a different id in a different game, so it can't be used to track players across the marketplace. It requires a real signed-in account — anonymous play sessions resolve signin-required.

Security

Outbound messages are posted with targetOrigin: "*" by default. They intentionally carry no secrets — only ready / score / progress-style signals — and a sandboxed game cannot know which shell origin embeds it (production, staging, or a local preview). Inbound messages are only delivered when their source is the direct parent window. If your game ships to exactly one shell, pin it:

carver.configure({ targetOrigin: "https://carverjs.dev" });

Links

License

MIT — MoneyTales EduTech Private Limited