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

@fuaran-ui/renderer

v0.25.0

Published

React renderer + reference CSS + per-instance custom-renderer registry for the Fuaran UI typed Node tree — a conformant TypeScript host of the language-neutral contract in fuaran-dotnet/docs/WIRE_FORMAT.md. Renders any tree @fuaran-ui/ui authors or @fuara

Readme

@fuaran-ui/renderer

The React renderer for the Fuaran UI typed Node tree — the user-visible surface of the TypeScript reference implementation. A conformant host of the language-neutral wire-format contract (fuaran-dotnet/docs/WIRE_FORMAT.md): it renders any tree @fuaran-ui/ui authors or @fuaran-ui/ops decodes, with class-name and ARIA parity to the F# reference renderer (the packaged reference CSS keys off those exact class names).

npm install @fuaran-ui/ui @fuaran-ui/ops @fuaran-ui/renderer react react-dom
import { FuaranRenderer } from '@fuaran-ui/renderer';
import '@fuaran-ui/renderer/css';
import { fuaran, node } from '@fuaran-ui/ui';

const tree = node('root', fuaran.heading({ level: 1, text: { kind: 'Literal', value: 'Hello' } }));

export const App = () => <FuaranRenderer tree={tree} />;

Custom-renderer registry

NodeKind.Custom is the bounded escape hatch. Registries are per-instance (never module-global) — the host threads one in via the runtime prop:

import {
  createCustomRendererRegistry,
  registerCustomRenderer,
  FuaranRenderer,
} from '@fuaran-ui/renderer';

const registry = createCustomRendererRegistry();
registerCustomRenderer(registry, 'charts', 'sparkline', ({ props }) => (
  <MySparkline data={props.points} />
));

<FuaranRenderer tree={tree} runtime={{ registry }} />;

Custom / Mount hardening posture — the named contract

NodeKind.Custom and NodeKind.Mount are the two surfaces that sit outside the dispatch gate structurally, so runtime.canDispatch does not reach them. Their posture is specified once, language-neutrally, in the sanitization contract — sections "What the registry scoping and ContentHash do and do NOT protect" and "The Mount boundary". A new host ports from that contract, not by reading another host's source; what follows is only how this host binds it.

ContentHash is drift detection, never authentication. The tree supplies its own hash record, so a match proves only that whoever wrote the tree knew the registered renderer's hash. Strictness is therefore a host floor a tree may only tighten, and under an enforcing floor a hash that cannot be verified — because the tree declared none, or the registry recorded none — is a refusal rather than a silent render:

<FuaranRenderer tree={tree} runtime={{ registry }} customHashFloor="StrictReplay" />

Omitting customHashFloor means 'AdvisoryWarning': a Custom node with no hash is the common legitimate case, so the default is the lenient one and enforcement is an act a host takes by name. classifyCustomHashUnder is exported as the pure join, so the rule is testable without a render.

Mount is unprivileged by default. runtime.loadGuest is the guest loader seam; the renderer's Mount arm owns the only call to it and derives the guest's privilege in the same expression, so a loader cannot construct a privileged guest context. With no runtime.guestSeam wired the guest receives a deny-all runtime — every capability refused through the host's warn, canDispatch false, no custom-renderer registry, no nested guest loading. And because channel.direction is a required wire field a decoded tree fills in itself, the channel is clamped to OutOnly before anything reads it; TwoWay is a host grant (GuestSeam.grantTwoWay) and a refused upgrade is warned, never dropped silently. The clamp is at the renderer rather than the decoder, deliberately: the decoder preserves what the wire said, so canonical round-trip and the shared conformance corpus are untouched.

A Mount in a host that wires no loader renders an inert placeholder, exactly as before.

Theme bridge

import { FuaranRenderer, defaultTheme } from '@fuaran-ui/renderer';

<FuaranRenderer tree={tree} theme={defaultTheme} />; // injects --fuaran-* vars at the root

In-page introspection (window.__fuaran)

Pass debug to register a DEBUG-only console global that exposes the running UI's typed layer — so the browser DevTools console answers "which node is this, what did this binding resolve to, where is it on screen?" instead of handing back an untyped post-projection DOM node.

// Gate on the bundler's dev flag so it never registers in a production build.
<FuaranRenderer tree={tree} sources={sources} debug={import.meta.env.DEV} />

Then, in the DevTools console of the running app:

