ironmark 1.13.0 → 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";
69
+ import { renderHtml, parseMarkdown } from "ironmark";
81
70
 
82
- const html = parse("# Hello\n\nThis is **fast**.");
83
-
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.
83
+ ### Markdown AST
100
84
 
101
- ### HTML to Markdown
102
-
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,29 +155,207 @@ 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**.");
204
+ ```
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 } }
194
313
  ```
195
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
361
  Render Markdown as HTML, ANSI terminal output, or an AST. Default format is `html`.
@@ -225,19 +388,24 @@ Both CLIs support the same flags:
225
388
 
226
389
  ```text
227
390
  OPTIONS (all formats):
228
- --format <html|ansi|ast> Output format; ast also accepts 'json' (default: html)
229
- --no-hard-breaks Don't turn soft newlines into hard line breaks
230
- --no-tables Disable pipe table syntax
231
- --no-highlight Disable ==highlight== syntax
232
- --no-strikethrough Disable ~~strikethrough~~ syntax
233
- --no-underline Disable ++underline++ syntax
234
- --no-autolink Disable bare URL auto-linking
235
- --no-task-lists Disable - [x] task list syntax
236
- --math Enable $inline$ and $$display$$ math
237
- --wiki-links Enable [[wiki link]] syntax
238
- --max-size N Truncate input to N bytes (Rust only)
239
- -h, --help Print this help and exit
240
- -V, --version Print version and exit
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
241
409
 
242
410
  OPTIONS (ansi format only):
243
411
  --width N Terminal column width (default: auto-detect, fallback 80)
@@ -253,6 +421,13 @@ OPTIONS (ansi format only):
253
421
  echo '# Hello' | npx ironmark
254
422
  npx ironmark README.md
255
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
+
256
431
  # Render as ANSI terminal output
257
432
  npx ironmark --format ansi README.md
258
433
  npx ironmark --format ansi --width 120 README.md
@@ -261,6 +436,10 @@ npx ironmark --format ansi --no-color README.md | less
261
436
  # Render as AST (JSON)
262
437
  npx ironmark --format ast README.md
263
438
 
439
+ # Inspect capabilities
440
+ npx ironmark --capabilities
441
+ npx ironmark --list-presets
442
+
264
443
  # Rust (after cargo install ironmark --features cli)
265
444
  echo '# Hello' | ironmark
266
445
  ironmark --format ansi README.md
@@ -275,21 +454,21 @@ cargo add ironmark
275
454
  ```
276
455
 
277
456
  ```rust
278
- use ironmark::{parse, ParseOptions};
457
+ use ironmark::{render_html, ParseOptions};
279
458
 
280
459
  fn main() {
281
460
  // with defaults
282
- let html = parse("# Hello\n\nThis is **fast**.", &ParseOptions::default());
461
+ let html = render_html("# Hello\n\nThis is **fast**.", &ParseOptions::default());
283
462
 
284
463
  // with custom options
285
- let html = parse("line one\nline two", &ParseOptions {
464
+ let html = render_html("line one\nline two", &ParseOptions {
286
465
  hard_breaks: false,
287
466
  enable_strikethrough: false,
288
467
  ..Default::default()
289
468
  });
290
469
 
291
470
  // safe mode for untrusted input
292
- let html = parse("<script>alert(1)</script>", &ParseOptions {
471
+ let html = render_html("<script>alert(1)</script>", &ParseOptions {
293
472
  disable_raw_html: true,
294
473
  max_input_size: 1_000_000, // 1 MB
295
474
  ..Default::default()
@@ -302,10 +481,10 @@ fn main() {
302
481
  `parse_to_ast()` returns the typed Rust AST (`Block`) directly:
303
482
 
304
483
  ```rust
305
- use ironmark::{Block, ParseOptions, parse_to_ast};
484
+ use ironmark::{Block, ParseOptions, parse_markdown};
306
485
 
307
486
  fn main() {
308
- let ast = parse_to_ast("# Hello", &ParseOptions::default());
487
+ let ast = parse_markdown("# Hello", &ParseOptions::default());
309
488
 
310
489
  match ast {
311
490
  Block::Document { children } => {
@@ -377,10 +556,10 @@ fn main() {
377
556
  Render an AST back to Markdown syntax:
378
557
 
379
558
  ```rust
380
- use ironmark::{parse_to_ast, render_markdown, ParseOptions};
559
+ use ironmark::{parse_markdown, render_markdown, ParseOptions};
381
560
 
382
561
  fn main() {
383
- let ast = parse_to_ast("# Hello\n\n**World**", &ParseOptions::default());
562
+ let ast = parse_markdown("# Hello\n\n**World**", &ParseOptions::default());
384
563
  let md = render_markdown(&ast);
385
564
  // Returns: "# Hello\n\n**World**"
386
565
  }
@@ -388,18 +567,18 @@ fn main() {
388
567
 
389
568
  ### ANSI Terminal Output
390
569
 
391
- `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.
392
571
 
393
572
  ````rust
394
- use ironmark::{AnsiOptions, ParseOptions, render_ansi};
573
+ use ironmark::{AnsiOptions, ParseOptions, render_ansi_terminal};
395
574
 
396
575
  fn main() {
397
576
  // Defaults — width 80, colour enabled
398
- 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);
399
578
  print!("{out}");
400
579
 
401
580
  // Custom options — 120 columns, line numbers in code blocks
402
- let out = render_ansi(
581
+ let out = render_ansi_terminal(
403
582
  "# Hello\n\n```rust\nfn main() {}\n```",
404
583
  &ParseOptions::default(),
405
584
  Some(&AnsiOptions { width: 120, line_numbers: true, ..AnsiOptions::default() }),
@@ -407,7 +586,7 @@ fn main() {
407
586
  print!("{out}");
408
587
 
409
588
  // With padding — adds horizontal spacing on both sides
410
- let out = render_ansi(
589
+ let out = render_ansi_terminal(
411
590
  "# Hello\n\n> A quote",
412
591
  &ParseOptions::default(),
413
592
  Some(&AnsiOptions { padding: 2, ..AnsiOptions::default() }),
@@ -415,7 +594,7 @@ fn main() {
415
594
  print!("{out}");
416
595
 
417
596
  // Plain text — no ANSI codes (e.g. for writing to a file)
418
- let plain = render_ansi(
597
+ let plain = render_ansi_terminal(
419
598
  "# Hello",
420
599
  &ParseOptions::default(),
421
600
  Some(&AnsiOptions { color: false, ..AnsiOptions::default() }),
@@ -461,7 +640,7 @@ cc -o example example.c -L target/release -l ironmark \
461
640
  #include <stdio.h>
462
641
 
463
642
  int main(void) {
464
- char *html = ironmark_parse("# Hello\n\nThis is **fast**.");
643
+ char *html = ironmark_render_html("# Hello\n\nThis is **fast**.");
465
644
  if (html) {
466
645
  printf("%s\n", html);
467
646
  ironmark_free(html);
@@ -470,7 +649,7 @@ int main(void) {
470
649
  }
471
650
  ```
472
651
 
473
- **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.
474
653
 
475
654
  Parsing always uses the default `ParseOptions` (all extensions enabled, `disable_raw_html` off). Options are not yet configurable through the C API.
476
655
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ironmark",
3
- "version": "1.13.0",
3
+ "version": "2.0.0",
4
4
  "description": "Very fast markdown parser in Rust, consumable from JavaScript/TypeScript via WebAssembly",
5
5
  "keywords": [
6
6
  "markdown",