page-markdown 0.1.5 → 0.2.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/CHANGELOG.md CHANGED
@@ -1,186 +1,273 @@
1
- # Changelog
2
-
3
- ## 0.1.5 — 2026-11-04
4
-
5
- Content-fidelity fixes, each with a new fixture run in Node and in the browser:
6
-
7
- - **Code blocks keep their lines.** Lines marked up with `<br>` or block
8
- elements (Docusaurus and similar highlighters) came out joined on one line.
9
- - **Copy buttons stay out of code.** A `<button>` inside `<pre>` leaked its
10
- label into the code and lost the language. Whitespace around the `<code>`
11
- no longer loses the fence.
12
- - **Text beside a heading is kept.** A `<div>` holding a heading and bare text
13
- was replaced by the heading, deleting the text.
14
- - **Row spans keep columns aligned.** Later cells shifted left. A spanned cell
15
- now repeats in each row of a GFM table, and block-rendered tables label cells
16
- by their true column.
17
- - **Large tables no longer freeze the page.** Table checks ran once per cell,
18
- so time grew with the square of the size: 500 rows took about 8 seconds in
19
- Chrome.
20
- - **Modes leave more code alone.** Fences in list items and blockquotes, and
21
- inline code, were edited by `no_links`, `text_only` and blank-line cleanup.
22
- - **A lead paragraph is no longer doubled** when it starts with a link or holds
23
- an entity such as `&lt;`.
24
- - **Script links are caught however they are written.** `javascript:` with an
25
- embedded newline, tab or control character got through, as did `vbscript:`
26
- and `data:`. These links keep only their text. Images with such a source are
27
- dropped.
28
- - **`<base href>` is respected** for links and images.
29
- - **A linked image with empty alt is kept.**
30
- - **An ad marker on `<body>` no longer empties the page.** Sites put page-wide ad
31
- settings there (`data-ad-unit` on heise.de), and the ad filter removed the whole
32
- body. `<html>` and `<body>` are never treated as ads; ads inside them still are.
33
- - `stats.markdownChars` now matches the returned Markdown.
34
- - The browser bundle grows to about 67.2 KB minified; the size ceiling is now
35
- 68 KB.
36
-
37
- - **TypeScript types.** Declarations for `pageToMarkdown`, its options, the full
38
- result and every stage export are generated from the JSDoc in `src` into
39
- `dist/types`, through a `types` condition in `exports`. The result is
40
- documented in a README table. `npm run typecheck` is part of `npm run ci`: it
41
- checks `src` against its JSDoc, checks a consumer file with `tsc`, and checks
42
- the declarations against real output from every fixture, in both directions.
43
- Only comments change in the existing modules; the bundle is the same size.
44
- - **Bench.** `npm run bench` adds a Readability + Turndown baseline, word recall
45
- and precision, and 11 held-out pages whose probes were committed before any
46
- engine ran on them. The headline changes from 32/32 against 11/32 to 84/100
47
- against 74/100 and 61/100 on the held-out pages; see `bench/RESULTS.md` for the
48
- failures. `bench/fetch-pages.mjs` now decodes by declared charset and keeps the
49
- last good copy when a fetch fails. `npm run bench:write` updates
50
- `bench/RESULTS.md`; `npm run bench` only prints.
51
- - **Licence summary.** The README now says in plain words who can use the package
52
- free and who needs a commercial licence, and how to ask for one. The licence
53
- itself is unchanged.
54
- - **Browser run.** `npm run test:browser` runs the golden fixtures in
55
- headless Chrome or Edge, with the browser's own DOM, and checks what Node
56
- can't show:
57
- - the live document is left unmodified;
58
- - real open shadow roots are captured, nested roots and slotted children
59
- included;
60
- - closed shadow roots stay out.
61
-
62
- It is part of `npm run ci` and needs no new dependencies. The package's output
63
- in Chrome and Edge matches its Node output.
64
-
65
- ## 0.1.4 — 2026-11-02
66
-
67
- - **text-sanctifier 1.3.8** (was 1.3.3). `removeHiddenText` now also removes
68
- runs of reserved tag-plane code points (U+E0080–U+E00FF, U+E01F0–U+E0FFF),
69
- which 1.3.3 did not find, and reports them as `hidden_reserved_tag_plane`.
70
- The browser bundle grows to about 65.6 KB minified.
71
- - A test checks that every run kind text-sanctifier can report has an
72
- omission note, so a new kind fails `npm run ci` instead of reaching users
73
- with no note.
74
- - A test fails when any source, test, fixture or doc file holds a raw
75
- invisible or control character. A test regex in 0.1.3 held four, written
76
- by an editing tool that expanded `\u` escapes; they never shipped, since
77
- tests are not in the package.
78
-
79
- ## 0.1.3 — 2026-10-30
80
-
81
- First release on npm. `npm run ci` (contract suite and size check) is the
82
- local gate before a commit; there is no cloud CI. The size ceiling is 66 KB
83
- minified.
84
-
85
- Content-fidelity fixes:
86
-
87
- - **Title heading kept beside shell comments.** A `# comment` line in fenced
88
- code no longer counts as the article's top-level heading, which had
89
- suppressed the `# <headline>` line.
90
- - **Every pipe in a table cell is escaped.** `a || b` escaped only its first
91
- pipe, which added a column and broke the table.
92
- - **Images beside a heading survive.** A heading wrapper that also holds an
93
- image (or picture, video, audio) is no longer unwrapped to the bare heading.
94
- - **`no_links` keeps linked images intact.** `[![alt](img)](href)` became
95
- `![alt](href)`, an image pointing at the link target; the image is now kept
96
- as it was.
97
- - **Code in tables stays fenced.** A GFM cell is one line, so a data table
98
- holding `<pre>` code (Wikipedia's syntax examples) was flattened to inline
99
- code, losing its line breaks and indentation. Such a table is now rendered
100
- as blocks: each cell under a bold label from its column header (or its row
101
- header, for infoboxes), with code as verbatim fences. Tables without code
102
- are still GFM grids.
103
- - Code language from MediaWiki's `mw-highlight-lang-*` class.
104
- - At most one blank line beside a fenced block.
105
- - Five new regression fixtures, one per fix.
106
-
107
- Hidden text (opt-in):
108
-
109
- - **`removeHiddenText: true`** removes runs of tag characters, variation
110
- selectors and invisible operators, which people can't see but models read
111
- (hidden prompt instructions). It applies to the Markdown, fenced code
112
- included, and to `fullPageMarkdown` and `metadata`. The England, Scotland
113
- and Wales flags and a lone emoji or text presentation selector are kept.
114
- `omissions` gets `hidden_tag`, `hidden_variation_selector` and
115
- `hidden_invisible_operator` entries with `count` (runs) and `chars` (code
116
- points). `removeHiddenText` is exported. Default output is unchanged.
117
- - New runtime dependency: text-sanctifier 1.3.3 (first-party, no
118
- dependencies), for its `text-sanctifier/hidden` run finder. About 1.5 KB
119
- minified in the browser bundle.
120
- - New contract fixture `hidden-text.html`, written with character references
121
- so it holds no raw hidden characters.
122
-
123
- ## 0.1.2 — 2026-10-23
124
-
125
- - **Page facts (opt-in).** `pageFacts: true` appends a `## Page facts` list
126
- of short label/value pairs that sit outside the extracted article: a
127
- package sidebar's version, license and downloads, spec and price rows,
128
- infobox rows. Readability drops these asides, which suits reading but
129
- loses the answer to "which version?". Pairs come from a heading followed by
130
- a short value, `<dt>`/`<dd>`, and two-cell table rows. Values already in
131
- the article, sentences, link lists, lone non-data links and anything in
132
- navigation, header, footer or forms are skipped. `extraction.pageFacts`
133
- counts them; `collectPageFacts` is exported. Default output is unchanged.
134
- - New regression fixture: a package page whose version lives only in the
135
- sidebar.
136
-
137
- ## 0.1.1 — 2026-10-22
138
-
139
- Fixes from the first in-browser comparison (MDN, Wikipedia, the Python docs,
140
- GitHub):
141
-
142
- - **Tables never come out as raw HTML.** Own GFM table rules replace the
143
- plugin's:
144
- - a header row is used even when the table has none;
145
- - colspans are padded with empty cells;
146
- - cell content is flattened to one line (code, lists, line breaks);
147
- - pipes are escaped;
148
- - layout and nested tables are rendered as blocks.
149
- - **Dropped lead paragraph restored.** When the extractor leaves out a
150
- paragraph that matches the meta description (MDN's heading-less first
151
- section), it is put back first. `extraction.leadRecovered` reports it, and
152
- `recoverLead: false` turns it off.
153
- - **Clean title heading.** The page's own H1 (permalink sign removed) when
154
- the title contains it, else `og:title` or the title, without a site-name
155
- segment at either end. It is exposed as `metadata.headline`.
156
- - **Smaller bundle.** Strikethrough and task-list rules are now built in, and
157
- `@joplin/turndown-plugin-gfm` is no longer a dependency. The browser bundle
158
- is 60.2 KB minified, down from 61.7 KB.
159
-
160
- ## 0.1.0 — 2026-10-22
161
-
162
- First release.
163
-
164
- - `pageToMarkdown(document, options)`: sanitize → standardize → extract →
165
- convert → clean, returning Markdown, metadata, extraction details,
166
- omissions, warnings and stats.
167
- - **Sanitize.** Builds an inert clone and inlines open shadow DOM within node,
168
- character and depth limits, with an omission notice past them. Removes
169
- scripts, styles, `noscript`, meta and link tags, and ads, plus any
170
- `extraStripSelectors`.
171
- - **Standardize.**
172
- - Turns `<pre>` into fenced code, with the language from
173
- `language-`/`lang-`/`highlight-` classes or `data-lang`.
174
- - Unwraps heading wrappers and removes edit and permalink widgets.
175
- - Removes empty and `javascript:` links.
176
- - Makes links and images absolute and resolves lazy images; drops inline
177
- `data:` images.
178
- - **Extract.** Mozilla Readability on a copy of the clone, falling back to the
179
- full page when it finds nothing or too little. Optionally restores the title
180
- heading.
181
- - **Convert.** Turndown with GitHub-flavoured tables and strikethrough.
182
- - **Clean.** Fence-safe. Keeps short lines and `<br>` hard breaks, removes only
183
- verified single-line JSON documents, and offers `full`, `no_links` and
184
- `text_only` modes.
185
- - **Metadata.** Meta tags, JSON-LD (`@graph` included), canonical URL and
186
- language.
1
+ # Changelog
2
+
3
+ ## 0.2.0 — 2026-11-07
4
+
5
+ Fixes for silent content loss, found by live runs in a real extension (HELIOS, in
6
+ Brave): pages lost their main content and reported a normal extraction.
7
+
8
+ A minor version because results change in ways callers can see. `omissions` has two
9
+ new kinds, `inactive_tabs` and `comments`, so an exhaustive `switch` on `kind` in
10
+ TypeScript stops compiling. `extraction.method` can be `main_content` on pages that
11
+ used to report `readability`, those pages come out longer, and some outputs gain a
12
+ closing line.
13
+
14
+ - **Threads and product grids keep their content.** When Readability keeps less
15
+ than `minExtractShare` (default 0.4) of the page's main content and misses at
16
+ least 500 characters, the main content is used, without navigation, asides and
17
+ forms. `extraction.method` is then `main_content`, `fallbackReason` is
18
+ `extracted_small_share_of_page`, and `warnings` says so. Word recall on a Lobsters
19
+ thread goes from 36% to 100%, and on an IKEA category page from 50% to 95%. These
20
+ pages keep more clutter: IKEA's precision falls from 92% to 30%.
21
+ - **Layout class names no longer cost a block its place.** Readability's clutter
22
+ list matches "scroll" and "hidden" in Tailwind's `scroll-mt-2`, `overflow-hidden`
23
+ and `md:hidden`, and it removed what carried them: every code block in Docker's
24
+ install guide and the plan cards on Tailscale's pricing page. Proven live on both.
25
+ - **Icon-only table cells say something.** A cell holding only an SVG came out
26
+ empty, so a comparison table couldn't say which plan has what. It now shows the
27
+ icon's accessible name, else `✓`, `–` or `✗` for those simple shapes, else
28
+ `[icon]`. On Tailscale, 114 ticks and 25 dashes where there were empty cells.
29
+ - **More cases fall back to the main content,** and say so with their own
30
+ `fallbackReason`. `extracted_site_menu`: Readability chose a container above
31
+ `<main>` and kept the site's menu (BBC News). `extracted_few_sections_of_page`:
32
+ it kept under 1.5 times `minExtractShare` of the main content and lost most of the
33
+ page's h2 sections, as on Adafruit's product page, which lost its price, stock and
34
+ technical details. Headings that are a link to another page don't count, so story
35
+ cards around an article, as on The Register, don't trigger it. The product page
36
+ keeps more clutter this way, including its "May We Also Suggest" list. Proven live
37
+ on all three.
38
+ - **The fallback keeps collapsed answers.** Hidden text of 40 characters or more
39
+ straight after a button or summary is kept, so an FAQ isn't lost when a page
40
+ hides its answers with inline styles. Proven live on Allbirds' FAQ.
41
+ - **Tab panels hidden by a class name keep their text.** Readability removes
42
+ `class="hidden"`, so Docker's "Specific version" install tab was lost live with
43
+ no closing line. Inactive panels are now recognized by that class too, and Docker's
44
+ `aria-role` tab strips are read as tabs. Proven live on Docker.
45
+ - **Shadow limits no longer erase a whole host.** A host over the limit keeps what
46
+ fits, limits apply per host in the page's own tree so a large site header can't
47
+ use up the article's budget, and `warnings` now reports the cut.
48
+ - **Slotted page content no longer counts against the shadow limit.** Material Web
49
+ puts its whole article in one host's slot, so the limit cut it off partway
50
+ through. Proven live on Material Web, Adobe Spectrum and Red Hat (no cut), though
51
+ no live page has yet gone over the limit.
52
+ - **Headings survive clutter-like names.** Readability removed headings named like
53
+ Docusaurus's `anchorTargetHideOnScrollNavbar`. Permalink text ("Section titled",
54
+ "Direct link to") goes too.
55
+ - **Permalinks are recognised by where they point.** A link to a heading's own id,
56
+ inside or beside it, is removed whatever its class or wording. Short headings
57
+ beside one ("Types", "Usage") were being dropped by Readability as clutter:
58
+ five on Material Web's button page.
59
+ - **Page facts skip more junk:** headings inside or holding a link (another story's
60
+ teaser), values holding form controls, and cookie settings.
61
+ - **An article above its comments stays an article.** Comments outweighing the
62
+ article made the main-content fallback fire, four times the length on a real
63
+ page. Readability's result now stands when what it kept isn't itself a comment,
64
+ and `omissions` gets `comments`.
65
+ - **Hidden text no longer counts or comes through in the fallback,** except code
66
+ behind a "Show code" toggle.
67
+ - **Tabs read as one document.** Inactive panels are kept under their tab's name,
68
+ in place of a tab strip of dead links. A panel over 5,000 characters is left out
69
+ and named (`stats.standardization.omittedTabs`, `omissions` kind `inactive_tabs`).
70
+ - **Note asides survive,** as blockquotes: Readability removes every `<aside>`.
71
+ - **What changes the content says so in the Markdown too:** a fallback, a removed
72
+ JSON line, left-out tabs and comments each add a closing line, since a caller may
73
+ pass on only the Markdown. The shadow notice stays last.
74
+ - **Too little from Readability uses the page's `<main>`,** when it marks one with
75
+ enough text, before the full page.
76
+ - **`stats.pageTextChars`** counts the page's text. A capture is a snapshot of the
77
+ DOM at call time: a caller can re-capture until the number settles.
78
+ - **Headlines:** a site-wide `og:title` no longer replaces the page's H1, and a badge
79
+ after an H1's title is dropped. A heading that is wholly a link to itself becomes
80
+ plain text.
81
+ - A link around block content stays on one line, and `.ad-label` counts as an ad.
82
+ - The browser bundle grows to about 75.4 KB minified; the size ceiling is now
83
+ 76 KB.
84
+ - **The browser run no longer launches by default.** Launching browsers from agent
85
+ sessions has crashed this machine. `npm run test:browser` now needs
86
+ `PAGE_MARKDOWN_ALLOW_BROWSER_LAUNCH=1`, set by a person in their own shell;
87
+ without it the run builds its page, launches nothing and exits with an error.
88
+ It is no longer part of `npm run ci` or `prepublishOnly`.
89
+
90
+ ## 0.1.5 — 2026-11-04
91
+
92
+ Content-fidelity fixes, each with a new fixture run in Node and in the browser:
93
+
94
+ - **Code blocks keep their lines.** Lines marked up with `<br>` or block
95
+ elements (Docusaurus and similar highlighters) came out joined on one line.
96
+ - **Copy buttons stay out of code.** A `<button>` inside `<pre>` leaked its
97
+ label into the code and lost the language. Whitespace around the `<code>`
98
+ no longer loses the fence.
99
+ - **Text beside a heading is kept.** A `<div>` holding a heading and bare text
100
+ was replaced by the heading, deleting the text.
101
+ - **Row spans keep columns aligned.** Later cells shifted left. A spanned cell
102
+ now repeats in each row of a GFM table, and block-rendered tables label cells
103
+ by their true column.
104
+ - **Large tables no longer freeze the page.** Table checks ran once per cell,
105
+ so time grew with the square of the size: 500 rows took about 8 seconds in
106
+ Chrome.
107
+ - **Modes leave more code alone.** Fences in list items and blockquotes, and
108
+ inline code, were edited by `no_links`, `text_only` and blank-line cleanup.
109
+ - **A lead paragraph is no longer doubled** when it starts with a link or holds
110
+ an entity such as `&lt;`.
111
+ - **Script links are caught however they are written.** `javascript:` with an
112
+ embedded newline, tab or control character got through, as did `vbscript:`
113
+ and `data:`. These links keep only their text. Images with such a source are
114
+ dropped.
115
+ - **`<base href>` is respected** for links and images.
116
+ - **A linked image with empty alt is kept.**
117
+ - **An ad marker on `<body>` no longer empties the page.** Sites put page-wide ad
118
+ settings there (`data-ad-unit` on heise.de), and the ad filter removed the whole
119
+ body. `<html>` and `<body>` are never treated as ads; ads inside them still are.
120
+ - `stats.markdownChars` now matches the returned Markdown.
121
+ - The browser bundle grows to about 67.2 KB minified; the size ceiling is now
122
+ 68 KB.
123
+
124
+ - **TypeScript types.** Declarations for `pageToMarkdown`, its options, the full
125
+ result and every stage export are generated from the JSDoc in `src` into
126
+ `dist/types`, through a `types` condition in `exports`. The result is
127
+ documented in a README table. `npm run typecheck` is part of `npm run ci`: it
128
+ checks `src` against its JSDoc, checks a consumer file with `tsc`, and checks
129
+ the declarations against real output from every fixture, in both directions.
130
+ Only comments change in the existing modules; the bundle is the same size.
131
+ - **Bench.** `npm run bench` adds a Readability + Turndown baseline, word recall
132
+ and precision, and 11 held-out pages whose probes were committed before any
133
+ engine ran on them. The headline changes from 32/32 against 11/32 to 84/100
134
+ against 74/100 and 61/100 on the held-out pages; see `bench/RESULTS.md` for the
135
+ failures. `bench/fetch-pages.mjs` now decodes by declared charset and keeps the
136
+ last good copy when a fetch fails. `npm run bench:write` updates
137
+ `bench/RESULTS.md`; `npm run bench` only prints.
138
+ - **Licence summary.** The README now says in plain words who can use the package
139
+ free and who needs a commercial licence, and how to ask for one. The licence
140
+ itself is unchanged.
141
+ - **Browser run.** `npm run test:browser` runs the golden fixtures in
142
+ headless Chrome or Edge, with the browser's own DOM, and checks what Node
143
+ can't show:
144
+ - the live document is left unmodified;
145
+ - real open shadow roots are captured, nested roots and slotted children
146
+ included;
147
+ - closed shadow roots stay out.
148
+
149
+ It is part of `npm run ci` and needs no new dependencies. The package's output
150
+ in Chrome and Edge matches its Node output.
151
+
152
+ ## 0.1.4 — 2026-11-02
153
+
154
+ - **text-sanctifier 1.3.8** (was 1.3.3). `removeHiddenText` now also removes
155
+ runs of reserved tag-plane code points (U+E0080–U+E00FF, U+E01F0–U+E0FFF),
156
+ which 1.3.3 did not find, and reports them as `hidden_reserved_tag_plane`.
157
+ The browser bundle grows to about 65.6 KB minified.
158
+ - A test checks that every run kind text-sanctifier can report has an
159
+ omission note, so a new kind fails `npm run ci` instead of reaching users
160
+ with no note.
161
+ - A test fails when any source, test, fixture or doc file holds a raw
162
+ invisible or control character. A test regex in 0.1.3 held four, written
163
+ by an editing tool that expanded `\u` escapes; they never shipped, since
164
+ tests are not in the package.
165
+
166
+ ## 0.1.3 — 2026-10-30
167
+
168
+ First release on npm. `npm run ci` (contract suite and size check) is the
169
+ local gate before a commit; there is no cloud CI. The size ceiling is 66 KB
170
+ minified.
171
+
172
+ Content-fidelity fixes:
173
+
174
+ - **Title heading kept beside shell comments.** A `# comment` line in fenced
175
+ code no longer counts as the article's top-level heading, which had
176
+ suppressed the `# <headline>` line.
177
+ - **Every pipe in a table cell is escaped.** `a || b` escaped only its first
178
+ pipe, which added a column and broke the table.
179
+ - **Images beside a heading survive.** A heading wrapper that also holds an
180
+ image (or picture, video, audio) is no longer unwrapped to the bare heading.
181
+ - **`no_links` keeps linked images intact.** `[![alt](img)](href)` became
182
+ `![alt](href)`, an image pointing at the link target; the image is now kept
183
+ as it was.
184
+ - **Code in tables stays fenced.** A GFM cell is one line, so a data table
185
+ holding `<pre>` code (Wikipedia's syntax examples) was flattened to inline
186
+ code, losing its line breaks and indentation. Such a table is now rendered
187
+ as blocks: each cell under a bold label from its column header (or its row
188
+ header, for infoboxes), with code as verbatim fences. Tables without code
189
+ are still GFM grids.
190
+ - Code language from MediaWiki's `mw-highlight-lang-*` class.
191
+ - At most one blank line beside a fenced block.
192
+ - Five new regression fixtures, one per fix.
193
+
194
+ Hidden text (opt-in):
195
+
196
+ - **`removeHiddenText: true`** removes runs of tag characters, variation
197
+ selectors and invisible operators, which people can't see but models read
198
+ (hidden prompt instructions). It applies to the Markdown, fenced code
199
+ included, and to `fullPageMarkdown` and `metadata`. The England, Scotland
200
+ and Wales flags and a lone emoji or text presentation selector are kept.
201
+ `omissions` gets `hidden_tag`, `hidden_variation_selector` and
202
+ `hidden_invisible_operator` entries with `count` (runs) and `chars` (code
203
+ points). `removeHiddenText` is exported. Default output is unchanged.
204
+ - New runtime dependency: text-sanctifier 1.3.3 (first-party, no
205
+ dependencies), for its `text-sanctifier/hidden` run finder. About 1.5 KB
206
+ minified in the browser bundle.
207
+ - New contract fixture `hidden-text.html`, written with character references
208
+ so it holds no raw hidden characters.
209
+
210
+ ## 0.1.2 — 2026-10-23
211
+
212
+ - **Page facts (opt-in).** `pageFacts: true` appends a `## Page facts` list
213
+ of short label/value pairs that sit outside the extracted article: a
214
+ package sidebar's version, license and downloads, spec and price rows,
215
+ infobox rows. Readability drops these asides, which suits reading but
216
+ loses the answer to "which version?". Pairs come from a heading followed by
217
+ a short value, `<dt>`/`<dd>`, and two-cell table rows. Values already in
218
+ the article, sentences, link lists, lone non-data links and anything in
219
+ navigation, header, footer or forms are skipped. `extraction.pageFacts`
220
+ counts them; `collectPageFacts` is exported. Default output is unchanged.
221
+ - New regression fixture: a package page whose version lives only in the
222
+ sidebar.
223
+
224
+ ## 0.1.1 — 2026-10-22
225
+
226
+ Fixes from the first in-browser comparison (MDN, Wikipedia, the Python docs,
227
+ GitHub):
228
+
229
+ - **Tables never come out as raw HTML.** Own GFM table rules replace the
230
+ plugin's:
231
+ - a header row is used even when the table has none;
232
+ - colspans are padded with empty cells;
233
+ - cell content is flattened to one line (code, lists, line breaks);
234
+ - pipes are escaped;
235
+ - layout and nested tables are rendered as blocks.
236
+ - **Dropped lead paragraph restored.** When the extractor leaves out a
237
+ paragraph that matches the meta description (MDN's heading-less first
238
+ section), it is put back first. `extraction.leadRecovered` reports it, and
239
+ `recoverLead: false` turns it off.
240
+ - **Clean title heading.** The page's own H1 (permalink sign removed) when
241
+ the title contains it, else `og:title` or the title, without a site-name
242
+ segment at either end. It is exposed as `metadata.headline`.
243
+ - **Smaller bundle.** Strikethrough and task-list rules are now built in, and
244
+ `@joplin/turndown-plugin-gfm` is no longer a dependency. The browser bundle
245
+ is 60.2 KB minified, down from 61.7 KB.
246
+
247
+ ## 0.1.0 — 2026-10-22
248
+
249
+ First release.
250
+
251
+ - `pageToMarkdown(document, options)`: sanitize → standardize → extract →
252
+ convert → clean, returning Markdown, metadata, extraction details,
253
+ omissions, warnings and stats.
254
+ - **Sanitize.** Builds an inert clone and inlines open shadow DOM within node,
255
+ character and depth limits, with an omission notice past them. Removes
256
+ scripts, styles, `noscript`, meta and link tags, and ads, plus any
257
+ `extraStripSelectors`.
258
+ - **Standardize.**
259
+ - Turns `<pre>` into fenced code, with the language from
260
+ `language-`/`lang-`/`highlight-` classes or `data-lang`.
261
+ - Unwraps heading wrappers and removes edit and permalink widgets.
262
+ - Removes empty and `javascript:` links.
263
+ - Makes links and images absolute and resolves lazy images; drops inline
264
+ `data:` images.
265
+ - **Extract.** Mozilla Readability on a copy of the clone, falling back to the
266
+ full page when it finds nothing or too little. Optionally restores the title
267
+ heading.
268
+ - **Convert.** Turndown with GitHub-flavoured tables and strikethrough.
269
+ - **Clean.** Fence-safe. Keeps short lines and `<br>` hard breaks, removes only
270
+ verified single-line JSON documents, and offers `full`, `no_links` and
271
+ `text_only` modes.
272
+ - **Metadata.** Meta tags, JSON-LD (`@graph` included), canonical URL and
273
+ language.