Skip to content

GenUI

Parser, validator, and store

The pieces GenUI is built from, for validating on a server, testing prompts, or building your own renderer.

Parse a partial response

parsePartialJson parses any prefix of a JSON document into the best complete tree. Open strings, arrays, and objects are closed, a key without a value is dropped, and half-written escapes, numbers, and literals are not guessed at. It never throws.

Nesting deeper than 100 levels and text beyond 2 MB are cut off, numbers beyond the double range become null, lone surrogates are repaired, and __proto__ is an ordinary key.

Exampletsx
import { parsePartialJson } from "@comp0/genui";

const parsed = parsePartialJson('{"type": "Stack", "children": [{"type": "Text", "text": "Hel');
parsed.value; // { type: "Stack", children: [{ type: "Text", text: "Hel" }] }
parsed.open; // Set { "", "/children", "/children/0", "/children/0/text" }
parsed.complete; // false
parsePartialJson options
PropTypeDescription
finalbooleanThe text will not grow: a number that ends it is complete, and trailing content is reported. Defaults to false.
PartialParse
FieldTypeDescription
valueJsonValue | undefinedThe best tree for the text so far; undefined while no value has started.
completebooleanThe document is a closed value with nothing but whitespace after it.
openReadonlySet<string>JSON Pointers of the values that may still grow. The root is the empty string.
issuesreadonly ParseIssue[]Syntax the parser cannot recover from, with path, message, and offset. Everything before an issue is still returned.

Validate on a server

validateResponse takes text, a PartialParse, or an object, and returns the component tree to render with every node keyed by its JSON Pointer, the errors, and the declared bindings. validateNode does the same for one finished object.

Exampletsx
import { formatErrors, validateResponse } from "@comp0/genui";

const { errors } = validateResponse(text, { complete: true });
if (errors.length > 0) await askAgain(formatErrors(errors));
validateResponse options
PropTypeDescription
catalogreadonly CatalogEntry[]The components a response may use. Defaults to the built-in catalog.
completebooleanWhether the response is finished. While it is not, nothing is reported. Defaults to true.
knownIterable<string>Names that already have a value, such as the keys of a saved state; expressions may read them.
ValidatedResponse
FieldTypeDescription
rootValidNode | undefinedThe component tree to render: each ValidNode has its key, catalog entry, and surviving props.
errorsGenUIError[]Problems to send back to the model; always empty while the response streams.
completebooleanWhether the response was finished when it was validated.
bindingsReadonlyMap<string, JsonValue | undefined>Every binding name the response declares, with the first initial value written for it.
  • Validated props hold a Binding (with name and initial) for every $bind and a Computed (with the parsed expression and its source) for every $expr.

Evaluate an expression

parseExpression returns { ok: true, expression } with the names the expression reads, or { ok: false, message, position }. evaluateExpression takes the source or a parsed expression and the values by name, as an object or a function. expressionFunctions lists the functions with their summaries.

Exampletsx
import { evaluateExpression, parseExpression } from "@comp0/genui";

evaluateExpression("round(seats * 1.5, 2)", { seats: 3 }); // 4.5
parseExpression("seats *"); // { ok: false, message, position }

Keep state in a store

createGenUIStore(initialState?) is the external store the controls and results read with useSyncExternalStore, so a change re-renders exactly the parts that read it. Entries of the initial state that are not JSON values are ignored.

GenUIStore
MethodTypeDescription
get(key: string) => JsonValue | undefinedThe stored value, or undefined when nothing is stored under the key.
getSnapshot() => GenUIStateEvery stored value; the same object until something changes.
set(key: string, value: JsonValue | undefined) => voidStores a value as an edit. undefined removes the key.
seed(key: string, value: JsonValue) => voidStores a default only when nothing is stored under the key. It notifies readers but is not an edit.
subscribe(listener: () => void) => () => voidCalls listener after every change, including seeds.
subscribeToEdits(listener: (state: GenUIState) => void) => () => voidCalls listener with a snapshot after every edit.

Helpers

Small functions the renderer uses, exported for hosts that build around it.

  • formatErrors(errors) writes the repair message for a list of GenUIErrors.
  • describeFormValues(fields, values) describes form values in the words on the screen: Plan: Pro; Seats: 5.
  • safeHref(value) returns a link target that is safe to use, or undefined; isSafeHref(value) says whether it is.
  • safeImageSrc(value) returns an image source that is safe to load, or undefined.
  • describeCatalog(catalog?) describes a catalog as plain JSON: every component with its group, description, and props.