Package reference
The toolchain is a workspace monorepo of seven packages. The dependency
direction is one-way: spec at the bottom, cli at the top, converters and
writers in between and unaware of each other.
| Package | Role |
|---|---|
@oaf/spec | Types and JSON Schemas. Depends on nothing |
@oaf/core | Read, write, and validate packages |
@oaf/converter-articulate | Storyline .story to OAF |
@oaf/output-markdown | OAF to Markdown |
@oaf/output-marp | OAF to Marp slides |
@oaf/output-marp-html | Marp Markdown to interactive HTML |
@oaf/cli | The oaf binary |
All ship CommonJS and ESM builds with type declarations, except @oaf/cli,
which is CommonJS only because it is an executable.
@oaf/spec
Types and schemas, and the one package everything else depends on.
import type { OAFPackage, OAFSlide, OAFQuiz } from '@oaf/spec';
import { textContentToPlain, createEmptyPackage } from '@oaf/spec';
Two runtime helpers alongside the types:
function textContentToPlain(content: OAFTextContent): string;
function createEmptyPackage(): OAFPackage;
createEmptyPackage() returns a valid skeleton — spec version 1.0, language
en, linear navigation, current timestamps, and a generated course id. It is
the right starting point for a new converter.
Schemas are exported separately:
import courseSchema from '@oaf/spec/schemas/course.schema.json';
See the OAF format for what the types mean.
@oaf/core
Reading, writing, and validating packages.
import {
OAFReader, readOAFPackage,
OAFWriter, writeOAFPackage,
OAFValidator, validateOAFPackage,
type ValidationError, type ValidationResult,
} from '@oaf/core';
Each capability comes as a class and a function; the function is the convenience wrapper.
@oaf/core re-exports everything from @oaf/spec, so importing both is
unnecessary — take types from @oaf/core if you already depend on it.
writeOAFPackage is wired into the CLI's --format oaf path. Reading a stored
package back is not: oaf convert on a .oaf input exits with
OAF input not yet implemented.
The reader exists as an export. The CLI does not use it.
@oaf/converter-articulate
Storyline .story files to OAF packages.
import { convertStoryToOAF, ArticulateConverter } from '@oaf/converter-articulate';
import { StoryParser, SlideXmlParser } from '@oaf/converter-articulate';
interface ConvertOptions {
includeMedia?: boolean;
includeNotes?: boolean;
sourcePath?: string;
}
interface ConvertResult {
package: OAFPackage;
warnings: string[];
mediaFiles?: Map<string, Buffer>;
}
.story is a ZIP container, which is why media comes back as a
Map<string, Buffer> rather than as paths — the files are extracted from the
archive, not read from disk.
Always check warnings. An empty array means nothing was dropped; entries name
what could not be represented.
StoryParser and SlideXmlParser are exported for callers who need the
underlying XML rather than the OAF package.
@oaf/output-markdown
import { generateMarkdown, MarkdownGenerator, type MarkdownOptions } from '@oaf/output-markdown';
interface MarkdownOptions {
mode?: 'full' | 'summary' | 'outline';
includeFrontmatter?: boolean;
includeToc?: boolean;
includeNotes?: boolean;
includeQuizzes?: boolean;
includeMedia?: boolean;
quizFormat?: 'checklist' | 'table' | 'details';
notesFormat?: 'blockquote' | 'callout' | 'section';
headingOffset?: number;
}
interface MarkdownResult {
content: string;
warnings: string[];
}
headingOffset has no CLI flag. It shifts every heading down a level, so a
course title becomes ## rather than # — what you want when the output is
being embedded in a larger document.
@oaf/output-marp and @oaf/output-marp-html
import { generateMarp, type MarpOptions } from '@oaf/output-marp';
import { generateMarpHtml, type MarpHtmlOptions } from '@oaf/output-marp-html';
output-marp takes an OAF package and produces Marp Markdown.
output-marp-html takes Marp Markdown and produces a self-contained HTML
presentation — so a --format marp-html conversion runs both in sequence.
See Convert a course to slides for the options, which mirror the CLI flags.
@oaf/cli
The oaf binary, built on commander. It composes the packages above and adds
no conversion logic of its own.
See the CLI reference.