webforai 3.0.0 → 4.1.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 (181) hide show
  1. package/README.md +47 -5
  2. package/dist/bin.js +3109 -490
  3. package/dist/cjs/detect-client-shell.js +78 -0
  4. package/dist/cjs/extract-mdast.js +58 -0
  5. package/dist/cjs/extract-meta-refresh.js +71 -0
  6. package/dist/cjs/extractors/index.js +5 -0
  7. package/dist/cjs/extractors/lib/block-features.js +465 -0
  8. package/dist/cjs/extractors/lib/block-model.generated.js +45 -0
  9. package/dist/cjs/extractors/lib/block-model.js +72 -0
  10. package/dist/cjs/extractors/lib/blocks.js +212 -0
  11. package/dist/cjs/extractors/lib/code-tabs.js +191 -0
  12. package/dist/cjs/extractors/lib/constants.js +1 -1
  13. package/dist/cjs/extractors/lib/layout-tables.js +123 -0
  14. package/dist/cjs/extractors/lib/sanitize.js +146 -26
  15. package/dist/cjs/extractors/lib/takumi-signal.js +76 -0
  16. package/dist/cjs/extractors/lib/ui-chrome.js +7 -0
  17. package/dist/cjs/extractors/presets/auto.js +3 -3
  18. package/dist/cjs/extractors/presets/kiwame.js +231 -0
  19. package/dist/cjs/extractors/presets/takumi.js +34 -3
  20. package/dist/cjs/html-to-markdown.js +58 -4
  21. package/dist/cjs/html-to-mdast.js +4 -3
  22. package/dist/cjs/index.js +16 -0
  23. package/dist/cjs/link-replacer.js +19 -13
  24. package/dist/cjs/loaders/cf-puppeteer.js +14 -7
  25. package/dist/cjs/loaders/fetch.js +14 -2
  26. package/dist/cjs/loaders/playwright.js +32 -30
  27. package/dist/cjs/loaders/puppeteer.js +14 -7
  28. package/dist/cjs/md-splitter.js +24 -11
  29. package/dist/cjs/mdast-handlers/custom-code-handler.js +32 -9
  30. package/dist/cjs/mdast-handlers/custom-div-handler.js +14 -35
  31. package/dist/cjs/mdast-handlers/custom-img-handler.js +10 -1
  32. package/dist/cjs/mdast-handlers/custom-table-handler.js +18 -1
  33. package/dist/cjs/mdast-handlers/definition-list-handler.js +17 -6
  34. package/dist/cjs/mdast-handlers/table-cell-flatten.js +195 -0
  35. package/dist/cjs/mdast-handlers/twoslash.js +71 -0
  36. package/dist/cjs/mdast-to-markdown.js +2 -5
  37. package/dist/cjs/metadata/index.js +4 -3
  38. package/dist/cjs/normalize/index.js +11 -2
  39. package/dist/cjs/platform/client.js +73 -16
  40. package/dist/cjs/platform/index.js +4 -0
  41. package/dist/cjs/platform/types.js +7 -1
  42. package/dist/cjs/utils/hast-fast.js +1 -1
  43. package/dist/cjs/utils/parse-html.js +113 -0
  44. package/dist/cjs/utils/pre-text.js +104 -0
  45. package/dist/detect-client-shell.js +53 -0
  46. package/dist/extract-mdast.js +58 -0
  47. package/dist/extract-meta-refresh.js +47 -0
  48. package/dist/extractors/index.js +3 -0
  49. package/dist/extractors/lib/block-features.js +437 -0
  50. package/dist/extractors/lib/block-model.generated.js +21 -0
  51. package/dist/extractors/lib/block-model.js +48 -0
  52. package/dist/extractors/lib/blocks.js +185 -0
  53. package/dist/extractors/lib/code-tabs.js +168 -0
  54. package/dist/extractors/lib/constants.js +1 -1
  55. package/dist/extractors/lib/layout-tables.js +99 -0
  56. package/dist/extractors/lib/sanitize.js +144 -26
  57. package/dist/extractors/lib/takumi-signal.js +53 -0
  58. package/dist/extractors/lib/ui-chrome.js +5 -0
  59. package/dist/extractors/presets/auto.js +3 -3
  60. package/dist/extractors/presets/kiwame.js +206 -0
  61. package/dist/extractors/presets/takumi.js +34 -3
  62. package/dist/html-to-markdown.js +57 -4
  63. package/dist/html-to-mdast.js +4 -3
  64. package/dist/index.js +18 -0
  65. package/dist/link-replacer.js +19 -13
  66. package/dist/loaders/cf-puppeteer.js +15 -8
  67. package/dist/loaders/fetch.js +14 -2
  68. package/dist/loaders/playwright.js +32 -30
  69. package/dist/loaders/puppeteer.js +15 -8
  70. package/dist/md-splitter.js +24 -11
  71. package/dist/mdast-handlers/custom-code-handler.js +30 -9
  72. package/dist/mdast-handlers/custom-div-handler.js +14 -35
  73. package/dist/mdast-handlers/custom-img-handler.js +10 -1
  74. package/dist/mdast-handlers/custom-table-handler.js +19 -2
  75. package/dist/mdast-handlers/definition-list-handler.js +17 -6
  76. package/dist/mdast-handlers/table-cell-flatten.js +170 -0
  77. package/dist/mdast-handlers/twoslash.js +48 -0
  78. package/dist/mdast-to-markdown.js +2 -5
  79. package/dist/metadata/index.js +4 -3
  80. package/dist/normalize/index.js +12 -2
  81. package/dist/platform/client.js +73 -16
  82. package/dist/platform/index.js +4 -0
  83. package/dist/platform/types.js +5 -1
  84. package/dist/types/cli/bin.js.map +1 -1
  85. package/dist/types/cli/commands/jobs/files.d.ts +28 -0
  86. package/dist/types/cli/commands/jobs/files.js.map +1 -0
  87. package/dist/types/cli/commands/jobs/llms-txt.d.ts +33 -0
  88. package/dist/types/cli/commands/jobs/llms-txt.js.map +1 -0
  89. package/dist/types/cli/commands/jobs/options.d.ts +58 -0
  90. package/dist/types/cli/commands/jobs/options.js.map +1 -0
  91. package/dist/types/cli/commands/jobs/run.d.ts +44 -0
  92. package/dist/types/cli/commands/jobs/run.js.map +1 -0
  93. package/dist/types/cli/commands/skill/content.js.map +1 -1
  94. package/dist/types/cli/commands/webforai/interactive.d.ts +1 -1
  95. package/dist/types/cli/commands/webforai/interactive.js.map +1 -1
  96. package/dist/types/cli/commands/webforai/options.d.ts +9 -2
  97. package/dist/types/cli/commands/webforai/options.js.map +1 -1
  98. package/dist/types/cli/commands/webforai/run.js.map +1 -1
  99. package/dist/types/cli/constants.d.ts +3 -1
  100. package/dist/types/cli/constants.js.map +1 -1
  101. package/dist/types/cli/utils.d.ts +8 -1
  102. package/dist/types/cli/utils.js.map +1 -1
  103. package/dist/types/detect-client-shell.d.ts +24 -0
  104. package/dist/types/detect-client-shell.js.map +1 -0
  105. package/dist/types/extract-mdast.js.map +1 -1
  106. package/dist/types/extract-meta-refresh.d.ts +29 -0
  107. package/dist/types/extract-meta-refresh.js.map +1 -0
  108. package/dist/types/extractors/index.d.ts +1 -0
  109. package/dist/types/extractors/index.js.map +1 -1
  110. package/dist/types/extractors/lib/block-features.d.ts +43 -0
  111. package/dist/types/extractors/lib/block-features.js.map +1 -0
  112. package/dist/types/extractors/lib/block-model.d.ts +28 -0
  113. package/dist/types/extractors/lib/block-model.generated.d.ts +5 -0
  114. package/dist/types/extractors/lib/block-model.generated.js.map +1 -0
  115. package/dist/types/extractors/lib/block-model.js.map +1 -0
  116. package/dist/types/extractors/lib/blocks.d.ts +53 -0
  117. package/dist/types/extractors/lib/blocks.js.map +1 -0
  118. package/dist/types/extractors/lib/code-tabs.d.ts +30 -0
  119. package/dist/types/extractors/lib/code-tabs.js.map +1 -0
  120. package/dist/types/extractors/lib/constants.js.map +1 -1
  121. package/dist/types/extractors/lib/layout-tables.d.ts +17 -0
  122. package/dist/types/extractors/lib/layout-tables.js.map +1 -0
  123. package/dist/types/extractors/lib/sanitize.d.ts +33 -3
  124. package/dist/types/extractors/lib/sanitize.js.map +1 -1
  125. package/dist/types/extractors/lib/takumi-signal.d.ts +12 -0
  126. package/dist/types/extractors/lib/takumi-signal.js.map +1 -0
  127. package/dist/types/extractors/lib/ui-chrome.d.ts +8 -0
  128. package/dist/types/extractors/lib/ui-chrome.js.map +1 -1
  129. package/dist/types/extractors/presets/auto.d.ts +6 -1
  130. package/dist/types/extractors/presets/auto.js.map +1 -1
  131. package/dist/types/extractors/presets/kiwame.d.ts +31 -0
  132. package/dist/types/extractors/presets/kiwame.js.map +1 -0
  133. package/dist/types/extractors/presets/takumi.js.map +1 -1
  134. package/dist/types/html-to-markdown.d.ts +9 -0
  135. package/dist/types/html-to-markdown.js.map +1 -1
  136. package/dist/types/html-to-mdast.js.map +1 -1
  137. package/dist/types/index.d.ts +3 -1
  138. package/dist/types/index.js.map +1 -1
  139. package/dist/types/link-replacer.d.ts +3 -1
  140. package/dist/types/link-replacer.js.map +1 -1
  141. package/dist/types/loaders/cf-puppeteer.js.map +1 -1
  142. package/dist/types/loaders/fetch.d.ts +4 -0
  143. package/dist/types/loaders/fetch.js.map +1 -1
  144. package/dist/types/loaders/playwright.js.map +1 -1
  145. package/dist/types/loaders/puppeteer.js.map +1 -1
  146. package/dist/types/md-splitter.d.ts +1 -1
  147. package/dist/types/md-splitter.js.map +1 -1
  148. package/dist/types/mdast-handlers/custom-code-handler.d.ts +9 -0
  149. package/dist/types/mdast-handlers/custom-code-handler.js.map +1 -1
  150. package/dist/types/mdast-handlers/custom-div-handler.js.map +1 -1
  151. package/dist/types/mdast-handlers/custom-img-handler.js.map +1 -1
  152. package/dist/types/mdast-handlers/custom-table-handler.js.map +1 -1
  153. package/dist/types/mdast-handlers/definition-list-handler.d.ts +0 -11
  154. package/dist/types/mdast-handlers/definition-list-handler.js.map +1 -1
  155. package/dist/types/mdast-handlers/table-cell-flatten.d.ts +18 -0
  156. package/dist/types/mdast-handlers/table-cell-flatten.js.map +1 -0
  157. package/dist/types/mdast-handlers/twoslash.d.ts +14 -0
  158. package/dist/types/mdast-handlers/twoslash.js.map +1 -0
  159. package/dist/types/mdast-to-markdown.js.map +1 -1
  160. package/dist/types/metadata/index.js.map +1 -1
  161. package/dist/types/normalize/index.js.map +1 -1
  162. package/dist/types/platform/client.d.ts +2 -1
  163. package/dist/types/platform/client.js.map +1 -1
  164. package/dist/types/platform/error.d.ts +5 -3
  165. package/dist/types/platform/error.js.map +1 -1
  166. package/dist/types/platform/fetch.d.ts +2 -0
  167. package/dist/types/platform/fetch.js.map +1 -1
  168. package/dist/types/platform/index.d.ts +1 -1
  169. package/dist/types/platform/index.js.map +1 -1
  170. package/dist/types/platform/types.d.ts +35 -6
  171. package/dist/types/platform/types.js.map +1 -1
  172. package/dist/types/utils/hast-fast.d.ts +1 -1
  173. package/dist/types/utils/hast-fast.js.map +1 -1
  174. package/dist/types/utils/parse-html.d.ts +33 -0
  175. package/dist/types/utils/parse-html.js.map +1 -0
  176. package/dist/types/utils/pre-text.d.ts +12 -0
  177. package/dist/types/utils/pre-text.js.map +1 -0
  178. package/dist/utils/hast-fast.js +1 -1
  179. package/dist/utils/parse-html.js +87 -0
  180. package/dist/utils/pre-text.js +81 -0
  181. package/package.json +18 -5
