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

@botfather/units

v2.0.1

Published

Lightweight DSL for building interactive UIs.

Readme

@botfather/units

The core library for the Units DSL — a lightweight, dependency-free declarative language for describing UI trees in .ui files.

What is it?

Units provides a minimal syntax for authoring UIs that is significantly terser than JSX, making it well-suited for code generation (e.g. LLM output) and human editing alike. A .ui file is parsed into a plain JSON AST, which can then be rendered by the included React renderer or any custom host framework via the custom renderer API.

Key characteristics:

  • O(n) single-pass parser — no external parsing dependencies
  • Render-agnostic — React is opt-in; plug in any framework
  • Canonical formatter for consistent diffs
  • Incremental parsing helpers for editor integrations

Installation

npm install @botfather/units
# react is a peer dependency for the React renderer
npm install react

The .ui syntax

TagName (prop:'value', expr=@scopeVar, visible?=@bool) {
  text 'Literal @{interpolated}'
  @rawExpr
  #if (@cond) { ... }
  #for (item, i in @list) {
    #key (@item.id)
    ChildTag (...)
  }
  #slot (slotName)
}

| Syntax | Meaning | |---|---| | Tag (props) { children } | Element node | | key:'string' / key:123 / key:true | Static prop value | | key=@expr | Dynamic expression prop | | key?=@expr | Conditional boolean prop (only set when truthy) | | !click { handler(@event) } | Event handler shorthand | | on:click={ handler(@event) } | Event handler long form | | text 'literal' | Text node (supports @{...} interpolation) | | @expr | Inline expression node | | #if (@cond) { } | Conditional directive | | #for (item, i in @list) { } | Loop with index | | #for item in @list { } | Compact loop | | #slot (name) | Named slot | | #key (@expr) | Key directive for the next tag | | // comment | Line comment |

API

Parsing

import { parseUnits } from "@botfather/units/parser";

const ast = parseUnits(`
  Button (variant:'primary') {
    text 'Click me'
  }
`);
// → { type: 'document', body: [...] }

Throws a descriptive error with source offset and snippet on syntax errors.

Formatting

import { formatUnits } from "@botfather/units/print";

const canonical = formatUnits(source);

Line width defaults to 100. Override with the UNITS_PRINT_WIDTH environment variable.

React renderer

import { renderUnits } from "@botfather/units/runtime";
import uiAst from "./app.ui"; // via vite-plugin-units

const element = renderUnits(uiAst, scope, {
  components: { Button: MyButtonComponent },
  slots: { default: <span>content</span> },
  set: (key, value) => setState(s => ({ ...s, [key]: value })),
});

| Option | Type | Description | |---|---|---| | components | Record<string, Component> | Tag name → React component mapping | | slots | Record<string, ReactNode \| fn> | Named slot content | | set | (key, value) => void | Mutation handler for set() expressions | | evalExpr | (expr, scope) => any | Custom expression evaluator |

Custom (non-React) renderer

import { createUnitsRenderer } from "@botfather/units/custom-renderer";

const renderer = createUnitsRenderer({
  element(name, props, events, children) { /* create node */ },
  text(value) { /* create text node */ },
  fragment(children) { /* group children */ },
});

const node = renderer.render(ast, scope, options);

Custom renderer directive behavior mirrors runtime semantics for #if/#elif/#else and #for/#key.

Expression evaluator

import { createUnitsEvaluator } from "@botfather/units/runtime";

const evalExpr = createUnitsEvaluator();
const result = evalExpr("@items.length > 0", scope);

Expressions use @ as the scope accessor prefix. The evaluator caches compiled Function objects per expression string, and scope-prefix normalization preserves literal @ characters inside quoted strings.

Incremental parsing

import {
  findChangedRange,
  findSmallestEnclosingNode,
  incrementalParse,
} from "@botfather/units/incremental";

const range = findChangedRange(prevSource, nextSource);
const node = findSmallestEnclosingNode(ast, range.start, range.endPrev);
const newAst = incrementalParse(prevAst, prevSource, nextSource);

Useful for editor integrations. incrementalParse attempts append-only and smallest-enclosing-node reparses first, then safely falls back to a full re-parse when needed.

Tree IR adapters

import {
  normalizeDomTree,
  normalizeA11yTree,
  serializeAgentTree,
} from "@botfather/units/tree-ir";

const domIr = normalizeDomTree(rawDomLikeTree);
const a11yIr = normalizeA11yTree(rawAccessibilityTree);
const compact = serializeAgentTree(domIr);

serializeAgentTree applies token-oriented defaults (dedupes redundant name/text pairs, omits role-implicit actions like click on buttons, prunes empty text leaves, and omits text-node ids). Pass options such as includeRedundantNameText: true, includeImplicitActions: true, or includeTextIds: true to keep those fields.

Transform programs

import {
  compileTransformProgram,
  runTransformProgram,
} from "@botfather/units/transform";

const program = compileTransformProgram(programSource);
const result = runTransformProgram(program, irTree, { task: "summarize" });

Transform programs use .ui syntax with a Program (kind:'transform') root and reserved transform tags (Rule, Filter, Merge, Pass) in transform mode.

Reward + verifier

import { scoreProgram, verifyProgram } from "@botfather/units/reward";

const score = scoreProgram({ inputTree, outputTree, expectations });
const verification = verifyProgram(score, {
  action_recall: 1,
  name_recall: 0.98,
  text_f1: 0.95,
});

Verified library + synthesis

import {
  createVerifiedProgramMetadata,
  writeVerifiedProgram,
} from "@botfather/units/library";
import { runSynthesisLoop } from "@botfather/units/synthesis";

TypeScript types for Vite imports

Add /// <reference types="@botfather/units/ui" /> (or import from @botfather/units/ui) to get types for .ui file imports:

import { ast, source } from "./app.ui";
import formatted from "./app.ui?format";
import tokens from "./app.ui?tokens";
import html from "./app.ui?highlight";

Package exports

| Export path | Description | |---|---| | @botfather/units | Re-exports everything | | @botfather/units/parser | parseUnits | | @botfather/units/print | formatUnits | | @botfather/units/runtime | renderUnits, createUnitsEvaluator | | @botfather/units/custom-renderer | createUnitsRenderer | | @botfather/units/incremental | Incremental parse helpers | | @botfather/units/tree-ir | DOM/AX normalization + compact serialization | | @botfather/units/transform | Transform program compiler + runtime | | @botfather/units/reward | Reward scoring + verifier gates | | @botfather/units/library | Verified program library helpers | | @botfather/units/synthesis | Iterative synthesis loop helpers | | @botfather/units/ui | Type declarations for .ui file imports |

License

MIT