@airalogy/aimd-core
@airalogy/aimd-core provides AIMD syntax parsing and canonical field extraction.
Protocol-level AIMD syntax, assigner semantics, and validation rules are normative in Airalogy docs. This page only describes how
@airalogy/aimd-coreparses and extracts those structures on the frontend.
Install
pnpm add @airalogy/aimd-coreMain Capabilities
- Parse AIMD templates and fenced
quiz/fig/mediablocks. - Parse fenced
assigner runtime=clientblocks into frontend assigner metadata. - Build MDAST-compatible AIMD nodes.
- Build MDAST-compatible CriticMarkup review nodes for additions, deletions, substitutions, comments, and highlights.
- Extract normalized field metadata for downstream renderer/editor/recorder, including simple
vardefinitions infields.var_definitions.
Example
import { unified } from "unified"
import remarkParse from "remark-parse"
import { remarkAimd } from "@airalogy/aimd-core/parser"
const content = "{{var|sample_name: str}}"
const processor = unified().use(remarkParse).use(remarkAimd)
const tree = processor.parse(content)
const file = { data: {} } as any
processor.runSync(tree, file)
console.log(file.data.aimdFields)Client assigner blocks use the same assigner fence name and declare the runtime in the header:
```assigner runtime=client
assigner(
{
mode: "auto",
dependent_fields: ["a", "b"],
assigned_fields: ["total"],
},
function calculate_total({ a, b }) {
return {
total: a + b,
};
}
);
```Choice Followups
Choice options may declare conditional structured fields under followups. The parser extracts these fields as options[].followups and accepts only str, int, float, and bool; number is intentionally not part of the AIMD followup type set.
```quiz
id: sample_storage
type: choice
mode: single
stem: "How is the sample currently stored?"
options:
- key: A
text: "Refrigerated"
followups:
- key: temperature_c
type: float
title: "Temperature"
unit: "°C"
- key: duration_hours
type: float
title: "Duration"
unit: "h"
- key: B
text: "Frozen"
- key: C
text: "Room temperature"
```True/False Quizzes
Use type: true_false for judgment questions. answer and default are booleans, and omitted options default to true. True and false. False. Custom true / false options may also declare followups.
```quiz
id: sample_kept_cold
type: true_false
stem: "The sample stayed cold during transfer."
answer: true
```Markdown Tables
If AIMD inline templates appear inside Markdown tables, protect them before parse() so GFM does not split on the template pipe:
import { protectAimdInlineTemplates, remarkAimd } from "@airalogy/aimd-core/parser"
const { content: protectedContent, templates } = protectAimdInlineTemplates(content)
const file = { data: { aimdInlineTemplates: templates } } as any
const tree = processor.parse(protectedContent)
processor.runSync(tree, file)CriticMarkup Review Nodes
Use remarkCriticMarkup when a host needs CriticMarkup-style review marks in the Markdown AST. This parser produces custom MDAST nodes such as criticAddition, criticDeletion, criticSubstitution, criticComment, and criticHighlight; it does not add entries to fields or change remarkAimd field extraction.
import { unified } from "unified"
import remarkParse from "remark-parse"
import remarkGfm from "remark-gfm"
import {
CRITIC_MARKUP_SUBSTITUTIONS_DATA_KEY,
protectCriticMarkupSubstitutions,
remarkCriticMarkup,
} from "@airalogy/aimd-core/parser"
const { content: protectedContent, substitutions } = protectCriticMarkupSubstitutions(
"Replace {~~old wording~>new wording~~} and mark {==important text==}.",
)
const file = { data: { [CRITIC_MARKUP_SUBSTITUTIONS_DATA_KEY]: substitutions } } as any
const processor = unified().use(remarkParse).use(remarkGfm).use(remarkCriticMarkup)
const tree = processor.parse(protectedContent)
processor.runSync(tree, file)protectCriticMarkupSubstitutions() should run before GFM because CriticMarkup substitutions also use ~~, which would otherwise be interpreted as strikethrough. Inline code and fenced code blocks remain literal source text.
Validation Helpers
If your editor, linter, or import pipeline needs parser-level validation before AIMD content reaches the renderer or recorder, @airalogy/aimd-core/parser also exports reusable helpers:
import {
parseMediaContent,
parseVarDefinition,
validateClientAssignerFunctionSource,
validateMediaDefinition,
validateVarDefinition,
validateVarDefaultType,
validateVarKwargs,
} from "@airalogy/aimd-core/parser"validateClientAssignerFunctionSource(functionSource, id)rejects unsafe or unsupported frontendclient_assignercode such aseval,window,fetch, Unicode-escape bypasses, and other non-deterministic constructs.parseMediaContent(content)parses key-value content from a fencedmediablock and preserves the rawkind.validateMediaDefinition(media)reports non-video/audio/filekindvalues as standard errors; static images should usefig.validateVarDefaultType(def)returns warning strings when an AIMD var default does not match its declared type.validateVarKwargs(def)returns warning strings when supported kwargs are applied to incompatible var definitions, including Pydantic-style numeric constraints (gt,ge,lt,le,multiple_of) on non-numeric var types.validateVarDefinition(def)combines default-value checks and kwargs checks, including nestedsubvars.
Built-in Type Metadata
@airalogy/aimd-core/utils exports getAimdBuiltInTypeMetadata() and getAimdBuiltInTypeEnumValues(). The metadata is generated from the Python airalogy.types registry, so official named enum types such as BloodType expose the same values to browser tools without duplicating Python type definitions in npm code.
Further Reading
- Parsed nodes and extracted fields now use
idonly. If you are upgrading older integrations, read Migration first. - Parsed Nodes
- Extracted Fields
- Migration