How to Use the OpenMAIC DSL for Defining Contracts
The OpenMAIC DSL is a zero-dependency TypeScript contract system that enables type-safe definition, migration, normalization, and validation of course documents across the OpenMAIC ecosystem.
The OpenMAIC Domain-Specific Language (DSL) serves as the immutable architectural backbone for the entire SDK family. Located in the @openmaic/dsl package, this contract-first layer defines the shape of Stage documents and their nested entities—Slides, Actions, and Scenes—ensuring structural consistency across renderers, importers, and storage backends without imposing runtime dependencies on consuming packages.
Architecture of the OpenMAIC DSL Contract
The DSL implements a layered architecture that separates type definitions from operational logic, ensuring that contracts remain pure and tree-shakeable.
Object Model Layer
The foundational types reside in dedicated source files that declare the immutable structure of course content. The root Stage type defines the document envelope, while [slides.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/slides.ts) and [action.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/action.ts) provide discriminated unions for content elements and playback verbs. These interfaces enforce compile-time safety through TypeScript’s strict type system.
Validation and Normalization Layer
Pure functions in [validate.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/validate.ts) implement structural verification that walks the object graph without short-circuiting, collecting all constraint violations into a single ValidationResult. Complementing this, [normalize.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/normalize.ts) exports repair-oriented helpers that fill missing defaults and derive computed geometry while preserving object immutability.
Version Migration Layer
The [version.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/version.ts) module exports DSL_VERSION—a semantic constant representing the current contract revision—and DSL_MIGRATIONS, an ordered ladder of transformation functions. The migrate() utility inspects a document’s dslVersion envelope and idempotently upgrades legacy schemas to the current specification, leaving forward-compatible documents untouched.
Runtime Contract Workflow
Processing a course document through the OpenMAIC DSL follows a strict four-phase pipeline that guarantees canonical output.
- Ingest: Accept raw JSON payloads from importers, LLM generators, or persisted storage.
- Migrate: Invoke
migrate(doc)to traverseDSL_MIGRATIONSand stamp the document with the latestDSL_VERSION. - Normalize: Execute
normalizeStage()to inject default values (such as slide backgrounds) and derive layout geometry. - Validate: Optionally run
validateStage()to perform a full structural check, returning aggregated errors for every contract violation.
Because the DSL carries zero runtime dependencies, this pipeline executes efficiently in both Node.js and browser environments without bloating bundle sizes.
Practical Implementation Examples
Defining a Type-Safe Stage
The Stage type guarantees that dslVersion, title, and scenes exist and that each scene’s type discriminator matches its content variant.
import type { Stage } from '@openmaic/dsl';
import { DSL_VERSION } from '@openmaic/dsl';
const myStage: Stage = {
dslVersion: DSL_VERSION,
title: 'Intro to Quantum Mechanics',
scenes: [
{
id: 'scene-1',
type: 'slide',
content: {
type: 'slide',
slide: {
elements: [],
title: 'What is Quantum?',
},
},
},
],
};
Migrating Legacy Documents
When accepting documents from external systems or older SDK versions, use the migration utility to ensure schema conformity before processing.
import { migrate } from '@openmaic/dsl';
import type { Stage } from '@openmaic/dsl';
// oldDoc may lack dslVersion or use deprecated shape
const migrated: Stage = migrate(oldDoc);
The migrate function walks the DSL_MIGRATIONS array defined in [version.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/version.ts) and returns a document stamped with the current DSL_VERSION.
Normalizing and Validating Documents
Normalization prepares a document for rendering by filling optional fields with defaults, while validation confirms structural integrity.
import { normalizeStage, validateStage } from '@openmaic/dsl';
const canonical = normalizeStage(migrated);
const result = validateStage(canonical);
if (!result.valid) {
console.error('Stage contract violations:', result.errors);
}
The normalizeStage function handles default injection and geometry derivation, whereas validateStage confirms every required discriminant and field shape according to the definitions in [validate.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/validate.ts).
Consuming JSON-Schema in Other Languages
The package generates JSON-Schema artifacts at build time, enabling contract validation in Python, Java, or Go environments.
import stageSchema from '@openmaic/dsl/schema/stage.schema.json' with { type: 'json' };
import Ajv from 'ajv';
const ajv = new Ajv();
const validate = ajv.compile(stageSchema);
const ok = validate(someJson);
if (!ok) console.log(validate.errors);
This schema mirrors the TypeScript types exactly, providing language-agnostic validation without requiring a TypeScript runtime.
Core DSL Source Files
Understanding the repository layout helps navigate the contract implementation:
- [
src/version.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/version.ts): ExportsDSL_VERSION,DSL_MIGRATIONS, and themigratehelper for schema evolution. - [
src/stage.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/stage.ts): Defines the top-levelStageinterface,StageModeenum, andScene<TAction, TContent>generic container. - [
src/slides.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/slides.ts): Describes slide objects, element variants, andELEMENT_DEFAULTSconstants. - [
src/action.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/action.ts): Enumerates playback verbs includingspotlight,laser, andwidget_*categories. - [
src/validate.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/validate.ts): Implements pure validators such asvalidateStage,validateScene, andvalidateAction. - [
src/normalize.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/normalize.ts): Provides default-filling and geometry-deriving normalizers likenormalizeSlideandnormalizeElement.
Summary
- The OpenMAIC DSL is a zero-dependency TypeScript package that serves as the single source of truth for course document contracts.
- Contract enforcement relies on pure functions for migration, normalization, and validation rather than class-based state management.
- The migration ladder in
version.tsensures forward compatibility with legacy documents through idempotent transformations. - JSON-Schema generation enables cross-language validation while maintaining type parity with TypeScript definitions.
- All core logic resides in
packages/@openmaic/dsl/src/, with distinct modules for object models, validation, normalization, and versioning.
Frequently Asked Questions
What is the structure of an OpenMAIC DSL contract?
An OpenMAIC DSL contract centers on the Stage type, a root object containing dslVersion, metadata, and a scenes array. Each scene uses a discriminated union pattern where the type field determines the shape of the content property—such as slide content referencing the Slide interface or action content referencing Action variants defined in [action.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/action.ts).
How does document migration work in OpenMAIC?
Document migration uses the migrate() function exported from [version.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/version.ts). This function inspects the dslVersion field of an incoming document, iterates through the DSL_MIGRATIONS array to apply sequential transformations, and returns a document stamped with the current DSL_VERSION. The process is idempotent and forward-compatible, leaving newer documents untouched.
What is the difference between normalizing and validating a Stage?
Normalization (via normalizeStage in [normalize.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/normalize.ts)) is a constructive process that fills missing fields with defaults and derives computed properties like geometry. Validation (via validateStage in [validate.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/validate.ts)) is an analytic process that checks structural constraints and returns a ValidationResult listing all errors without modifying the input object.
Can I validate OpenMAIC DSL contracts in Python or Java?
Yes. The build process generates JSON-Schema artifacts—such as stage.schema.json—that exactly mirror the TypeScript types. These schemas can be consumed by validation libraries like Python’s jsonschema or Java’s Everit JSON Schema to enforce the same structural constraints without requiring TypeScript tooling.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →