@debriefer/sources 1.0.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.
- package/README.md +59 -0
- package/dist/__tests__/archives/chronicling-america.test.d.ts +8 -0
- package/dist/__tests__/archives/chronicling-america.test.d.ts.map +1 -0
- package/dist/__tests__/archives/chronicling-america.test.js +151 -0
- package/dist/__tests__/archives/chronicling-america.test.js.map +1 -0
- package/dist/__tests__/archives/europeana.test.d.ts +8 -0
- package/dist/__tests__/archives/europeana.test.d.ts.map +1 -0
- package/dist/__tests__/archives/europeana.test.js +200 -0
- package/dist/__tests__/archives/europeana.test.js.map +1 -0
- package/dist/__tests__/archives/internet-archive.test.d.ts +8 -0
- package/dist/__tests__/archives/internet-archive.test.d.ts.map +1 -0
- package/dist/__tests__/archives/internet-archive.test.js +189 -0
- package/dist/__tests__/archives/internet-archive.test.js.map +1 -0
- package/dist/__tests__/archives/trove.test.d.ts +8 -0
- package/dist/__tests__/archives/trove.test.d.ts.map +1 -0
- package/dist/__tests__/archives/trove.test.js +202 -0
- package/dist/__tests__/archives/trove.test.js.map +1 -0
- package/dist/__tests__/books/google-books.test.d.ts +8 -0
- package/dist/__tests__/books/google-books.test.d.ts.map +1 -0
- package/dist/__tests__/books/google-books.test.js +221 -0
- package/dist/__tests__/books/google-books.test.js.map +1 -0
- package/dist/__tests__/books/open-library.test.d.ts +8 -0
- package/dist/__tests__/books/open-library.test.d.ts.map +1 -0
- package/dist/__tests__/books/open-library.test.js +159 -0
- package/dist/__tests__/books/open-library.test.js.map +1 -0
- package/dist/__tests__/news/guardian.test.d.ts +9 -0
- package/dist/__tests__/news/guardian.test.d.ts.map +1 -0
- package/dist/__tests__/news/guardian.test.js +224 -0
- package/dist/__tests__/news/guardian.test.js.map +1 -0
- package/dist/__tests__/news/nytimes.test.d.ts +9 -0
- package/dist/__tests__/news/nytimes.test.d.ts.map +1 -0
- package/dist/__tests__/news/nytimes.test.js +271 -0
- package/dist/__tests__/news/nytimes.test.js.map +1 -0
- package/dist/__tests__/news/site-search-source.test.d.ts +9 -0
- package/dist/__tests__/news/site-search-source.test.d.ts.map +1 -0
- package/dist/__tests__/news/site-search-source.test.js +342 -0
- package/dist/__tests__/news/site-search-source.test.js.map +1 -0
- package/dist/__tests__/obituary/find-a-grave.test.d.ts +8 -0
- package/dist/__tests__/obituary/find-a-grave.test.d.ts.map +1 -0
- package/dist/__tests__/obituary/find-a-grave.test.js +238 -0
- package/dist/__tests__/obituary/find-a-grave.test.js.map +1 -0
- package/dist/__tests__/shared/duckduckgo-search.test.d.ts +9 -0
- package/dist/__tests__/shared/duckduckgo-search.test.d.ts.map +1 -0
- package/dist/__tests__/shared/duckduckgo-search.test.js +218 -0
- package/dist/__tests__/shared/duckduckgo-search.test.js.map +1 -0
- package/dist/__tests__/shared/fetch-page.test.d.ts +9 -0
- package/dist/__tests__/shared/fetch-page.test.d.ts.map +1 -0
- package/dist/__tests__/shared/fetch-page.test.js +281 -0
- package/dist/__tests__/shared/fetch-page.test.js.map +1 -0
- package/dist/__tests__/shared/html-utils.test.d.ts +2 -0
- package/dist/__tests__/shared/html-utils.test.d.ts.map +1 -0
- package/dist/__tests__/shared/html-utils.test.js +169 -0
- package/dist/__tests__/shared/html-utils.test.js.map +1 -0
- package/dist/__tests__/shared/readability-extract.test.d.ts +2 -0
- package/dist/__tests__/shared/readability-extract.test.d.ts.map +1 -0
- package/dist/__tests__/shared/readability-extract.test.js +107 -0
- package/dist/__tests__/shared/readability-extract.test.js.map +1 -0
- package/dist/__tests__/shared/sanitize-text.test.d.ts +2 -0
- package/dist/__tests__/shared/sanitize-text.test.d.ts.map +1 -0
- package/dist/__tests__/shared/sanitize-text.test.js +77 -0
- package/dist/__tests__/shared/sanitize-text.test.js.map +1 -0
- package/dist/__tests__/shared/search-utils.test.d.ts +2 -0
- package/dist/__tests__/shared/search-utils.test.d.ts.map +1 -0
- package/dist/__tests__/shared/search-utils.test.js +26 -0
- package/dist/__tests__/shared/search-utils.test.js.map +1 -0
- package/dist/__tests__/structured/wikidata.test.d.ts +9 -0
- package/dist/__tests__/structured/wikidata.test.d.ts.map +1 -0
- package/dist/__tests__/structured/wikidata.test.js +509 -0
- package/dist/__tests__/structured/wikidata.test.js.map +1 -0
- package/dist/__tests__/structured/wikipedia.test.d.ts +9 -0
- package/dist/__tests__/structured/wikipedia.test.d.ts.map +1 -0
- package/dist/__tests__/structured/wikipedia.test.js +643 -0
- package/dist/__tests__/structured/wikipedia.test.js.map +1 -0
- package/dist/__tests__/web-search/base.test.d.ts +9 -0
- package/dist/__tests__/web-search/base.test.d.ts.map +1 -0
- package/dist/__tests__/web-search/base.test.js +622 -0
- package/dist/__tests__/web-search/base.test.js.map +1 -0
- package/dist/__tests__/web-search/bing.test.d.ts +10 -0
- package/dist/__tests__/web-search/bing.test.d.ts.map +1 -0
- package/dist/__tests__/web-search/bing.test.js +277 -0
- package/dist/__tests__/web-search/bing.test.js.map +1 -0
- package/dist/__tests__/web-search/brave.test.d.ts +10 -0
- package/dist/__tests__/web-search/brave.test.d.ts.map +1 -0
- package/dist/__tests__/web-search/brave.test.js +264 -0
- package/dist/__tests__/web-search/brave.test.js.map +1 -0
- package/dist/__tests__/web-search/duckduckgo.test.d.ts +10 -0
- package/dist/__tests__/web-search/duckduckgo.test.d.ts.map +1 -0
- package/dist/__tests__/web-search/duckduckgo.test.js +107 -0
- package/dist/__tests__/web-search/duckduckgo.test.js.map +1 -0
- package/dist/__tests__/web-search/google.test.d.ts +9 -0
- package/dist/__tests__/web-search/google.test.d.ts.map +1 -0
- package/dist/__tests__/web-search/google.test.js +189 -0
- package/dist/__tests__/web-search/google.test.js.map +1 -0
- package/dist/archives/chronicling-america.d.ts +33 -0
- package/dist/archives/chronicling-america.d.ts.map +1 -0
- package/dist/archives/chronicling-america.js +85 -0
- package/dist/archives/chronicling-america.js.map +1 -0
- package/dist/archives/europeana.d.ts +37 -0
- package/dist/archives/europeana.d.ts.map +1 -0
- package/dist/archives/europeana.js +92 -0
- package/dist/archives/europeana.js.map +1 -0
- package/dist/archives/internet-archive.d.ts +32 -0
- package/dist/archives/internet-archive.d.ts.map +1 -0
- package/dist/archives/internet-archive.js +90 -0
- package/dist/archives/internet-archive.js.map +1 -0
- package/dist/archives/trove.d.ts +37 -0
- package/dist/archives/trove.d.ts.map +1 -0
- package/dist/archives/trove.js +97 -0
- package/dist/archives/trove.js.map +1 -0
- package/dist/books/google-books.d.ts +48 -0
- package/dist/books/google-books.d.ts.map +1 -0
- package/dist/books/google-books.js +111 -0
- package/dist/books/google-books.js.map +1 -0
- package/dist/books/open-library.d.ts +44 -0
- package/dist/books/open-library.d.ts.map +1 -0
- package/dist/books/open-library.js +103 -0
- package/dist/books/open-library.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/news/guardian.d.ts +51 -0
- package/dist/news/guardian.d.ts.map +1 -0
- package/dist/news/guardian.js +131 -0
- package/dist/news/guardian.js.map +1 -0
- package/dist/news/nytimes.d.ts +27 -0
- package/dist/news/nytimes.d.ts.map +1 -0
- package/dist/news/nytimes.js +104 -0
- package/dist/news/nytimes.js.map +1 -0
- package/dist/news/site-search-source.d.ts +89 -0
- package/dist/news/site-search-source.d.ts.map +1 -0
- package/dist/news/site-search-source.js +182 -0
- package/dist/news/site-search-source.js.map +1 -0
- package/dist/news/sources.d.ts +52 -0
- package/dist/news/sources.d.ts.map +1 -0
- package/dist/news/sources.js +276 -0
- package/dist/news/sources.js.map +1 -0
- package/dist/obituary/find-a-grave.d.ts +43 -0
- package/dist/obituary/find-a-grave.d.ts.map +1 -0
- package/dist/obituary/find-a-grave.js +173 -0
- package/dist/obituary/find-a-grave.js.map +1 -0
- package/dist/shared/duckduckgo-search.d.ts +86 -0
- package/dist/shared/duckduckgo-search.d.ts.map +1 -0
- package/dist/shared/duckduckgo-search.js +218 -0
- package/dist/shared/duckduckgo-search.js.map +1 -0
- package/dist/shared/fetch-page.d.ts +50 -0
- package/dist/shared/fetch-page.d.ts.map +1 -0
- package/dist/shared/fetch-page.js +212 -0
- package/dist/shared/fetch-page.js.map +1 -0
- package/dist/shared/html-utils.d.ts +99 -0
- package/dist/shared/html-utils.d.ts.map +1 -0
- package/dist/shared/html-utils.js +246 -0
- package/dist/shared/html-utils.js.map +1 -0
- package/dist/shared/readability-extract.d.ts +33 -0
- package/dist/shared/readability-extract.d.ts.map +1 -0
- package/dist/shared/readability-extract.js +45 -0
- package/dist/shared/readability-extract.js.map +1 -0
- package/dist/shared/sanitize-text.d.ts +24 -0
- package/dist/shared/sanitize-text.d.ts.map +1 -0
- package/dist/shared/sanitize-text.js +49 -0
- package/dist/shared/sanitize-text.js.map +1 -0
- package/dist/shared/search-utils.d.ts +18 -0
- package/dist/shared/search-utils.d.ts.map +1 -0
- package/dist/shared/search-utils.js +20 -0
- package/dist/shared/search-utils.js.map +1 -0
- package/dist/structured/wikidata.d.ts +128 -0
- package/dist/structured/wikidata.d.ts.map +1 -0
- package/dist/structured/wikidata.js +361 -0
- package/dist/structured/wikidata.js.map +1 -0
- package/dist/structured/wikipedia.d.ts +184 -0
- package/dist/structured/wikipedia.d.ts.map +1 -0
- package/dist/structured/wikipedia.js +275 -0
- package/dist/structured/wikipedia.js.map +1 -0
- package/dist/web-search/base.d.ts +128 -0
- package/dist/web-search/base.d.ts.map +1 -0
- package/dist/web-search/base.js +251 -0
- package/dist/web-search/base.js.map +1 -0
- package/dist/web-search/bing.d.ts +21 -0
- package/dist/web-search/bing.d.ts.map +1 -0
- package/dist/web-search/bing.js +53 -0
- package/dist/web-search/bing.js.map +1 -0
- package/dist/web-search/brave.d.ts +21 -0
- package/dist/web-search/brave.d.ts.map +1 -0
- package/dist/web-search/brave.js +56 -0
- package/dist/web-search/brave.js.map +1 -0
- package/dist/web-search/duckduckgo.d.ts +15 -0
- package/dist/web-search/duckduckgo.d.ts.map +1 -0
- package/dist/web-search/duckduckgo.js +21 -0
- package/dist/web-search/duckduckgo.js.map +1 -0
- package/dist/web-search/google.d.ts +24 -0
- package/dist/web-search/google.d.ts.map +1 -0
- package/dist/web-search/google.js +48 -0
- package/dist/web-search/google.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Final safety-net text sanitization.
|
|
3
|
+
*
|
|
4
|
+
* Runs on source text before AI prompt assembly, catching remaining
|
|
5
|
+
* artifacts regardless of source (Wikipedia footnotes, navigation text,
|
|
6
|
+
* boilerplate phrases, etc.). This is the last line of defense against
|
|
7
|
+
* noisy input reaching the synthesis step.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Sanitize source text before sending to an AI model for synthesis.
|
|
11
|
+
*
|
|
12
|
+
* Catches remaining artifacts from any source:
|
|
13
|
+
* - Wikipedia citation markers ([1], [2][3])
|
|
14
|
+
* - Editorial tags ([edit], [citation needed])
|
|
15
|
+
* - Footnote reference lines (lines starting with ^)
|
|
16
|
+
* - Navigation-like pipe-separated text (e.g., "News | Sports | Weather")
|
|
17
|
+
* - Common web boilerplate (sign in, subscribe, cookie policy)
|
|
18
|
+
* - Excess whitespace and blank lines
|
|
19
|
+
*
|
|
20
|
+
* @param text - Raw source text to sanitize
|
|
21
|
+
* @returns Cleaned text ready for prompt assembly
|
|
22
|
+
*/
|
|
23
|
+
export declare function sanitizeSourceText(text: string): string;
|
|
24
|
+
//# sourceMappingURL=sanitize-text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize-text.d.ts","sourceRoot":"","sources":["../../src/shared/sanitize-text.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAsCvD"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Final safety-net text sanitization.
|
|
3
|
+
*
|
|
4
|
+
* Runs on source text before AI prompt assembly, catching remaining
|
|
5
|
+
* artifacts regardless of source (Wikipedia footnotes, navigation text,
|
|
6
|
+
* boilerplate phrases, etc.). This is the last line of defense against
|
|
7
|
+
* noisy input reaching the synthesis step.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Sanitize source text before sending to an AI model for synthesis.
|
|
11
|
+
*
|
|
12
|
+
* Catches remaining artifacts from any source:
|
|
13
|
+
* - Wikipedia citation markers ([1], [2][3])
|
|
14
|
+
* - Editorial tags ([edit], [citation needed])
|
|
15
|
+
* - Footnote reference lines (lines starting with ^)
|
|
16
|
+
* - Navigation-like pipe-separated text (e.g., "News | Sports | Weather")
|
|
17
|
+
* - Common web boilerplate (sign in, subscribe, cookie policy)
|
|
18
|
+
* - Excess whitespace and blank lines
|
|
19
|
+
*
|
|
20
|
+
* @param text - Raw source text to sanitize
|
|
21
|
+
* @returns Cleaned text ready for prompt assembly
|
|
22
|
+
*/
|
|
23
|
+
export function sanitizeSourceText(text) {
|
|
24
|
+
let cleaned = text;
|
|
25
|
+
// 1. Strip Wikipedia citation markers (with and without spaces)
|
|
26
|
+
cleaned = cleaned.replace(/\[\s*\d+\s*\]/g, "");
|
|
27
|
+
// 2. Strip [edit], [citation needed], etc.
|
|
28
|
+
cleaned = cleaned.replace(/\[\s*(?:edit|citation needed|clarification needed|when\?|who\?|where\?|dubious|discuss|further explanation needed|original research\??|not in citation given|failed verification|unreliable source\??|needs? update|verification needed)\s*\]/gi, "");
|
|
29
|
+
// 3. Strip Wikipedia footnote blocks (lines starting with ^)
|
|
30
|
+
cleaned = cleaned.replace(/^\s*\^[^\n]+$/gm, "");
|
|
31
|
+
// 4. Strip navigation-like text patterns:
|
|
32
|
+
// pipe-separated short items (e.g. "News | Sports | Weather | ...")
|
|
33
|
+
/* eslint-disable-next-line security/detect-unsafe-regex --
|
|
34
|
+
Bounded quantifier on short pipe-separated nav text; no backtracking risk */
|
|
35
|
+
const navPattern = /^(?:[A-Z][a-z]+\s*\|\s*){3,}[A-Z][a-z]+\s*$/gm;
|
|
36
|
+
cleaned = cleaned.replace(navPattern, "");
|
|
37
|
+
// 5. Strip common boilerplate phrases
|
|
38
|
+
cleaned = cleaned.replace(/\b(?:Sign [Ii]n|Sign [Uu]p|Subscribe|Newsletter|Cookie [Pp]olicy|Privacy [Pp]olicy|Terms (?:of|and) (?:Service|Use)|Accept (?:All )?Cookies|Manage Preferences)\b[^\n]*/g, "");
|
|
39
|
+
// 6. Collapse whitespace
|
|
40
|
+
cleaned = cleaned.replace(/\n{3,}/g, "\n\n");
|
|
41
|
+
cleaned = cleaned.replace(/[ \t]+/g, " ");
|
|
42
|
+
cleaned = cleaned
|
|
43
|
+
.split("\n")
|
|
44
|
+
.map((l) => l.trim())
|
|
45
|
+
.filter((l) => l.length > 0)
|
|
46
|
+
.join("\n");
|
|
47
|
+
return cleaned.trim();
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=sanitize-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize-text.js","sourceRoot":"","sources":["../../src/shared/sanitize-text.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,IAAI,OAAO,GAAG,IAAI,CAAA;IAElB,gEAAgE;IAChE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAA;IAE/C,2CAA2C;IAC3C,OAAO,GAAG,OAAO,CAAC,OAAO,CACvB,iPAAiP,EACjP,EAAE,CACH,CAAA;IAED,6DAA6D;IAC7D,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAA;IAEhD,0CAA0C;IAC1C,uEAAuE;IACvE;mFAC+E;IAC/E,MAAM,UAAU,GAAG,+CAA+C,CAAA;IAClE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;IAEzC,sCAAsC;IACtC,OAAO,GAAG,OAAO,CAAC,OAAO,CACvB,0KAA0K,EAC1K,EAAE,CACH,CAAA;IAED,yBAAyB;IACzB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;IAC5C,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAA;IACzC,OAAO,GAAG,OAAO;SACd,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;SAC3B,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,OAAO,OAAO,CAAC,IAAI,EAAE,CAAA;AACvB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search query utilities.
|
|
3
|
+
*
|
|
4
|
+
* Helpers for splitting and normalizing search strings, used by sources
|
|
5
|
+
* that need multi-word ILIKE matching or search term tokenization.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Split a search string into individual words for multi-word matching.
|
|
9
|
+
*
|
|
10
|
+
* Trims leading/trailing whitespace, splits on one or more whitespace
|
|
11
|
+
* characters, and filters out empty strings. Returns an empty array
|
|
12
|
+
* for blank or whitespace-only input.
|
|
13
|
+
*
|
|
14
|
+
* @param query - Search string to split
|
|
15
|
+
* @returns Array of individual search words
|
|
16
|
+
*/
|
|
17
|
+
export declare function splitSearchWords(query: string): string[];
|
|
18
|
+
//# sourceMappingURL=search-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-utils.d.ts","sourceRoot":"","sources":["../../src/shared/search-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAExD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search query utilities.
|
|
3
|
+
*
|
|
4
|
+
* Helpers for splitting and normalizing search strings, used by sources
|
|
5
|
+
* that need multi-word ILIKE matching or search term tokenization.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Split a search string into individual words for multi-word matching.
|
|
9
|
+
*
|
|
10
|
+
* Trims leading/trailing whitespace, splits on one or more whitespace
|
|
11
|
+
* characters, and filters out empty strings. Returns an empty array
|
|
12
|
+
* for blank or whitespace-only input.
|
|
13
|
+
*
|
|
14
|
+
* @param query - Search string to split
|
|
15
|
+
* @returns Array of individual search words
|
|
16
|
+
*/
|
|
17
|
+
export function splitSearchWords(query) {
|
|
18
|
+
return query.trim().split(/\s+/).filter(Boolean);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=search-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-utils.js","sourceRoot":"","sources":["../../src/shared/search-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;AAClD,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic Wikidata SPARQL source for structured data.
|
|
3
|
+
*
|
|
4
|
+
* Queries Wikidata's SPARQL endpoint to retrieve structured facts about
|
|
5
|
+
* a research subject. Domain-agnostic: consumers customize the SPARQL query
|
|
6
|
+
* via the `queryBuilder` option and control what properties to extract.
|
|
7
|
+
*
|
|
8
|
+
* Default query searches by name with an optional birth year filter,
|
|
9
|
+
* returning basic identity properties (labels, descriptions, Wikipedia links).
|
|
10
|
+
* Consumers can provide a custom `queryBuilder` for domain-specific properties
|
|
11
|
+
* (e.g., cause of death P509, education P69, occupation P106).
|
|
12
|
+
*/
|
|
13
|
+
import { BaseResearchSource, ReliabilityTier, type BaseSourceOptions, type ResearchSubject, type RawFinding } from "@debriefer/core";
|
|
14
|
+
/**
|
|
15
|
+
* Escape a string for use in a SPARQL string literal.
|
|
16
|
+
* Escapes backslashes first, then double quotes, to prevent injection.
|
|
17
|
+
*/
|
|
18
|
+
export declare function escapeSparql(str: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Check if a Wikidata label value is valid (not a URL, blank node, or raw entity ID).
|
|
21
|
+
* Wikidata sometimes returns genid URLs instead of actual labels when the value
|
|
22
|
+
* is a complex statement or blank node.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isValidLabel(value: string | undefined): value is string;
|
|
25
|
+
/**
|
|
26
|
+
* Get a valid label value or null if invalid.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getValidLabel(value: string | undefined): string | null;
|
|
29
|
+
/**
|
|
30
|
+
* Filter a comma-separated string of labels, removing invalid entries.
|
|
31
|
+
* Returns null if no valid labels remain.
|
|
32
|
+
*/
|
|
33
|
+
export declare function filterValidLabels(concatenated: string | undefined): string | null;
|
|
34
|
+
/** A single binding (row) from a SPARQL response */
|
|
35
|
+
export interface SparqlBinding {
|
|
36
|
+
[key: string]: {
|
|
37
|
+
value: string;
|
|
38
|
+
type?: string;
|
|
39
|
+
} | undefined;
|
|
40
|
+
}
|
|
41
|
+
/** Standard SPARQL JSON response format */
|
|
42
|
+
export interface SparqlResponse {
|
|
43
|
+
results: {
|
|
44
|
+
bindings: SparqlBinding[];
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Function that builds a SPARQL query for a given research subject.
|
|
49
|
+
* The subject's `context` bag may contain domain-specific fields
|
|
50
|
+
* (e.g., `birthYear`, `deathYear`) for more targeted queries.
|
|
51
|
+
*/
|
|
52
|
+
export type SparqlQueryBuilder = (subject: ResearchSubject) => string;
|
|
53
|
+
/**
|
|
54
|
+
* Function that parses SPARQL bindings into a text string and confidence score.
|
|
55
|
+
* Returns null if no relevant data was found in the bindings.
|
|
56
|
+
*/
|
|
57
|
+
export type SparqlResultParser = (bindings: SparqlBinding[], subject: ResearchSubject) => {
|
|
58
|
+
text: string;
|
|
59
|
+
confidence: number;
|
|
60
|
+
metadata?: Record<string, unknown>;
|
|
61
|
+
} | null;
|
|
62
|
+
/** Options for the Wikidata source */
|
|
63
|
+
export interface WikidataOptions extends BaseSourceOptions {
|
|
64
|
+
/** Custom SPARQL query builder. Default searches by name. */
|
|
65
|
+
queryBuilder?: SparqlQueryBuilder;
|
|
66
|
+
/** Custom result parser. Default formats all bindings as key: value text. */
|
|
67
|
+
resultParser?: SparqlResultParser;
|
|
68
|
+
/** User-Agent string for Wikidata API requests. */
|
|
69
|
+
userAgent?: string;
|
|
70
|
+
/** Maximum retries on 429/5xx responses (default: 3) */
|
|
71
|
+
maxRetries?: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Wikidata SPARQL source for structured research data.
|
|
75
|
+
*
|
|
76
|
+
* Queries Wikidata for structured facts about a subject. Consumers customize
|
|
77
|
+
* what data is queried via `queryBuilder` and how results are parsed via
|
|
78
|
+
* `resultParser`. Ships with sensible defaults that search by person name.
|
|
79
|
+
*/
|
|
80
|
+
export declare class WikidataSource extends BaseResearchSource<ResearchSubject> {
|
|
81
|
+
readonly name = "Wikidata";
|
|
82
|
+
readonly type = "wikidata";
|
|
83
|
+
readonly reliabilityTier = ReliabilityTier.STRUCTURED_DATA;
|
|
84
|
+
readonly domain = "query.wikidata.org";
|
|
85
|
+
readonly isFree = true;
|
|
86
|
+
readonly estimatedCostPerQuery = 0;
|
|
87
|
+
private queryBuilder;
|
|
88
|
+
private resultParser;
|
|
89
|
+
private userAgent;
|
|
90
|
+
private maxRetries;
|
|
91
|
+
constructor(options?: WikidataOptions);
|
|
92
|
+
protected fetchResult(subject: ResearchSubject, signal: AbortSignal): Promise<RawFinding | null>;
|
|
93
|
+
/**
|
|
94
|
+
* Build the search query for cache key generation.
|
|
95
|
+
* Uses the actual SPARQL query (truncated) so different queryBuilders
|
|
96
|
+
* don't collide in cache. Falls back to name + birth year for the default builder.
|
|
97
|
+
*/
|
|
98
|
+
buildQuery(subject: ResearchSubject): string;
|
|
99
|
+
/**
|
|
100
|
+
* Fetch SPARQL results with retry on 429/5xx responses.
|
|
101
|
+
* Uses exponential backoff: 2s, 4s, 8s.
|
|
102
|
+
*/
|
|
103
|
+
private fetchWithRetry;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Create a Wikidata SPARQL source instance.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* // Default: search by name, return basic info
|
|
111
|
+
* const source = wikidata()
|
|
112
|
+
*
|
|
113
|
+
* // Custom: death-specific query
|
|
114
|
+
* const deathSource = wikidata({
|
|
115
|
+
* queryBuilder: (subject) => {
|
|
116
|
+
* const name = escapeSparql(subject.name)
|
|
117
|
+
* const deathYear = subject.context?.deathYear as number
|
|
118
|
+
* return `SELECT ... WHERE { ... }`
|
|
119
|
+
* },
|
|
120
|
+
* resultParser: (bindings, subject) => {
|
|
121
|
+
* // Extract cause of death, manner, location...
|
|
122
|
+
* return { text: "...", confidence: 0.8 }
|
|
123
|
+
* },
|
|
124
|
+
* })
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export declare function wikidata(options?: WikidataOptions): WikidataSource;
|
|
128
|
+
//# sourceMappingURL=wikidata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wikidata.d.ts","sourceRoot":"","sources":["../../src/structured/wikidata.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,iBAAiB,CAAA;AAexB;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOhD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,IAAI,MAAM,CAMvE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAEtE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAIjF;AAMD,oDAAoD;AACpD,MAAM,WAAW,aAAa;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAA;CAC5D;AAED,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE;QACP,QAAQ,EAAE,aAAa,EAAE,CAAA;KAC1B,CAAA;CACF;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,eAAe,KAAK,MAAM,CAAA;AAErE;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAC/B,QAAQ,EAAE,aAAa,EAAE,EACzB,OAAO,EAAE,eAAe,KACrB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,IAAI,CAAA;AAEpF,sCAAsC;AACtC,MAAM,WAAW,eAAgB,SAAQ,iBAAiB;IACxD,6DAA6D;IAC7D,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC,6EAA6E;IAC7E,YAAY,CAAC,EAAE,kBAAkB,CAAA;IACjC,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAyID;;;;;;GAMG;AACH,qBAAa,cAAe,SAAQ,kBAAkB,CAAC,eAAe,CAAC;IACrE,QAAQ,CAAC,IAAI,cAAa;IAC1B,QAAQ,CAAC,IAAI,cAAa;IAC1B,QAAQ,CAAC,eAAe,mCAAkC;IAC1D,QAAQ,CAAC,MAAM,wBAAuB;IACtC,QAAQ,CAAC,MAAM,QAAO;IACtB,QAAQ,CAAC,qBAAqB,KAAI;IAElC,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,UAAU,CAAQ;gBAEd,OAAO,GAAE,eAAoB;cAQzB,WAAW,CACzB,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IA4B7B;;;;OAIG;IACM,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM;IAqBrD;;;OAGG;YACW,cAAc;CA+D7B;AAMD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,cAAc,CAElE"}
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic Wikidata SPARQL source for structured data.
|
|
3
|
+
*
|
|
4
|
+
* Queries Wikidata's SPARQL endpoint to retrieve structured facts about
|
|
5
|
+
* a research subject. Domain-agnostic: consumers customize the SPARQL query
|
|
6
|
+
* via the `queryBuilder` option and control what properties to extract.
|
|
7
|
+
*
|
|
8
|
+
* Default query searches by name with an optional birth year filter,
|
|
9
|
+
* returning basic identity properties (labels, descriptions, Wikipedia links).
|
|
10
|
+
* Consumers can provide a custom `queryBuilder` for domain-specific properties
|
|
11
|
+
* (e.g., cause of death P509, education P69, occupation P106).
|
|
12
|
+
*/
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
14
|
+
import { BaseResearchSource, ReliabilityTier, } from "@debriefer/core";
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Constants
|
|
17
|
+
// ============================================================================
|
|
18
|
+
const WIKIDATA_ENDPOINT = "https://query.wikidata.org/sparql";
|
|
19
|
+
const MAX_RETRIES = 3;
|
|
20
|
+
const RETRY_BASE_DELAY_MS = 2000;
|
|
21
|
+
const DEFAULT_USER_AGENT = "debriefer/0.1.0 (https://github.com/chenders/debriefer)";
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// SPARQL Helpers
|
|
24
|
+
// ============================================================================
|
|
25
|
+
/**
|
|
26
|
+
* Escape a string for use in a SPARQL string literal.
|
|
27
|
+
* Escapes backslashes first, then double quotes, to prevent injection.
|
|
28
|
+
*/
|
|
29
|
+
export function escapeSparql(str) {
|
|
30
|
+
return str
|
|
31
|
+
.replace(/\\/g, "\\\\")
|
|
32
|
+
.replace(/"/g, '\\"')
|
|
33
|
+
.replace(/\n/g, "\\n")
|
|
34
|
+
.replace(/\r/g, "\\r")
|
|
35
|
+
.replace(/\t/g, "\\t");
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Check if a Wikidata label value is valid (not a URL, blank node, or raw entity ID).
|
|
39
|
+
* Wikidata sometimes returns genid URLs instead of actual labels when the value
|
|
40
|
+
* is a complex statement or blank node.
|
|
41
|
+
*/
|
|
42
|
+
export function isValidLabel(value) {
|
|
43
|
+
if (!value)
|
|
44
|
+
return false;
|
|
45
|
+
if (value.startsWith("http://") || value.startsWith("https://"))
|
|
46
|
+
return false;
|
|
47
|
+
if (value.includes("genid"))
|
|
48
|
+
return false;
|
|
49
|
+
if (/^Q\d+$/.test(value))
|
|
50
|
+
return false;
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get a valid label value or null if invalid.
|
|
55
|
+
*/
|
|
56
|
+
export function getValidLabel(value) {
|
|
57
|
+
return isValidLabel(value) ? value : null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Filter a comma-separated string of labels, removing invalid entries.
|
|
61
|
+
* Returns null if no valid labels remain.
|
|
62
|
+
*/
|
|
63
|
+
export function filterValidLabels(concatenated) {
|
|
64
|
+
if (!concatenated)
|
|
65
|
+
return null;
|
|
66
|
+
const labels = concatenated.split(/\s*,\s*/).filter((label) => isValidLabel(label));
|
|
67
|
+
return labels.length > 0 ? labels.join(", ") : null;
|
|
68
|
+
}
|
|
69
|
+
// ============================================================================
|
|
70
|
+
// Default Query Builder
|
|
71
|
+
// ============================================================================
|
|
72
|
+
/**
|
|
73
|
+
* Default SPARQL query builder that searches for a person by name.
|
|
74
|
+
* If the subject context includes `birthYear`, filters by birth year for disambiguation.
|
|
75
|
+
* Returns label, description, and English Wikipedia article link.
|
|
76
|
+
*/
|
|
77
|
+
function defaultQueryBuilder(subject) {
|
|
78
|
+
const escapedName = escapeSparql(subject.name);
|
|
79
|
+
const rawBirthYear = subject.context?.birthYear;
|
|
80
|
+
const parsedBirthYear = typeof rawBirthYear === "number" && Number.isFinite(rawBirthYear)
|
|
81
|
+
? rawBirthYear
|
|
82
|
+
: typeof rawBirthYear === "string"
|
|
83
|
+
? parseInt(rawBirthYear, 10)
|
|
84
|
+
: undefined;
|
|
85
|
+
const validBirthYear = parsedBirthYear !== undefined && Number.isFinite(parsedBirthYear) ? parsedBirthYear : undefined;
|
|
86
|
+
const birthFilter = validBirthYear
|
|
87
|
+
? `?person wdt:P569 ?birthDate .
|
|
88
|
+
FILTER(YEAR(?birthDate) = ${validBirthYear})`
|
|
89
|
+
: "";
|
|
90
|
+
return `
|
|
91
|
+
SELECT ?person ?personLabel ?personDescription ?article
|
|
92
|
+
WHERE {
|
|
93
|
+
?person wdt:P31 wd:Q5 .
|
|
94
|
+
?person rdfs:label "${escapedName}"@en .
|
|
95
|
+
${birthFilter}
|
|
96
|
+
|
|
97
|
+
OPTIONAL {
|
|
98
|
+
?article schema:about ?person .
|
|
99
|
+
?article schema:isPartOf <https://en.wikipedia.org/> .
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
|
|
103
|
+
}
|
|
104
|
+
LIMIT 5
|
|
105
|
+
`;
|
|
106
|
+
}
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Default Result Parser
|
|
109
|
+
// ============================================================================
|
|
110
|
+
/**
|
|
111
|
+
* Default result parser that extracts person label, description, and Wikipedia URL.
|
|
112
|
+
* Formats them as plain text with a base confidence of 0.5.
|
|
113
|
+
*/
|
|
114
|
+
function defaultResultParser(bindings, subject) {
|
|
115
|
+
if (bindings.length === 0)
|
|
116
|
+
return null;
|
|
117
|
+
// Find the first binding whose label matches the subject name
|
|
118
|
+
for (const binding of bindings) {
|
|
119
|
+
const personName = binding.personLabel?.value ?? "";
|
|
120
|
+
if (!isNameMatch(subject.name, personName))
|
|
121
|
+
continue;
|
|
122
|
+
const lines = [];
|
|
123
|
+
const label = getValidLabel(binding.personLabel?.value);
|
|
124
|
+
const description = binding.personDescription?.value;
|
|
125
|
+
const articleUrl = binding.article?.value;
|
|
126
|
+
if (label)
|
|
127
|
+
lines.push(`Name: ${label}`);
|
|
128
|
+
if (description)
|
|
129
|
+
lines.push(`Description: ${description}`);
|
|
130
|
+
if (articleUrl)
|
|
131
|
+
lines.push(`Wikipedia: ${articleUrl}`);
|
|
132
|
+
// Add all other non-standard fields
|
|
133
|
+
for (const [key, val] of Object.entries(binding)) {
|
|
134
|
+
if (["person", "personLabel", "personDescription", "article"].includes(key))
|
|
135
|
+
continue;
|
|
136
|
+
const labelValue = getValidLabel(val?.value);
|
|
137
|
+
if (labelValue) {
|
|
138
|
+
const readableKey = key
|
|
139
|
+
.replace(/Label$/, "")
|
|
140
|
+
.replace(/([A-Z])/g, " $1")
|
|
141
|
+
.trim();
|
|
142
|
+
lines.push(`${readableKey}: ${labelValue}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (lines.length === 0)
|
|
146
|
+
continue;
|
|
147
|
+
return {
|
|
148
|
+
text: lines.join("\n"),
|
|
149
|
+
confidence: 0.5 + Math.min(0.3, (lines.length - 1) * 0.1),
|
|
150
|
+
metadata: { entityUrl: binding.person?.value, articleUrl },
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
// ============================================================================
|
|
156
|
+
// Name Matching
|
|
157
|
+
// ============================================================================
|
|
158
|
+
/**
|
|
159
|
+
* Check if two names match, handling common variations.
|
|
160
|
+
* Normalizes to lowercase ASCII and checks for exact match, substring
|
|
161
|
+
* containment, or last-name + first-initial match.
|
|
162
|
+
*
|
|
163
|
+
* Note: Normalization strips non-ASCII characters (accents, diacritics),
|
|
164
|
+
* which works for most Western names but may cause false matches for
|
|
165
|
+
* names that differ only in diacritics.
|
|
166
|
+
*/
|
|
167
|
+
function isNameMatch(name1, name2) {
|
|
168
|
+
const normalize = (s) => s.toLowerCase().replace(/[^a-z]/g, "");
|
|
169
|
+
const norm1 = normalize(name1);
|
|
170
|
+
const norm2 = normalize(name2);
|
|
171
|
+
if (norm1 === norm2)
|
|
172
|
+
return true;
|
|
173
|
+
if (norm1.includes(norm2) || norm2.includes(norm1))
|
|
174
|
+
return true;
|
|
175
|
+
// Last name + first initial match (avoids "John Smith" matching "Mary Smith")
|
|
176
|
+
const parts1 = name1.toLowerCase().split(/\s+/);
|
|
177
|
+
const parts2 = name2.toLowerCase().split(/\s+/);
|
|
178
|
+
if (parts1.length < 2 || parts2.length < 2)
|
|
179
|
+
return false;
|
|
180
|
+
const last1 = parts1[parts1.length - 1];
|
|
181
|
+
const last2 = parts2[parts2.length - 1];
|
|
182
|
+
if (last1 !== last2)
|
|
183
|
+
return false;
|
|
184
|
+
// Require first initial to match
|
|
185
|
+
return parts1[0][0] === parts2[0][0];
|
|
186
|
+
}
|
|
187
|
+
// ============================================================================
|
|
188
|
+
// Source Implementation
|
|
189
|
+
// ============================================================================
|
|
190
|
+
/**
|
|
191
|
+
* Wikidata SPARQL source for structured research data.
|
|
192
|
+
*
|
|
193
|
+
* Queries Wikidata for structured facts about a subject. Consumers customize
|
|
194
|
+
* what data is queried via `queryBuilder` and how results are parsed via
|
|
195
|
+
* `resultParser`. Ships with sensible defaults that search by person name.
|
|
196
|
+
*/
|
|
197
|
+
export class WikidataSource extends BaseResearchSource {
|
|
198
|
+
name = "Wikidata";
|
|
199
|
+
type = "wikidata";
|
|
200
|
+
reliabilityTier = ReliabilityTier.STRUCTURED_DATA;
|
|
201
|
+
domain = "query.wikidata.org";
|
|
202
|
+
isFree = true;
|
|
203
|
+
estimatedCostPerQuery = 0;
|
|
204
|
+
queryBuilder;
|
|
205
|
+
resultParser;
|
|
206
|
+
userAgent;
|
|
207
|
+
maxRetries;
|
|
208
|
+
constructor(options = {}) {
|
|
209
|
+
super({ rateLimitMs: 500, ...options });
|
|
210
|
+
this.queryBuilder = options.queryBuilder ?? defaultQueryBuilder;
|
|
211
|
+
this.resultParser = options.resultParser ?? defaultResultParser;
|
|
212
|
+
this.userAgent = options.userAgent ?? DEFAULT_USER_AGENT;
|
|
213
|
+
this.maxRetries = options.maxRetries ?? MAX_RETRIES;
|
|
214
|
+
}
|
|
215
|
+
async fetchResult(subject, signal) {
|
|
216
|
+
const query = this.queryBuilder(subject);
|
|
217
|
+
const data = await this.fetchWithRetry(query, signal);
|
|
218
|
+
if (!data)
|
|
219
|
+
return null;
|
|
220
|
+
const parsed = this.resultParser(data.results.bindings, subject);
|
|
221
|
+
if (!parsed)
|
|
222
|
+
return null;
|
|
223
|
+
// Build URL — use Wikipedia article URL if available, otherwise Wikidata entity
|
|
224
|
+
const entityUrl = parsed.metadata?.entityUrl;
|
|
225
|
+
const articleUrl = parsed.metadata?.articleUrl;
|
|
226
|
+
const url = articleUrl ?? entityUrl;
|
|
227
|
+
return {
|
|
228
|
+
text: parsed.text,
|
|
229
|
+
confidence: parsed.confidence,
|
|
230
|
+
costUsd: 0,
|
|
231
|
+
url,
|
|
232
|
+
publication: "Wikidata",
|
|
233
|
+
metadata: {
|
|
234
|
+
...parsed.metadata,
|
|
235
|
+
sparqlQuery: query,
|
|
236
|
+
bindingCount: data.results.bindings.length,
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Build the search query for cache key generation.
|
|
242
|
+
* Uses the actual SPARQL query (truncated) so different queryBuilders
|
|
243
|
+
* don't collide in cache. Falls back to name + birth year for the default builder.
|
|
244
|
+
*/
|
|
245
|
+
buildQuery(subject) {
|
|
246
|
+
if (this.queryBuilder !== defaultQueryBuilder) {
|
|
247
|
+
// Custom queryBuilder: hash the full SPARQL query for a collision-resistant cache key
|
|
248
|
+
const query = this.queryBuilder(subject);
|
|
249
|
+
return createHash("sha256").update(query).digest("hex").slice(0, 16);
|
|
250
|
+
}
|
|
251
|
+
// Default builder: name + validated birth year (number or numeric string,
|
|
252
|
+
// matching the same parsing logic used in defaultQueryBuilder)
|
|
253
|
+
const rawBirthYear = subject.context?.birthYear;
|
|
254
|
+
let birthYear;
|
|
255
|
+
if (typeof rawBirthYear === "number" && Number.isFinite(rawBirthYear)) {
|
|
256
|
+
birthYear = rawBirthYear;
|
|
257
|
+
}
|
|
258
|
+
else if (typeof rawBirthYear === "string") {
|
|
259
|
+
const parsed = parseInt(rawBirthYear, 10);
|
|
260
|
+
if (Number.isFinite(parsed)) {
|
|
261
|
+
birthYear = parsed;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return birthYear !== undefined ? `${subject.name}:${birthYear}` : subject.name;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Fetch SPARQL results with retry on 429/5xx responses.
|
|
268
|
+
* Uses exponential backoff: 2s, 4s, 8s.
|
|
269
|
+
*/
|
|
270
|
+
async fetchWithRetry(query, signal) {
|
|
271
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
272
|
+
try {
|
|
273
|
+
const response = await fetch(`${WIKIDATA_ENDPOINT}?query=${encodeURIComponent(query)}`, {
|
|
274
|
+
headers: {
|
|
275
|
+
Accept: "application/sparql-results+json",
|
|
276
|
+
"User-Agent": this.userAgent,
|
|
277
|
+
},
|
|
278
|
+
signal,
|
|
279
|
+
});
|
|
280
|
+
if ((response.status === 429 || response.status >= 500) && attempt < this.maxRetries) {
|
|
281
|
+
if (signal.aborted)
|
|
282
|
+
throw new DOMException("Aborted", "AbortError");
|
|
283
|
+
const delay = RETRY_BASE_DELAY_MS * Math.pow(2, attempt);
|
|
284
|
+
await new Promise((resolve, reject) => {
|
|
285
|
+
const onAbort = () => {
|
|
286
|
+
clearTimeout(timer);
|
|
287
|
+
reject(signal.reason);
|
|
288
|
+
};
|
|
289
|
+
const timer = setTimeout(() => {
|
|
290
|
+
signal.removeEventListener("abort", onAbort);
|
|
291
|
+
resolve();
|
|
292
|
+
}, delay);
|
|
293
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
294
|
+
});
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
if (!response.ok) {
|
|
298
|
+
// 404 = subject not found, return null. Other errors should propagate
|
|
299
|
+
// for telemetry recording via BaseResearchSource.lookup()
|
|
300
|
+
if (response.status === 404)
|
|
301
|
+
return null;
|
|
302
|
+
throw new Error(`Wikidata SPARQL request failed: ${response.status} ${response.statusText}`);
|
|
303
|
+
}
|
|
304
|
+
return (await response.json());
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
// Don't retry abort errors
|
|
308
|
+
if (error instanceof DOMException && error.name === "AbortError")
|
|
309
|
+
throw error;
|
|
310
|
+
if (signal.aborted)
|
|
311
|
+
throw new DOMException("Aborted", "AbortError");
|
|
312
|
+
if (attempt < this.maxRetries) {
|
|
313
|
+
const delay = RETRY_BASE_DELAY_MS * Math.pow(2, attempt);
|
|
314
|
+
await new Promise((resolve, reject) => {
|
|
315
|
+
const onAbort = () => {
|
|
316
|
+
clearTimeout(timer);
|
|
317
|
+
reject(signal.reason);
|
|
318
|
+
};
|
|
319
|
+
const timer = setTimeout(() => {
|
|
320
|
+
signal.removeEventListener("abort", onAbort);
|
|
321
|
+
resolve();
|
|
322
|
+
}, delay);
|
|
323
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
324
|
+
});
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// ============================================================================
|
|
334
|
+
// Factory Function
|
|
335
|
+
// ============================================================================
|
|
336
|
+
/**
|
|
337
|
+
* Create a Wikidata SPARQL source instance.
|
|
338
|
+
*
|
|
339
|
+
* @example
|
|
340
|
+
* ```typescript
|
|
341
|
+
* // Default: search by name, return basic info
|
|
342
|
+
* const source = wikidata()
|
|
343
|
+
*
|
|
344
|
+
* // Custom: death-specific query
|
|
345
|
+
* const deathSource = wikidata({
|
|
346
|
+
* queryBuilder: (subject) => {
|
|
347
|
+
* const name = escapeSparql(subject.name)
|
|
348
|
+
* const deathYear = subject.context?.deathYear as number
|
|
349
|
+
* return `SELECT ... WHERE { ... }`
|
|
350
|
+
* },
|
|
351
|
+
* resultParser: (bindings, subject) => {
|
|
352
|
+
* // Extract cause of death, manner, location...
|
|
353
|
+
* return { text: "...", confidence: 0.8 }
|
|
354
|
+
* },
|
|
355
|
+
* })
|
|
356
|
+
* ```
|
|
357
|
+
*/
|
|
358
|
+
export function wikidata(options) {
|
|
359
|
+
return new WikidataSource(options);
|
|
360
|
+
}
|
|
361
|
+
//# sourceMappingURL=wikidata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wikidata.js","sourceRoot":"","sources":["../../src/structured/wikidata.ts"],"names":[],"mappings":"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"}
|