Plan
ID:SPEC-011Status:accepted

Rune: {% symbol %} — High-Level Plan

@refrakt-md/runes — Data & Documentation Rune


Purpose

Document code constructs — functions, classes, interfaces, enums, modules, type aliases — using natural Markdown. Transforms headings, lists, code blocks, and blockquotes into structured, navigable SDK documentation with type-aware rendering.


Attributes

AttributeTypeRequiredDefaultDescription
kindstringNo"function"Construct type — function, class, interface, enum, type, module, hook, component
langstringNo"typescript"Programming language — typescript, javascript, python, rust, go, etc.
sincestringNoVersion when this construct was introduced
deprecatedstringNoVersion when deprecated, or "true" for general deprecation
sourcestringNoURL to source code (GitHub permalink, etc.)
headingLevelnumberNo2Base heading level for construct name (shifts all heading interpretations)

Markdown Reinterpretation

Universal Rules (all kinds)

Markdown PrimitiveInterpretation
Base heading (## at default level)Construct name
First fenced code block after nameType signature / declaration
Paragraph after name (before first fence or list)Description prose
ImageDiagram or visual explanation
Horizontal rule (---)Visual separator between sections

Reinterpretation by Kind

Primitivefunction / hookclass / interface / moduleenumtype
Base heading = nameyesyesyesyes
First fence = signatureyesyes (declaration)yesyes
### group labels (Constructor, Properties, Methods, Events)noyesnono
#### member namesnoyesnono
Unordered list = parameter/property definitionsyesyes (per member)nono
Bold at start of list item = param nameyesyes (per member)nono
`code` after bold = type annotationyesyes (per member)nono
Italic in list item = optional marker / defaultyesyes (per member)nono
Nested list under parameter = object propertiesyesyes (per member)nono
> Returns blockquoteyesyes (per method)nono
> Throws blockquoteyesyes (per method)nono
> Deprecated blockquoteyesyesyesyes
Blockquote (other)general notesgeneral notesgeneral notesgeneral notes
Unordered list = enum member definitionsnonoyesno

Heading Level Shifting

The headingLevel attribute shifts all heading interpretations. Default is 2:

headingLevelNameGroupMember
2 (default)#########
3############
4###############

This allows {% symbol %} to be nested inside other runes like {% section %} or {% tabs %} without heading conflicts.

Code Block Disambiguation

  • First bare fence after the construct name → signature/declaration
  • {% codegroup %} → usage examples (handled by codegroup rune)
  • Subsequent bare fences → treated as description content (additional code context)
  • For class members (####), the first fence after the member heading → that member's signature

Blockquote Prefix Conventions

Blockquote meaning is determined by the first word(s), case-insensitive:

Prefix (any of)Meaning
Returns, Return, Return valueReturn value description
Throws, Throw, RaisesException/error documentation
Deprecated, DeprecationDeprecation notice with migration guidance
(anything else)General notes or remarks

Authoring Examples

Function Documentation

{% symbol kind="function" lang="typescript" since="1.0.0" %}

## renderContent

Transforms a Markdoc document into a renderable tree using the specified rune definitions. The source string is parsed, validated against known rune schemas, and transformed into a framework-agnostic tree structure.

```typescript
renderContent(source: string, options?: RenderOptions): RenderTree
```

- **source** `string` — Raw Markdoc content to parse and transform
- **options** `RenderOptions` *(optional)* — Configuration for the render pass
  - **runes** `RuneMap` — Custom rune definitions to merge with defaults
  - **variables** `Record<string, any>` — Template variables available in content
  - **strict** `boolean` — Throw on unknown runes instead of using fallback. *Default: `false`*
  - **validate** `boolean` — Run validation pass before transformation. *Default: `true`*

> Returns `RenderTree` — A framework-agnostic tree that can be passed to any implementation adapter for rendering.

> Throws `ParseError` if the source contains invalid Markdoc syntax.

> Throws `ValidationError` if `strict` is `true` and an unknown rune is encountered.

{% codegroup %}
```typescript
import { renderContent } from '@refrakt-md/core';

const tree = renderContent('{% hero %}\n# Hello World\n{% /hero %}', {
  strict: true,
  variables: { siteName: 'My Site' }
});
```

```javascript
const { renderContent } = require('@refrakt-md/core');

const tree = renderContent('{% hero %}\n# Hello World\n{% /hero %}');
```
{% /codegroup %}

{% callout type="tip" %}
For large documents, consider using `renderContentAsync` which supports streaming parsing and returns a `Promise<RenderTree>`.
{% /callout %}

{% /symbol %}

Class Documentation

{% symbol kind="class" lang="typescript" since="1.0.0" %}

## ContentParser

The core parsing engine that transforms raw Markdoc source into an abstract syntax tree. Handles tokenization, rune resolution, and Markdown primitive reinterpretation.

```typescript
class ContentParser extends EventEmitter
```

### Constructor

```typescript
new ContentParser(config: ParserConfig)
```

- **config** `ParserConfig` — Parser configuration
  - **runeRegistry** `RuneRegistry` — Registry of available rune definitions
  - **strictMode** `boolean` — Reject unknown runes. *Default: `false`*
  - **maxDepth** `number` — Maximum nesting depth for runes. *Default: `10`*

---

### Properties

- **runes** `ReadonlyMap<string, RuneDefinition>` — Map of registered rune definitions
- **errors** `ParseError[]` — Accumulated errors from the last parse operation
- **stats** `ParseStats` — Performance statistics from the last parse

---

### Methods

#### parse

Parse a Markdoc source string into a raw AST.

```typescript
parse(source: string): ASTNode
```

- **source** `string` — Raw Markdoc content

> Returns `ASTNode` — The root node of the parsed abstract syntax tree.

#### transform

Transform a parsed AST into a renderable tree by applying rune reinterpretation rules.

```typescript
transform(ast: ASTNode, variables?: Record<string, any>): RenderTree
```

- **ast** `ASTNode` — A parsed AST from the `parse` method
- **variables** `Record<string, any>` *(optional)* — Template variables

> Returns `RenderTree` — A framework-agnostic renderable tree.

> Throws `TransformError` if a rune's reinterpretation rules fail.

#### validate

Run validation checks on a parsed AST without transforming it.

```typescript
validate(ast: ASTNode): ValidationResult
```

- **ast** `ASTNode` — A parsed AST to validate

> Returns `ValidationResult` — Object containing `valid: boolean` and `errors: ValidationError[]`.

---

### Events

- **rune:enter** — Emitted when the parser enters a rune node. Payload: `{ name: string, attributes: object, depth: number }`
- **rune:exit** — Emitted when the parser exits a rune node. Payload: `{ name: string }`
- **warning** — Emitted for non-fatal issues. Payload: `{ message: string, line: number, column: number }`

{% /symbol %}

Interface Documentation

{% symbol kind="interface" lang="typescript" since="1.0.0" %}

## ThemeImplementation

Defines the contract that a framework-specific theme implementation must satisfy. Each implementation maps rune names to framework components and handles the rendering lifecycle.

```typescript
interface ThemeImplementation<T extends FrameworkAdapter>
```

### Properties

- **name** `string` — Theme identity name (e.g., "lumina")
- **framework** `string` — Target framework identifier (e.g., "sveltekit", "astro")
- **version** `string` — Implementation version (should match identity version)
- **adapter** `T` — Framework-specific adapter instance

### Methods

#### getComponent

Resolve a rune name to its framework component, accounting for context overrides.

```typescript
getComponent(rune: string, context?: RuneContext): FrameworkComponent<T>
```

- **rune** `string` — Rune name to resolve
- **context** `RuneContext` *(optional)* — Parent rune context for context-aware resolution

> Returns `FrameworkComponent<T>` — The resolved component for the target framework.

> Throws `UnknownRuneError` if the rune is not supported and no fallback is configured.

#### getLayout

Resolve a layout name to its framework layout component.

```typescript
getLayout(name: string): FrameworkLayout<T>
```

- **name** `string` — Layout name as declared in the theme manifest

> Returns `FrameworkLayout<T>` — The resolved layout component with region slot mappings.

{% /symbol %}

Enum Documentation

{% symbol kind="enum" lang="typescript" since="1.2.0" %}

## RuneCategory

Classification categories for runes in the registry. Used for documentation generation, marketplace filtering, and content analysis dashboards.

```typescript
enum RuneCategory
```

- **Layout** `"layout"` — Core structural runes (section, grid, columns)
- **Content** `"content"` — Content structure runes (tabs, accordion, callout)
- **Data** `"data"` — Data display and reference runes (datatable, chart, codegroup)
- **Commercial** `"commercial"` — Landing page and conversion runes (pricing, cta, testimonial)
- **Specialized** `"specialized"` — Domain-specific content runes (recipe, event, timeline)
- **Interactive** `"interactive"` — User interaction runes (form, quiz, poll)
- **Creative** `"creative"` — Experimental and visual runes (bento, storyboard, reveal)
- **Integration** `"integration"` — External platform runes (feed, map, embed)

{% /symbol %}

Type Alias Documentation

{% symbol kind="type" lang="typescript" since="1.0.0" %}

## RuneMap

A mapping of rune names to their definitions. Used to register custom runes or override built-in rune behavior.

```typescript
type RuneMap = Record<string, RuneDefinition>
```

{% codegroup %}
```typescript
import type { RuneMap } from '@refrakt-md/runes';

const customRunes: RuneMap = {
  'product-card': {
    name: 'product-card',
    category: RuneCategory.Commercial,
    attributes: { /* ... */ },
    reinterprets: { /* ... */ }
  }
};
```
{% /codegroup %}

{% /symbol %}

React Hook Documentation

{% symbol kind="hook" lang="typescript" since="2.0.0" %}

## useRuneContext

Access the current rune nesting context within a framework component. Returns the parent rune's name and attributes, enabling context-aware rendering at the component level.

```typescript
useRuneContext(): RuneContext | null
```

> Returns `RuneContext | null` — The parent rune context, or `null` if the component is not nested inside a rune.

{% codegroup %}
```tsx
import { useRuneContext } from '@refrakt-md/react';

function List({ items }) {
  const context = useRuneContext();

  if (context?.rune === 'pricing') {
    return <PricingList items={items} />;
  }

  return <ul>{items.map(i => <li key={i}>{i}</li>)}</ul>;
}
```
{% /codegroup %}

{% callout type="warning" %}
This hook is only available in React implementations. SvelteKit uses `getContext('rune')` and Astro resolves context at build time.
{% /callout %}

{% /symbol %}

Composition with Other Runes

Child RuneBehavior
{% codegroup %}Multi-language usage examples within the symbol block
{% callout %}Warnings, tips, deprecation notices, version-specific notes
{% tabs %}Version-specific documentation ("v1 API" / "v2 API")
{% diff %}Show API changes between versions (old signature vs new)
{% compare %}Side-by-side comparison of similar constructs
Parent RuneBehavior
{% tabs %}Each tab is a different module or namespace's reference docs
{% accordion %}Collapsible method/property groups for large APIs
{% section %}Symbol as one section of a larger documentation page (use headingLevel)

Differentiation from {% api %}

Aspect{% api %}{% symbol %}
DocumentsHTTP endpointsCode constructs
Key elementsMethod, path, request/response bodiesSignatures, types, parameters
OrganizationBy endpoint routeBy module/class/function
ExamplesHTTP requests (curl, fetch)Code usage in target language
SchemaREST/GraphQL conventionsLanguage-specific type systems

A complete SDK documentation site would use both: {% api %} for the REST API reference and {% symbol %} for the client library that wraps it.


Lumina Rendering Guidance

These are rendering recommendations for the Lumina theme. The rune emits a structured renderable tree; the theme decides presentation.

Function/Method View

  1. Name as heading with anchor link
  2. Since badge and deprecation badge inline with name
  3. Description as prose
  4. Signature in a styled code block with syntax highlighting
  5. Parameters as a structured definition list with types, defaults, and descriptions
  6. Nested parameters (object properties) indented under their parent
  7. Return value in a distinct "Returns" block
  8. Exceptions in a distinct "Throws" block
  9. Examples in tabbed code blocks
  10. Notes/callouts as contextual messages

Class/Interface View

  1. Name with type badges (since, deprecated)
  2. Description as prose
  3. Declaration code block
  4. Constructor section (if class)
  5. Properties as definition list or table
  6. Methods each rendered as a mini function view
  7. Events as a separate section with payload descriptions

Enum View

  1. Name as heading
  2. Description as prose
  3. Declaration code block
  4. Members as a styled list with name, value, and description per entry

SEO / Structured Data

ContextSchema
Individual constructTechArticle with proficiencyLevel, dependencies
Code examples within symbolSoftwareSourceCode with programmingLanguage

Well-structured symbol documentation with proper headings, parameter lists, and type annotations can surface in Google's developer search results.


Accessibility

  • Signatures use <code> with role="text" to prevent screen readers from spelling out symbols
  • Parameter lists use proper semantic markup for name, type, and description
  • Keyboard navigation between members via skip links
  • Collapsible sections (for large classes) use aria-expanded state
  • Color is never the only indicator — types use both color and formatting (italic for optional, bold for required)

AI Authoring Integration

The {% symbol %} rune should be fully supported by all AI authoring modes:

  • refrakt write --source ./src/auth — When generating SDK documentation from source files, the AI should emit {% symbol %} blocks with correct kind, signature formatting, and parameter lists following the Markdown reinterpretation rules. The --source flag provides TypeScript/JavaScript source for the AI to analyze.
  • refrakt enhance — Should detect code documentation patterns in plain Markdown (function signatures as code blocks, parameter lists, "Returns:" descriptions) and suggest wrapping them in {% symbol %}.
  • refrakt review — Should validate {% symbol %} blocks: check that signature matches parameter list, flag missing return documentation, suggest since/deprecated attributes where appropriate.
  • Base system prompt (packages/ai/src/prompt.ts) — Needs {% symbol %} added to the rune vocabulary with kind-specific authoring examples so all modes understand the conventions.

Deferred to Future Versions

FeatureRationale
Cross-linking / symbol indexBuild-time infrastructure, not a rune concern. Needs site-wide analysis step in packages/content/ or packages/sveltekit/. Separate plan.
Language-specific attributes (abstract, extends, implements, generic, access)TypeScript/Java-centric. Add when multi-language usage patterns emerge and the need is concrete.
Live playgroundAmbitious — executable code examples in sandboxed environment. Separate feature.
Versioned docsSymbol index would need version awareness. Depends on cross-linking being built first.
Search integrationSymbol index as search database ("find all functions returning RenderTree"). Depends on cross-linking.
Change detection / stalenessValidation step comparing {% symbol %} content against real type definitions. Build-time tool, not rune responsibility.
Auto-generation from sourcerefrakt typedoc-style tool that generates {% symbol %} content from JSDoc/TSDoc/docstrings. See Phase 7 in internal-spec.md.