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/wasm/cli.js CHANGED
@@ -1,6 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFileSync } from "node:fs";
3
- import { parse, parseToAst, renderAnsi } from "./node.js";
3
+ import {
4
+ renderHtml,
5
+ parseMarkdown,
6
+ renderAnsiTerminal,
7
+ getCapabilities,
8
+ getDefaultOptions,
9
+ getPresets,
10
+ } from "./node.js";
4
11
 
5
12
  const VERSION = JSON.parse(
6
13
  readFileSync(new URL("../package.json", import.meta.url), "utf8"),
@@ -15,18 +22,23 @@ USAGE:
15
22
  Default format is html.
16
23
 
17
24
  OPTIONS (all formats):
18
- --format <html|ansi|ast> Output format; ast also accepts 'json' (default: html)
19
- --no-hard-breaks Don't turn soft newlines into hard line breaks
20
- --no-tables Disable pipe table syntax
21
- --no-highlight Disable ==highlight== syntax
22
- --no-strikethrough Disable ~~strikethrough~~ syntax
23
- --no-underline Disable ++underline++ syntax
24
- --no-autolink Disable bare URL auto-linking
25
- --no-task-lists Disable - [x] task list syntax
26
- --math Enable $inline$ and $$display$$ math
27
- --wiki-links Enable [[wiki link]] syntax
28
- -h, --help Print this help and exit
29
- -V, --version Print version and exit
25
+ --format <html|ansi|ast> Output format; ast also accepts 'json' (default: html)
26
+ --preset <name> Apply a named option preset: default, safe, strict, llm
27
+ --safe Alias for --preset safe (disable raw HTML, enable tag filter)
28
+ --no-hard-breaks Don't turn soft newlines into hard line breaks
29
+ --no-tables Disable pipe table syntax
30
+ --no-highlight Disable ==highlight== syntax
31
+ --no-strikethrough Disable ~~strikethrough~~ syntax
32
+ --no-underline Disable ++underline++ syntax
33
+ --no-autolink Disable bare URL auto-linking
34
+ --no-task-lists Disable - [x] task list syntax
35
+ --math Enable $inline$ and $$display$$ math
36
+ --wiki-links Enable [[wiki link]] syntax
37
+ --capabilities Print machine-readable capabilities JSON and exit
38
+ --default-options Print resolved default options JSON and exit
39
+ --list-presets Print all preset names and their options and exit
40
+ -h, --help Print this help and exit
41
+ -V, --version Print version and exit
30
42
 
31
43
  OPTIONS (ansi format only):
32
44
  --width N Terminal column width (default: auto-detect, fallback 80)
@@ -41,6 +53,9 @@ EXAMPLES:
41
53
  npx ironmark --format ast README.md
42
54
  npx ironmark --format ansi --width 120 --no-color README.md | less
43
55
  cat doc.md | npx ironmark --format ansi --math --wiki-links
56
+ npx ironmark --safe README.md
57
+ npx ironmark --preset llm README.md
58
+ npx ironmark --capabilities
44
59
  `;
45
60
 
46
61
  const args = process.argv.slice(2);
@@ -61,6 +76,18 @@ for (let i = 0; i < args.length; i++) {
61
76
  console.log(`ironmark ${VERSION}`);
62
77
  process.exit(0);
63
78
  break;
79
+ case "--capabilities":
80
+ process.stdout.write(JSON.stringify(getCapabilities(), null, 2) + "\n");
81
+ process.exit(0);
82
+ break;
83
+ case "--default-options":
84
+ process.stdout.write(JSON.stringify(getDefaultOptions(), null, 2) + "\n");
85
+ process.exit(0);
86
+ break;
87
+ case "--list-presets":
88
+ process.stdout.write(JSON.stringify(getPresets(), null, 2) + "\n");
89
+ process.exit(0);
90
+ break;
64
91
  case "--format": {
65
92
  const val = args[++i];
66
93
  if (val === undefined) {
@@ -70,6 +97,18 @@ for (let i = 0; i < args.length; i++) {
70
97
  format = val;
71
98
  break;
72
99
  }
100
+ case "--preset": {
101
+ const val = args[++i];
102
+ if (val === undefined) {
103
+ console.error("error: --preset requires a value (default, safe, strict, llm)");
104
+ process.exit(2);
105
+ }
106
+ parseOptions.preset = val;
107
+ break;
108
+ }
109
+ case "--safe":
110
+ parseOptions.safe = true;
111
+ break;
73
112
  case "--no-color":
74
113
  case "--no-colour":
75
114
  ansiOptions.color = false;
@@ -188,9 +227,9 @@ if (files.length === 0) {
188
227
  }
189
228
 
190
229
  if (fmt === "html") {
191
- process.stdout.write(parse(input, parseOptions));
230
+ process.stdout.write(renderHtml(input, parseOptions));
192
231
  } else if (fmt === "ansi") {
193
- process.stdout.write(renderAnsi(input, parseOptions, ansiOptions));
232
+ process.stdout.write(renderAnsiTerminal(input, parseOptions, ansiOptions));
194
233
  } else {
195
- process.stdout.write(JSON.stringify(parseToAst(input, parseOptions), null, 2) + "\n");
234
+ process.stdout.write(JSON.stringify(parseMarkdown(input, parseOptions), null, 2) + "\n");
196
235
  }
package/wasm/index.d.ts CHANGED
@@ -1,6 +1,48 @@
1
+ // ─── Input types ─────────────────────────────────────────────────────────────
2
+
1
3
  export type MarkdownInput = string | Uint8Array | ArrayBuffer | ArrayBufferView;
2
4
 
5
+ // ─── Preset names ─────────────────────────────────────────────────────────────
6
+
7
+ /**
8
+ * Named option presets for common use cases.
9
+ *
10
+ * - `"default"` — Default ironmark behavior; all extensions enabled.
11
+ * - `"safe"` — Disables raw HTML and enables the GFM tag filter. Use for untrusted input.
12
+ * - `"strict"` — CommonMark-only; disables extensions and restricts permissive behaviors.
13
+ * - `"llm"` — Deterministic, structure-first output optimized for AI pipelines and agents.
14
+ * Disables autolink, wiki links, math, hard breaks, and raw HTML; enables heading IDs
15
+ * and whitespace normalization.
16
+ */
17
+ export type PresetName = "default" | "safe" | "strict" | "llm";
18
+
19
+ // ─── Parse options ────────────────────────────────────────────────────────────
20
+
3
21
  export interface ParseOptions {
22
+ /**
23
+ * Apply a named preset before applying any explicit options.
24
+ * Explicit options always override the preset.
25
+ */
26
+ preset?: PresetName;
27
+
28
+ /**
29
+ * Enable safe rendering. Equivalent to `{ disableRawHtml: true, tagFilter: true }`.
30
+ * Explicit `disableRawHtml` / `tagFilter` values override this.
31
+ */
32
+ safe?: boolean;
33
+
34
+ /**
35
+ * When true, output is normalized for deterministic comparison:
36
+ * whitespace is collapsed and output is stable across runs.
37
+ */
38
+ deterministic?: boolean;
39
+
40
+ /**
41
+ * Reserved for future use. When true, the AST will include source position
42
+ * metadata. Has no effect in the current version.
43
+ */
44
+ stableAst?: boolean;
45
+
4
46
  /** When true, every newline in a paragraph becomes a hard line break (`<br />`). Default: true. */
5
47
  hardBreaks?: boolean;
6
48
  /** Enable ==highlight== syntax for `<mark>`. Default: true. */
@@ -39,37 +81,13 @@ export interface ParseOptions {
39
81
  enableLatexMath?: boolean;
40
82
  }
41
83
 
42
- /**
43
- * Initialize the WASM module.
44
- *
45
- * - **Node.js**: This is a no-op — WASM is embedded and loaded synchronously at import time.
46
- * - **Browser/Bundler**: Must be called (and awaited) before using `parse()`.
47
- * Optionally pass a URL or `WebAssembly.Module` to override the default WASM location.
48
- * Calling `init()` multiple times is safe (subsequent calls are no-ops).
49
- */
50
- export declare function init(input?: string | URL | WebAssembly.Module): Promise<void>;
84
+ // ─── Render-specific option aliases ──────────────────────────────────────────
51
85
 
52
- /**
53
- * Parse Markdown to HTML.
54
- *
55
- * @param markdown - Markdown source (string or binary).
56
- * @param options - Optional parsing options.
57
- * @returns HTML string.
58
- */
59
- export declare function parse(markdown: MarkdownInput, options?: ParseOptions): string;
86
+ /** Options for `renderHtml()`. Same as `ParseOptions`. */
87
+ export type RenderHtmlOptions = ParseOptions;
60
88
 
61
- /**
62
- * Parse Markdown and return the block-level AST as a JSON string.
63
- *
64
- * @param markdown - Markdown source (string or binary).
65
- * @param options - Optional parsing options.
66
- * @returns JSON string representing the AST.
67
- */
68
- export declare function parseToAst(markdown: MarkdownInput, options?: ParseOptions): string;
89
+ // ─── ANSI options ─────────────────────────────────────────────────────────────
69
90
 
70
- /**
71
- * Options for the ANSI terminal renderer.
72
- */
73
91
  export interface AnsiOptions {
74
92
  /**
75
93
  * Terminal column width for word-wrap, heading underlines, and thematic breaks.
@@ -88,99 +106,267 @@ export interface AnsiOptions {
88
106
  lineNumbers?: boolean;
89
107
  /**
90
108
  * Horizontal padding to add on both sides of every output line.
91
- * The output width remains `width`; padding reduces the available text area.
92
109
  * Also adds `ceil(padding / 2)` blank lines at the top. Default: `0`.
93
110
  */
94
111
  padding?: number;
95
112
  }
96
113
 
114
+ // ─── HTML parse options ───────────────────────────────────────────────────────
115
+
116
+ export interface HtmlParseOptions {
117
+ /**
118
+ * If true, unknown HTML tags (like `<sup>`, `<sub>`, `<abbr>`) are preserved
119
+ * as raw HTML in the Markdown output. If false (default), unknown tags are
120
+ * stripped but their text content is kept.
121
+ */
122
+ preserveUnknownAsHtml?: boolean;
123
+ }
124
+
125
+ // ─── AST node types ───────────────────────────────────────────────────────────
126
+
127
+ /**
128
+ * A single AST node. The `t` field is the type discriminant.
129
+ * Child nodes are in `c`; leaf text content is in `text`.
130
+ */
131
+ export interface AstNode {
132
+ t: string;
133
+ c?: AstNode[];
134
+ text?: string;
135
+ [key: string]: unknown;
136
+ }
137
+
138
+ /** Parsed AST: an array of top-level block nodes returned by `parseMarkdown()`. */
139
+ export type MarkdownAst = AstNode[];
140
+
141
+ // ─── Introspection return types ───────────────────────────────────────────────
142
+
143
+ export interface Capabilities {
144
+ astSchemaVersion: string;
145
+ formats: string[];
146
+ presets: PresetName[];
147
+ extensions: string[];
148
+ security: string[];
149
+ }
150
+
151
+ export interface HeadingInfo {
152
+ level: number;
153
+ text: string;
154
+ id: string;
155
+ }
156
+
157
+ export interface AstSummary {
158
+ blockCount: number;
159
+ nodeCounts: Record<string, number>;
160
+ }
161
+
162
+ // ─── Initialization ───────────────────────────────────────────────────────────
163
+
164
+ /**
165
+ * Initialize the WASM module.
166
+ *
167
+ * - **Node.js**: no-op — WASM is embedded and loaded synchronously at import time.
168
+ * - **Browser/Bundler**: must be awaited before calling any other function.
169
+ * Accepts a URL or `WebAssembly.Module` to override the default WASM location.
170
+ * Safe to call multiple times.
171
+ *
172
+ * @example
173
+ * ```ts
174
+ * import { init, renderHtml } from "ironmark";
175
+ * await init();
176
+ * const html = renderHtml("# Hello");
177
+ * ```
178
+ */
179
+ export declare function init(input?: string | URL | WebAssembly.Module): Promise<void>;
180
+
181
+ // ─── Core API ─────────────────────────────────────────────────────────────────
182
+
183
+ /**
184
+ * Parse Markdown and return the structured AST as a JavaScript object.
185
+ *
186
+ * The recommended entry point for AI pipelines, agents, and any workflow
187
+ * that needs to inspect or transform document structure.
188
+ *
189
+ * @param input - Markdown source.
190
+ * @param options - Optional parse options or preset.
191
+ * @returns Parsed AST — a JavaScript array, no `JSON.parse()` needed.
192
+ *
193
+ * @example
194
+ * ```ts
195
+ * import { parseMarkdown } from "ironmark";
196
+ *
197
+ * const ast = parseMarkdown("# Hello\n\n**World**");
198
+ * ```
199
+ *
200
+ * @example With a preset
201
+ * ```ts
202
+ * const ast = parseMarkdown(userInput, { preset: "llm" });
203
+ * ```
204
+ */
205
+ export declare function parseMarkdown(input: MarkdownInput, options?: ParseOptions): MarkdownAst;
206
+
207
+ /**
208
+ * Render Markdown to an HTML string.
209
+ *
210
+ * Use `safe: true` or `preset: "safe"` for any untrusted input.
211
+ *
212
+ * @param input - Markdown source.
213
+ * @param options - Optional render options.
214
+ * @returns HTML string.
215
+ *
216
+ * @example
217
+ * ```ts
218
+ * import { renderHtml } from "ironmark";
219
+ *
220
+ * const html = renderHtml("# Hello");
221
+ * const safeHtml = renderHtml(userInput, { safe: true });
222
+ * ```
223
+ */
224
+ export declare function renderHtml(input: MarkdownInput, options?: RenderHtmlOptions): string;
225
+
226
+ /**
227
+ * Render an AST back to a Markdown string.
228
+ *
229
+ * Pass the result of `parseMarkdown()` directly — accepts an AST object or
230
+ * a JSON string. Useful for normalizing Markdown or round-trip conversion.
231
+ *
232
+ * @param ast - AST from `parseMarkdown()`, or a JSON string.
233
+ * @returns Markdown string.
234
+ *
235
+ * @example
236
+ * ```ts
237
+ * import { parseMarkdown, renderMarkdown } from "ironmark";
238
+ *
239
+ * const ast = parseMarkdown("**Hello**");
240
+ * const md = renderMarkdown(ast);
241
+ * ```
242
+ */
243
+ export declare function renderMarkdown(ast: MarkdownAst | string): string;
244
+
97
245
  /**
98
246
  * Render Markdown as ANSI-coloured terminal output.
99
247
  *
100
- * Produces a string containing ANSI 256-colour escape codes suitable for
101
- * display in a terminal emulator. Headings, code blocks, inline code,
102
- * blockquotes, tables, and inline formatting are all styled distinctly.
248
+ * Produces a string with ANSI 256-colour escape codes suitable for TTY display.
103
249
  *
104
- * @param markdown - Markdown source (string or binary).
105
- * @param options - Optional parse options (same flags as `parse()`).
106
- * @param ansiOptions - Optional ANSI rendering options (width, color, lineNumbers).
107
- * @returns String with ANSI escape codes (or plain text when `color: false`).
250
+ * @param input - Markdown source.
251
+ * @param options - Optional parse options.
252
+ * @param ansiOptions - Optional ANSI rendering options.
253
+ * @returns String with ANSI escape codes.
108
254
  *
109
255
  * @example
110
256
  * ```ts
111
- * import { renderAnsi } from "ironmark";
112
- * const out = renderAnsi("# Hello\n\n**bold** and `code`");
113
- * process.stdout.write(out);
257
+ * import { renderAnsiTerminal } from "ironmark";
258
+ *
259
+ * process.stdout.write(renderAnsiTerminal("# Hello\n\n**bold**"));
114
260
  * ```
115
261
  */
116
- export declare function renderAnsi(
117
- markdown: MarkdownInput,
262
+ export declare function renderAnsiTerminal(
263
+ input: MarkdownInput,
118
264
  options?: ParseOptions,
119
265
  ansiOptions?: AnsiOptions,
120
266
  ): string;
121
267
 
122
268
  /**
123
- * Options for HTML-to-Markdown parsing.
124
- */
125
- export interface HtmlParseOptions {
126
- /**
127
- * If true, unknown HTML tags (like `<sup>`, `<sub>`, `<abbr>`) are preserved
128
- * as raw HTML in the Markdown output. If false (default), unknown tags are
129
- * stripped but their text content is kept.
130
- */
131
- preserveUnknownAsHtml?: boolean;
132
- }
133
-
134
- /**
135
- * Parse an HTML string and return the AST as a JSON string.
269
+ * Parse an HTML string and return the AST as a JavaScript object.
136
270
  *
137
- * This converts HTML back into the same AST structure used by the Markdown parser,
138
- * enabling HTML-to-Markdown conversion.
271
+ * Converts HTML into the same AST structure used by the Markdown parser,
272
+ * enabling HTMLMarkdown conversion via `renderMarkdown()`.
139
273
  *
140
274
  * @param html - HTML source string.
141
275
  * @param preserveUnknownAsHtml - If true, unknown HTML tags are preserved as raw HTML.
142
- * @returns JSON string representing the AST.
276
+ * @returns Parsed AST object.
143
277
  *
144
278
  * @example
145
279
  * ```ts
146
- * import { parseHtmlToAst } from "ironmark";
280
+ * import { parseHtmlToAst, renderMarkdown } from "ironmark";
281
+ *
147
282
  * const ast = parseHtmlToAst("<h1>Hello</h1><p>World</p>");
148
- * console.log(JSON.parse(ast));
283
+ * const md = renderMarkdown(ast);
149
284
  * ```
150
285
  */
151
- export declare function parseHtmlToAst(html: string, preserveUnknownAsHtml?: boolean): string;
286
+ export declare function parseHtmlToAst(html: string, preserveUnknownAsHtml?: boolean): MarkdownAst;
152
287
 
153
288
  /**
154
289
  * Convert HTML to Markdown.
155
290
  *
156
- * This parses HTML and renders it as Markdown syntax.
157
- *
158
291
  * @param html - HTML source string.
159
- * @param preserveUnknownAsHtml - If true, unknown HTML tags are preserved as raw HTML in output.
292
+ * @param preserveUnknownAsHtml - If true, unknown HTML tags are preserved as raw HTML.
160
293
  * @returns Markdown string.
161
294
  *
162
295
  * @example
163
296
  * ```ts
164
297
  * import { htmlToMarkdown } from "ironmark";
298
+ *
165
299
  * const md = htmlToMarkdown("<p><strong>Bold</strong> text</p>");
166
300
  * // Returns: "**Bold** text"
167
301
  * ```
168
302
  */
169
303
  export declare function htmlToMarkdown(html: string, preserveUnknownAsHtml?: boolean): string;
170
304
 
305
+ // ─── Introspection helpers ────────────────────────────────────────────────────
306
+
307
+ /**
308
+ * Return machine-readable metadata about this ironmark build.
309
+ *
310
+ * @example
311
+ * ```ts
312
+ * import { getCapabilities } from "ironmark";
313
+ * const caps = getCapabilities();
314
+ * // { astSchemaVersion: "2", formats: [...], presets: [...], ... }
315
+ * ```
316
+ */
317
+ export declare function getCapabilities(): Capabilities;
318
+
319
+ /**
320
+ * Return the current AST schema version string.
321
+ * An increment signals a breaking change to the AST node shape.
322
+ */
323
+ export declare function getAstSchemaVersion(): string;
324
+
325
+ /**
326
+ * Return the resolved default `ParseOptions` — the effective value of every
327
+ * option when none are specified.
328
+ */
329
+ export declare function getDefaultOptions(): Required<
330
+ Omit<ParseOptions, "preset" | "safe" | "deterministic" | "stableAst">
331
+ >;
332
+
171
333
  /**
172
- * Render an AST (as JSON) to Markdown.
334
+ * Return all named presets and their resolved option objects.
173
335
  *
174
- * This takes a JSON string representing an ironmark AST and renders it as Markdown.
336
+ * @example
337
+ * ```ts
338
+ * import { getPresets } from "ironmark";
339
+ * const { llm } = getPresets();
340
+ * ```
341
+ */
342
+ export declare function getPresets(): Record<PresetName, Partial<ParseOptions>>;
343
+
344
+ // ─── Utility functions ────────────────────────────────────────────────────────
345
+
346
+ /**
347
+ * Extract all headings from a parsed AST.
175
348
  *
176
- * @param astJson - JSON string representing the AST (as returned by `parseToAst` or `parseHtmlToAst`).
177
- * @returns Markdown string.
349
+ * Returns level, plain-text content, and a slugified `id` for each heading.
178
350
  *
179
351
  * @example
180
352
  * ```ts
181
- * import { parseToAst, renderMarkdown } from "ironmark";
182
- * const ast = parseToAst("# Hello\n\n**World**");
183
- * const md = renderMarkdown(ast);
353
+ * import { parseMarkdown, extractHeadings } from "ironmark";
354
+ *
355
+ * const headings = extractHeadings(parseMarkdown("# Hello\n\n## World"));
356
+ * // [{ level: 1, text: "Hello", id: "hello" }, { level: 2, text: "World", id: "world" }]
357
+ * ```
358
+ */
359
+ export declare function extractHeadings(ast: MarkdownAst): HeadingInfo[];
360
+
361
+ /**
362
+ * Summarize an AST: count top-level blocks and all node types.
363
+ *
364
+ * @example
365
+ * ```ts
366
+ * import { parseMarkdown, summarizeAst } from "ironmark";
367
+ *
368
+ * const summary = summarizeAst(parseMarkdown("# Hello\n\nA paragraph.\n\n- item"));
369
+ * // { blockCount: 3, nodeCounts: { Heading: 1, Paragraph: 1, List: 1, ... } }
184
370
  * ```
185
371
  */
186
- export declare function renderMarkdown(astJson: string): string;
372
+ export declare function summarizeAst(ast: MarkdownAst): AstSummary;