webforai 4.0.0 → 4.1.1

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 (113) hide show
  1. package/README.md +74 -69
  2. package/dist/bin.js +1895 -74
  3. package/dist/cjs/extract-mdast.js +58 -0
  4. package/dist/cjs/extractors/index.js +5 -0
  5. package/dist/cjs/extractors/lib/block-features.js +465 -0
  6. package/dist/cjs/extractors/lib/block-model.generated.js +45 -0
  7. package/dist/cjs/extractors/lib/block-model.js +72 -0
  8. package/dist/cjs/extractors/lib/blocks.js +212 -0
  9. package/dist/cjs/extractors/lib/code-tabs.js +191 -0
  10. package/dist/cjs/extractors/lib/constants.js +1 -1
  11. package/dist/cjs/extractors/lib/layout-tables.js +123 -0
  12. package/dist/cjs/extractors/lib/sanitize.js +144 -28
  13. package/dist/cjs/extractors/lib/takumi-signal.js +76 -0
  14. package/dist/cjs/extractors/lib/ui-chrome.js +7 -0
  15. package/dist/cjs/extractors/presets/auto.js +3 -3
  16. package/dist/cjs/extractors/presets/kiwame.js +231 -0
  17. package/dist/cjs/extractors/presets/takumi.js +34 -3
  18. package/dist/cjs/html-to-markdown.js +58 -4
  19. package/dist/cjs/html-to-mdast.js +2 -2
  20. package/dist/cjs/index.js +4 -0
  21. package/dist/cjs/mdast-handlers/custom-code-handler.js +16 -4
  22. package/dist/cjs/mdast-handlers/custom-div-handler.js +3 -3
  23. package/dist/cjs/mdast-handlers/custom-img-handler.js +10 -1
  24. package/dist/cjs/mdast-handlers/custom-table-handler.js +18 -1
  25. package/dist/cjs/mdast-handlers/table-cell-flatten.js +195 -0
  26. package/dist/cjs/mdast-handlers/twoslash.js +71 -0
  27. package/dist/cjs/metadata/index.js +4 -3
  28. package/dist/cjs/utils/hast-fast.js +1 -1
  29. package/dist/cjs/utils/parse-html.js +113 -0
  30. package/dist/cjs/utils/pre-text.js +104 -0
  31. package/dist/extract-mdast.js +58 -0
  32. package/dist/extractors/index.js +3 -0
  33. package/dist/extractors/lib/block-features.js +437 -0
  34. package/dist/extractors/lib/block-model.generated.js +21 -0
  35. package/dist/extractors/lib/block-model.js +48 -0
  36. package/dist/extractors/lib/blocks.js +185 -0
  37. package/dist/extractors/lib/code-tabs.js +168 -0
  38. package/dist/extractors/lib/constants.js +1 -1
  39. package/dist/extractors/lib/layout-tables.js +99 -0
  40. package/dist/extractors/lib/sanitize.js +142 -28
  41. package/dist/extractors/lib/takumi-signal.js +53 -0
  42. package/dist/extractors/lib/ui-chrome.js +5 -0
  43. package/dist/extractors/presets/auto.js +3 -3
  44. package/dist/extractors/presets/kiwame.js +206 -0
  45. package/dist/extractors/presets/takumi.js +34 -3
  46. package/dist/html-to-markdown.js +57 -4
  47. package/dist/html-to-mdast.js +2 -2
  48. package/dist/index.js +4 -0
  49. package/dist/mdast-handlers/custom-code-handler.js +15 -4
  50. package/dist/mdast-handlers/custom-div-handler.js +4 -4
  51. package/dist/mdast-handlers/custom-img-handler.js +10 -1
  52. package/dist/mdast-handlers/custom-table-handler.js +19 -2
  53. package/dist/mdast-handlers/table-cell-flatten.js +170 -0
  54. package/dist/mdast-handlers/twoslash.js +48 -0
  55. package/dist/metadata/index.js +4 -3
  56. package/dist/types/cli/commands/jobs/options.d.ts +2 -2
  57. package/dist/types/cli/commands/jobs/options.js.map +1 -1
  58. package/dist/types/cli/commands/webforai/options.js.map +1 -1
  59. package/dist/types/cli/commands/webforai/run.js.map +1 -1
  60. package/dist/types/cli/constants.d.ts +3 -1
  61. package/dist/types/cli/constants.js.map +1 -1
  62. package/dist/types/extract-mdast.js.map +1 -1
  63. package/dist/types/extractors/index.d.ts +1 -0
  64. package/dist/types/extractors/index.js.map +1 -1
  65. package/dist/types/extractors/lib/block-features.d.ts +43 -0
  66. package/dist/types/extractors/lib/block-features.js.map +1 -0
  67. package/dist/types/extractors/lib/block-model.d.ts +28 -0
  68. package/dist/types/extractors/lib/block-model.generated.d.ts +5 -0
  69. package/dist/types/extractors/lib/block-model.generated.js.map +1 -0
  70. package/dist/types/extractors/lib/block-model.js.map +1 -0
  71. package/dist/types/extractors/lib/blocks.d.ts +53 -0
  72. package/dist/types/extractors/lib/blocks.js.map +1 -0
  73. package/dist/types/extractors/lib/code-tabs.d.ts +30 -0
  74. package/dist/types/extractors/lib/code-tabs.js.map +1 -0
  75. package/dist/types/extractors/lib/constants.js.map +1 -1
  76. package/dist/types/extractors/lib/layout-tables.d.ts +17 -0
  77. package/dist/types/extractors/lib/layout-tables.js.map +1 -0
  78. package/dist/types/extractors/lib/sanitize.d.ts +33 -3
  79. package/dist/types/extractors/lib/sanitize.js.map +1 -1
  80. package/dist/types/extractors/lib/takumi-signal.d.ts +12 -0
  81. package/dist/types/extractors/lib/takumi-signal.js.map +1 -0
  82. package/dist/types/extractors/lib/ui-chrome.d.ts +8 -0
  83. package/dist/types/extractors/lib/ui-chrome.js.map +1 -1
  84. package/dist/types/extractors/presets/auto.d.ts +6 -1
  85. package/dist/types/extractors/presets/auto.js.map +1 -1
  86. package/dist/types/extractors/presets/kiwame.d.ts +31 -0
  87. package/dist/types/extractors/presets/kiwame.js.map +1 -0
  88. package/dist/types/extractors/presets/takumi.js.map +1 -1
  89. package/dist/types/html-to-markdown.d.ts +9 -0
  90. package/dist/types/html-to-markdown.js.map +1 -1
  91. package/dist/types/html-to-mdast.js.map +1 -1
  92. package/dist/types/index.d.ts +1 -1
  93. package/dist/types/index.js.map +1 -1
  94. package/dist/types/mdast-handlers/custom-code-handler.d.ts +7 -0
  95. package/dist/types/mdast-handlers/custom-code-handler.js.map +1 -1
  96. package/dist/types/mdast-handlers/custom-div-handler.js.map +1 -1
  97. package/dist/types/mdast-handlers/custom-img-handler.js.map +1 -1
  98. package/dist/types/mdast-handlers/custom-table-handler.js.map +1 -1
  99. package/dist/types/mdast-handlers/table-cell-flatten.d.ts +18 -0
  100. package/dist/types/mdast-handlers/table-cell-flatten.js.map +1 -0
  101. package/dist/types/mdast-handlers/twoslash.d.ts +14 -0
  102. package/dist/types/mdast-handlers/twoslash.js.map +1 -0
  103. package/dist/types/metadata/index.js.map +1 -1
  104. package/dist/types/utils/hast-fast.d.ts +1 -1
  105. package/dist/types/utils/hast-fast.js.map +1 -1
  106. package/dist/types/utils/parse-html.d.ts +33 -0
  107. package/dist/types/utils/parse-html.js.map +1 -0
  108. package/dist/types/utils/pre-text.d.ts +12 -0
  109. package/dist/types/utils/pre-text.js.map +1 -0
  110. package/dist/utils/hast-fast.js +1 -1
  111. package/dist/utils/parse-html.js +87 -0
  112. package/dist/utils/pre-text.js +81 -0
  113. package/package.json +3 -1
