@j0hanz/fetch-url-mcp 1.10.2 → 1.10.4
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/dist/http/native.d.ts.map +1 -1
- package/dist/http/native.js +20 -26
- package/dist/http/session-teardown.d.ts +17 -0
- package/dist/http/session-teardown.d.ts.map +1 -0
- package/dist/http/session-teardown.js +30 -0
- package/dist/lib/code-lang.d.ts.map +1 -1
- package/dist/lib/code-lang.js +2 -8
- package/dist/lib/core.d.ts.map +1 -1
- package/dist/lib/core.js +2 -0
- package/dist/lib/dom-prep.d.ts +2 -0
- package/dist/lib/dom-prep.d.ts.map +1 -1
- package/dist/lib/dom-prep.js +150 -139
- package/dist/lib/md-cleanup.d.ts.map +1 -1
- package/dist/lib/md-cleanup.js +53 -20
- package/dist/lib/progress.d.ts.map +1 -1
- package/dist/lib/progress.js +59 -32
- package/dist/lib/sdk-interop.d.ts +8 -0
- package/dist/lib/sdk-interop.d.ts.map +1 -0
- package/dist/lib/sdk-interop.js +73 -0
- package/dist/lib/task-handlers.d.ts +0 -2
- package/dist/lib/task-handlers.d.ts.map +1 -1
- package/dist/lib/task-handlers.js +9 -94
- package/dist/resources/index.js +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +10 -3
- package/dist/tasks/call-contract.d.ts +25 -0
- package/dist/tasks/call-contract.d.ts.map +1 -0
- package/dist/tasks/call-contract.js +58 -0
- package/dist/tasks/cursor-codec.d.ts +5 -0
- package/dist/tasks/cursor-codec.d.ts.map +1 -0
- package/dist/tasks/cursor-codec.js +41 -0
- package/dist/tasks/execution.d.ts +1 -20
- package/dist/tasks/execution.d.ts.map +1 -1
- package/dist/tasks/execution.js +7 -32
- package/dist/tasks/manager.d.ts +1 -5
- package/dist/tasks/manager.d.ts.map +1 -1
- package/dist/tasks/manager.js +16 -144
- package/dist/tasks/owner.d.ts +12 -2
- package/dist/tasks/owner.d.ts.map +1 -1
- package/dist/tasks/owner.js +52 -3
- package/dist/tasks/tool-registry.d.ts +1 -0
- package/dist/tasks/tool-registry.d.ts.map +1 -1
- package/dist/tasks/waiters.d.ts +27 -0
- package/dist/tasks/waiters.d.ts.map +1 -0
- package/dist/tasks/waiters.js +113 -0
- package/dist/tools/fetch-url-progress.d.ts +17 -0
- package/dist/tools/fetch-url-progress.d.ts.map +1 -0
- package/dist/tools/fetch-url-progress.js +78 -0
- package/dist/tools/fetch-url.d.ts +5 -9
- package/dist/tools/fetch-url.d.ts.map +1 -1
- package/dist/tools/fetch-url.js +43 -96
- package/dist/transform/next-flight.d.ts +2 -0
- package/dist/transform/next-flight.d.ts.map +1 -0
- package/dist/transform/next-flight.js +285 -0
- package/dist/transform/title-policy.d.ts +12 -0
- package/dist/transform/title-policy.d.ts.map +1 -0
- package/dist/transform/title-policy.js +85 -0
- package/dist/transform/transform.d.ts.map +1 -1
- package/dist/transform/transform.js +62 -475
- package/package.json +4 -4
- package/dist/tools/index.d.ts +0 -3
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js +0 -4
package/dist/lib/dom-prep.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { parseHTML } from 'linkedom';
|
|
2
2
|
import { config, logDebug } from './core.js';
|
|
3
|
+
// ── Thresholds ──────────────────────────────────────────────────────
|
|
3
4
|
const NOISE_SCAN_LIMIT = 50_000;
|
|
4
5
|
const MIN_BODY_CONTENT_LENGTH = 100;
|
|
5
6
|
const DIALOG_MIN_CHARS_FOR_PRESERVATION = 500;
|
|
6
7
|
const NAV_FOOTER_MIN_CHARS_FOR_PRESERVATION = 500;
|
|
7
8
|
const ABORT_CHECK_INTERVAL = 500;
|
|
8
9
|
const NODE_FILTER_SHOW_TEXT = 4;
|
|
10
|
+
const ASIDE_NAV_LINK_DENSITY_THRESHOLD = 0.5;
|
|
11
|
+
const ASIDE_NAV_MIN_LINKS = 10;
|
|
12
|
+
const INLINE_DEMO_INSTRUCTION_MAX_CHARS = 160;
|
|
13
|
+
const REDUNDANT_PREVIEW_SEGMENT_MAX_CHARS = 60;
|
|
14
|
+
const REDUNDANT_PREVIEW_MAX_SEGMENTS = 12;
|
|
15
|
+
// ── Regex patterns ──────────────────────────────────────────────────
|
|
9
16
|
const HTML_DOCUMENT_MARKERS = /<\s*(?:!doctype|html|head|body)\b/i;
|
|
10
17
|
const HTML_FRAGMENT_MARKERS = /<\s*(?:article|main|section|div|nav|footer|header|aside|table|ul|ol)\b/i;
|
|
11
18
|
const NOISE_PATTERNS = [
|
|
@@ -19,6 +26,10 @@ const HEADER_NOISE_PATTERN = /\b(site-header|masthead|topbar|navbar|nav(?:bar)?|
|
|
|
19
26
|
const FIXED_OR_HIGH_Z_PATTERN = /\b(?:fixed|sticky|z-(?:4\d|50)|isolate)\b/;
|
|
20
27
|
const HEADING_PERMALINK_TEXT_PATTERN = /^(?:#|¶|§|¤|🔗)$/u;
|
|
21
28
|
const HEADING_PERMALINK_CLASS_PATTERN = /\b(?:mark|permalink|hash-link|anchor(?:js)?-?link|header-?link|heading-anchor|deep-link)\b/i;
|
|
29
|
+
const HIDDEN_STYLE_REGEX = /\b(?:display\s*:\s*none|visibility\s*:\s*hidden)\b/i;
|
|
30
|
+
const UTM_PARAM_REGEX = /[?&]utm_(?:source|medium|campaign)=/i;
|
|
31
|
+
const NO_MATCH_REGEX = /a^/i;
|
|
32
|
+
// ── URL prefixes to skip during resolution ──────────────────────────
|
|
22
33
|
const SKIP_URL_PREFIXES = [
|
|
23
34
|
'#',
|
|
24
35
|
'javascript:',
|
|
@@ -27,6 +38,7 @@ const SKIP_URL_PREFIXES = [
|
|
|
27
38
|
'data:',
|
|
28
39
|
'blob:',
|
|
29
40
|
];
|
|
41
|
+
// ── Tag / role sets ─────────────────────────────────────────────────
|
|
30
42
|
const BASE_STRUCTURAL_TAGS = new Set([
|
|
31
43
|
'script',
|
|
32
44
|
'style',
|
|
@@ -52,9 +64,6 @@ const NAVIGATION_ROLES = new Set([
|
|
|
52
64
|
'alertdialog',
|
|
53
65
|
'search',
|
|
54
66
|
]);
|
|
55
|
-
const INLINE_DEMO_INSTRUCTION_MAX_CHARS = 160;
|
|
56
|
-
const REDUNDANT_PREVIEW_SEGMENT_MAX_CHARS = 60;
|
|
57
|
-
const REDUNDANT_PREVIEW_MAX_SEGMENTS = 12;
|
|
58
67
|
const INTERACTIVE_CONTENT_ROLES = new Set([
|
|
59
68
|
'tabpanel',
|
|
60
69
|
'tab',
|
|
@@ -69,6 +78,7 @@ const INTERACTIVE_CONTENT_ROLES = new Set([
|
|
|
69
78
|
'tooltip',
|
|
70
79
|
'alert',
|
|
71
80
|
]);
|
|
81
|
+
// ── Promo tokens ────────────────────────────────────────────────────
|
|
72
82
|
const PROMO_TOKENS_ALWAYS = [
|
|
73
83
|
'banner',
|
|
74
84
|
'promo',
|
|
@@ -87,20 +97,30 @@ const PROMO_TOKENS_ALWAYS = [
|
|
|
87
97
|
'fb-post',
|
|
88
98
|
'instagram-media',
|
|
89
99
|
'social-embed',
|
|
100
|
+
'author-bio',
|
|
101
|
+
'byline',
|
|
102
|
+
'sharedaddy',
|
|
103
|
+
'sharing',
|
|
90
104
|
];
|
|
91
105
|
const PROMO_TOKENS_AGGRESSIVE = ['ad', 'related', 'comment'];
|
|
92
106
|
const PROMO_TOKENS_BY_CATEGORY = {
|
|
93
107
|
'cookie-banners': ['cookie', 'consent', 'popup', 'modal', 'overlay', 'toast'],
|
|
94
108
|
newsletters: ['newsletter', 'subscribe'],
|
|
95
|
-
'social-share': ['share', 'social'],
|
|
109
|
+
'social-share': ['share', 'social', 'share-button'],
|
|
110
|
+
'author-blocks': ['author-bio', 'byline', 'author-info', 'writer-profile'],
|
|
111
|
+
'related-content': [
|
|
112
|
+
'related-post',
|
|
113
|
+
'related-article',
|
|
114
|
+
'more-stories',
|
|
115
|
+
'recommended-posts',
|
|
116
|
+
],
|
|
96
117
|
};
|
|
97
|
-
// Noise selector configurations
|
|
118
|
+
// ── Noise selector configurations ───────────────────────────────────
|
|
98
119
|
const BASE_NOISE_SELECTORS = {
|
|
99
120
|
navFooter: 'nav,footer,header[class*="site"],header[class*="nav"],header[class*="menu"],[role="banner"],[role="navigation"],[class*="breadcrumb"]',
|
|
100
121
|
cookieBanners: '[role="dialog"]',
|
|
101
122
|
hidden: '[style*="display: none"],[style*="display:none"],[style*="visibility: hidden"],[style*="visibility:hidden"],[hidden],[aria-hidden="true"]',
|
|
102
123
|
};
|
|
103
|
-
const NO_MATCH_REGEX = /a^/i;
|
|
104
124
|
let cachedContext;
|
|
105
125
|
let lastContextKey;
|
|
106
126
|
function escapeRegexLiteral(value) {
|
|
@@ -111,24 +131,18 @@ function buildTokenRegex(tokens) {
|
|
|
111
131
|
return NO_MATCH_REGEX;
|
|
112
132
|
return new RegExp(`(?:^|[^a-z0-9])(?:${[...tokens].map(escapeRegexLiteral).join('|')})(?:$|[^a-z0-9])`, 'i');
|
|
113
133
|
}
|
|
114
|
-
function
|
|
115
|
-
for (const token of tokens)
|
|
116
|
-
target.add(token);
|
|
117
|
-
}
|
|
118
|
-
function getPromoMatchers(currentConfig, flags) {
|
|
134
|
+
function getPromoMatchers(currentConfig, enabledCategories) {
|
|
119
135
|
const baseTokens = new Set(PROMO_TOKENS_ALWAYS);
|
|
120
136
|
const aggressiveTokens = new Set();
|
|
121
137
|
if (currentConfig.aggressiveMode) {
|
|
122
|
-
|
|
138
|
+
for (const token of PROMO_TOKENS_AGGRESSIVE)
|
|
139
|
+
aggressiveTokens.add(token);
|
|
123
140
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
if (flags.socialShare) {
|
|
131
|
-
addTokens(baseTokens, PROMO_TOKENS_BY_CATEGORY['social-share']);
|
|
141
|
+
for (const [category, tokens] of Object.entries(PROMO_TOKENS_BY_CATEGORY)) {
|
|
142
|
+
if (enabledCategories.has(category)) {
|
|
143
|
+
for (const token of tokens)
|
|
144
|
+
baseTokens.add(token);
|
|
145
|
+
}
|
|
132
146
|
}
|
|
133
147
|
for (const t of currentConfig.extraTokens) {
|
|
134
148
|
const n = t.toLowerCase().trim();
|
|
@@ -149,7 +163,6 @@ function getContext() {
|
|
|
149
163
|
extraSelectors: currentConfig.extraSelectors,
|
|
150
164
|
aggressiveMode: currentConfig.aggressiveMode,
|
|
151
165
|
preserveSvgCanvas: currentConfig.preserveSvgCanvas,
|
|
152
|
-
weights: currentConfig.weights,
|
|
153
166
|
});
|
|
154
167
|
if (cachedContext !== undefined && lastContextKey === contextKey)
|
|
155
168
|
return cachedContext;
|
|
@@ -164,15 +177,13 @@ function getContext() {
|
|
|
164
177
|
const flags = {
|
|
165
178
|
navFooter: isEnabled('nav-footer'),
|
|
166
179
|
cookieBanners: isEnabled('cookie-banners'),
|
|
167
|
-
newsletters: isEnabled('newsletters'),
|
|
168
|
-
socialShare: isEnabled('social-share'),
|
|
169
180
|
};
|
|
170
181
|
const structuralTags = new Set(BASE_STRUCTURAL_TAGS);
|
|
171
182
|
if (!currentConfig.preserveSvgCanvas) {
|
|
172
183
|
structuralTags.add('svg');
|
|
173
184
|
structuralTags.add('canvas');
|
|
174
185
|
}
|
|
175
|
-
const promoMatchers = getPromoMatchers(currentConfig,
|
|
186
|
+
const promoMatchers = getPromoMatchers(currentConfig, enabled);
|
|
176
187
|
const extraSelectors = currentConfig.extraSelectors
|
|
177
188
|
.map((s) => s.trim())
|
|
178
189
|
.filter((s) => s.length > 0);
|
|
@@ -182,7 +193,8 @@ function getContext() {
|
|
|
182
193
|
selectors.push(BASE_NOISE_SELECTORS.navFooter);
|
|
183
194
|
if (flags.cookieBanners)
|
|
184
195
|
selectors.push(BASE_NOISE_SELECTORS.cookieBanners);
|
|
185
|
-
const
|
|
196
|
+
const noiseSelector = selectors.join(',');
|
|
197
|
+
const extraSelector = extraSelectors.length > 0 ? extraSelectors.join(',') : null;
|
|
186
198
|
const candidateSelector = [
|
|
187
199
|
...structuralTags,
|
|
188
200
|
...ALWAYS_NOISE_TAGS,
|
|
@@ -196,11 +208,10 @@ function getContext() {
|
|
|
196
208
|
cachedContext = {
|
|
197
209
|
flags,
|
|
198
210
|
structuralTags,
|
|
199
|
-
weights: currentConfig.weights,
|
|
200
211
|
promoMatchers,
|
|
201
|
-
promoEnabled:
|
|
202
|
-
|
|
203
|
-
|
|
212
|
+
promoEnabled: Object.keys(PROMO_TOKENS_BY_CATEGORY).some((cat) => enabled.has(cat)),
|
|
213
|
+
noiseSelector,
|
|
214
|
+
extraSelector,
|
|
204
215
|
candidateSelector,
|
|
205
216
|
};
|
|
206
217
|
lastContextKey = contextKey;
|
|
@@ -231,8 +242,6 @@ function isWithinPrimaryContent(element) {
|
|
|
231
242
|
}
|
|
232
243
|
return false;
|
|
233
244
|
}
|
|
234
|
-
const ASIDE_NAV_LINK_DENSITY_THRESHOLD = 0.5;
|
|
235
|
-
const ASIDE_NAV_MIN_LINKS = 10;
|
|
236
245
|
function isNavigationAside(element) {
|
|
237
246
|
if (element.querySelector('nav'))
|
|
238
247
|
return true;
|
|
@@ -278,69 +287,57 @@ function removeNodes(nodes) {
|
|
|
278
287
|
}
|
|
279
288
|
}
|
|
280
289
|
}
|
|
281
|
-
|
|
282
|
-
function calculateNavFooterScore(tagName, className, id, role, weights) {
|
|
283
|
-
let score = 0;
|
|
284
|
-
if (ALWAYS_NOISE_TAGS.has(tagName))
|
|
285
|
-
score += weights.structural;
|
|
286
|
-
if (tagName === 'header') {
|
|
287
|
-
if ((role && NAVIGATION_ROLES.has(role)) ||
|
|
288
|
-
HEADER_NOISE_PATTERN.test(`${className} ${id}`)) {
|
|
289
|
-
score += weights.structural;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
if (tagName === 'aside') {
|
|
293
|
-
score += weights.structural;
|
|
294
|
-
}
|
|
295
|
-
if (role && NAVIGATION_ROLES.has(role)) {
|
|
296
|
-
if (tagName !== 'aside' || role !== 'complementary') {
|
|
297
|
-
score += weights.structural;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
return score;
|
|
301
|
-
}
|
|
302
|
-
function calculatePromoScore(element, className, id, context) {
|
|
290
|
+
function isPromoMatch(className, id, element, context) {
|
|
303
291
|
if (!context.promoEnabled)
|
|
304
|
-
return
|
|
292
|
+
return false;
|
|
305
293
|
const aggTest = context.promoMatchers.aggressive.test(className) ||
|
|
306
294
|
context.promoMatchers.aggressive.test(id);
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
return isAggressiveMatch || isBaseMatch ? context.weights.promo : 0;
|
|
295
|
+
if (aggTest)
|
|
296
|
+
return !isWithinPrimaryContent(element);
|
|
297
|
+
return (context.promoMatchers.base.test(className) ||
|
|
298
|
+
context.promoMatchers.base.test(id));
|
|
312
299
|
}
|
|
313
300
|
function isNoiseElement(element, context) {
|
|
314
301
|
const tagName = element.tagName.toLowerCase();
|
|
302
|
+
const role = element.getAttribute('role');
|
|
315
303
|
const className = element.getAttribute('class') ?? '';
|
|
316
304
|
const id = element.getAttribute('id') ?? '';
|
|
317
|
-
const
|
|
305
|
+
const interactive = isInteractive(element, role);
|
|
318
306
|
const style = element.getAttribute('style');
|
|
319
|
-
const
|
|
320
|
-
const elIsHidden = element.hasAttribute('hidden') ||
|
|
307
|
+
const hidden = element.hasAttribute('hidden') ||
|
|
321
308
|
element.getAttribute('aria-hidden') === 'true' ||
|
|
322
309
|
(style !== null && HIDDEN_STYLE_REGEX.test(style));
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (context.structuralTags.has(tagName) && !elIsInteractive) {
|
|
327
|
-
score += weights.structural;
|
|
328
|
-
}
|
|
329
|
-
// Nav/Footer Scoring
|
|
310
|
+
// Structural tags (script, style, form, etc.)
|
|
311
|
+
if (context.structuralTags.has(tagName) && !interactive)
|
|
312
|
+
return true;
|
|
330
313
|
if (context.flags.navFooter) {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
314
|
+
// Always-noise tags (nav, footer)
|
|
315
|
+
if (ALWAYS_NOISE_TAGS.has(tagName))
|
|
316
|
+
return true;
|
|
317
|
+
// Header with navigation role or noise class/id
|
|
318
|
+
if (tagName === 'header' &&
|
|
319
|
+
((role !== null && NAVIGATION_ROLES.has(role)) ||
|
|
320
|
+
HEADER_NOISE_PATTERN.test(`${className} ${id}`)))
|
|
321
|
+
return true;
|
|
322
|
+
// Aside elements
|
|
323
|
+
if (tagName === 'aside')
|
|
324
|
+
return true;
|
|
325
|
+
// Navigation roles (except aside+complementary)
|
|
326
|
+
if (role !== null &&
|
|
327
|
+
NAVIGATION_ROLES.has(role) &&
|
|
328
|
+
(tagName !== 'aside' || role !== 'complementary'))
|
|
329
|
+
return true;
|
|
340
330
|
}
|
|
341
|
-
//
|
|
342
|
-
|
|
343
|
-
|
|
331
|
+
// Hidden elements
|
|
332
|
+
if (hidden && !interactive)
|
|
333
|
+
return true;
|
|
334
|
+
// Sticky/fixed positioned elements
|
|
335
|
+
if (FIXED_OR_HIGH_Z_PATTERN.test(className))
|
|
336
|
+
return true;
|
|
337
|
+
// Promotional/noise content
|
|
338
|
+
if (isPromoMatch(className, id, element, context))
|
|
339
|
+
return true;
|
|
340
|
+
return false;
|
|
344
341
|
}
|
|
345
342
|
function cleanHeadings(document) {
|
|
346
343
|
const headings = document.querySelectorAll('h1,h2,h3,h4,h5,h6');
|
|
@@ -421,13 +418,18 @@ function hoistNestedRows(table) {
|
|
|
421
418
|
}
|
|
422
419
|
}
|
|
423
420
|
}
|
|
424
|
-
function stripNoise(document,
|
|
425
|
-
|
|
421
|
+
function stripNoise(document, signal) {
|
|
422
|
+
const context = getContext();
|
|
423
|
+
if (config.noiseRemoval.debug) {
|
|
424
|
+
logDebug('Noise removal audit enabled', {
|
|
425
|
+
categories: [...(context.flags.navFooter ? ['nav-footer'] : [])],
|
|
426
|
+
});
|
|
427
|
+
}
|
|
426
428
|
// Structural Removal
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
if (
|
|
430
|
-
removeNodes(document.querySelectorAll(
|
|
429
|
+
removeNodes(document.querySelectorAll(context.noiseSelector));
|
|
430
|
+
// Extra selectors (evaluated after base removal so DOM state is updated)
|
|
431
|
+
if (context.extraSelector) {
|
|
432
|
+
removeNodes(document.querySelectorAll(context.extraSelector));
|
|
431
433
|
}
|
|
432
434
|
// Candidates (conditional removal)
|
|
433
435
|
const candidates = document.querySelectorAll(context.candidateSelector);
|
|
@@ -531,20 +533,54 @@ function surfaceHiddenTabPanels(document) {
|
|
|
531
533
|
}
|
|
532
534
|
}
|
|
533
535
|
function stripTabTriggers(document) {
|
|
534
|
-
|
|
535
|
-
const tabs = document.querySelectorAll('button[role="tab"]');
|
|
536
|
+
const tabs = document.querySelectorAll('[role="tab"]');
|
|
536
537
|
for (let i = tabs.length - 1; i >= 0; i--) {
|
|
537
|
-
tabs[i]
|
|
538
|
+
const tab = tabs[i];
|
|
539
|
+
if (!tab)
|
|
540
|
+
continue;
|
|
541
|
+
const isSelected = tab.getAttribute('aria-selected') === 'true' ||
|
|
542
|
+
tab.getAttribute('data-state') === 'active' ||
|
|
543
|
+
tab.hasAttribute('data-selected');
|
|
544
|
+
if (!isSelected) {
|
|
545
|
+
tab.remove();
|
|
546
|
+
}
|
|
538
547
|
}
|
|
539
548
|
}
|
|
540
|
-
|
|
549
|
+
/** Surface hidden tab panels, then strip unselected tab triggers. */
|
|
550
|
+
export function normalizeTabContent(document) {
|
|
551
|
+
surfaceHiddenTabPanels(document);
|
|
552
|
+
stripTabTriggers(document);
|
|
553
|
+
}
|
|
554
|
+
function normalizeTableCells(document) {
|
|
541
555
|
const cells = document.querySelectorAll('td, th');
|
|
542
556
|
for (const cell of cells) {
|
|
557
|
+
const brs = cell.querySelectorAll('br');
|
|
558
|
+
for (const br of brs) {
|
|
559
|
+
br.replaceWith(' ');
|
|
560
|
+
}
|
|
561
|
+
const blocks = Array.from(cell.querySelectorAll('div, p, ul, li, h1, h2, h3, h4, h5, h6'));
|
|
562
|
+
for (const block of blocks) {
|
|
563
|
+
if (!block.parentNode)
|
|
564
|
+
continue;
|
|
565
|
+
const span = document.createElement('span');
|
|
566
|
+
span.appendChild(document.createTextNode(' '));
|
|
567
|
+
while (block.firstChild) {
|
|
568
|
+
span.appendChild(block.firstChild);
|
|
569
|
+
}
|
|
570
|
+
span.appendChild(document.createTextNode(' '));
|
|
571
|
+
for (const attr of Array.from(block.attributes)) {
|
|
572
|
+
span.setAttribute(attr.name, attr.value);
|
|
573
|
+
}
|
|
574
|
+
block.replaceWith(span);
|
|
575
|
+
}
|
|
543
576
|
const walker = document.createTreeWalker(cell, NODE_FILTER_SHOW_TEXT);
|
|
544
577
|
let node;
|
|
545
578
|
while ((node = walker.nextNode())) {
|
|
546
|
-
if (node.
|
|
547
|
-
node.
|
|
579
|
+
if (node.nodeValue) {
|
|
580
|
+
node.nodeValue = node.nodeValue.replace(/\r?\n/g, ' ');
|
|
581
|
+
if (node.nodeValue.includes('|')) {
|
|
582
|
+
node.nodeValue = node.nodeValue.replace(/\|/g, '\\|');
|
|
583
|
+
}
|
|
548
584
|
}
|
|
549
585
|
}
|
|
550
586
|
}
|
|
@@ -673,8 +709,20 @@ function cleanCodeExamples(document) {
|
|
|
673
709
|
pruneDemoInstructionBlocks(document);
|
|
674
710
|
normalizeHighlightedCodeLines(document);
|
|
675
711
|
}
|
|
712
|
+
function stripPromoLinks(document) {
|
|
713
|
+
const links = document.querySelectorAll('a[href]');
|
|
714
|
+
for (let i = links.length - 1; i >= 0; i--) {
|
|
715
|
+
const link = links[i];
|
|
716
|
+
if (!link)
|
|
717
|
+
continue;
|
|
718
|
+
const href = link.getAttribute('href');
|
|
719
|
+
if (href && UTM_PARAM_REGEX.test(href)) {
|
|
720
|
+
link.remove();
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
676
724
|
function separateAdjacentInlineElements(document) {
|
|
677
|
-
const badges = document.querySelectorAll('span.chakra-badge, [data-scope="badge"], [class*="badge"]');
|
|
725
|
+
const badges = document.querySelectorAll('span.chakra-badge, [data-scope="badge"], [class*="badge"], [data-slot="label"], [slot="label"]');
|
|
678
726
|
for (const badge of badges) {
|
|
679
727
|
const next = badge.nextSibling;
|
|
680
728
|
if (next?.nodeType === 1) {
|
|
@@ -682,19 +730,17 @@ function separateAdjacentInlineElements(document) {
|
|
|
682
730
|
}
|
|
683
731
|
}
|
|
684
732
|
}
|
|
733
|
+
// Called on both raw documents (pre-article path) and article fragments
|
|
734
|
+
// (post-Readability). Some passes (stripTabTriggers, etc.) are no-ops
|
|
735
|
+
// on Readability output since tabs are already stripped or absent.
|
|
685
736
|
export function prepareDocumentForMarkdown(document, baseUrl, signal) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
stripNoise(document, context, signal);
|
|
693
|
-
stripTabTriggers(document);
|
|
737
|
+
normalizeTabContent(document);
|
|
738
|
+
cleanHeadings(document);
|
|
739
|
+
stripNoise(document, signal);
|
|
740
|
+
stripPromoLinks(document);
|
|
694
741
|
cleanCodeExamples(document);
|
|
695
742
|
separateAdjacentInlineElements(document);
|
|
696
|
-
|
|
697
|
-
escapeTableCellPipes(document);
|
|
743
|
+
normalizeTableCells(document);
|
|
698
744
|
normalizeTableStructure(document);
|
|
699
745
|
if (baseUrl)
|
|
700
746
|
resolveUrls(document, baseUrl);
|
|
@@ -723,41 +769,6 @@ function normalizeTableStructure(document) {
|
|
|
723
769
|
hoistNestedRows(table);
|
|
724
770
|
}
|
|
725
771
|
}
|
|
726
|
-
function flattenTableCellBreaks(document) {
|
|
727
|
-
const cells = document.querySelectorAll('td, th');
|
|
728
|
-
for (const cell of cells) {
|
|
729
|
-
const brs = cell.querySelectorAll('br');
|
|
730
|
-
for (const br of brs) {
|
|
731
|
-
br.replaceWith(' ');
|
|
732
|
-
}
|
|
733
|
-
const blocks = Array.from(cell.querySelectorAll('div, p, ul, li, h1, h2, h3, h4, h5, h6'));
|
|
734
|
-
for (const block of blocks) {
|
|
735
|
-
if (!block.parentNode)
|
|
736
|
-
continue;
|
|
737
|
-
const span = document.createElement('span');
|
|
738
|
-
span.appendChild(document.createTextNode(' '));
|
|
739
|
-
while (block.firstChild) {
|
|
740
|
-
span.appendChild(block.firstChild);
|
|
741
|
-
}
|
|
742
|
-
span.appendChild(document.createTextNode(' '));
|
|
743
|
-
for (const attr of Array.from(block.attributes)) {
|
|
744
|
-
span.setAttribute(attr.name, attr.value);
|
|
745
|
-
}
|
|
746
|
-
block.replaceWith(span);
|
|
747
|
-
}
|
|
748
|
-
const filterNewlines = (node) => {
|
|
749
|
-
if (node.nodeType === 3 && node.nodeValue) {
|
|
750
|
-
node.nodeValue = node.nodeValue.replace(/\r?\n/g, ' ');
|
|
751
|
-
}
|
|
752
|
-
else {
|
|
753
|
-
for (const child of Array.from(node.childNodes)) {
|
|
754
|
-
filterNewlines(child);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
};
|
|
758
|
-
filterNewlines(cell);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
772
|
export function removeNoiseFromHtml(html, document, baseUrl, signal) {
|
|
762
773
|
const shouldParse = isFullDocumentHtml(html) ||
|
|
763
774
|
mayContainNoise(html) ||
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"md-cleanup.d.ts","sourceRoot":"","sources":["../../src/lib/md-cleanup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"md-cleanup.d.ts","sourceRoot":"","sources":["../../src/lib/md-cleanup.ts"],"names":[],"mappings":"AAiGA,UAAU,cAAc;IACtB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAkeD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAC3C,MAAM,CAuCR;AAWD,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,cAAc,GACvB,MAAM,CAmBR"}
|
package/dist/lib/md-cleanup.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { config } from './core.js';
|
|
2
2
|
import { throwIfAborted } from './utils.js';
|
|
3
|
+
// ── ASCII code constants ────────────────────────────────────────────
|
|
3
4
|
const ASCII_HASH = 35;
|
|
4
5
|
const ASCII_ASTERISK = 42;
|
|
5
6
|
const ASCII_PLUS = 43;
|
|
@@ -10,13 +11,21 @@ const ASCII_DIGIT_9 = 57;
|
|
|
10
11
|
const ASCII_EXCLAMATION = 33;
|
|
11
12
|
const ASCII_QUESTION = 63;
|
|
12
13
|
const ASCII_BRACKET_OPEN = 91;
|
|
14
|
+
// ── Title heuristic thresholds ──────────────────────────────────────
|
|
13
15
|
const TITLE_MIN_WORDS = 2;
|
|
14
16
|
const TITLE_MAX_WORDS = 10;
|
|
15
17
|
const TITLE_MIN_CAPITALIZED = 2;
|
|
18
|
+
// ── Processing limits ───────────────────────────────────────────────
|
|
16
19
|
const HAS_FOLLOWING_LOOKAHEAD = 10;
|
|
17
20
|
const PROPERTY_FIX_MAX_PASSES = 5;
|
|
18
21
|
const MAX_LINE_LENGTH = 80;
|
|
22
|
+
// ── TOC thresholds ──────────────────────────────────────────────────
|
|
23
|
+
const TOC_SCAN_LIMIT = 20;
|
|
24
|
+
const TOC_MAX_NON_EMPTY = 12;
|
|
25
|
+
const TOC_LINK_RATIO_THRESHOLD = 0.8;
|
|
26
|
+
// ── Fence pattern ───────────────────────────────────────────────────
|
|
19
27
|
const FENCE_PATTERN = /^\s*(`{3,}|~{3,})/;
|
|
28
|
+
// ── Regex collection ────────────────────────────────────────────────
|
|
20
29
|
const REGEX = {
|
|
21
30
|
HEADING_MARKER: /^#{1,6}\s/m,
|
|
22
31
|
HEADING_STRICT: /^#{1,6}\s+/m,
|
|
@@ -47,11 +56,11 @@ const REGEX = {
|
|
|
47
56
|
NESTED_LIST_INDENT: /^( +)((?:[-*+])|\d+\.)\s/gm,
|
|
48
57
|
TYPEDOC_COMMENT: /(`+)(?:(?!\1)[\s\S])*?\1|\s?\/\\?\*[\s\S]*?\\?\*\//g,
|
|
49
58
|
};
|
|
59
|
+
// ── Heading keywords (config-driven) ────────────────────────────────
|
|
50
60
|
const HEADING_KEYWORDS = new Set(config.markdownCleanup.headingKeywords.map((value) => value.toLocaleLowerCase(config.i18n.locale)));
|
|
61
|
+
// ── Prefix patterns ─────────────────────────────────────────────────
|
|
51
62
|
const SPECIAL_PREFIXES = /^(?:example|note|tip|warning|important|caution):\s+\S/i;
|
|
52
|
-
|
|
53
|
-
const TOC_MAX_NON_EMPTY = 12;
|
|
54
|
-
const TOC_LINK_RATIO_THRESHOLD = 0.8;
|
|
63
|
+
// ── TypeDoc prefixes ────────────────────────────────────────────────
|
|
55
64
|
const TYPEDOC_PREFIXES = [
|
|
56
65
|
'Defined in:',
|
|
57
66
|
'Returns:',
|
|
@@ -399,24 +408,48 @@ function fixConcatenatedProperties(text) {
|
|
|
399
408
|
}
|
|
400
409
|
function applyGlobalRegexes(text, options) {
|
|
401
410
|
const checkAbort = createAbortChecker(options);
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
411
|
+
const passes = [
|
|
412
|
+
{
|
|
413
|
+
stage: 'markdown:cleanup:nbsp',
|
|
414
|
+
apply: (t) => t.replace(/\u00A0/g, ' '),
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
stage: 'markdown:cleanup:headings',
|
|
418
|
+
apply: (t) => t
|
|
419
|
+
.replace(REGEX.HEADING_SPACING, '$1\n\n$2')
|
|
420
|
+
.replace(REGEX.HEADING_CODE_BLOCK, '$1\n\n```'),
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
stage: 'markdown:cleanup:typedoc',
|
|
424
|
+
enabled: () => config.markdownCleanup.removeTypeDocComments,
|
|
425
|
+
apply: removeTypeDocArtifacts,
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
stage: 'markdown:cleanup:skip-links',
|
|
429
|
+
enabled: () => config.markdownCleanup.removeSkipLinks,
|
|
430
|
+
apply: removeSkipLinks,
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
stage: 'markdown:cleanup:spacing',
|
|
434
|
+
apply: normalizeMarkdownSpacing,
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
stage: 'markdown:cleanup:properties',
|
|
438
|
+
apply: fixConcatenatedProperties,
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
stage: 'markdown:cleanup:permalinks',
|
|
442
|
+
apply: stripTrailingHeadingPermalinks,
|
|
443
|
+
},
|
|
444
|
+
];
|
|
445
|
+
let result = text;
|
|
446
|
+
for (const pass of passes) {
|
|
447
|
+
if (pass.enabled !== undefined && !pass.enabled())
|
|
448
|
+
continue;
|
|
449
|
+
checkAbort(pass.stage);
|
|
450
|
+
result = pass.apply(result);
|
|
414
451
|
}
|
|
415
|
-
|
|
416
|
-
result = normalizeMarkdownSpacing(result);
|
|
417
|
-
checkAbort('markdown:cleanup:properties');
|
|
418
|
-
result = fixConcatenatedProperties(result);
|
|
419
|
-
return stripTrailingHeadingPermalinks(result);
|
|
452
|
+
return result;
|
|
420
453
|
}
|
|
421
454
|
function normalizeNestedListIndentation(text) {
|
|
422
455
|
return text.replace(REGEX.NESTED_LIST_INDENT, (match, spaces, marker) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/lib/progress.ts"],"names":[],"mappings":"AAOA,KAAK,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AACrC,UAAU,WAAW;IACnB,aAAa,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC1C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AACD,MAAM,WAAW,0BAA0B;IACzC,aAAa,EAAE,aAAa,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AACD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,EAAE,0BAA0B,CAAC;CACpC;AACD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,gBAAgB,CAAC,EAAE,CAAC,YAAY,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC;CACnC;AACD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;
|
|
1
|
+
{"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/lib/progress.ts"],"names":[],"mappings":"AAOA,KAAK,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AACrC,UAAU,WAAW;IACnB,aAAa,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC1C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AACD,MAAM,WAAW,0BAA0B;IACzC,aAAa,EAAE,aAAa,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AACD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,EAAE,0BAA0B,CAAC;CACpC;AACD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,gBAAgB,CAAC,EAAE,CAAC,YAAY,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC;CACnC;AACD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAiLD,wBAAgB,sBAAsB,CACpC,KAAK,CAAC,EAAE,gBAAgB,GACvB,gBAAgB,CAElB"}
|