page-markdown 0.1.5 → 0.3.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,353 @@
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
+ ## Unreleased
4
+
5
+ ## 0.3.0 — 2026-11-13
6
+
7
+ - Build the live-style report with `npm run report:build`, then open `.browser-test/concealment-report.html` in an already running browser.
8
+
9
+ - **Opt-in:** enable `detectConcealedText: true` to separate text concealed by
10
+ supported live source-document computed styles:
11
+ `display:none`, hidden/collapsed visibility, zero opacity, zero font size and
12
+ supported empty clips (including nonzero zero-area rects; legacy `clip`
13
+ requires absolute/fixed positioning), transparent text and text exactly
14
+ matching its own or nearest opaque background
15
+ into capped `concealedText`, with uncapped omission
16
+ counts and notices in both Markdown outputs. Detection is off by default;
17
+ HELIOS enables it explicitly. Default-on is withdrawn for 0.3.0 and will be
18
+ reconsidered after a real-site trial. Disabled detection makes no style calls.
19
+ Static documents retain previous output.
20
+ Whitespace-only groups are excluded from entries, totals, truncation and
21
+ notices, also correcting slice 1; substantive groups retain original
22
+ whitespace and UTF-16 counts.
23
+ Source whitespace is removed for display-none and zero-font-size runs; other
24
+ reasons retain whitespace-only runs and replace substantive runs with one
25
+ ordinary space only when their original text starts or ends with JavaScript
26
+ whitespace.
27
+ The check reports computed, static-only, disabled or incomplete capability.
28
+ Short tabs and existing answer/code exceptions remain; independent reasons
29
+ still lift text and can change extraction choice and tab omissions.
30
+ Opacity-applicable transitions and named animations with nonzero durations
31
+ retain text with a warning; this scroll-reveal rule remains provisional
32
+ pending real-site evidence and Astra review.
33
+ Colour detection handles computed sRGB rgb()/rgba() only, for own text.
34
+ OKLCH and Display P3 computed colours are not parsed, so even white text on
35
+ white in those forms can stay in the output.
36
+ Measured in Chrome 157 on Windows, on one machine, using synthetic pages
37
+ of 20,006 elements built from `95bf97d`, with five warmed alternating pairs
38
+ per page: full conversion with detection added a paired-median 100.5 ms
39
+ on the deep page and 108.5 ms on the shallow page, about 12.5% each.
40
+ Both full-conversion targets (at most 25% and at most 250 ms added) were met.
41
+ The sanitize stage with detection (including preparation and lift) added
42
+ about 127 ms on either page. The earlier candidate (`2a3b0b5`) added about
43
+ 442 ms deep and 340 ms shallow by a difference of medians, which is not
44
+ directly comparable. This is not a general performance guarantee;
45
+ real-site cost is not yet measured.
46
+ Chrome is the only browser tested; Firefox and
47
+ Safari are untested.
48
+ Unknown colours, missing opaque backgrounds, intervening translucent layers
49
+ or images, text clipping, differing text fill, shadows and strokes prevent
50
+ equality detection. Fully transparent text fill is detected when no other
51
+ effect paints the text. Near-colour matches and partially transparent text
52
+ with non-zero alpha (including alpha 0.01) are not detected. White text on a
53
+ page with no declared
54
+ opaque background is not detected: the root background is unknown.
55
+
56
+ - Boxes a page marks as skippable are left out of the article: a box that opens with a
57
+ link to the end marker it closes with, as BBC's "most read" and podcast boxes do in
58
+ every language. Boxes holding a quote, embed, code or table stay.
59
+ - Malformed web links keep their text without a link even in browsers that accept spaces in hostnames.
60
+ - Table cells with `rowspan="0"` span to the end of their row group, including with `colspan`.
61
+ - Text-only output keeps image alt text without URL fragments from parentheses or titles.
62
+ - Captions survive in single-column, nested and code-bearing tables.
63
+
64
+ - **AP News linked names to impossible web addresses.** Malformed hrefs such as
65
+ `https://ron%20desantis/` now keep their text without a link when a base URL is
66
+ available.
67
+ - **Linked story headings stay headings.** On BBC News, card headlines appeared
68
+ inside link text and the logo left an empty heading. Headlines now link from
69
+ real headings, card text and images stay, and emptied headings are removed.
70
+ - **Note labels stay.** Readability removed Docker's `.admonition-header` because
71
+ its name holds "header", so its Warning, Note and Tip labels were lost. Each now
72
+ opens its blockquote in bold.
73
+ - **Spanned cells fill their columns.** A cell with `colspan` used to fill one
74
+ column and leave blanks, so Tailscale's "Unlimited" across all plans read as
75
+ Personal only. Its value now repeats in each column it covers, except when it
76
+ spans the whole row, as a caption does.
77
+ - **FAQ answers inside `<details>` survive.** Readability dropped a one-paragraph
78
+ answer wrapper with a link in it: Tailscale's answer on SSH check mode.
79
+ - **Ars Technica's MacBook Pro review repeated photo captions and left its kicker
80
+ as body text.** Matching lightbox captions and image descriptions now appear
81
+ once, distinct alt text and gallery images stay, and the kicker reads as a label.
82
+
83
+ ## 0.2.0 — 2026-11-07
84
+
85
+ Fixes for silent content loss, found by live runs in a real extension (HELIOS, in
86
+ Brave): pages lost their main content and reported a normal extraction.
87
+
88
+ A minor version because results change in ways callers can see. `omissions` has two
89
+ new kinds, `inactive_tabs` and `comments`, so an exhaustive `switch` on `kind` in
90
+ TypeScript stops compiling. `extraction.method` can be `main_content` on pages that
91
+ used to report `readability`, those pages come out longer, and some outputs gain a
92
+ closing line.
93
+
94
+ - **Threads and product grids keep their content.** When Readability keeps less
95
+ than `minExtractShare` (default 0.4) of the page's main content and misses at
96
+ least 500 characters, the main content is used, without navigation, asides and
97
+ forms. `extraction.method` is then `main_content`, `fallbackReason` is
98
+ `extracted_small_share_of_page`, and `warnings` says so. Word recall on a Lobsters
99
+ thread goes from 36% to 100%, and on an IKEA category page from 50% to 95%. These
100
+ pages keep more clutter: IKEA's precision falls from 92% to 30%.
101
+ - **Layout class names no longer cost a block its place.** Readability's clutter
102
+ list matches "scroll" and "hidden" in Tailwind's `scroll-mt-2`, `overflow-hidden`
103
+ and `md:hidden`, and it removed what carried them: every code block in Docker's
104
+ install guide and the plan cards on Tailscale's pricing page. Proven live on both.
105
+ - **Icon-only table cells say something.** A cell holding only an SVG came out
106
+ empty, so a comparison table couldn't say which plan has what. It now shows the
107
+ icon's accessible name, else `✓`, `–` or `✗` for those simple shapes, else
108
+ `[icon]`. On Tailscale, 114 ticks and 25 dashes where there were empty cells.
109
+ - **More cases fall back to the main content,** and say so with their own
110
+ `fallbackReason`. `extracted_site_menu`: Readability chose a container above
111
+ `<main>` and kept the site's menu (BBC News). `extracted_few_sections_of_page`:
112
+ it kept under 1.5 times `minExtractShare` of the main content and lost most of the
113
+ page's h2 sections, as on Adafruit's product page, which lost its price, stock and
114
+ technical details. Headings that are a link to another page don't count, so story
115
+ cards around an article, as on The Register, don't trigger it. The product page
116
+ keeps more clutter this way, including its "May We Also Suggest" list. Proven live
117
+ on all three.
118
+ - **The fallback keeps collapsed answers.** Hidden text of 40 characters or more
119
+ straight after a button or summary is kept, so an FAQ isn't lost when a page
120
+ hides its answers with inline styles. Proven live on Allbirds' FAQ.
121
+ - **Tab panels hidden by a class name keep their text.** Readability removes
122
+ `class="hidden"`, so Docker's "Specific version" install tab was lost live with
123
+ no closing line. Inactive panels are now recognized by that class too, and Docker's
124
+ `aria-role` tab strips are read as tabs. Proven live on Docker.
125
+ - **Shadow limits no longer erase a whole host.** A host over the limit keeps what
126
+ fits, limits apply per host in the page's own tree so a large site header can't
127
+ use up the article's budget, and `warnings` now reports the cut.
128
+ - **Slotted page content no longer counts against the shadow limit.** Material Web
129
+ puts its whole article in one host's slot, so the limit cut it off partway
130
+ through. Proven live on Material Web, Adobe Spectrum and Red Hat (no cut), though
131
+ no live page has yet gone over the limit.
132
+ - **Headings survive clutter-like names.** Readability removed headings named like
133
+ Docusaurus's `anchorTargetHideOnScrollNavbar`. Permalink text ("Section titled",
134
+ "Direct link to") goes too.
135
+ - **Permalinks are recognised by where they point.** A link to a heading's own id,
136
+ inside or beside it, is removed whatever its class or wording. Short headings
137
+ beside one ("Types", "Usage") were being dropped by Readability as clutter:
138
+ five on Material Web's button page.
139
+ - **Page facts skip more junk:** headings inside or holding a link (another story's
140
+ teaser), values holding form controls, and cookie settings.
141
+ - **An article above its comments stays an article.** Comments outweighing the
142
+ article made the main-content fallback fire, four times the length on a real
143
+ page. Readability's result now stands when what it kept isn't itself a comment,
144
+ and `omissions` gets `comments`.
145
+ - **Hidden text no longer counts or comes through in the fallback,** except code
146
+ behind a "Show code" toggle.
147
+ - **Tabs read as one document.** Inactive panels are kept under their tab's name,
148
+ in place of a tab strip of dead links. A panel over 5,000 characters is left out
149
+ and named (`stats.standardization.omittedTabs`, `omissions` kind `inactive_tabs`).
150
+ - **Note asides survive,** as blockquotes: Readability removes every `<aside>`.
151
+ - **What changes the content says so in the Markdown too:** a fallback, a removed
152
+ JSON line, left-out tabs and comments each add a closing line, since a caller may
153
+ pass on only the Markdown. The shadow notice stays last.
154
+ - **Too little from Readability uses the page's `<main>`,** when it marks one with
155
+ enough text, before the full page.
156
+ - **`stats.pageTextChars`** counts the page's text. A capture is a snapshot of the
157
+ DOM at call time: a caller can re-capture until the number settles.
158
+ - **Headlines:** a site-wide `og:title` no longer replaces the page's H1, and a badge
159
+ after an H1's title is dropped. A heading that is wholly a link to itself becomes
160
+ plain text.
161
+ - A link around block content stays on one line, and `.ad-label` counts as an ad.
162
+ - The browser bundle grows to about 75.4 KB minified; the size ceiling is now
163
+ 76 KB.
164
+ - **The browser run no longer launches by default.** Launching browsers from agent
165
+ sessions has crashed this machine. `npm run test:browser` now needs
166
+ `PAGE_MARKDOWN_ALLOW_BROWSER_LAUNCH=1`, set by a person in their own shell;
167
+ without it the run builds its page, launches nothing and exits with an error.
168
+ It is no longer part of `npm run ci` or `prepublishOnly`.
169
+
170
+ ## 0.1.5 — 2026-11-04
171
+
172
+ Content-fidelity fixes, each with a new fixture run in Node and in the browser:
173
+
174
+ - **Code blocks keep their lines.** Lines marked up with `<br>` or block
175
+ elements (Docusaurus and similar highlighters) came out joined on one line.
176
+ - **Copy buttons stay out of code.** A `<button>` inside `<pre>` leaked its
177
+ label into the code and lost the language. Whitespace around the `<code>`
178
+ no longer loses the fence.
179
+ - **Text beside a heading is kept.** A `<div>` holding a heading and bare text
180
+ was replaced by the heading, deleting the text.
181
+ - **Row spans keep columns aligned.** Later cells shifted left. A spanned cell
182
+ now repeats in each row of a GFM table, and block-rendered tables label cells
183
+ by their true column.
184
+ - **Large tables no longer freeze the page.** Table checks ran once per cell,
185
+ so time grew with the square of the size: 500 rows took about 8 seconds in
186
+ Chrome.
187
+ - **Modes leave more code alone.** Fences in list items and blockquotes, and
188
+ inline code, were edited by `no_links`, `text_only` and blank-line cleanup.
189
+ - **A lead paragraph is no longer doubled** when it starts with a link or holds
190
+ an entity such as `&lt;`.
191
+ - **Script links are caught however they are written.** `javascript:` with an
192
+ embedded newline, tab or control character got through, as did `vbscript:`
193
+ and `data:`. These links keep only their text. Images with such a source are
194
+ dropped.
195
+ - **`<base href>` is respected** for links and images.
196
+ - **A linked image with empty alt is kept.**
197
+ - **An ad marker on `<body>` no longer empties the page.** Sites put page-wide ad
198
+ settings there (`data-ad-unit` on heise.de), and the ad filter removed the whole
199
+ body. `<html>` and `<body>` are never treated as ads; ads inside them still are.
200
+ - `stats.markdownChars` now matches the returned Markdown.
201
+ - The browser bundle grows to about 67.2 KB minified; the size ceiling is now
202
+ 68 KB.
203
+
204
+ - **TypeScript types.** Declarations for `pageToMarkdown`, its options, the full
205
+ result and every stage export are generated from the JSDoc in `src` into
206
+ `dist/types`, through a `types` condition in `exports`. The result is
207
+ documented in a README table. `npm run typecheck` is part of `npm run ci`: it
208
+ checks `src` against its JSDoc, checks a consumer file with `tsc`, and checks
209
+ the declarations against real output from every fixture, in both directions.
210
+ Only comments change in the existing modules; the bundle is the same size.
211
+ - **Bench.** `npm run bench` adds a Readability + Turndown baseline, word recall
212
+ and precision, and 11 held-out pages whose probes were committed before any
213
+ engine ran on them. The headline changes from 32/32 against 11/32 to 84/100
214
+ against 74/100 and 61/100 on the held-out pages; see `bench/RESULTS.md` for the
215
+ failures. `bench/fetch-pages.mjs` now decodes by declared charset and keeps the
216
+ last good copy when a fetch fails. `npm run bench:write` updates
217
+ `bench/RESULTS.md`; `npm run bench` only prints.
218
+ - **Licence summary.** The README now says in plain words who can use the package
219
+ free and who needs a commercial licence, and how to ask for one. The licence
220
+ itself is unchanged.
221
+ - **Browser run.** `npm run test:browser` runs the golden fixtures in
222
+ headless Chrome or Edge, with the browser's own DOM, and checks what Node
223
+ can't show:
224
+ - the live document is left unmodified;
225
+ - real open shadow roots are captured, nested roots and slotted children
226
+ included;
227
+ - closed shadow roots stay out.
228
+
229
+ It is part of `npm run ci` and needs no new dependencies. The package's output
230
+ in Chrome and Edge matches its Node output.
231
+
232
+ ## 0.1.4 — 2026-11-02
233
+
234
+ - **text-sanctifier 1.3.8** (was 1.3.3). `removeHiddenText` now also removes
235
+ runs of reserved tag-plane code points (U+E0080–U+E00FF, U+E01F0–U+E0FFF),
236
+ which 1.3.3 did not find, and reports them as `hidden_reserved_tag_plane`.
237
+ The browser bundle grows to about 65.6 KB minified.
238
+ - A test checks that every run kind text-sanctifier can report has an
239
+ omission note, so a new kind fails `npm run ci` instead of reaching users
240
+ with no note.
241
+ - A test fails when any source, test, fixture or doc file holds a raw
242
+ invisible or control character. A test regex in 0.1.3 held four, written
243
+ by an editing tool that expanded `\u` escapes; they never shipped, since
244
+ tests are not in the package.
245
+
246
+ ## 0.1.3 — 2026-10-30
247
+
248
+ First release on npm. `npm run ci` (contract suite and size check) is the
249
+ local gate before a commit; there is no cloud CI. The size ceiling is 66 KB
250
+ minified.
251
+
252
+ Content-fidelity fixes:
253
+
254
+ - **Title heading kept beside shell comments.** A `# comment` line in fenced
255
+ code no longer counts as the article's top-level heading, which had
256
+ suppressed the `# <headline>` line.
257
+ - **Every pipe in a table cell is escaped.** `a || b` escaped only its first
258
+ pipe, which added a column and broke the table.
259
+ - **Images beside a heading survive.** A heading wrapper that also holds an
260
+ image (or picture, video, audio) is no longer unwrapped to the bare heading.
261
+ - **`no_links` keeps linked images intact.** `[![alt](img)](href)` became
262
+ `![alt](href)`, an image pointing at the link target; the image is now kept
263
+ as it was.
264
+ - **Code in tables stays fenced.** A GFM cell is one line, so a data table
265
+ holding `<pre>` code (Wikipedia's syntax examples) was flattened to inline
266
+ code, losing its line breaks and indentation. Such a table is now rendered
267
+ as blocks: each cell under a bold label from its column header (or its row
268
+ header, for infoboxes), with code as verbatim fences. Tables without code
269
+ are still GFM grids.
270
+ - Code language from MediaWiki's `mw-highlight-lang-*` class.
271
+ - At most one blank line beside a fenced block.
272
+ - Five new regression fixtures, one per fix.
273
+
274
+ Hidden text (opt-in):
275
+
276
+ - **`removeHiddenText: true`** removes runs of tag characters, variation
277
+ selectors and invisible operators, which people can't see but models read
278
+ (hidden prompt instructions). It applies to the Markdown, fenced code
279
+ included, and to `fullPageMarkdown` and `metadata`. The England, Scotland
280
+ and Wales flags and a lone emoji or text presentation selector are kept.
281
+ `omissions` gets `hidden_tag`, `hidden_variation_selector` and
282
+ `hidden_invisible_operator` entries with `count` (runs) and `chars` (code
283
+ points). `removeHiddenText` is exported. Default output is unchanged.
284
+ - New runtime dependency: text-sanctifier 1.3.3 (first-party, no
285
+ dependencies), for its `text-sanctifier/hidden` run finder. About 1.5 KB
286
+ minified in the browser bundle.
287
+ - New contract fixture `hidden-text.html`, written with character references
288
+ so it holds no raw hidden characters.
289
+
290
+ ## 0.1.2 — 2026-10-23
291
+
292
+ - **Page facts (opt-in).** `pageFacts: true` appends a `## Page facts` list
293
+ of short label/value pairs that sit outside the extracted article: a
294
+ package sidebar's version, license and downloads, spec and price rows,
295
+ infobox rows. Readability drops these asides, which suits reading but
296
+ loses the answer to "which version?". Pairs come from a heading followed by
297
+ a short value, `<dt>`/`<dd>`, and two-cell table rows. Values already in
298
+ the article, sentences, link lists, lone non-data links and anything in
299
+ navigation, header, footer or forms are skipped. `extraction.pageFacts`
300
+ counts them; `collectPageFacts` is exported. Default output is unchanged.
301
+ - New regression fixture: a package page whose version lives only in the
302
+ sidebar.
303
+
304
+ ## 0.1.1 — 2026-10-22
305
+
306
+ Fixes from the first in-browser comparison (MDN, Wikipedia, the Python docs,
307
+ GitHub):
308
+
309
+ - **Tables never come out as raw HTML.** Own GFM table rules replace the
310
+ plugin's:
311
+ - a header row is used even when the table has none;
312
+ - colspans are padded with empty cells;
313
+ - cell content is flattened to one line (code, lists, line breaks);
314
+ - pipes are escaped;
315
+ - layout and nested tables are rendered as blocks.
316
+ - **Dropped lead paragraph restored.** When the extractor leaves out a
317
+ paragraph that matches the meta description (MDN's heading-less first
318
+ section), it is put back first. `extraction.leadRecovered` reports it, and
319
+ `recoverLead: false` turns it off.
320
+ - **Clean title heading.** The page's own H1 (permalink sign removed) when
321
+ the title contains it, else `og:title` or the title, without a site-name
322
+ segment at either end. It is exposed as `metadata.headline`.
323
+ - **Smaller bundle.** Strikethrough and task-list rules are now built in, and
324
+ `@joplin/turndown-plugin-gfm` is no longer a dependency. The browser bundle
325
+ is 60.2 KB minified, down from 61.7 KB.
326
+
327
+ ## 0.1.0 — 2026-10-22
328
+
329
+ First release.
330
+
331
+ - `pageToMarkdown(document, options)`: sanitize → standardize → extract →
332
+ convert → clean, returning Markdown, metadata, extraction details,
333
+ omissions, warnings and stats.
334
+ - **Sanitize.** Builds an inert clone and inlines open shadow DOM within node,
335
+ character and depth limits, with an omission notice past them. Removes
336
+ scripts, styles, `noscript`, meta and link tags, and ads, plus any
337
+ `extraStripSelectors`.
338
+ - **Standardize.**
339
+ - Turns `<pre>` into fenced code, with the language from
340
+ `language-`/`lang-`/`highlight-` classes or `data-lang`.
341
+ - Unwraps heading wrappers and removes edit and permalink widgets.
342
+ - Removes empty and `javascript:` links.
343
+ - Makes links and images absolute and resolves lazy images; drops inline
344
+ `data:` images.
345
+ - **Extract.** Mozilla Readability on a copy of the clone, falling back to the
346
+ full page when it finds nothing or too little. Optionally restores the title
347
+ heading.
348
+ - **Convert.** Turndown with GitHub-flavoured tables and strikethrough.
349
+ - **Clean.** Fence-safe. Keeps short lines and `<br>` hard breaks, removes only
350
+ verified single-line JSON documents, and offers `full`, `no_links` and
351
+ `text_only` modes.
352
+ - **Metadata.** Meta tags, JSON-LD (`@graph` included), canonical URL and
353
+ language.