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:
SlideandPPTElementin [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 theSceneTypeenum in [src/stage.ts](https://github.com/THU-MAIC/OpenMAIC/blob/main/packages/@openmaic/dsl/src/stage.ts) model the lesson skeleton and navigation structure.Actionand its variants in [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/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(), andvalidateAction()(implemented in [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(), andnormalizeStage()functions in [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/packages/@openmaic/dsl/src/version.ts) pins the current schema revision.migrate(): A pure function that walks theDSL_MIGRATIONSladder 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/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:
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/dslisolates 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →