@ziradocs/ast-types
v2.22.0
Published
TypeScript types for the SlideLang/DocLang JSON/AST contract (--format json), generated from the Go structs in core/ast.
Readme
@ziradocs/ast-types
TypeScript types for the JSON/AST contract emitted by slidelang build --format json (see the JSON/AST contract and schema/ast.schema.json).
The types are generated from the Go structs in core/ast/ — never hand-written — so the TypeScript contract can never silently drift from the real AST produced by the CLI.
Installation
Status: the package is publish-ready (scoped,
publishConfig.access: public, CI-guarded) but not yet published to the npm registry. Until the first release lands, consume it via a relative path or a localnpm pack—npm installbelow will 404 until then.
npm install --save-dev @ziradocs/ast-typesUsage
import type { AST, ContentBlock, Element } from "@ziradocs/ast-types";
const doc: AST = JSON.parse(rawJson);
for (const block of doc.contentBlocks) {
for (const el of block.elements) {
// `el` is the discriminated union `Element`; use `el.type` to narrow it
if (el.type === "quote") {
console.log(el.content, el.author);
}
}
}Element is a union discriminated by the type field ("text" | "points" | "code" | "image" | "table" | "special_block" | "code_group" | "mermaid" | "plantuml" | "chart" | "map" | "quote" | "checklist" | "grid" | "column" | "directive" | "math" | "media"), reflecting the 18 Go structs that implement ast.Element.
Regenerating the types
Requires Go and tygo installed:
go install github.com/gzuidhof/tygo@latest
npm run generate # runs tygo generate + scripts/postprocess.cjs
npm run build # typecheck + emits dist/tygo parses the Go structs directly (no reflection), so it needs two manual adjustments applied by scripts/postprocess.cjs after generation:
Elementis a Go interface — tygo can't enumerate its implementers via static parsing, so it emitsElement = Node(any). The script replaces it with the real discriminated union. The type list must stay in sync with thefunc (X) element() {}methods incore/ast/nodes.go/directives.goand with theelementTypeslist incore/cmd/gen-schema/main.go.diagnostics.Positionis generated into a separate file (generated/diagnostics.ts); the script adds theimport type { Position } from "./diagnostics"that tygo doesn't automatically add across packages.
CI (.github/workflows/schema-drift.yml) runs this regeneration on every PR that touches core/ast/ and fails if the result diverges from what's committed — so an AST change that doesn't update ast-types (or schema/ast.schema.json) can't be merged unnoticed.
Compatibility policy
schemaVersion (a field on the root document, see ast.SchemaVersion in Go) is semver:
- MAJOR: a breaking change to the serialized shape — a field removed or renamed, a field's type changed, a discriminator (
type) value changed or reordered incompatibly. - MINOR: a new optional field added, a new element type added to the
Elementunion. - PATCH: fixes that don't change the JSON shape (e.g. fixing a serialization bug so it matches what the schema already documented).
The package's MAJOR.MINOR tracks schemaVersion's MAJOR.MINOR 1:1 (CI fails if they drift — see schema-drift.yml). PATCH is free to diverge for packaging-only releases (e.g. fixing package.json metadata) that don't touch the generated types.
2.20.0
- Opt-in:
QuizElement/PollElement.results(percent 0-100 per option) and.responses.quiz-poll-results-v1is inferred from the authoredresults:/responses:keys and emits 2.20.0 alone or with the other capabilities. See quiz and poll results.
2.19.0
- Opt-in:
CodeElement.filename.code-filename-v1is inferred fromCODE <language> <file>or a fence with a filename, and emits 2.19.0 alone or with the other capabilities. See code block filenames.
2.18.0
- Opt-in:
MediaElement.poster,.captionand.captionHTML.media-figure-v1is inferred from the authoredposter=/caption=attributes and emits 2.18.0 alone or with the other capabilities. See figures and media.
2.17.0
- Opt-in:
HeadingElement(type: "heading") carries a subsection heading aslevel, authored inlinetext, and resolvedanchor, instead of a raw-HTMLTextElement.typed-headings-v1uses explicit frontmatter opt-in and emits 2.17.0 alone or with the other capabilities. See portable typed headings.
2.16.0
- Opt-in:
PointItem.subListTypepreserves the type of that item'ssubPointsat every nesting level.nested-list-types-v1uses explicit frontmatter opt-in and emits 2.16.0 alone or withtable-rows-v1. See portable nested list types.
2.15.0
- Opt-in:
tableRowsholds authored rows/cells with optional explicitnodeIdvalues and portablesectionsemantics.capabilitiesdeclarestable-rows-v1on documents that use it. Legacy source and JSON remain 2.14.0; see portable table identities.
2.14.0
- Additive:
nodeIdis an optional explicit source identity on AST nodes. It is independent of existing cross-reference and HTML anchor IDs. See portable node identities for source syntax, uniqueness, and formatter behavior.
2.13.0
- Additive:
frontMatter.theme_mode("light" | "dark"at the document-language level) selects a visual light/dark mode within the declared theme family. It does not reusefrontMatter.mode, which remains the SlideLang/DocLang dialect selector (strict,flex, etc.).
2.12.0 (issues #340/#343/#344/#347/#348)
Adds structured KPIs (MetricElement), kicker, and image framing fields.
2.11.0 (audit 2026-09-11, finding F2)
- Additive:
chartElement.seriesAxes(string[] | undefined) — the per-series Chart.js scale id for a combo chart ("y","y1", ...), parallel toseries/seriesTypes. Lets a combo chart put a series with a very different magnitude (e.g. a percentage next to a currency amount) on its own axis instead of being flattened against the first series' scale.
2.5.0 (issue #100)
- Additive:
frontMatter.numbering(boolean | undefined) — a tri-state override for DocLang's section auto-numbering:true/falsefor an explicit opinion in the document itself, or the field absent for "no opinion" (distinct fromfalse), leaving the--numbering/--numbering=falseCLI flag or its own default to decide. Has no effect on SlideLang output — section numbering is a DocLang-only concept. Seellm-kit/reference/frontmatter.mdfor the frontmatter YAML shapes this maps from (including a legacy map form kept for backward compatibility).
2.4.0 (issue #63 code review, finding #9)
- Additive: the
langRunsfield (LangRun[]) added onSpecialBlockElement,GridElement, andColumnElement— each also carries its own loosecontentprose that the 2.3.0 population pass was skipping. Same derivation and posture as 2.3.0'slangRunsfields.
2.3.0 (issue #63)
- Additive: new
LangRuntype (text,lang) plus alangRunsfield (LangRun[]) onTextElement,PointItem,ChecklistItem, andQuoteElement— exposes[texto]{lang=xx}inline spans as structured runs, so a rulepack can flag a passage marked in a different language than the document'sfrontMatter.langwithout re-parsing rendered HTML. Derived fresh fromcontenton every build; unlike the*HTMLfields, not cleared when an external--filterruns (there's nothing pre-rendered here to distrust — it's always re-derived, never carried over from the filter's output).
2.2.0 (issues #62/#63 prerequisite)
- Additive:
frontMatter.lang— the document's declared language as a first-class BCP 47 field, so a renderer can emit a real<html lang>and a rulepack (e.g.A11Y005) can read it without depending on the author having written it into the free-formvariablesmap. Deliberately not folded intovariables— see the field's own doc comment incore/ast/nodes.go.
2.1.0 (issues #22, #20, #21 — A11Y AST seams)
- Additive:
TextElement.level— the heading level (1-6) for a##-######heading, so an A11Y rulepack can check heading order/nesting without re-parsing the rendered<hN>incontent. - Additive:
TableElement.cells— real cell structure (content,isHeader,scope,colSpan,rowSpan) alongside the existingheaders/rows, which are unchanged for compatibility. Populated for every table, including simple ones (derived fromheaders/rows), so a rulepack always has cell-level structure to walk. - Additive: new
MediaElement(type: "media") for embedded audio/video (mediaType,source,autoplay,controls,loop,muted), exposing autoplay/controls as first-class fields so a rule can flag autoplay content with no controls exposed to the user.
2.0.0 (issues #60, #64)
- Breaking:
ChecklistItem.typechanged from"point_item"(shared, ambiguous, withPointItem) to its own"checklist_item". - Additive: prose fields (
content,title,author,source,headers,rows,caption,alt) gained an optional sibling*HTMLfield with the same prose already rendered to inline HTML (Markdown applied,{{variables}}substituted and escaped) — see the JSON/AST contract.
