How to Use the @openmaic/dsl Package in OpenMAIC: Complete Developer's Guide
The @openmaic/dsl package provides pure, zero-dependency TypeScript utilities for validating, normalizing, and migrating MAIC lesson documents through functions like validateStage, normalizeSlide, and enumerateAssetManifest.
The @openmaic/dsl package defines the core data contract for the THU-MAIC/OpenMAIC ecosystem, offering type definitions and pure functions to handle slides, scenes, and playback actions without runtime dependencies. This library enables developers to process lesson skeletons, validate document structure, and enumerate media assets in both Node.js and browser environments.
Installing the @openmaic/dsl Package
Install the package using your preferred package manager. The library ships as ESM with TypeScript definitions and generated JSON schemas under dist/schema/.
# Using pnpm (repository preferred)
pnpm add @openmaic/dsl
# Using npm
npm install @openmaic/dsl
# Using yarn
yarn add @openmaic/dsl
The package contains no runtime dependencies, making it safe to import without pulling in React, PPTX processing libraries, or other heavy frameworks.
Core Concepts and Architecture
The package organizes functionality around seven primary domains:
- Slides & Elements – Object model for slide content (text, images, shapes, tables) defined in
src/slides.ts - Lesson Skeleton – Hierarchical structure of
Stage→Scene→SlideContentdefined insrc/stage.ts - Playback Actions – Union type system for interactions (spotlight, laser, speech) in
src/action.ts - Validators – Structural integrity checks (
validateStage,validateScene) insrc/validate.ts - Normalizers – Default value injection and geometry derivation in
src/normalize.ts - Version & Migration – Contract versioning (
DSL_VERSION) and upgrade utilities insrc/version.ts - Asset Manifest – Media reference enumeration via
enumerateAssetManifestinsrc/asset-manifest.ts
All public APIs are re-exported through the main entry point at src/index.ts.
Standard Workflow for Processing Documents
When working with MAIC documents, follow this six-step pipeline:
- Parse the incoming JSON document from file, database, or API
- Migrate the document to the current
DSL_VERSIONif it uses an older schema - Normalize the structure to fill required defaults and derive calculated geometry
- Validate the normalized document to catch structural errors
- Consume the data for rendering, exporting, or execution
- Enumerate assets to identify all media files requiring upload or caching
Each step uses pure functions with no side effects, making them safe for server-side rendering and client-side processing.
Practical Code Examples
Importing Types and Utilities
Import type definitions and helper functions from the package entry point:
import type {
Slide,
PPTElement,
Action,
Stage,
Scene,
} from '@openmaic/dsl';
import {
validateStage,
validateScene,
validateAction,
normalizeSlide,
normalizeScene,
DSL_VERSION,
migrate,
enumerateAssetManifest,
} from '@openmaic/dsl';
Migrating Legacy Documents
The migrate function brings older documents up to the current contract version:
import { migrate, DSL_VERSION, needsMigration } from '@openmaic/dsl';
// Check if migration is needed
if (needsMigration(rawDoc)) {
const migratedDoc = migrate(rawDoc);
console.log(`Migrated to DSL version ${DSL_VERSION}`);
}
The migrate function walks the DSL_MIGRATIONS ladder and is idempotent—running it on a current document returns the same object unchanged.
Normalizing Slides and Scenes
Use normalizers to fill missing required fields and derive geometry:
import { normalizeSlide, normalizeScene, normalizeStage } from '@openmaic/dsl';
// Fill defaults for a single slide (positions, dimensions, etc.)
const completeSlide = normalizeSlide(partialSlide);
// Normalize an entire scene hierarchy
const completeScene = normalizeScene(partialScene);
// Normalize a full stage document
const completeStage = normalizeStage(partialStage);
Normalization applies ELEMENT_DEFAULTS and calculates derived values like line endpoints.
Validating Document Structure
Validate documents before processing to catch structural errors:
import { validateStage, validateScene } from '@openmaic/dsl';
const result = validateStage(stageDocument);
if (!result.valid) {
console.error('Validation errors:');
result.errors.forEach(err => {
console.error(` ${err.path}: ${err.message}`);
});
}
The ValidationResult type returns { valid: true } on success or { valid: false; errors: Array<{path: string, message: string}> } on failure.
Enumerating Media Assets
Discover all media references in a stage for upload or caching:
import { enumerateAssetManifest } from '@openmaic/dsl';
const manifest = enumerateAssetManifest(stageDoc, {
metadata(ref, kind) {
// Optional: Enrich with file metadata (size, mime type) — pure function
return { size: 0, mimeType: 'image/png' };
},
});
console.log(`Found ${manifest.entries.length} assets`);
console.log('Reference counts:', manifest.referenceCounts);
The manifest contains unique entries for each (ref, kind) pair and tracks ownership counts across the document.
Complete Pipeline Example
Combine all operations in a production workflow:
import { readFileSync } from 'fs';
import {
migrate,
normalizeStage,
validateStage,
enumerateAssetManifest,
} from '@openmaic/dsl';
// 1. Load document
const raw = JSON.parse(readFileSync('lesson.json', 'utf8'));
// 2. Migrate to current version
const migrated = migrate(raw);
// 3. Normalize to fill defaults
const normalized = normalizeStage(migrated);
// 4. Validate structure
const validation = validateStage(normalized);
if (!validation.valid) {
throw new Error(
'Invalid stage: ' +
validation.errors.map(e => `${e.path}: ${e.message}`).join('; ')
);
}
// 5. Enumerate assets
const manifest = enumerateAssetManifest(normalized);
manifest.entries.forEach(e => {
console.log(`Asset: ${e.ref} (${e.kind})`);
});
Key Source Files and Implementation Details
Understanding the internal structure helps when debugging or extending the package:
| File | Responsibility |
|---|---|
src/index.ts |
Public API entry point; re-exports all modules |
src/slides.ts |
Type definitions for Slide, PPTElement, and element variants |
src/stage.ts |
Types for Stage, Scene<TAction, TContent>, SlideContent, QuizContent |
src/action.ts |
Union type Action and helper constants (ACTION_TYPES) |
src/guards.ts |
Type guards (isTextElement, PPT_ELEMENT_TYPES) |
src/validate.ts |
Pure validators: validateStage, validateScene, validateAction |
src/normalize.ts |
Normalizers: normalizeSlide, normalizeScene, ELEMENT_DEFAULTS |
src/version.ts |
DSL_VERSION constant and migration ladder (DSL_MIGRATIONS) |
src/asset-manifest.ts |
enumerateAssetManifest and manifest types |
src/runtime.ts |
Runtime envelope types for learner session data |
All validators and normalizers reside in separate files to maintain tree-shakeability.
Summary
- @openmaic/dsl is a zero-dependency contract package defining the MAIC SDK data model
- Use
migrate()andDSL_VERSIONto handle schema evolution safely validateStage()andvalidateScene()provide structural integrity checks with detailed error pathsnormalizeStage()andnormalizeSlide()fill required defaults without mutating inputsenumerateAssetManifest()extracts all media references for asset management pipelines- All functions are pure and isomorphic, running identically in Node.js and browsers
Frequently Asked Questions
How do I check if a document needs migration before processing?
Use the needsMigration() function from src/version.ts. This utility inspects the document's dslVersion field and returns a boolean indicating whether the migrate() function must run.
import { needsMigration, migrate } from '@openmaic/dsl';
const doc = needsMigration(rawDoc) ? migrate(rawDoc) : rawDoc;
What is the difference between validation and normalization in @openmaic/dsl?
Validation checks structural integrity and returns errors without modifying the document, while normalization returns a new object with filled defaults and derived geometry. Always normalize before validating to ensure required fields exist.
Can I use @openmaic/dsl in a browser environment without bundling issues?
Yes. The package has zero runtime dependencies and ships as ESM. It does not depend on Node.js-specific APIs, React, or heavy processing libraries, making it safe for client-side validation and normalization in browsers.
How do I enumerate all image assets in a stage for uploading to a CDN?
Call enumerateAssetManifest() with the stage document. It returns distinct entries for every media reference (images, audio, video) along with reference counts showing how many elements use each asset.
const manifest = enumerateAssetManifest(stage);
const images = manifest.entries.filter(e => e.kind === 'image');
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 →