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

@hyperfixi/reactivity

v2.5.0

Published

Reactive features for hyperfixi — adds `live`, `bind`, `when X changes`, and `^name` DOM-scoped vars (upstream _hyperscript 0.9.90).

Readme

@hyperfixi/reactivity

Signal-based reactive features for hyperfixi. Adds four constructs from upstream _hyperscript 0.9.90:

| Construct | Purpose | | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | live [commandList] end | Reactive block. Body re-runs whenever any tracked read changes. | | when <expr> [or <expr>]* changes [commandList] end | Observer. Body fires when any watched expression's value changes (Object.is semantics). | | bind <var> [to\|and\|with] <element> | Two-way DOM ⇄ var binding with auto-detected property by element type. | | ^name [on <target>] | DOM-scoped inherited variable. Reads walk up the parent chain; writes hit the nearest defining ancestor. |

Install

import { createRuntime, installPlugin } from '@hyperfixi/core';
import { reactivityPlugin } from '@hyperfixi/reactivity';

const runtime = createRuntime();
installPlugin(runtime, reactivityPlugin);

Re-installing is a no-op (the plugin guards on parserExtensions.hasFeature('live')), so it is safe to call from HMR boundaries.

live

live
  set $total to $price * $quantity
end

The body is one effect; its dependency set is the union of every read performed during execution. Writes to any tracked dependency schedule a re-run on the next microtask flush.

hx-live bridge

When this plugin is installed, @hyperfixi/core's htmx-compat layer also recognizes the htmx v4 hx-live attribute and translates it to a live ... end block. The attribute value is hyperscript syntax — not JavaScript like upstream htmx v4 — so it reads naturally and inherits hyperscript's multilingual support:

<!-- English -->
<div hx-live="put $count into me"></div>

<!-- Japanese (within <section lang="ja"> once Phase 8 lands) -->
<div hx-live="$count を me に 入れる"></div>

If reactivity isn't installed at the moment an hx-live element is processed, a clear console error is logged and the element is skipped — other htmx attributes on the same element still wire up normally. The hyperfixi-hx-v4.js bundle (in @hyperfixi/core/dist/) auto-installs reactivity so this gate is invisible to users of that bundle.

when ... changes

when $message changes
  put it into me
end

Each watched expression becomes its own effect. The body runs with it and result bound to the new value. Multiple expressions can be observed in one feature using or:

when $a or $b changes
  log 'a or b changed'
end

Init behavior: the body fires once on initial evaluation if the watched expression has a non-null/non-undefined value, then on every subsequent change (Object.is semantics). If you need pure change-only semantics, gate the body on a sentinel:

when $x changes
  if not :primed
    set :primed to true
    exit
  end
  log 'x actually changed'
end

bind

bind $greeting to me

Two effects are created (registration order is load-bearing):

  1. DOM → var — reads the auto-detected property, writes the var. DOM "wins" on init.
  2. var → DOM — reads the var, writes the DOM property. Fires on programmatic var writes after init.

Auto-detected property by element type:

| Element | Property | | ----------------------------------- | --------------- | | <input type="checkbox\|radio"> | checked | | <input type="number\|range"> | valueAsNumber | | <input>, <textarea>, <select> | value | | [contenteditable="true"] | textContent | | Custom elements with own value | value |

Explicit property

Bind to a specific property using either possessive or member-access syntax:

bind $color to #picker's value     -- possessive (preferred — reads in any language)
bind $color to #picker.value       -- dot syntax (JS-style alternative)
bind $text to #div's textContent   -- non-form properties: var→DOM only

For form-like elements (<input>, <textarea>, <select>, [contenteditable=true]) both directions work. For non-form elements (e.g., binding a <div>'s textContent), only var→DOM fires — there are no input/change events to drive DOM→var, so user mutations of the property via devtools won't propagate back. Enable debug logging to see a one-time warning when this happens.

Chained property access (bind $x to #el's dataset.value) is not supported in v1 — only one level deep.

Both $globals and :locals are accepted on the var side. Local writes propagate through the localWriteHook keyed off context.me, so a set :foo to ... from any handler running on the same me element will update the bound DOM property.

^name — DOM-scoped variables

-- on a parent element
set ^count to 0

-- on a descendant button
on click increment ^count

Reads walk up from me (or the element passed via on <target>), tracking each element visited so writes at any ancestor notify dependent effects. The walk stops at any [dom-scope="isolated"] boundary that doesn't itself define the var, allowing nested components to hold independent state.

Debug logging

Set localStorage.setItem('hyperfixi:debug', '*') in the browser console to surface effect errors that would otherwise be swallowed. The reactive scheduler is intentionally tolerant of expression/handler exceptions to avoid breaking the microtask flush; debug mode logs them via console.warn.

API exports

  • reactivityPlugin (default export): the HyperfixiPlugin to pass to installPlugin.
  • reactive: the singleton Reactive instance. Useful for explicit teardown via reactive.stopElementEffects(el), or for direct caret-var read/write outside hyperscript code.
  • Types: CaretVarNode, LiveFeatureNode, WhenFeatureNode, BindFeatureNode.

License

MIT