@j0hanz/fetch-url-mcp 1.10.21 → 1.10.22
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/auth.d.ts +2 -0
- package/dist/http/auth.d.ts.map +1 -1
- package/dist/http/auth.js +30 -0
- package/dist/lib/core.d.ts +1 -1
- package/dist/lib/core.d.ts.map +1 -1
- package/dist/lib/core.js +44 -28
- 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 +68 -23
- package/dist/lib/http.d.ts.map +1 -1
- package/dist/lib/http.js +84 -55
- package/dist/lib/sdk-interop.d.ts +12 -0
- package/dist/lib/sdk-interop.d.ts.map +1 -1
- package/dist/lib/sdk-interop.js +12 -0
- package/dist/lib/session.d.ts +4 -1
- package/dist/lib/session.d.ts.map +1 -1
- package/dist/lib/session.js +1 -1
- package/dist/lib/utils.d.ts +5 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +5 -0
- package/dist/resources/index.js +2 -2
- package/dist/schemas.js +1 -1
- package/dist/transform/html-translators.d.ts.map +1 -1
- package/dist/transform/html-translators.js +50 -8
- package/dist/transform/markdown-cleanup.d.ts.map +1 -1
- package/dist/transform/markdown-cleanup.js +71 -63
- package/dist/transform/transform.d.ts.map +1 -1
- package/dist/transform/transform.js +118 -60
- package/dist/transform/worker-pool.d.ts +2 -0
- package/dist/transform/worker-pool.d.ts.map +1 -1
- package/dist/transform/worker-pool.js +38 -17
- package/package.json +1 -1
|
@@ -35,6 +35,9 @@ const MAX_LINE_LENGTH = 80;
|
|
|
35
35
|
const TOC_SCAN_LIMIT = 20;
|
|
36
36
|
const TOC_MAX_NON_EMPTY = 12;
|
|
37
37
|
const TOC_LINK_RATIO_THRESHOLD = 0.8;
|
|
38
|
+
// ── List indentation normalization ───────────────────────────────────
|
|
39
|
+
const SOURCE_INDENT_STEP = 2;
|
|
40
|
+
const TARGET_INDENT_STEP = 4;
|
|
38
41
|
// ── Docs-chrome scan depth ───────────────────────────────────────────
|
|
39
42
|
const CHROME_SCAN_LINE_LIMIT = 12;
|
|
40
43
|
// ── Fence pattern ───────────────────────────────────────────────────
|
|
@@ -52,6 +55,9 @@ const REGEX = {
|
|
|
52
55
|
TOC_HEADING: /^(?:#{1,6}\s+)?(?:table of contents|contents|on this page)\s*$/i,
|
|
53
56
|
COMBINED_LINE_REMOVALS: /^(?:\[Skip to (?:main )?(?:content|navigation)\]\(#[^)]*\)|\[Skip link\]\(#[^)]*\)|Was this page helpful\??|\[Back to top\]\(#[^)]*\)|\[\s*\]\(https?:\/\/[^)]*\))\s*$/gim,
|
|
54
57
|
ZERO_WIDTH_ANCHOR: /\[(?:\s|\u200B)*\]\(#[^)]*\)[ \t]*/g,
|
|
58
|
+
// ReDoS-safe: {0,30} bounds identifier backtracking, negated char class
|
|
59
|
+
// [^\u0022\u201C\u201D]* has no overlap with delimiters, and \s+ is anchored
|
|
60
|
+
// between ':' and a quote. Multi-pass capped at PROPERTY_FIX_MAX_PASSES.
|
|
55
61
|
CONCATENATED_PROPS: /([a-z_][a-z0-9_]{0,30}\??:\s+)([\u0022\u201C][^\u0022\u201C\u201D]*[\u0022\u201D])([a-z_][a-z0-9_]{0,30}\??:)/g,
|
|
56
62
|
DOUBLE_NEWLINE_REDUCER: /\n{3,}/g,
|
|
57
63
|
HEADING_SPACING: /(^#{1,6}\s[^\n]*)\n([^\n])/gm,
|
|
@@ -101,13 +107,15 @@ function hasFollowingContent(lines, startIndex) {
|
|
|
101
107
|
}
|
|
102
108
|
return false;
|
|
103
109
|
}
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
function findNextNonBlank(lines, startIndex, maxLookahead) {
|
|
111
|
+
const limit = maxLookahead !== undefined
|
|
112
|
+
? Math.min(lines.length, startIndex + maxLookahead)
|
|
113
|
+
: lines.length;
|
|
114
|
+
for (let i = startIndex; i < limit; i++) {
|
|
115
|
+
if (!isBlank(lines[i]))
|
|
116
|
+
return i;
|
|
109
117
|
}
|
|
110
|
-
return
|
|
118
|
+
return -1;
|
|
111
119
|
}
|
|
112
120
|
function stripAnchorOnlyHeading(line) {
|
|
113
121
|
return line.replace(/^(#{1,6})\s+\[([^\]]+)\]\(#[^)]+\)\s*$/, '$1 $2');
|
|
@@ -140,33 +148,37 @@ function isTitleCaseOrKeyword(trimmed) {
|
|
|
140
148
|
}
|
|
141
149
|
return capitalizedCount >= TITLE_MIN_CAPITALIZED;
|
|
142
150
|
}
|
|
151
|
+
function isMarkdownStructuralLine(trimmed) {
|
|
152
|
+
const firstChar = trimmed.charCodeAt(0);
|
|
153
|
+
if (firstChar !== ASCII_MARKERS.HASH &&
|
|
154
|
+
firstChar !== ASCII_MARKERS.DASH &&
|
|
155
|
+
firstChar !== ASCII_MARKERS.ASTERISK &&
|
|
156
|
+
firstChar !== ASCII_MARKERS.PLUS &&
|
|
157
|
+
firstChar !== ASCII_MARKERS.BRACKET_OPEN &&
|
|
158
|
+
(firstChar < ASCII_MARKERS.DIGIT_0 || firstChar > ASCII_MARKERS.DIGIT_9)) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
return (REGEX.HEADING_MARKER.test(trimmed) ||
|
|
162
|
+
REGEX.LIST_MARKER.test(trimmed) ||
|
|
163
|
+
/^\d+\.\s/.test(trimmed) ||
|
|
164
|
+
/^\[.*\]\(.*\)$/.test(trimmed));
|
|
165
|
+
}
|
|
166
|
+
function isTerminalPunctuation(charCode) {
|
|
167
|
+
return (charCode === ASCII_MARKERS.PERIOD ||
|
|
168
|
+
charCode === ASCII_MARKERS.EXCLAMATION ||
|
|
169
|
+
charCode === ASCII_MARKERS.QUESTION);
|
|
170
|
+
}
|
|
143
171
|
function getHeadingPrefix(trimmed) {
|
|
144
172
|
if (trimmed.length > MAX_LINE_LENGTH)
|
|
145
173
|
return null;
|
|
146
174
|
if (REPL_PROMPT_LINE.test(trimmed))
|
|
147
175
|
return null;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (firstChar === ASCII_MARKERS.HASH ||
|
|
151
|
-
firstChar === ASCII_MARKERS.DASH ||
|
|
152
|
-
firstChar === ASCII_MARKERS.ASTERISK ||
|
|
153
|
-
firstChar === ASCII_MARKERS.PLUS ||
|
|
154
|
-
firstChar === ASCII_MARKERS.BRACKET_OPEN ||
|
|
155
|
-
(firstChar >= ASCII_MARKERS.DIGIT_0 && firstChar <= ASCII_MARKERS.DIGIT_9)) {
|
|
156
|
-
if (REGEX.HEADING_MARKER.test(trimmed) ||
|
|
157
|
-
REGEX.LIST_MARKER.test(trimmed) ||
|
|
158
|
-
/^\d+\.\s/.test(trimmed) ||
|
|
159
|
-
/^\[.*\]\(.*\)$/.test(trimmed)) {
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
176
|
+
if (isMarkdownStructuralLine(trimmed))
|
|
177
|
+
return null;
|
|
163
178
|
if (SPECIAL_PREFIXES.test(trimmed)) {
|
|
164
179
|
return /^example:\s/i.test(trimmed) ? '### ' : '## ';
|
|
165
180
|
}
|
|
166
|
-
|
|
167
|
-
if (lastChar === ASCII_MARKERS.PERIOD ||
|
|
168
|
-
lastChar === ASCII_MARKERS.EXCLAMATION ||
|
|
169
|
-
lastChar === ASCII_MARKERS.QUESTION)
|
|
181
|
+
if (isTerminalPunctuation(trimmed.charCodeAt(trimmed.length - 1)))
|
|
170
182
|
return null;
|
|
171
183
|
return isTitleCaseOrKeyword(trimmed) ? '## ' : null;
|
|
172
184
|
}
|
|
@@ -219,57 +231,59 @@ function isTypeDocArtifactLine(line) {
|
|
|
219
231
|
}
|
|
220
232
|
return false;
|
|
221
233
|
}
|
|
222
|
-
function tryPromoteOrphan(
|
|
223
|
-
const prevLine = lines[
|
|
224
|
-
const isOrphan =
|
|
234
|
+
function tryPromoteOrphan(ctx) {
|
|
235
|
+
const prevLine = ctx.lines[ctx.index - 1];
|
|
236
|
+
const isOrphan = ctx.index === 0 || !prevLine || prevLine.trim().length === 0;
|
|
225
237
|
if (!isOrphan)
|
|
226
238
|
return null;
|
|
227
|
-
const prefix = getHeadingPrefix(trimmed);
|
|
239
|
+
const prefix = getHeadingPrefix(ctx.trimmed);
|
|
228
240
|
if (!prefix)
|
|
229
241
|
return null;
|
|
230
|
-
const isSpecialPrefix = SPECIAL_PREFIXES.test(trimmed);
|
|
231
|
-
if (!isSpecialPrefix && !hasFollowingContent(lines,
|
|
242
|
+
const isSpecialPrefix = SPECIAL_PREFIXES.test(ctx.trimmed);
|
|
243
|
+
if (!isSpecialPrefix && !hasFollowingContent(ctx.lines, ctx.index))
|
|
232
244
|
return null;
|
|
233
245
|
if (!isSpecialPrefix) {
|
|
234
|
-
const
|
|
246
|
+
const nextIdx = findNextNonBlank(ctx.lines, ctx.index + 1, HAS_FOLLOWING_LOOKAHEAD);
|
|
247
|
+
const nextLine = nextIdx >= 0 ? ctx.lines[nextIdx]?.trim() : undefined;
|
|
235
248
|
if (nextLine && REGEX.HEADING_MARKER.test(nextLine))
|
|
236
249
|
return null;
|
|
237
250
|
}
|
|
238
|
-
return `${prefix}${trimmed}`;
|
|
251
|
+
return `${prefix}${ctx.trimmed}`;
|
|
239
252
|
}
|
|
240
|
-
function shouldSkipAsToc(
|
|
241
|
-
if (!removeToc || !REGEX.TOC_HEADING.test(trimmed))
|
|
253
|
+
function shouldSkipAsToc(ctx, removeToc, options) {
|
|
254
|
+
if (!removeToc || !REGEX.TOC_HEADING.test(ctx.trimmed))
|
|
242
255
|
return null;
|
|
243
|
-
const { total, linkCount, nonLinkCount } = getTocBlockStats(lines,
|
|
256
|
+
const { total, linkCount, nonLinkCount } = getTocBlockStats(ctx.lines, ctx.index);
|
|
244
257
|
if (total === 0 || nonLinkCount > 0)
|
|
245
258
|
return null;
|
|
246
259
|
const ratio = linkCount / total;
|
|
247
260
|
if (ratio <= TOC_LINK_RATIO_THRESHOLD)
|
|
248
261
|
return null;
|
|
249
262
|
throwIfAborted(options?.signal, options?.url ?? '', 'markdown:cleanup:toc');
|
|
250
|
-
return skipTocLines(lines,
|
|
263
|
+
return skipTocLines(ctx.lines, ctx.index + 1);
|
|
251
264
|
}
|
|
252
|
-
function normalizePreprocessLine(
|
|
253
|
-
if (REGEX.EMPTY_HEADING_LINE.test(trimmed))
|
|
265
|
+
function normalizePreprocessLine(ctx, options) {
|
|
266
|
+
if (REGEX.EMPTY_HEADING_LINE.test(ctx.trimmed))
|
|
254
267
|
return null;
|
|
255
|
-
if (!REGEX.ANCHOR_ONLY_HEADING.test(trimmed))
|
|
256
|
-
return line;
|
|
257
|
-
if (!hasFollowingContent(lines,
|
|
268
|
+
if (!REGEX.ANCHOR_ONLY_HEADING.test(ctx.trimmed))
|
|
269
|
+
return ctx.line;
|
|
270
|
+
if (!hasFollowingContent(ctx.lines, ctx.index)) {
|
|
258
271
|
return options?.preserveEmptyHeadings
|
|
259
|
-
? stripAnchorOnlyHeading(trimmed)
|
|
272
|
+
? stripAnchorOnlyHeading(ctx.trimmed)
|
|
260
273
|
: null;
|
|
261
274
|
}
|
|
262
|
-
return stripAnchorOnlyHeading(trimmed);
|
|
275
|
+
return stripAnchorOnlyHeading(ctx.trimmed);
|
|
263
276
|
}
|
|
264
|
-
function maybeSkipTocBlock(
|
|
265
|
-
return shouldSkipAsToc(
|
|
277
|
+
function maybeSkipTocBlock(ctx, options) {
|
|
278
|
+
return shouldSkipAsToc(ctx, config.markdownCleanup.removeTocBlocks, options);
|
|
266
279
|
}
|
|
267
|
-
function maybePromoteOrphanHeading(
|
|
268
|
-
if (!config.markdownCleanup.promoteOrphanHeadings ||
|
|
280
|
+
function maybePromoteOrphanHeading(ctx, checkAbort) {
|
|
281
|
+
if (!config.markdownCleanup.promoteOrphanHeadings ||
|
|
282
|
+
ctx.trimmed.length === 0) {
|
|
269
283
|
return null;
|
|
270
284
|
}
|
|
271
285
|
checkAbort('markdown:cleanup:promote');
|
|
272
|
-
return tryPromoteOrphan(
|
|
286
|
+
return tryPromoteOrphan(ctx);
|
|
273
287
|
}
|
|
274
288
|
function preprocessLines(lines, options) {
|
|
275
289
|
const checkAbort = createAbortChecker(options);
|
|
@@ -280,15 +294,16 @@ function preprocessLines(lines, options) {
|
|
|
280
294
|
continue;
|
|
281
295
|
const currentLine = lines[i] ?? '';
|
|
282
296
|
const trimmed = currentLine.trim();
|
|
283
|
-
const
|
|
297
|
+
const ctx = { lines, index: i, trimmed, line: currentLine };
|
|
298
|
+
const normalizedLine = normalizePreprocessLine(ctx, options);
|
|
284
299
|
if (normalizedLine === null)
|
|
285
300
|
continue;
|
|
286
|
-
const tocSkip = maybeSkipTocBlock(
|
|
301
|
+
const tocSkip = maybeSkipTocBlock(ctx, options);
|
|
287
302
|
if (tocSkip !== null) {
|
|
288
303
|
skipUntil = tocSkip;
|
|
289
304
|
continue;
|
|
290
305
|
}
|
|
291
|
-
const promotedLine = maybePromoteOrphanHeading(
|
|
306
|
+
const promotedLine = maybePromoteOrphanHeading(ctx, checkAbort);
|
|
292
307
|
result.push(promotedLine ?? normalizedLine);
|
|
293
308
|
}
|
|
294
309
|
return result.join('\n');
|
|
@@ -369,13 +384,6 @@ function getHeadingInfo(line) {
|
|
|
369
384
|
return null;
|
|
370
385
|
return { level: match[1]?.length ?? 0 };
|
|
371
386
|
}
|
|
372
|
-
function findNextNonBlankIndex(lines, startIndex) {
|
|
373
|
-
let idx = startIndex;
|
|
374
|
-
while (idx < lines.length && isBlank(lines[idx])) {
|
|
375
|
-
idx += 1;
|
|
376
|
-
}
|
|
377
|
-
return idx;
|
|
378
|
-
}
|
|
379
387
|
function removeEmptyHeadingSections(text) {
|
|
380
388
|
const lines = text.split('\n');
|
|
381
389
|
const kept = [];
|
|
@@ -386,8 +394,8 @@ function removeEmptyHeadingSections(text) {
|
|
|
386
394
|
kept.push(line);
|
|
387
395
|
continue;
|
|
388
396
|
}
|
|
389
|
-
const nextIndex =
|
|
390
|
-
const nextLine = lines[nextIndex];
|
|
397
|
+
const nextIndex = findNextNonBlank(lines, i + 1);
|
|
398
|
+
const nextLine = nextIndex >= 0 ? lines[nextIndex] : undefined;
|
|
391
399
|
if (nextLine === undefined) {
|
|
392
400
|
kept.push(line);
|
|
393
401
|
continue;
|
|
@@ -482,9 +490,9 @@ function applyGlobalRegexes(text, options) {
|
|
|
482
490
|
function normalizeNestedListIndentation(text) {
|
|
483
491
|
return text.replace(REGEX.NESTED_LIST_INDENT, (match, spaces, marker) => {
|
|
484
492
|
const count = spaces.length;
|
|
485
|
-
if (count <
|
|
493
|
+
if (count < SOURCE_INDENT_STEP || count % SOURCE_INDENT_STEP !== 0)
|
|
486
494
|
return match;
|
|
487
|
-
const normalized = ' '.repeat((count /
|
|
495
|
+
const normalized = ' '.repeat((count / SOURCE_INDENT_STEP) * TARGET_INDENT_STEP);
|
|
488
496
|
return `${normalized}${marker} `;
|
|
489
497
|
});
|
|
490
498
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/transform/transform.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/transform/transform.ts"],"names":[],"mappings":"AA0CA,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,oBAAoB,EACrB,MAAM,uBAAuB,CAAC;AAqB/B,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EAEtB,MAAM,YAAY,CAAC;AA+BpB,UAAU,WAAW;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AA4LD,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,WAAW,GACnB,qBAAqB,GAAG,IAAI,CAE9B;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,qBAAqB,GAAG,IAAI,EACrC,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAChC,MAAM,CAER;AA2aD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAExD,GACA,gBAAgB,CAGlB;AAuKD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,aAAa,EACxB,OAAO,CAAC,EAAE;IACR,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAChC,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACxC,GACA,MAAM,CAyBR;AA2DD,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,gBAAgB,GAAG,IAAI,EAChC,sBAAsB,EAAE,MAAM,GAAG,QAAQ,GACxC,OAAO,CAQT;AAKD,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,gBAAgB,GAAG,IAAI,GAC/B,OAAO,IAAI,gBAAgB,CAE7B;AAED,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,gBAAgB,GAAG,IAAI,EAChC,aAAa,EAAE,iBAAiB,EAChC,wBAAwB,EAAE,OAAO,EACjC,eAAe,EAAE,OAAO,GACvB,aAAa,GAAG,SAAS,CAuB3B;AA8bD,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,gBAAgB,GACxB,uBAAuB,CAMzB;AAaD,UAAU,kBAAkB;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,qBAAqB,IAAI,kBAAkB,GAAG,IAAI,CAEjE;AAED,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC,CAEjE;AAED,KAAK,yBAAyB,GAAG,gBAAgB,GAAG;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAuG1E,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,uBAAuB,CAAC,CAElC;AAED,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,UAAU,EACtB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,uBAAuB,CAAC,CAElC;AAED,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,oBAAoB,GACrB,CAAC"}
|
|
@@ -3,7 +3,7 @@ import { isProbablyReaderable, Readability } from '@mozilla/readability';
|
|
|
3
3
|
import { parseHTML } from 'linkedom';
|
|
4
4
|
import { config } from '../lib/core.js';
|
|
5
5
|
import { getOperationId, getRequestId, logDebug, logError, logInfo, logWarn, redactUrl, } from '../lib/core.js';
|
|
6
|
-
import { evaluateArticleContent, extractNoscriptImages, getVisibleTextLength, normalizeTabContent, prepareDocumentForMarkdown, removeNoiseFromHtml, serializeDocumentForMarkdown, surfaceCodeEditorContent, } from '../lib/dom-prep.js';
|
|
6
|
+
import { evaluateArticleContent, extractNoscriptImages, getVisibleTextLength, normalizeTabContent, prepareDocumentForMarkdown, removeNoiseFromHtml, serializeDocumentForMarkdown, stripDocsControls, surfaceCodeEditorContent, } from '../lib/dom-prep.js';
|
|
7
7
|
import { isRawTextContentUrl } from '../lib/http.js';
|
|
8
8
|
import { composeAbortSignal, FetchError, getErrorMessage, getUtf8ByteLength, isAsciiOnly, isObject, throwIfAborted, toError, trimDanglingTagFragment, truncateToUtf8Boundary, } from '../lib/utils.js';
|
|
9
9
|
import { extractLanguageFromClassName } from './html-translators.js';
|
|
@@ -139,6 +139,36 @@ class StageTracker {
|
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
|
+
runTrackedSync(url, signal, fn) {
|
|
143
|
+
const totalStage = this.start(url, 'transform:total');
|
|
144
|
+
try {
|
|
145
|
+
throwIfAborted(signal, url, 'transform:begin');
|
|
146
|
+
const result = fn();
|
|
147
|
+
this.end(totalStage, result.truncated !== undefined
|
|
148
|
+
? { truncated: result.truncated }
|
|
149
|
+
: undefined);
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
this.end(totalStage);
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async runTrackedAsync(url, signal, fn) {
|
|
158
|
+
const totalStage = this.start(url, 'transform:total');
|
|
159
|
+
try {
|
|
160
|
+
throwIfAborted(signal, url, 'transform:begin');
|
|
161
|
+
const result = await fn();
|
|
162
|
+
this.end(totalStage, result.truncated !== undefined
|
|
163
|
+
? { truncated: result.truncated }
|
|
164
|
+
: undefined);
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
this.end(totalStage);
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
142
172
|
}
|
|
143
173
|
const stageTracker = new StageTracker();
|
|
144
174
|
export function startTransformStage(url, stage, budget) {
|
|
@@ -148,7 +178,7 @@ export function endTransformStage(context, options) {
|
|
|
148
178
|
return stageTracker.end(context, options);
|
|
149
179
|
}
|
|
150
180
|
function truncateHtml(html, inputTruncated = false) {
|
|
151
|
-
const maxSize = config.constants.
|
|
181
|
+
const maxSize = config.constants.maxHtmlBytes;
|
|
152
182
|
if (maxSize <= 0)
|
|
153
183
|
return { html, truncated: false };
|
|
154
184
|
if (html.length <= maxSize) {
|
|
@@ -182,7 +212,7 @@ function isReadabilityCompatible(doc) {
|
|
|
182
212
|
'function' &&
|
|
183
213
|
typeof record.querySelector === 'function');
|
|
184
214
|
}
|
|
185
|
-
function
|
|
215
|
+
function getNormalizedTextLengthUpTo(text, max) {
|
|
186
216
|
if (max <= 0)
|
|
187
217
|
return 0;
|
|
188
218
|
let length = 0;
|
|
@@ -234,6 +264,45 @@ function preserveAlertElements(doc) {
|
|
|
234
264
|
el.replaceWith(bq);
|
|
235
265
|
}
|
|
236
266
|
}
|
|
267
|
+
function preserveHeadingLayouts(doc) {
|
|
268
|
+
// Readability aggressively drops elements matching /header/i in their class/id.
|
|
269
|
+
// Many technical docs use `<div class="layout__header">` to wrap their title and intro text,
|
|
270
|
+
// causing the ENTIRE intro and H1 to be dropped.
|
|
271
|
+
for (const heading of doc.querySelectorAll('h1, h2')) {
|
|
272
|
+
let p = heading.parentNode;
|
|
273
|
+
while (p && p.tagName !== 'BODY' && p.tagName !== 'HTML') {
|
|
274
|
+
const cls = p.getAttribute('class');
|
|
275
|
+
if (cls && /header/i.test(cls)) {
|
|
276
|
+
p.setAttribute('class', cls.replace(/header/gi, 'hdr-preserved'));
|
|
277
|
+
}
|
|
278
|
+
const id = p.getAttribute('id');
|
|
279
|
+
if (id && /header/i.test(id)) {
|
|
280
|
+
p.setAttribute('id', id.replace(/header/gi, 'hdr-preserved'));
|
|
281
|
+
}
|
|
282
|
+
p = p.parentNode;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// To prevent Readability from penalizing sibling document sections
|
|
286
|
+
// (e.g. intro vs reference tables) and picking only one, we unwrap structural wrappers inside main boundaries.
|
|
287
|
+
for (const main of doc.querySelectorAll('main, [role="main"], article')) {
|
|
288
|
+
for (const child of Array.from(main.children)) {
|
|
289
|
+
// Don't unwrap nav, aside, or blockquotes (alerts are already converted to blockquotes here)
|
|
290
|
+
if (child.tagName === 'DIV' ||
|
|
291
|
+
child.tagName === 'HEADER' ||
|
|
292
|
+
child.tagName === 'SECTION') {
|
|
293
|
+
// preserve specific structural features Readability might want to keep
|
|
294
|
+
const cls = child.getAttribute('class') ?? '';
|
|
295
|
+
if (cls.includes('mermaid'))
|
|
296
|
+
continue;
|
|
297
|
+
const frag = doc.createDocumentFragment();
|
|
298
|
+
while (child.firstChild) {
|
|
299
|
+
frag.appendChild(child.firstChild);
|
|
300
|
+
}
|
|
301
|
+
child.replaceWith(frag);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
237
306
|
function preserveCodeLanguageAttributes(doc) {
|
|
238
307
|
for (const el of doc.querySelectorAll('pre, code')) {
|
|
239
308
|
if (el.getAttribute('data-language'))
|
|
@@ -247,9 +316,11 @@ function prepareReadabilityDocument(readabilityDoc) {
|
|
|
247
316
|
extractNoscriptImages(readabilityDoc);
|
|
248
317
|
preserveGalleryImages(readabilityDoc);
|
|
249
318
|
preserveAlertElements(readabilityDoc);
|
|
319
|
+
preserveHeadingLayouts(readabilityDoc);
|
|
250
320
|
preserveCodeLanguageAttributes(readabilityDoc);
|
|
251
321
|
normalizeTabContent(readabilityDoc);
|
|
252
322
|
surfaceCodeEditorContent(readabilityDoc);
|
|
323
|
+
stripDocsControls(readabilityDoc);
|
|
253
324
|
for (const el of readabilityDoc.querySelectorAll('[class*="breadcrumb"],[class*="pagination"]')) {
|
|
254
325
|
if (el.tagName === 'HTML' || el.tagName === 'BODY')
|
|
255
326
|
continue;
|
|
@@ -261,7 +332,7 @@ function validateReaderability(doc, url, signal) {
|
|
|
261
332
|
const rawText = doc.querySelector('body')?.textContent ??
|
|
262
333
|
doc.documentElement.textContent ??
|
|
263
334
|
'';
|
|
264
|
-
const textLength =
|
|
335
|
+
const textLength = getNormalizedTextLengthUpTo(rawText, MIN_READERABLE_TEXT_LENGTH + 1);
|
|
265
336
|
if (textLength < MIN_SPA_CONTENT_LENGTH) {
|
|
266
337
|
logWarn('Very minimal server-rendered content detected (< 100 chars). ' +
|
|
267
338
|
'This might be a client-side rendered (SPA) application. ' +
|
|
@@ -360,7 +431,7 @@ function createEmptyExtractionContext() {
|
|
|
360
431
|
return { article: null, metadata: {}, document };
|
|
361
432
|
}
|
|
362
433
|
function extractEarlyMetadataIfNeeded(html, url) {
|
|
363
|
-
const maxSize = config.constants.
|
|
434
|
+
const maxSize = config.constants.maxHtmlBytes;
|
|
364
435
|
if (maxSize <= 0)
|
|
365
436
|
return null;
|
|
366
437
|
if (html.length <= maxSize &&
|
|
@@ -447,27 +518,26 @@ function findBalancedCloseParen(text, start) {
|
|
|
447
518
|
return -1;
|
|
448
519
|
}
|
|
449
520
|
function findInlineLink(markdown, start) {
|
|
450
|
-
let
|
|
451
|
-
while (
|
|
452
|
-
const openBracket = markdown.indexOf('[', searchFrom);
|
|
453
|
-
if (openBracket === -1)
|
|
454
|
-
return null;
|
|
521
|
+
let openBracket = markdown.indexOf('[', start);
|
|
522
|
+
while (openBracket !== -1) {
|
|
455
523
|
const closeBracket = markdown.indexOf(']', openBracket + 1);
|
|
456
524
|
if (closeBracket === -1)
|
|
457
525
|
return null;
|
|
458
526
|
if (markdown[closeBracket + 1] !== '(') {
|
|
459
|
-
|
|
527
|
+
openBracket = markdown.indexOf('[', closeBracket + 1);
|
|
460
528
|
continue;
|
|
461
529
|
}
|
|
462
530
|
const closeParen = findBalancedCloseParen(markdown, closeBracket + 2);
|
|
463
531
|
if (closeParen === -1)
|
|
464
532
|
return null;
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
533
|
+
const isImage = openBracket > 0 && markdown[openBracket - 1] === '!';
|
|
534
|
+
const prefixStart = isImage ? openBracket - 1 : openBracket;
|
|
535
|
+
return {
|
|
536
|
+
prefixStart,
|
|
537
|
+
closeParen,
|
|
538
|
+
prefix: markdown.slice(prefixStart, closeBracket + 1),
|
|
539
|
+
href: markdown.slice(closeBracket + 2, closeParen),
|
|
540
|
+
};
|
|
471
541
|
}
|
|
472
542
|
return null;
|
|
473
543
|
}
|
|
@@ -646,7 +716,7 @@ function resolveContentTitle(params) {
|
|
|
646
716
|
normalizeSyntheticTitleToken(params.primaryHeading),
|
|
647
717
|
};
|
|
648
718
|
}
|
|
649
|
-
const
|
|
719
|
+
const CONTENT_REGION_SELECTORS = [
|
|
650
720
|
'article',
|
|
651
721
|
'main',
|
|
652
722
|
'[role="main"]',
|
|
@@ -662,14 +732,12 @@ const CONTENT_ROOT_SELECTORS = [
|
|
|
662
732
|
'.post-body',
|
|
663
733
|
'.article-body',
|
|
664
734
|
];
|
|
665
|
-
const
|
|
666
|
-
...CONTENT_ROOT_SELECTORS,
|
|
735
|
+
const HEADING_REGION_EXTRA_SELECTORS = [
|
|
667
736
|
'.markdown-body',
|
|
668
|
-
'.entry-content',
|
|
669
737
|
'[itemprop="text"]',
|
|
670
738
|
];
|
|
671
739
|
function findContentRoot(document) {
|
|
672
|
-
for (const selector of
|
|
740
|
+
for (const selector of CONTENT_REGION_SELECTORS) {
|
|
673
741
|
const element = document.querySelector(selector);
|
|
674
742
|
if (!element)
|
|
675
743
|
continue;
|
|
@@ -702,7 +770,10 @@ function findPrimaryHeading(document) {
|
|
|
702
770
|
const globalHeading = extractHeadingText(document, PRIMARY_HEADING_SELECTORS_GLOBAL);
|
|
703
771
|
if (globalHeading)
|
|
704
772
|
return globalHeading;
|
|
705
|
-
for (const selector of
|
|
773
|
+
for (const selector of [
|
|
774
|
+
...CONTENT_REGION_SELECTORS,
|
|
775
|
+
...HEADING_REGION_EXTRA_SELECTORS,
|
|
776
|
+
]) {
|
|
706
777
|
const root = document.querySelector(selector);
|
|
707
778
|
if (!root)
|
|
708
779
|
continue;
|
|
@@ -759,39 +830,44 @@ function buildRawSource(base, params) {
|
|
|
759
830
|
title: params.extractedMeta.title,
|
|
760
831
|
};
|
|
761
832
|
}
|
|
762
|
-
function
|
|
763
|
-
const { html, url, article, extractedMeta, includeMetadata, evaluatedArticleDoc, document, truncated, signal, } =
|
|
764
|
-
const
|
|
765
|
-
const metadata = createContentMetadataBlock(url, article, extractedMeta, useArticleContent, includeMetadata);
|
|
833
|
+
function resolveBaseContentSource(input) {
|
|
834
|
+
const { html, url, article, extractedMeta, includeMetadata, evaluatedArticleDoc, document, truncated, signal, } = input;
|
|
835
|
+
const metadata = createContentMetadataBlock(url, article, extractedMeta, evaluatedArticleDoc !== null, includeMetadata);
|
|
766
836
|
const preparedDocument = document
|
|
767
837
|
? prepareContentSourceDocument(document, url, signal)
|
|
768
838
|
: undefined;
|
|
769
|
-
const primaryHeading = preparedDocument?.primaryHeading;
|
|
770
839
|
const base = {
|
|
771
840
|
favicon: extractedMeta.favicon,
|
|
772
841
|
metadata,
|
|
773
842
|
extractedMetadata: extractedMeta,
|
|
774
843
|
truncated,
|
|
775
|
-
primaryHeading,
|
|
844
|
+
primaryHeading: preparedDocument?.primaryHeading,
|
|
776
845
|
originalHtml: html,
|
|
777
846
|
};
|
|
778
|
-
|
|
847
|
+
return { base, preparedDocument };
|
|
848
|
+
}
|
|
849
|
+
function buildContentSource(input) {
|
|
850
|
+
const { base, preparedDocument } = resolveBaseContentSource(input);
|
|
851
|
+
if (input.evaluatedArticleDoc && input.article) {
|
|
779
852
|
return buildArticleSource(base, {
|
|
780
|
-
evaluatedArticleDoc,
|
|
781
|
-
article,
|
|
782
|
-
extractedMeta,
|
|
783
|
-
url,
|
|
784
|
-
signal,
|
|
853
|
+
evaluatedArticleDoc: input.evaluatedArticleDoc,
|
|
854
|
+
article: input.article,
|
|
855
|
+
extractedMeta: input.extractedMeta,
|
|
856
|
+
url: input.url,
|
|
857
|
+
signal: input.signal,
|
|
785
858
|
});
|
|
786
859
|
}
|
|
787
860
|
if (preparedDocument) {
|
|
788
861
|
return buildDocumentSource(base, {
|
|
789
862
|
resolvedDocument: preparedDocument.document,
|
|
790
|
-
html,
|
|
791
|
-
extractedMeta,
|
|
863
|
+
html: input.html,
|
|
864
|
+
extractedMeta: input.extractedMeta,
|
|
792
865
|
});
|
|
793
866
|
}
|
|
794
|
-
return buildRawSource(base, {
|
|
867
|
+
return buildRawSource(base, {
|
|
868
|
+
html: input.html,
|
|
869
|
+
extractedMeta: input.extractedMeta,
|
|
870
|
+
});
|
|
795
871
|
}
|
|
796
872
|
function resolveContentSource(params) {
|
|
797
873
|
const { article, metadata: extractedMeta, document, truncated, } = extractContentContext(params.html, params.url, {
|
|
@@ -857,10 +933,6 @@ function resolveTransformContentResult(html, url, options, signal) {
|
|
|
857
933
|
}));
|
|
858
934
|
return buildMarkdownFromContext(context, url, signal);
|
|
859
935
|
}
|
|
860
|
-
function completeTrackedTransform(totalStage, result) {
|
|
861
|
-
stageTracker.end(totalStage, result.truncated !== undefined ? { truncated: result.truncated } : undefined);
|
|
862
|
-
return result;
|
|
863
|
-
}
|
|
864
936
|
const REPLACEMENT_CHAR = '\ufffd';
|
|
865
937
|
const BINARY_INDICATOR_THRESHOLD = 0.1;
|
|
866
938
|
function hasBinaryIndicators(content) {
|
|
@@ -879,16 +951,10 @@ function hasBinaryIndicators(content) {
|
|
|
879
951
|
}
|
|
880
952
|
export function transformHtmlToMarkdownInProcess(html, url, options) {
|
|
881
953
|
const signal = buildTransformSignal(options.signal);
|
|
882
|
-
|
|
883
|
-
try {
|
|
884
|
-
throwIfAborted(signal, url, 'transform:begin');
|
|
954
|
+
return stageTracker.runTrackedSync(url, signal, () => {
|
|
885
955
|
validateBinaryContent(html, url);
|
|
886
|
-
return
|
|
887
|
-
}
|
|
888
|
-
catch (error) {
|
|
889
|
-
stageTracker.end(totalStage);
|
|
890
|
-
throw error;
|
|
891
|
-
}
|
|
956
|
+
return resolveTransformContentResult(html, url, options, signal);
|
|
957
|
+
});
|
|
892
958
|
}
|
|
893
959
|
function validateBinaryContent(html, url) {
|
|
894
960
|
if (hasBinaryIndicators(html)) {
|
|
@@ -957,15 +1023,7 @@ async function runWorkerTransformWithFallback(htmlOrBuffer, url, options) {
|
|
|
957
1023
|
});
|
|
958
1024
|
}
|
|
959
1025
|
async function transformInputToMarkdown(htmlOrBuffer, url, options) {
|
|
960
|
-
|
|
961
|
-
try {
|
|
962
|
-
throwIfAborted(options.signal, url, 'transform:begin');
|
|
963
|
-
return completeTrackedTransform(totalStage, await runWorkerTransformWithFallback(htmlOrBuffer, url, options));
|
|
964
|
-
}
|
|
965
|
-
catch (error) {
|
|
966
|
-
stageTracker.end(totalStage);
|
|
967
|
-
throw error;
|
|
968
|
-
}
|
|
1026
|
+
return stageTracker.runTrackedAsync(url, options.signal, () => runWorkerTransformWithFallback(htmlOrBuffer, url, options));
|
|
969
1027
|
}
|
|
970
1028
|
export async function transformHtmlToMarkdown(html, url, options) {
|
|
971
1029
|
return transformInputToMarkdown(html, url, options);
|
|
@@ -24,6 +24,8 @@ declare class WorkerPool implements TransformWorkerPool {
|
|
|
24
24
|
private closed;
|
|
25
25
|
private taskIdSeq;
|
|
26
26
|
private busyCount;
|
|
27
|
+
private draining;
|
|
28
|
+
private readonly restartBackoff;
|
|
27
29
|
constructor(size: number, timeoutMs: number);
|
|
28
30
|
transform(html: string, url: string, options: {
|
|
29
31
|
includeMetadata: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../../src/transform/worker-pool.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EACV,uBAAuB,EAKxB,MAAM,YAAY,CAAC;AAqIpB,UAAU,mBAAmB;IAC3B,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QACP,eAAe,EAAE,OAAO,CAAC;QACzB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,cAAc,CAAC,EAAE,OAAO,CAAC;KAC1B,GACA,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACpC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,aAAa,IAAI,MAAM,CAAC;IACxB,gBAAgB,IAAI,MAAM,CAAC;IAC3B,WAAW,IAAI,MAAM,CAAC;CACvB;AAmJD,cAAM,UAAW,YAAW,mBAAmB;IAC7C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAkC;IAExE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAEhD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IACtD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAC5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0B;IAErD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,SAAS,CAAK;
|
|
1
|
+
{"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../../src/transform/worker-pool.ts"],"names":[],"mappings":"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"}
|