What Is the Purpose of the `@openmaic/dsl` Package? A Deep Architectural Guide

The @openmaic/dsl package provides the pure, zero-dependency data contract—types, validators, and migration tools—that defines how slide objects, stages, and actions are structured across the entire OpenMAIC SDK.

In the OpenMAIC ecosystem, maintaining a consistent data shape between producers (importers, editors) and consumers (renderers, storage engines) requires a single source of truth. The @openmaic/dsl package fulfills this role by isolating the domain model from implementation details, ensuring that any package can depend on the contract without dragging in React, PPTX parsers, or charting libraries. According to the OpenMAIC source code, this package acts as the contract keystone that enables modular architecture.

Core Architectural Responsibilities

The package fulfills three tightly-coupled responsibilities that together form the backbone of the OpenMAIC data layer.

Object Model Definition

At its foundation, @openmaic/dsl exports the canonical TypeScript types that describe the slide domain. These definitions live in discrete modules to maintain separation of concerns:

By centralizing these types in [src/index.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/index.ts), the package guarantees that all consumers—from the React renderer to the Python storage backend—speak the same language when describing presentation data.

Runtime Validation and Normalization

Because external producers may generate malformed or incomplete documents, @openmaic/dsl provides zero-dependency utilities to validate structure and repair missing fields:

These utilities allow producers to validate input at the edge and automatically repair legacy documents without manual intervention.

Versioning and Migration

Data contracts evolve, and @openmaic/dsl manages this through a forward-compatible migration system:

This versioning layer ensures that documents created six months ago can still be rendered by today's engine without breaking changes.

Key Source Files and Their Roles

The implementation is organized into focused modules, each with a specific contractual responsibility:

File Purpose
[src/index.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/index.ts) Public API surface that re-exports all types and utilities.
[src/slides.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/slides.ts) Slide object model including Slide, PPTElement, and element type enums.
[src/stage.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/stage.ts) Lesson skeleton definitions: Stage, Scene, and scene navigation types.
[src/action.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/action.ts) Playback verbs and interaction models.
[src/validate.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/validate.ts) Zero-dependency structural validators (validateStage, validateAction).
[src/normalize.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/normalize.ts) Default-filling and geometry derivation (normalizeSlide, normalizeScene).
[src/version.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/version.ts) Contract versioning constants and the migration runner.
[src/asset-manifest.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/asset-manifest.ts) Asset enumeration logic for dependency tracking.

Practical Usage Examples

The following TypeScript examples demonstrate how downstream packages interact with the DSL contract:

Validating and Normalizing Documents

import {
  validateStage,
  normalizeSlide,
  type Slide,
  type Stage,
} from '@openmaic/dsl';

// Validate a stage document for structural integrity
const result = validateStage(myStage);
if (!result.valid) {
  console.error('Validation errors:', result.errors);
  process.exit(1);
}

// Normalize a raw slide to fill defaults and derive geometry
const rawSlide: Partial<Slide> = { id: 'slide-1', elements: [] };
const cleanSlide: Slide = normalizeSlide(rawSlide);

Migrating Legacy Documents

import { migrate, DSL_VERSION } from '@openmaic/dsl';

// Migrate an old document to the current DSL version
const oldStage = JSON.parse(fs.readFileSync('legacy-stage.json', 'utf-8'));
const upToDateStage = migrate(oldStage);

console.log(`Migrated from version ${oldStage.version} to ${DSL_VERSION}`);

Enumerating Assets

import { enumerateAssetManifest, type AssetManifest } from '@openmaic/dsl';

// Build a manifest of all media assets referenced in a stage
const manifest: AssetManifest = enumerateAssetManifest(myStage, {
  metadata(ref, kind) {
    // Optional: attach extra metadata (file size, MIME type)
    return { size: getFileSize(ref), mime: kind };
  },
});

// Upload all assets before storing the stage document
await Promise.all(
  manifest.assets.map(asset => uploadToStorage(asset.ref))
);

JSON Schema Generation

Because the TypeScript types are the single source of truth, @openmaic/dsl generates JSON Schema at build time using ts-json-schema-generator. This enables non-TypeScript consumers—such as Python microservices or Go storage workers—to validate incoming data against the same contract without importing TypeScript code. The dual-layer approach (in-process validators plus cross-language schema) maintains a tiny runtime footprint while supporting exhaustive validation across polyglot architectures.

Summary

  • @openmaic/dsl isolates the data contract from implementation details, allowing renderers, importers, and storage engines to share a common language.
  • Zero runtime dependencies ensure that importing the package never pulls in UI frameworks or parsing libraries.
  • Validation and normalization utilities (validateStage, normalizeSlide) provide immediate feedback and automatic repair for malformed documents.
  • Versioned migrations (migrate, DSL_VERSION) guarantee forward compatibility as the schema evolves.
  • Asset enumeration (enumerateAssetManifest) enables storage backends to track media dependencies without parsing implementation-specific logic.

Frequently Asked Questions

What does "DSL" stand for in @openmaic/dsl?

DSL stands for Domain-Specific Language. In this context, it refers to the specialized vocabulary of types—Slide, Stage, Action—that describe the structure of educational presentations within the OpenMAIC ecosystem. The package codifies this vocabulary as TypeScript interfaces and runtime validators.

Can I use @openmaic/dsl without the rest of the OpenMAIC SDK?

Yes. Because the package has zero runtime dependencies, you can install it independently to validate OpenMAIC documents in any Node.js environment. This is useful for server-side validation, CLI tools, or third-party importers that need to ensure data conformance before submitting to a storage backend.

How does the migration system handle breaking changes?

The migration system uses a ladder pattern where each version increment is a pure function that transforms data from version N to N+1. The migrate() function in [src/version.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/version.ts) composes these steps idempotently, ensuring that calling migrate() on an already-current document returns it unchanged. This forward-compatible approach prevents breaking changes from affecting legacy documents stored in production databases.

Why are there both TypeScript types and runtime validators?

The TypeScript types provide compile-time safety for TypeScript consumers, while the runtime validators (validateStage, validateScene) guard against data arriving from untyped sources (JSON files, HTTP requests, or databases). Additionally, the package generates JSON Schema for non-TypeScript environments, creating a triple-layered validation strategy: compile-time, runtime, and cross-language.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →