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

@aihu/arbor

v0.1.4

Published

Reactive component tree (the rendering layer that consumes @aihu/signals).

Readme

@aihu/arbor

Aihu — agentic discovery and interaction, for human purpose.

Reactive component tree (the rendering layer that consumes @aihu/signals).

Part of the runtime core layer of the Aihu meta-framework. Shipped to the client; sized via bun run size. The runtime core is dep-free and stacks under @aihu/runtime@aihu/router@aihu/server@aihu/app.

DOM materialization primitives for the aihu project. Build a tree of branch / leaf nodes, hand it to mount, and the renderer wires every reactive binding once and tears it down LIFO when the scope disposes. No JSX runtime, no virtual DOM, no scheduler queue — just direct DOM operations against the live nodes.

Status: v0 surface frozen (Phase 3). Spec: .team/phase-3/spec-arbor.md. Bundle ≤ 2 kB gzipped.

Hello mount

import { signal } from '@aihu/signals'
import { branch, leaf, mount } from '@aihu/arbor'

const [count, setCount] = signal(0)

const tree = branch('div', { class: 'counter' }, [
  leaf('Count: '),
  leaf([count, setCount]),    // signal as text content (reactive)
  branch('button', { onClick: () => setCount(c => c + 1) }, [
    leaf('+1'),
  ]),
])

const scope = mount(tree, document.body)
// later…
scope.dispose()  // removes the DOM and tears down every binding LIFO

API

branch(tag, attrs?, children?): Branch

Constructs a branch node. tag is the element tag name, or null for a fragment (children mount directly into the parent).

branch('section', { id: 'main' }, [ leaf('hi') ])
branch(null, null, [ leaf('a'), leaf('b') ])  // fragment

leaf(value): Leaf / leaf.element(tag, attrs?): Leaf

Two shapes share one symbol:

  • leaf(value) — text leaf. value is string (static) or a Signal<string> tuple (reactive).
  • leaf.element(tag, attrs?) — terminal element leaf for <img>, <br>, <input>, <hr>, etc.
leaf('hi')                     // static text
leaf([greeting, setGreeting])  // reactive text
leaf.element('img', { src: '/logo.png' })
leaf.element('hr')

mount(node, host): MountScope

Materializes node into host (an Element or ShadowRoot) synchronously. By the time mount returns, every reactive binding has run once and subscribed to its signal, every static attr is applied, and every DOM node is appended.

const scope = mount(tree, document.querySelector('#app')!)
scope.dispose()         // synchronous teardown, idempotent
scope.agent             // sub-project #7 stub (don't use in v0)
scope.serialize()       // throws ArborNotImplementedError in v0

Attribute semantics

Inside attrs, each [key, value] pair is dispatched at mount time:

| Detection | Treatment | |---|---| | key.startsWith('on') AND value is a function | el.addEventListener(key.slice(2).toLowerCase(), value) | | Array.isArray(value) (a Signal tuple [Read, Write]) | Wired through an effect — the DOM property/attribute tracks the signal. | | string / number / boolean | Static. Set once at mount; never re-applied. |

Property vs attribute split: if key in el (e.g. disabled, value, className), the value is set as a DOM property; otherwise setAttribute(key, String(value)). See .team/phase-3/spec-arbor.md §2.4.

Trust boundary. attrs is the renderer's trust boundary. The compiler is responsible for never emitting attacker-controllable keys. If you call branch() / leaf.element() from hand-written code with user-controlled data, do not let user data flow into attribute keys — keys like innerHTML, srcdoc, outerHTML are real DOM properties and the runtime will assign them directly. Allow-list known-safe keys at your boundary.

Disposal

MountScope.dispose() runs LIFO: deepest/latest effects first (so parent effects don't re-run against partially-cleaned children), then DOM root removal. Idempotent — calling twice is a no-op.

const a = mount(treeA, host)
const b = mount(treeB, host)
b.dispose()   // tears down b's effects then removes its roots
a.dispose()   // independent

Coming in v1 (today: stubs that throw)

import { when, each } from '@aihu/arbor'
when(condition, () => branch(...))                     // ArborNotImplementedError in v0
each(list, item => item.id, item => branch(...))      // ArborNotImplementedError in v0

The signatures are locked; the v1 reconciler will swap the bodies.

Pairing with non-@aihu/signals reactive systems

Arbor only requires the signal shape: a tuple readonly [Read<T>, Write<T>] where Read<T> = () => T. Anything that exposes that shape works. The runtime detects it via Array.isArray(value) (per the Deviation #11 invariant).

Tests

bunx vitest run packages/arbor

Includes an arbor microbench (tests/bench.test.ts) that mounts 10K static-leaf nodes in JSDOM.

Install

npm install @aihu/arbor
# or
bun add @aihu/arbor

Auto-generated against @aihu/[email protected].

Package facts

| | | |---|---| | Version | 0.1.3 | | Tier | A — Reactive runtime core — DOM materialization layer | | Bundle size | 2.66 kB (gz) — limit 2800 B | | Published files | 3 entries | | License | MIT |

Auto-generated against @aihu/[email protected].

Exports

| Subpath | ESM | CJS | |---|---|---| | . | ./dist/index.js | |

Auto-generated against @aihu/[email protected].

Dependencies

Dependencies:

  • @aihu/signalsworkspace:*

Auto-generated against @aihu/[email protected].

See also

Auto-generated against @aihu/[email protected].

License

MIT — see LICENSE.

Auto-generated against @aihu/[email protected].