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

> Explore the purpose of the @openmaic/dsl package. Discover its role in defining data contracts, types, validators, and migration tools for the OpenMAIC SDK.

- Repository: [MAIC/OpenMAIC](https://github.com/THU-MAIC/OpenMAIC)
- Tags: deep-dive
- Published: 2026-09-01

---

**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](https://github.com/THU-MAIC/OpenMAIC), 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:

- **`Slide`** and **`PPTElement`** in [[`src/slides.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/slides.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/slides.ts) define the visual components of a presentation.
- **`Stage`**, **`Scene`**, and the **`SceneType`** enum in [[`src/stage.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/stage.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/stage.ts) model the lesson skeleton and navigation structure.
- **`Action`** and its variants in [[`src/action.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/action.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/action.ts) represent playback verbs and user interactions.

By centralizing these types in [[`src/index.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/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:

- **Structural validators**: Functions like `validateStage()`, `validateScene()`, and `validateAction()` (implemented in [[`src/validate.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/validate.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/validate.ts)) check object conformance without requiring external validator libraries such as Zod or Yup.
- **Normalization**: The `normalizeSlide()`, `normalizeScene()`, and `normalizeStage()` functions in [[`src/normalize.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/normalize.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/normalize.ts) fill default values, derive geometry, and ensure that optional fields exist before downstream processing.

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:

- **`DSL_VERSION`**: A constant exported from [[`src/version.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/version.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/version.ts) pins the current schema revision.
- **`migrate()`**: A pure function that walks the `DSL_MIGRATIONS` ladder to upgrade old documents to the current shape in an idempotent manner.
- **Asset enumeration**: The `enumerateAssetManifest()` function in [[`src/asset-manifest.ts`](https://github.com/THU-MAIC/OpenMAIC/blob/main/src/asset-manifest.ts)](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/asset-manifest.ts) extracts all media references from a stage, enabling storage backends to build dependency graphs.

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/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/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/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/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/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/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/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/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

```typescript
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

```typescript
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

```typescript
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/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.