extract-cite 1.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 (47) hide show
  1. package/README.md +56 -0
  2. package/dist/extract-cite.cjs.js +2 -0
  3. package/dist/extract-cite.cjs.js.map +1 -0
  4. package/dist/extract-cite.es.js +4 -0
  5. package/dist/extract-cite.es.js.map +1 -0
  6. package/dist/html-to-cite/extract-author.d.ts +11 -0
  7. package/dist/html-to-cite/extract-cite.d.ts +33 -0
  8. package/dist/html-to-cite/extract-date/date-extractors.d.ts +40 -0
  9. package/dist/html-to-cite/extract-date/date-validators.d.ts +15 -0
  10. package/dist/html-to-cite/extract-date/extract-date-quick.d.ts +8 -0
  11. package/dist/html-to-cite/extract-date/extract-date.d.ts +26 -0
  12. package/dist/html-to-cite/extract-source.d.ts +11 -0
  13. package/dist/html-to-cite/extract-title.d.ts +11 -0
  14. package/dist/html-to-cite/human-names-recognize.d.ts +16 -0
  15. package/dist/html-to-cite/metadata-to-cite.d.ts +16 -0
  16. package/dist/html-to-cite/url-to-domain.d.ts +20 -0
  17. package/dist/index.d.ts +14 -0
  18. package/dist/llm/call-llm.d.ts +33 -0
  19. package/dist/llm/extract-cite-llm.d.ts +19 -0
  20. package/dist/llm/format-citation.d.ts +22 -0
  21. package/dist/llm/index.d.ts +7 -0
  22. package/dist/llm/page-text.d.ts +21 -0
  23. package/dist/llm/parse-reply.d.ts +26 -0
  24. package/dist/llm/prompt.d.ts +14 -0
  25. package/dist/llm/types.d.ts +115 -0
  26. package/package.json +61 -0
  27. package/src/html-to-cite/extract-author.ts +125 -0
  28. package/src/html-to-cite/extract-cite.ts +97 -0
  29. package/src/html-to-cite/extract-date/date-extractors.ts +509 -0
  30. package/src/html-to-cite/extract-date/date-validators.ts +191 -0
  31. package/src/html-to-cite/extract-date/extract-date-quick.ts +184 -0
  32. package/src/html-to-cite/extract-date/extract-date.ts +1051 -0
  33. package/src/html-to-cite/extract-source.ts +35 -0
  34. package/src/html-to-cite/extract-title.ts +78 -0
  35. package/src/html-to-cite/human-names-92k.json +1 -0
  36. package/src/html-to-cite/human-names-recognize.ts +396 -0
  37. package/src/html-to-cite/metadata-to-cite.ts +77 -0
  38. package/src/html-to-cite/url-to-domain.ts +50 -0
  39. package/src/index.ts +14 -0
  40. package/src/llm/call-llm.ts +112 -0
  41. package/src/llm/extract-cite-llm.ts +100 -0
  42. package/src/llm/format-citation.ts +399 -0
  43. package/src/llm/index.ts +7 -0
  44. package/src/llm/page-text.ts +106 -0
  45. package/src/llm/parse-reply.ts +279 -0
  46. package/src/llm/prompt.ts +95 -0
  47. package/src/llm/types.ts +174 -0
