# Metadata Helper Return Shapes

## Plain Text

```ts
toPlainText(input: Body | BodyBlock | BodyBlock[] | BodyRichText | BodyRichText[]): string
richTextToPlainText(richText: readonly BodyRichText[]): string
```

`toPlainText` joins block output with newlines. Tables join cells with tabs.
Images and galleries fall back to captions or alt text.

## Headings and Anchors

```ts
type ExtractedHeading = {
  id?: string;
  level: 1 | 2 | 3 | 4;
  text: string;
  block: HeadingBlock;
  path: TraversalPath;
};

type ExtractedHeadingAnchor = ExtractedHeading & {
  anchorId: string;
  href: `#${string}`;
  slug: string;
};
```

`extractHeadingAnchors(input, options)` creates unique slugs. Options:

```ts
type HeadingSlugOptions = {
  preserveExistingIds?: boolean;
  fallbackPrefix?: string;
  duplicateSeparator?: string;
  slugify?: (heading: ExtractedHeading) => string;
};
```

Defaults preserve existing ids, use `heading` as fallback prefix, and use `-`
as duplicate separator.

## Excerpts and Reading Time

```ts
type ExcerptOptions = {
  maxLength?: number;
  omission?: string;
  preserveWords?: boolean;
};

type ReadingTimeOptions = {
  wordsPerMinute?: number;
  charactersPerMinute?: number;
  minimumMinutes?: number;
};

type ReadingTimeEstimate = {
  minutes: number;
  words: number;
  characters: number;
};
```

Defaults:

- excerpt max length: `160`
- omission: `"..."`
- words per minute: `200`
- CJK characters per minute: `500`
- minimum minutes: `1`

## Images

`collectImageSources(input)` returns image block, gallery image, and callout
image icon entries. Each entry includes a traversal path.

```ts
type CollectedImageSource =
  | {
      kind: "image";
      source: ImageSource;
      alt?: string;
      caption?: BodyRichText[];
      path: TraversalPath;
    }
  | {
      kind: "gallery_image";
      source: ImageSource;
      alt?: string;
      caption?: BodyRichText[];
      imageId?: string;
      path: TraversalPath;
    }
  | { kind: "callout_icon"; source: ImageSource; path: TraversalPath };
```

## Links

`collectLinks(input)` returns text links and page links.

```ts
type CollectedLink =
  | {
      kind: "text_link";
      href: string;
      title?: string;
      text: string;
      link: TextLink;
      path: TraversalPath;
    }
  | {
      kind: "page_link";
      page: PageReference;
      text: string;
      block: PageLinkBlock;
      path: TraversalPath;
    };
```

For page links without a title, `text` falls back to `pageId` or `url`.
