RuntimePlan IR Reference
The RuntimePlan is the intermediate representation (IR) at the heart of Renderify. Every piece of UI that Renderify renders — whether generated by an LLM, authored by hand, or loaded from a file — is expressed as a RuntimePlan.
RuntimePlan Structure
interface RuntimePlan {
specVersion?: string; // Protocol version, e.g. "runtime-plan/v1"
id: string; // Unique plan identifier
version: number; // Positive integer, incremented on updates
root: RuntimeNode; // The UI tree
capabilities: RuntimeCapabilities; // Execution permissions and limits
state?: RuntimeStateModel; // Optional reactive state
imports?: string[]; // Module specifiers to pre-load
moduleManifest?: RuntimeModuleManifest; // Resolved module URLs
source?: RuntimeSourceModule; // Optional executable source code
metadata?: RuntimePlanMetadata; // Provenance and tags
}Required Fields
id— non-empty string identifying the plan. Used for state management and caching.version— positive integer. Must be >= 1.root— the top-level node of the UI tree.capabilities— declares what the plan needs (even if empty{}).
Spec Version
The specVersion field declares the protocol contract. Currently the only version is "runtime-plan/v1". In strict and balanced security profiles, this field is required.
const RUNTIME_PLAN_SPEC_VERSION_V1 = "runtime-plan/v1";Node Types
The UI tree is composed of three node types:
Text Node
interface RuntimeTextNode {
type: "text";
value: string; // Text content, supports template interpolation
}Template interpolation resolves {{state.count}}, {{context.userId}}, {{vars.name}} at render time.
Element Node
interface RuntimeElementNode {
type: "element";
tag: string; // HTML tag name
props?: Record<string, JsonValue>; // Attributes and properties
children?: RuntimeNode[]; // Child nodes
}Props support standard HTML attributes. Event handlers use the onClick, onInput format and are converted to runtime event bindings.
In the declarative RuntimeNode lane, those bindings dispatch static runtime events. They do not extract live browser values such as event.target.value, checked, or FormData. Use source.runtime: "preact" when the UI needs value-driven forms or richer DOM event handling.
Component Node
interface RuntimeComponentNode {
type: "component";
module: string; // Module specifier (bare, URL, or relative)
exportName?: string; // Named export (default: "default")
props?: Record<string, JsonValue>;
children?: RuntimeNode[];
}Component nodes reference external modules loaded via the JSPM module loader. The module specifier is resolved through the module manifest or JSPM CDN.
The referenced export is invoked as a Renderify component factory and must return a RuntimeNode tree or a string. Component nodes are not React or Preact component hosts.
Capabilities
interface RuntimeCapabilities {
domWrite?: boolean; // Allow DOM manipulation
networkHosts?: string[]; // Allowed network hosts
allowedModules?: string[]; // Permitted module specifiers
timers?: boolean; // Allow setTimeout/setInterval
storage?: Array<"localStorage" | "sessionStorage">;
executionProfile?: RuntimeExecutionProfile;
maxImports?: number; // Maximum import count
maxComponentInvocations?: number; // Maximum component renders
maxExecutionMs?: number; // Maximum execution time (ms)
}Execution Profiles
type RuntimeExecutionProfile =
| "standard" // Default, in-page execution
| "isolated-vm" // Reserved; current runtimes fail closed before module loading
| "sandbox-worker" // Terminable Web Worker sandbox
| "sandbox-iframe" // Compatibility name routed to a Worker
| "sandbox-shadowrealm"; // Compatibility name routed to a WorkerExecution Budgets
Three budget dimensions are enforced at runtime:
| Budget | Field | Description |
|---|---|---|
| Import count | maxImports | Declared imports and distinct remote materializations |
| Component invocations | maxComponentInvocations | Number of component renders |
| Wall-clock time | maxExecutionMs | Total execution time in milliseconds |
maxImports covers declared imports and remote modules reached transitively
through component or runtime-source imports during the same execution. When a
budget is exceeded, execution stops and a diagnostic error is emitted.
State Model
interface RuntimeStateModel {
initial: RuntimeStateSnapshot; // Required: initial state values
transitions?: Record<string, RuntimeAction[]>; // Event-driven mutations
}
type RuntimeStateSnapshot = Record<string, JsonValue>;When state is present, state.initial is required. The runtime maintains a snapshot per plan ID.
Actions
Four action types are available for state transitions:
// Set a value at a path
interface RuntimeSetAction {
type: "set";
path: string; // Dot-separated path, e.g. "user.name"
value: JsonValue | { $from: string }; // Literal or reference
}
// Increment a numeric value
interface RuntimeIncrementAction {
type: "increment";
path: string;
by?: number; // Default: 1
}
// Toggle a boolean value
interface RuntimeToggleAction {
type: "toggle";
path: string;
}
// Push a value to an array
interface RuntimePushAction {
type: "push";
path: string;
value: JsonValue | { $from: string };
}Value References
The $from syntax references values from other contexts:
{ "$from": "state.count" } // Current state
{ "$from": "event.payload.id" } // Event payload
{ "$from": "context.userId" } // Execution context
{ "$from": "vars.theme" } // Context variablesPath Safety
All state paths are validated against prototype pollution. The following segments are rejected:
__proto__prototypeconstructor
Module Manifest
type RuntimeModuleManifest = Record<string, RuntimeModuleDescriptor>;
interface RuntimeModuleDescriptor {
resolvedUrl: string; // Full URL to the module
integrity?: string; // SRI hash (e.g. "sha384-...")
version?: string; // Package version
signer?: string; // Signing authority
}The manifest provides deterministic module resolution. In strict security profile, bare specifiers (like "recharts") must have a manifest entry. In strict mode, remote modules must also include integrity hashes.
Example Manifest
{
"preact": {
"resolvedUrl": "https://ga.jspm.io/npm:preact@10.28.3/dist/preact.module.js",
"version": "10.28.3"
},
"recharts": {
"resolvedUrl": "https://ga.jspm.io/npm:recharts@3.3.0/es6/index.js",
"version": "3.3.0"
}
}Source Module
interface RuntimeSourceModule {
code: string; // Source code
language: "js" | "jsx" | "ts" | "tsx"; // Source language
exportName?: string; // Export to render (default: "default")
runtime?: "renderify" | "preact"; // JSX runtime target ("preact" is the trusted browser source lane)
}Source modules enable richer interactivity than the declarative node tree. The source code is transpiled via Babel, imports are rewritten to browser-resolvable URLs, and the default export is rendered as a Preact component (when runtime: "preact") or as a RuntimeNode tree.
runtime: "renderify"expects the exported function to returnRuntimeNode | stringand can participate in the declarative execution/sandbox model.runtime: "preact"expects a Preact-compatible component and should be rendered withrenderTrustedPlanInBrowseror thetrustedsecurity profile.runtime: "preact"does not run inside the browser worker/iframe/ShadowRealm sandbox adapters.
Example Source Module
{
"source": {
"code": "import { useState } from 'preact/hooks';\nexport default function Counter() {\n const [count, setCount] = useState(0);\n return <button onClick={() => setCount(c => c+1)}>Count: {count}</button>;\n}",
"language": "tsx",
"runtime": "preact"
}
}Metadata
interface RuntimePlanMetadata {
sourcePrompt?: string; // The prompt that generated this plan
sourceModel?: string; // LLM model used
tags?: string[]; // Classification tags
[key: string]: JsonValue | undefined; // Arbitrary extensions
}Built-in Compatibility Aliases
The IR package includes pre-resolved module mappings for the React/Preact ecosystem:
| Specifier | Resolved To |
|---|---|
preact | preact@10.28.3/dist/preact.module.js |
preact/hooks | preact@10.28.3/hooks/dist/hooks.module.js |
preact/compat | preact@10.28.3/compat/dist/compat.module.js |
react | preact@10.28.3/compat/dist/compat.module.js |
react-dom | preact@10.28.3/compat/dist/compat.module.js |
react-dom/client | preact@10.28.3/compat/dist/compat.module.js |
react/jsx-runtime | preact@10.28.3/jsx-runtime/dist/jsxRuntime.module.js |
recharts | recharts@3.3.0/es6/index.js |
These aliases mean LLMs can generate standard React code that runs directly via Preact.
Utility Functions
Node Creation
createTextNode("Hello");
createElementNode("div", { class: "card" }, [createTextNode("Content")]);
createComponentNode("npm:@acme/renderify-widgets", "SummaryCard", {
title: "Quarterly Report",
});Validation Guards
isRuntimePlan(value); // Full plan validation
isRuntimeNode(value); // Node type check
isRuntimeAction(value); // Action validation
isRuntimeStateModel(value); // State model validation
isRuntimeCapabilities(value); // Capabilities validation
isRuntimeSourceModule(value); // Source module validation
isRuntimeModuleManifest(value); // Manifest validation
isJsonValue(value); // JSON-safe value checkTree Utilities
// Walk all nodes depth-first
walkRuntimeNode(plan.root, (node, depth) => {
console.log(`${" ".repeat(depth * 2)}${node.type}`);
});
// Collect all component module specifiers
const modules = collectComponentModules(plan.root);
// => ["recharts", "my-component"]Path Utilities
getValueByPath(state, "user.name"); // => "Alice"
setValueByPath(state, "user.name", "Bob");
isSafePath("user.name"); // => true
isSafePath("__proto__.hack"); // => falseComplete Example
{
"specVersion": "runtime-plan/v1",
"id": "analytics-dashboard",
"version": 1,
"root": {
"type": "element",
"tag": "div",
"props": { "style": "padding: 16px" },
"children": [
{
"type": "element",
"tag": "h1",
"children": [{ "type": "text", "value": "Dashboard" }]
},
{
"type": "element",
"tag": "p",
"children": [{ "type": "text", "value": "Count: {{state.count}}" }]
}
]
},
"capabilities": {
"domWrite": true,
"maxExecutionMs": 5000,
"maxComponentInvocations": 100
},
"state": {
"initial": { "count": 0 },
"transitions": {
"increment": [{ "type": "increment", "path": "count" }]
}
},
"imports": ["recharts"],
"moduleManifest": {
"recharts": {
"resolvedUrl": "https://ga.jspm.io/npm:recharts@3.3.0/es6/index.js",
"version": "3.3.0"
}
},
"metadata": {
"sourcePrompt": "Build an analytics dashboard",
"sourceModel": "gpt-5-mini",
"tags": ["dashboard", "analytics"]
}
}