paper-search-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +165 -0
- package/LICENSE +21 -0
- package/README-sc.md +642 -0
- package/README.md +642 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +637 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/ConfigService.d.ts +26 -0
- package/dist/config/ConfigService.d.ts.map +1 -0
- package/dist/config/ConfigService.js +145 -0
- package/dist/config/ConfigService.js.map +1 -0
- package/dist/config/constants.d.ts +140 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +93 -0
- package/dist/config/constants.js.map +1 -0
- package/dist/core/diagnostics.d.ts +43 -0
- package/dist/core/diagnostics.d.ts.map +1 -0
- package/dist/core/diagnostics.js +544 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/handleToolCall.d.ts +8 -0
- package/dist/core/handleToolCall.d.ts.map +1 -0
- package/dist/core/handleToolCall.js +440 -0
- package/dist/core/handleToolCall.js.map +1 -0
- package/dist/core/schemas.d.ts +454 -0
- package/dist/core/schemas.d.ts.map +1 -0
- package/dist/core/schemas.js +322 -0
- package/dist/core/schemas.js.map +1 -0
- package/dist/core/searchers.d.ts +45 -0
- package/dist/core/searchers.d.ts.map +1 -0
- package/dist/core/searchers.js +73 -0
- package/dist/core/searchers.js.map +1 -0
- package/dist/core/tools.d.ts +7 -0
- package/dist/core/tools.d.ts.map +1 -0
- package/dist/core/tools.js +640 -0
- package/dist/core/tools.js.map +1 -0
- package/dist/models/Paper.d.ts +64 -0
- package/dist/models/Paper.d.ts.map +1 -0
- package/dist/models/Paper.js +70 -0
- package/dist/models/Paper.js.map +1 -0
- package/dist/platforms/ArxivSearcher.d.ts +64 -0
- package/dist/platforms/ArxivSearcher.d.ts.map +1 -0
- package/dist/platforms/ArxivSearcher.js +531 -0
- package/dist/platforms/ArxivSearcher.js.map +1 -0
- package/dist/platforms/BioRxivSearcher.d.ts +47 -0
- package/dist/platforms/BioRxivSearcher.d.ts.map +1 -0
- package/dist/platforms/BioRxivSearcher.js +196 -0
- package/dist/platforms/BioRxivSearcher.js.map +1 -0
- package/dist/platforms/CORESearcher.d.ts +16 -0
- package/dist/platforms/CORESearcher.d.ts.map +1 -0
- package/dist/platforms/CORESearcher.js +148 -0
- package/dist/platforms/CORESearcher.js.map +1 -0
- package/dist/platforms/CrossrefSearcher.d.ts +34 -0
- package/dist/platforms/CrossrefSearcher.d.ts.map +1 -0
- package/dist/platforms/CrossrefSearcher.js +339 -0
- package/dist/platforms/CrossrefSearcher.js.map +1 -0
- package/dist/platforms/EuropePMCSearcher.d.ts +20 -0
- package/dist/platforms/EuropePMCSearcher.d.ts.map +1 -0
- package/dist/platforms/EuropePMCSearcher.js +173 -0
- package/dist/platforms/EuropePMCSearcher.js.map +1 -0
- package/dist/platforms/GoogleScholarSearcher.d.ts +77 -0
- package/dist/platforms/GoogleScholarSearcher.d.ts.map +1 -0
- package/dist/platforms/GoogleScholarSearcher.js +262 -0
- package/dist/platforms/GoogleScholarSearcher.js.map +1 -0
- package/dist/platforms/IACRSearcher.d.ts +51 -0
- package/dist/platforms/IACRSearcher.d.ts.map +1 -0
- package/dist/platforms/IACRSearcher.js +339 -0
- package/dist/platforms/IACRSearcher.js.map +1 -0
- package/dist/platforms/OpenAIRESearcher.d.ts +22 -0
- package/dist/platforms/OpenAIRESearcher.d.ts.map +1 -0
- package/dist/platforms/OpenAIRESearcher.js +223 -0
- package/dist/platforms/OpenAIRESearcher.js.map +1 -0
- package/dist/platforms/OpenAlexSearcher.d.ts +14 -0
- package/dist/platforms/OpenAlexSearcher.d.ts.map +1 -0
- package/dist/platforms/OpenAlexSearcher.js +114 -0
- package/dist/platforms/OpenAlexSearcher.js.map +1 -0
- package/dist/platforms/PMCSearcher.d.ts +20 -0
- package/dist/platforms/PMCSearcher.d.ts.map +1 -0
- package/dist/platforms/PMCSearcher.js +177 -0
- package/dist/platforms/PMCSearcher.js.map +1 -0
- package/dist/platforms/PaperSource.d.ts +143 -0
- package/dist/platforms/PaperSource.d.ts.map +1 -0
- package/dist/platforms/PaperSource.js +125 -0
- package/dist/platforms/PaperSource.js.map +1 -0
- package/dist/platforms/PubMedSearcher.d.ts +104 -0
- package/dist/platforms/PubMedSearcher.d.ts.map +1 -0
- package/dist/platforms/PubMedSearcher.js +422 -0
- package/dist/platforms/PubMedSearcher.js.map +1 -0
- package/dist/platforms/SciHubSearcher.d.ts +66 -0
- package/dist/platforms/SciHubSearcher.d.ts.map +1 -0
- package/dist/platforms/SciHubSearcher.js +398 -0
- package/dist/platforms/SciHubSearcher.js.map +1 -0
- package/dist/platforms/ScienceDirectSearcher.d.ts +42 -0
- package/dist/platforms/ScienceDirectSearcher.d.ts.map +1 -0
- package/dist/platforms/ScienceDirectSearcher.js +326 -0
- package/dist/platforms/ScienceDirectSearcher.js.map +1 -0
- package/dist/platforms/ScopusSearcher.d.ts +43 -0
- package/dist/platforms/ScopusSearcher.d.ts.map +1 -0
- package/dist/platforms/ScopusSearcher.js +364 -0
- package/dist/platforms/ScopusSearcher.js.map +1 -0
- package/dist/platforms/SemanticScholarSearcher.d.ts +96 -0
- package/dist/platforms/SemanticScholarSearcher.d.ts.map +1 -0
- package/dist/platforms/SemanticScholarSearcher.js +419 -0
- package/dist/platforms/SemanticScholarSearcher.js.map +1 -0
- package/dist/platforms/SpringerSearcher.d.ts +54 -0
- package/dist/platforms/SpringerSearcher.d.ts.map +1 -0
- package/dist/platforms/SpringerSearcher.js +407 -0
- package/dist/platforms/SpringerSearcher.js.map +1 -0
- package/dist/platforms/UnpaywallSearcher.d.ts +18 -0
- package/dist/platforms/UnpaywallSearcher.d.ts.map +1 -0
- package/dist/platforms/UnpaywallSearcher.js +115 -0
- package/dist/platforms/UnpaywallSearcher.js.map +1 -0
- package/dist/platforms/WebOfScienceSearcher.d.ts +111 -0
- package/dist/platforms/WebOfScienceSearcher.d.ts.map +1 -0
- package/dist/platforms/WebOfScienceSearcher.js +500 -0
- package/dist/platforms/WebOfScienceSearcher.js.map +1 -0
- package/dist/platforms/WileySearcher.d.ts +44 -0
- package/dist/platforms/WileySearcher.d.ts.map +1 -0
- package/dist/platforms/WileySearcher.js +148 -0
- package/dist/platforms/WileySearcher.js.map +1 -0
- package/dist/services/CitationService.d.ts +66 -0
- package/dist/services/CitationService.d.ts.map +1 -0
- package/dist/services/CitationService.js +237 -0
- package/dist/services/CitationService.js.map +1 -0
- package/dist/services/MultiSourceSearchService.d.ts +19 -0
- package/dist/services/MultiSourceSearchService.d.ts.map +1 -0
- package/dist/services/MultiSourceSearchService.js +96 -0
- package/dist/services/MultiSourceSearchService.js.map +1 -0
- package/dist/services/OpenAccessFallbackService.d.ts +20 -0
- package/dist/services/OpenAccessFallbackService.d.ts.map +1 -0
- package/dist/services/OpenAccessFallbackService.js +124 -0
- package/dist/services/OpenAccessFallbackService.js.map +1 -0
- package/dist/utils/ErrorHandler.d.ts +99 -0
- package/dist/utils/ErrorHandler.d.ts.map +1 -0
- package/dist/utils/ErrorHandler.js +266 -0
- package/dist/utils/ErrorHandler.js.map +1 -0
- package/dist/utils/Logger.d.ts +6 -0
- package/dist/utils/Logger.d.ts.map +1 -0
- package/dist/utils/Logger.js +26 -0
- package/dist/utils/Logger.js.map +1 -0
- package/dist/utils/PDFExtractor.d.ts +34 -0
- package/dist/utils/PDFExtractor.d.ts.map +1 -0
- package/dist/utils/PDFExtractor.js +130 -0
- package/dist/utils/PDFExtractor.js.map +1 -0
- package/dist/utils/PdfDownload.d.ts +7 -0
- package/dist/utils/PdfDownload.d.ts.map +1 -0
- package/dist/utils/PdfDownload.js +52 -0
- package/dist/utils/PdfDownload.js.map +1 -0
- package/dist/utils/QuotaManager.d.ts +32 -0
- package/dist/utils/QuotaManager.d.ts.map +1 -0
- package/dist/utils/QuotaManager.js +95 -0
- package/dist/utils/QuotaManager.js.map +1 -0
- package/dist/utils/RateLimiter.d.ts +50 -0
- package/dist/utils/RateLimiter.d.ts.map +1 -0
- package/dist/utils/RateLimiter.js +121 -0
- package/dist/utils/RateLimiter.js.map +1 -0
- package/dist/utils/RequestCache.d.ts +26 -0
- package/dist/utils/RequestCache.d.ts.map +1 -0
- package/dist/utils/RequestCache.js +66 -0
- package/dist/utils/RequestCache.js.map +1 -0
- package/dist/utils/SecurityUtils.d.ts +80 -0
- package/dist/utils/SecurityUtils.d.ts.map +1 -0
- package/dist/utils/SecurityUtils.js +357 -0
- package/dist/utils/SecurityUtils.js.map +1 -0
- package/package.json +111 -0
- package/skills/paper-search/SKILL.md +192 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wiley TDM (Text and Data Mining) API - PDF Download Only
|
|
3
|
+
*
|
|
4
|
+
* Documentation: https://onlinelibrary.wiley.com/library-info/resources/text-and-datamining
|
|
5
|
+
* GitHub Client: https://github.com/WileyLabs/tdm-client
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: Wiley TDM API does NOT support keyword search.
|
|
8
|
+
* It only supports downloading PDFs by DOI.
|
|
9
|
+
* For searching Wiley content, use Crossref API with publisher filter.
|
|
10
|
+
*
|
|
11
|
+
* API Endpoint: https://api.wiley.com/onlinelibrary/tdm/v1/articles/{DOI}
|
|
12
|
+
* Header: Wiley-TDM-Client-Token: <token>
|
|
13
|
+
*
|
|
14
|
+
* Rate limits:
|
|
15
|
+
* - Up to 3 articles per second
|
|
16
|
+
* - Up to 60 requests per 10 minutes (build in 10 second delay between requests)
|
|
17
|
+
*/
|
|
18
|
+
import { PaperSource, SearchOptions, DownloadOptions, PlatformCapabilities } from './PaperSource.js';
|
|
19
|
+
import { Paper } from '../models/Paper.js';
|
|
20
|
+
export declare class WileySearcher extends PaperSource {
|
|
21
|
+
private client;
|
|
22
|
+
private rateLimiter;
|
|
23
|
+
constructor(tdmToken?: string);
|
|
24
|
+
/**
|
|
25
|
+
* Search is NOT supported by Wiley TDM API.
|
|
26
|
+
* Use Crossref API to search for Wiley articles, then use download() to get PDFs.
|
|
27
|
+
*/
|
|
28
|
+
search(query: string, options?: SearchOptions): Promise<Paper[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Download PDF by DOI using Wiley TDM API
|
|
31
|
+
* @param doi - The DOI of the article (e.g., "10.1111/jtsb.12390")
|
|
32
|
+
* @param options - Download options including savePath
|
|
33
|
+
*/
|
|
34
|
+
downloadPdf(doi: string, options?: {
|
|
35
|
+
savePath?: string;
|
|
36
|
+
}): Promise<string>;
|
|
37
|
+
/**
|
|
38
|
+
* Get article metadata and download link (without downloading)
|
|
39
|
+
*/
|
|
40
|
+
getArticleInfo(doi: string): Promise<Paper>;
|
|
41
|
+
getCapabilities(): PlatformCapabilities;
|
|
42
|
+
readPaper(paperId: string, options?: DownloadOptions): Promise<string>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=WileySearcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WileySearcher.d.ts","sourceRoot":"","sources":["../../src/platforms/WileySearcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACrG,OAAO,EAAE,KAAK,EAAgB,MAAM,oBAAoB,CAAC;AAMzD,qBAAa,aAAc,SAAQ,WAAW;IAC5C,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,WAAW,CAAc;gBAErB,QAAQ,CAAC,EAAE,MAAM;IAoB7B;;;OAGG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAQ1E;;;;OAIG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,MAAM,CAAC;IAiEpF;;OAEG;IACG,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAsBjD,eAAe,IAAI,oBAAoB;IAWjC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;CAGjF"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wiley TDM (Text and Data Mining) API - PDF Download Only
|
|
3
|
+
*
|
|
4
|
+
* Documentation: https://onlinelibrary.wiley.com/library-info/resources/text-and-datamining
|
|
5
|
+
* GitHub Client: https://github.com/WileyLabs/tdm-client
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: Wiley TDM API does NOT support keyword search.
|
|
8
|
+
* It only supports downloading PDFs by DOI.
|
|
9
|
+
* For searching Wiley content, use Crossref API with publisher filter.
|
|
10
|
+
*
|
|
11
|
+
* API Endpoint: https://api.wiley.com/onlinelibrary/tdm/v1/articles/{DOI}
|
|
12
|
+
* Header: Wiley-TDM-Client-Token: <token>
|
|
13
|
+
*
|
|
14
|
+
* Rate limits:
|
|
15
|
+
* - Up to 3 articles per second
|
|
16
|
+
* - Up to 60 requests per 10 minutes (build in 10 second delay between requests)
|
|
17
|
+
*/
|
|
18
|
+
import axios from 'axios';
|
|
19
|
+
import { PaperSource } from './PaperSource.js';
|
|
20
|
+
import { PaperFactory } from '../models/Paper.js';
|
|
21
|
+
import { RateLimiter } from '../utils/RateLimiter.js';
|
|
22
|
+
import { ErrorHandler } from '../utils/ErrorHandler.js';
|
|
23
|
+
import { sanitizeDoi } from '../utils/SecurityUtils.js';
|
|
24
|
+
import { TIMEOUTS } from '../config/constants.js';
|
|
25
|
+
export class WileySearcher extends PaperSource {
|
|
26
|
+
client;
|
|
27
|
+
rateLimiter;
|
|
28
|
+
constructor(tdmToken) {
|
|
29
|
+
super('wiley', 'https://api.wiley.com/onlinelibrary/tdm/v1', tdmToken);
|
|
30
|
+
this.client = axios.create({
|
|
31
|
+
baseURL: 'https://api.wiley.com/onlinelibrary/tdm/v1',
|
|
32
|
+
headers: {
|
|
33
|
+
'Accept': 'application/pdf',
|
|
34
|
+
...(tdmToken ? { 'Wiley-TDM-Client-Token': tdmToken } : {})
|
|
35
|
+
},
|
|
36
|
+
maxRedirects: 5,
|
|
37
|
+
timeout: TIMEOUTS.EXTENDED
|
|
38
|
+
});
|
|
39
|
+
// Wiley rate limits: 3 articles/sec, 60 requests/10min
|
|
40
|
+
this.rateLimiter = new RateLimiter({
|
|
41
|
+
requestsPerSecond: 0.1, // Conservative: ~6 per minute
|
|
42
|
+
burstCapacity: 3
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Search is NOT supported by Wiley TDM API.
|
|
47
|
+
* Use Crossref API to search for Wiley articles, then use download() to get PDFs.
|
|
48
|
+
*/
|
|
49
|
+
async search(query, options = {}) {
|
|
50
|
+
throw new Error('Wiley TDM API does not support keyword search. ' +
|
|
51
|
+
'Use Crossref API (search_crossref) to find Wiley articles by DOI, ' +
|
|
52
|
+
'then use download_paper with platform="wiley" to download PDFs.');
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Download PDF by DOI using Wiley TDM API
|
|
56
|
+
* @param doi - The DOI of the article (e.g., "10.1111/jtsb.12390")
|
|
57
|
+
* @param options - Download options including savePath
|
|
58
|
+
*/
|
|
59
|
+
async downloadPdf(doi, options = {}) {
|
|
60
|
+
if (!this.apiKey) {
|
|
61
|
+
throw new Error('Wiley TDM token is required. Set WILEY_TDM_TOKEN environment variable.');
|
|
62
|
+
}
|
|
63
|
+
// Clean and validate DOI format
|
|
64
|
+
const doiResult = sanitizeDoi(doi);
|
|
65
|
+
if (!doiResult.valid) {
|
|
66
|
+
throw new Error(`Invalid DOI format: ${doi}. ${doiResult.error || ''}`);
|
|
67
|
+
}
|
|
68
|
+
const cleanDoi = doiResult.sanitized;
|
|
69
|
+
const fs = await import('fs');
|
|
70
|
+
const path = await import('path');
|
|
71
|
+
const savePath = options.savePath || './downloads';
|
|
72
|
+
if (!fs.existsSync(savePath)) {
|
|
73
|
+
fs.mkdirSync(savePath, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
// Encode DOI for URL (replace / with %2F)
|
|
76
|
+
const encodedDoi = encodeURIComponent(cleanDoi);
|
|
77
|
+
const url = `/articles/${encodedDoi}`;
|
|
78
|
+
await this.rateLimiter.waitForPermission();
|
|
79
|
+
try {
|
|
80
|
+
const response = await ErrorHandler.retryWithBackoff(() => this.client.get(url, {
|
|
81
|
+
responseType: 'stream',
|
|
82
|
+
headers: {
|
|
83
|
+
'Wiley-TDM-Client-Token': this.apiKey,
|
|
84
|
+
'Accept': 'application/pdf'
|
|
85
|
+
}
|
|
86
|
+
}), { context: 'Wiley download' });
|
|
87
|
+
// Generate filename from DOI
|
|
88
|
+
const fileName = `${cleanDoi.replace(/[\/\\:*?"<>|]/g, '_')}.pdf`;
|
|
89
|
+
const filePath = path.join(savePath, fileName);
|
|
90
|
+
const writer = fs.createWriteStream(filePath);
|
|
91
|
+
response.data.pipe(writer);
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
writer.on('finish', () => resolve(filePath));
|
|
94
|
+
writer.on('error', reject);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
const status = error.response?.status;
|
|
99
|
+
const errorMessages = {
|
|
100
|
+
400: 'No TDM Client Token was found in the request',
|
|
101
|
+
403: 'TDM Client Token is invalid or not registered',
|
|
102
|
+
404: 'Access denied - you or your institution does not have access to this content. Check your subscription.',
|
|
103
|
+
429: 'Rate limit exceeded. Please reduce request frequency (max 60 requests per 10 minutes).'
|
|
104
|
+
};
|
|
105
|
+
if (status && errorMessages[status]) {
|
|
106
|
+
throw new Error(`Wiley TDM Error (${status}): ${errorMessages[status]}`);
|
|
107
|
+
}
|
|
108
|
+
throw new Error(`Failed to download PDF: ${error.message}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get article metadata and download link (without downloading)
|
|
113
|
+
*/
|
|
114
|
+
async getArticleInfo(doi) {
|
|
115
|
+
// Clean and validate DOI
|
|
116
|
+
const doiResult = sanitizeDoi(doi);
|
|
117
|
+
const cleanDoi = doiResult.valid ? doiResult.sanitized : doi;
|
|
118
|
+
// Since TDM API only provides PDF download, we create basic paper info from DOI
|
|
119
|
+
return PaperFactory.create({
|
|
120
|
+
paperId: cleanDoi,
|
|
121
|
+
title: `Wiley Article: ${cleanDoi}`,
|
|
122
|
+
authors: [],
|
|
123
|
+
abstract: '',
|
|
124
|
+
doi: cleanDoi,
|
|
125
|
+
publishedDate: null,
|
|
126
|
+
pdfUrl: `https://api.wiley.com/onlinelibrary/tdm/v1/articles/${encodeURIComponent(cleanDoi)}`,
|
|
127
|
+
url: `https://doi.org/${cleanDoi}`,
|
|
128
|
+
source: 'wiley',
|
|
129
|
+
extra: {
|
|
130
|
+
note: 'Use Crossref API for full metadata. This endpoint only provides PDF download.'
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
getCapabilities() {
|
|
135
|
+
return {
|
|
136
|
+
search: false, // TDM API does not support search
|
|
137
|
+
download: true, // PDF download by DOI
|
|
138
|
+
fullText: true, // Full PDF available
|
|
139
|
+
citations: false,
|
|
140
|
+
requiresApiKey: true,
|
|
141
|
+
supportedOptions: [] // No search options - only DOI-based download
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
async readPaper(paperId, options = {}) {
|
|
145
|
+
return 'Wiley TDM API only supports PDF download. Use downloadPdf() method with a DOI to get the full PDF.';
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=WileySearcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WileySearcher.js","sourceRoot":"","sources":["../../src/platforms/WileySearcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export interface CitationData {
|
|
2
|
+
paperId: string;
|
|
3
|
+
title: string;
|
|
4
|
+
citationCount: number;
|
|
5
|
+
referenceCount: number;
|
|
6
|
+
influentialCitationCount?: number;
|
|
7
|
+
year?: number;
|
|
8
|
+
authors?: Array<{
|
|
9
|
+
name: string;
|
|
10
|
+
authorId?: string;
|
|
11
|
+
}>;
|
|
12
|
+
venue?: string;
|
|
13
|
+
doi?: string;
|
|
14
|
+
url?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface BatchCitationRequest {
|
|
17
|
+
paperId?: string;
|
|
18
|
+
doi?: string;
|
|
19
|
+
arxivId?: string;
|
|
20
|
+
title?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare class CitationService {
|
|
23
|
+
private client;
|
|
24
|
+
private cache;
|
|
25
|
+
private rateLimiter;
|
|
26
|
+
private apiKey?;
|
|
27
|
+
constructor(apiKey?: string);
|
|
28
|
+
/**
|
|
29
|
+
* Get citation data for a single paper by ID
|
|
30
|
+
*/
|
|
31
|
+
getCitationData(paperId: string, forceRefresh?: boolean): Promise<CitationData | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Get citation data by DOI
|
|
34
|
+
*/
|
|
35
|
+
getCitationDataByDoi(doi: string, forceRefresh?: boolean): Promise<CitationData | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Get citation data by arXiv ID
|
|
38
|
+
*/
|
|
39
|
+
getCitationDataByArxiv(arxivId: string, forceRefresh?: boolean): Promise<CitationData | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Batch lookup citations for multiple papers
|
|
42
|
+
*/
|
|
43
|
+
batchLookup(requests: BatchCitationRequest[]): Promise<Map<string, CitationData | null>>;
|
|
44
|
+
/**
|
|
45
|
+
* Get references for a paper
|
|
46
|
+
*/
|
|
47
|
+
getReferences(paperId: string, limit?: number): Promise<CitationData[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Get citations for a paper
|
|
50
|
+
*/
|
|
51
|
+
getCitations(paperId: string, limit?: number): Promise<CitationData[]>;
|
|
52
|
+
/**
|
|
53
|
+
* Parse Semantic Scholar response to CitationData
|
|
54
|
+
*/
|
|
55
|
+
private parseCitationData;
|
|
56
|
+
/**
|
|
57
|
+
* Get cache statistics
|
|
58
|
+
*/
|
|
59
|
+
getCacheStats(): import("../utils/RequestCache.js").CacheStats;
|
|
60
|
+
/**
|
|
61
|
+
* Clear cache
|
|
62
|
+
*/
|
|
63
|
+
clearCache(): void;
|
|
64
|
+
}
|
|
65
|
+
export default CitationService;
|
|
66
|
+
//# sourceMappingURL=CitationService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CitationService.d.ts","sourceRoot":"","sources":["../../src/services/CitationService.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,MAAM,CAAC,CAAS;gBAEZ,MAAM,CAAC,EAAE,MAAM;IA0B3B;;OAEG;IACG,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,GAAE,OAAe,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAkDnG;;OAEG;IACG,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,GAAE,OAAe,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAUpG;;OAEG;IACG,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,GAAE,OAAe,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAU1G;;OAEG;IACG,WAAW,CAAC,QAAQ,EAAE,oBAAoB,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC,CAAC;IAqC9F;;OAEG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAY,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAkClF;;OAEG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,GAAE,MAAY,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAkCjF;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAkBzB;;OAEG;IACH,aAAa;IAIb;;OAEG;IACH,UAAU;CAGX;AAED,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { RequestCache } from '../utils/RequestCache.js';
|
|
3
|
+
import { RateLimiter } from '../utils/RateLimiter.js';
|
|
4
|
+
import { ErrorHandler } from '../utils/ErrorHandler.js';
|
|
5
|
+
import { TIMEOUTS, USER_AGENT } from '../config/constants.js';
|
|
6
|
+
import { logDebug, logWarn } from '../utils/Logger.js';
|
|
7
|
+
export class CitationService {
|
|
8
|
+
client;
|
|
9
|
+
cache;
|
|
10
|
+
rateLimiter;
|
|
11
|
+
apiKey;
|
|
12
|
+
constructor(apiKey) {
|
|
13
|
+
this.apiKey = apiKey || process.env.SEMANTIC_SCHOLAR_API_KEY;
|
|
14
|
+
this.client = axios.create({
|
|
15
|
+
baseURL: 'https://api.semanticscholar.org/graph/v1',
|
|
16
|
+
timeout: TIMEOUTS.DEFAULT,
|
|
17
|
+
headers: {
|
|
18
|
+
'Accept': 'application/json',
|
|
19
|
+
'User-Agent': USER_AGENT,
|
|
20
|
+
...(this.apiKey ? { 'x-api-key': this.apiKey } : {})
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
// Semantic Scholar rate limits: 100 req/5min (free), 1000 req/5min (paid)
|
|
24
|
+
const requestsPerMinute = this.apiKey ? 180 : 18;
|
|
25
|
+
this.rateLimiter = new RateLimiter({
|
|
26
|
+
requestsPerSecond: requestsPerMinute / 60,
|
|
27
|
+
burstCapacity: Math.max(3, Math.floor(requestsPerMinute / 20))
|
|
28
|
+
});
|
|
29
|
+
this.cache = new RequestCache({
|
|
30
|
+
maxSize: 500,
|
|
31
|
+
ttlMs: 86400000 // 24 hours
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get citation data for a single paper by ID
|
|
36
|
+
*/
|
|
37
|
+
async getCitationData(paperId, forceRefresh = false) {
|
|
38
|
+
try {
|
|
39
|
+
// Check cache first
|
|
40
|
+
if (!forceRefresh) {
|
|
41
|
+
const cacheKey = this.cache.generateKey('citation', paperId, {});
|
|
42
|
+
const cached = this.cache.get(cacheKey);
|
|
43
|
+
if (cached) {
|
|
44
|
+
return cached;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
await this.rateLimiter.waitForPermission();
|
|
48
|
+
const fields = [
|
|
49
|
+
'paperId',
|
|
50
|
+
'title',
|
|
51
|
+
'citationCount',
|
|
52
|
+
'referenceCount',
|
|
53
|
+
'influentialCitationCount',
|
|
54
|
+
'year',
|
|
55
|
+
'authors',
|
|
56
|
+
'venue',
|
|
57
|
+
'externalIds',
|
|
58
|
+
'url'
|
|
59
|
+
].join(',');
|
|
60
|
+
const response = await ErrorHandler.retryWithBackoff(() => this.client.get(`/paper/${paperId}`, {
|
|
61
|
+
params: { fields }
|
|
62
|
+
}), { context: 'Semantic Scholar citation lookup' });
|
|
63
|
+
if (response.status !== 200 || !response.data) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const data = this.parseCitationData(response.data);
|
|
67
|
+
// Cache the result
|
|
68
|
+
const cacheKey = this.cache.generateKey('citation', paperId, {});
|
|
69
|
+
this.cache.set(cacheKey, data);
|
|
70
|
+
return data;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
logWarn(`Error fetching citation data for ${paperId}:`, error.message);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Get citation data by DOI
|
|
79
|
+
*/
|
|
80
|
+
async getCitationDataByDoi(doi, forceRefresh = false) {
|
|
81
|
+
try {
|
|
82
|
+
const paperId = `DOI:${doi}`;
|
|
83
|
+
return await this.getCitationData(paperId, forceRefresh);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
logWarn(`Error fetching citation data by DOI ${doi}:`, error.message);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get citation data by arXiv ID
|
|
92
|
+
*/
|
|
93
|
+
async getCitationDataByArxiv(arxivId, forceRefresh = false) {
|
|
94
|
+
try {
|
|
95
|
+
const paperId = `ARXIV:${arxivId}`;
|
|
96
|
+
return await this.getCitationData(paperId, forceRefresh);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
logWarn(`Error fetching citation data by arXiv ID ${arxivId}:`, error.message);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Batch lookup citations for multiple papers
|
|
105
|
+
*/
|
|
106
|
+
async batchLookup(requests) {
|
|
107
|
+
const results = new Map();
|
|
108
|
+
for (const request of requests) {
|
|
109
|
+
let paperId = null;
|
|
110
|
+
let key;
|
|
111
|
+
if (request.paperId) {
|
|
112
|
+
paperId = request.paperId;
|
|
113
|
+
key = request.paperId;
|
|
114
|
+
}
|
|
115
|
+
else if (request.doi) {
|
|
116
|
+
paperId = `DOI:${request.doi}`;
|
|
117
|
+
key = request.doi;
|
|
118
|
+
}
|
|
119
|
+
else if (request.arxivId) {
|
|
120
|
+
paperId = `ARXIV:${request.arxivId}`;
|
|
121
|
+
key = request.arxivId;
|
|
122
|
+
}
|
|
123
|
+
else if (request.title) {
|
|
124
|
+
key = request.title;
|
|
125
|
+
// Search by title (not implemented in this version)
|
|
126
|
+
results.set(key, null);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const data = await this.getCitationData(paperId);
|
|
134
|
+
results.set(key, data);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
logDebug(`Error in batch lookup for ${key}:`, error);
|
|
138
|
+
results.set(key, null);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return results;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get references for a paper
|
|
145
|
+
*/
|
|
146
|
+
async getReferences(paperId, limit = 100) {
|
|
147
|
+
try {
|
|
148
|
+
await this.rateLimiter.waitForPermission();
|
|
149
|
+
const fields = [
|
|
150
|
+
'paperId',
|
|
151
|
+
'title',
|
|
152
|
+
'citationCount',
|
|
153
|
+
'year',
|
|
154
|
+
'authors',
|
|
155
|
+
'venue'
|
|
156
|
+
].join(',');
|
|
157
|
+
const response = await ErrorHandler.retryWithBackoff(() => this.client.get(`/paper/${paperId}/references`, {
|
|
158
|
+
params: { fields, limit }
|
|
159
|
+
}), { context: 'Semantic Scholar references lookup' });
|
|
160
|
+
if (response.status !== 200 || !response.data?.data) {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
return response.data.data
|
|
164
|
+
.map((item) => item.citedPaper)
|
|
165
|
+
.filter((paper) => paper)
|
|
166
|
+
.map((paper) => this.parseCitationData(paper));
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
logWarn(`Error fetching references for ${paperId}:`, error.message);
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get citations for a paper
|
|
175
|
+
*/
|
|
176
|
+
async getCitations(paperId, limit = 100) {
|
|
177
|
+
try {
|
|
178
|
+
await this.rateLimiter.waitForPermission();
|
|
179
|
+
const fields = [
|
|
180
|
+
'paperId',
|
|
181
|
+
'title',
|
|
182
|
+
'citationCount',
|
|
183
|
+
'year',
|
|
184
|
+
'authors',
|
|
185
|
+
'venue'
|
|
186
|
+
].join(',');
|
|
187
|
+
const response = await ErrorHandler.retryWithBackoff(() => this.client.get(`/paper/${paperId}/citations`, {
|
|
188
|
+
params: { fields, limit }
|
|
189
|
+
}), { context: 'Semantic Scholar citations lookup' });
|
|
190
|
+
if (response.status !== 200 || !response.data?.data) {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
return response.data.data
|
|
194
|
+
.map((item) => item.citingPaper)
|
|
195
|
+
.filter((paper) => paper)
|
|
196
|
+
.map((paper) => this.parseCitationData(paper));
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
logWarn(`Error fetching citations for ${paperId}:`, error.message);
|
|
200
|
+
return [];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Parse Semantic Scholar response to CitationData
|
|
205
|
+
*/
|
|
206
|
+
parseCitationData(data) {
|
|
207
|
+
return {
|
|
208
|
+
paperId: data.paperId,
|
|
209
|
+
title: data.title,
|
|
210
|
+
citationCount: data.citationCount || 0,
|
|
211
|
+
referenceCount: data.referenceCount || 0,
|
|
212
|
+
influentialCitationCount: data.influentialCitationCount,
|
|
213
|
+
year: data.year,
|
|
214
|
+
authors: data.authors?.map((author) => ({
|
|
215
|
+
name: author.name,
|
|
216
|
+
authorId: author.authorId
|
|
217
|
+
})),
|
|
218
|
+
venue: data.venue,
|
|
219
|
+
doi: data.externalIds?.DOI,
|
|
220
|
+
url: data.url
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Get cache statistics
|
|
225
|
+
*/
|
|
226
|
+
getCacheStats() {
|
|
227
|
+
return this.cache.getStats();
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Clear cache
|
|
231
|
+
*/
|
|
232
|
+
clearCache() {
|
|
233
|
+
this.cache.clear();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
export default CitationService;
|
|
237
|
+
//# sourceMappingURL=CitationService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CitationService.js","sourceRoot":"","sources":["../../src/services/CitationService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Searchers } from '../core/searchers.js';
|
|
2
|
+
import { Paper } from '../models/Paper.js';
|
|
3
|
+
import { SearchOptions } from '../platforms/PaperSource.js';
|
|
4
|
+
export interface MultiSourceSearchResult {
|
|
5
|
+
query: string;
|
|
6
|
+
sources_requested: string;
|
|
7
|
+
sources_used: string[];
|
|
8
|
+
source_results: Record<string, number>;
|
|
9
|
+
errors: Record<string, string>;
|
|
10
|
+
failed_sources: string[];
|
|
11
|
+
warnings: string[];
|
|
12
|
+
total: number;
|
|
13
|
+
raw_total: number;
|
|
14
|
+
papers: Record<string, unknown>[];
|
|
15
|
+
}
|
|
16
|
+
export declare function parseSourceList(sources: string | undefined, searchers: Searchers): string[];
|
|
17
|
+
export declare function searchMultipleSources(searchers: Searchers, query: string, sources: string, options: SearchOptions, sourceTimeoutMs?: number): Promise<MultiSourceSearchResult>;
|
|
18
|
+
export declare function dedupePapers(papers: Paper[]): Paper[];
|
|
19
|
+
//# sourceMappingURL=MultiSourceSearchService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MultiSourceSearchService.d.ts","sourceRoot":"","sources":["../../src/services/MultiSourceSearchService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,KAAK,EAAgB,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAe,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAGzE,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CACnC;AAuBD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,EAAE,CAc3F;AAED,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,aAAa,EACtB,eAAe,GAAE,MAA6B,GAC7C,OAAO,CAAC,uBAAuB,CAAC,CA8ClC;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CAYrD"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { TIMEOUTS } from '../config/constants.js';
|
|
2
|
+
import { PaperFactory } from '../models/Paper.js';
|
|
3
|
+
import { withTimeout } from '../utils/SecurityUtils.js';
|
|
4
|
+
const DEFAULT_ALL_SOURCES = [
|
|
5
|
+
'crossref',
|
|
6
|
+
'openalex',
|
|
7
|
+
'pubmed',
|
|
8
|
+
'pmc',
|
|
9
|
+
'europepmc',
|
|
10
|
+
'arxiv',
|
|
11
|
+
'biorxiv',
|
|
12
|
+
'medrxiv',
|
|
13
|
+
'iacr',
|
|
14
|
+
'core',
|
|
15
|
+
'openaire'
|
|
16
|
+
];
|
|
17
|
+
const ALIASES = {
|
|
18
|
+
google_scholar: 'googlescholar',
|
|
19
|
+
webofscience: 'webofscience',
|
|
20
|
+
wos: 'webofscience',
|
|
21
|
+
europe_pmc: 'europepmc',
|
|
22
|
+
pubmed_central: 'pmc'
|
|
23
|
+
};
|
|
24
|
+
export function parseSourceList(sources, searchers) {
|
|
25
|
+
const requested = !sources || sources.trim() === '' ? 'crossref' : sources.trim();
|
|
26
|
+
if (requested.toLowerCase() === 'all') {
|
|
27
|
+
return DEFAULT_ALL_SOURCES.filter(source => source in searchers);
|
|
28
|
+
}
|
|
29
|
+
return requested
|
|
30
|
+
.split(',')
|
|
31
|
+
.map(source => source.trim().toLowerCase())
|
|
32
|
+
.filter(Boolean)
|
|
33
|
+
.map(source => ALIASES[source] || source)
|
|
34
|
+
.filter((source, index, values) => values.indexOf(source) === index)
|
|
35
|
+
.filter(source => source in searchers);
|
|
36
|
+
}
|
|
37
|
+
export async function searchMultipleSources(searchers, query, sources, options, sourceTimeoutMs = TIMEOUTS.SOURCE_TASK) {
|
|
38
|
+
const selected = parseSourceList(sources, searchers);
|
|
39
|
+
const settled = await Promise.allSettled(selected.map(async (source) => {
|
|
40
|
+
const searcher = searchers[source];
|
|
41
|
+
const results = await withTimeout(searcher.search(query, options), sourceTimeoutMs, `${source} search timed out after ${sourceTimeoutMs}ms`);
|
|
42
|
+
return { source, results };
|
|
43
|
+
}));
|
|
44
|
+
const sourceResults = {};
|
|
45
|
+
const errors = {};
|
|
46
|
+
const failedSources = [];
|
|
47
|
+
const merged = [];
|
|
48
|
+
for (let i = 0; i < settled.length; i += 1) {
|
|
49
|
+
const source = selected[i];
|
|
50
|
+
const result = settled[i];
|
|
51
|
+
if (result.status === 'rejected') {
|
|
52
|
+
sourceResults[source] = 0;
|
|
53
|
+
errors[source] = result.reason?.message || String(result.reason);
|
|
54
|
+
failedSources.push(source);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
sourceResults[source] = result.value.results.length;
|
|
58
|
+
merged.push(...result.value.results);
|
|
59
|
+
}
|
|
60
|
+
const deduped = dedupePapers(merged);
|
|
61
|
+
return {
|
|
62
|
+
query,
|
|
63
|
+
sources_requested: sources,
|
|
64
|
+
sources_used: selected,
|
|
65
|
+
source_results: sourceResults,
|
|
66
|
+
errors,
|
|
67
|
+
failed_sources: failedSources,
|
|
68
|
+
warnings: failedSources.map(source => `${source}: ${errors[source]}`),
|
|
69
|
+
total: deduped.length,
|
|
70
|
+
raw_total: merged.length,
|
|
71
|
+
papers: deduped.map(paper => PaperFactory.toDict(paper))
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function dedupePapers(papers) {
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
const out = [];
|
|
77
|
+
for (const paper of papers) {
|
|
78
|
+
const key = paperKey(paper);
|
|
79
|
+
if (seen.has(key))
|
|
80
|
+
continue;
|
|
81
|
+
seen.add(key);
|
|
82
|
+
out.push(paper);
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
function paperKey(paper) {
|
|
87
|
+
const doi = paper.doi.trim().toLowerCase();
|
|
88
|
+
if (doi)
|
|
89
|
+
return `doi:${doi}`;
|
|
90
|
+
const title = paper.title.trim().toLowerCase();
|
|
91
|
+
const authors = paper.authors.join(';').trim().toLowerCase();
|
|
92
|
+
if (title)
|
|
93
|
+
return `title:${title}|authors:${authors}`;
|
|
94
|
+
return `id:${paper.source}:${paper.paperId}`;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=MultiSourceSearchService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MultiSourceSearchService.js","sourceRoot":"","sources":["../../src/services/MultiSourceSearchService.ts"],"names":[],"mappings":"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"}
|