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,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Springer Nature Searcher
|
|
3
|
+
*
|
|
4
|
+
* Documentation: https://dev.springernature.com/
|
|
5
|
+
* API Endpoints:
|
|
6
|
+
* - Metadata API v2: https://api.springernature.com/meta/v2/json
|
|
7
|
+
* - OpenAccess API: https://api.springernature.com/openaccess/json (if available with your key)
|
|
8
|
+
*
|
|
9
|
+
* Required API Key: Yes (api_key parameter)
|
|
10
|
+
* Get API key from: https://dev.springernature.com/signup
|
|
11
|
+
*
|
|
12
|
+
* Note: Meta API v2 is the primary API. OpenAccess API may require special access.
|
|
13
|
+
*/
|
|
14
|
+
import axios from 'axios';
|
|
15
|
+
import { sanitizeDoi, escapeQueryValue, withTimeout } from '../utils/SecurityUtils.js';
|
|
16
|
+
import { PaperSource } from './PaperSource.js';
|
|
17
|
+
import { PaperFactory } from '../models/Paper.js';
|
|
18
|
+
import { RateLimiter } from '../utils/RateLimiter.js';
|
|
19
|
+
import { ErrorHandler } from '../utils/ErrorHandler.js';
|
|
20
|
+
import { QuotaManager } from '../utils/QuotaManager.js';
|
|
21
|
+
import { logDebug, logWarn } from '../utils/Logger.js';
|
|
22
|
+
import { TIMEOUTS, USER_AGENT } from '../config/constants.js';
|
|
23
|
+
export class SpringerSearcher extends PaperSource {
|
|
24
|
+
metadataClient;
|
|
25
|
+
openAccessClient;
|
|
26
|
+
rateLimiter;
|
|
27
|
+
quotaManager;
|
|
28
|
+
hasOpenAccessAPI;
|
|
29
|
+
openAccessApiKey;
|
|
30
|
+
testingPromise = null;
|
|
31
|
+
constructor(apiKey, openAccessApiKey) {
|
|
32
|
+
super('springer', 'https://api.springernature.com', apiKey);
|
|
33
|
+
// Check for separate OpenAccess API key from environment
|
|
34
|
+
this.openAccessApiKey = openAccessApiKey || process.env.SPRINGER_OPENACCESS_API_KEY || apiKey;
|
|
35
|
+
// Use v2 API endpoint for metadata
|
|
36
|
+
this.metadataClient = axios.create({
|
|
37
|
+
baseURL: 'https://api.springernature.com/meta/v2',
|
|
38
|
+
timeout: TIMEOUTS.DEFAULT,
|
|
39
|
+
headers: {
|
|
40
|
+
'Accept': 'application/json',
|
|
41
|
+
'User-Agent': USER_AGENT
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
// OpenAccess API client (may not be available for all API keys)
|
|
45
|
+
this.openAccessClient = axios.create({
|
|
46
|
+
baseURL: 'https://api.springernature.com/openaccess',
|
|
47
|
+
timeout: TIMEOUTS.DEFAULT,
|
|
48
|
+
headers: {
|
|
49
|
+
'Accept': 'application/json',
|
|
50
|
+
'User-Agent': USER_AGENT
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
// Springer rate limits:
|
|
54
|
+
// - 5000 requests per day for both APIs combined
|
|
55
|
+
// - Approximately 200 per hour or 3-4 per minute to be safe
|
|
56
|
+
// Note: The same API key works for both Metadata and OpenAccess APIs
|
|
57
|
+
this.rateLimiter = new RateLimiter({
|
|
58
|
+
requestsPerSecond: 0.05, // Conservative: 3 per minute
|
|
59
|
+
burstCapacity: 5
|
|
60
|
+
});
|
|
61
|
+
this.quotaManager = QuotaManager.getInstance();
|
|
62
|
+
this.quotaManager.registerPlatform('springer', {
|
|
63
|
+
dailyLimit: 5000,
|
|
64
|
+
envPrefix: 'SPRINGER'
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async search(query, options = {}) {
|
|
68
|
+
const customOptions = options;
|
|
69
|
+
if (!this.apiKey) {
|
|
70
|
+
throw new Error('Springer API key is required');
|
|
71
|
+
}
|
|
72
|
+
const maxResults = Math.min(options.maxResults || 10, 100);
|
|
73
|
+
const papers = [];
|
|
74
|
+
try {
|
|
75
|
+
// Decide which API to use
|
|
76
|
+
let useOpenAccess = customOptions.openAccess === true;
|
|
77
|
+
// If openAccess is requested and we haven't tested the API yet, test it
|
|
78
|
+
if (useOpenAccess && this.hasOpenAccessAPI === undefined) {
|
|
79
|
+
await this.testOpenAccessAPI();
|
|
80
|
+
}
|
|
81
|
+
// Fall back to Meta API if OpenAccess API is not available
|
|
82
|
+
if (useOpenAccess && !this.hasOpenAccessAPI) {
|
|
83
|
+
logDebug('OpenAccess API not available, using Meta API with filtering');
|
|
84
|
+
useOpenAccess = false;
|
|
85
|
+
}
|
|
86
|
+
// Build query parameters
|
|
87
|
+
const params = {
|
|
88
|
+
q: query,
|
|
89
|
+
api_key: useOpenAccess ? this.openAccessApiKey : this.apiKey,
|
|
90
|
+
s: 1, // start index
|
|
91
|
+
p: maxResults // page size
|
|
92
|
+
};
|
|
93
|
+
// Add filters - Note: Some filters may require premium access
|
|
94
|
+
if (options.author) {
|
|
95
|
+
const sanitizedAuthor = this.sanitizeQueryValue(options.author);
|
|
96
|
+
params.q += ` name:"${sanitizedAuthor}"`;
|
|
97
|
+
}
|
|
98
|
+
if (options.journal) {
|
|
99
|
+
const sanitizedJournal = this.sanitizeQueryValue(options.journal);
|
|
100
|
+
params.q += ` pub:"${sanitizedJournal}"`;
|
|
101
|
+
}
|
|
102
|
+
if (options.year) {
|
|
103
|
+
// Year filter may cause 403 for some API keys
|
|
104
|
+
if (options.year.includes('-')) {
|
|
105
|
+
const [startYear, endYear] = options.year.split('-');
|
|
106
|
+
params.q += ` year:${startYear} TO ${endYear || '*'}`;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
params.q += ` year:${options.year}`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (customOptions.subject) {
|
|
113
|
+
// Subject filter may cause 403 for some API keys
|
|
114
|
+
const sanitizedSubject = this.sanitizeQueryValue(customOptions.subject);
|
|
115
|
+
params.q += ` subject:"${sanitizedSubject}"`;
|
|
116
|
+
}
|
|
117
|
+
if (customOptions.type) {
|
|
118
|
+
// Type filter generally works
|
|
119
|
+
params.q += ` type:${customOptions.type}`;
|
|
120
|
+
}
|
|
121
|
+
await this.rateLimiter.waitForPermission();
|
|
122
|
+
this.quotaManager.checkQuota('springer');
|
|
123
|
+
// Choose the appropriate API
|
|
124
|
+
let response;
|
|
125
|
+
if (useOpenAccess) {
|
|
126
|
+
response = await ErrorHandler.retryWithBackoff(() => this.openAccessClient.get('/json', { params }), { context: 'Springer OpenAccess search' });
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
response = await ErrorHandler.retryWithBackoff(() => this.metadataClient.get('/json', { params }), { context: 'Springer Meta search' });
|
|
130
|
+
}
|
|
131
|
+
this.quotaManager.incrementUsage('springer');
|
|
132
|
+
// Handle different response structures
|
|
133
|
+
// Meta v2 API: records contains the actual papers, result contains metadata
|
|
134
|
+
// OpenAccess API: might use either records or result for the actual papers
|
|
135
|
+
let results = [];
|
|
136
|
+
// For Meta v2 API, records is always the array of papers
|
|
137
|
+
if (response.data.records && Array.isArray(response.data.records)) {
|
|
138
|
+
results = response.data.records;
|
|
139
|
+
}
|
|
140
|
+
// For older API versions or different response format
|
|
141
|
+
else if (response.data.result && Array.isArray(response.data.result) &&
|
|
142
|
+
response.data.result.length > 0 &&
|
|
143
|
+
response.data.result[0].title) {
|
|
144
|
+
// If result contains actual papers (has title field), use it
|
|
145
|
+
results = response.data.result;
|
|
146
|
+
}
|
|
147
|
+
if (results && results.length > 0) {
|
|
148
|
+
for (const result of results) {
|
|
149
|
+
const paper = this.parseResult(result);
|
|
150
|
+
if (paper) {
|
|
151
|
+
// If openAccess filter was requested but using Meta API, filter results
|
|
152
|
+
if (customOptions.openAccess && !useOpenAccess && result.openaccess !== 'true') {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
papers.push(paper);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return papers;
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
if (error.response?.status === 401) {
|
|
163
|
+
this.handleHttpError(error, 'search');
|
|
164
|
+
}
|
|
165
|
+
if (error.response?.status === 403) {
|
|
166
|
+
// Some filters require premium access
|
|
167
|
+
logWarn('Springer API returned 403 - some filters may require premium access');
|
|
168
|
+
// Try a simpler query without advanced filters
|
|
169
|
+
if (options.year || customOptions.subject) {
|
|
170
|
+
logDebug('Retrying without year/subject filters...');
|
|
171
|
+
const simpleOptions = { ...options };
|
|
172
|
+
delete simpleOptions.year;
|
|
173
|
+
delete simpleOptions.subject;
|
|
174
|
+
return this.search(query, simpleOptions);
|
|
175
|
+
}
|
|
176
|
+
this.handleHttpError(error, 'search');
|
|
177
|
+
}
|
|
178
|
+
if (error.response?.status === 429) {
|
|
179
|
+
this.handleHttpError(error, 'search');
|
|
180
|
+
}
|
|
181
|
+
this.handleHttpError(error, 'search');
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
parseResult(result) {
|
|
185
|
+
try {
|
|
186
|
+
// Extract authors
|
|
187
|
+
const authors = result.creators?.map(c => c.creator).join(', ') || '';
|
|
188
|
+
// Extract URL
|
|
189
|
+
let paperUrl;
|
|
190
|
+
let pdfUrl;
|
|
191
|
+
if (result.url && result.url.length > 0) {
|
|
192
|
+
for (const urlObj of result.url) {
|
|
193
|
+
if (urlObj.format === 'pdf') {
|
|
194
|
+
pdfUrl = urlObj.value;
|
|
195
|
+
}
|
|
196
|
+
else if (!paperUrl) {
|
|
197
|
+
paperUrl = urlObj.value;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// If no URL found, construct from DOI
|
|
202
|
+
if (!paperUrl && result.doi) {
|
|
203
|
+
paperUrl = `https://doi.org/${result.doi}`;
|
|
204
|
+
}
|
|
205
|
+
// Extract page range
|
|
206
|
+
let pages;
|
|
207
|
+
if (result.startingPage && result.endingPage) {
|
|
208
|
+
pages = `${result.startingPage}-${result.endingPage}`;
|
|
209
|
+
}
|
|
210
|
+
else if (result.startingPage) {
|
|
211
|
+
pages = result.startingPage;
|
|
212
|
+
}
|
|
213
|
+
return PaperFactory.create({
|
|
214
|
+
paperId: result.doi || result.identifier || '',
|
|
215
|
+
title: result.title || '',
|
|
216
|
+
authors: authors ? authors.split(', ') : [],
|
|
217
|
+
abstract: result.abstract || '',
|
|
218
|
+
doi: result.doi,
|
|
219
|
+
publishedDate: result.publicationDate ? new Date(result.publicationDate) : null,
|
|
220
|
+
pdfUrl: pdfUrl,
|
|
221
|
+
url: paperUrl,
|
|
222
|
+
source: 'springer',
|
|
223
|
+
journal: result.publicationName,
|
|
224
|
+
volume: result.volume,
|
|
225
|
+
issue: result.number,
|
|
226
|
+
pages: pages,
|
|
227
|
+
extra: {
|
|
228
|
+
isbn: result.isbn,
|
|
229
|
+
issn: result.issn,
|
|
230
|
+
contentType: result.contentType,
|
|
231
|
+
genre: result.genre,
|
|
232
|
+
language: result.language,
|
|
233
|
+
openAccess: result.openaccess === 'true',
|
|
234
|
+
copyright: result.copyright
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
logDebug('Error parsing Springer result:', error);
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async downloadPdf(doi, options = {}) {
|
|
244
|
+
// Search for the paper and check if it has a PDF URL
|
|
245
|
+
const papers = await this.search(doi, { maxResults: 1 });
|
|
246
|
+
if (papers.length === 0) {
|
|
247
|
+
throw new Error('Paper not found');
|
|
248
|
+
}
|
|
249
|
+
if (!papers[0].pdfUrl) {
|
|
250
|
+
// Try searching with openAccess filter to get PDF links
|
|
251
|
+
const openAccessPapers = await this.search(doi, { maxResults: 1, openAccess: true });
|
|
252
|
+
if (openAccessPapers.length === 0 || !openAccessPapers[0].pdfUrl) {
|
|
253
|
+
throw new Error('PDF not available (may require institutional access or not be open access)');
|
|
254
|
+
}
|
|
255
|
+
papers[0] = openAccessPapers[0];
|
|
256
|
+
}
|
|
257
|
+
const paper = papers[0];
|
|
258
|
+
if (!paper.pdfUrl) {
|
|
259
|
+
throw new Error('PDF URL not available for this paper');
|
|
260
|
+
}
|
|
261
|
+
// Download PDF
|
|
262
|
+
const fs = await import('fs');
|
|
263
|
+
const path = await import('path');
|
|
264
|
+
const savePath = options.savePath || './downloads';
|
|
265
|
+
if (!fs.existsSync(savePath)) {
|
|
266
|
+
fs.mkdirSync(savePath, { recursive: true });
|
|
267
|
+
}
|
|
268
|
+
const fileName = `${doi.replace(/[\/\\:*?"<>|]/g, '_')}.pdf`;
|
|
269
|
+
const filePath = path.join(savePath, fileName);
|
|
270
|
+
try {
|
|
271
|
+
const response = await ErrorHandler.retryWithBackoff(() => axios.get(paper.pdfUrl, { responseType: 'stream' }), { context: 'Springer download' });
|
|
272
|
+
const writer = fs.createWriteStream(filePath);
|
|
273
|
+
response.data.pipe(writer);
|
|
274
|
+
return new Promise((resolve, reject) => {
|
|
275
|
+
writer.on('finish', () => resolve(filePath));
|
|
276
|
+
writer.on('error', reject);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
throw new Error(`Failed to download PDF: ${error.message}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
getCapabilities() {
|
|
284
|
+
return {
|
|
285
|
+
search: true,
|
|
286
|
+
download: true,
|
|
287
|
+
fullText: false,
|
|
288
|
+
citations: true,
|
|
289
|
+
requiresApiKey: true,
|
|
290
|
+
supportedOptions: ['maxResults', 'year', 'author', 'journal', 'openAccess', 'subject', 'type']
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* 获取引用此论文的文献列表(通过Crossref/OpenCitations)
|
|
295
|
+
*/
|
|
296
|
+
async getCitations(doi) {
|
|
297
|
+
try {
|
|
298
|
+
// Validate DOI format
|
|
299
|
+
if (!doi || typeof doi !== 'string') {
|
|
300
|
+
throw new Error('Invalid DOI: must be a non-empty string');
|
|
301
|
+
}
|
|
302
|
+
// Sanitize and validate DOI
|
|
303
|
+
const doiValidation = sanitizeDoi(doi);
|
|
304
|
+
if (!doiValidation.valid) {
|
|
305
|
+
throw new Error(`Invalid DOI: ${doiValidation.error}`);
|
|
306
|
+
}
|
|
307
|
+
const { CrossrefSearcher } = await import('./CrossrefSearcher.js');
|
|
308
|
+
const crossref = new CrossrefSearcher();
|
|
309
|
+
// Add timeout wrapper for Crossref API calls
|
|
310
|
+
return await withTimeout(crossref.getCitations(doiValidation.sanitized), TIMEOUTS.HEALTH_CHECK, 'Crossref citation request timed out');
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
// Don't log the DOI in case it's sensitive
|
|
314
|
+
logDebug('Error getting Springer citations:', error instanceof Error ? error.message : 'Unknown error');
|
|
315
|
+
return [];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* 获取论文的参考文献列表(通过Crossref)
|
|
320
|
+
*/
|
|
321
|
+
async getReferences(doi) {
|
|
322
|
+
try {
|
|
323
|
+
// Validate DOI format
|
|
324
|
+
if (!doi || typeof doi !== 'string') {
|
|
325
|
+
throw new Error('Invalid DOI: must be a non-empty string');
|
|
326
|
+
}
|
|
327
|
+
// Sanitize and validate DOI
|
|
328
|
+
const doiValidation = sanitizeDoi(doi);
|
|
329
|
+
if (!doiValidation.valid) {
|
|
330
|
+
throw new Error(`Invalid DOI: ${doiValidation.error}`);
|
|
331
|
+
}
|
|
332
|
+
const { CrossrefSearcher } = await import('./CrossrefSearcher.js');
|
|
333
|
+
const crossref = new CrossrefSearcher();
|
|
334
|
+
// Add timeout wrapper for Crossref API calls
|
|
335
|
+
return await withTimeout(crossref.getReferences(doiValidation.sanitized), TIMEOUTS.HEALTH_CHECK, 'Crossref references request timed out');
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
// Don't log the DOI in case it's sensitive
|
|
339
|
+
logDebug('Error getting Springer references:', error instanceof Error ? error.message : 'Unknown error');
|
|
340
|
+
return [];
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* 清理和转义查询参数中的特殊字符
|
|
345
|
+
*/
|
|
346
|
+
sanitizeQueryValue(value, context = 'general') {
|
|
347
|
+
return escapeQueryValue(value, 'springer');
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Test if OpenAccess API is available for this API key
|
|
351
|
+
* Uses promise caching to prevent race conditions with concurrent requests
|
|
352
|
+
*/
|
|
353
|
+
async testOpenAccessAPI() {
|
|
354
|
+
// Already tested
|
|
355
|
+
if (this.hasOpenAccessAPI !== undefined) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
// Test already in progress - wait for it
|
|
359
|
+
if (this.testingPromise) {
|
|
360
|
+
return this.testingPromise;
|
|
361
|
+
}
|
|
362
|
+
// Start new test and cache the promise
|
|
363
|
+
this.testingPromise = this.performOpenAccessTest();
|
|
364
|
+
try {
|
|
365
|
+
await this.testingPromise;
|
|
366
|
+
}
|
|
367
|
+
finally {
|
|
368
|
+
this.testingPromise = null;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Perform the actual OpenAccess API test
|
|
373
|
+
*/
|
|
374
|
+
async performOpenAccessTest() {
|
|
375
|
+
try {
|
|
376
|
+
const response = await this.openAccessClient.get('/json', {
|
|
377
|
+
params: {
|
|
378
|
+
q: 'test',
|
|
379
|
+
api_key: this.openAccessApiKey,
|
|
380
|
+
s: 1,
|
|
381
|
+
p: 1
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
this.hasOpenAccessAPI = response.status === 200;
|
|
385
|
+
logDebug('OpenAccess API is available');
|
|
386
|
+
}
|
|
387
|
+
catch (error) {
|
|
388
|
+
if (error.response?.status === 401) {
|
|
389
|
+
this.hasOpenAccessAPI = false;
|
|
390
|
+
logDebug('OpenAccess API is not available (401 Unauthorized - check API key permissions)');
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
// Network error or other issue, assume not available
|
|
394
|
+
this.hasOpenAccessAPI = false;
|
|
395
|
+
logDebug('OpenAccess API test failed:', error.message);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
async readPaper(paperId, options = {}) {
|
|
400
|
+
const papers = await this.search(paperId, { maxResults: 1 });
|
|
401
|
+
if (papers.length === 0) {
|
|
402
|
+
throw new Error('Paper not found');
|
|
403
|
+
}
|
|
404
|
+
return papers[0].abstract || 'Abstract not available';
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
//# sourceMappingURL=SpringerSearcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpringerSearcher.js","sourceRoot":"","sources":["../../src/platforms/SpringerSearcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Paper } from '../models/Paper.js';
|
|
2
|
+
import { PaperSource, SearchOptions, DownloadOptions, PlatformCapabilities } from './PaperSource.js';
|
|
3
|
+
export declare class UnpaywallSearcher extends PaperSource {
|
|
4
|
+
private readonly client;
|
|
5
|
+
private readonly email;
|
|
6
|
+
constructor(email?: string);
|
|
7
|
+
getCapabilities(): PlatformCapabilities;
|
|
8
|
+
hasApiKey(): boolean;
|
|
9
|
+
search(query: string, _options?: SearchOptions): Promise<Paper[]>;
|
|
10
|
+
getPaperByDoi(doi: string): Promise<Paper | null>;
|
|
11
|
+
resolveBestPdfUrl(doi: string): Promise<string>;
|
|
12
|
+
downloadPdf(_paperId: string, _options?: DownloadOptions): Promise<string>;
|
|
13
|
+
readPaper(_paperId: string, _options?: DownloadOptions): Promise<string>;
|
|
14
|
+
private parseRecord;
|
|
15
|
+
private firstLocationUrl;
|
|
16
|
+
private extractDoi;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=UnpaywallSearcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UnpaywallSearcher.d.ts","sourceRoot":"","sources":["../../src/platforms/UnpaywallSearcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAgB,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AA+BrG,qBAAa,iBAAkB,SAAQ,WAAW;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,KAAK,CAAC,EAAE,MAAM;IAgB1B,eAAe,IAAI,oBAAoB;IAWvC,SAAS,IAAI,OAAO;IAId,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,GAAE,aAAkB,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAQrE,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAqBjD,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK/C,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IAI9E,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IAIlF,OAAO,CAAC,WAAW;IA6BnB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,UAAU;CAKnB"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { PaperFactory } from '../models/Paper.js';
|
|
3
|
+
import { PaperSource } from './PaperSource.js';
|
|
4
|
+
import { TIMEOUTS, USER_AGENT } from '../config/constants.js';
|
|
5
|
+
import { sanitizeDoi } from '../utils/SecurityUtils.js';
|
|
6
|
+
export class UnpaywallSearcher extends PaperSource {
|
|
7
|
+
client;
|
|
8
|
+
email;
|
|
9
|
+
constructor(email) {
|
|
10
|
+
const configuredEmail = email || process.env.PAPER_SEARCH_UNPAYWALL_EMAIL || process.env.UNPAYWALL_EMAIL || '';
|
|
11
|
+
super('unpaywall', 'https://api.unpaywall.org/v2', configuredEmail);
|
|
12
|
+
this.email = configuredEmail.trim();
|
|
13
|
+
this.client = axios.create({
|
|
14
|
+
baseURL: this.baseUrl,
|
|
15
|
+
timeout: TIMEOUTS.DEFAULT,
|
|
16
|
+
headers: {
|
|
17
|
+
Accept: 'application/json',
|
|
18
|
+
'User-Agent': `${USER_AGENT} (https://github.com/dr-dumpling/paper-search-cli)`
|
|
19
|
+
},
|
|
20
|
+
validateStatus: status => status < 500
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
getCapabilities() {
|
|
24
|
+
return {
|
|
25
|
+
search: true,
|
|
26
|
+
download: false,
|
|
27
|
+
fullText: false,
|
|
28
|
+
citations: false,
|
|
29
|
+
requiresApiKey: true,
|
|
30
|
+
supportedOptions: ['maxResults']
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
hasApiKey() {
|
|
34
|
+
return Boolean(this.email);
|
|
35
|
+
}
|
|
36
|
+
async search(query, _options = {}) {
|
|
37
|
+
const doi = this.extractDoi(query);
|
|
38
|
+
if (!doi || !this.email)
|
|
39
|
+
return [];
|
|
40
|
+
const paper = await this.getPaperByDoi(doi);
|
|
41
|
+
return paper ? [paper] : [];
|
|
42
|
+
}
|
|
43
|
+
async getPaperByDoi(doi) {
|
|
44
|
+
if (!this.email)
|
|
45
|
+
return null;
|
|
46
|
+
const cleanDoi = this.extractDoi(doi);
|
|
47
|
+
if (!cleanDoi)
|
|
48
|
+
return null;
|
|
49
|
+
try {
|
|
50
|
+
const response = await this.client.get(`/${encodeURIComponent(cleanDoi)}`, {
|
|
51
|
+
params: { email: this.email }
|
|
52
|
+
});
|
|
53
|
+
if (response.status === 404 || response.status === 422)
|
|
54
|
+
return null;
|
|
55
|
+
if (response.status >= 400) {
|
|
56
|
+
throw new Error(`Unpaywall request failed with HTTP ${response.status}`);
|
|
57
|
+
}
|
|
58
|
+
return this.parseRecord(response.data, cleanDoi);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
this.handleHttpError(error, 'getPaperByDoi');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async resolveBestPdfUrl(doi) {
|
|
65
|
+
const paper = await this.getPaperByDoi(doi);
|
|
66
|
+
return paper?.pdfUrl || '';
|
|
67
|
+
}
|
|
68
|
+
async downloadPdf(_paperId, _options = {}) {
|
|
69
|
+
throw new Error('Unpaywall does not host PDFs. Use returned pdf_url with download_with_fallback.');
|
|
70
|
+
}
|
|
71
|
+
async readPaper(_paperId, _options = {}) {
|
|
72
|
+
throw new Error('Unpaywall provides OA metadata only; it does not provide full text.');
|
|
73
|
+
}
|
|
74
|
+
parseRecord(record, doi) {
|
|
75
|
+
const best = record.best_oa_location || {};
|
|
76
|
+
const pdfUrl = best.url_for_pdf || best.url || this.firstLocationUrl(record.oa_locations || []);
|
|
77
|
+
const authors = (record.z_authors || [])
|
|
78
|
+
.map(author => `${author.given || ''} ${author.family || ''}`.trim())
|
|
79
|
+
.filter(Boolean);
|
|
80
|
+
return PaperFactory.create({
|
|
81
|
+
paperId: `unpaywall:${doi}`,
|
|
82
|
+
title: record.title || doi,
|
|
83
|
+
authors,
|
|
84
|
+
abstract: '',
|
|
85
|
+
doi: record.doi || doi,
|
|
86
|
+
publishedDate: record.published_date ? this.parseDate(record.published_date) : null,
|
|
87
|
+
pdfUrl: pdfUrl || '',
|
|
88
|
+
url: best.url || record.doi_url || `https://doi.org/${doi}`,
|
|
89
|
+
source: 'unpaywall',
|
|
90
|
+
journal: record.journal_name || '',
|
|
91
|
+
extra: {
|
|
92
|
+
isOpenAccess: Boolean(record.is_oa),
|
|
93
|
+
oaStatus: record.oa_status || '',
|
|
94
|
+
publisher: record.publisher || '',
|
|
95
|
+
hostType: best.host_type || '',
|
|
96
|
+
license: best.license || '',
|
|
97
|
+
version: best.version || ''
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
firstLocationUrl(locations) {
|
|
102
|
+
for (const location of locations) {
|
|
103
|
+
const candidate = location.url_for_pdf || location.url || '';
|
|
104
|
+
if (candidate)
|
|
105
|
+
return candidate;
|
|
106
|
+
}
|
|
107
|
+
return '';
|
|
108
|
+
}
|
|
109
|
+
extractDoi(value) {
|
|
110
|
+
const candidate = value.match(/10\.\d{4,9}\/[-._;()/:A-Z0-9]+/i)?.[0] || value;
|
|
111
|
+
const result = sanitizeDoi(candidate);
|
|
112
|
+
return result.valid ? result.sanitized : '';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=UnpaywallSearcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UnpaywallSearcher.js","sourceRoot":"","sources":["../../src/platforms/UnpaywallSearcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAwB,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAS,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAwD,MAAM,kBAAkB,CAAC;AACrG,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AA6BxD,MAAM,OAAO,iBAAkB,SAAQ,WAAW;IAC/B,MAAM,CAAgB;IACtB,KAAK,CAAS;IAE/B,YAAY,KAAc;QACxB,MAAM,eAAe,GACnB,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC;QACzF,KAAK,CAAC,WAAW,EAAE,8BAA8B,EAAE,eAAe,CAAC,CAAC;QACpE,IAAI,CAAC,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,CAAC;QACpC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;YACzB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,OAAO,EAAE;gBACP,MAAM,EAAE,kBAAkB;gBAC1B,YAAY,EAAE,GAAG,UAAU,oDAAoD;aAChF;YACD,cAAc,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,GAAG,GAAG;SACvC,CAAC,CAAC;IACL,CAAC;IAED,eAAe;QACb,OAAO;YACL,MAAM,EAAE,IAAI;YACZ,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,KAAK;YACf,SAAS,EAAE,KAAK;YAChB,cAAc,EAAE,IAAI;YACpB,gBAAgB,EAAE,CAAC,YAAY,CAAC;SACjC,CAAC;IACJ,CAAC;IAED,SAAS;QACP,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,WAA0B,EAAE;QACtD,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,CAAC;QAEnC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QAC5C,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,GAAW;QAC7B,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,QAAQ,CAAC,EAAE,EAAE;gBACzE,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;aAC9B,CAAC,CAAC;YAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;gBAAE,OAAO,IAAI,CAAC;YACpE,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;gBAC3B,MAAM,IAAI,KAAK,CAAC,sCAAsC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YAC3E,CAAC;YAED,OAAO,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAuB,EAAE,QAAQ,CAAC,CAAC;QACtE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,GAAW;QACjC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QAC5C,OAAO,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,QAAgB,EAAE,WAA4B,EAAE;QAChE,MAAM,IAAI,KAAK,CAAC,iFAAiF,CAAC,CAAC;IACrG,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAAgB,EAAE,WAA4B,EAAE;QAC9D,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;IACzF,CAAC;IAEO,WAAW,CAAC,MAAuB,EAAE,GAAW;QACtD,MAAM,IAAI,GAAG,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;QAChG,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC;aACrC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,IAAI,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;aACpE,MAAM,CAAC,OAAO,CAAC,CAAC;QAEnB,OAAO,YAAY,CAAC,MAAM,CAAC;YACzB,OAAO,EAAE,aAAa,GAAG,EAAE;YAC3B,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,GAAG;YAC1B,OAAO;YACP,QAAQ,EAAE,EAAE;YACZ,GAAG,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG;YACtB,aAAa,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI;YACnF,MAAM,EAAE,MAAM,IAAI,EAAE;YACpB,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,MAAM,CAAC,OAAO,IAAI,mBAAmB,GAAG,EAAE;YAC3D,MAAM,EAAE,WAAW;YACnB,OAAO,EAAE,MAAM,CAAC,YAAY,IAAI,EAAE;YAClC,KAAK,EAAE;gBACL,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;gBACnC,QAAQ,EAAE,MAAM,CAAC,SAAS,IAAI,EAAE;gBAChC,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,EAAE;gBACjC,QAAQ,EAAE,IAAI,CAAC,SAAS,IAAI,EAAE;gBAC9B,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,EAAE;gBAC3B,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,EAAE;aAC5B;SACF,CAAC,CAAC;IACL,CAAC;IAEO,gBAAgB,CAAC,SAA8B;QACrD,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,SAAS,GAAG,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC,GAAG,IAAI,EAAE,CAAC;YAC7D,IAAI,SAAS;gBAAE,OAAO,SAAS,CAAC;QAClC,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAEO,UAAU,CAAC,KAAa;QAC9B,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,iCAAiC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;QAC/E,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;QACtC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,CAAC;CACF"}
|