@@ -10,6 +10,61 @@ var emptyDeclarationFilter = (node) => {
10
10
  }
11
11
  return true;
12
12
  };
13
+ var trimEdge = (children, edge) => {
14
+ const index = edge === "start" ? 0 : children.length - 1;
15
+ const node = children[index];
16
+ if (node?.type !== "text") {
17
+ return children;
18
+ }
19
+ const value = edge === "start" ? node.value.trimStart() : node.value.trimEnd();
20
+ const copy = [...children];
21
+ if (value.length === 0) {
22
+ copy.splice(index, 1);
23
+ } else {
24
+ copy[index] = { ...node, value };
25
+ }
26
+ return copy;
27
+ };
28
+ var liftDisplayMath = (paragraph) => {
29
+ if (!paragraph.children.some((child) => child.type === "math")) {
30
+ return void 0;
31
+ }
32
+ const out = [];
33
+ let run = [];
34
+ const flush = () => {
35
+ const trimmed = trimEdge(trimEdge(run, "start"), "end");
36
+ if (trimmed.length > 0) {
37
+ out.push({ ...paragraph, children: trimmed });
38
+ }
39
+ run = [];
40
+ };
41
+ for (const child of paragraph.children) {
42
+ if (child.type === "math") {
43
+ flush();
44
+ out.push(child);
45
+ } else {
46
+ run.push(child);
47
+ }
48
+ }
49
+ flush();
50
+ return out;
51
+ };
52
+ var liftDisplayMathIn = (node) => {
53
+ if (!("children" in node)) {
54
+ return;
55
+ }
56
+ const parent = node;
57
+ for (let index = 0; index < parent.children.length; index++) {
58
+ const child = parent.children[index];
59
+ const lifted = child.type === "paragraph" ? liftDisplayMath(child) : void 0;
60
+ if (lifted) {
61
+ parent.children.splice(index, 1, ...lifted);
62
+ index += lifted.length - 1;
63
+ continue;
64
+ }
65
+ liftDisplayMathIn(child);
66
+ }
67
+ };
13
68
  var extractMdast = (node) => {
14
69
  const extracted = filter(node, (node2) => {
15
70
  if (!emptyDeclarationFilter(node2)) {
@@ -17,6 +72,9 @@ var extractMdast = (node) => {
17
72
  }
18
73
  return true;
19
74
  });
75
+ if (extracted) {
76
+ liftDisplayMathIn(extracted);
77
+ }
20
78
  return extracted;
21
79
  };
22
80
  export {
@@ -0,0 +1,47 @@
1
+ // src/extract-meta-refresh.ts
2
+ var MAX_META_REFRESH_DELAY_SECONDS = 10;
3
+ var META_TAG_PATTERN = /<meta\b[^>]*>/gi;
4
+ var HTTP_EQUIV_REFRESH_PATTERN = /\bhttp-equiv\s*=\s*("\s*refresh\s*"|'\s*refresh\s*'|refresh\b)/i;
5
+ var CONTENT_ATTR_PATTERN = /\bcontent\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i;
6
+ var CONTENT_VALUE_PATTERN = /^\s*(\d+(?:\.\d+)?)\s*[;,]\s*url\s*=\s*(?:"([^"]*)"|'([^']*)'|(.+?))\s*$/i;
7
+ var resolveTarget = (raw, baseUrl) => {
8
+ try {
9
+ return baseUrl === void 0 ? new URL(raw) : new URL(raw, baseUrl);
10
+ } catch {
11
+ return void 0;
12
+ }
13
+ };
14
+ var extractMetaRefresh = (html, baseUrl) => {
15
+ for (const [tag] of html.matchAll(META_TAG_PATTERN)) {
16
+ if (!HTTP_EQUIV_REFRESH_PATTERN.test(tag)) {
17
+ continue;
18
+ }
19
+ const contentMatch = CONTENT_ATTR_PATTERN.exec(tag);
20
+ const content = contentMatch?.[1] ?? contentMatch?.[2] ?? contentMatch?.[3];
21
+ if (!content) {
22
+ continue;
23
+ }
24
+ const value = CONTENT_VALUE_PATTERN.exec(content);
25
+ if (!value) {
26
+ continue;
27
+ }
28
+ const delaySeconds = Number.parseFloat(value[1] ?? "");
29
+ const raw = (value[2] ?? value[3] ?? value[4] ?? "").trim();
30
+ if (!raw || delaySeconds > MAX_META_REFRESH_DELAY_SECONDS) {
31
+ continue;
32
+ }
33
+ const target = resolveTarget(raw, baseUrl);
34
+ if (!target || target.protocol !== "http:" && target.protocol !== "https:") {
35
+ continue;
36
+ }
37
+ if (baseUrl !== void 0 && resolveTarget(baseUrl, void 0)?.href === target.href) {
38
+ continue;
39
+ }
40
+ return { url: target.href, delaySeconds };
41
+ }
42
+ return void 0;
43
+ };
44
+ export {
45
+ MAX_META_REFRESH_DELAY_SECONDS,
46
+ extractMetaRefresh
47
+ };
@@ -3,11 +3,14 @@ import {
3
3
  pipeExtractors
4
4
  } from "./pipeExtractors.js";
5
5
  import { takumiExtractor } from "./presets/takumi.js";
6
+ import { createKiwameExtractor, kiwameExtractor } from "./presets/kiwame.js";
6
7
  import { minimalFilter } from "./presets/minimal-filter.js";
7
8
  import { autoExtractor, createAutoExtractor } from "./presets/auto.js";
8
9
  export {
9
10
  autoExtractor,
10
11
  createAutoExtractor,
12
+ createKiwameExtractor,
13
+ kiwameExtractor,
11
14
  minimalFilter,
12
15
  pipeExtractors,
13
16
  takumiExtractor
@@ -0,0 +1,437 @@
1
+ // src/extractors/lib/block-features.ts
2
+ import { classList, matchString, stringProperty } from "../../utils/hast-fast.js";
3
+ import { ancestorAt } from "./blocks.js";
4
+ import { CONTENT_ROLES, REGEXPS, UNLIKELY_ROLES } from "./constants.js";
5
+ import { findLandmarkChrome, unlikelyTier } from "./sanitize.js";
6
+ import { buildScoringContext, scoreCandidates } from "./score.js";
7
+ var FEATURE_NAMES = [
8
+ "bias",
9
+ "logLen",
10
+ "linkDensity",
11
+ "logLinks",
12
+ "hasImage",
13
+ "textless",
14
+ "punctDensity",
15
+ "digitRatio",
16
+ "sentenceEnds",
17
+ "tag_p",
18
+ "tag_li",
19
+ "tag_cell",
20
+ "tag_h1",
21
+ "tag_h2_6",
22
+ "tag_pre",
23
+ "tag_div",
24
+ "tag_dtdd",
25
+ "tag_blockquote",
26
+ "tag_figcaption",
27
+ "in_nav",
28
+ "in_header",
29
+ "in_footer",
30
+ "in_aside",
31
+ "in_article",
32
+ "in_main",
33
+ "in_form",
34
+ "in_table",
35
+ "in_list",
36
+ "role_chrome",
37
+ "role_content",
38
+ "cls_unlikely",
39
+ "cls_positive",
40
+ "cls_negative",
41
+ "cls_strong",
42
+ "cls_special",
43
+ "cls_byline",
44
+ "cls_comment",
45
+ "landmark",
46
+ "doom_strong",
47
+ "doom_weak",
48
+ "depth",
49
+ "relPos",
50
+ "relCharPos",
51
+ "prev_logLen",
52
+ "prev_linkDensity",
53
+ "next_logLen",
54
+ "next_linkDensity",
55
+ "win_linkDensity",
56
+ "win_logLen",
57
+ "parent_linkDensity",
58
+ "parent_logText",
59
+ "gparent_linkDensity",
60
+ "gparent_logText",
61
+ "inTopCandidate",
62
+ "candidateScore",
63
+ "inTopSibling",
64
+ "takumi_kept"
65
+ ];
66
+ var FEATURE_COUNT = FEATURE_NAMES.length;
67
+ var PUNCT = /* @__PURE__ */ new Set([...".,;:!?\u3001\u3002\uFF0C\uFF0E\uFF01\uFF1F\uFF1B\uFF1A"]);
68
+ var SENTENCE_END = /* @__PURE__ */ new Set([...".!?\u3002\uFF01\uFF1F"]);
69
+ var textStats = (text) => {
70
+ let punct = 0;
71
+ let digits = 0;
72
+ let sentences = 0;
73
+ for (let index = 0; index < text.length; index++) {
74
+ const char = text[index];
75
+ const code = text.charCodeAt(index);
76
+ if (code >= 48 && code <= 57) {
77
+ digits += 1;
78
+ } else if (PUNCT.has(char)) {
79
+ punct += 1;
80
+ const next = text.charCodeAt(index + 1);
81
+ if (SENTENCE_END.has(char) && (Number.isNaN(next) || next === 32 || next === 10 || code > 12288)) {
82
+ sentences += 1;
83
+ }
84
+ }
85
+ }
86
+ return { punct, digits, sentences };
87
+ };
88
+ var COMMENT = /comment|disqus|reply|respond/i;
89
+ var log1p = (value) => Math.log(1 + value);
90
+ var signalsOf = (element, cache) => {
91
+ let signals = cache.get(element);
92
+ if (signals) {
93
+ return signals;
94
+ }
95
+ const match = matchString(element);
96
+ const role = stringProperty(element, "role") ?? "";
97
+ const names = [...classList(element), stringProperty(element, "id") ?? ""];
98
+ signals = {
99
+ unlikely: REGEXPS.unlikelyCandidates.test(match) && !REGEXPS.okMaybeItsaCandidate.test(match),
100
+ positive: REGEXPS.positive.test(match),
101
+ negative: REGEXPS.negative.test(match),
102
+ strong: names.some((name) => REGEXPS.stronglyUnlikely.test(name)),
103
+ special: REGEXPS.specialUnlikelyCandidates.test(match),
104
+ byline: REGEXPS.byline.test(match),
105
+ comment: COMMENT.test(match),
106
+ roleChrome: UNLIKELY_ROLES.has(role),
107
+ roleContent: CONTENT_ROLES.has(role)
108
+ };
109
+ cache.set(element, signals);
110
+ return signals;
111
+ };
112
+ var SIGNAL_LEVELS = 6;
113
+ var SIGNAL_COUNT = 9;
114
+ var RowWriter = class {
115
+ constructor(out) {
116
+ this.out = out;
117
+ this.offset = 0;
118
+ }
119
+ push(value) {
120
+ this.out[this.offset++] = value;
121
+ }
122
+ flag(condition) {
123
+ this.out[this.offset++] = condition ? 1 : 0;
124
+ }
125
+ };
126
+ var EMPTY_CHAIN = {
127
+ inNav: 0,
128
+ inHeader: 0,
129
+ inFooter: 0,
130
+ inAside: 0,
131
+ inArticle: 0,
132
+ inMain: 0,
133
+ inForm: 0,
134
+ inTable: 0,
135
+ inList: 0,
136
+ landmark: 0,
137
+ doomStrong: 0,
138
+ doomWeak: 0,
139
+ inTop: 0,
140
+ inTopSibling: 0,
141
+ nearestScore: 0,
142
+ distance: new Array(SIGNAL_COUNT).fill(Number.POSITIVE_INFINITY)
143
+ };
144
+ var signalFlags = (signals) => [
145
+ signals.roleChrome,
146
+ signals.roleContent,
147
+ signals.unlikely,
148
+ signals.positive,
149
+ signals.negative,
150
+ signals.strong,
151
+ signals.special,
152
+ signals.byline,
153
+ signals.comment
154
+ ];
155
+ var any = (parent, condition) => parent || condition ? 1 : 0;
156
+ var ChainResolver = class {
157
+ constructor(landmarks, candidateScore, parents, top) {
158
+ this.landmarks = landmarks;
159
+ this.candidateScore = candidateScore;
160
+ this.parents = parents;
161
+ this.top = top;
162
+ }
163
+ #chains = /* @__PURE__ */ new Map();
164
+ #signals = /* @__PURE__ */ new WeakMap();
165
+ /** Iterative, so a deeply nested page cannot exhaust the call stack. */
166
+ resolve(frame) {
167
+ const pending = [];
168
+ let current = frame;
169
+ let chain = EMPTY_CHAIN;
170
+ while (current) {
171
+ const memo = this.#chains.get(current.element);
172
+ if (memo) {
173
+ chain = memo;
174
+ break;
175
+ }
176
+ pending.push(current);
177
+ current = current.parent;
178
+ }
179
+ for (let index = pending.length - 1; index >= 0; index--) {
180
+ chain = this.#derive(chain, pending[index].element);
181
+ this.#chains.set(pending[index].element, chain);
182
+ }
183
+ return chain;
184
+ }
185
+ #derive(parent, element) {
186
+ const tag = element.tagName;
187
+ const tier = unlikelyTier(element);
188
+ const flags = signalFlags(signalsOf(element, this.#signals));
189
+ const top = this.top;
190
+ const topParent = top ? this.parents.get(top) : void 0;
191
+ return {
192
+ inNav: any(parent.inNav, tag === "nav"),
193
+ inHeader: any(parent.inHeader, tag === "header"),
194
+ inFooter: any(parent.inFooter, tag === "footer"),
195
+ inAside: any(parent.inAside, tag === "aside"),
196
+ inArticle: any(parent.inArticle, tag === "article"),
197
+ inMain: any(parent.inMain, tag === "main"),
198
+ inForm: any(parent.inForm, tag === "form"),
199
+ inTable: any(parent.inTable, tag === "table"),
200
+ inList: any(parent.inList, tag === "ul" || tag === "ol"),
201
+ landmark: any(parent.landmark, this.landmarks.has(element)),
202
+ doomStrong: any(parent.doomStrong, tier === "strong"),
203
+ doomWeak: any(parent.doomWeak, tier === "weak"),
204
+ inTop: any(parent.inTop, element === top),
205
+ inTopSibling: any(
206
+ parent.inTopSibling,
207
+ topParent !== void 0 && element !== top && this.parents.get(element) === topParent
208
+ ),
209
+ nearestScore: this.candidateScore.get(element) ?? parent.nearestScore,
210
+ distance: parent.distance.map((distance, signal) => flags[signal] ? 0 : distance + 1)
211
+ };
212
+ }
213
+ };
214
+ var evidence = (distance) => distance < SIGNAL_LEVELS ? 1 / (1 + distance) : 0;
215
+ var writeText = (row, block, density) => {
216
+ const length = block.text.length;
217
+ const stats = textStats(block.text);
218
+ row.push(1);
219
+ row.push(log1p(length));
220
+ row.push(density);
221
+ row.push(log1p(block.links));
222
+ row.flag(block.images > 0);
223
+ row.flag(length === 0);
224
+ row.push(length === 0 ? 0 : stats.punct / length);
225
+ row.push(length === 0 ? 0 : stats.digits / length);
226
+ row.push(log1p(stats.sentences));
227
+ };
228
+ var writeTag = (row, tag) => {
229
+ row.flag(tag === "p");
230
+ row.flag(tag === "li");
231
+ row.flag(tag === "td" || tag === "th");
232
+ row.flag(tag === "h1");
233
+ row.flag(/^h[2-6]$/.test(tag));
234
+ row.flag(tag === "pre");
235
+ row.flag(tag === "div" || tag === "section");
236
+ row.flag(tag === "dt" || tag === "dd");
237
+ row.flag(tag === "blockquote");
238
+ row.flag(tag === "figcaption");
239
+ };
240
+ var writeChain = (row, chain) => {
241
+ for (const value of [
242
+ chain.inNav,
243
+ chain.inHeader,
244
+ chain.inFooter,
245
+ chain.inAside,
246
+ chain.inArticle,
247
+ chain.inMain,
248
+ chain.inForm,
249
+ chain.inTable,
250
+ chain.inList
251
+ ]) {
252
+ row.push(value);
253
+ }
254
+ for (const distance of chain.distance) {
255
+ row.push(evidence(distance));
256
+ }
257
+ row.push(chain.landmark);
258
+ row.push(chain.doomStrong);
259
+ row.push(chain.doomWeak);
260
+ };
261
+ var windowMeans = (textLen, density, index, radius) => {
262
+ let link = 0;
263
+ let len = 0;
264
+ let size = 0;
265
+ for (let near = Math.max(0, index - radius); near <= Math.min(textLen.length - 1, index + radius); near++) {
266
+ link += density[near];
267
+ len += log1p(textLen[near]);
268
+ size += 1;
269
+ }
270
+ return [link / size, len / size];
271
+ };
272
+ var writeNeighbourhood = (row, index, textLen, density) => {
273
+ const hasPrevious = index > 0;
274
+ const hasNext = index < textLen.length - 1;
275
+ row.push(hasPrevious ? log1p(textLen[index - 1]) : 0);
276
+ row.push(hasPrevious ? density[index - 1] : 0);
277
+ row.push(hasNext ? log1p(textLen[index + 1]) : 0);
278
+ row.push(hasNext ? density[index + 1] : 0);
279
+ const [windowLink, windowLen] = windowMeans(textLen, density, index, 2);
280
+ row.push(windowLink);
281
+ row.push(windowLen);
282
+ };
283
+ var writeContainers = (row, block, collector) => {
284
+ for (const container of [ancestorAt(block, 1), ancestorAt(block, 2)]) {
285
+ row.push(container ? collector.linkDensity(container) : 0);
286
+ row.push(container ? log1p(collector.textLength(container)) : 0);
287
+ }
288
+ };
289
+ var blockFeatures = ({ root, blocks, collector, lang, takumiKept }) => {
290
+ const count = blocks.length;
291
+ const out = new Float32Array(count * FEATURE_COUNT);
292
+ const context = buildScoringContext(root, collector, lang);
293
+ const candidates = scoreCandidates(root, context);
294
+ const topScore = candidates[0]?.score ?? 1;
295
+ const candidateScore = /* @__PURE__ */ new WeakMap();
296
+ for (const candidate of candidates) {
297
+ candidateScore.set(candidate.element, candidate.score / topScore);
298
+ }
299
+ const chains = new ChainResolver(findLandmarkChrome(root), candidateScore, context.parents, candidates[0]?.element);
300
+ const textLen = blocks.map((block) => block.text.length);
301
+ const density = blocks.map((block) => block.text.length === 0 ? 0 : block.linkChars / block.text.length);
302
+ const totalChars = textLen.reduce((sum, length) => sum + length, 0) || 1;
303
+ let charsBefore = 0;
304
+ for (let index = 0; index < count; index++) {
305
+ const block = blocks[index];
306
+ const row = new RowWriter(out.subarray(index * FEATURE_COUNT, (index + 1) * FEATURE_COUNT));
307
+ const chain = chains.resolve(block.frame);
308
+ writeText(row, block, density[index]);
309
+ writeTag(row, block.owner.tagName);
310
+ writeChain(row, chain);
311
+ row.push(Math.min(block.frame.depth, 40) / 40);
312
+ row.push(count <= 1 ? 0 : index / (count - 1));
313
+ row.push(charsBefore / totalChars);
314
+ charsBefore += textLen[index];
315
+ writeNeighbourhood(row, index, textLen, density);
316
+ writeContainers(row, block, collector);
317
+ row.push(chain.inTop);
318
+ row.push(chain.nearestScore);
319
+ row.push(chain.inTopSibling);
320
+ row.flag(takumiKept?.has(block.owner));
321
+ }
322
+ return out;
323
+ };
324
+ var STACK_FEATURE_NAMES = [
325
+ "s1_logit",
326
+ "s1_win1",
327
+ "s1_win3",
328
+ "s1_parent",
329
+ "s1_gparent",
330
+ "s1_ggparent",
331
+ "s1_parentShare",
332
+ "s1_distHigh",
333
+ "s1_docShare"
334
+ ];
335
+ var STACKED_FEATURE_COUNT = FEATURE_COUNT + STACK_FEATURE_NAMES.length;
336
+ var logit = (p) => Math.log(Math.max(1e-6, p) / Math.max(1e-6, 1 - p));
337
+ var ancestorMass = (blocks, probabilities) => {
338
+ const mass = /* @__PURE__ */ new Map();
339
+ const add = (element, p, weight) => {
340
+ if (!element) {
341
+ return;
342
+ }
343
+ const entry = mass.get(element);
344
+ if (entry) {
345
+ entry.p += p * weight;
346
+ entry.w += weight;
347
+ } else {
348
+ mass.set(element, { p: p * weight, w: weight });
349
+ }
350
+ };
351
+ let docP = 0;
352
+ let docW = 0;
353
+ for (const block of blocks) {
354
+ const weight = block.text.length + 10;
355
+ const p = probabilities[block.index];
356
+ add(ancestorAt(block, 1), p, weight);
357
+ add(ancestorAt(block, 2), p, weight);
358
+ add(ancestorAt(block, 3), p, weight);
359
+ docP += p * weight;
360
+ docW += weight;
361
+ }
362
+ const parentWeightShare = (element) => {
363
+ const entry = element ? mass.get(element) : void 0;
364
+ return entry && docW > 0 ? entry.w / docW : 0;
365
+ };
366
+ return { mass, docShare: docW === 0 ? 0 : docP / docW, parentWeightShare };
367
+ };
368
+ var distanceToHigh = (probabilities) => {
369
+ const count = probabilities.length;
370
+ const distance = new Float32Array(count).fill(count);
371
+ let last = -1;
372
+ for (let index = 0; index < count; index++) {
373
+ if (probabilities[index] >= 0.5) {
374
+ last = index;
375
+ }
376
+ if (last >= 0) {
377
+ distance[index] = index - last;
378
+ }
379
+ }
380
+ last = -1;
381
+ for (let index = count - 1; index >= 0; index--) {
382
+ if (probabilities[index] >= 0.5) {
383
+ last = index;
384
+ }
385
+ if (last >= 0) {
386
+ distance[index] = Math.min(distance[index], last - index);
387
+ }
388
+ }
389
+ return distance;
390
+ };
391
+ var stackedFeatures = (blocks, base, probabilities) => {
392
+ const count = blocks.length;
393
+ const stride = STACKED_FEATURE_COUNT;
394
+ const out = new Float32Array(count * stride);
395
+ const { mass, docShare, parentWeightShare } = ancestorMass(blocks, probabilities);
396
+ const meanOf = (element, fallback) => {
397
+ const entry = element ? mass.get(element) : void 0;
398
+ return entry ? entry.p / entry.w : fallback;
399
+ };
400
+ const distance = distanceToHigh(probabilities);
401
+ for (let index = 0; index < count; index++) {
402
+ const row = index * stride;
403
+ out.set(base.subarray(index * FEATURE_COUNT, (index + 1) * FEATURE_COUNT), row);
404
+ const p = probabilities[index];
405
+ const window = (radius) => {
406
+ let sum = 0;
407
+ let n = 0;
408
+ for (let near = Math.max(0, index - radius); near <= Math.min(count - 1, index + radius); near++) {
409
+ if (near !== index) {
410
+ sum += probabilities[near];
411
+ n += 1;
412
+ }
413
+ }
414
+ return n === 0 ? p : sum / n;
415
+ };
416
+ const block = blocks[index];
417
+ let f = row + FEATURE_COUNT;
418
+ out[f++] = logit(p);
419
+ out[f++] = window(1);
420
+ out[f++] = window(3);
421
+ out[f++] = meanOf(ancestorAt(block, 1), p);
422
+ out[f++] = meanOf(ancestorAt(block, 2), p);
423
+ out[f++] = meanOf(ancestorAt(block, 3), p);
424
+ out[f++] = parentWeightShare(ancestorAt(block, 1));
425
+ out[f++] = Math.log1p(distance[index]);
426
+ out[f++] = docShare;
427
+ }
428
+ return out;
429
+ };
430
+ export {
431
+ FEATURE_COUNT,
432
+ FEATURE_NAMES,
433
+ STACKED_FEATURE_COUNT,
434
+ STACK_FEATURE_NAMES,
435
+ blockFeatures,
436
+ stackedFeatures
437
+ };