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.
Files changed (203) hide show
  1. package/README.md +86 -4
  2. package/dist/adapters/index.js +58 -0
  3. package/dist/adapters/registry.js +149 -0
  4. package/dist/adapters/sites/declarative.js +178 -0
  5. package/dist/adapters/sites/hackernews.js +94 -0
  6. package/dist/adapters/sites/youtube.js +165 -0
  7. package/dist/adapters/types.js +0 -0
  8. package/dist/bin.js +4142 -549
  9. package/dist/cjs/adapters/index.js +86 -0
  10. package/dist/cjs/adapters/registry.js +178 -0
  11. package/dist/cjs/adapters/sites/declarative.js +202 -0
  12. package/dist/cjs/adapters/sites/hackernews.js +117 -0
  13. package/dist/cjs/adapters/sites/youtube.js +191 -0
  14. package/dist/cjs/adapters/types.js +16 -0
  15. package/dist/cjs/constants.js +2 -2
  16. package/dist/cjs/detect-client-shell.js +78 -0
  17. package/dist/cjs/extract-meta-refresh.js +71 -0
  18. package/dist/cjs/extractors/index.js +5 -0
  19. package/dist/cjs/extractors/lib/constants.js +186 -0
  20. package/dist/cjs/extractors/lib/sanitize.js +429 -0
  21. package/dist/cjs/extractors/lib/score.js +264 -0
  22. package/dist/cjs/extractors/lib/terminators.js +131 -0
  23. package/dist/cjs/extractors/lib/ui-chrome.js +119 -0
  24. package/dist/cjs/extractors/pipeExtractors.js +12 -9
  25. package/dist/cjs/extractors/presets/auto.js +44 -0
  26. package/dist/cjs/extractors/presets/minimal-filter.js +27 -48
  27. package/dist/cjs/extractors/presets/takumi.js +84 -122
  28. package/dist/cjs/html-to-markdown.js +44 -7
  29. package/dist/cjs/html-to-mdast.js +10 -1
  30. package/dist/cjs/index.js +43 -2
  31. package/dist/cjs/link-replacer.js +19 -13
  32. package/dist/cjs/loaders/cf-puppeteer.js +14 -7
  33. package/dist/cjs/loaders/fetch.js +14 -2
  34. package/dist/cjs/loaders/playwright.js +32 -30
  35. package/dist/cjs/loaders/puppeteer.js +14 -7
  36. package/dist/cjs/md-splitter.js +24 -11
  37. package/dist/cjs/mdast-handlers/custom-a-handler.js +25 -9
  38. package/dist/cjs/mdast-handlers/custom-code-handler.js +18 -7
  39. package/dist/cjs/mdast-handlers/custom-div-handler.js +12 -33
  40. package/dist/cjs/mdast-handlers/custom-img-handler.js +23 -1
  41. package/dist/cjs/mdast-handlers/custom-table-handler.js +16 -0
  42. package/dist/cjs/mdast-handlers/definition-list-handler.js +110 -0
  43. package/dist/cjs/mdast-handlers/math-handler.js +12 -3
  44. package/dist/cjs/mdast-handlers/sup-sub-handler.js +39 -0
  45. package/dist/cjs/mdast-to-markdown.js +2 -5
  46. package/dist/cjs/metadata/index.js +223 -0
  47. package/dist/cjs/normalize/index.js +177 -0
  48. package/dist/cjs/platform/client.js +204 -0
  49. package/dist/cjs/platform/error.js +36 -0
  50. package/dist/cjs/platform/fetch.js +37 -0
  51. package/dist/cjs/platform/index.js +44 -0
  52. package/dist/cjs/platform/types.js +40 -0
  53. package/dist/cjs/utils/hast-fast.js +284 -0
  54. package/dist/constants.js +2 -2
  55. package/dist/detect-client-shell.js +53 -0
  56. package/dist/extract-meta-refresh.js +47 -0
  57. package/dist/extractors/index.js +3 -0
  58. package/dist/extractors/lib/constants.js +150 -0
  59. package/dist/extractors/lib/sanitize.js +419 -0
  60. package/dist/extractors/lib/score.js +242 -0
  61. package/dist/extractors/lib/terminators.js +107 -0
  62. package/dist/extractors/lib/ui-chrome.js +96 -0
  63. package/dist/extractors/pipeExtractors.js +12 -9
  64. package/dist/extractors/presets/auto.js +20 -0
  65. package/dist/extractors/presets/minimal-filter.js +35 -47
  66. package/dist/extractors/presets/takumi.js +91 -122
  67. package/dist/html-to-markdown.js +42 -6
  68. package/dist/html-to-mdast.js +10 -1
  69. package/dist/index.js +45 -2
  70. package/dist/link-replacer.js +19 -13
  71. package/dist/loaders/cf-puppeteer.js +15 -8
  72. package/dist/loaders/fetch.js +14 -2
  73. package/dist/loaders/playwright.js +32 -30
  74. package/dist/loaders/puppeteer.js +15 -8
  75. package/dist/md-splitter.js +24 -11
  76. package/dist/mdast-handlers/custom-a-handler.js +25 -9
  77. package/dist/mdast-handlers/custom-code-handler.js +17 -7
  78. package/dist/mdast-handlers/custom-div-handler.js +12 -33
  79. package/dist/mdast-handlers/custom-img-handler.js +23 -1
  80. package/dist/mdast-handlers/custom-table-handler.js +16 -0
  81. package/dist/mdast-handlers/definition-list-handler.js +87 -0
  82. package/dist/mdast-handlers/math-handler.js +12 -3
  83. package/dist/mdast-handlers/sup-sub-handler.js +16 -0
  84. package/dist/mdast-to-markdown.js +2 -5
  85. package/dist/metadata/index.js +198 -0
  86. package/dist/normalize/index.js +159 -0
  87. package/dist/platform/client.js +182 -0
  88. package/dist/platform/error.js +13 -0
  89. package/dist/platform/fetch.js +14 -0
  90. package/dist/platform/index.js +23 -0
  91. package/dist/platform/types.js +13 -0
  92. package/dist/types/adapters/index.d.ts +24 -0
  93. package/dist/types/adapters/index.js.map +1 -0
  94. package/dist/types/adapters/registry.d.ts +35 -0
  95. package/dist/types/adapters/registry.js.map +1 -0
  96. package/dist/types/adapters/sites/declarative.d.ts +13 -0
  97. package/dist/types/adapters/sites/declarative.js.map +1 -0
  98. package/dist/types/adapters/sites/hackernews.d.ts +13 -0
  99. package/dist/types/adapters/sites/hackernews.js.map +1 -0
  100. package/dist/types/adapters/sites/youtube.d.ts +31 -0
  101. package/dist/types/adapters/sites/youtube.js.map +1 -0
  102. package/dist/types/adapters/types.d.ts +99 -0
  103. package/dist/types/adapters/types.js.map +1 -0
  104. package/dist/types/cli/bin.js.map +1 -1
  105. package/dist/types/cli/commands/jobs/files.d.ts +28 -0
  106. package/dist/types/cli/commands/jobs/files.js.map +1 -0
  107. package/dist/types/cli/commands/jobs/llms-txt.d.ts +33 -0
  108. package/dist/types/cli/commands/jobs/llms-txt.js.map +1 -0
  109. package/dist/types/cli/commands/jobs/options.d.ts +58 -0
  110. package/dist/types/cli/commands/jobs/options.js.map +1 -0
  111. package/dist/types/cli/commands/jobs/run.d.ts +44 -0
  112. package/dist/types/cli/commands/jobs/run.js.map +1 -0
  113. package/dist/types/cli/commands/skill/content.d.ts +9 -0
  114. package/dist/types/cli/commands/skill/content.js.map +1 -0
  115. package/dist/types/cli/commands/skill/index.d.ts +19 -0
  116. package/dist/types/cli/commands/skill/index.js.map +1 -0
  117. package/dist/types/cli/commands/webforai/interactive.d.ts +7 -0
  118. package/dist/types/cli/commands/webforai/interactive.js.map +1 -0
  119. package/dist/types/cli/commands/webforai/loadHtml.js.map +1 -1
  120. package/dist/types/cli/commands/webforai/options.d.ts +62 -0
  121. package/dist/types/cli/commands/webforai/options.js.map +1 -0
  122. package/dist/types/cli/commands/webforai/run.d.ts +10 -0
  123. package/dist/types/cli/commands/webforai/run.js.map +1 -0
  124. package/dist/types/cli/constants.d.ts +14 -4
  125. package/dist/types/cli/constants.js.map +1 -1
  126. package/dist/types/cli/helpers/selectExtractMode.js.map +1 -1
  127. package/dist/types/cli/helpers/selectLoader.js.map +1 -1
  128. package/dist/types/cli/utils.d.ts +8 -1
  129. package/dist/types/cli/utils.js.map +1 -1
  130. package/dist/types/constants.d.ts +7 -1
  131. package/dist/types/constants.js.map +1 -1
  132. package/dist/types/detect-client-shell.d.ts +24 -0
  133. package/dist/types/detect-client-shell.js.map +1 -0
  134. package/dist/types/extract-meta-refresh.d.ts +29 -0
  135. package/dist/types/extract-meta-refresh.js.map +1 -0
  136. package/dist/types/extractors/index.d.ts +1 -0
  137. package/dist/types/extractors/index.js.map +1 -1
  138. package/dist/types/extractors/lib/constants.d.ts +81 -0
  139. package/dist/types/extractors/lib/constants.js.map +1 -0
  140. package/dist/types/extractors/lib/sanitize.d.ts +66 -0
  141. package/dist/types/extractors/lib/sanitize.js.map +1 -0
  142. package/dist/types/extractors/lib/score.d.ts +85 -0
  143. package/dist/types/extractors/lib/score.js.map +1 -0
  144. package/dist/types/extractors/lib/terminators.d.ts +26 -0
  145. package/dist/types/extractors/lib/terminators.js.map +1 -0
  146. package/dist/types/extractors/lib/ui-chrome.d.ts +18 -0
  147. package/dist/types/extractors/lib/ui-chrome.js.map +1 -0
  148. package/dist/types/extractors/pipeExtractors.d.ts +5 -0
  149. package/dist/types/extractors/pipeExtractors.js.map +1 -1
  150. package/dist/types/extractors/presets/auto.d.ts +30 -0
  151. package/dist/types/extractors/presets/auto.js.map +1 -0
  152. package/dist/types/extractors/presets/minimal-filter.d.ts +10 -2
  153. package/dist/types/extractors/presets/minimal-filter.js.map +1 -1
  154. package/dist/types/extractors/presets/takumi.d.ts +19 -3
  155. package/dist/types/extractors/presets/takumi.js.map +1 -1
  156. package/dist/types/extractors/types.d.ts +8 -0
  157. package/dist/types/html-to-markdown.d.ts +38 -0
  158. package/dist/types/html-to-markdown.js.map +1 -1
  159. package/dist/types/html-to-mdast.d.ts +11 -0
  160. package/dist/types/html-to-mdast.js.map +1 -1
  161. package/dist/types/index.d.ts +8 -3
  162. package/dist/types/index.js.map +1 -1
  163. package/dist/types/link-replacer.d.ts +3 -1
  164. package/dist/types/link-replacer.js.map +1 -1
  165. package/dist/types/loaders/cf-puppeteer.js.map +1 -1
  166. package/dist/types/loaders/fetch.d.ts +4 -0
  167. package/dist/types/loaders/fetch.js.map +1 -1
  168. package/dist/types/loaders/playwright.js.map +1 -1
  169. package/dist/types/loaders/puppeteer.js.map +1 -1
  170. package/dist/types/md-splitter.d.ts +1 -1
  171. package/dist/types/md-splitter.js.map +1 -1
  172. package/dist/types/mdast-handlers/custom-a-handler.js.map +1 -1
  173. package/dist/types/mdast-handlers/custom-code-handler.d.ts +2 -0
  174. package/dist/types/mdast-handlers/custom-code-handler.js.map +1 -1
  175. package/dist/types/mdast-handlers/custom-div-handler.js.map +1 -1
  176. package/dist/types/mdast-handlers/custom-img-handler.js.map +1 -1
  177. package/dist/types/mdast-handlers/custom-table-handler.js.map +1 -1
  178. package/dist/types/mdast-handlers/definition-list-handler.d.ts +2 -0
  179. package/dist/types/mdast-handlers/definition-list-handler.js.map +1 -0
  180. package/dist/types/mdast-handlers/math-handler.js.map +1 -1
  181. package/dist/types/mdast-handlers/sup-sub-handler.d.ts +10 -0
  182. package/dist/types/mdast-handlers/sup-sub-handler.js.map +1 -0
  183. package/dist/types/mdast-to-markdown.js.map +1 -1
  184. package/dist/types/metadata/index.d.ts +47 -0
  185. package/dist/types/metadata/index.js.map +1 -0
  186. package/dist/types/normalize/index.d.ts +53 -0
  187. package/dist/types/normalize/index.js.map +1 -0
  188. package/dist/types/platform/client.d.ts +55 -0
  189. package/dist/types/platform/client.js.map +1 -0
  190. package/dist/types/platform/error.d.ts +22 -0
  191. package/dist/types/platform/error.js.map +1 -0
  192. package/dist/types/platform/fetch.d.ts +36 -0
  193. package/dist/types/platform/fetch.js.map +1 -0
  194. package/dist/types/platform/index.d.ts +4 -0
  195. package/dist/types/platform/index.js.map +1 -0
  196. package/dist/types/platform/types.d.ts +164 -0
  197. package/dist/types/platform/types.js.map +1 -0
  198. package/dist/types/utils/hast-fast.d.ts +101 -0
  199. package/dist/types/utils/hast-fast.js.map +1 -0
  200. package/dist/utils/hast-fast.js +246 -0
  201. package/package.json +25 -7
  202. package/dist/types/cli/commands/webforai/index.d.ts +0 -6
  203. package/dist/types/cli/commands/webforai/index.js.map +0 -1
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var terminators_exports = {};
20
+ __export(terminators_exports, {
21
+ TERMINATOR_TAIL_FRACTION: () => TERMINATOR_TAIL_FRACTION,
22
+ truncateTrailingBoilerplate: () => truncateTrailingBoilerplate
23
+ });
24
+ module.exports = __toCommonJS(terminators_exports);
25
+ var import_hast_fast = require("../../utils/hast-fast");
26
+ const TERMINATOR_PATTERNS = [
27
+ /^was this (page|article|document) helpful/i,
28
+ /^thanks for the feedback/i,
29
+ /^(this page was )?last (modified|updated) (on|at|:)?\s/i,
30
+ /^learn how to contribute/i,
31
+ /^help (us )?improve\b/i,
32
+ // The stranded Yes/No pair of a feedback widget whose question was removed separately.
33
+ /^yes ?no$/i,
34
+ /^(related (articles?|posts?|stories|content)|you might also like|recommended for you|more from|read next)$/i,
35
+ /^(comments?|leave a (comment|reply)|join the (conversation|discussion))$/i,
36
+ /^(この(ページ|記事)は(参考|役に立ち)ました?か)/,
37
+ /^(関連記事|あわせて読みたい|こちらもおすすめ|おすすめ記事)$/,
38
+ /^(コメントを(残す|書く)|コメント一覧)$/,
39
+ /^最終更新日?[::\s]/
40
+ ];
41
+ const TAIL_START_FRACTION = 0.8;
42
+ const MAX_TERMINATOR_BLOCK_LENGTH = 500;
43
+ const BLOCK_TAGS = /* @__PURE__ */ new Set(["p", "div", "section", "footer", "aside", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol"]);
44
+ const isTerminatorBlock = (element, start, tailStart) => {
45
+ if (!BLOCK_TAGS.has(element.tagName) || start < tailStart) {
46
+ return false;
47
+ }
48
+ const text = textOf(element).replace(/\s+/g, " ").trim();
49
+ if (text.length === 0 || text.length > MAX_TERMINATOR_BLOCK_LENGTH) {
50
+ return false;
51
+ }
52
+ return TERMINATOR_PATTERNS.some((pattern) => pattern.test(text));
53
+ };
54
+ const textOf = (node) => {
55
+ if (node.type === "text") {
56
+ return node.value;
57
+ }
58
+ if (!(0, import_hast_fast.isParent)(node)) {
59
+ return "";
60
+ }
61
+ let text = "";
62
+ for (const child of node.children) {
63
+ text += textOf(child);
64
+ }
65
+ return text;
66
+ };
67
+ const truncateTrailingBoilerplate = (tree) => {
68
+ const starts = /* @__PURE__ */ new Map();
69
+ let total = 0;
70
+ const measure = (node) => {
71
+ starts.set(node, total);
72
+ if (node.type === "text") {
73
+ total += node.value.trim().length;
74
+ return;
75
+ }
76
+ if ((0, import_hast_fast.isParent)(node)) {
77
+ for (const child of node.children) {
78
+ measure(child);
79
+ }
80
+ }
81
+ };
82
+ measure(tree);
83
+ if (total === 0) {
84
+ return false;
85
+ }
86
+ const tailStart = total * TAIL_START_FRACTION;
87
+ let cutoff = -1;
88
+ const find = (node) => {
89
+ if (cutoff !== -1 || !(0, import_hast_fast.isParent)(node)) {
90
+ return;
91
+ }
92
+ for (const child of node.children) {
93
+ if (cutoff !== -1) {
94
+ return;
95
+ }
96
+ if (!(0, import_hast_fast.isElement)(child)) {
97
+ continue;
98
+ }
99
+ const start = starts.get(child) ?? 0;
100
+ if (isTerminatorBlock(child, start, tailStart)) {
101
+ cutoff = start;
102
+ return;
103
+ }
104
+ find(child);
105
+ }
106
+ };
107
+ find(tree);
108
+ if (cutoff === -1) {
109
+ return false;
110
+ }
111
+ (0, import_hast_fast.pruneInPlace)(tree, (node) => {
112
+ if (node === tree) {
113
+ return true;
114
+ }
115
+ const start = starts.get(node);
116
+ if (start === void 0) {
117
+ return true;
118
+ }
119
+ if (start >= cutoff) {
120
+ return false;
121
+ }
122
+ return true;
123
+ });
124
+ return true;
125
+ };
126
+ const TERMINATOR_TAIL_FRACTION = TAIL_START_FRACTION;
127
+ // Annotate the CommonJS export names for ESM import in node:
128
+ 0 && (module.exports = {
129
+ TERMINATOR_TAIL_FRACTION,
130
+ truncateTrailingBoilerplate
131
+ });
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var ui_chrome_exports = {};
20
+ __export(ui_chrome_exports, {
21
+ isUiChromeText: () => isUiChromeText
22
+ });
23
+ module.exports = __toCommonJS(ui_chrome_exports);
24
+ const UI_PHRASES = /* @__PURE__ */ new Set([
25
+ // Documentation and article controls
26
+ "copy",
27
+ "copy page",
28
+ "copy link",
29
+ "copy code",
30
+ "copied",
31
+ "edit this page",
32
+ "edit on github",
33
+ "on this page",
34
+ "in this article",
35
+ "table of contents",
36
+ "contents",
37
+ "print",
38
+ "print this page",
39
+ "back to top",
40
+ "scroll to top",
41
+ "skip to content",
42
+ "skip to main content",
43
+ "permalink",
44
+ "anchor",
45
+ // Feedback widgets
46
+ "was this page helpful",
47
+ "was this helpful",
48
+ "was this article helpful",
49
+ "yes no",
50
+ "feedback",
51
+ "send feedback",
52
+ "report an issue",
53
+ "rate this page",
54
+ // Sharing and subscription
55
+ "share",
56
+ "share this",
57
+ "share this article",
58
+ "share on twitter",
59
+ "share on facebook",
60
+ "subscribe",
61
+ "sign up",
62
+ "sign in",
63
+ "log in",
64
+ "login",
65
+ "newsletter",
66
+ // Advertising and promotion
67
+ "advertisement",
68
+ "advertisements",
69
+ "sponsored",
70
+ "sponsored content",
71
+ "promoted",
72
+ // Navigation affordances
73
+ "read more",
74
+ "show more",
75
+ "show less",
76
+ "see more",
77
+ "load more",
78
+ "previous",
79
+ "next",
80
+ "previous page",
81
+ "next page",
82
+ "menu",
83
+ "close",
84
+ "search",
85
+ // Japanese equivalents
86
+ "\u76EE\u6B21",
87
+ "\u5E83\u544A",
88
+ "\u30B7\u30A7\u30A2",
89
+ "\u5171\u6709",
90
+ "\u3053\u306E\u8A18\u4E8B\u3092\u30B7\u30A7\u30A2",
91
+ "\u7D9A\u304D\u3092\u8AAD\u3080",
92
+ "\u3082\u3063\u3068\u898B\u308B",
93
+ "\u95A2\u9023\u8A18\u4E8B",
94
+ "\u30B9\u30DD\u30F3\u30B5\u30FC\u30EA\u30F3\u30AF",
95
+ "\u30DA\u30FC\u30B8\u306E\u5148\u982D\u3078",
96
+ "\u30C8\u30C3\u30D7\u3078\u623B\u308B",
97
+ "\u524D\u3078",
98
+ "\u6B21\u3078",
99
+ "\u9589\u3058\u308B",
100
+ "\u691C\u7D22",
101
+ "\u30ED\u30B0\u30A4\u30F3",
102
+ "\u4F1A\u54E1\u767B\u9332"
103
+ ]);
104
+ const MAX_PHRASE_LENGTH = 40;
105
+ const normalize = (text) => text.toLowerCase().replace(/[?!.,:;·・…"'“”‘’()[\]{}<>|/\\—–-]/g, " ").replace(/\s+/g, " ").trim();
106
+ const isUiChromeText = (text) => {
107
+ if (text.length === 0 || text.length > MAX_PHRASE_LENGTH) {
108
+ return false;
109
+ }
110
+ const normalized = normalize(text);
111
+ if (normalized.length === 0) {
112
+ return false;
113
+ }
114
+ return UI_PHRASES.has(normalized);
115
+ };
116
+ // Annotate the CommonJS export names for ESM import in node:
117
+ 0 && (module.exports = {
118
+ isUiChromeText
119
+ });
@@ -23,18 +23,21 @@ __export(pipeExtractors_exports, {
23
23
  module.exports = __toCommonJS(pipeExtractors_exports);
24
24
  var import_constants = require("../constants");
25
25
  const pipeExtractors = (params, extractors = import_constants.DEFAULT_EXTRACTORS) => {
26
- const { hast, lang } = params;
27
- const _extractors = Array.isArray(extractors) ? extractors : [extractors];
28
- const extracted = _extractors.reduce((acc, extractor) => {
26
+ const { hast, lang, url, owned } = params;
27
+ const list = Array.isArray(extractors) ? extractors : [extractors];
28
+ let current = hast;
29
+ let currentOwned = owned ?? false;
30
+ for (const extractor of list) {
29
31
  if (extractor === false) {
30
- return acc;
32
+ continue;
31
33
  }
32
- if (typeof extractor === "function") {
33
- return extractor({ hast: acc, lang });
34
+ if (typeof extractor !== "function") {
35
+ throw new Error(`Invalid extractor: ${String(extractor)}`);
34
36
  }
35
- throw new Error(`Invalid extractor: ${extractor}`);
36
- }, hast) || hast;
37
- return extracted;
37
+ current = extractor({ hast: current, lang, url, owned: currentOwned });
38
+ currentOwned = true;
39
+ }
40
+ return current;
38
41
  };
39
42
  // Annotate the CommonJS export names for ESM import in node:
40
43
  0 && (module.exports = {
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var auto_exports = {};
20
+ __export(auto_exports, {
21
+ autoExtractor: () => autoExtractor,
22
+ createAutoExtractor: () => createAutoExtractor
23
+ });
24
+ module.exports = __toCommonJS(auto_exports);
25
+ var import_adapters = require("../../adapters");
26
+ var import_takumi = require("./takumi");
27
+ const createAutoExtractor = (options = {}) => {
28
+ const { adapters } = options;
29
+ return (params) => {
30
+ if (adapters !== false) {
31
+ const extracted = (0, import_adapters.runAdapters)({ hast: params.hast, url: params.url, lang: params.lang }, adapters ?? void 0);
32
+ if (extracted) {
33
+ return extracted;
34
+ }
35
+ }
36
+ return (0, import_takumi.takumiExtractor)(params);
37
+ };
38
+ };
39
+ const autoExtractor = createAutoExtractor();
40
+ // Annotate the CommonJS export names for ESM import in node:
41
+ 0 && (module.exports = {
42
+ autoExtractor,
43
+ createAutoExtractor
44
+ });
@@ -21,65 +21,44 @@ __export(minimal_filter_exports, {
21
21
  minimalFilter: () => minimalFilter
22
22
  });
23
23
  module.exports = __toCommonJS(minimal_filter_exports);
24
- var import_hast_util_select = require("hast-util-select");
25
- var import_hast_util_to_string = require("hast-util-to-string");
26
- var import_unist_util_filter = require("unist-util-filter");
27
- var import_utils = require("./utils");
28
- const UNLIKELY_ROLES = ["menu", "menubar", "complementary", "navigation", "alert", "alertdialog", "dialog"];
29
- const REGEXPS = {
30
- hidden: /hidden|invisible|fallback-image/i,
31
- byline: /byline|author|dateline|writtenby|p-author/i,
32
- specialUnlikelyCandidates: /frb-|uls-menu|language-link/i,
33
- unlikelyCandidates: /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|tooltip|disqus|extra|footer|gdpr|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote|speechify-ignore|avatar/i,
34
- okMaybeItsaCandidate: /and|article|body|column|content|main|shadow|code/i
35
- };
36
- const metadataFilter = (node) => {
37
- return !(["comment", "doctype"].includes(node.type) || node.type === "element" && ["script", "style", "link", "meta", "noscript", "svg", "title"].includes(node.tagName));
38
- };
39
- const universalElementFilter = (node) => {
40
- if (node.type !== "element") {
24
+ var import_hast_fast = require("../../utils/hast-fast");
25
+ var import_constants = require("../lib/constants");
26
+ var import_sanitize = require("../lib/sanitize");
27
+ const MIN_RETAINED_FRACTION = 1 / 3;
28
+ const ALWAYS_ACCEPT_LENGTH = 5e3;
29
+ const isChrome = (element) => {
30
+ if (element.tagName === "aside" || element.tagName === "nav" || element.tagName === "dialog") {
41
31
  return true;
42
32
  }
43
- const element = node;
44
- if (["aside", "nav"].includes(element.tagName)) {
45
- return false;
46
- }
47
- if (["hidden", "aria-hidden"].some((key) => element.properties[key])) {
48
- return false;
49
- }
50
- if ((0, import_utils.classnames)(element).some((classname) => REGEXPS.hidden.test(classname))) {
51
- return false;
52
- }
53
- if (element.tagName === "dialog") {
54
- return false;
55
- }
56
- if (element.properties.role === "dialog" && element.properties["aria-modal"]) {
57
- return false;
33
+ const role = (0, import_hast_fast.stringProperty)(element, "role");
34
+ if (role && import_constants.UNLIKELY_ROLES.has(role)) {
35
+ return true;
58
36
  }
59
- if (element.properties.rel === "author" && (0, import_utils.isStrInclude)(element.properties.itemprop, "author")) {
60
- return false;
37
+ if (role === "dialog" && (0, import_hast_fast.isTruthyAttribute)(element, "aria-modal")) {
38
+ return true;
61
39
  }
62
- if (REGEXPS.byline.test((0, import_utils.matchString)(element))) {
63
- return false;
40
+ if ((0, import_hast_fast.stringProperty)(element, "rel") === "author") {
41
+ return true;
64
42
  }
65
- if (element.properties.role && UNLIKELY_ROLES.includes(element.properties.role)) {
66
- return false;
43
+ if (import_constants.REGEXPS.byline.test((0, import_hast_fast.matchString)(element))) {
44
+ return true;
67
45
  }
68
- return true;
46
+ return false;
69
47
  };
70
48
  const minimalFilter = (params) => {
71
49
  const { hast } = params;
72
- const body = (0, import_hast_util_select.select)("body", hast) ?? hast;
73
- const metadataFilteredHast = (0, import_unist_util_filter.filter)(body, (node) => metadataFilter(node));
74
- const metadataFilteredHastText = metadataFilteredHast && (0, import_hast_util_to_string.toString)(metadataFilteredHast);
75
- if (!(metadataFilteredHast && metadataFilteredHastText)) {
50
+ const body = (0, import_hast_fast.cloneHast)((0, import_hast_fast.findElement)(hast, (element) => element.tagName === "body") ?? hast);
51
+ (0, import_sanitize.stripNonContent)(body);
52
+ const collector = new import_hast_fast.MetricsCollector();
53
+ const baseText = collector.textLength(body);
54
+ if (baseText === 0) {
76
55
  return body;
77
56
  }
78
- const baseFilterd = (0, import_unist_util_filter.filter)(metadataFilteredHast, (node) => universalElementFilter(node));
79
- const baseFilterdText = baseFilterd ? (0, import_hast_util_to_string.toString)(baseFilterd) : "";
80
- const isOverExtracted = baseFilterdText.length > metadataFilteredHastText.length / 3 || baseFilterdText.length > 5e3;
81
- const baseTree = isOverExtracted && baseFilterd ? baseFilterd : metadataFilteredHast;
82
- return baseTree;
57
+ const filtered = (0, import_hast_fast.cloneHast)(body);
58
+ (0, import_hast_fast.pruneInPlace)(filtered, (node) => !((0, import_hast_fast.isElement)(node) && isChrome(node)));
59
+ const filteredText = new import_hast_fast.MetricsCollector().textLength(filtered);
60
+ const isOverExtracted = filteredText < baseText * MIN_RETAINED_FRACTION && filteredText < ALWAYS_ACCEPT_LENGTH;
61
+ return isOverExtracted ? body : filtered;
83
62
  };
84
63
  // Annotate the CommonJS export names for ESM import in node:
85
64
  0 && (module.exports = {
@@ -21,136 +21,98 @@ __export(takumi_exports, {
21
21
  takumiExtractor: () => takumiExtractor
22
22
  });
23
23
  module.exports = __toCommonJS(takumi_exports);
24
- var import_hast_util_select = require("hast-util-select");
25
- var import_hast_util_to_string = require("hast-util-to-string");
26
- var import_unist_util_filter = require("unist-util-filter");
27
- var import_utils = require("./utils");
28
- const UNLIKELY_ROLES = ["menu", "menubar", "complementary", "navigation", "alert", "alertdialog", "dialog"];
29
- const REGEXPS = {
30
- hidden: /hidden|invisible|fallback-image/i,
31
- byline: /byline|author|dateline|writtenby|p-author/i,
32
- specialUnlikelyCandidates: /frb-|uls-menu|language-link/i,
33
- unlikelyCandidates: /-ad-|ai2html|banner|breadcrumbs|combx|comment|community|cover-wrap|tooltip|disqus|extra|footer|gdpr|legends|menu|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote|speechify-ignore|avatar/i,
34
- okMaybeItsaCandidate: /and|article|body|column|content|main|shadow|code/i
35
- };
36
- const BODY_SELECTORS = ["article", "#article", ".article_body", ".article-body", "#content", ".entry"];
37
- const PARAGRAPH_TAGS = ["a", "p", "div", "section", "article", "main", "ul", "ol", "li"];
38
- const CONTENTABLE_TAGS = ["article", "main", "section", "h1", "h2", "h3", "h4", "h5", "h6", "p"];
39
- const TEXTABLE_TAGS = ["p", "h1", "h2", "h3", "h4", "h5", "h6", "li", "ul"];
40
- const BASE_MINIMAL_LENGTH = { ja: 200, en: 500 };
41
- const metadataFilter = (node) => {
42
- return !(["comment", "doctype"].includes(node.type) || node.type === "element" && ["script", "style", "link", "meta", "noscript", "svg", "title"].includes(node.tagName));
43
- };
44
- const universalElementFilter = (node) => {
45
- if (node.type !== "element") {
46
- return true;
47
- }
48
- const element = node;
49
- if (["aside", "nav"].includes(element.tagName)) {
50
- return false;
51
- }
52
- if (["hidden", "aria-hidden"].some((key) => element.properties[key])) {
53
- return false;
54
- }
55
- if ((0, import_utils.classnames)(element).some((classname) => REGEXPS.hidden.test(classname))) {
56
- return false;
57
- }
58
- if (element.tagName === "dialog") {
59
- return false;
60
- }
61
- if (element.properties.role === "dialog" && element.properties["aria-modal"]) {
62
- return false;
63
- }
64
- if (element.properties.rel === "author" && (0, import_utils.isStrInclude)(element.properties.itemprop, "author")) {
65
- return false;
66
- }
67
- if (REGEXPS.byline.test((0, import_utils.matchString)(element))) {
68
- return false;
69
- }
70
- if (element.properties.role && UNLIKELY_ROLES.includes(element.properties.role)) {
71
- return false;
24
+ var import_hast_fast = require("../../utils/hast-fast");
25
+ var import_constants = require("../lib/constants");
26
+ var import_sanitize = require("../lib/sanitize");
27
+ var import_score = require("../lib/score");
28
+ var import_terminators = require("../lib/terminators");
29
+ const MIN_RETAINED_FRACTION = 0.25;
30
+ const SEMANTIC_SELECTORS = [
31
+ (element) => (0, import_hast_fast.stringProperty)(element, "itemprop")?.includes("articleBody") ?? false,
32
+ (element) => element.tagName === "article",
33
+ (element) => element.tagName === "main",
34
+ (element) => (0, import_hast_fast.stringProperty)(element, "role") === "main"
35
+ ];
36
+ const findBody = (hast) => (0, import_hast_fast.findElement)(hast, (element) => element.tagName === "body") ?? hast;
37
+ const semanticShortcut = (tree, collector, minLength) => {
38
+ for (const matches of SEMANTIC_SELECTORS) {
39
+ const found = [];
40
+ collectMatching(tree, matches, found);
41
+ if (found.length !== 1) {
42
+ continue;
43
+ }
44
+ const candidate = found[0];
45
+ const metrics = collector.metrics(candidate);
46
+ if (metrics.text >= minLength && collector.linkDensity(candidate) < 0.5) {
47
+ return candidate;
48
+ }
72
49
  }
73
- return true;
50
+ return void 0;
74
51
  };
75
- const unlikelyElementFilter = (node) => {
76
- if (node.type !== "element") {
77
- return true;
78
- }
79
- const element = node;
80
- if (CONTENTABLE_TAGS.includes(element.tagName)) {
81
- return true;
82
- }
83
- const match = (0, import_utils.matchString)(element);
84
- if (REGEXPS.specialUnlikelyCandidates.test(match)) {
85
- return false;
86
- }
87
- if (REGEXPS.unlikelyCandidates.test(match) && !REGEXPS.okMaybeItsaCandidate.test(match)) {
88
- return false;
52
+ const collectMatching = (tree, matches, into) => {
53
+ const stack = [tree];
54
+ while (stack.length > 0 && into.length < 2) {
55
+ const node = stack.pop();
56
+ if ((0, import_hast_fast.isElement)(node) && matches(node)) {
57
+ into.push(node);
58
+ continue;
59
+ }
60
+ if ("children" in node) {
61
+ for (const child of node.children) {
62
+ stack.push(child);
63
+ }
64
+ }
89
65
  }
90
- return true;
91
66
  };
92
- const removeEmptyFilter = (node, _lang) => {
93
- if (node.type !== "element") {
94
- return true;
95
- }
96
- const element = node;
97
- if (PARAGRAPH_TAGS.includes(element.tagName)) {
98
- return true;
99
- }
100
- if (element.tagName === "img" && !element.properties.src) {
101
- return false;
102
- }
103
- if (TEXTABLE_TAGS.includes(element.tagName) && (0, import_hast_util_to_string.toString)(element).length === 0) {
104
- return false;
105
- }
106
- return true;
67
+ const asRootOf = (nodes) => ({ type: "root", children: nodes });
68
+ const scoredSelection = (tree, collector, lang) => {
69
+ const context = (0, import_score.buildScoringContext)(tree, collector, lang);
70
+ const candidates = (0, import_score.scoreCandidates)(tree, context);
71
+ const top = candidates[0];
72
+ if (!top) {
73
+ return void 0;
74
+ }
75
+ const promoted = (0, import_score.climbToArticleBoundary)(top, candidates, context);
76
+ const anchor = promoted === top.element ? top : { element: promoted, score: top.score };
77
+ const selected = (0, import_score.collectArticleNodes)(anchor, candidates, context);
78
+ return selected.length === 1 ? selected[0] : asRootOf(selected);
107
79
  };
108
80
  const takumiExtractor = (params) => {
109
- const { hast, lang = "en" } = params;
110
- const body = (0, import_hast_util_select.select)("body", hast) ?? hast;
111
- const metadataFilteredHast = (0, import_unist_util_filter.filter)(body, (node) => metadataFilter(node));
112
- const metadataFilteredHastText = metadataFilteredHast && (0, import_hast_util_to_string.toString)(metadataFilteredHast);
113
- if (!(metadataFilteredHast && metadataFilteredHastText)) {
81
+ const { hast, lang, owned } = params;
82
+ const body = owned ? findBody(hast) : (0, import_hast_fast.cloneHast)(findBody(hast));
83
+ (0, import_sanitize.stripNonContent)(body);
84
+ let collector = new import_hast_fast.MetricsCollector();
85
+ const baseText = collector.textLength(body);
86
+ if (baseText === 0) {
114
87
  return body;
115
88
  }
116
- const baseFilterd = (0, import_unist_util_filter.filter)(metadataFilteredHast, (node) => universalElementFilter(node));
117
- const baseFilterdText = baseFilterd ? (0, import_hast_util_to_string.toString)(baseFilterd) : "";
118
- const [baseTree, baseText] = baseFilterdText.length > metadataFilteredHastText.length / 3 || baseFilterdText.length > 5e3 ? [baseFilterd, baseFilterdText] : [metadataFilteredHast, metadataFilteredHastText];
119
- let minimalLength = lang in BASE_MINIMAL_LENGTH ? BASE_MINIMAL_LENGTH[lang] : 500;
120
- if (baseText.length < minimalLength) {
121
- minimalLength = Math.max(0, baseText.length - 200);
122
- }
123
- let extractedTree = baseTree;
124
- let extractedText = baseText;
125
- for (const selector of BODY_SELECTORS) {
126
- const content = { type: "root", children: (0, import_hast_util_select.selectAll)(selector, baseFilterd) };
127
- const contentText = (0, import_hast_util_to_string.toString)(content);
128
- if (contentText.length < 25) {
129
- continue;
130
- }
131
- const links = (0, import_hast_util_select.selectAll)("a", content);
132
- const linkText = links.map((link) => (0, import_hast_util_to_string.toString)(link)).join("");
133
- const linkDensity = linkText.length / contentText.length;
134
- if (linkDensity > 0.4) {
135
- continue;
136
- }
137
- if (contentText.length > minimalLength) {
138
- extractedTree = content;
139
- extractedText = contentText;
140
- break;
141
- }
142
- }
143
- const finalFilteredTree = (0, import_unist_util_filter.filter)(extractedTree, (node) => {
144
- if (!removeEmptyFilter(node, lang)) {
145
- return false;
146
- }
147
- if (!unlikelyElementFilter(node)) {
148
- return false;
149
- }
150
- return true;
151
- });
152
- const finalTree = (0, import_hast_util_to_string.toString)(finalFilteredTree).length > extractedText.length / 3 ? finalFilteredTree : extractedTree;
153
- return finalTree;
89
+ const minLength = Math.min((0, import_constants.minContentLength)(lang), Math.max(0, baseText - 200));
90
+ const shortcut = semanticShortcut(body, collector, minLength);
91
+ const searchRoot = shortcut ?? body;
92
+ const searchText = collector.textLength(searchRoot);
93
+ const doomed = (0, import_sanitize.findUnlikelyElements)(searchRoot);
94
+ const doomedText = doomed.reduce((sum, element) => sum + collector.metrics(element).text, 0);
95
+ if (searchText - doomedText > Math.max(minLength, searchText * MIN_RETAINED_FRACTION)) {
96
+ const doomedSet = new Set(doomed);
97
+ (0, import_hast_fast.pruneInPlace)(searchRoot, (node) => !((0, import_hast_fast.isElement)(node) && doomedSet.has(node)));
98
+ collector = new import_hast_fast.MetricsCollector();
99
+ }
100
+ const scoringRoot = searchRoot;
101
+ const scoringRootText = collector.textLength(scoringRoot);
102
+ const selection = shortcut ? scoringRoot : scoredSelection(scoringRoot, collector, lang) ?? scoringRoot;
103
+ const selectionText = collector.textLength(selection);
104
+ const selectionIsSound = selectionText >= minLength || selectionText > scoringRootText * MIN_RETAINED_FRACTION;
105
+ const chosen = selectionIsSound ? selection : scoringRoot;
106
+ (0, import_terminators.truncateTrailingBoilerplate)(chosen);
107
+ const cleanupCollector = new import_hast_fast.MetricsCollector();
108
+ const chosenText = cleanupCollector.textLength(chosen);
109
+ const widgets = (0, import_sanitize.findCleanupTargets)(chosen, cleanupCollector);
110
+ const widgetText = widgets.reduce((sum, element) => sum + cleanupCollector.metrics(element).text, 0);
111
+ const remainingText = chosenText - widgetText;
112
+ if (remainingText < chosenText * MIN_RETAINED_FRACTION && remainingText < minLength) {
113
+ return chosen;
114
+ }
115
+ return (0, import_sanitize.cleanContent)(chosen, cleanupCollector);
154
116
  };
155
117
  // Annotate the CommonJS export names for ESM import in node:
156
118
  0 && (module.exports = {