ironmark 1.12.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,159 +6,144 @@ Fast Markdown parser written in Rust with **zero third-party** parsing dependenc
6
6
 
7
7
  ## Table of Contents
8
8
 
9
- - [Configuration](#configuration)
10
- - [Extensions](#extensions)
11
- - [Security](#security)
12
- - [Other Options](#other-options)
9
+ - [Quick start](#quick-start)
10
+ - [Which function should I use?](#which-function-should-i-use)
13
11
  - [JavaScript / TypeScript](#javascript--typescript)
14
12
  - [Node.js](#nodejs)
15
- - [AST Output](#ast-output)
16
- - [HTML to Markdown](#html-to-markdown)
17
- - [AST to Markdown](#ast-to-markdown)
13
+ - [Markdown → HTML](#markdown--html)
14
+ - [Markdown AST](#markdown--ast)
15
+ - [Markdown → AST Markdown](#markdown--ast--markdown)
16
+ - [Safe HTML rendering](#safe-html-rendering)
18
17
  - [ANSI Terminal Output](#ansi-terminal-output)
18
+ - [HTML to Markdown](#html-to-markdown)
19
19
  - [Browser / Bundler](#browser--bundler)
20
+ - [Using ironmark in AI pipelines](#using-ironmark-in-ai-pipelines)
21
+ - [Presets](#presets)
22
+ - [Introspection helpers](#introspection-helpers)
23
+ - [Utility functions](#utility-functions)
24
+ - [Configuration](#configuration)
25
+ - [Extensions](#extensions)
26
+ - [Security](#security)
27
+ - [Other Options](#other-options)
20
28
  - [CLI](#cli)
21
29
  - [Rust](#rust)
22
30
  - [C / C++](#c--c++)
23
31
  - [Benchmarks](#benchmarks)
24
32
  - [Development](#development)
25
33
 
26
- ## Configuration
34
+ ## Quick start
27
35
 
28
- ### Extensions (default `true`)
29
-
30
- | Option | JS (`camelCase`) | Rust (`snake_case`) | Description |
31
- | ------------------- | -------------------------- | ----------------------------- | ------------------------------------------------------ |
32
- | Hard breaks | `hardBreaks` | `hard_breaks` | Every newline becomes `<br />` |
33
- | Highlight | `enableHighlight` | `enable_highlight` | `==text==` → `<mark>` |
34
- | Strikethrough | `enableStrikethrough` | `enable_strikethrough` | `~~text~~` → `<del>` |
35
- | Underline | `enableUnderline` | `enable_underline` | `++text++` → `<u>` |
36
- | Tables | `enableTables` | `enable_tables` | Pipe table syntax |
37
- | Autolink | `enableAutolink` | `enable_autolink` | Bare URLs & emails → `<a>` |
38
- | Task lists | `enableTaskLists` | `enable_task_lists` | `- [ ]` / `- [x]` checkboxes |
39
- | Indented code | `enableIndentedCodeBlocks` | `enable_indented_code_blocks` | 4-space indent → `<pre><code>` |
40
- | Wiki links | `enableWikiLinks` | `enable_wiki_links` | `[[page]]` → `<a href="page">` |
41
- | LaTeX math | `enableLateXMath` | `enable_latex_math` | `$inline$` and `$$display$$` → `<span class="math-…">` |
42
- | Heading IDs | `enableHeadingIds` | `enable_heading_ids` | Auto `id=` on headings from slugified text |
43
- | Heading anchors | `enableHeadingAnchors` | `enable_heading_anchors` | `<a class="anchor">` inside each heading (implies IDs) |
44
- | Permissive headings | `permissiveAtxHeaders` | `permissive_atx_headers` | Allow `#Heading` without space after `#` |
45
-
46
- ### Security
36
+ ```bash
37
+ npm install ironmark
38
+ # or
39
+ pnpm add ironmark
40
+ ```
47
41
 
48
- | Option | JS (`camelCase`) | Rust (`snake_case`) | Default | Description |
49
- | ---------------- | ---------------- | ------------------- | -------------- | ------------------------------------------------------------------ |
50
- | Disable raw HTML | `disableRawHtml` | `disable_raw_html` | `false` | Escape **all** HTML blocks and inline HTML |
51
- | No HTML blocks | `noHtmlBlocks` | `no_html_blocks` | `false` | Escape block-level HTML only (more granular than `disableRawHtml`) |
52
- | No HTML spans | `noHtmlSpans` | `no_html_spans` | `false` | Escape inline HTML only |
53
- | Tag filter | `tagFilter` | `tag_filter` | `false` | GFM tag filter: escape `<script>`, `<iframe>`, etc. |
54
- | Max nesting | — | `max_nesting_depth` | `128` | Limit blockquote/list nesting depth (DoS prevention) |
55
- | Max input size | — | `max_input_size` | `0` (no limit) | Truncate input beyond this byte count |
42
+ ```ts
43
+ import { renderHtml, parseMarkdown } from "ironmark";
56
44
 
57
- > In the WASM build, `max_nesting_depth` is fixed at `128` and `max_input_size` at `10 MB`.
45
+ // Markdown HTML
46
+ const html = renderHtml("# Hello\n\n**World**");
58
47
 
59
- Dangerous URI schemes (`javascript:`, `vbscript:`, `data:` except `data:image/…`) are **always** stripped from link and image destinations, regardless of options.
48
+ // Markdown AST (parsed JavaScript object)
49
+ const ast = parseMarkdown("# Hello\n\n**World**");
50
+ ```
60
51
 
61
- ### Other Options
52
+ ## Which function should I use?
62
53
 
63
- | Option | JS (`camelCase`) | Rust (`snake_case`) | Default | Description |
64
- | ------------------- | -------------------- | --------------------- | ------- | ------------------------------------------------- |
65
- | Collapse whitespace | `collapseWhitespace` | `collapse_whitespace` | `false` | Collapse runs of spaces/tabs in text to one space |
54
+ | Goal | Function |
55
+ | ------------------------------- | --------------------------------------------------- |
56
+ | Render Markdown to HTML | `renderHtml(input, options?)` |
57
+ | Parse Markdown to AST object | `parseMarkdown(input, options?)` |
58
+ | Normalize / round-trip Markdown | `parseMarkdown()` + `renderMarkdown()` |
59
+ | Terminal / CLI output | `renderAnsiTerminal(input, options?, ansiOptions?)` |
60
+ | Convert HTML back to Markdown | `htmlToMarkdown(html)` / `parseHtmlToAst(html)` |
66
61
 
67
62
  ## JavaScript / TypeScript
68
63
 
69
- ```bash
70
- npm install ironmark
71
- # or
72
- pnpm add ironmark
73
- ```
74
-
75
64
  ### Node.js
76
65
 
77
66
  WASM is embedded and loaded synchronously — no `init()` needed:
78
67
 
79
68
  ```ts
80
- import { parse } from "ironmark";
81
-
82
- const html = parse("# Hello\n\nThis is **fast**.");
69
+ import { renderHtml, parseMarkdown } from "ironmark";
83
70
 
84
- // safe mode for untrusted input
85
- const safe = parse(userInput, { disableRawHtml: true });
71
+ const html = renderHtml("# Hello\n\nThis is **fast**.");
72
+ const ast = parseMarkdown("# Hello");
86
73
  ```
87
74
 
88
- ### AST Output
89
-
90
- Use `parseToAst()` when you need the block-level document structure instead of rendered HTML.
75
+ ### Markdown → HTML
91
76
 
92
77
  ```ts
93
- import { parseToAst } from "ironmark";
78
+ import { renderHtml } from "ironmark";
94
79
 
95
- const astJson = parseToAst("# Hello\n\n- [x] done");
96
- const ast = JSON.parse(astJson);
80
+ const html = renderHtml("# Hello\n\nThis is **fast**.");
97
81
  ```
98
82
 
99
- `parseToAst()` returns a JSON string for portability across JS runtimes and WASM boundaries.
100
-
101
- ### HTML to Markdown
83
+ ### Markdown AST
102
84
 
103
- Convert HTML back to Markdown syntax using `htmlToMarkdown()`. Useful for importing content from HTML sources or round-trip conversion.
85
+ `parseMarkdown()` returns a parsed JavaScript array no `JSON.parse()` needed.
104
86
 
105
87
  ```ts
106
- import { htmlToMarkdown } from "ironmark";
88
+ import { parseMarkdown } from "ironmark";
107
89
 
108
- const md = htmlToMarkdown("<h1>Hello</h1><p><strong>Bold</strong> text</p>");
109
- // Returns: "# Hello\n\n**Bold** text"
110
-
111
- // Preserve unknown HTML tags (e.g., <sup>, <sub>) as raw HTML in output
112
- const md = htmlToMarkdown("<p>H<sub>2</sub>O</p>", true);
113
- // Returns: "H<sub>2</sub>O"
90
+ const ast = parseMarkdown("# Hello\n\n- [x] done");
91
+ // ast is a JavaScript array of block nodes
92
+ console.log(ast[0]); // { t: "Heading", level: 1, ... }
114
93
  ```
115
94
 
116
- For AST access, use `parseHtmlToAst()`:
95
+ ### Markdown → AST Markdown
117
96
 
118
97
  ```ts
119
- import { parseHtmlToAst } from "ironmark";
98
+ import { parseMarkdown, renderMarkdown } from "ironmark";
120
99
 
121
- const astJson = parseHtmlToAst("<h1>Hello</h1><p>World</p>");
122
- const ast = JSON.parse(astJson);
100
+ const ast = parseMarkdown("**Hello**");
101
+ const md = renderMarkdown(ast);
102
+ // Returns: "**Hello**"
123
103
  ```
124
104
 
125
- ### AST to Markdown
105
+ ### Safe HTML rendering
126
106
 
127
- Render an AST back to Markdown syntax using `renderMarkdown()`. Combined with `parseToAst()` or `parseHtmlToAst()`, this enables round-trip conversion.
107
+ Use `safe: true` for **any untrusted input** it disables raw HTML passthrough and enables the GFM tag filter.
128
108
 
129
109
  ```ts
130
- import { parseToAst, renderMarkdown } from "ironmark";
110
+ import { renderHtml } from "ironmark";
131
111
 
132
- const ast = parseToAst("# Hello\n\n**World**");
133
- const md = renderMarkdown(ast);
134
- // Returns: "# Hello\n\n**World**"
112
+ // Simple safe flag
113
+ const html = renderHtml(userInput, { safe: true });
114
+
115
+ // Or use the built-in preset
116
+ const html = renderHtml(userInput, { preset: "safe" });
135
117
  ```
136
118
 
119
+ What `safe: true` does:
120
+
121
+ - Sets `disableRawHtml: true` — all inline and block HTML is escaped
122
+ - Sets `tagFilter: true` — dangerous tags (`<script>`, `<iframe>`, etc.) are escaped
123
+
124
+ Dangerous URI schemes (`javascript:`, `vbscript:`, `data:` except `data:image/…`) are **always** stripped from link destinations regardless of options.
125
+
137
126
  ### ANSI Terminal Output
138
127
 
139
- Use `renderAnsi()` to render Markdown as coloured terminal output (ANSI 256-colour escape codes). Useful for CLI tools, terminal UIs, or any environment with a TTY.
128
+ Use `renderAnsiTerminal()` to render Markdown as coloured terminal output (ANSI 256-colour escape codes).
140
129
 
141
130
  ````ts
142
- import { renderAnsi } from "ironmark";
131
+ import { renderAnsiTerminal } from "ironmark";
143
132
 
144
- // Render with defaults (width 80, colour enabled)
145
- const ansi = renderAnsi("# Hello\n\n**bold** and `code`");
133
+ // Defaults: width 80, color enabled
134
+ const ansi = renderAnsiTerminal("# Hello\n\n**bold** and `code`");
146
135
  process.stdout.write(ansi);
147
136
 
148
- // Custom terminal width and line numbers in code blocks
149
- const ansi = renderAnsi(
137
+ // Custom options
138
+ const ansi = renderAnsiTerminal(
150
139
  "# Hello\n\n```rust\nfn main() {}\n```",
151
- {}, // parse options (same as parse())
140
+ {},
152
141
  { width: 120, lineNumbers: true },
153
142
  );
154
143
  process.stdout.write(ansi);
155
144
 
156
- // With paddingadds horizontal spacing on both sides
157
- const ansi = renderAnsi("# Hello\n\n> A quote", {}, { padding: 2 });
158
- process.stdout.write(ansi);
159
-
160
- // Plain text — strips all ANSI codes (useful for piping to files)
161
- const plain = renderAnsi("# Hello\n\n> quote", {}, { color: false });
145
+ // Plain textno ANSI codes (useful for piping to files)
146
+ const plain = renderAnsiTerminal("# Hello", {}, { color: false });
162
147
  ````
163
148
 
164
149
  #### ANSI options
@@ -170,44 +155,222 @@ const plain = renderAnsi("# Hello\n\n> quote", {}, { color: false });
170
155
  | `lineNumbers` | `boolean` | `false` | Show line numbers in fenced code blocks. |
171
156
  | `padding` | `number` | `0` | Horizontal padding added to both sides of each line, plus ⌈padding/2⌉ blank lines at the top. |
172
157
 
158
+ ### HTML to Markdown
159
+
160
+ Convert HTML back to Markdown syntax using `htmlToMarkdown()`.
161
+
162
+ ```ts
163
+ import { htmlToMarkdown } from "ironmark";
164
+
165
+ const md = htmlToMarkdown("<h1>Hello</h1><p><strong>Bold</strong> text</p>");
166
+ // Returns: "# Hello\n\n**Bold** text"
167
+
168
+ // Preserve unknown HTML tags (e.g. <sup>, <sub>) as raw HTML in output
169
+ const md = htmlToMarkdown("<p>H<sub>2</sub>O</p>", true);
170
+ // Returns: "H<sub>2</sub>O"
171
+ ```
172
+
173
+ For AST access, use `parseHtmlToAst()`:
174
+
175
+ ```ts
176
+ import { parseHtmlToAst, renderMarkdown } from "ironmark";
177
+
178
+ const ast = parseHtmlToAst("<h1>Hello</h1><p>World</p>");
179
+ const md = renderMarkdown(ast);
180
+ ```
181
+
173
182
  ### Browser / Bundler
174
183
 
175
- Call `init()` once before using `parse()`. It's idempotent and optionally accepts a custom `.wasm` URL.
184
+ Call `init()` once before using any function. It's idempotent and optionally accepts a custom `.wasm` URL.
176
185
 
177
186
  ```ts
178
- import { init, parse } from "ironmark";
187
+ import { init, renderHtml, parseMarkdown } from "ironmark";
179
188
 
180
189
  await init();
181
190
 
182
- const html = parse("# Hello\n\nThis is **fast**.");
191
+ const html = renderHtml("# Hello\n\nThis is **fast**.");
192
+ const ast = parseMarkdown("# Hello");
183
193
  ```
184
194
 
185
195
  #### Vite
186
196
 
187
197
  ```ts
188
- import { init, parse } from "ironmark";
198
+ import { init, renderHtml } from "ironmark";
189
199
  import wasmUrl from "ironmark/ironmark.wasm?url";
190
200
 
191
201
  await init(wasmUrl);
192
202
 
193
- const html = parse("# Hello\n\nThis is **fast**.");
203
+ const html = renderHtml("# Hello\n\nThis is **fast**.");
194
204
  ```
195
205
 
206
+ ### Using ironmark in AI pipelines
207
+
208
+ ironmark is designed to be a reliable, deterministic parsing layer for AI agents and code generation tools.
209
+
210
+ **Recommended pattern for agents:**
211
+
212
+ ```ts
213
+ import { parseMarkdown, renderHtml, renderMarkdown, getCapabilities } from "ironmark";
214
+
215
+ // 1. Inspect capabilities at startup
216
+ const caps = getCapabilities();
217
+ // { astSchemaVersion: "2", formats: [...], presets: [...] }
218
+
219
+ // 2. Parse with deterministic options
220
+ const ast = parseMarkdown(content, { preset: "llm" });
221
+
222
+ // 3. Render or transform
223
+ const html = renderHtml(content, { preset: "llm" });
224
+ const normalized = renderMarkdown(ast);
225
+ ```
226
+
227
+ **The `llm` preset** produces deterministic, structure-first output:
228
+
229
+ - Disables autolinks, wiki links, math, hard breaks (ambiguous in AI-generated text)
230
+ - Enables heading IDs and whitespace normalization
231
+ - Disables raw HTML passthrough (safe by default)
232
+
233
+ ```ts
234
+ import { parseMarkdown, extractHeadings, summarizeAst } from "ironmark";
235
+
236
+ const ast = parseMarkdown(content, { preset: "llm" });
237
+
238
+ // Extract document structure
239
+ const headings = extractHeadings(ast);
240
+ // [{ level: 1, text: "Introduction", id: "introduction" }, ...]
241
+
242
+ // Summarize node types
243
+ const summary = summarizeAst(ast);
244
+ // { blockCount: 5, nodeCounts: { Heading: 2, Paragraph: 3, ... } }
245
+ ```
246
+
247
+ ### Presets
248
+
249
+ Named presets configure multiple options at once. Explicit options always override the preset.
250
+
251
+ | Preset | Description |
252
+ | ----------- | -------------------------------------------------------------------------------------------------- |
253
+ | `"default"` | All extensions enabled. Current default behavior. |
254
+ | `"safe"` | Disables raw HTML, enables GFM tag filter. Use for untrusted input. |
255
+ | `"strict"` | CommonMark-only: disables extensions and permissive behaviors. |
256
+ | `"llm"` | Deterministic, stable output for AI pipelines. Disables ambiguous extensions, enables heading IDs. |
257
+
258
+ ```ts
259
+ import { renderHtml, parseMarkdown } from "ironmark";
260
+
261
+ // Use a preset
262
+ const html = renderHtml(content, { preset: "safe" });
263
+
264
+ // Presets compose with explicit options (explicit wins)
265
+ const html = renderHtml(content, { preset: "llm", enableTables: false });
266
+ ```
267
+
268
+ ### Introspection helpers
269
+
270
+ These functions return stable machine-readable metadata. Useful for agents and tooling to discover available features at runtime.
271
+
272
+ ```ts
273
+ import { getCapabilities, getAstSchemaVersion, getDefaultOptions, getPresets } from "ironmark";
274
+
275
+ // What does this build support?
276
+ const caps = getCapabilities();
277
+ // {
278
+ // astSchemaVersion: "1",
279
+ // formats: ["html", "ast", "markdown", "ansi"],
280
+ // presets: ["default", "safe", "strict", "llm"],
281
+ // extensions: [...],
282
+ // security: [...]
283
+ // }
284
+
285
+ // What is the current AST schema version?
286
+ const version = getAstSchemaVersion(); // "1"
287
+
288
+ // What are the resolved defaults for every option?
289
+ const defaults = getDefaultOptions();
290
+
291
+ // What options does each preset set?
292
+ const presets = getPresets();
293
+ console.log(presets.llm);
294
+ ```
295
+
296
+ ### Utility functions
297
+
298
+ ```ts
299
+ import { parseMarkdown, extractHeadings, summarizeAst } from "ironmark";
300
+
301
+ const ast = parseMarkdown("# Hello\n\n## World\n\nA paragraph.");
302
+
303
+ // Extract heading structure
304
+ const headings = extractHeadings(ast);
305
+ // [
306
+ // { level: 1, text: "Hello", id: "hello" },
307
+ // { level: 2, text: "World", id: "world" },
308
+ // ]
309
+
310
+ // Count node types
311
+ const summary = summarizeAst(ast);
312
+ // { blockCount: 3, nodeCounts: { Heading: 2, Paragraph: 1 } }
313
+ ```
314
+
315
+ ## Configuration
316
+
317
+ ### Extensions (default `true`)
318
+
319
+ | Option | JS (`camelCase`) | Rust (`snake_case`) | Description |
320
+ | ------------------- | -------------------------- | ----------------------------- | ------------------------------------------------------ |
321
+ | Hard breaks | `hardBreaks` | `hard_breaks` | Every newline becomes `<br />` |
322
+ | Highlight | `enableHighlight` | `enable_highlight` | `==text==` → `<mark>` |
323
+ | Strikethrough | `enableStrikethrough` | `enable_strikethrough` | `~~text~~` → `<del>` |
324
+ | Underline | `enableUnderline` | `enable_underline` | `++text++` → `<u>` |
325
+ | Tables | `enableTables` | `enable_tables` | Pipe table syntax |
326
+ | Autolink | `enableAutolink` | `enable_autolink` | Bare URLs & emails → `<a>` |
327
+ | Task lists | `enableTaskLists` | `enable_task_lists` | `- [ ]` / `- [x]` checkboxes |
328
+ | Indented code | `enableIndentedCodeBlocks` | `enable_indented_code_blocks` | 4-space indent → `<pre><code>` |
329
+ | Wiki links | `enableWikiLinks` | `enable_wiki_links` | `[[page]]` → `<a href="page">` |
330
+ | LaTeX math | `enableLatexMath` | `enable_latex_math` | `$inline$` and `$$display$$` → `<span class="math-…">` |
331
+ | Heading IDs | `enableHeadingIds` | `enable_heading_ids` | Auto `id=` on headings from slugified text |
332
+ | Heading anchors | `enableHeadingAnchors` | `enable_heading_anchors` | `<a class="anchor">` inside each heading (implies IDs) |
333
+ | Permissive headings | `permissiveAtxHeaders` | `permissive_atx_headers` | Allow `#Heading` without space after `#` |
334
+
335
+ ### Security
336
+
337
+ | Option | JS (`camelCase`) | Rust (`snake_case`) | Default | Description |
338
+ | ---------------- | ---------------- | ------------------- | -------------- | ------------------------------------------------------------------ |
339
+ | Safe mode | `safe` | — | `false` | Shorthand: sets `disableRawHtml: true` and `tagFilter: true` |
340
+ | Disable raw HTML | `disableRawHtml` | `disable_raw_html` | `false` | Escape **all** HTML blocks and inline HTML |
341
+ | No HTML blocks | `noHtmlBlocks` | `no_html_blocks` | `false` | Escape block-level HTML only (more granular than `disableRawHtml`) |
342
+ | No HTML spans | `noHtmlSpans` | `no_html_spans` | `false` | Escape inline HTML only |
343
+ | Tag filter | `tagFilter` | `tag_filter` | `false` | GFM tag filter: escape `<script>`, `<iframe>`, etc. |
344
+ | Max nesting | — | `max_nesting_depth` | `128` | Limit blockquote/list nesting depth (DoS prevention) |
345
+ | Max input size | — | `max_input_size` | `0` (no limit) | Truncate input beyond this byte count |
346
+
347
+ > In the WASM build, `max_nesting_depth` is fixed at `128` and `max_input_size` at `10 MB`.
348
+
349
+ Dangerous URI schemes (`javascript:`, `vbscript:`, `data:` except `data:image/…`) are **always** stripped from link and image destinations, regardless of options.
350
+
351
+ ### Other Options
352
+
353
+ | Option | JS (`camelCase`) | Rust (`snake_case`) | Default | Description |
354
+ | ------------------- | -------------------- | --------------------- | ------- | ------------------------------------------------- |
355
+ | Collapse whitespace | `collapseWhitespace` | `collapse_whitespace` | `false` | Collapse runs of spaces/tabs in text to one space |
356
+ | Preset | `preset` | — | — | Apply a named option preset (JS only) |
357
+ | Deterministic | `deterministic` | — | `false` | Normalize whitespace for stable output (JS only) |
358
+
196
359
  ## CLI
197
360
 
198
- Render Markdown as coloured terminal output. Two ways to install:
361
+ Render Markdown as HTML, ANSI terminal output, or an AST. Default format is `html`.
199
362
 
200
363
  ### npm
201
364
 
202
365
  ```bash
203
- npx ironmark --ansi README.md
366
+ npx ironmark README.md
204
367
  ```
205
368
 
206
369
  Or install globally:
207
370
 
208
371
  ```bash
209
372
  npm install -g ironmark
210
- ironmark --ansi README.md
373
+ ironmark README.md
211
374
  ```
212
375
 
213
376
  ### Rust
@@ -216,47 +379,72 @@ Native binary — faster startup, auto-detects terminal width via `$COLUMNS` / `
216
379
 
217
380
  ```bash
218
381
  cargo install ironmark --features cli
219
- ironmark --ansi README.md
382
+ ironmark README.md
220
383
  ```
221
384
 
222
385
  ### Options
223
386
 
224
- Both CLIs support the same flags (the npm CLI requires `--ansi` as the first flag):
387
+ Both CLIs support the same flags:
225
388
 
226
389
  ```text
227
- OPTIONS:
390
+ OPTIONS (all formats):
391
+ --format <html|ansi|ast> Output format; ast also accepts 'json' (default: html)
392
+ --preset <name> Apply a named option preset: default, safe, strict, llm
393
+ --safe Alias for --preset safe
394
+ --no-hard-breaks Don't turn soft newlines into hard line breaks
395
+ --no-tables Disable pipe table syntax
396
+ --no-highlight Disable ==highlight== syntax
397
+ --no-strikethrough Disable ~~strikethrough~~ syntax
398
+ --no-underline Disable ++underline++ syntax
399
+ --no-autolink Disable bare URL auto-linking
400
+ --no-task-lists Disable - [x] task list syntax
401
+ --math Enable $inline$ and $$display$$ math
402
+ --wiki-links Enable [[wiki link]] syntax
403
+ --capabilities Print machine-readable capabilities JSON and exit
404
+ --default-options Print resolved default options JSON and exit
405
+ --list-presets Print all presets and exit
406
+ --max-size N Truncate input to N bytes (Rust only)
407
+ -h, --help Print this help and exit
408
+ -V, --version Print version and exit
409
+
410
+ OPTIONS (ansi format only):
228
411
  --width N Terminal column width (default: auto-detect, fallback 80)
229
- --padding N Horizontal padding added to both sides of each line, plus ceil(padding/2) blank lines at the top (default: 0)
412
+ --padding N Horizontal padding added to both sides of each line (default: 0)
230
413
  --no-color Disable ANSI escape codes (plain text)
231
414
  -n, --line-numbers Show line numbers in fenced code blocks
232
- --no-hard-breaks Don't turn soft newlines into hard line breaks
233
- --no-tables Disable pipe table syntax
234
- --no-highlight Disable ==highlight== syntax
235
- --no-strikethrough Disable ~~strikethrough~~ syntax
236
- --no-underline Disable ++underline++ syntax
237
- --no-autolink Disable bare URL auto-linking
238
- --no-task-lists Disable - [x] task list syntax
239
- --math Enable $inline$ and $$display$$ math
240
- --wiki-links Enable [[wiki link]] syntax
241
- --max-size N Truncate input to N bytes (Rust only)
242
- -h, --help Print this help and exit
243
- -V, --version Print version and exit
244
415
  ```
245
416
 
246
417
  ### Examples
247
418
 
248
419
  ```bash
249
- # npm
250
- npx ironmark --ansi README.md
251
- npx ironmark --ansi --width 120 README.md
252
- echo '# Hello' | npx ironmark --ansi
253
- npx ironmark --ansi --no-color README.md | less
420
+ # Render as HTML (default)
421
+ echo '# Hello' | npx ironmark
422
+ npx ironmark README.md
423
+
424
+ # Safe mode for untrusted content
425
+ npx ironmark --safe README.md
426
+ npx ironmark --preset safe README.md
427
+
428
+ # LLM-optimized output
429
+ npx ironmark --preset llm README.md
430
+
431
+ # Render as ANSI terminal output
432
+ npx ironmark --format ansi README.md
433
+ npx ironmark --format ansi --width 120 README.md
434
+ npx ironmark --format ansi --no-color README.md | less
435
+
436
+ # Render as AST (JSON)
437
+ npx ironmark --format ast README.md
438
+
439
+ # Inspect capabilities
440
+ npx ironmark --capabilities
441
+ npx ironmark --list-presets
254
442
 
255
443
  # Rust (after cargo install ironmark --features cli)
256
- ironmark --ansi README.md
257
- ironmark --ansi --width 120 README.md
258
- echo '# Hello' | ironmark --ansi
259
- cat doc.md | ironmark --ansi --math --wiki-links
444
+ echo '# Hello' | ironmark
445
+ ironmark --format ansi README.md
446
+ ironmark --format ansi --width 120 README.md
447
+ cat doc.md | ironmark --format ansi --math --wiki-links
260
448
  ```
261
449
 
262
450
  ## Rust
@@ -266,21 +454,21 @@ cargo add ironmark
266
454
  ```
267
455
 
268
456
  ```rust
269
- use ironmark::{parse, ParseOptions};
457
+ use ironmark::{render_html, ParseOptions};
270
458
 
271
459
  fn main() {
272
460
  // with defaults
273
- let html = parse("# Hello\n\nThis is **fast**.", &ParseOptions::default());
461
+ let html = render_html("# Hello\n\nThis is **fast**.", &ParseOptions::default());
274
462
 
275
463
  // with custom options
276
- let html = parse("line one\nline two", &ParseOptions {
464
+ let html = render_html("line one\nline two", &ParseOptions {
277
465
  hard_breaks: false,
278
466
  enable_strikethrough: false,
279
467
  ..Default::default()
280
468
  });
281
469
 
282
470
  // safe mode for untrusted input
283
- let html = parse("<script>alert(1)</script>", &ParseOptions {
471
+ let html = render_html("<script>alert(1)</script>", &ParseOptions {
284
472
  disable_raw_html: true,
285
473
  max_input_size: 1_000_000, // 1 MB
286
474
  ..Default::default()
@@ -293,10 +481,10 @@ fn main() {
293
481
  `parse_to_ast()` returns the typed Rust AST (`Block`) directly:
294
482
 
295
483
  ```rust
296
- use ironmark::{Block, ParseOptions, parse_to_ast};
484
+ use ironmark::{Block, ParseOptions, parse_markdown};
297
485
 
298
486
  fn main() {
299
- let ast = parse_to_ast("# Hello", &ParseOptions::default());
487
+ let ast = parse_markdown("# Hello", &ParseOptions::default());
300
488
 
301
489
  match ast {
302
490
  Block::Document { children } => {
@@ -368,10 +556,10 @@ fn main() {
368
556
  Render an AST back to Markdown syntax:
369
557
 
370
558
  ```rust
371
- use ironmark::{parse_to_ast, render_markdown, ParseOptions};
559
+ use ironmark::{parse_markdown, render_markdown, ParseOptions};
372
560
 
373
561
  fn main() {
374
- let ast = parse_to_ast("# Hello\n\n**World**", &ParseOptions::default());
562
+ let ast = parse_markdown("# Hello\n\n**World**", &ParseOptions::default());
375
563
  let md = render_markdown(&ast);
376
564
  // Returns: "# Hello\n\n**World**"
377
565
  }
@@ -379,18 +567,18 @@ fn main() {
379
567
 
380
568
  ### ANSI Terminal Output
381
569
 
382
- `render_ansi()` renders Markdown as ANSI-coloured terminal output. Pass `Some(&AnsiOptions { .. })` to control width, colour, and line numbers, or `None` for defaults.
570
+ `render_ansi_terminal()` renders Markdown as ANSI-coloured terminal output. Pass `Some(&AnsiOptions { .. })` to control width, colour, and line numbers, or `None` for defaults.
383
571
 
384
572
  ````rust
385
- use ironmark::{AnsiOptions, ParseOptions, render_ansi};
573
+ use ironmark::{AnsiOptions, ParseOptions, render_ansi_terminal};
386
574
 
387
575
  fn main() {
388
576
  // Defaults — width 80, colour enabled
389
- let out = render_ansi("# Hello\n\n**bold** and `code`", &ParseOptions::default(), None);
577
+ let out = render_ansi_terminal("# Hello\n\n**bold** and `code`", &ParseOptions::default(), None);
390
578
  print!("{out}");
391
579
 
392
580
  // Custom options — 120 columns, line numbers in code blocks
393
- let out = render_ansi(
581
+ let out = render_ansi_terminal(
394
582
  "# Hello\n\n```rust\nfn main() {}\n```",
395
583
  &ParseOptions::default(),
396
584
  Some(&AnsiOptions { width: 120, line_numbers: true, ..AnsiOptions::default() }),
@@ -398,7 +586,7 @@ fn main() {
398
586
  print!("{out}");
399
587
 
400
588
  // With padding — adds horizontal spacing on both sides
401
- let out = render_ansi(
589
+ let out = render_ansi_terminal(
402
590
  "# Hello\n\n> A quote",
403
591
  &ParseOptions::default(),
404
592
  Some(&AnsiOptions { padding: 2, ..AnsiOptions::default() }),
@@ -406,7 +594,7 @@ fn main() {
406
594
  print!("{out}");
407
595
 
408
596
  // Plain text — no ANSI codes (e.g. for writing to a file)
409
- let plain = render_ansi(
597
+ let plain = render_ansi_terminal(
410
598
  "# Hello",
411
599
  &ParseOptions::default(),
412
600
  Some(&AnsiOptions { color: false, ..AnsiOptions::default() }),
@@ -452,7 +640,7 @@ cc -o example example.c -L target/release -l ironmark \
452
640
  #include <stdio.h>
453
641
 
454
642
  int main(void) {
455
- char *html = ironmark_parse("# Hello\n\nThis is **fast**.");
643
+ char *html = ironmark_render_html("# Hello\n\nThis is **fast**.");
456
644
  if (html) {
457
645
  printf("%s\n", html);
458
646
  ironmark_free(html);
@@ -461,7 +649,7 @@ int main(void) {
461
649
  }
462
650
  ```
463
651
 
464
- **Memory contract**: `ironmark_parse` returns a heap-allocated string. You **must** free it with `ironmark_free`. Passing any other pointer to `ironmark_free` is undefined behaviour. Both functions are null-safe: `ironmark_parse(NULL)` returns `NULL`; `ironmark_free(NULL)` is a no-op.
652
+ **Memory contract**: `ironmark_render_html` returns a heap-allocated string. You **must** free it with `ironmark_free`. Passing any other pointer to `ironmark_free` is undefined behaviour. Both functions are null-safe: `ironmark_render_html(NULL)` returns `NULL`; `ironmark_free(NULL)` is a no-op.
465
653
 
466
654
  Parsing always uses the default `ParseOptions` (all extensions enabled, `disable_raw_html` off). Options are not yet configurable through the C API.
467
655