@@ -32,6 +32,61 @@ const emptyDeclarationFilter = (node) => {
32
32
  }
33
33
  return true;
34
34
  };
35
+ const trimEdge = (children, edge) => {
36
+ const index = edge === "start" ? 0 : children.length - 1;
37
+ const node = children[index];
38
+ if (node?.type !== "text") {
39
+ return children;
40
+ }
41
+ const value = edge === "start" ? node.value.trimStart() : node.value.trimEnd();
42
+ const copy = [...children];
43
+ if (value.length === 0) {
44
+ copy.splice(index, 1);
45
+ } else {
46
+ copy[index] = { ...node, value };
47
+ }
48
+ return copy;
49
+ };
50
+ const liftDisplayMath = (paragraph) => {
51
+ if (!paragraph.children.some((child) => child.type === "math")) {
52
+ return void 0;
53
+ }
54
+ const out = [];
55
+ let run = [];
56
+ const flush = () => {
57
+ const trimmed = trimEdge(trimEdge(run, "start"), "end");
58
+ if (trimmed.length > 0) {
59
+ out.push({ ...paragraph, children: trimmed });
60
+ }
61
+ run = [];
62
+ };
63
+ for (const child of paragraph.children) {
64
+ if (child.type === "math") {
65
+ flush();
66
+ out.push(child);
67
+ } else {
68
+ run.push(child);
69
+ }
70
+ }
71
+ flush();
72
+ return out;
73
+ };
74
+ const liftDisplayMathIn = (node) => {
75
+ if (!("children" in node)) {
76
+ return;
77
+ }
78
+ const parent = node;
79
+ for (let index = 0; index < parent.children.length; index++) {
80
+ const child = parent.children[index];
81
+ const lifted = child.type === "paragraph" ? liftDisplayMath(child) : void 0;
82
+ if (lifted) {
83
+ parent.children.splice(index, 1, ...lifted);
84
+ index += lifted.length - 1;
85
+ continue;
86
+ }
87
+ liftDisplayMathIn(child);
88
+ }
89
+ };
35
90
  const extractMdast = (node) => {
36
91
  const extracted = (0, import_unist_util_filter.filter)(node, (node2) => {
37
92
  if (!emptyDeclarationFilter(node2)) {
@@ -39,6 +94,9 @@ const extractMdast = (node) => {
39
94
  }
40
95
  return true;
41
96
  });
97
+ if (extracted) {
98
+ liftDisplayMathIn(extracted);
99
+ }
42
100
  return extracted;
43
101
  };
44
102
  // Annotate the CommonJS export names for ESM import in node:
@@ -20,6 +20,8 @@ var extractors_exports = {};
20
20
  __export(extractors_exports, {
21
21
  autoExtractor: () => import_auto.autoExtractor,
22
22
  createAutoExtractor: () => import_auto.createAutoExtractor,
23
+ createKiwameExtractor: () => import_kiwame.createKiwameExtractor,
24
+ kiwameExtractor: () => import_kiwame.kiwameExtractor,
23
25
  minimalFilter: () => import_minimal_filter.minimalFilter,
24
26
  pipeExtractors: () => import_pipeExtractors.pipeExtractors,
25
27
  takumiExtractor: () => import_takumi.takumiExtractor
@@ -27,12 +29,15 @@ __export(extractors_exports, {
27
29
  module.exports = __toCommonJS(extractors_exports);
28
30
  var import_pipeExtractors = require("./pipeExtractors");
29
31
  var import_takumi = require("./presets/takumi");
32
+ var import_kiwame = require("./presets/kiwame");
30
33
  var import_minimal_filter = require("./presets/minimal-filter");
31
34
  var import_auto = require("./presets/auto");
32
35
  // Annotate the CommonJS export names for ESM import in node:
33
36
  0 && (module.exports = {
34
37
  autoExtractor,
35
38
  createAutoExtractor,
39
+ createKiwameExtractor,
40
+ kiwameExtractor,
36
41
  minimalFilter,
37
42
  pipeExtractors,
38
43
  takumiExtractor
@@ -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
+ });