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
@@ -1,21 +1,37 @@
1
1
  // src/mdast-handlers/custom-a-handler.ts
2
2
  import { defaultHandlers } from "hast-util-to-mdast";
3
3
  import { toString as hastToString } from "hast-util-to-string";
4
+ import { stringProperty } from "../utils/hast-fast.js";
5
+ var ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:", "ftp:", "ftps:"]);
6
+ var isFollowableHref = (href) => {
7
+ const trimmed = href?.trim();
8
+ if (!trimmed) {
9
+ return false;
10
+ }
11
+ const scheme = trimmed.match(/^[a-z][a-z0-9+.-]*:/i)?.[0];
12
+ if (!scheme) {
13
+ return true;
14
+ }
15
+ return ALLOWED_SCHEMES.has(scheme.toLowerCase());
16
+ };
4
17
  var customAHandler = (options) => (state, node) => {
5
- if (options?.asText) {
18
+ const asPlainText = () => {
6
19
  const text = hastToString(node);
7
- if (3 >= text.length) {
20
+ if (text.length === 0) {
8
21
  return void 0;
9
22
  }
10
- const link2 = { type: "text", value: text };
11
- state.patch(node, link2);
12
- return link2;
23
+ const result = { type: "text", value: text };
24
+ state.patch(node, result);
25
+ return result;
26
+ };
27
+ if (options?.asText) {
28
+ return hastToString(node).length <= 3 ? void 0 : asPlainText();
13
29
  }
14
- const link = defaultHandlers.a(state, node);
15
- if (link.children.length > 0) {
16
- return link;
30
+ if (!isFollowableHref(stringProperty(node, "href"))) {
31
+ return asPlainText();
17
32
  }
18
- return void 0;
33
+ const link = defaultHandlers.a(state, node);
34
+ return link.children.length > 0 ? link : void 0;
19
35
  };
20
36
  export {
21
37
  customAHandler
@@ -2,19 +2,29 @@
2
2
  import { toText } from "hast-util-to-text";
3
3
  import { trimTrailingLines } from "trim-trailing-lines";
4
4
  import { detectLanguage } from "../utils/detect-code-lang.js";
5
- var LANGUAGE_MATCH_REGEX = [/language-(\w+)/, /highlight-source-(\w+)/, /CodeBlock--language-(\w+)/];
5
+ import { classList, isElement } from "../utils/hast-fast.js";
6
+ var LANGUAGE_MATCH_REGEX = [/^language-(\S+)$/, /^highlight-source-(\S+)$/, /^CodeBlock--language-(\S+)$/];
7
+ var LANGUAGE_ATTRIBUTES = ["dataLanguage", "dataLang"];
8
+ var attributeLanguage = (element) => {
9
+ for (const name of LANGUAGE_ATTRIBUTES) {
10
+ const value = element.properties?.[name];
11
+ if (typeof value === "string" && /^[\w+#.-]+$/.test(value.trim()) && value.trim() !== "plaintext") {
12
+ return value.trim();
13
+ }
14
+ }
15
+ return void 0;
16
+ };
17
+ var codeLanguage = (...elements) => elements.flatMap(classList).map((className) => LANGUAGE_MATCH_REGEX.map((regex) => className.match(regex)).find(Boolean)?.[1]).find(Boolean) ?? elements.map(attributeLanguage).find(Boolean);
6
18
  var customCodeHandler = (state, node) => {
7
- const classNames = node.properties?.className || [];
8
- const codeValue = trimTrailingLines(toText(node)).trim();
9
- const classLang = classNames.map((className) => {
10
- const match = LANGUAGE_MATCH_REGEX.map((regex) => className.match(regex)).find((match2) => match2);
11
- return match?.[1];
12
- }).find((className) => className);
19
+ const code = node.children.find((child) => isElement(child) && child.tagName === "code");
20
+ const codeValue = trimTrailingLines(toText(node));
21
+ const classLang = codeLanguage(...isElement(code) ? [code, node] : [node]);
13
22
  const lang = classLang || detectLanguage(codeValue) || null;
14
23
  const result = { type: "code", lang, meta: null, value: codeValue };
15
24
  state.patch(node, result);
16
25
  return result;
17
26
  };
18
27
  export {
28
+ codeLanguage,
19
29
  customCodeHandler
20
30
  };
@@ -5,44 +5,23 @@ import { toString as hastToString } from "hast-util-to-string";
5
5
  import { toText } from "hast-util-to-text";
6
6
  import { trimTrailingLines } from "trim-trailing-lines";
7
7
  import { detectLanguage } from "../utils/detect-code-lang.js";
8
+ import { classList, collectElements, isElement } from "../utils/hast-fast.js";
9
+ import { codeLanguage } from "./custom-code-handler.js";
8
10
  var CODE_BLOCK_REGEX = /highlight-source|language-|codegroup|codeblock|code-block/i;
9
11
  var CODE_FILENAME_SELECTORS = "[class*='fileName'],[class*='fileName'],[class*='title'],[class*='Title']";
10
- var LANGUAGE_MATCH_REGEX = [/language-(\w+)/, /highlight-source-(\w+)/, /CodeBlock--language-(\w+)/];
11
- var findRecursive = (array, condition, maxDepth = 3) => {
12
- if (maxDepth <= 0) {
13
- return null;
14
- }
15
- for (const value of array) {
16
- const result = condition(value);
17
- if (Array.isArray(result)) {
18
- return findRecursive(result, condition, maxDepth - 1);
19
- }
20
- if (result) {
21
- return value;
22
- }
23
- }
24
- return null;
25
- };
26
12
  var customDivHandler = (state, node) => {
27
- const classNames = Array.isArray(node.properties.className) ? node.properties.className : [];
28
- const codeBlock = findRecursive(node.children, (child) => {
29
- if (child.type !== "element") {
30
- return false;
31
- }
32
- if (child.tagName === "pre") {
33
- return true;
34
- }
35
- return child.children.filter((child2) => child2.type === "element");
36
- });
37
- if (codeBlock && classNames.some((className) => CODE_BLOCK_REGEX.test(className))) {
38
- const codeBlockClassNames = codeBlock.type === "element" ? codeBlock.properties.className ?? [] : [];
39
- const codeValue = trimTrailingLines(toText(codeBlock)).trim();
13
+ const classNames = classList(node);
14
+ if (!classNames.some((className) => CODE_BLOCK_REGEX.test(className))) {
15
+ return defaultHandlers.div(state, node);
16
+ }
17
+ const codeBlocks = collectElements(node, (element) => element.tagName === "pre");
18
+ const codeBlock = codeBlocks.length === 1 ? codeBlocks[0] : void 0;
19
+ if (codeBlock) {
20
+ const codeValue = trimTrailingLines(toText(codeBlock));
40
21
  const filenameElement = select(CODE_FILENAME_SELECTORS, node);
41
22
  const fileLang = filenameElement ? hastToString(filenameElement).match(/\.(\w+)$/)?.[1] : null;
42
- const classLang = [...classNames, ...codeBlockClassNames].map((className) => {
43
- const match = LANGUAGE_MATCH_REGEX.map((regex) => className.match(regex)).find((match2) => match2);
44
- return match?.[1];
45
- }).find((className) => className);
23
+ const code = codeBlock.children.find((child) => isElement(child) && child.tagName === "code");
24
+ const classLang = codeLanguage(...isElement(code) ? [code, codeBlock, node] : [codeBlock, node]);
46
25
  const lang = fileLang || classLang || detectLanguage(codeValue) || null;
47
26
  const result = { type: "code", lang, meta: null, value: codeValue };
48
27
  state.patch(node, result);
@@ -1,10 +1,32 @@
1
1
  // src/mdast-handlers/custom-img-handler.ts
2
2
  import { defaultHandlers } from "hast-util-to-mdast";
3
+ import { stringProperty } from "../utils/hast-fast.js";
4
+ var MAX_DATA_URL_LENGTH = 256;
5
+ var hasNoUsableSource = (node) => {
6
+ const src = stringProperty(node, "src")?.trim();
7
+ if (!src) {
8
+ return true;
9
+ }
10
+ if (src.startsWith("data:")) {
11
+ return src.length > MAX_DATA_URL_LENGTH;
12
+ }
13
+ return false;
14
+ };
3
15
  var customImgHandler = (options) => (state, node) => {
4
16
  if (options?.hideImage) {
5
17
  return void 0;
6
18
  }
7
- return defaultHandlers.image(state, node);
19
+ if (hasNoUsableSource(node)) {
20
+ return void 0;
21
+ }
22
+ const image = defaultHandlers.image(state, node);
23
+ if (!image.alt) {
24
+ const title = stringProperty(node, "title");
25
+ if (title) {
26
+ image.alt = title;
27
+ }
28
+ }
29
+ return image;
8
30
  };
9
31
  export {
10
32
  customImgHandler
@@ -1,12 +1,28 @@
1
1
  // src/mdast-handlers/custom-table-handler.ts
2
2
  import { defaultHandlers } from "hast-util-to-mdast";
3
3
  import { toText } from "hast-util-to-text";
4
+ import { isElement, numericProperty, walk } from "../utils/hast-fast.js";
5
+ var MAX_SPAN = 1e3;
6
+ var clampSpans = (node) => {
7
+ walk(node, (candidate) => {
8
+ if (!isElement(candidate) || candidate.tagName !== "td" && candidate.tagName !== "th") {
9
+ return;
10
+ }
11
+ for (const attribute of ["colSpan", "rowSpan"]) {
12
+ const span = numericProperty(candidate, attribute);
13
+ if (span !== void 0 && span > MAX_SPAN) {
14
+ candidate.properties[attribute] = MAX_SPAN;
15
+ }
16
+ }
17
+ });
18
+ };
4
19
  var customTableHandler = (options) => (state, node) => {
5
20
  if (options?.asText) {
6
21
  const paragraph = { type: "paragraph", children: [{ type: "text", value: toText(node) }] };
7
22
  state.patch(node, paragraph);
8
23
  return paragraph;
9
24
  }
25
+ clampSpans(node);
10
26
  return defaultHandlers.table(state, node);
11
27
  };
12
28
  export {
@@ -0,0 +1,87 @@
1
+ // src/mdast-handlers/definition-list-handler.ts
2
+ var hoistBoundaryWhitespace = (nodes) => {
3
+ const result = [];
4
+ for (const node of nodes) {
5
+ if (node.type !== "emphasis" && node.type !== "strong") {
6
+ result.push(node);
7
+ continue;
8
+ }
9
+ node.children = hoistBoundaryWhitespace(node.children);
10
+ const first = node.children[0];
11
+ if (first?.type === "text" && /^\s/.test(first.value)) {
12
+ result.push({ type: "text", value: " " });
13
+ first.value = first.value.replace(/^\s+/, "");
14
+ }
15
+ let trailing = false;
16
+ const last = node.children[node.children.length - 1];
17
+ if (last?.type === "text" && /\s$/.test(last.value)) {
18
+ last.value = last.value.replace(/\s+$/, "");
19
+ trailing = true;
20
+ }
21
+ node.children = node.children.filter((child) => !(child.type === "text" && child.value.length === 0));
22
+ if (node.children.length > 0) {
23
+ result.push(node);
24
+ }
25
+ if (trailing) {
26
+ result.push({ type: "text", value: " " });
27
+ }
28
+ }
29
+ return result;
30
+ };
31
+ var termsAndDefinitions = (parent) => {
32
+ const found = [];
33
+ for (const child of parent.children) {
34
+ if (child.type !== "element") {
35
+ continue;
36
+ }
37
+ if (child.tagName === "dt" || child.tagName === "dd") {
38
+ found.push(child);
39
+ } else if (child.tagName === "div") {
40
+ found.push(...termsAndDefinitions(child));
41
+ }
42
+ }
43
+ return found;
44
+ };
45
+ var definitionListHandler = (state, node) => {
46
+ const items = [];
47
+ let current;
48
+ const flush = () => {
49
+ if (!current) {
50
+ return;
51
+ }
52
+ const heading = {
53
+ type: "paragraph",
54
+ children: current.term.length > 0 ? [{ type: "strong", children: current.term }] : []
55
+ };
56
+ items.push({ type: "listItem", spread: false, children: [heading, ...current.definitions] });
57
+ current = void 0;
58
+ };
59
+ for (const element of termsAndDefinitions(node)) {
60
+ if (element.tagName === "dt") {
61
+ if (current && current.definitions.length > 0) {
62
+ flush();
63
+ }
64
+ const phrasing = hoistBoundaryWhitespace(state.all(element));
65
+ current = current ? { term: [...current.term, { type: "text", value: ", " }, ...phrasing], definitions: [] } : { term: phrasing, definitions: [] };
66
+ continue;
67
+ }
68
+ if (element.tagName === "dd") {
69
+ if (!current) {
70
+ current = { term: [], definitions: [] };
71
+ }
72
+ const content = state.toFlow(state.all(element));
73
+ current.definitions.push(...content);
74
+ }
75
+ }
76
+ flush();
77
+ if (items.length === 0) {
78
+ const fallback = state.toFlow(state.all(node));
79
+ return fallback.length > 0 ? fallback : void 0;
80
+ }
81
+ const result = { type: "list", ordered: false, spread: false, children: items };
82
+ state.patch(node, result);
83
+ return result;
84
+ };
85
+ export {
86
+ definitionListHandler
87
+ };
@@ -1,10 +1,19 @@
1
1
  // src/mdast-handlers/math-handler.ts
2
2
  import { toHtml } from "hast-util-to-html";
3
3
  import { MathMLToLaTeX } from "mathml-to-latex";
4
+ import { stringProperty } from "../utils/hast-fast.js";
5
+ var readAnnotation = (node) => {
6
+ const alttext = stringProperty(node, "alttext");
7
+ if (!alttext) {
8
+ return void 0;
9
+ }
10
+ const unwrapped = alttext.trim().replace(/^\{\\displaystyle\s*([\s\S]*)\}$/, "$1").trim();
11
+ return unwrapped.length > 0 ? unwrapped : void 0;
12
+ };
13
+ var isDisplayMath = (node) => stringProperty(node, "display") === "block";
4
14
  var mathHandler = (state, node) => {
5
- const mathMl = toHtml(node);
6
- const latex = MathMLToLaTeX.convert(mathMl);
7
- const result = { type: "inlineMath", value: latex };
15
+ const latex = readAnnotation(node) ?? MathMLToLaTeX.convert(toHtml(node));
16
+ const result = isDisplayMath(node) ? { type: "math", value: latex, meta: null } : { type: "inlineMath", value: latex };
8
17
  state.patch(node, result);
9
18
  return result;
10
19
  };
@@ -0,0 +1,16 @@
1
+ // src/mdast-handlers/sup-sub-handler.ts
2
+ import { toString as hastToString } from "hast-util-to-string";
3
+ var MAX_ANNOTATION_LENGTH = 32;
4
+ var supSubHandler = (tagName) => (state, node) => {
5
+ const inner = hastToString(node).trim();
6
+ if (inner.length === 0 || inner.length > MAX_ANNOTATION_LENGTH) {
7
+ return state.all(node);
8
+ }
9
+ const escaped = inner.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
10
+ const result = { type: "html", value: `<${tagName}>${escaped}</${tagName}>` };
11
+ state.patch(node, result);
12
+ return result;
13
+ };
14
+ export {
15
+ supSubHandler
16
+ };
@@ -10,11 +10,8 @@ var DEFAULT_MDAST_TO_MARKDOWN_OPTIONS = {
10
10
  };
11
11
  var mdastToMarkdown = (mdast, options) => {
12
12
  const { baseUrl, ...toMarkdownOptions } = { ...DEFAULT_MDAST_TO_MARKDOWN_OPTIONS, ...options };
13
- let markdown = toMarkdown(warpRoot(mdast), toMarkdownOptions).replace(/\*\*\*\*/g, "");
14
- if (baseUrl) {
15
- markdown = linkReplacer(markdown, baseUrl);
16
- }
17
- return markdown;
13
+ const tree = warpRoot(mdast);
14
+ return toMarkdown(baseUrl ? linkReplacer(tree, baseUrl) : tree, toMarkdownOptions);
18
15
  };
19
16
  export {
20
17
  DEFAULT_MDAST_TO_MARKDOWN_OPTIONS,
@@ -0,0 +1,198 @@
1
+ // src/metadata/index.ts
2
+ import { collectElements, findElement, isElement, stringProperty, walk } from "../utils/hast-fast.js";
3
+ var readMetaTags = (tree) => {
4
+ const tags = /* @__PURE__ */ new Map();
5
+ for (const meta of collectElements(tree, (element) => element.tagName === "meta")) {
6
+ const key = stringProperty(meta, "property") ?? stringProperty(meta, "name") ?? stringProperty(meta, "itemprop");
7
+ const content = stringProperty(meta, "content");
8
+ if (!(key && content)) {
9
+ continue;
10
+ }
11
+ const normalized = key.toLowerCase();
12
+ if (!tags.has(normalized)) {
13
+ tags.set(normalized, content);
14
+ }
15
+ }
16
+ return tags;
17
+ };
18
+ var readJsonLd = (tree) => {
19
+ const entries = [];
20
+ for (const script of collectElements(tree, (element) => element.tagName === "script")) {
21
+ const type = stringProperty(script, "type");
22
+ if (type?.toLowerCase() !== "application/ld+json") {
23
+ continue;
24
+ }
25
+ const source = script.children.map((child) => child.type === "text" ? child.value : "").join("").trim();
26
+ if (source.length === 0) {
27
+ continue;
28
+ }
29
+ let parsed;
30
+ try {
31
+ parsed = JSON.parse(source);
32
+ } catch {
33
+ continue;
34
+ }
35
+ for (const entry of flattenJsonLd(parsed)) {
36
+ entries.push(entry);
37
+ }
38
+ }
39
+ return entries;
40
+ };
41
+ var flattenJsonLd = (value) => {
42
+ if (Array.isArray(value)) {
43
+ return value.flatMap(flattenJsonLd);
44
+ }
45
+ if (typeof value !== "object" || value === null) {
46
+ return [];
47
+ }
48
+ const record = value;
49
+ const graph = record["@graph"];
50
+ if (graph) {
51
+ return flattenJsonLd(graph);
52
+ }
53
+ return [record];
54
+ };
55
+ var ARTICLE_TYPES = /* @__PURE__ */ new Set([
56
+ "article",
57
+ "newsarticle",
58
+ "blogposting",
59
+ "techarticle",
60
+ "scholarlyarticle",
61
+ "report",
62
+ "webpage"
63
+ ]);
64
+ var jsonLdType = (entry) => {
65
+ const type = entry["@type"];
66
+ if (typeof type === "string") {
67
+ return [type.toLowerCase()];
68
+ }
69
+ if (Array.isArray(type)) {
70
+ return type.filter((value) => typeof value === "string").map((value) => value.toLowerCase());
71
+ }
72
+ return [];
73
+ };
74
+ var readName = (value) => {
75
+ if (typeof value === "string") {
76
+ return value;
77
+ }
78
+ if (Array.isArray(value)) {
79
+ for (const entry of value) {
80
+ const name = readName(entry);
81
+ if (name) {
82
+ return name;
83
+ }
84
+ }
85
+ return void 0;
86
+ }
87
+ if (typeof value === "object" && value !== null) {
88
+ const name = value.name;
89
+ return typeof name === "string" ? name : void 0;
90
+ }
91
+ return void 0;
92
+ };
93
+ var asString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
94
+ var MAX_TITLE_LENGTH = 160;
95
+ var asTitle = (value) => {
96
+ const cleaned = clean(value);
97
+ return cleaned && cleaned.length <= MAX_TITLE_LENGTH ? cleaned : void 0;
98
+ };
99
+ var clean = (value) => {
100
+ if (value === void 0) {
101
+ return void 0;
102
+ }
103
+ const trimmed = value.replace(/\s+/g, " ").trim();
104
+ return trimmed.length > 0 ? trimmed : void 0;
105
+ };
106
+ var documentTitle = (tree) => {
107
+ const title = findElement(tree, (element) => element.tagName === "title");
108
+ if (!title) {
109
+ return void 0;
110
+ }
111
+ return title.children.map((child) => child.type === "text" ? child.value : "").join("");
112
+ };
113
+ var firstHeading = (tree) => {
114
+ const heading = findElement(tree, (element) => element.tagName === "h1");
115
+ if (!heading) {
116
+ return void 0;
117
+ }
118
+ let text = "";
119
+ walk(heading, (node) => {
120
+ if (node.type === "text") {
121
+ text += node.value;
122
+ }
123
+ });
124
+ return text;
125
+ };
126
+ var linkHref = (tree, rel) => {
127
+ const link = findElement(
128
+ tree,
129
+ (element) => element.tagName === "link" && (stringProperty(element, "rel") ?? "").toLowerCase().includes(rel)
130
+ );
131
+ return link ? stringProperty(link, "href") : void 0;
132
+ };
133
+ var documentLang = (tree) => {
134
+ const html = findElement(tree, (element) => element.tagName === "html");
135
+ if (html) {
136
+ const declared = stringProperty(html, "lang") ?? stringProperty(html, "xml:lang");
137
+ if (declared) {
138
+ return declared;
139
+ }
140
+ }
141
+ if (isElement(tree)) {
142
+ const own = stringProperty(tree, "lang");
143
+ if (own) {
144
+ return own;
145
+ }
146
+ }
147
+ const tagged = findElement(tree, (element) => Boolean(stringProperty(element, "lang")));
148
+ return tagged ? stringProperty(tagged, "lang") : void 0;
149
+ };
150
+ var extractMetadata = (tree) => {
151
+ const meta = readMetaTags(tree);
152
+ const jsonLd = readJsonLd(tree);
153
+ const article = jsonLd.find((entry) => jsonLdType(entry).some((type) => ARTICLE_TYPES.has(type))) ?? jsonLd[0];
154
+ const get = (...keys) => {
155
+ for (const key of keys) {
156
+ const value = meta.get(key);
157
+ if (value) {
158
+ return value;
159
+ }
160
+ }
161
+ return void 0;
162
+ };
163
+ const title = asTitle(get("og:title", "twitter:title")) ?? asTitle(asString(article?.headline)) ?? asTitle(asString(article?.name)) ?? asTitle(documentTitle(tree)) ?? asTitle(firstHeading(tree));
164
+ return {
165
+ title,
166
+ description: clean(get("og:description", "twitter:description", "description")) ?? clean(asString(article?.description)),
167
+ author: clean(readName(article?.author)) ?? clean(get("author", "article:author", "twitter:creator")),
168
+ published: clean(asString(article?.datePublished)) ?? clean(get("article:published_time", "date", "pubdate")),
169
+ modified: clean(asString(article?.dateModified)) ?? clean(get("article:modified_time", "og:updated_time")),
170
+ siteName: clean(get("og:site_name", "application-name")) ?? clean(readName(article?.publisher)),
171
+ canonicalUrl: clean(linkHref(tree, "canonical")) ?? clean(get("og:url", "twitter:url")),
172
+ lang: clean(documentLang(tree)),
173
+ image: clean(get("og:image", "twitter:image", "twitter:image:src")),
174
+ type: clean(get("og:type"))
175
+ };
176
+ };
177
+ var yamlString = (value) => `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
178
+ var toFrontmatter = (metadata) => {
179
+ const lines = [];
180
+ for (const [key, value] of Object.entries(metadata)) {
181
+ if (typeof value === "string" && value.length > 0) {
182
+ lines.push(`${key}: ${yamlString(value)}`);
183
+ }
184
+ }
185
+ if (lines.length === 0) {
186
+ return "";
187
+ }
188
+ return `---
189
+ ${lines.join("\n")}
190
+ ---
191
+
192
+ `;
193
+ };
194
+ export {
195
+ extractMetadata,
196
+ readJsonLd,
197
+ toFrontmatter
198
+ };