# How the Variable Validation System Works in Hyperframes Runtime

> Discover how Hyperframes runtime ensures type safety with its variable validation system. Learn about schema-based checks for composition variables before rendering.

- Repository: [HeyGen/hyperframes](https://github.com/heygen-com/hyperframes)
- Tags: internals
- Published: 2026-05-17

---

**The Hyperframes runtime validates composition variables through a deterministic schema-based check that ensures type safety, required field presence, and enum constraints before rendering.**

The `heygen-com/hyperframes` repository implements a strict variable validation system to prevent runtime errors in dynamic compositions. When a composition loads, the runtime executes the validation helper to compare supplied values against a declared schema extracted from the composition's metadata. This process ensures that rendering pipelines receive only well-typed, expected data, eliminating crashes caused by type mismatches or missing required fields.

## Schema Declaration and Variable Types

Variables in Hyperframes are declared using a `<script type="application/hyperframes-variables">` block within the composition HTML or via a standalone JSON configuration file. The schema defines each variable's **type**, whether it is **required**, allowed **enum values**, and optional **default** values. The parser in [`packages/core/src/parsers/htmlParser.ts`](https://github.com/heygen-com/hyperframes/blob/main/packages/core/src/parsers/htmlParser.ts) extracts these declarations into a `VariableDeclarationMap` that serves as the validation contract.

Supported types include:

- **string**: Plain text values
- **number**: Numeric values rejecting `NaN` and `Infinity`
- **boolean**: Boolean values or string coercion of `"true"` and `"false"`
- **enum**: Values constrained to a specific set of allowed literals
- **custom formats**: Built-in patterns such as hex color strings

## The Validation Pipeline

The core validation logic resides in [`packages/core/src/runtime/validateVariables.ts`](https://github.com/heygen-com/hyperframes/blob/main/packages/core/src/runtime/validateVariables.ts). The `validateVariables` function accepts a `Record<string, unknown>` of user-supplied values and a `VariableDeclarationMap`, returning an array of `VariableValidationIssue` objects.

### Required Field Verification

The validator first iterates through the declaration map to identify missing required variables. If a required key is absent from the supplied data, the system pushes an issue with type `"missing"` containing the variable name.

### Type-Specific Validation

For each supplied variable, the system performs strict type checking according to the schema definition:

- **String validation**: Verifies `typeof value === "string"`
- **Number validation**: Confirms `typeof value === "number"` and explicitly rejects `NaN` or `Infinity`
- **Boolean coercion**: Accepts native booleans or the exact strings `"true"` and `"false"`; other values generate an `"invalid-boolean"` issue
- **Enum constraints**: Validates that the value exists within the declared `decl.values` array; mismatches produce `"invalid-enum-value"` issues listing the allowed options
- **Format validation**: Applies regex patterns for declared formats, flagging failures as `"invalid-format"` issues

### Extra Field Detection

Any key present in the user-supplied object but missing from the declaration map is flagged as an `"extra-field"` issue. This strict matching prevents typos and undefined behavior in compositions.

## Core Implementation

The following implementation from [`packages/core/src/runtime/validateVariables.ts`](https://github.com/heygen-com/hyperframes/blob/main/packages/core/src/runtime/validateVariables.ts) illustrates the validation logic:

```typescript
export function validateVariables(
  vars: Record<string, unknown>,
  decls: VariableDeclarationMap,
): VariableValidationIssue[] {
  const issues: VariableValidationIssue[] = [];

  // Check required variables
  for (const [name, decl] of Object.entries(decls)) {
    if (decl.required && !(name in vars)) {
      issues.push({ type: "missing", name });
    }
  }

  // Validate each supplied variable
  for (const [name, value] of Object.entries(vars)) {
    const decl = decls[name];
    if (!decl) {
      issues.push({ type: "extra-field", name });
      continue;
    }

    // Type-specific checks
    switch (decl.type) {
      case "string":
        if (typeof value !== "string") {
          issues.push({ type: "type-mismatch", name, expected: "string" });
        }
        break;

      case "number":
        if (typeof value !== "number" || Number.isNaN(value)) {
          issues.push({ type: "invalid-number", name });
        }
        break;

      case "boolean":
        if (typeof value !== "boolean" && !(value === "true" || value === "false")) {
          issues.push({ type: "invalid-boolean", name });
        }
        break;

      case "enum":
        if (!decl.values?.includes(value as string)) {
          issues.push({ type: "invalid-enum-value", name, allowed: decl.values! });
        }
        break;

      // Add more custom format checks here
    }
  }

  return issues;
}

```

### Test Suite Examples

The test file [`packages/core/src/runtime/validateVariables.test.ts`](https://github.com/heygen-com/hyperframes/blob/main/packages/core/src/runtime/validateVariables.test.ts) demonstrates expected behavior:

```typescript
const DECLS = {
  title: { type: "string", required: true },
  count: { type: "number" },
  active: { type: "boolean" },
  theme: { type: "enum", values: ["midnight", "sunrise"] },
};

// Valid case
expect(validateVariables({ title: "Demo" }, DECLS)).toEqual([]);

// Missing required
expect(validateVariables({ }, DECLS)).toEqual([{type:"missing",name:"title"}]);

// Type mismatch
expect(validateVariables({ count: "three" }, DECLS)).toEqual([
  {type:"type-mismatch",name:"count",expected:"number"}
]);

// Invalid enum
expect(validateVariables({ theme: "neon" }, DECLS)).toEqual([
  {type:"invalid-enum-value",name:"theme",allowed:["midnight","sunrise"]}
]);

```

## CLI and Programmatic Usage

### Command-Line Interface

When using the Hyperframes CLI, developers pass variables via `--var` flags:

```bash
hyperframes render my-composition.html \
  --var title="My Video" \
  --var count=5 \
  --var active=true \
  --var theme=midnight

```

The CLI implementation in [`packages/cli/src/commands/render.ts`](https://github.com/heygen-com/hyperframes/blob/main/packages/cli/src/commands/render.ts) parses these flags into an object and invokes `validateVariables` before executing the render pipeline.

### Programmatic Integration

Custom plugins can import the validator directly:

```typescript
import { validateVariables } from "hyperframes/core/runtime";

export function myPlugin(context) {
  const vars = context.getCompositionVariables();
  const schema = context.getVariableSchema();

  const problems = validateVariables(vars, schema);
  if (problems.length) {
    throw new Error(`Invalid variables: ${problems.map(i => i.name).join(", ")}`);
  }

  // Variables validated - safe to proceed with rendering
}

```

## Summary

- The variable validation system in Hyperframes runtime enforces type safety through a declarative schema extracted from composition metadata in [`packages/core/src/parsers/htmlParser.ts`](https://github.com/heygen-com/hyperframes/blob/main/packages/core/src/parsers/htmlParser.ts).
- The `validateVariables` function in [`packages/core/src/runtime/validateVariables.ts`](https://github.com/heygen-com/hyperframes/blob/main/packages/core/src/runtime/validateVariables.ts) performs deterministic checks for required fields, type mismatches, enum constraints, and extra fields.
- The system rejects `NaN` and `Infinity` for numeric types and coerces boolean strings while rejecting invalid formats.
- Validation returns an array of `VariableValidationIssue` objects; an empty array indicates success and permits rendering to proceed.
- Both CLI tools and programmatic APIs expose this validation layer to ensure deterministic composition rendering across all execution contexts.

## Frequently Asked Questions

### What happens if I pass an undefined variable to a Hyperframes composition?

The validator flags undefined values as type mismatches for non-optional fields. If the variable is declared as required in the schema, the system generates a `"missing"` issue. If the variable is optional but provided as `undefined`, it typically fails the type check for the declared type, resulting in a type-mismatch error.

### Does Hyperframes support custom validation rules beyond the built-in types?

The current implementation supports custom format validation through regex patterns, such as hex color validation. However, complex custom validation logic requires manual implementation outside the `validateVariables` function. Developers can pre-validate variables programmatically using the exposed API before passing them to the runtime.

### How does the runtime handle boolean values passed as strings?

The validation system accepts both native JavaScript booleans and the string literals `"true"` and `"false"`, coercing them to proper boolean values. Any other string value generates an `"invalid-boolean"` validation issue, preventing ambiguous truthy or falsy interpretations that could cause rendering inconsistencies.

### Where is the variable schema defined in a Hyperframes project?

The schema is declared within a `<script type="application/hyperframes-variables">` block inside your composition HTML file, or alternatively via a separate JSON configuration. The parser in [`packages/core/src/parsers/htmlParser.ts`](https://github.com/heygen-com/hyperframes/blob/main/packages/core/src/parsers/htmlParser.ts) extracts these declarations during the loading phase to create the `VariableDeclarationMap` used by the validator.