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

@firsthandjs/devtools

v0.12.2

Published

Experimental devtools for Firsthand: see what updates what, why it ran, and when.

Readme

@firsthandjs/devtools

Experimental. This package is new and its shape is still moving. The names, the returned structures and the panel will change without a major version while that is true, and nothing else in the framework depends on it.

See which signal updates which DOM node, what depends on what, and why an effect ran.

npm install --save-dev @firsthandjs/devtools
// main.tsx, above everything else
import { attach } from '@firsthandjs/devtools';

if (import.meta.env.DEV) {
  attach();
}

Then, in the browser:

Press Ctrl+Shift+F, or:

__FIRSTHAND__.panel(); // a panel: pick an element, see what writes it and why

Or from the console, on whatever the Elements panel has selected — no import, because a console cannot resolve a bare specifier:

__FIRSTHAND__.chain($0);
__FIRSTHAND__.causeOf($0);
__FIRSTHAND__.queries();

And from a test or a module, where the types apply:

import { chain, inspect, causeOf } from '@firsthandjs/devtools';

chain(document.querySelector('button'));
// order.ts:12:19
//    ↓
// computed(isEditable)
//    ↓
// button.disabled

causeOf(document.querySelector('button'));
// 'order.ts:12:19' — what changed to make it run

inspect(node) returns the same thing as data: each part that writes the node, with its dependencies and their dependencies, as far down as you ask. cells() lists every computed and effect currently alive.

What it costs

The framework's side of it ships nothing: the hooks this reads live in modules the production build replaces with empty functions, so a shipped bundle contains neither that code nor its strings, whether or not you use devtools.

This package is not stripped, though — it is an ordinary module, so an unconditional import puts it in your production bundle. Guard it if that matters:

if (import.meta.env.DEV) {
  const { attach } = await import('@firsthandjs/devtools');
  attach();
}

An import pulls in 1.96 kB gzip; the panel is a further 4.83 kB, loaded when it is opened.

In development it records nothing until attach() is called, because naming every cell costs a WeakMap write and a hundred thousand rows would feel it.

How it works

It does not instrument anything. The reactive graph is already there, because propagation and disposal need it: every cell carries its dependencies and its subscribers, and every owner carries its children. This package attaches names to those nodes and reads the structure when asked.

Documentation: guide · API reference · ADR-0020 for the reasoning.

Licence

MIT