__fuaran.help(); // method reference
__fuaran.inspectTree(); // recursive structural snapshot (every node id)
__fuaran.getNodeState('submit-btn'); // kind + bound binding slots + child ids
__fuaran.getBindingValue('counter-kpi', 'Source'); // resolved value vs the live sources
__fuaran.getRenderedDom('counter-kpi'); // live geometry (x/y/size + overflow/hidden)
__fuaran.findNodes('Button'); // ids of every node of a kind
__fuaran.apply(op); // policy-gated TreeOp mutation (default-deny)
__fuaran.getBindingState('counter-kpi', 'Source'); // as getBindingValue, tagged with the binding's identity
__fuaran.treeRevision(); // opaque token identifying the current tree state
__fuaran.subscribe((c) => console.log(c)); // committed-tree-change signal → unsubscribe fn

The global tracks the live tree + sources (it re-registers on each render), so a value read after a state change reflects the new state. Its shape is DEBUG-only and unstable (excluded from semver), and it is undefined unless debug is set — leave it on import.meta.env.DEV and a production build never registers it. buildDebugGlobal(tree, sources, options?) / registerDebugGlobal(global) are exported for hosts that want to wire the global on their own terms.

Policy-gated apply(op)

apply decodes a canonical TreeOp and applies it to the live tree — but only when the host's policy gate permits, so an in-page mutation obeys the same default-deny contract as every other dispatch path. Wire it by passing onApply (the host's "re-render with this tree" callback, typically a setState) alongside debug; the apply consults runtime.canDispatch first:

<FuaranRenderer
  tree={tree}
  runtime={runtime}
  debug={import.meta.env.DEV}
  onApply={setTree}
  validate={(candidate) => {
    const r = preEmitValidate(candidate); // @fuaran-ui/ui — optional
    return r.ok ? [] : r.error;
  }}
/>

The op may be a JSON string (paste it into the console) or a structured object — the same edit, and the host serialises the object canonically rather than asking the caller to.

apply returns a structured envelope, never a silent no-op: { ok: true, status: 'applied', treeRevision } on success; { ok: false, status: 'denied', denied: true, error } when the gate refuses (the tree is unchanged); 'decodeFailed' (carrying the codec's DecodeError) / 'rejected' (carrying the diagnostic code) for a malformed or inapplicable op; and 'unwired' when no onApply was supplied (a read-only surface). A host that omits runtime (or its canDispatch) allows by default, matching every other dispatch path.

The optional validate prop runs the host's tree validator on the candidate tree and folds the edit only when it introduces no defect the tree did not already carry — a pre-existing defect is not the edit's fault and does not block it.

Change subscription

__fuaran.subscribe(cb) reports committed tree changes and returns an unsubscribe handle. It pushes — there is nothing to poll — and rapid changes coalesce into one notification carrying the latest treeRevision, because a change is a staleness signal rather than a change log: re-read what you need. The subscription lives on a page-wide hub, so it survives the surface rebuild that every tree change causes.

const off = __fuaran.subscribe(({ treeRevision, cause }) => refresh(treeRevision, cause));
off(); // stop

treeRevision is opaque: compare it for equality to notice a cached read has gone stale; never parse or order it.

DevTools relay (relay) — off by default

relay installs a same-origin postMessage endpoint that carries the surface above across the page/extension boundary, so a browser extension (or any other same-page script) can inspect — and, where onApply is wired, edit — the live tree. It implements the [email protected] contract (DEVTOOLS_RELAY.md in the specification repository) and is pinned against that contract's fixture family.

<FuaranRenderer
  tree={tree}
  debug={import.meta.env.DEV}
  relay={import.meta.env.DEV}
  onApply={setTree}
/>

Three properties are deliberate and worth knowing before you enable it:

  • Off by default, and absent rather than inert. Without relay, no listener is installed at all, so a probe gets no answer whatsoever. Gate it on the same dev flag as debug; it is a debugging affordance, not a feature flag.
  • No side door. Every relayed mutation crosses this host's own decode → validate → policy path, in the page. The relay contributes no apply engine, no validator, and no policy — a relay client's reach is exactly the set of legal, permitted ops, and no message can widen it.
  • Origin discipline. Messages are accepted only from this same window at this same origin, replies are posted at window.origin (never "*"), and anything failing those checks is ignored in silence — a refusal would itself disclose that a Fuaran host is present.

createRelayPeer / installRelayPeer are exported for hosts wiring the peer on their own terms; both are not opted in unless told to be.

Sanitisation

Every string→DOM seam (href, dangerouslySetInnerHTML, custom attributes) routes through @fuaran-ui/renderer/sanitize. Custom-registered React components are a host trust boundary — they are expected to do their own escaping.

Apache-2.0. Part of the @fuaran-ui/* package set.