webforai 2.1.1 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -4
- package/dist/adapters/index.js +58 -0
- package/dist/adapters/registry.js +149 -0
- package/dist/adapters/sites/declarative.js +178 -0
- package/dist/adapters/sites/hackernews.js +94 -0
- package/dist/adapters/sites/youtube.js +165 -0
- package/dist/adapters/types.js +0 -0
- package/dist/bin.js +4142 -549
- package/dist/cjs/adapters/index.js +86 -0
- package/dist/cjs/adapters/registry.js +178 -0
- package/dist/cjs/adapters/sites/declarative.js +202 -0
- package/dist/cjs/adapters/sites/hackernews.js +117 -0
- package/dist/cjs/adapters/sites/youtube.js +191 -0
- package/dist/cjs/adapters/types.js +16 -0
- package/dist/cjs/constants.js +2 -2
- package/dist/cjs/detect-client-shell.js +78 -0
- package/dist/cjs/extract-meta-refresh.js +71 -0
- package/dist/cjs/extractors/index.js +5 -0
- package/dist/cjs/extractors/lib/constants.js +186 -0
- package/dist/cjs/extractors/lib/sanitize.js +429 -0
- package/dist/cjs/extractors/lib/score.js +264 -0
- package/dist/cjs/extractors/lib/terminators.js +131 -0
- package/dist/cjs/extractors/lib/ui-chrome.js +119 -0
- package/dist/cjs/extractors/pipeExtractors.js +12 -9
- package/dist/cjs/extractors/presets/auto.js +44 -0
- package/dist/cjs/extractors/presets/minimal-filter.js +27 -48
- package/dist/cjs/extractors/presets/takumi.js +84 -122
- package/dist/cjs/html-to-markdown.js +44 -7
- package/dist/cjs/html-to-mdast.js +10 -1
- package/dist/cjs/index.js +43 -2
- package/dist/cjs/link-replacer.js +19 -13
- package/dist/cjs/loaders/cf-puppeteer.js +14 -7
- package/dist/cjs/loaders/fetch.js +14 -2
- package/dist/cjs/loaders/playwright.js +32 -30
- package/dist/cjs/loaders/puppeteer.js +14 -7
- package/dist/cjs/md-splitter.js +24 -11
- package/dist/cjs/mdast-handlers/custom-a-handler.js +25 -9
- package/dist/cjs/mdast-handlers/custom-code-handler.js +18 -7
- package/dist/cjs/mdast-handlers/custom-div-handler.js +12 -33
- package/dist/cjs/mdast-handlers/custom-img-handler.js +23 -1
- package/dist/cjs/mdast-handlers/custom-table-handler.js +16 -0
- package/dist/cjs/mdast-handlers/definition-list-handler.js +110 -0
- package/dist/cjs/mdast-handlers/math-handler.js +12 -3
- package/dist/cjs/mdast-handlers/sup-sub-handler.js +39 -0
- package/dist/cjs/mdast-to-markdown.js +2 -5
- package/dist/cjs/metadata/index.js +223 -0
- package/dist/cjs/normalize/index.js +177 -0
- package/dist/cjs/platform/client.js +204 -0
- package/dist/cjs/platform/error.js +36 -0
- package/dist/cjs/platform/fetch.js +37 -0
- package/dist/cjs/platform/index.js +44 -0
- package/dist/cjs/platform/types.js +40 -0
- package/dist/cjs/utils/hast-fast.js +284 -0
- package/dist/constants.js +2 -2
- package/dist/detect-client-shell.js +53 -0
- package/dist/extract-meta-refresh.js +47 -0
- package/dist/extractors/index.js +3 -0
- package/dist/extractors/lib/constants.js +150 -0
- package/dist/extractors/lib/sanitize.js +419 -0
- package/dist/extractors/lib/score.js +242 -0
- package/dist/extractors/lib/terminators.js +107 -0
- package/dist/extractors/lib/ui-chrome.js +96 -0
- package/dist/extractors/pipeExtractors.js +12 -9
- package/dist/extractors/presets/auto.js +20 -0
- package/dist/extractors/presets/minimal-filter.js +35 -47
- package/dist/extractors/presets/takumi.js +91 -122
- package/dist/html-to-markdown.js +42 -6
- package/dist/html-to-mdast.js +10 -1
- package/dist/index.js +45 -2
- package/dist/link-replacer.js +19 -13
- package/dist/loaders/cf-puppeteer.js +15 -8
- package/dist/loaders/fetch.js +14 -2
- package/dist/loaders/playwright.js +32 -30
- package/dist/loaders/puppeteer.js +15 -8
- package/dist/md-splitter.js +24 -11
- package/dist/mdast-handlers/custom-a-handler.js +25 -9
- package/dist/mdast-handlers/custom-code-handler.js +17 -7
- package/dist/mdast-handlers/custom-div-handler.js +12 -33
- package/dist/mdast-handlers/custom-img-handler.js +23 -1
- package/dist/mdast-handlers/custom-table-handler.js +16 -0
- package/dist/mdast-handlers/definition-list-handler.js +87 -0
- package/dist/mdast-handlers/math-handler.js +12 -3
- package/dist/mdast-handlers/sup-sub-handler.js +16 -0
- package/dist/mdast-to-markdown.js +2 -5
- package/dist/metadata/index.js +198 -0
- package/dist/normalize/index.js +159 -0
- package/dist/platform/client.js +182 -0
- package/dist/platform/error.js +13 -0
- package/dist/platform/fetch.js +14 -0
- package/dist/platform/index.js +23 -0
- package/dist/platform/types.js +13 -0
- package/dist/types/adapters/index.d.ts +24 -0
- package/dist/types/adapters/index.js.map +1 -0
- package/dist/types/adapters/registry.d.ts +35 -0
- package/dist/types/adapters/registry.js.map +1 -0
- package/dist/types/adapters/sites/declarative.d.ts +13 -0
- package/dist/types/adapters/sites/declarative.js.map +1 -0
- package/dist/types/adapters/sites/hackernews.d.ts +13 -0
- package/dist/types/adapters/sites/hackernews.js.map +1 -0
- package/dist/types/adapters/sites/youtube.d.ts +31 -0
- package/dist/types/adapters/sites/youtube.js.map +1 -0
- package/dist/types/adapters/types.d.ts +99 -0
- package/dist/types/adapters/types.js.map +1 -0
- package/dist/types/cli/bin.js.map +1 -1
- package/dist/types/cli/commands/jobs/files.d.ts +28 -0
- package/dist/types/cli/commands/jobs/files.js.map +1 -0
- package/dist/types/cli/commands/jobs/llms-txt.d.ts +33 -0
- package/dist/types/cli/commands/jobs/llms-txt.js.map +1 -0
- package/dist/types/cli/commands/jobs/options.d.ts +58 -0
- package/dist/types/cli/commands/jobs/options.js.map +1 -0
- package/dist/types/cli/commands/jobs/run.d.ts +44 -0
- package/dist/types/cli/commands/jobs/run.js.map +1 -0
- package/dist/types/cli/commands/skill/content.d.ts +9 -0
- package/dist/types/cli/commands/skill/content.js.map +1 -0
- package/dist/types/cli/commands/skill/index.d.ts +19 -0
- package/dist/types/cli/commands/skill/index.js.map +1 -0
- package/dist/types/cli/commands/webforai/interactive.d.ts +7 -0
- package/dist/types/cli/commands/webforai/interactive.js.map +1 -0
- package/dist/types/cli/commands/webforai/loadHtml.js.map +1 -1
- package/dist/types/cli/commands/webforai/options.d.ts +62 -0
- package/dist/types/cli/commands/webforai/options.js.map +1 -0
- package/dist/types/cli/commands/webforai/run.d.ts +10 -0
- package/dist/types/cli/commands/webforai/run.js.map +1 -0
- package/dist/types/cli/constants.d.ts +14 -4
- package/dist/types/cli/constants.js.map +1 -1
- package/dist/types/cli/helpers/selectExtractMode.js.map +1 -1
- package/dist/types/cli/helpers/selectLoader.js.map +1 -1
- package/dist/types/cli/utils.d.ts +8 -1
- package/dist/types/cli/utils.js.map +1 -1
- package/dist/types/constants.d.ts +7 -1
- package/dist/types/constants.js.map +1 -1
- package/dist/types/detect-client-shell.d.ts +24 -0
- package/dist/types/detect-client-shell.js.map +1 -0
- package/dist/types/extract-meta-refresh.d.ts +29 -0
- package/dist/types/extract-meta-refresh.js.map +1 -0
- package/dist/types/extractors/index.d.ts +1 -0
- package/dist/types/extractors/index.js.map +1 -1
- package/dist/types/extractors/lib/constants.d.ts +81 -0
- package/dist/types/extractors/lib/constants.js.map +1 -0
- package/dist/types/extractors/lib/sanitize.d.ts +66 -0
- package/dist/types/extractors/lib/sanitize.js.map +1 -0
- package/dist/types/extractors/lib/score.d.ts +85 -0
- package/dist/types/extractors/lib/score.js.map +1 -0
- package/dist/types/extractors/lib/terminators.d.ts +26 -0
- package/dist/types/extractors/lib/terminators.js.map +1 -0
- package/dist/types/extractors/lib/ui-chrome.d.ts +18 -0
- package/dist/types/extractors/lib/ui-chrome.js.map +1 -0
- package/dist/types/extractors/pipeExtractors.d.ts +5 -0
- package/dist/types/extractors/pipeExtractors.js.map +1 -1
- package/dist/types/extractors/presets/auto.d.ts +30 -0
- package/dist/types/extractors/presets/auto.js.map +1 -0
- package/dist/types/extractors/presets/minimal-filter.d.ts +10 -2
- package/dist/types/extractors/presets/minimal-filter.js.map +1 -1
- package/dist/types/extractors/presets/takumi.d.ts +19 -3
- package/dist/types/extractors/presets/takumi.js.map +1 -1
- package/dist/types/extractors/types.d.ts +8 -0
- package/dist/types/html-to-markdown.d.ts +38 -0
- package/dist/types/html-to-markdown.js.map +1 -1
- package/dist/types/html-to-mdast.d.ts +11 -0
- package/dist/types/html-to-mdast.js.map +1 -1
- package/dist/types/index.d.ts +8 -3
- package/dist/types/index.js.map +1 -1
- package/dist/types/link-replacer.d.ts +3 -1
- package/dist/types/link-replacer.js.map +1 -1
- package/dist/types/loaders/cf-puppeteer.js.map +1 -1
- package/dist/types/loaders/fetch.d.ts +4 -0
- package/dist/types/loaders/fetch.js.map +1 -1
- package/dist/types/loaders/playwright.js.map +1 -1
- package/dist/types/loaders/puppeteer.js.map +1 -1
- package/dist/types/md-splitter.d.ts +1 -1
- package/dist/types/md-splitter.js.map +1 -1
- package/dist/types/mdast-handlers/custom-a-handler.js.map +1 -1
- package/dist/types/mdast-handlers/custom-code-handler.d.ts +2 -0
- package/dist/types/mdast-handlers/custom-code-handler.js.map +1 -1
- package/dist/types/mdast-handlers/custom-div-handler.js.map +1 -1
- package/dist/types/mdast-handlers/custom-img-handler.js.map +1 -1
- package/dist/types/mdast-handlers/custom-table-handler.js.map +1 -1
- package/dist/types/mdast-handlers/definition-list-handler.d.ts +2 -0
- package/dist/types/mdast-handlers/definition-list-handler.js.map +1 -0
- package/dist/types/mdast-handlers/math-handler.js.map +1 -1
- package/dist/types/mdast-handlers/sup-sub-handler.d.ts +10 -0
- package/dist/types/mdast-handlers/sup-sub-handler.js.map +1 -0
- package/dist/types/mdast-to-markdown.js.map +1 -1
- package/dist/types/metadata/index.d.ts +47 -0
- package/dist/types/metadata/index.js.map +1 -0
- package/dist/types/normalize/index.d.ts +53 -0
- package/dist/types/normalize/index.js.map +1 -0
- package/dist/types/platform/client.d.ts +55 -0
- package/dist/types/platform/client.js.map +1 -0
- package/dist/types/platform/error.d.ts +22 -0
- package/dist/types/platform/error.js.map +1 -0
- package/dist/types/platform/fetch.d.ts +36 -0
- package/dist/types/platform/fetch.js.map +1 -0
- package/dist/types/platform/index.d.ts +4 -0
- package/dist/types/platform/index.js.map +1 -0
- package/dist/types/platform/types.d.ts +164 -0
- package/dist/types/platform/types.js.map +1 -0
- package/dist/types/utils/hast-fast.d.ts +101 -0
- package/dist/types/utils/hast-fast.js.map +1 -0
- package/dist/utils/hast-fast.js +246 -0
- package/package.json +25 -7
- package/dist/types/cli/commands/webforai/index.d.ts +0 -6
- package/dist/types/cli/commands/webforai/index.js.map +0 -1
|
@@ -1,134 +1,103 @@
|
|
|
1
1
|
// src/extractors/presets/takumi.ts
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
var
|
|
15
|
-
var
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
var
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
if (element.tagName === "dialog") {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
if (element.properties.role === "dialog" && element.properties["aria-modal"]) {
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
if (element.properties.rel === "author" && isStrInclude(element.properties.itemprop, "author")) {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
if (REGEXPS.byline.test(matchString(element))) {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
if (element.properties.role && UNLIKELY_ROLES.includes(element.properties.role)) {
|
|
49
|
-
return false;
|
|
2
|
+
import {
|
|
3
|
+
MetricsCollector,
|
|
4
|
+
cloneHast,
|
|
5
|
+
findElement,
|
|
6
|
+
isElement,
|
|
7
|
+
pruneInPlace,
|
|
8
|
+
stringProperty
|
|
9
|
+
} from "../../utils/hast-fast.js";
|
|
10
|
+
import { minContentLength } from "../lib/constants.js";
|
|
11
|
+
import { cleanContent, findCleanupTargets, findUnlikelyElements, stripNonContent } from "../lib/sanitize.js";
|
|
12
|
+
import { buildScoringContext, climbToArticleBoundary, collectArticleNodes, scoreCandidates } from "../lib/score.js";
|
|
13
|
+
import { truncateTrailingBoilerplate } from "../lib/terminators.js";
|
|
14
|
+
var MIN_RETAINED_FRACTION = 0.25;
|
|
15
|
+
var SEMANTIC_SELECTORS = [
|
|
16
|
+
(element) => stringProperty(element, "itemprop")?.includes("articleBody") ?? false,
|
|
17
|
+
(element) => element.tagName === "article",
|
|
18
|
+
(element) => element.tagName === "main",
|
|
19
|
+
(element) => stringProperty(element, "role") === "main"
|
|
20
|
+
];
|
|
21
|
+
var findBody = (hast) => findElement(hast, (element) => element.tagName === "body") ?? hast;
|
|
22
|
+
var semanticShortcut = (tree, collector, minLength) => {
|
|
23
|
+
for (const matches of SEMANTIC_SELECTORS) {
|
|
24
|
+
const found = [];
|
|
25
|
+
collectMatching(tree, matches, found);
|
|
26
|
+
if (found.length !== 1) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const candidate = found[0];
|
|
30
|
+
const metrics = collector.metrics(candidate);
|
|
31
|
+
if (metrics.text >= minLength && collector.linkDensity(candidate) < 0.5) {
|
|
32
|
+
return candidate;
|
|
33
|
+
}
|
|
50
34
|
}
|
|
51
|
-
return
|
|
35
|
+
return void 0;
|
|
52
36
|
};
|
|
53
|
-
var
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return false;
|
|
37
|
+
var collectMatching = (tree, matches, into) => {
|
|
38
|
+
const stack = [tree];
|
|
39
|
+
while (stack.length > 0 && into.length < 2) {
|
|
40
|
+
const node = stack.pop();
|
|
41
|
+
if (isElement(node) && matches(node)) {
|
|
42
|
+
into.push(node);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if ("children" in node) {
|
|
46
|
+
for (const child of node.children) {
|
|
47
|
+
stack.push(child);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
67
50
|
}
|
|
68
|
-
return true;
|
|
69
51
|
};
|
|
70
|
-
var
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
if (
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
return true;
|
|
52
|
+
var asRootOf = (nodes) => ({ type: "root", children: nodes });
|
|
53
|
+
var scoredSelection = (tree, collector, lang) => {
|
|
54
|
+
const context = buildScoringContext(tree, collector, lang);
|
|
55
|
+
const candidates = scoreCandidates(tree, context);
|
|
56
|
+
const top = candidates[0];
|
|
57
|
+
if (!top) {
|
|
58
|
+
return void 0;
|
|
59
|
+
}
|
|
60
|
+
const promoted = climbToArticleBoundary(top, candidates, context);
|
|
61
|
+
const anchor = promoted === top.element ? top : { element: promoted, score: top.score };
|
|
62
|
+
const selected = collectArticleNodes(anchor, candidates, context);
|
|
63
|
+
return selected.length === 1 ? selected[0] : asRootOf(selected);
|
|
85
64
|
};
|
|
86
65
|
var takumiExtractor = (params) => {
|
|
87
|
-
const { hast, lang
|
|
88
|
-
const body =
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
66
|
+
const { hast, lang, owned } = params;
|
|
67
|
+
const body = owned ? findBody(hast) : cloneHast(findBody(hast));
|
|
68
|
+
stripNonContent(body);
|
|
69
|
+
let collector = new MetricsCollector();
|
|
70
|
+
const baseText = collector.textLength(body);
|
|
71
|
+
if (baseText === 0) {
|
|
92
72
|
return body;
|
|
93
73
|
}
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const finalFilteredTree = filter(extractedTree, (node) => {
|
|
122
|
-
if (!removeEmptyFilter(node, lang)) {
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
if (!unlikelyElementFilter(node)) {
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
return true;
|
|
129
|
-
});
|
|
130
|
-
const finalTree = hastToString(finalFilteredTree).length > extractedText.length / 3 ? finalFilteredTree : extractedTree;
|
|
131
|
-
return finalTree;
|
|
74
|
+
const minLength = Math.min(minContentLength(lang), Math.max(0, baseText - 200));
|
|
75
|
+
const shortcut = semanticShortcut(body, collector, minLength);
|
|
76
|
+
const searchRoot = shortcut ?? body;
|
|
77
|
+
const searchText = collector.textLength(searchRoot);
|
|
78
|
+
const doomed = findUnlikelyElements(searchRoot);
|
|
79
|
+
const doomedText = doomed.reduce((sum, element) => sum + collector.metrics(element).text, 0);
|
|
80
|
+
if (searchText - doomedText > Math.max(minLength, searchText * MIN_RETAINED_FRACTION)) {
|
|
81
|
+
const doomedSet = new Set(doomed);
|
|
82
|
+
pruneInPlace(searchRoot, (node) => !(isElement(node) && doomedSet.has(node)));
|
|
83
|
+
collector = new MetricsCollector();
|
|
84
|
+
}
|
|
85
|
+
const scoringRoot = searchRoot;
|
|
86
|
+
const scoringRootText = collector.textLength(scoringRoot);
|
|
87
|
+
const selection = shortcut ? scoringRoot : scoredSelection(scoringRoot, collector, lang) ?? scoringRoot;
|
|
88
|
+
const selectionText = collector.textLength(selection);
|
|
89
|
+
const selectionIsSound = selectionText >= minLength || selectionText > scoringRootText * MIN_RETAINED_FRACTION;
|
|
90
|
+
const chosen = selectionIsSound ? selection : scoringRoot;
|
|
91
|
+
truncateTrailingBoilerplate(chosen);
|
|
92
|
+
const cleanupCollector = new MetricsCollector();
|
|
93
|
+
const chosenText = cleanupCollector.textLength(chosen);
|
|
94
|
+
const widgets = findCleanupTargets(chosen, cleanupCollector);
|
|
95
|
+
const widgetText = widgets.reduce((sum, element) => sum + cleanupCollector.metrics(element).text, 0);
|
|
96
|
+
const remainingText = chosenText - widgetText;
|
|
97
|
+
if (remainingText < chosenText * MIN_RETAINED_FRACTION && remainingText < minLength) {
|
|
98
|
+
return chosen;
|
|
99
|
+
}
|
|
100
|
+
return cleanContent(chosen, cleanupCollector);
|
|
132
101
|
};
|
|
133
102
|
export {
|
|
134
103
|
takumiExtractor
|
package/dist/html-to-markdown.js
CHANGED
|
@@ -1,12 +1,48 @@
|
|
|
1
1
|
// src/html-to-markdown.ts
|
|
2
|
+
import { fromHtml } from "hast-util-from-html";
|
|
2
3
|
import { htmlToMdast } from "./html-to-mdast.js";
|
|
3
4
|
import { mdastToMarkdown } from "./mdast-to-markdown.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
5
|
+
import { extractMetadata, toFrontmatter } from "./metadata/index.js";
|
|
6
|
+
import { getLangFromStr } from "./utils/hast-utils.js";
|
|
7
|
+
var htmlToMarkdownWithMetadata = (htmlOrHast, options) => {
|
|
8
|
+
const { baseUrl, frontmatter, title, formatting: toMarkdownOptions, ...toMdastOptions } = options || {};
|
|
9
|
+
const hast = typeof htmlOrHast === "string" ? fromHtml(htmlOrHast, { fragment: true }) : htmlOrHast;
|
|
10
|
+
const isOwned = typeof htmlOrHast === "string";
|
|
11
|
+
const declaredLang = typeof htmlOrHast === "string" ? getLangFromStr(htmlOrHast) : void 0;
|
|
12
|
+
const metadata = extractMetadata(hast);
|
|
13
|
+
if (declaredLang && !metadata.lang) {
|
|
14
|
+
metadata.lang = declaredLang;
|
|
15
|
+
}
|
|
16
|
+
const mdast = htmlToMdast(hast, {
|
|
17
|
+
...toMdastOptions,
|
|
18
|
+
owned: isOwned,
|
|
19
|
+
lang: toMdastOptions.lang ?? metadata.lang,
|
|
20
|
+
url: toMdastOptions.url ?? metadata.canonicalUrl
|
|
21
|
+
});
|
|
22
|
+
const body = mdastToMarkdown(mdast, { baseUrl, ...toMarkdownOptions });
|
|
23
|
+
const titled = title === false ? body : withTitle(body, metadata.title);
|
|
24
|
+
const markdown = frontmatter ? `${toFrontmatter(metadata)}${titled}` : titled;
|
|
25
|
+
return { markdown, metadata };
|
|
9
26
|
};
|
|
27
|
+
var TITLE_LOOKAHEAD = 400;
|
|
28
|
+
var withTitle = (body, title) => {
|
|
29
|
+
if (!title) {
|
|
30
|
+
return body;
|
|
31
|
+
}
|
|
32
|
+
const opening = body.slice(0, TITLE_LOOKAHEAD);
|
|
33
|
+
if (/^#\s+\S/m.test(opening)) {
|
|
34
|
+
return body;
|
|
35
|
+
}
|
|
36
|
+
if (normalizeForCompare(opening).includes(normalizeForCompare(title))) {
|
|
37
|
+
return body;
|
|
38
|
+
}
|
|
39
|
+
return `# ${title}
|
|
40
|
+
|
|
41
|
+
${body}`;
|
|
42
|
+
};
|
|
43
|
+
var normalizeForCompare = (value) => value.replace(/\s+/g, " ").trim().toLowerCase();
|
|
44
|
+
var htmlToMarkdown = (htmlOrHast, options) => htmlToMarkdownWithMetadata(htmlOrHast, options).markdown;
|
|
10
45
|
export {
|
|
11
|
-
htmlToMarkdown
|
|
46
|
+
htmlToMarkdown,
|
|
47
|
+
htmlToMarkdownWithMetadata
|
|
12
48
|
};
|
package/dist/html-to-mdast.js
CHANGED
|
@@ -8,18 +8,27 @@ import { customCodeHandler } from "./mdast-handlers/custom-code-handler.js";
|
|
|
8
8
|
import { customDivHandler } from "./mdast-handlers/custom-div-handler.js";
|
|
9
9
|
import { customImgHandler } from "./mdast-handlers/custom-img-handler.js";
|
|
10
10
|
import { customTableHandler } from "./mdast-handlers/custom-table-handler.js";
|
|
11
|
+
import { definitionListHandler } from "./mdast-handlers/definition-list-handler.js";
|
|
11
12
|
import { mathHandler } from "./mdast-handlers/math-handler.js";
|
|
13
|
+
import { supSubHandler } from "./mdast-handlers/sup-sub-handler.js";
|
|
14
|
+
import { normalizeHast } from "./normalize/index.js";
|
|
15
|
+
import { cloneHast } from "./utils/hast-fast.js";
|
|
12
16
|
import { getLangFromHast, getLangFromStr, getUrlFromHast } from "./utils/hast-utils.js";
|
|
13
17
|
var htmlToMdast = (htmlOrHast, options) => {
|
|
14
18
|
const { extractors, url: defaultUrl, lang: defaultLang } = options || {};
|
|
15
19
|
const [lang, hast] = typeof htmlOrHast === "string" ? [defaultLang || getLangFromStr(htmlOrHast), fromHtml(htmlOrHast, { fragment: true })] : [defaultLang || getLangFromHast(htmlOrHast), htmlOrHast];
|
|
20
|
+
const isOwned = typeof htmlOrHast === "string" || (options?.owned ?? false);
|
|
16
21
|
const url = defaultUrl || getUrlFromHast(hast);
|
|
17
|
-
const extractedHast = pipeExtractors({ hast, lang, url }, extractors);
|
|
22
|
+
const extractedHast = pipeExtractors({ hast: isOwned ? hast : cloneHast(hast), lang, url, owned: true }, extractors);
|
|
23
|
+
normalizeHast(extractedHast, options?.normalize);
|
|
18
24
|
const mdast = toMdast(extractedHast, {
|
|
19
25
|
handlers: {
|
|
20
26
|
math: mathHandler,
|
|
21
27
|
div: customDivHandler,
|
|
22
28
|
pre: customCodeHandler,
|
|
29
|
+
dl: definitionListHandler,
|
|
30
|
+
sup: supSubHandler("sup"),
|
|
31
|
+
sub: supSubHandler("sub"),
|
|
23
32
|
a: customAHandler({ asText: options?.linkAsText }),
|
|
24
33
|
img: customImgHandler({ hideImage: options?.hideImage }),
|
|
25
34
|
table: customTableHandler({ asText: options?.tableAsText })
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,62 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
htmlToMarkdown,
|
|
4
|
+
htmlToMarkdownWithMetadata
|
|
5
|
+
} from "./html-to-markdown.js";
|
|
3
6
|
import { mdastSplitter } from "./md-splitter.js";
|
|
4
7
|
import { htmlToMdast } from "./html-to-mdast.js";
|
|
5
8
|
import { mdastToMarkdown } from "./mdast-to-markdown.js";
|
|
6
9
|
import {
|
|
7
10
|
pipeExtractors,
|
|
11
|
+
autoExtractor,
|
|
12
|
+
createAutoExtractor,
|
|
8
13
|
takumiExtractor,
|
|
9
14
|
minimalFilter
|
|
10
15
|
} from "./extractors/index.js";
|
|
16
|
+
import {
|
|
17
|
+
BUILTIN_ADAPTERS,
|
|
18
|
+
adapterById,
|
|
19
|
+
buildPageSignature,
|
|
20
|
+
defineSelectorAdapter,
|
|
21
|
+
resolveAdapter,
|
|
22
|
+
runAdapters,
|
|
23
|
+
youtubeCaptionTracks
|
|
24
|
+
} from "./adapters/index.js";
|
|
25
|
+
import {
|
|
26
|
+
detectClientShell,
|
|
27
|
+
SHELL_EMPTY_THRESHOLD,
|
|
28
|
+
SHELL_SUSPICION_THRESHOLD
|
|
29
|
+
} from "./detect-client-shell.js";
|
|
30
|
+
import {
|
|
31
|
+
extractMetaRefresh,
|
|
32
|
+
MAX_META_REFRESH_DELAY_SECONDS
|
|
33
|
+
} from "./extract-meta-refresh.js";
|
|
34
|
+
import { extractMetadata, toFrontmatter } from "./metadata/index.js";
|
|
35
|
+
import { normalizeHast } from "./normalize/index.js";
|
|
11
36
|
export {
|
|
37
|
+
BUILTIN_ADAPTERS,
|
|
38
|
+
MAX_META_REFRESH_DELAY_SECONDS,
|
|
39
|
+
SHELL_EMPTY_THRESHOLD,
|
|
40
|
+
SHELL_SUSPICION_THRESHOLD,
|
|
41
|
+
adapterById,
|
|
42
|
+
autoExtractor,
|
|
43
|
+
buildPageSignature,
|
|
44
|
+
createAutoExtractor,
|
|
45
|
+
defineSelectorAdapter,
|
|
46
|
+
detectClientShell,
|
|
47
|
+
extractMetaRefresh,
|
|
48
|
+
extractMetadata,
|
|
12
49
|
htmlToMarkdown,
|
|
50
|
+
htmlToMarkdownWithMetadata,
|
|
13
51
|
htmlToMdast,
|
|
14
52
|
mdastSplitter,
|
|
15
53
|
mdastToMarkdown,
|
|
16
54
|
minimalFilter,
|
|
55
|
+
normalizeHast,
|
|
17
56
|
pipeExtractors,
|
|
18
|
-
|
|
57
|
+
resolveAdapter,
|
|
58
|
+
runAdapters,
|
|
59
|
+
takumiExtractor,
|
|
60
|
+
toFrontmatter,
|
|
61
|
+
youtubeCaptionTracks
|
|
19
62
|
};
|
package/dist/link-replacer.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
// src/link-replacer.ts
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
2
|
+
var absoluteUrl = (url, base) => {
|
|
3
|
+
if (/^(?:[a-z][a-z\d+.-]*:|#)/i.test(url)) {
|
|
4
|
+
return url;
|
|
5
|
+
}
|
|
6
|
+
try {
|
|
7
|
+
return new URL(url, base).href;
|
|
8
|
+
} catch {
|
|
9
|
+
return url;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var linkReplacer = (node, base) => {
|
|
13
|
+
let result = node;
|
|
14
|
+
if (node.type === "link" || node.type === "image" || node.type === "definition") {
|
|
15
|
+
result = { ...node, url: absoluteUrl(node.url, base) };
|
|
16
|
+
}
|
|
17
|
+
if ("children" in result) {
|
|
18
|
+
return { ...result, children: result.children.map((child) => linkReplacer(child, base)) };
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
15
21
|
};
|
|
16
22
|
export {
|
|
17
23
|
linkReplacer
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
// src/loaders/cf-puppeteer.ts
|
|
2
|
-
import puppeteer from "@cloudflare/puppeteer";
|
|
3
|
-
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
2
|
+
import puppeteer, { TimeoutError } from "@cloudflare/puppeteer";
|
|
4
3
|
var loadHtml = async (url, ctx) => {
|
|
5
4
|
const browser = await puppeteer.launch(ctx);
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
try {
|
|
6
|
+
const page = await browser.newPage();
|
|
7
|
+
await page.goto(url);
|
|
8
|
+
try {
|
|
9
|
+
await page.waitForNetworkIdle({ timeout: 1e4 });
|
|
10
|
+
} catch (error) {
|
|
11
|
+
if (!(error instanceof TimeoutError)) {
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return await page.content();
|
|
16
|
+
} finally {
|
|
17
|
+
await browser.close();
|
|
18
|
+
}
|
|
12
19
|
};
|
|
13
20
|
export {
|
|
14
21
|
loadHtml
|
package/dist/loaders/fetch.js
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
// src/loaders/fetch.ts
|
|
2
|
+
import { extractMetaRefresh } from "../extract-meta-refresh.js";
|
|
2
3
|
var USER_AGENT = "mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/125.0.0.0 safari/537.36";
|
|
4
|
+
var MAX_META_REFRESH_HOPS = 3;
|
|
3
5
|
var loadHtml = async (url, userAgent = USER_AGENT) => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
let current = url;
|
|
7
|
+
let html = "";
|
|
8
|
+
for (let hop = 0; hop <= MAX_META_REFRESH_HOPS; hop++) {
|
|
9
|
+
const response = await fetch(current, { headers: { "User-Agent": userAgent } });
|
|
10
|
+
html = await response.text();
|
|
11
|
+
const refresh = extractMetaRefresh(html, response.url || current);
|
|
12
|
+
if (!refresh) {
|
|
13
|
+
break;
|
|
14
|
+
}
|
|
15
|
+
current = refresh.url;
|
|
16
|
+
}
|
|
17
|
+
return html;
|
|
6
18
|
};
|
|
7
19
|
export {
|
|
8
20
|
USER_AGENT,
|
|
@@ -13,40 +13,42 @@ var SUPER_BYPASS_DEVICE = {
|
|
|
13
13
|
var loadHtml = async (url, options) => {
|
|
14
14
|
const { browser, waitUntil, timeout, superBypassMode } = options ?? {};
|
|
15
15
|
const _browser = browser ?? await chromium.launch({ headless: true });
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
await
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
await
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
let context;
|
|
17
|
+
try {
|
|
18
|
+
context = await _browser.newContext(superBypassMode ? SUPER_BYPASS_DEVICE : devices["Desktop Chrome"]);
|
|
19
|
+
if (superBypassMode) {
|
|
20
|
+
await context.addInitScript(() => {
|
|
21
|
+
Object.defineProperty(navigator, "webdriver", { get: () => void 0 });
|
|
22
|
+
Object.defineProperty(navigator, "languages", { get: () => ["en-US", "en"] });
|
|
23
|
+
Object.defineProperty(navigator, "plugins", { get: () => [1, 2, 3, 4, 5] });
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
const page = await context.newPage();
|
|
27
|
+
if (superBypassMode) {
|
|
28
|
+
await page.route("**/*.js", (route) => {
|
|
29
|
+
if (route.request().url().includes("captcha-delivery")) {
|
|
30
|
+
return route.abort();
|
|
31
|
+
}
|
|
32
|
+
return route.continue();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
await page.goto(url, { waitUntil: waitUntil ?? "load", timeout });
|
|
36
|
+
await page.evaluate(() => {
|
|
37
|
+
const elements = document.querySelectorAll("*");
|
|
38
|
+
for (const element of elements) {
|
|
39
|
+
const rect = element.getBoundingClientRect();
|
|
40
|
+
element.setAttribute("data-rwidth", rect.width.toString());
|
|
41
|
+
element.setAttribute("data-rheight", rect.height.toString());
|
|
29
42
|
}
|
|
30
|
-
return route.continue();
|
|
31
43
|
});
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
element.setAttribute("data-rwidth", rect.width.toString());
|
|
39
|
-
element.setAttribute("data-rheight", rect.height.toString());
|
|
44
|
+
return await page.content();
|
|
45
|
+
} finally {
|
|
46
|
+
if (browser) {
|
|
47
|
+
await context?.close();
|
|
48
|
+
} else {
|
|
49
|
+
await _browser.close();
|
|
40
50
|
}
|
|
41
|
-
});
|
|
42
|
-
const html = await page.content();
|
|
43
|
-
await page.close();
|
|
44
|
-
if (browser) {
|
|
45
|
-
await context.close();
|
|
46
|
-
} else {
|
|
47
|
-
await _browser.close();
|
|
48
51
|
}
|
|
49
|
-
return html;
|
|
50
52
|
};
|
|
51
53
|
export {
|
|
52
54
|
loadHtml
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// src/loaders/puppeteer.ts
|
|
2
|
-
import puppeteer from "puppeteer";
|
|
3
|
-
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
2
|
+
import puppeteer, { TimeoutError } from "puppeteer";
|
|
4
3
|
var loadHtml = async (url, ctx) => {
|
|
5
4
|
const browser = await puppeteer.launch(
|
|
6
5
|
ctx || {
|
|
@@ -8,12 +7,20 @@ var loadHtml = async (url, ctx) => {
|
|
|
8
7
|
args: ["--no-sandbox", "--disable-setuid-sandbox"]
|
|
9
8
|
}
|
|
10
9
|
);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
try {
|
|
11
|
+
const page = await browser.newPage();
|
|
12
|
+
await page.goto(url);
|
|
13
|
+
try {
|
|
14
|
+
await page.waitForNetworkIdle({ timeout: 1e4 });
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (!(error instanceof TimeoutError)) {
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return await page.content();
|
|
21
|
+
} finally {
|
|
22
|
+
await browser.close();
|
|
23
|
+
}
|
|
17
24
|
};
|
|
18
25
|
export {
|
|
19
26
|
loadHtml
|
package/dist/md-splitter.js
CHANGED
|
@@ -3,15 +3,13 @@ import { mdastToMarkdown } from "./mdast-to-markdown.js";
|
|
|
3
3
|
import { chunk } from "./utils/common.js";
|
|
4
4
|
import { internalType, unwarpRoot, warpRoot } from "./utils/mdast-utils.js";
|
|
5
5
|
var PRIORITY_SPLITTERS = ["h1", "h2", "h3", "h4", "h5", "h6", "list", "table", "code"];
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
};
|
|
11
|
-
var _mdastSplitter = async (contents, checker, splitterGenerator) => {
|
|
12
|
-
const splitter = splitterGenerator.next().value;
|
|
6
|
+
var _mdastSplitter = async (contents, checker, priority, signal) => {
|
|
7
|
+
signal?.throwIfAborted();
|
|
8
|
+
const splitter = PRIORITY_SPLITTERS[priority];
|
|
13
9
|
const markdown = mdastToMarkdown(warpRoot(contents));
|
|
14
|
-
|
|
10
|
+
const accepted = await checker(markdown);
|
|
11
|
+
signal?.throwIfAborted();
|
|
12
|
+
if (accepted || contents.length === 1) {
|
|
15
13
|
return [contents];
|
|
16
14
|
}
|
|
17
15
|
const chunked = splitter ? contents.reduce((acc, content) => {
|
|
@@ -22,11 +20,26 @@ var _mdastSplitter = async (contents, checker, splitterGenerator) => {
|
|
|
22
20
|
acc[acc.length - 1].push(content);
|
|
23
21
|
return acc;
|
|
24
22
|
}, []) : chunk(contents, Math.ceil(contents.length / 2));
|
|
25
|
-
const splitting = chunked.map((chunk2) => _mdastSplitter(chunk2, checker,
|
|
23
|
+
const splitting = chunked.map((chunk2) => _mdastSplitter(chunk2, checker, priority + 1, signal));
|
|
26
24
|
return Promise.all(splitting).then((chunks) => chunks.flat());
|
|
27
25
|
};
|
|
28
|
-
var mdastSplitter = (mdast, checker,
|
|
29
|
-
|
|
26
|
+
var mdastSplitter = async (mdast, checker, options) => {
|
|
27
|
+
const signal = options?.signal;
|
|
28
|
+
if (!signal) {
|
|
29
|
+
return _mdastSplitter(unwarpRoot(mdast), checker, 0);
|
|
30
|
+
}
|
|
31
|
+
signal.throwIfAborted();
|
|
32
|
+
let onAbort = () => {
|
|
33
|
+
};
|
|
34
|
+
const aborted = new Promise((_, reject) => {
|
|
35
|
+
onAbort = () => reject(signal.reason);
|
|
36
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
37
|
+
});
|
|
38
|
+
try {
|
|
39
|
+
return await Promise.race([_mdastSplitter(unwarpRoot(mdast), checker, 0, signal), aborted]);
|
|
40
|
+
} finally {
|
|
41
|
+
signal.removeEventListener("abort", onAbort);
|
|
42
|
+
}
|
|
30
43
|
};
|
|
31
44
|
export {
|
|
32
45
|
mdastSplitter
|