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

@agnosticui/schema

v2.0.0-alpha.1

Published

Standard Schema-validated JSON catalog for AgnosticUI SDUI

Readme

@agnosticui/schema

Schema definitions, Zod validation, and code generation for the AgnosticUI Server-Driven UI (SDUI) system.

What is SDUI?

Server-Driven UI lets a server (or an LLM) describe a UI as a JSON node tree instead of sending HTML or framework-specific markup. The client receives that tree and renders it using its own component library — so the same JSON drives React, Vue, and Lit renderers with no client-side logic changes.

AgnosticUI uses this to let AI assistants generate valid, safe UI by emitting AgNode[] arrays. The schema package enforces what is valid, and the renderer packages turn it into real components.

Code flow

v2/lib/src/components/*/core/_*.ts   (source of truth: TypeScript interfaces)
         |
         | npm run codegen  (from v2/sdui/schema/)
         v
v2/sdui/schema/src/types.ts        AgNode union type + per-component interfaces
v2/sdui/schema/src/schema.ts       Zod discriminated-union schema for every component
v2/sdui/schema/src/index.ts        Re-exports types, schemas, and validate()
         |
         | same codegen run
         v
v2/sdui/renderers/react/src/AgDynamicRenderer.tsx
v2/sdui/renderers/vue/src/AgDynamicRenderer.ts
v2/sdui/renderers/lit/src/AgDynamicRenderer.ts

All six files are AUTO-GENERATED. Never edit them by hand. Edit the core interfaces or codegen.config.ts and re-run codegen.

Running codegen

cd v2/sdui/schema
npm run codegen

This reads every v2/lib/src/components/*/core/_*.ts file via ts-morph, extracts the *Props interface for each component, and regenerates all six output files.

Checking for drift

cd v2/sdui/schema
npm run check-codegen

Generates all six files in memory and diffs them against the committed versions. Exits non-zero with a diff and exact git add instructions if anything is out of sync. CI runs this automatically on any PR touching v2/lib/src/components/**, v2/sdui/schema/**, or v2/sdui/renderers/**.

Package contents

src/types.ts

One Ag*Node interface per component, plus the AgNode discriminated union and AgComponentName type. Each interface has id, component (literal string), optional props derived from the core interface, and children?: string[] for nesting.

src/schema.ts

One Ag*Schema Zod object per component, plus AgNodeSchema — a z.discriminatedUnion('component', [...]) that validates any node against the correct component schema. Compatible with the Standard Schema spec.

src/validate.ts

import { validate } from '@agnosticui/schema';

const result = validate(unknownNode);
if (result.success) {
  // result.data is a typed AgNode
} else {
  // result.errors is string[] with "field.path: message" entries
}

validate() wraps AgNodeSchema.safeParse() and normalizes Zod errors into human-readable strings. Use it at the boundary where untrusted JSON enters your system (API response, LLM tool call output, etc.).

Renderer packages

Each renderer consumes a flat AgNode[] and an optional actions map:

// React
<AgDynamicRenderer nodes={nodes} actions={actions} />

// Vue
<AgDynamicRenderer :nodes="nodes" :actions="actions" />

// Lit
const el = document.querySelector('ag-dynamic-renderer');
el.nodes = nodes;
el.actions = actions;

Nodes not referenced as children by any other node are treated as roots. Child references are resolved from a Map<id, AgNode> built at render time, so the array can be in any order.

The actions map and XSS boundary

Function props (onClick, onChange, etc.) are replaced in the schema with optional string aliases (on_click, on_change, etc.). The renderer dispatches them against the caller-supplied map:

onClick={() => dispatch(node.on_click, actions)}

function dispatch(alias: string | undefined, actions: Actions): void {
  if (alias) actions[alias]?.();
}

Only keys present in the actions map are ever called. An unknown alias is silently ignored. There is no eval(), no dynamic code execution, and no way for a JSON node to inject arbitrary behavior. The caller controls the full set of callable functions at mount time.

codegen.config.ts

Four configuration objects control what codegen emits.

omitConfig

Non-function props to exclude per component. Only needed for props that are runtime-stateful and cannot be expressed as a static JSON value (for example toggle/pressed require JS to track state; indeterminate is a tri-state that must be set programmatically). Do not list function-typed props here; they are auto-detected.

// Adding a new runtime-state prop to omit:
AgMyComponent: ['myStatefulProp'],

actionAliasMap

Maps function prop names to their SDUI string alias. Props in this map get an optional string prop in the schema instead of being dropped. Props not in this map are silently dropped (low-level events like onFocus and onBlur are not SDUI-actionable).

onMyEvent: 'on_my_event',

rendererSlotConfig

Controls how each component renders its content slot:

| Value | Behavior | |---|---| | 'none' (default) | Self-contained, no slot content emitted | | 'label-child' | Renders node.label as text content (used by Button, ButtonFx) | | 'children' | Renders child nodes recursively (used by Alert, Card, Fieldset, etc.) |

skipComponents

Component directory names excluded from all generated output. These require controlled runtime state that cannot be expressed in a static node tree (Dialog, Drawer, Toast, Slider, etc.). See issue #375 for the stateful SDUI design discussion.

typeOverrides

Explicit TS type and Zod expression overrides for specific props. Use when the auto-detected type from the source interface is not what the SDUI schema should expose.

// AgInput.type excludes 'textarea' because textarea is a separate SDUI concern:
AgInput: {
  type: {
    tsType: "'text' | 'password' | 'email' | ...",
    zodExpr: "z.enum(['text', 'password', 'email', ...])",
  },
},

M4 LLM test harness

"M4" is shorthand for claude-sonnet-4 (the Claude 4 model family used in this test). m4-llm-test.ts is a smoke test that sends the render_ui tool definition to Claude and validates every returned node against the schema. It confirms that Claude does not hallucinate props and produces structurally correct output.

# Live run (requires an API key from console.anthropic.com)
ANTHROPIC_API_KEY=sk-... npm run m4

# Dry run (validates a hardcoded representative payload, no network needed)
npm run m4:dry

Claude Pro / Claude Code OAuth credentials do not work here. The Anthropic API requires a separate key (free tier available at console.anthropic.com, no paid plan required for low-volume testing).

Standard Schema compliance

AgNodeSchema is a Zod v3 schema, which implements the Standard Schema v1 interface via the ~standard property. This means it integrates with any framework adapter that accepts Standard Schema without an additional adapter layer.