@@ -0,0 +1,125 @@
1
+ /**
2
+ * @fileoverview Utility for identifying and extracting author names from HTML metadata and content.
3
+ * Handles individual authors, multiple authors, and organizational authors.
4
+ */
5
+ import { extractHumanName } from "./human-names-recognize";
6
+
7
+ // https://www.scribbr.com/citation/generator/folders/2rx21jyIjZIKRrcwLk2oXE/lists/4OeJQ4euxzyTk9BiPTRjwn/
8
+ const AUTHOR_META_TAGS = [
9
+ "byl",
10
+ "clmst",
11
+ "dc.author",
12
+ "dcsext.author",
13
+ "dc.creator",
14
+ "rbauthors",
15
+ "authors",
16
+ "sailthru.author",
17
+ "article:author",
18
+ "parsely-author",
19
+ ];
20
+
21
+ const AUTHOR_SELECTORS = [
22
+ ".entry .entry-author",
23
+ ".author.vcard .fn",
24
+ ".author .vcard .fn",
25
+ ".byline.vcard .fn",
26
+ ".byline .vcard .fn",
27
+ ".byline .by .author",
28
+ ".byline .by",
29
+ ".byline .author",
30
+ ".post-author.vcard",
31
+ ".post-author .vcard",
32
+ "a[rel=author]",
33
+ "#by_author",
34
+ ".by_author",
35
+ "#entryAuthor",
36
+ ".entryAuthor",
37
+ ".byline a[href*=author]",
38
+ "#author .authorname",
39
+ ".author .authorname",
40
+ "#author",
41
+ ".author",
42
+ ".articleauthor",
43
+ ".ArticleAuthor",
44
+ ".byline",
45
+ ];
46
+
47
+ const AUTHOR_MAX_LENGTH = 300;
48
+ const BYLINE_REGEX = /^[\n\s]*By\s*:?\s*/i;
49
+ const CLEAN_AUTHOR_RE = /^\s*(posted |written )?by\s*:?\s*(.*)/i;
50
+
51
+ /**
52
+ * Extracts the author from the document and validates it as a human name
53
+ *
54
+ * @param {Document} document
55
+ * @returns {object|null} author_cite, author_short, author_type - or null if no valid author found
56
+ */
57
+ export function extractAuthor(document) {
58
+
59
+
60
+ // 1. Check meta tags
61
+ for (const tag of AUTHOR_META_TAGS) {
62
+ const metaElement = document.querySelector(
63
+ `meta[name="${tag}"], meta[property="${tag}"]`
64
+ );
65
+ if (metaElement) {
66
+ const author = extractAndValidateHumanName(
67
+ metaElement.getAttribute("content")
68
+ );
69
+ if (author) return author;
70
+ }
71
+ }
72
+
73
+ // 2. Check selectors
74
+ for (const selector of AUTHOR_SELECTORS) {
75
+ const element = document.querySelector(selector);
76
+ if (element) {
77
+ const author = extractAndValidateHumanName(element.textContent);
78
+ if (author) return author;
79
+ }
80
+ }
81
+
82
+ // 4. Broader search in divs and spans
83
+ const elements = [
84
+ ...document.getElementsByTagName("div"),
85
+ ...document.getElementsByTagName("span"),
86
+ ];
87
+ for (const element of elements) {
88
+ if (
89
+ element.id.match(/author|byline/i) ||
90
+ element?.className?.match(/author|byline/i)
91
+ ) {
92
+ const author = extractAndValidateHumanName(element.innerText);
93
+ if (author) return author;
94
+ }
95
+ }
96
+
97
+
98
+ return null;
99
+ }
100
+
101
+ // Helper function to clean and validate author string
102
+ const validateAuthor = (author) => {
103
+ if (!author) return null;
104
+
105
+ if (author.startsWith("@")) author = author.slice(1);
106
+ if (author.startsWith("http")) return null;
107
+
108
+ author = author?.replace(CLEAN_AUTHOR_RE, "$2").trim();
109
+ return author.length > 0 && author.length < AUTHOR_MAX_LENGTH
110
+ ? author
111
+ : null;
112
+ };
113
+
114
+ // Function to extract and validate human name
115
+ const extractAndValidateHumanName = (author) => {
116
+ const validatedAuthor = validateAuthor(author);
117
+ if (validatedAuthor) {
118
+ return extractHumanName(validatedAuthor, {
119
+ formatCiteShortenAuthor: false
120
+ });
121
+ }
122
+ return null;
123
+ };
124
+
125
+
@@ -0,0 +1,97 @@
1
+ /**
2
+ * @fileoverview Orchestrator for extracting high-quality citations (author, date, title, source) from HTML.
3
+ * Validates names and sources against known patterns and benchmarks.
4
+ */
5
+ import { parseHTML } from "linkedom";
6
+ import { extractAuthor } from "./extract-author";
7
+ import { extractDateQuick } from "./extract-date/extract-date-quick";
8
+ import { extractDate } from "./extract-date/extract-date";
9
+ import { extractSource } from "./extract-source";
10
+ import { extractTitle } from "./extract-title";
11
+ import { extractCiteFromMetadata } from "./metadata-to-cite";
12
+ import { extractHumanName } from "./human-names-recognize";
13
+ import { parseDate } from "chrono-node";
14
+
15
+ export interface ExtractCiteResult {
16
+ author?: string;
17
+ author_cite?: string;
18
+ date?: string;
19
+ title?: string;
20
+ source?: string;
21
+ }
22
+
23
+ export interface ExtractCiteOptions {
24
+ url?: string;
25
+ }
26
+
27
+ /**
28
+ * ### \u1f4da\u1f48e Extract Expert Excerpt
29
+ * <img width="350px" src="https://i.imgur.com/4GOOM9s.jpeg" />
30
+ *
31
+ * Extract author, date, source, and title from HTML using meta tags
32
+ * and common class names. Validates human name from author string to check
33
+ * against common list of 90k first names, last names,and organizations to infer
34
+ * if it should be reversed starting by author last name (accounting for affixes/titles),
35
+ * since organizations are not reversed.
36
+ * [Article Extraction Benchmark](https://github.com/scrapinghub/article-extraction-benchmark?tab=readme-ov-file#results)
37
+ * @param {Document | string} document dom object or html string with article content
38
+ * @param {ExtractCiteOptions} [options={}]
39
+ * @returns {ExtractCiteResult | null} An object containing extracted citation information.
40
+ * @category Extract
41
+ * @author [vtempest (2025)](https://github.com/vtempest)
42
+ */
43
+ export function extractCite(
44
+ document: Document | string,
45
+ options: ExtractCiteOptions = {}
46
+ ) {
47
+ const { url = "" } = options;
48
+
49
+ if (!document) return null;
50
+
51
+ //if passing in html string, convert to dom object
52
+ if (typeof document === "string") document = parseHTML(document)?.document;
53
+
54
+ var { author, date, title, source } = extractCiteFromMetadata(document);
55
+
56
+ if (author?.length < 3 || author?.length > 100) author = null;
57
+
58
+ var { author_cite, author_short, author_type } =
59
+ extractAuthor(document) || extractHumanName(author);
60
+
61
+ date = extractDateQuick(document, url) || date;
62
+
63
+ //extract from ways of writing dates in natural language into standard format
64
+ date = parseDate(date)?.toISOString().split("T")[0] || null;
65
+
66
+ if (!date)
67
+ try {
68
+ date = extractDate(
69
+ document,
70
+ true,
71
+ true,
72
+ "%Y-%m-%d",
73
+ url,
74
+ false,
75
+ new Date("2002-01-01")
76
+ );
77
+ } catch (e) {
78
+ console.log(e);
79
+ }
80
+
81
+ date = parseDate(date)?.toISOString().split("T")[0] || null;
82
+
83
+ title = extractTitle(document) || title;
84
+ source = extractSource(document) || source;
85
+
86
+ // URL TO SOURCE
87
+ if (!source && url?.length > 20)
88
+ source = url
89
+ .split("//")[1]
90
+ .split("/")[0]
91
+ ?.replace("www.", "")
92
+ .split(" ")
93
+ .map((word) => word[0].toUpperCase() + word.slice(1))
94
+ .join(" ");
95
+
96
+ return { author, author_cite, date, title, source };
97
+ }