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

amanita

v0.4.0

Published

A magical web components framework enabling effortless communication via a pub/sub system, like a mycelium network for your components.

Readme

🍄 Amanita.js: The Magical Web Components Framework

Ever wished your web components could talk to each other like mushrooms in a forest's underground network? Meet Amanita.js, the framework that turns your components into a thriving ecosystem of interconnected elements!

What's the Magic? 🪄

Amanita.js is a tiny (~700 lines), zero-dependency mycelium network for your web components — it lets them communicate effortlessly through a pub/sub system. Just like mushrooms share nutrients underground, your components share data through named topics, addressed by simple, intuitive refs.

Components never hold references to each other; they only publish topics about themselves and subscribe to topics by ref. That decoupling is the whole idea — and it's what lets a component teleport its logic to a Web Worker or even to a server without changing a single line of the components that talk to it. Just wrap it in a scheduler and add server="true", and poof — your worker component is now running on the server, still speaking the same topics.

Amanita is the wiring, not the view layer: no templating, no virtual DOM. You render with plain DOM (or pair it with a templating library); Amanita connects everything together. That's why it drops cleanly onto whatever you're already using.

Features That'll Get You Hooked 🎣

  • 🔌 Plug-and-play pub/sub system, where every topic is a retained behavior-value (late subscribers get the current value automatically)
  • 🎯 Simple, structural ref-based targeting (/store/items, ../route, ..my-app/feed/chunk, @click)
  • 🌐 Seamless, decoupled component communication — state flows down as topics, intent flows up as bubbling events
  • 🚀 Easy Web Worker offloading (for heavy compute or OffscreenCanvas drawing)
  • Server-side execution with one attribute (server="true")
  • 🧠 Run whole component trees server-side via jsdom — a declarative actor system, UI or not
  • 🧙 Transparent worker/server execution — the same code, three places

Don't let your components live in isolation — let them join the fungal... err, functional revolution with Amanita.js!

Warning: Unlike its namesake, this framework won't make you hallucinate. But it might make you see web components in a whole new light! 🍄✨

Install

npm install amanita
import A from "amanita"        // the A(HTMLElement) mixin + A.define
import "amanita/stdlib"        // optional: a-var, a-switch, a-url, a-match, … (declarative helpers)

No build step is required — Amanita is plain ES modules and works in the browser via an import map too.

Quick Start 🚀

Let's build a temperature converter: one component publishes the temperature, and others subscribe to convert and display it.

<temp-input name="source">
  <input type="range" min="0" max="100" value="20">
</temp-input>

<!-- Two subscribers, same topic, different units -->
<temp-display src="/source/celsius" unit="°F"></temp-display>
<temp-display src="/source/celsius" unit="K"></temp-display>
import A from "amanita"

// Publisher
class TempInput extends A(HTMLElement) {
  onConnect() {
    this.input = this.querySelector("input")
    this.pub("celsius", Number(this.input.value)) // publish the starting value
  }

  // A class FIELD whose name is a ref is auto-subscribed on connect.
  // Here: the `input` DOM event of our child <input>.
  // ⚠️ It MUST be an arrow-function field — `"...": e => {}` works, a method `"..."(e){}` does NOT.
  "input/@input" = e => this.pub("celsius", Number(e.target.value))
}
A.define("temp-input", TempInput)

// Subscriber
class TempDisplay extends A(HTMLElement) {
  onConnect() {
    this.sub(this.attr("src"), this.show) // subscribe to the ref in our `src` attribute
  }

  show = celsius => {
    const unit = this.attr("unit")
    this.textContent = `${this.convert(celsius, unit).toFixed(1)} ${unit}`
  }

  convert(celsius, unit) {
    if (unit === "°F") return celsius * 9 / 5 + 32
    if (unit === "K") return celsius + 273.15
    return celsius
  }
}
A.define("temp-display", TempDisplay)

Move the slider and both displays update. Neither display knows the input exists — they only know the topic /source/celsius. Because topics are retained, a display that mounts after the input already published still gets the current value, and sub() retries until the ref resolves, so declaration order doesn't matter.

For the <input> to be found by the ref input/@input, give it a name (<input name="input">) — a bare ref step matches the name attribute. The Quickstart guide walks through every line and the alternatives.

Now that's what we call a magic mushroom network! 🍄

Documentation 📚

Full docs live in docs/, in three layers:

License

MIT