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