@mantra-ai/core 0.4.1 → 0.4.3
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/dist/discovery/ranking.d.ts +19 -3
- package/dist/discovery/ranking.d.ts.map +1 -1
- package/dist/discovery/ranking.js +54 -13
- package/dist/discovery/ranking.js.map +1 -1
- package/dist/discovery/registry.d.ts.map +1 -1
- package/dist/discovery/registry.js +4 -1
- package/dist/discovery/registry.js.map +1 -1
- package/dist/discovery/types.d.ts +7 -4
- package/dist/discovery/types.d.ts.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/sources/crossref/client.d.ts +70 -0
- package/dist/sources/crossref/client.d.ts.map +1 -1
- package/dist/sources/crossref/client.js +190 -3
- package/dist/sources/crossref/client.js.map +1 -1
- package/dist/sources/crossref/provider.d.ts +20 -0
- package/dist/sources/crossref/provider.d.ts.map +1 -0
- package/dist/sources/crossref/provider.js +39 -0
- package/dist/sources/crossref/provider.js.map +1 -0
- package/dist/sources/opencitations/citation-graph-provider.d.ts +28 -0
- package/dist/sources/opencitations/citation-graph-provider.d.ts.map +1 -0
- package/dist/sources/opencitations/citation-graph-provider.js +136 -0
- package/dist/sources/opencitations/citation-graph-provider.js.map +1 -0
- package/dist/sources/opencitations/client.d.ts +29 -0
- package/dist/sources/opencitations/client.d.ts.map +1 -0
- package/dist/sources/opencitations/client.js +108 -0
- package/dist/sources/opencitations/client.js.map +1 -0
- package/package.json +1 -1
- package/dist/sources/semantic-scholar/citation-graph-provider.d.ts +0 -19
- package/dist/sources/semantic-scholar/citation-graph-provider.d.ts.map +0 -1
- package/dist/sources/semantic-scholar/citation-graph-provider.js +0 -91
- package/dist/sources/semantic-scholar/citation-graph-provider.js.map +0 -1
|
@@ -6,15 +6,31 @@ import type { DiscoverCandidateCore, DiscoverSourceKind } from "./types";
|
|
|
6
6
|
* - Mantra FTS: BM25 rank (negative float, lower = better match)
|
|
7
7
|
* - Zotero: match-location score (negative int, e.g. -15 for title match)
|
|
8
8
|
* - Semantic Scholar: position in relevance-ordered results (0-based index)
|
|
9
|
+
* - Crossref: raw relevance score (positive float, higher = better match)
|
|
9
10
|
* - null score: treated as 0.5 (neutral)
|
|
10
11
|
*/
|
|
11
12
|
export declare function normalizeScore(raw: number | null, sourceKind: DiscoverSourceKind, opts?: {
|
|
12
13
|
resultCount?: number;
|
|
14
|
+
maxRawScore?: number;
|
|
13
15
|
}): number;
|
|
14
16
|
/**
|
|
15
|
-
* Merge candidates from multiple sources.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
17
|
+
* Merge candidates from multiple sources into a deduplicated list.
|
|
18
|
+
*
|
|
19
|
+
* Dedup strategy:
|
|
20
|
+
* 1. DOI is the primary key: all candidates sharing the same normalized DOI
|
|
21
|
+
* are merged into one entry.
|
|
22
|
+
* 2. For DOI-less candidates, normalized title (≥10 chars) is the secondary key.
|
|
23
|
+
* Title-keyed entries that match a DOI-keyed entry are reconciled against it
|
|
24
|
+
* via a pre-built reverse index (normalizedTitle → doiKey), giving O(1)
|
|
25
|
+
* lookups instead of scanning byDoi for each title entry.
|
|
26
|
+
* 3. Candidates without a usable DOI or title fall into `unkeyed` and pass
|
|
27
|
+
* through unmerged.
|
|
28
|
+
*
|
|
29
|
+
* Invariants:
|
|
30
|
+
* - DOI dedup is always preferred over title dedup.
|
|
31
|
+
* - When multiple DOI-keyed entries share the same normalized title, the first
|
|
32
|
+
* encountered DOI candidate wins (Map insertion-order behavior).
|
|
33
|
+
* - Output order: [...byDoi.values(), ...unkeyed].
|
|
18
34
|
*/
|
|
19
35
|
export declare function mergeCandidates(candidates: DiscoverCandidateCore[]): DiscoverCandidateCore[];
|
|
20
36
|
export interface RankOpts {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ranking.d.ts","sourceRoot":"","sources":["../../src/discovery/ranking.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAkBzE
|
|
1
|
+
{"version":3,"file":"ranking.d.ts","sourceRoot":"","sources":["../../src/discovery/ranking.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAkBzE;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,GAAG,IAAI,EAClB,UAAU,EAAE,kBAAkB,EAC9B,IAAI,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACpD,MAAM,CAuCR;AAiGD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,qBAAqB,EAAE,GAClC,qBAAqB,EAAE,CAyDzB;AAID,MAAM,WAAW,QAAQ;IACvB,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,qBAAqB,EAAE,EACnC,IAAI,CAAC,EAAE,QAAQ,GACd,qBAAqB,EAAE,CAoCzB"}
|
|
@@ -15,6 +15,7 @@ const CITATION_SATURATION = 10_000;
|
|
|
15
15
|
* - Mantra FTS: BM25 rank (negative float, lower = better match)
|
|
16
16
|
* - Zotero: match-location score (negative int, e.g. -15 for title match)
|
|
17
17
|
* - Semantic Scholar: position in relevance-ordered results (0-based index)
|
|
18
|
+
* - Crossref: raw relevance score (positive float, higher = better match)
|
|
18
19
|
* - null score: treated as 0.5 (neutral)
|
|
19
20
|
*/
|
|
20
21
|
export function normalizeScore(raw, sourceKind, opts) {
|
|
@@ -39,6 +40,14 @@ export function normalizeScore(raw, sourceKind, opts) {
|
|
|
39
40
|
const count = opts?.resultCount ?? 20;
|
|
40
41
|
return Math.min(1, raw / Math.max(1, count));
|
|
41
42
|
}
|
|
43
|
+
case "crossref": {
|
|
44
|
+
// Crossref returns a positive relevance score (higher = better).
|
|
45
|
+
// Normalize as 1 - (score / maxScoreInBatch) so top result → ~0.
|
|
46
|
+
const max = opts?.maxRawScore;
|
|
47
|
+
if (max == null || max <= 0)
|
|
48
|
+
return 0.5;
|
|
49
|
+
return Math.max(0, Math.min(1, 1 - raw / max));
|
|
50
|
+
}
|
|
42
51
|
default: {
|
|
43
52
|
// Unknown source: assume raw is already roughly 0–1 or treat as neutral.
|
|
44
53
|
if (raw >= 0 && raw <= 1)
|
|
@@ -131,9 +140,23 @@ function bestRawScore(a, b) {
|
|
|
131
140
|
return Math.min(a, b);
|
|
132
141
|
}
|
|
133
142
|
/**
|
|
134
|
-
* Merge candidates from multiple sources.
|
|
135
|
-
*
|
|
136
|
-
*
|
|
143
|
+
* Merge candidates from multiple sources into a deduplicated list.
|
|
144
|
+
*
|
|
145
|
+
* Dedup strategy:
|
|
146
|
+
* 1. DOI is the primary key: all candidates sharing the same normalized DOI
|
|
147
|
+
* are merged into one entry.
|
|
148
|
+
* 2. For DOI-less candidates, normalized title (≥10 chars) is the secondary key.
|
|
149
|
+
* Title-keyed entries that match a DOI-keyed entry are reconciled against it
|
|
150
|
+
* via a pre-built reverse index (normalizedTitle → doiKey), giving O(1)
|
|
151
|
+
* lookups instead of scanning byDoi for each title entry.
|
|
152
|
+
* 3. Candidates without a usable DOI or title fall into `unkeyed` and pass
|
|
153
|
+
* through unmerged.
|
|
154
|
+
*
|
|
155
|
+
* Invariants:
|
|
156
|
+
* - DOI dedup is always preferred over title dedup.
|
|
157
|
+
* - When multiple DOI-keyed entries share the same normalized title, the first
|
|
158
|
+
* encountered DOI candidate wins (Map insertion-order behavior).
|
|
159
|
+
* - Output order: [...byDoi.values(), ...unkeyed].
|
|
137
160
|
*/
|
|
138
161
|
export function mergeCandidates(candidates) {
|
|
139
162
|
const byDoi = new Map();
|
|
@@ -166,17 +189,26 @@ export function mergeCandidates(candidates) {
|
|
|
166
189
|
}
|
|
167
190
|
unkeyed.push(c);
|
|
168
191
|
}
|
|
169
|
-
//
|
|
192
|
+
// Build a reverse index: normalizedTitle → doi key for O(1) cross-check lookups.
|
|
193
|
+
// Preserves first-insertion semantics (first DOI candidate wins for duplicate normalized titles).
|
|
194
|
+
const doiByNormalizedTitle = new Map();
|
|
195
|
+
for (const [doiKey, doiCandidate] of byDoi) {
|
|
196
|
+
if (!doiCandidate.title)
|
|
197
|
+
continue;
|
|
198
|
+
const nt = normalizeTitle(doiCandidate.title);
|
|
199
|
+
if (!doiByNormalizedTitle.has(nt)) {
|
|
200
|
+
doiByNormalizedTitle.set(nt, doiKey);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Cross-check: title-keyed entries that also match a DOI-keyed entry.
|
|
204
|
+
// O(1) lookup via the pre-built normalized-title index.
|
|
170
205
|
for (const [titleKey, titleCandidate] of byTitle) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
merged = true;
|
|
176
|
-
break;
|
|
177
|
-
}
|
|
206
|
+
const doiKey = doiByNormalizedTitle.get(titleKey);
|
|
207
|
+
if (doiKey !== undefined) {
|
|
208
|
+
const doiCandidate = byDoi.get(doiKey);
|
|
209
|
+
byDoi.set(doiKey, mergeTwo(doiCandidate, titleCandidate));
|
|
178
210
|
}
|
|
179
|
-
|
|
211
|
+
else {
|
|
180
212
|
unkeyed.push(titleCandidate);
|
|
181
213
|
}
|
|
182
214
|
}
|
|
@@ -189,10 +221,19 @@ export function mergeCandidates(candidates) {
|
|
|
189
221
|
* 3. Sort ascending (lower = better). Deterministic tiebreak by title.
|
|
190
222
|
*/
|
|
191
223
|
export function rankCandidates(candidates, opts) {
|
|
224
|
+
// Pre-compute max raw score for Crossref candidates (Option B normalization)
|
|
225
|
+
const crossrefScores = candidates
|
|
226
|
+
.filter((c) => c.sources[0] === "crossref" && c.score != null)
|
|
227
|
+
.map((c) => c.score);
|
|
228
|
+
const crossrefMaxScore = crossrefScores.length > 0
|
|
229
|
+
? Math.max(...crossrefScores)
|
|
230
|
+
: undefined;
|
|
192
231
|
const scored = candidates.map((c) => {
|
|
193
232
|
// Use the first source for normalization context
|
|
194
233
|
const primarySource = c.sources[0] ?? "unknown";
|
|
195
|
-
const normalized = normalizeScore(c.score, primarySource
|
|
234
|
+
const normalized = normalizeScore(c.score, primarySource, {
|
|
235
|
+
maxRawScore: crossrefMaxScore,
|
|
236
|
+
});
|
|
196
237
|
// Boosts are subtracted (lower = better)
|
|
197
238
|
const finalScore = normalized -
|
|
198
239
|
localBoost(c) -
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ranking.js","sourceRoot":"","sources":["../../src/discovery/ranking.ts"],"names":[],"mappings":"AAEA,wEAAwE;AAExE,6DAA6D;AAC7D,MAAM,WAAW,GAAG,IAAI,CAAC;AAEzB,sDAAsD;AACtD,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC,mDAAmD;AACnD,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,4DAA4D;AAC5D,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEnC,wEAAwE;AAExE
|
|
1
|
+
{"version":3,"file":"ranking.js","sourceRoot":"","sources":["../../src/discovery/ranking.ts"],"names":[],"mappings":"AAEA,wEAAwE;AAExE,6DAA6D;AAC7D,MAAM,WAAW,GAAG,IAAI,CAAC;AAEzB,sDAAsD;AACtD,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC,mDAAmD;AACnD,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,4DAA4D;AAC5D,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEnC,wEAAwE;AAExE;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAC5B,GAAkB,EAClB,UAA8B,EAC9B,IAAqD;IAErD,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,GAAG,CAAC;IAE7B,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,gDAAgD;YAChD,wCAAwC;YACxC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YAChD,OAAO,CAAC,OAAO,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;QAC7B,CAAC;QAED,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,yDAAyD;YACzD,yBAAyB;YACzB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YAChD,OAAO,CAAC,OAAO,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;QAC7B,CAAC;QAED,KAAK,kBAAkB,CAAC,CAAC,CAAC;YACxB,2DAA2D;YAC3D,wDAAwD;YACxD,MAAM,KAAK,GAAG,IAAI,EAAE,WAAW,IAAI,EAAE,CAAC;YACtC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;QAC/C,CAAC;QAED,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,iEAAiE;YACjE,iEAAiE;YACjE,MAAM,GAAG,GAAG,IAAI,EAAE,WAAW,CAAC;YAC9B,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,IAAI,CAAC;gBAAE,OAAO,GAAG,CAAC;YACxC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC;QACjD,CAAC;QAED,OAAO,CAAC,CAAC,CAAC;YACR,yEAAyE;YACzE,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;gBAAE,OAAO,GAAG,CAAC;YACrC,OAAO,GAAG,CAAC;QACb,CAAC;IACH,CAAC;AACH,CAAC;AAED,wEAAwE;AAExE,SAAS,UAAU,CAAC,SAAgC;IAClD,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAC5C,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,WAAW,CACtD,CAAC;IACF,OAAO,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,aAAa,CAAC,SAAgC;IACrD,MAAM,KAAK,GAAG,SAAS,CAAC,aAAa,CAAC;IACtC,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAC1C,mEAAmE;IACnE,OAAO,IAAI,CAAC,GAAG,CACb,kBAAkB,EAClB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC,GAAG,kBAAkB,CAC/E,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,SAAgC;IACpD,IAAI,SAAS,CAAC,IAAI,IAAI,IAAI;QAAE,OAAO,CAAC,CAAC;IACrC,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC7C,MAAM,GAAG,GAAG,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC;IACzC,IAAI,GAAG,GAAG,CAAC;QAAE,OAAO,iBAAiB,CAAC,CAAC,6BAA6B;IACpE,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACvB,mDAAmD;IACnD,OAAO,iBAAiB,GAAG,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,wEAAwE;AAExE;;;GAGG;AACH,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK;SACT,WAAW,EAAE;SACb,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,QAAQ,CACf,CAAwB,EACxB,CAAwB;IAExB,OAAO;QACL,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG;QACnB,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK;QACzB,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO;QAC/B,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI;QACtB,OAAO,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACnD,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC;QACrC,mDAAmD;QACnD,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,QAAQ;QAClC,SAAS,EAAE,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS;QACrC,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO;QAC/B,0CAA0C;QAC1C,iBAAiB,EAAE,CAAC,CAAC,iBAAiB,IAAI,CAAC,CAAC,iBAAiB;QAC7D,aAAa,EAAE,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,aAAa;QACjD,wBAAwB,EAAE,CAAC,CAAC,wBAAwB,IAAI,CAAC,CAAC,wBAAwB;QAClF,eAAe,EAAE,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,eAAe;QACvD,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK;QACzB,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG;QACnB,uDAAuD;QACvD,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,WAAW;YAChC,CAAC,CAAC;gBACE,WAAW,EAAE,gBAAgB,CAAC;oBAC5B,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC;oBACxB,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC;iBACzB,CAAC;aACH;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CACvB,OAAuC;IAEvC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;QACnD,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAChC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,YAAY,CAAC,CAAgB,EAAE,CAAgB;IACtD,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,CAAC,CAAC;IACzB,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,CAAC,CAAC;IACzB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACxB,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,eAAe,CAC7B,UAAmC;IAEnC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAiC,CAAC;IACvD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAiC,CAAC;IACzD,MAAM,OAAO,GAA4B,EAAE,CAAC;IAE5C,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;YACV,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YAChC,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,QAAQ,EAAE,CAAC;gBACb,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;YACxC,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YAC3B,CAAC;YACD,SAAS;QACX,CAAC;QAED,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YACZ,MAAM,GAAG,GAAG,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACpC,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;gBACrB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAClC,IAAI,QAAQ,EAAE,CAAC;oBACb,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC1C,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC7B,CAAC;gBACD,SAAS;YACX,CAAC;QACH,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,iFAAiF;IACjF,kGAAkG;IAClG,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvD,KAAK,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,IAAI,KAAK,EAAE,CAAC;QAC3C,IAAI,CAAC,YAAY,CAAC,KAAK;YAAE,SAAS;QAClC,MAAM,EAAE,GAAG,cAAc,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAClC,oBAAoB,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAED,sEAAsE;IACtE,wDAAwD;IACxD,KAAK,MAAM,CAAC,QAAQ,EAAE,cAAc,CAAC,IAAI,OAAO,EAAE,CAAC;QACjD,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAClD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC;YACxC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC,CAAC;QAC5D,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,CAAC;AACzC,CAAC;AASD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,UAAmC,EACnC,IAAe;IAEf,6EAA6E;IAC7E,MAAM,cAAc,GAAG,UAAU;SAC9B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,UAAU,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC;SAC7D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAM,CAAC,CAAC;IACxB,MAAM,gBAAgB,GAAG,cAAc,CAAC,MAAM,GAAG,CAAC;QAChD,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC;QAC7B,CAAC,CAAC,SAAS,CAAC;IAEd,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAClC,iDAAiD;QACjD,MAAM,aAAa,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC;QAChD,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,KAAK,EAAE,aAAa,EAAE;YACxD,WAAW,EAAE,gBAAgB;SAC9B,CAAC,CAAC;QAEH,yCAAyC;QACzC,MAAM,UAAU,GACd,UAAU;YACV,UAAU,CAAC,CAAC,CAAC;YACb,aAAa,CAAC,CAAC,CAAC;YAChB,YAAY,CAAC,CAAC,CAAC,CAAC;QAElB,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,sDAAsD;IACtD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACnB,IAAI,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU;YAAE,OAAO,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;QACtE,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACvD,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACvD,OAAO,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC;IAC3C,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AACxD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/discovery/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAEjB;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAA0B;IAE3C,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAI1C,4DAA4D;IAC5D,YAAY,CAAC,MAAM,CAAC,EAAE;QACpB,UAAU,CAAC,EAAE,kBAAkB,CAAC;QAChC,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,GAAG,gBAAgB,EAAE;IAetB;;;;;OAKG;IACG,SAAS,CACb,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/discovery/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAEjB;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAA0B;IAE3C,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAI1C,4DAA4D;IAC5D,YAAY,CAAC,MAAM,CAAC,EAAE;QACpB,UAAU,CAAC,EAAE,kBAAkB,CAAC;QAChC,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,GAAG,gBAAgB,EAAE;IAetB;;;;;OAKG;IACG,SAAS,CACb,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,oBAAoB,CAAC;IA6ChC;;;;;OAKG;IACG,UAAU,CACd,UAAU,EAAE,qBAAqB,EAAE,GAClC,OAAO,CAAC;QAAE,UAAU,EAAE,qBAAqB,EAAE,CAAC;QAAC,MAAM,EAAE,aAAa,EAAE,CAAA;KAAE,CAAC;CAoB7E"}
|
|
@@ -28,9 +28,12 @@ export class ProviderRegistry {
|
|
|
28
28
|
*/
|
|
29
29
|
async searchAll(query, opts) {
|
|
30
30
|
const searchProviders = this.getProviders({ capability: "search" });
|
|
31
|
+
// localOnly and remoteOnly are mutually exclusive; localOnly takes precedence.
|
|
31
32
|
const active = opts?.localOnly
|
|
32
33
|
? searchProviders.filter((p) => p.isLocal)
|
|
33
|
-
:
|
|
34
|
+
: opts?.remoteOnly
|
|
35
|
+
? searchProviders.filter((p) => !p.isLocal)
|
|
36
|
+
: searchProviders;
|
|
34
37
|
const local = [];
|
|
35
38
|
const remote = [];
|
|
36
39
|
const errors = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/discovery/registry.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,MAAM,OAAO,gBAAgB;IACnB,SAAS,GAAuB,EAAE,CAAC;IAE3C,QAAQ,CAAC,QAA0B;QACjC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED,4DAA4D;IAC5D,YAAY,CAAC,MAGZ;QACC,IAAI,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAE1B,IAAI,MAAM,EAAE,UAAU,EAAE,CAAC;YACvB,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC;YAC9B,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,MAAM,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;YAC3B,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC;QACjD,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,SAAS,CACb,KAAa,EACb,IAAyB;QAEzB,MAAM,eAAe,GAAG,IAAI,CAAC,YAAY,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC;QAEpE,MAAM,MAAM,GAAG,IAAI,EAAE,SAAS;YAC5B,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YAC1C,CAAC,CAAC,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/discovery/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@ export type DiscoverCapability = "search" | "augment";
|
|
|
10
10
|
* Extensible source identifier.
|
|
11
11
|
* Built-in kinds are listed here; providers may add new ones.
|
|
12
12
|
*/
|
|
13
|
-
export type DiscoverSourceKind = "mantra" | "zotero" | "pdf-index" | "semantic-scholar" | "
|
|
13
|
+
export type DiscoverSourceKind = "mantra" | "zotero" | "pdf-index" | "semantic-scholar" | "crossref" | "opencitations-citation-graph" | (string & {});
|
|
14
14
|
/**
|
|
15
15
|
* Tracks which seed paper(s) led to a candidate during citation-graph expansion.
|
|
16
16
|
*/
|
|
@@ -41,11 +41,11 @@ export interface DiscoverCandidateCore {
|
|
|
41
41
|
zoteroKey?: string | null;
|
|
42
42
|
/** Resolved local PDF path — set by PDF index augmenter or Zotero. */
|
|
43
43
|
pdfPath?: string | null;
|
|
44
|
-
/** Semantic Scholar paper ID. */
|
|
44
|
+
/** Semantic Scholar paper ID (legacy, kept for backward compat). */
|
|
45
45
|
semanticScholarId?: string | null;
|
|
46
|
-
/** Total citation count. */
|
|
46
|
+
/** Total citation count (from Crossref is-referenced-by-count or S2). */
|
|
47
47
|
citationCount?: number | null;
|
|
48
|
-
/** Influential citation count (Semantic Scholar). */
|
|
48
|
+
/** Influential citation count (Semantic Scholar only, legacy). */
|
|
49
49
|
influentialCitationCount?: number | null;
|
|
50
50
|
/** Short abstract or snippet. */
|
|
51
51
|
abstractSnippet?: string | null;
|
|
@@ -88,7 +88,10 @@ export interface DiscoverProvider {
|
|
|
88
88
|
}
|
|
89
89
|
export interface RegistrySearchOpts {
|
|
90
90
|
limit?: number;
|
|
91
|
+
/** Only run local providers (Mantra, Zotero, PDF index). */
|
|
91
92
|
localOnly?: boolean;
|
|
93
|
+
/** Only run remote providers (Crossref, citation graph, etc.). */
|
|
94
|
+
remoteOnly?: boolean;
|
|
92
95
|
}
|
|
93
96
|
export interface RegistrySearchResult {
|
|
94
97
|
local: DiscoverCandidateCore[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/discovery/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,8BAA8B;AAC9B,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAItD;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,kBAAkB,GAClB,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/discovery/types.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -22,8 +22,12 @@ export type { DiscoverCapability, DiscoverSourceKind, DiscoverCandidateCore, Dis
|
|
|
22
22
|
export { ProviderRegistry } from "./discovery/registry";
|
|
23
23
|
export { normalizeScore, mergeCandidates, rankCandidates, } from "./discovery/ranking";
|
|
24
24
|
export type { RankOpts } from "./discovery/ranking";
|
|
25
|
-
export {
|
|
26
|
-
export {
|
|
25
|
+
export { CrossrefProvider } from "./sources/crossref/provider";
|
|
26
|
+
export { crossrefWorkToCandidate } from "./sources/crossref/provider";
|
|
27
|
+
export { searchWorks as crossrefSearchWorks, getWorkByDoi as crossrefGetWorkByDoi, batchHydrateByDois as crossrefBatchHydrate, } from "./sources/crossref/client";
|
|
28
|
+
export type { CrossrefWork, CrossrefAuthor, CrossrefSearchResponse, } from "./sources/crossref/client";
|
|
29
|
+
export { OpenCitationsCitationGraphProvider } from "./sources/opencitations/citation-graph-provider";
|
|
30
|
+
export { getReferences as cociGetReferences, getCitations as cociGetCitations, } from "./sources/opencitations/client";
|
|
27
31
|
export { searchPapers as s2SearchPapers, getPaperDetails as s2GetPaperDetails, getPaperReferences as s2GetPaperReferences, getPaperCitations as s2GetPaperCitations, } from "./sources/semantic-scholar/client";
|
|
28
32
|
export type { S2Paper, S2Author, S2ExternalIds, S2SearchResponse, S2CitationEdge, S2CitationResponse, S2ClientOpts, } from "./sources/semantic-scholar/client";
|
|
29
33
|
export type { PaperDigest, HypothesisSet, Hypothesis } from "./analysis/schemas";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AAGjC,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACrE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGxD,YAAY,EACV,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,WAAW,EACX,YAAY,EACZ,UAAU,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACb,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,IAAI,EACJ,eAAe,EACf,YAAY,EACZ,eAAe,EACf,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,WAAW,GACZ,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EACL,YAAY,EACZ,SAAS,EACT,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,YAAY,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,YAAY,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGpD,OAAO,EACL,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,uCAAuC,CAAC;AAC/C,YAAY,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AAG3E,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,EACb,UAAU,GACX,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EACL,cAAc,EACd,eAAe,EACf,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAGpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -20,9 +20,14 @@ export { createWorkId } from "./works/util/work-id";
|
|
|
20
20
|
export { NormalizedDocSchema, SectionCategoryEnum, } from "./normalization/normalized-doc-schema";
|
|
21
21
|
export { ProviderRegistry } from "./discovery/registry";
|
|
22
22
|
export { normalizeScore, mergeCandidates, rankCandidates, } from "./discovery/ranking";
|
|
23
|
-
// ── Sources:
|
|
24
|
-
export {
|
|
25
|
-
export {
|
|
23
|
+
// ── Sources: Crossref (discovery) ────────────────────────────────
|
|
24
|
+
export { CrossrefProvider } from "./sources/crossref/provider";
|
|
25
|
+
export { crossrefWorkToCandidate } from "./sources/crossref/provider";
|
|
26
|
+
export { searchWorks as crossrefSearchWorks, getWorkByDoi as crossrefGetWorkByDoi, batchHydrateByDois as crossrefBatchHydrate, } from "./sources/crossref/client";
|
|
27
|
+
// ── Sources: OpenCitations (citation graph) ─────────────────────
|
|
28
|
+
export { OpenCitationsCitationGraphProvider } from "./sources/opencitations/citation-graph-provider";
|
|
29
|
+
export { getReferences as cociGetReferences, getCitations as cociGetCitations, } from "./sources/opencitations/client";
|
|
30
|
+
// ── Sources: Semantic Scholar (legacy, retained for ingest paths) ──
|
|
26
31
|
export { searchPapers as s2SearchPapers, getPaperDetails as s2GetPaperDetails, getPaperReferences as s2GetPaperReferences, getPaperCitations as s2GetPaperCitations, } from "./sources/semantic-scholar/client";
|
|
27
32
|
export { PaperDigestSchema, HypothesisSetSchema } from "./analysis/schemas";
|
|
28
33
|
export { extractFindings } from "./analysis/extract-findings";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAEjC,sEAAsE;AACtE,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AAEjC,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAsCrE,sEAAsE;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzC,OAAO,EACL,YAAY,EACZ,SAAS,EACT,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAEvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGhE,sEAAsE;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,sEAAsE;AACtE,OAAO,EACL,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,uCAAuC,CAAC;AAe/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EACL,cAAc,EACd,eAAe,EACf,cAAc,GACf,MAAM,qBAAqB,CAAC;AAG7B,oEAAoE;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAEjC,sEAAsE;AACtE,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AAEjC,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAsCrE,sEAAsE;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzC,OAAO,EACL,YAAY,EACZ,SAAS,EACT,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,6BAA6B,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAEvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGhE,sEAAsE;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,sEAAsE;AACtE,OAAO,EACL,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,uCAAuC,CAAC;AAe/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EACL,cAAc,EACd,eAAe,EACf,cAAc,GACf,MAAM,qBAAqB,CAAC;AAG7B,oEAAoE;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EACL,WAAW,IAAI,mBAAmB,EAClC,YAAY,IAAI,oBAAoB,EACpC,kBAAkB,IAAI,oBAAoB,GAC3C,MAAM,2BAA2B,CAAC;AAOnC,mEAAmE;AACnE,OAAO,EAAE,kCAAkC,EAAE,MAAM,iDAAiD,CAAC;AACrG,OAAO,EACL,aAAa,IAAI,iBAAiB,EAClC,YAAY,IAAI,gBAAgB,GACjC,MAAM,gCAAgC,CAAC;AAExC,sEAAsE;AACtE,OAAO,EACL,YAAY,IAAI,cAAc,EAC9B,eAAe,IAAI,iBAAiB,EACpC,kBAAkB,IAAI,oBAAoB,EAC1C,iBAAiB,IAAI,mBAAmB,GACzC,MAAM,mCAAmC,CAAC;AAa3C,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -1,3 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed client for the Crossref REST API.
|
|
3
|
+
*
|
|
4
|
+
* Free, no API key required. Uses the polite pool via `mailto` parameter
|
|
5
|
+
* for better rate limits.
|
|
6
|
+
*
|
|
7
|
+
* @see https://api.crossref.org/swagger-ui/index.html
|
|
8
|
+
*/
|
|
1
9
|
import type { AdapterResult } from "../../works/types";
|
|
10
|
+
export interface CrossrefWork {
|
|
11
|
+
DOI: string;
|
|
12
|
+
title?: string[];
|
|
13
|
+
author?: CrossrefAuthor[];
|
|
14
|
+
"published-print"?: CrossrefDate;
|
|
15
|
+
"published-online"?: CrossrefDate;
|
|
16
|
+
published?: CrossrefDate;
|
|
17
|
+
"container-title"?: string[];
|
|
18
|
+
score?: number;
|
|
19
|
+
"is-referenced-by-count"?: number;
|
|
20
|
+
"references-count"?: number;
|
|
21
|
+
abstract?: string;
|
|
22
|
+
URL?: string;
|
|
23
|
+
"reference"?: CrossrefReference[];
|
|
24
|
+
}
|
|
25
|
+
export interface CrossrefAuthor {
|
|
26
|
+
given?: string;
|
|
27
|
+
family?: string;
|
|
28
|
+
name?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface CrossrefDate {
|
|
31
|
+
"date-parts"?: number[][];
|
|
32
|
+
}
|
|
33
|
+
export interface CrossrefReference {
|
|
34
|
+
DOI?: string;
|
|
35
|
+
key?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface CrossrefSearchResponse {
|
|
38
|
+
total: number;
|
|
39
|
+
items: CrossrefWork[];
|
|
40
|
+
}
|
|
41
|
+
/** Extract the best publication year from a Crossref work. */
|
|
42
|
+
export declare function extractYear(work: CrossrefWork): number | null;
|
|
43
|
+
/** Format authors into a single string. */
|
|
44
|
+
export declare function formatAuthors(authors?: CrossrefAuthor[]): string | null;
|
|
45
|
+
/** Extract first title from the title array. */
|
|
46
|
+
export declare function extractTitle(work: CrossrefWork): string | null;
|
|
47
|
+
/** Extract venue/journal name. */
|
|
48
|
+
export declare function extractVenue(work: CrossrefWork): string | null;
|
|
49
|
+
/** Truncate abstract to a snippet. */
|
|
50
|
+
export declare function extractAbstract(work: CrossrefWork): string | null;
|
|
51
|
+
/**
|
|
52
|
+
* Fetch a single Crossref work by DOI (existing ingest pathway).
|
|
53
|
+
*/
|
|
2
54
|
export declare function fetchCrossrefWorkByDoi(doi: string): Promise<AdapterResult<any>>;
|
|
55
|
+
/**
|
|
56
|
+
* Search for works by keyword query.
|
|
57
|
+
* Results are sorted by Crossref relevance score (descending).
|
|
58
|
+
*/
|
|
59
|
+
export declare function searchWorks(query: string, opts?: {
|
|
60
|
+
limit?: number;
|
|
61
|
+
offset?: number;
|
|
62
|
+
mailto?: string;
|
|
63
|
+
}): Promise<AdapterResult<CrossrefSearchResponse>>;
|
|
64
|
+
/**
|
|
65
|
+
* Fetch a single Crossref work by DOI as a typed CrossrefWork.
|
|
66
|
+
*/
|
|
67
|
+
export declare function getWorkByDoi(doi: string): Promise<AdapterResult<CrossrefWork>>;
|
|
68
|
+
/**
|
|
69
|
+
* Fetch metadata for multiple DOIs in batches with bounded concurrency.
|
|
70
|
+
* Returns a DOI-keyed map. Missing/failed DOIs are silently omitted.
|
|
71
|
+
*/
|
|
72
|
+
export declare function batchHydrateByDois(dois: string[]): Promise<Map<string, CrossrefWork>>;
|
|
3
73
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/sources/crossref/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/sources/crossref/client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAkBvD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;IAC1B,iBAAiB,CAAC,EAAE,YAAY,CAAC;IACjC,kBAAkB,CAAC,EAAE,YAAY,CAAC;IAClC,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,iBAAiB,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAwCD,8DAA8D;AAC9D,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAO7D;AAED,2CAA2C;AAC3C,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,cAAc,EAAE,GAAG,MAAM,GAAG,IAAI,CAUvE;AAED,gDAAgD;AAChD,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAE9D;AAED,kCAAkC;AAClC,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAE9D;AAED,sCAAsC;AACtC,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAMjE;AAID;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAiB7B;AAID;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1D,OAAO,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC,CAuChD;AAID;;GAEG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAiBtC;AAID;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAyBpC"}
|
|
@@ -1,8 +1,98 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Typed client for the Crossref REST API.
|
|
3
|
+
*
|
|
4
|
+
* Free, no API key required. Uses the polite pool via `mailto` parameter
|
|
5
|
+
* for better rate limits.
|
|
6
|
+
*
|
|
7
|
+
* @see https://api.crossref.org/swagger-ui/index.html
|
|
8
|
+
*/
|
|
9
|
+
const BASE_URL = "https://api.crossref.org";
|
|
10
|
+
/** Max retries on 429 / 5xx before giving up. */
|
|
11
|
+
const MAX_RETRIES = 2;
|
|
12
|
+
/** Request timeout in ms. */
|
|
13
|
+
const REQUEST_TIMEOUT_MS = 15_000;
|
|
14
|
+
/** Max concurrent requests for batch operations. */
|
|
15
|
+
const BATCH_CONCURRENCY = 5;
|
|
16
|
+
/** Chunk size for batch DOI lookups. */
|
|
17
|
+
const BATCH_CHUNK_SIZE = 20;
|
|
18
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
19
|
+
const HEADERS = {
|
|
20
|
+
Accept: "application/json",
|
|
21
|
+
};
|
|
22
|
+
async function fetchWithRetry(url) {
|
|
23
|
+
let lastError;
|
|
24
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
25
|
+
try {
|
|
26
|
+
const res = await fetch(url, {
|
|
27
|
+
headers: HEADERS,
|
|
28
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
29
|
+
});
|
|
30
|
+
if ((res.status === 429 || res.status >= 500) && attempt < MAX_RETRIES) {
|
|
31
|
+
await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
return res;
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
lastError = e;
|
|
38
|
+
if (attempt < MAX_RETRIES) {
|
|
39
|
+
await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
throw lastError ?? new Error("fetchWithRetry exhausted");
|
|
45
|
+
}
|
|
46
|
+
// ── Parsing helpers ──────────────────────────────────────────────────
|
|
47
|
+
/** Extract the best publication year from a Crossref work. */
|
|
48
|
+
export function extractYear(work) {
|
|
49
|
+
const dateParts = work.published?.["date-parts"]?.[0] ??
|
|
50
|
+
work["published-online"]?.["date-parts"]?.[0] ??
|
|
51
|
+
work["published-print"]?.["date-parts"]?.[0];
|
|
52
|
+
const year = dateParts?.[0];
|
|
53
|
+
return typeof year === "number" && year > 0 ? year : null;
|
|
54
|
+
}
|
|
55
|
+
/** Format authors into a single string. */
|
|
56
|
+
export function formatAuthors(authors) {
|
|
57
|
+
if (!authors || authors.length === 0)
|
|
58
|
+
return null;
|
|
59
|
+
return authors
|
|
60
|
+
.map((a) => {
|
|
61
|
+
if (a.name)
|
|
62
|
+
return a.name;
|
|
63
|
+
if (a.family && a.given)
|
|
64
|
+
return `${a.given} ${a.family}`;
|
|
65
|
+
return a.family ?? a.given ?? null;
|
|
66
|
+
})
|
|
67
|
+
.filter(Boolean)
|
|
68
|
+
.join(", ") || null;
|
|
69
|
+
}
|
|
70
|
+
/** Extract first title from the title array. */
|
|
71
|
+
export function extractTitle(work) {
|
|
72
|
+
return work.title?.[0] ?? null;
|
|
73
|
+
}
|
|
74
|
+
/** Extract venue/journal name. */
|
|
75
|
+
export function extractVenue(work) {
|
|
76
|
+
return work["container-title"]?.[0] ?? null;
|
|
77
|
+
}
|
|
78
|
+
/** Truncate abstract to a snippet. */
|
|
79
|
+
export function extractAbstract(work) {
|
|
80
|
+
if (!work.abstract)
|
|
81
|
+
return null;
|
|
82
|
+
// Crossref abstracts may contain JATS XML tags — strip them
|
|
83
|
+
const plain = work.abstract.replace(/<[^>]+>/g, "").trim();
|
|
84
|
+
if (!plain)
|
|
85
|
+
return null;
|
|
86
|
+
return plain.length > 200 ? plain.slice(0, 197) + "..." : plain;
|
|
87
|
+
}
|
|
88
|
+
// ── Public API: existing ingest adapter ──────────────────────────────
|
|
89
|
+
/**
|
|
90
|
+
* Fetch a single Crossref work by DOI (existing ingest pathway).
|
|
91
|
+
*/
|
|
2
92
|
export async function fetchCrossrefWorkByDoi(doi) {
|
|
3
|
-
const url =
|
|
93
|
+
const url = `${BASE_URL}/works/${encodeURIComponent(doi)}`;
|
|
4
94
|
try {
|
|
5
|
-
const res = await
|
|
95
|
+
const res = await fetchWithRetry(url);
|
|
6
96
|
if (!res.ok) {
|
|
7
97
|
const status = res.status;
|
|
8
98
|
if (status === 404)
|
|
@@ -21,4 +111,101 @@ export async function fetchCrossrefWorkByDoi(doi) {
|
|
|
21
111
|
return { ok: false, reason: "network_error" };
|
|
22
112
|
}
|
|
23
113
|
}
|
|
114
|
+
// ── Public API: discovery search ─────────────────────────────────────
|
|
115
|
+
/**
|
|
116
|
+
* Search for works by keyword query.
|
|
117
|
+
* Results are sorted by Crossref relevance score (descending).
|
|
118
|
+
*/
|
|
119
|
+
export async function searchWorks(query, opts) {
|
|
120
|
+
const rows = Math.min(opts?.limit ?? 20, 100);
|
|
121
|
+
const offset = opts?.offset ?? 0;
|
|
122
|
+
const params = new URLSearchParams({
|
|
123
|
+
query,
|
|
124
|
+
rows: String(rows),
|
|
125
|
+
offset: String(offset),
|
|
126
|
+
sort: "relevance",
|
|
127
|
+
order: "desc",
|
|
128
|
+
});
|
|
129
|
+
if (opts?.mailto)
|
|
130
|
+
params.set("mailto", opts.mailto);
|
|
131
|
+
const url = `${BASE_URL}/works?${params}`;
|
|
132
|
+
try {
|
|
133
|
+
const res = await fetchWithRetry(url);
|
|
134
|
+
if (!res.ok) {
|
|
135
|
+
const status = res.status;
|
|
136
|
+
if (status === 400)
|
|
137
|
+
return { ok: false, status, reason: "not_found" };
|
|
138
|
+
if (status === 429)
|
|
139
|
+
return { ok: false, status, reason: "rate_limited" };
|
|
140
|
+
return { ok: false, status, reason: "upstream_error" };
|
|
141
|
+
}
|
|
142
|
+
const data = await res.json();
|
|
143
|
+
const message = data?.message;
|
|
144
|
+
if (!message)
|
|
145
|
+
return { ok: false, reason: "upstream_error" };
|
|
146
|
+
return {
|
|
147
|
+
ok: true,
|
|
148
|
+
data: {
|
|
149
|
+
total: message["total-results"] ?? 0,
|
|
150
|
+
items: (message.items ?? []),
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return { ok: false, reason: "network_error" };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// ── Public API: typed single-work fetch ──────────────────────────────
|
|
159
|
+
/**
|
|
160
|
+
* Fetch a single Crossref work by DOI as a typed CrossrefWork.
|
|
161
|
+
*/
|
|
162
|
+
export async function getWorkByDoi(doi) {
|
|
163
|
+
const url = `${BASE_URL}/works/${encodeURIComponent(doi)}`;
|
|
164
|
+
try {
|
|
165
|
+
const res = await fetchWithRetry(url);
|
|
166
|
+
if (!res.ok) {
|
|
167
|
+
const status = res.status;
|
|
168
|
+
if (status === 404)
|
|
169
|
+
return { ok: false, status, reason: "not_found" };
|
|
170
|
+
if (status === 429)
|
|
171
|
+
return { ok: false, status, reason: "rate_limited" };
|
|
172
|
+
return { ok: false, status, reason: "upstream_error" };
|
|
173
|
+
}
|
|
174
|
+
const data = await res.json();
|
|
175
|
+
const work = data?.message;
|
|
176
|
+
if (!work?.DOI)
|
|
177
|
+
return { ok: false, reason: "not_found" };
|
|
178
|
+
return { ok: true, data: work };
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return { ok: false, reason: "network_error" };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// ── Public API: batch DOI hydration ──────────────────────────────────
|
|
185
|
+
/**
|
|
186
|
+
* Fetch metadata for multiple DOIs in batches with bounded concurrency.
|
|
187
|
+
* Returns a DOI-keyed map. Missing/failed DOIs are silently omitted.
|
|
188
|
+
*/
|
|
189
|
+
export async function batchHydrateByDois(dois) {
|
|
190
|
+
const results = new Map();
|
|
191
|
+
if (dois.length === 0)
|
|
192
|
+
return results;
|
|
193
|
+
// Deduplicate and normalize
|
|
194
|
+
const uniqueDois = [...new Set(dois.map((d) => d.toLowerCase()))];
|
|
195
|
+
// Process in chunks with bounded concurrency
|
|
196
|
+
for (let i = 0; i < uniqueDois.length; i += BATCH_CHUNK_SIZE) {
|
|
197
|
+
const chunk = uniqueDois.slice(i, i + BATCH_CHUNK_SIZE);
|
|
198
|
+
const promises = chunk.map(async (doi) => {
|
|
199
|
+
const result = await getWorkByDoi(doi);
|
|
200
|
+
if (result.ok) {
|
|
201
|
+
results.set(doi.toLowerCase(), result.data);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
// Run chunk with bounded concurrency
|
|
205
|
+
for (let j = 0; j < promises.length; j += BATCH_CONCURRENCY) {
|
|
206
|
+
await Promise.allSettled(promises.slice(j, j + BATCH_CONCURRENCY));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return results;
|
|
210
|
+
}
|
|
24
211
|
//# sourceMappingURL=client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/sources/crossref/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/sources/crossref/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crossref discovery provider.
|
|
3
|
+
*
|
|
4
|
+
* Adapts the Crossref API client to the DiscoverProvider interface.
|
|
5
|
+
*/
|
|
6
|
+
import type { DiscoverCandidateCore, DiscoverProvider, DiscoverSearchOpts } from "../../discovery/types";
|
|
7
|
+
import { type CrossrefWork } from "./client";
|
|
8
|
+
/**
|
|
9
|
+
* Map a CrossrefWork to a DiscoverCandidateCore.
|
|
10
|
+
* Preserves the raw Crossref relevance score for ranking normalization.
|
|
11
|
+
*/
|
|
12
|
+
export declare function crossrefWorkToCandidate(work: CrossrefWork): DiscoverCandidateCore;
|
|
13
|
+
export declare class CrossrefProvider implements DiscoverProvider {
|
|
14
|
+
readonly name = "Crossref";
|
|
15
|
+
readonly sourceKind: "crossref";
|
|
16
|
+
readonly capabilities: readonly ["search"];
|
|
17
|
+
readonly isLocal = false;
|
|
18
|
+
search(query: string, opts?: DiscoverSearchOpts): Promise<DiscoverCandidateCore[]>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/sources/crossref/provider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAOL,KAAK,YAAY,EAClB,MAAM,UAAU,CAAC;AAElB;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,YAAY,GACjB,qBAAqB,CAavB;AAED,qBAAa,gBAAiB,YAAW,gBAAgB;IACvD,QAAQ,CAAC,IAAI,cAAc;IAC3B,QAAQ,CAAC,UAAU,EAAG,UAAU,CAAU;IAC1C,QAAQ,CAAC,YAAY,sBAAuB;IAC5C,QAAQ,CAAC,OAAO,SAAS;IAEnB,MAAM,CACV,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,qBAAqB,EAAE,CAAC;CAapC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crossref discovery provider.
|
|
3
|
+
*
|
|
4
|
+
* Adapts the Crossref API client to the DiscoverProvider interface.
|
|
5
|
+
*/
|
|
6
|
+
import { searchWorks, extractTitle, extractYear, formatAuthors, extractVenue, extractAbstract, } from "./client";
|
|
7
|
+
/**
|
|
8
|
+
* Map a CrossrefWork to a DiscoverCandidateCore.
|
|
9
|
+
* Preserves the raw Crossref relevance score for ranking normalization.
|
|
10
|
+
*/
|
|
11
|
+
export function crossrefWorkToCandidate(work) {
|
|
12
|
+
return {
|
|
13
|
+
doi: work.DOI ?? null,
|
|
14
|
+
title: extractTitle(work),
|
|
15
|
+
authors: formatAuthors(work.author),
|
|
16
|
+
year: extractYear(work),
|
|
17
|
+
sources: ["crossref"],
|
|
18
|
+
score: work.score ?? null,
|
|
19
|
+
citationCount: work["is-referenced-by-count"] ?? null,
|
|
20
|
+
abstractSnippet: extractAbstract(work),
|
|
21
|
+
venue: extractVenue(work),
|
|
22
|
+
url: work.URL ?? null,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export class CrossrefProvider {
|
|
26
|
+
name = "Crossref";
|
|
27
|
+
sourceKind = "crossref";
|
|
28
|
+
capabilities = ["search"];
|
|
29
|
+
isLocal = false;
|
|
30
|
+
async search(query, opts) {
|
|
31
|
+
const limit = opts?.limit ?? 20;
|
|
32
|
+
const result = await searchWorks(query, { limit });
|
|
33
|
+
if (!result.ok) {
|
|
34
|
+
throw new Error(`Crossref search failed: ${result.reason}${result.status ? ` (${result.status})` : ""}`);
|
|
35
|
+
}
|
|
36
|
+
return result.data.items.map((work) => crossrefWorkToCandidate(work));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/sources/crossref/provider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,EACL,WAAW,EACX,YAAY,EACZ,WAAW,EACX,aAAa,EACb,YAAY,EACZ,eAAe,GAEhB,MAAM,UAAU,CAAC;AAElB;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,IAAkB;IAElB,OAAO;QACL,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,IAAI;QACrB,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC;QACzB,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC;QACnC,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC;QACvB,OAAO,EAAE,CAAC,UAAU,CAAC;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI;QACzB,aAAa,EAAE,IAAI,CAAC,wBAAwB,CAAC,IAAI,IAAI;QACrD,eAAe,EAAE,eAAe,CAAC,IAAI,CAAC;QACtC,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC;QACzB,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,IAAI;KACtB,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,gBAAgB;IAClB,IAAI,GAAG,UAAU,CAAC;IAClB,UAAU,GAAG,UAAmB,CAAC;IACjC,YAAY,GAAG,CAAC,QAAQ,CAAU,CAAC;IACnC,OAAO,GAAG,KAAK,CAAC;IAEzB,KAAK,CAAC,MAAM,CACV,KAAa,EACb,IAAyB;QAEzB,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;QAEhC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAEnD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,2BAA2B,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACxF,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC;IACxE,CAAC;CACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCitations COCI citation-graph discovery provider.
|
|
3
|
+
*
|
|
4
|
+
* Expands from a set of seed DOIs by fetching their references (bibliography)
|
|
5
|
+
* and citations (downstream work) via OpenCitations, then hydrates metadata
|
|
6
|
+
* from Crossref before returning deduplicated candidates.
|
|
7
|
+
*
|
|
8
|
+
* Fetch budget:
|
|
9
|
+
* - PER_SEED_LIMIT caps the per-direction result count per seed to guard
|
|
10
|
+
* against prolific seeds flooding the accumulator.
|
|
11
|
+
* - A global `budget = limit × BUDGET_MULTIPLIER` unique non-seed DOIs are
|
|
12
|
+
* accumulated before the outer seed loop is stopped, providing slack for
|
|
13
|
+
* seed exclusions and dedup losses while avoiding fetching far more data
|
|
14
|
+
* than the requested limit.
|
|
15
|
+
* - Only `limit` entries from the accumulated set are sent to Crossref
|
|
16
|
+
* hydration, so hydration cost is always proportional to requested output.
|
|
17
|
+
*/
|
|
18
|
+
import type { DiscoverCandidateCore, DiscoverProvider, DiscoverSearchOpts } from "../../discovery/types";
|
|
19
|
+
export declare class OpenCitationsCitationGraphProvider implements DiscoverProvider {
|
|
20
|
+
readonly name = "OpenCitations Citation Graph";
|
|
21
|
+
readonly sourceKind: "opencitations-citation-graph";
|
|
22
|
+
readonly capabilities: readonly ["search"];
|
|
23
|
+
readonly isLocal = false;
|
|
24
|
+
private seedDois;
|
|
25
|
+
constructor(seedDois: string[]);
|
|
26
|
+
search(_query: string, opts?: DiscoverSearchOpts): Promise<DiscoverCandidateCore[]>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=citation-graph-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"citation-graph-provider.d.ts","sourceRoot":"","sources":["../../../src/sources/opencitations/citation-graph-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EACV,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAEnB,MAAM,uBAAuB,CAAC;AAwB/B,qBAAa,kCAAmC,YAAW,gBAAgB;IACzE,QAAQ,CAAC,IAAI,kCAAkC;IAC/C,QAAQ,CAAC,UAAU,EAAG,8BAA8B,CAAU;IAC9D,QAAQ,CAAC,YAAY,sBAAuB;IAC5C,QAAQ,CAAC,OAAO,SAAS;IAEzB,OAAO,CAAC,QAAQ,CAAW;gBAEf,QAAQ,EAAE,MAAM,EAAE;IAIxB,MAAM,CACV,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,qBAAqB,EAAE,CAAC;CAuFpC"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCitations COCI citation-graph discovery provider.
|
|
3
|
+
*
|
|
4
|
+
* Expands from a set of seed DOIs by fetching their references (bibliography)
|
|
5
|
+
* and citations (downstream work) via OpenCitations, then hydrates metadata
|
|
6
|
+
* from Crossref before returning deduplicated candidates.
|
|
7
|
+
*
|
|
8
|
+
* Fetch budget:
|
|
9
|
+
* - PER_SEED_LIMIT caps the per-direction result count per seed to guard
|
|
10
|
+
* against prolific seeds flooding the accumulator.
|
|
11
|
+
* - A global `budget = limit × BUDGET_MULTIPLIER` unique non-seed DOIs are
|
|
12
|
+
* accumulated before the outer seed loop is stopped, providing slack for
|
|
13
|
+
* seed exclusions and dedup losses while avoiding fetching far more data
|
|
14
|
+
* than the requested limit.
|
|
15
|
+
* - Only `limit` entries from the accumulated set are sent to Crossref
|
|
16
|
+
* hydration, so hydration cost is always proportional to requested output.
|
|
17
|
+
*/
|
|
18
|
+
import { getReferences, getCitations } from "./client";
|
|
19
|
+
import { batchHydrateByDois, extractTitle, extractYear, formatAuthors, extractVenue, extractAbstract, } from "../crossref/client";
|
|
20
|
+
/** Max concurrent seed expansions to avoid burst traffic. */
|
|
21
|
+
const MAX_CONCURRENCY = 3;
|
|
22
|
+
/** Max results per direction per seed (guards against very prolific seeds). */
|
|
23
|
+
const PER_SEED_LIMIT = 50;
|
|
24
|
+
/**
|
|
25
|
+
* Multiplier applied to `opts.limit` to set the global unique-candidate budget.
|
|
26
|
+
* Provides slack for seed exclusions and dedup losses before the final slice.
|
|
27
|
+
*/
|
|
28
|
+
const BUDGET_MULTIPLIER = 3;
|
|
29
|
+
export class OpenCitationsCitationGraphProvider {
|
|
30
|
+
name = "OpenCitations Citation Graph";
|
|
31
|
+
sourceKind = "opencitations-citation-graph";
|
|
32
|
+
capabilities = ["search"];
|
|
33
|
+
isLocal = false;
|
|
34
|
+
seedDois;
|
|
35
|
+
constructor(seedDois) {
|
|
36
|
+
this.seedDois = seedDois;
|
|
37
|
+
}
|
|
38
|
+
async search(_query, opts) {
|
|
39
|
+
if (this.seedDois.length === 0)
|
|
40
|
+
return [];
|
|
41
|
+
const limit = opts?.limit ?? 20;
|
|
42
|
+
// Global budget: stop fanout once this many unique non-seed DOIs are
|
|
43
|
+
// accumulated — avoids fetching far more data than the final limit needs.
|
|
44
|
+
const budget = limit * BUDGET_MULTIPLIER;
|
|
45
|
+
const seedDoiSet = new Set(this.seedDois.map((d) => d.toLowerCase()));
|
|
46
|
+
// Accumulate deduplicated non-seed DOIs and their seed origins inline
|
|
47
|
+
// during fanout — no intermediate collection pass needed.
|
|
48
|
+
const doiMap = new Map();
|
|
49
|
+
for (let i = 0; i < this.seedDois.length; i += MAX_CONCURRENCY) {
|
|
50
|
+
// Early-stop: enough unique candidates collected; skip remaining seeds.
|
|
51
|
+
if (doiMap.size >= budget)
|
|
52
|
+
break;
|
|
53
|
+
const batch = this.seedDois.slice(i, i + MAX_CONCURRENCY);
|
|
54
|
+
const requests = [];
|
|
55
|
+
for (const doi of batch) {
|
|
56
|
+
requests.push({
|
|
57
|
+
doi,
|
|
58
|
+
relation: "references",
|
|
59
|
+
promise: getReferences(doi),
|
|
60
|
+
});
|
|
61
|
+
requests.push({
|
|
62
|
+
doi,
|
|
63
|
+
relation: "cited-by",
|
|
64
|
+
promise: getCitations(doi),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const results = await Promise.allSettled(requests.map((r) => r.promise));
|
|
68
|
+
for (let j = 0; j < results.length; j++) {
|
|
69
|
+
const result = results[j];
|
|
70
|
+
const req = requests[j];
|
|
71
|
+
if (result.status === "fulfilled" && result.value.ok) {
|
|
72
|
+
const dois = result.value.data;
|
|
73
|
+
for (const doi of dois.slice(0, PER_SEED_LIMIT)) {
|
|
74
|
+
const key = doi.toLowerCase();
|
|
75
|
+
// Exclude seed papers from results
|
|
76
|
+
if (seedDoiSet.has(key))
|
|
77
|
+
continue;
|
|
78
|
+
const existing = doiMap.get(key);
|
|
79
|
+
if (existing) {
|
|
80
|
+
const originKey = `${req.doi.toLowerCase()}:${req.relation}`;
|
|
81
|
+
if (!existing.origins.some((o) => `${o.doi.toLowerCase()}:${o.relation}` === originKey)) {
|
|
82
|
+
existing.origins.push({ doi: req.doi, relation: req.relation });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
doiMap.set(key, {
|
|
87
|
+
doi,
|
|
88
|
+
origins: [{ doi: req.doi, relation: req.relation }],
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Slice to limit before hydration so only chosen entries incur Crossref cost.
|
|
96
|
+
const entries = [...doiMap.values()].slice(0, limit);
|
|
97
|
+
if (entries.length === 0)
|
|
98
|
+
return [];
|
|
99
|
+
// Hydrate metadata from Crossref
|
|
100
|
+
const doisToHydrate = entries.map((e) => e.doi);
|
|
101
|
+
const hydrated = await batchHydrateByDois(doisToHydrate);
|
|
102
|
+
// Map to candidates
|
|
103
|
+
return entries.map(({ doi, origins }, i) => {
|
|
104
|
+
const work = hydrated.get(doi.toLowerCase());
|
|
105
|
+
return workToCandidate(doi, work, origins, i);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function workToCandidate(doi, work, origins, position) {
|
|
110
|
+
if (!work) {
|
|
111
|
+
// Minimal candidate when Crossref hydration missed
|
|
112
|
+
return {
|
|
113
|
+
doi,
|
|
114
|
+
title: null,
|
|
115
|
+
authors: null,
|
|
116
|
+
year: null,
|
|
117
|
+
sources: ["opencitations-citation-graph"],
|
|
118
|
+
score: position,
|
|
119
|
+
seedOrigins: origins,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
doi: work.DOI ?? doi,
|
|
124
|
+
title: extractTitle(work),
|
|
125
|
+
authors: formatAuthors(work.author),
|
|
126
|
+
year: extractYear(work),
|
|
127
|
+
sources: ["opencitations-citation-graph"],
|
|
128
|
+
score: position,
|
|
129
|
+
citationCount: work["is-referenced-by-count"] ?? null,
|
|
130
|
+
abstractSnippet: extractAbstract(work),
|
|
131
|
+
venue: extractVenue(work),
|
|
132
|
+
url: work.URL ?? null,
|
|
133
|
+
seedOrigins: origins,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=citation-graph-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"citation-graph-provider.js","sourceRoot":"","sources":["../../../src/sources/opencitations/citation-graph-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed client for the OpenCitations COCI REST API.
|
|
3
|
+
*
|
|
4
|
+
* Free, no API key required, no rate limits documented.
|
|
5
|
+
* Returns citation edges by DOI in both directions.
|
|
6
|
+
*
|
|
7
|
+
* @see https://opencitations.net/index/coci/api/v1
|
|
8
|
+
*/
|
|
9
|
+
import type { AdapterResult } from "../../works/types";
|
|
10
|
+
/** A single citation edge from COCI. */
|
|
11
|
+
export interface CociCitation {
|
|
12
|
+
/** DOI of the citing paper. */
|
|
13
|
+
citing: string;
|
|
14
|
+
/** DOI of the cited paper. */
|
|
15
|
+
cited: string;
|
|
16
|
+
/** Creation date of the citation record (YYYY-MM-DD). */
|
|
17
|
+
creation?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get papers referenced by a given paper (its bibliography).
|
|
21
|
+
* Returns deduplicated, normalized DOIs of cited papers.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getReferences(doi: string): Promise<AdapterResult<string[]>>;
|
|
24
|
+
/**
|
|
25
|
+
* Get papers that cite a given paper.
|
|
26
|
+
* Returns deduplicated, normalized DOIs of citing papers.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getCitations(doi: string): Promise<AdapterResult<string[]>>;
|
|
29
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/sources/opencitations/client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAYvD,wCAAwC;AACxC,MAAM,WAAW,YAAY;IAC3B,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA2DD;;;GAGG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAmBlC;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAmBlC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed client for the OpenCitations COCI REST API.
|
|
3
|
+
*
|
|
4
|
+
* Free, no API key required, no rate limits documented.
|
|
5
|
+
* Returns citation edges by DOI in both directions.
|
|
6
|
+
*
|
|
7
|
+
* @see https://opencitations.net/index/coci/api/v1
|
|
8
|
+
*/
|
|
9
|
+
const BASE_URL = "https://opencitations.net/index/coci/api/v1";
|
|
10
|
+
/** Max retries on 5xx before giving up. */
|
|
11
|
+
const MAX_RETRIES = 2;
|
|
12
|
+
/** Request timeout in ms. */
|
|
13
|
+
const REQUEST_TIMEOUT_MS = 15_000;
|
|
14
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
15
|
+
async function fetchWithRetry(url) {
|
|
16
|
+
let lastError;
|
|
17
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
18
|
+
try {
|
|
19
|
+
const res = await fetch(url, {
|
|
20
|
+
headers: { Accept: "application/json" },
|
|
21
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
22
|
+
});
|
|
23
|
+
if (res.status >= 500 && attempt < MAX_RETRIES) {
|
|
24
|
+
await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
return res;
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
lastError = e;
|
|
31
|
+
if (attempt < MAX_RETRIES) {
|
|
32
|
+
await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
throw lastError ?? new Error("fetchWithRetry exhausted");
|
|
38
|
+
}
|
|
39
|
+
/** Normalize a DOI to lowercase, trimmed. */
|
|
40
|
+
function normalizeDoi(doi) {
|
|
41
|
+
return doi.trim().toLowerCase();
|
|
42
|
+
}
|
|
43
|
+
/** Extract DOIs from COCI edges, filtering out empty/invalid values. */
|
|
44
|
+
function extractDois(edges, field) {
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
const result = [];
|
|
47
|
+
for (const edge of edges) {
|
|
48
|
+
const raw = edge[field];
|
|
49
|
+
if (!raw)
|
|
50
|
+
continue;
|
|
51
|
+
const doi = normalizeDoi(raw);
|
|
52
|
+
if (!doi || seen.has(doi))
|
|
53
|
+
continue;
|
|
54
|
+
seen.add(doi);
|
|
55
|
+
result.push(doi);
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
// ── Public API ───────────────────────────────────────────────────────
|
|
60
|
+
/**
|
|
61
|
+
* Get papers referenced by a given paper (its bibliography).
|
|
62
|
+
* Returns deduplicated, normalized DOIs of cited papers.
|
|
63
|
+
*/
|
|
64
|
+
export async function getReferences(doi) {
|
|
65
|
+
const url = `${BASE_URL}/references/${encodeURIComponent(doi)}`;
|
|
66
|
+
try {
|
|
67
|
+
const res = await fetchWithRetry(url);
|
|
68
|
+
if (!res.ok) {
|
|
69
|
+
const status = res.status;
|
|
70
|
+
if (status === 404)
|
|
71
|
+
return { ok: false, status, reason: "not_found" };
|
|
72
|
+
if (status === 429)
|
|
73
|
+
return { ok: false, status, reason: "rate_limited" };
|
|
74
|
+
return { ok: false, status, reason: "upstream_error" };
|
|
75
|
+
}
|
|
76
|
+
const edges = (await res.json());
|
|
77
|
+
// References: papers cited by the query DOI → extract "cited" field
|
|
78
|
+
return { ok: true, data: extractDois(edges, "cited") };
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return { ok: false, reason: "network_error" };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Get papers that cite a given paper.
|
|
86
|
+
* Returns deduplicated, normalized DOIs of citing papers.
|
|
87
|
+
*/
|
|
88
|
+
export async function getCitations(doi) {
|
|
89
|
+
const url = `${BASE_URL}/citations/${encodeURIComponent(doi)}`;
|
|
90
|
+
try {
|
|
91
|
+
const res = await fetchWithRetry(url);
|
|
92
|
+
if (!res.ok) {
|
|
93
|
+
const status = res.status;
|
|
94
|
+
if (status === 404)
|
|
95
|
+
return { ok: false, status, reason: "not_found" };
|
|
96
|
+
if (status === 429)
|
|
97
|
+
return { ok: false, status, reason: "rate_limited" };
|
|
98
|
+
return { ok: false, status, reason: "upstream_error" };
|
|
99
|
+
}
|
|
100
|
+
const edges = (await res.json());
|
|
101
|
+
// Citations: papers that cite the query DOI → extract "citing" field
|
|
102
|
+
return { ok: true, data: extractDois(edges, "citing") };
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return { ok: false, reason: "network_error" };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/sources/opencitations/client.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Semantic Scholar citation-graph discovery provider.
|
|
3
|
-
*
|
|
4
|
-
* Expands from a set of seed DOIs by fetching their references (bibliography)
|
|
5
|
-
* and citations (downstream work), then returns deduplicated candidates.
|
|
6
|
-
*/
|
|
7
|
-
import type { DiscoverCandidateCore, DiscoverProvider, DiscoverSearchOpts } from "../../discovery/types";
|
|
8
|
-
import { type S2ClientOpts } from "./client";
|
|
9
|
-
export declare class SemanticScholarCitationGraphProvider implements DiscoverProvider {
|
|
10
|
-
readonly name = "Semantic Scholar Citation Graph";
|
|
11
|
-
readonly sourceKind: "semantic-scholar-citation-graph";
|
|
12
|
-
readonly capabilities: readonly ["search"];
|
|
13
|
-
readonly isLocal = false;
|
|
14
|
-
private clientOpts;
|
|
15
|
-
private seedDois;
|
|
16
|
-
constructor(seedDois: string[], clientOpts?: S2ClientOpts);
|
|
17
|
-
search(_query: string, opts?: DiscoverSearchOpts): Promise<DiscoverCandidateCore[]>;
|
|
18
|
-
}
|
|
19
|
-
//# sourceMappingURL=citation-graph-provider.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"citation-graph-provider.d.ts","sourceRoot":"","sources":["../../../src/sources/semantic-scholar/citation-graph-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAEnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,UAAU,CAAC;AAgBlB,qBAAa,oCAAqC,YAAW,gBAAgB;IAC3E,QAAQ,CAAC,IAAI,qCAAqC;IAClD,QAAQ,CAAC,UAAU,EAAG,iCAAiC,CAAU;IACjE,QAAQ,CAAC,YAAY,sBAAuB;IAC5C,QAAQ,CAAC,OAAO,SAAS;IAEzB,OAAO,CAAC,UAAU,CAAe;IACjC,OAAO,CAAC,QAAQ,CAAW;gBAEf,QAAQ,EAAE,MAAM,EAAE,EAAE,UAAU,CAAC,EAAE,YAAY;IAKnD,MAAM,CACV,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,qBAAqB,EAAE,CAAC;CA0EpC"}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Semantic Scholar citation-graph discovery provider.
|
|
3
|
-
*
|
|
4
|
-
* Expands from a set of seed DOIs by fetching their references (bibliography)
|
|
5
|
-
* and citations (downstream work), then returns deduplicated candidates.
|
|
6
|
-
*/
|
|
7
|
-
import { getPaperReferences, getPaperCitations, } from "./client";
|
|
8
|
-
import { s2PaperToCandidate } from "./provider";
|
|
9
|
-
/** Max concurrent seed expansions to avoid rate-limit bursts. */
|
|
10
|
-
const MAX_CONCURRENCY = 3;
|
|
11
|
-
/** Max results per direction per seed. */
|
|
12
|
-
const PER_SEED_LIMIT = 50;
|
|
13
|
-
export class SemanticScholarCitationGraphProvider {
|
|
14
|
-
name = "Semantic Scholar Citation Graph";
|
|
15
|
-
sourceKind = "semantic-scholar-citation-graph";
|
|
16
|
-
capabilities = ["search"];
|
|
17
|
-
isLocal = false;
|
|
18
|
-
clientOpts;
|
|
19
|
-
seedDois;
|
|
20
|
-
constructor(seedDois, clientOpts) {
|
|
21
|
-
this.seedDois = seedDois;
|
|
22
|
-
this.clientOpts = clientOpts ?? {};
|
|
23
|
-
}
|
|
24
|
-
async search(_query, opts) {
|
|
25
|
-
if (this.seedDois.length === 0)
|
|
26
|
-
return [];
|
|
27
|
-
const limit = opts?.limit ?? 20;
|
|
28
|
-
// Fan out across seeds with bounded concurrency, tagging each result
|
|
29
|
-
const allTagged = [];
|
|
30
|
-
const seedDoiSet = new Set(this.seedDois.map((d) => d.toLowerCase()));
|
|
31
|
-
for (let i = 0; i < this.seedDois.length; i += MAX_CONCURRENCY) {
|
|
32
|
-
const batch = this.seedDois.slice(i, i + MAX_CONCURRENCY);
|
|
33
|
-
// Build tagged request pairs: [refs-doi1, cites-doi1, refs-doi2, ...]
|
|
34
|
-
const requests = [];
|
|
35
|
-
for (const doi of batch) {
|
|
36
|
-
requests.push({
|
|
37
|
-
doi,
|
|
38
|
-
relation: "references",
|
|
39
|
-
promise: getPaperReferences(`DOI:${doi}`, { ...this.clientOpts, limit: PER_SEED_LIMIT }),
|
|
40
|
-
});
|
|
41
|
-
requests.push({
|
|
42
|
-
doi,
|
|
43
|
-
relation: "cited-by",
|
|
44
|
-
promise: getPaperCitations(`DOI:${doi}`, { ...this.clientOpts, limit: PER_SEED_LIMIT }),
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
const results = await Promise.allSettled(requests.map((r) => r.promise));
|
|
48
|
-
for (let j = 0; j < results.length; j++) {
|
|
49
|
-
const result = results[j];
|
|
50
|
-
const req = requests[j];
|
|
51
|
-
if (result.status === "fulfilled" && result.value.ok) {
|
|
52
|
-
for (const paper of result.value.data) {
|
|
53
|
-
allTagged.push({ paper, seedDoi: req.doi, relation: req.relation });
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
// Deduplicate by S2 paperId, collecting seed origins per paper
|
|
59
|
-
const paperMap = new Map();
|
|
60
|
-
for (const { paper, seedDoi, relation } of allTagged) {
|
|
61
|
-
// Exclude seed papers from results
|
|
62
|
-
const paperDoi = paper.externalIds?.DOI?.toLowerCase();
|
|
63
|
-
if (paperDoi && seedDoiSet.has(paperDoi))
|
|
64
|
-
continue;
|
|
65
|
-
const existing = paperMap.get(paper.paperId);
|
|
66
|
-
if (existing) {
|
|
67
|
-
// Add this origin if not already tracked
|
|
68
|
-
const originKey = `${seedDoi.toLowerCase()}:${relation}`;
|
|
69
|
-
if (!existing.origins.some((o) => `${o.doi.toLowerCase()}:${o.relation}` === originKey)) {
|
|
70
|
-
existing.origins.push({ doi: seedDoi, relation });
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
paperMap.set(paper.paperId, {
|
|
75
|
-
paper,
|
|
76
|
-
origins: [{ doi: seedDoi, relation }],
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
// Map to candidates, scored by position (will be re-ranked by core)
|
|
81
|
-
const entries = [...paperMap.values()].slice(0, limit);
|
|
82
|
-
const candidates = entries.map(({ paper, origins }, i) => {
|
|
83
|
-
const c = s2PaperToCandidate(paper, i);
|
|
84
|
-
c.sources = ["semantic-scholar-citation-graph"];
|
|
85
|
-
c.seedOrigins = origins;
|
|
86
|
-
return c;
|
|
87
|
-
});
|
|
88
|
-
return candidates;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
//# sourceMappingURL=citation-graph-provider.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"citation-graph-provider.js","sourceRoot":"","sources":["../../../src/sources/semantic-scholar/citation-graph-provider.ts"],"names":[],"mappings":"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"}
|