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

@screenplay.space/state

v0.1.2

Published

Mirror a prototype's UI state up to the screenplay canvas as a read-only synced view alongside the artboard.

Readme

@screenplay.space/state

Bridge a prototype's UI state to the parent screenplay canvas so it syncs across every viewer in the room. The canvas persists the merged state per artboard via Yjs and pushes it back down into every connected client's iframe — change currentUser in one viewer's prototype and every other viewer's prototype updates too.

The canvas itself doesn't render an editor for shared state today; it shows a tiny indicator on the route pill with the JSON in a tooltip. That's deliberate — direct editing from the canvas may come later.

The package is dev-only by design. In any build with NODE_ENV set to anything other than "development", every API on this module is a no-op and the postMessage paths are dead-code-eliminated by the bundler. Nothing ever leaves the page, no inbound listener is attached, no remote setter is ever invoked. Shipping useSharedState calls to production is safe.

Install

npm install --save @screenplay.space/state

react >= 17 is a peer dependency.

Use

Bidirectional — wire existing UI state both ways:

import { useSharedState } from "@screenplay.space/state"

function Counter() {
  const [count, setCount] = useState(0)
  useSharedState("count", count, setCount)
  return <button onClick={() => setCount((c) => c + 1)}>{count}</button>
}

Now bumping count in one client's iframe shows up in every other client's iframe in the same room.

Publish-only — derived snapshot, no remote write-back:

const user = useUser()
useSharedState("user", user ? { id: user.id, role: user.role } : null)

Stable, descriptive keys persist across reloads. Rename a key and the canvas drops the old entry; treat it like an id.

What syncs

| Aspect | Behavior | | --------------------------- | -------------------------------------------------------------- | | Local change → other clients | Yes (via canvas + Yjs) | | Other clients → local | Yes when you pass setter (3-arg form) | | Canvas user editing | No editor UI yet — read-only at the canvas surface | | Persistence across reloads | Yes — state lives on the artboard until cleared | | Cross into prototype player | Yes — same protocol, same room, same Yjs |

Non-React API

import {
  setSharedState,
  subscribeSharedState,
  clearSharedState,
} from "@screenplay.space/state"

const remove = setSharedState("session", { id: "...", role: "admin" })

const unsubscribe = subscribeSharedState("session", (next) => {
  console.log("session changed", next)
})

// later
remove()
unsubscribe()
// or drop a key without holding the remover:
clearSharedState("session")

What's published

Values are JSON-serialized before being posted to the canvas. Functions, class instances, BigInt (throws — caught and dropped), undefined, and circular references are stripped or the entry is skipped. The combined payload is capped at 64 KB; oversize updates log a warning and are not published.

Production safety

The module gates everything on process.env.NODE_ENV === "development" plus window.parent !== window, evaluated once at load time. In any non-development build — production, test, or a no-bundler load where NODE_ENV isn't defined — the publish path never runs, the in-memory map is never written to, and the inbound message listener is never installed. Bundlers (Next, Vite, esbuild, webpack) statically inline process.env.NODE_ENV, so a production bundle dead-code-eliminates those branches entirely; the package contributes effectively zero runtime to a prod build.

That's the production-safety story: a prototype that ships useSharedState calls in committed code, then gets iframed by some non-screenplay parent in production, cannot have its state read or written via this protocol — none of the protocol is wired up at all.

In development, the legacy gate (window.parent !== window) still applies. A prototype rendered standalone never publishes; only when hosted by a real screenplay canvas does the bridge come alive.

Releasing

Publishing is automated via the Publish @screenplay.space/state workflow in GitHub Actions. Open the Actions tab, pick that workflow, and click Run workflow — same flow as the knobs package.

License

MIT