@ncukondo/search-hub 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +39 -9
  2. package/dist/cli/commands/check.d.ts +34 -0
  3. package/dist/cli/commands/check.d.ts.map +1 -0
  4. package/dist/cli/commands/check.js +126 -0
  5. package/dist/cli/commands/check.js.map +1 -0
  6. package/dist/cli/commands/export.d.ts +5 -3
  7. package/dist/cli/commands/export.d.ts.map +1 -1
  8. package/dist/cli/commands/export.js +0 -4
  9. package/dist/cli/commands/export.js.map +1 -1
  10. package/dist/cli/commands/query/init.d.ts.map +1 -1
  11. package/dist/cli/commands/query/init.js +17 -7
  12. package/dist/cli/commands/query/init.js.map +1 -1
  13. package/dist/cli/commands/query/inspect.d.ts +36 -0
  14. package/dist/cli/commands/query/inspect.d.ts.map +1 -0
  15. package/dist/cli/commands/query/inspect.js +155 -0
  16. package/dist/cli/commands/query/inspect.js.map +1 -0
  17. package/dist/cli/commands/query/translate.d.ts.map +1 -1
  18. package/dist/cli/commands/query/translate.js +3 -1
  19. package/dist/cli/commands/query/translate.js.map +1 -1
  20. package/dist/cli/commands/query-filter.d.ts +13 -0
  21. package/dist/cli/commands/query-filter.d.ts.map +1 -0
  22. package/dist/cli/commands/query-filter.js +149 -0
  23. package/dist/cli/commands/query-filter.js.map +1 -0
  24. package/dist/cli/commands/results.d.ts +3 -3
  25. package/dist/cli/commands/results.d.ts.map +1 -1
  26. package/dist/cli/commands/results.js +12 -3
  27. package/dist/cli/commands/results.js.map +1 -1
  28. package/dist/cli/commands/search-executor.d.ts.map +1 -1
  29. package/dist/cli/commands/search-executor.js +12 -7
  30. package/dist/cli/commands/search-executor.js.map +1 -1
  31. package/dist/cli/e2e-helpers.d.ts +5 -2
  32. package/dist/cli/e2e-helpers.d.ts.map +1 -1
  33. package/dist/cli/index.d.ts.map +1 -1
  34. package/dist/cli/index.js +228 -45
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/index.js +4 -2
  37. package/dist/index.js.map +1 -1
  38. package/dist/providers/arxiv/provider.d.ts +3 -3
  39. package/dist/providers/arxiv/provider.d.ts.map +1 -1
  40. package/dist/providers/arxiv/provider.js +3 -3
  41. package/dist/providers/arxiv/provider.js.map +1 -1
  42. package/dist/providers/arxiv/translator.d.ts +3 -3
  43. package/dist/providers/arxiv/translator.d.ts.map +1 -1
  44. package/dist/providers/arxiv/translator.js +6 -8
  45. package/dist/providers/arxiv/translator.js.map +1 -1
  46. package/dist/providers/base/index.d.ts +1 -1
  47. package/dist/providers/base/index.d.ts.map +1 -1
  48. package/dist/providers/base/mock-provider.d.ts +2 -2
  49. package/dist/providers/base/mock-provider.d.ts.map +1 -1
  50. package/dist/providers/base/mock-provider.js +2 -9
  51. package/dist/providers/base/mock-provider.js.map +1 -1
  52. package/dist/providers/base/provider.d.ts +3 -3
  53. package/dist/providers/base/provider.d.ts.map +1 -1
  54. package/dist/providers/base/provider.js.map +1 -1
  55. package/dist/providers/base/types.d.ts +4 -6
  56. package/dist/providers/base/types.d.ts.map +1 -1
  57. package/dist/providers/base/types.js.map +1 -1
  58. package/dist/providers/eric/provider.d.ts +3 -3
  59. package/dist/providers/eric/provider.d.ts.map +1 -1
  60. package/dist/providers/eric/provider.js +3 -3
  61. package/dist/providers/eric/provider.js.map +1 -1
  62. package/dist/providers/eric/translator.d.ts +5 -5
  63. package/dist/providers/eric/translator.d.ts.map +1 -1
  64. package/dist/providers/eric/translator.js +6 -7
  65. package/dist/providers/eric/translator.js.map +1 -1
  66. package/dist/providers/pubmed/provider.d.ts +3 -3
  67. package/dist/providers/pubmed/provider.d.ts.map +1 -1
  68. package/dist/providers/pubmed/provider.js +3 -3
  69. package/dist/providers/pubmed/provider.js.map +1 -1
  70. package/dist/providers/pubmed/translator.d.ts +3 -3
  71. package/dist/providers/pubmed/translator.d.ts.map +1 -1
  72. package/dist/providers/pubmed/translator.js +4 -23
  73. package/dist/providers/pubmed/translator.js.map +1 -1
  74. package/dist/providers/scopus/provider.d.ts +3 -3
  75. package/dist/providers/scopus/provider.d.ts.map +1 -1
  76. package/dist/providers/scopus/provider.js +3 -3
  77. package/dist/providers/scopus/provider.js.map +1 -1
  78. package/dist/providers/scopus/translator.d.ts +3 -3
  79. package/dist/providers/scopus/translator.d.ts.map +1 -1
  80. package/dist/providers/scopus/translator.js +7 -9
  81. package/dist/providers/scopus/translator.js.map +1 -1
  82. package/dist/query/index.d.ts +3 -2
  83. package/dist/query/index.d.ts.map +1 -1
  84. package/dist/query/json-schema.d.ts.map +1 -1
  85. package/dist/query/json-schema.js +20 -11
  86. package/dist/query/json-schema.js.map +1 -1
  87. package/dist/query/mesh-lookup.d.ts.map +1 -1
  88. package/dist/query/mesh-lookup.js +66 -3
  89. package/dist/query/mesh-lookup.js.map +1 -1
  90. package/dist/query/resolver.d.ts +14 -0
  91. package/dist/query/resolver.d.ts.map +1 -0
  92. package/dist/query/resolver.js +61 -0
  93. package/dist/query/resolver.js.map +1 -0
  94. package/dist/query/types.d.ts +31 -11
  95. package/dist/query/types.d.ts.map +1 -1
  96. package/dist/query/validator.d.ts +659 -348
  97. package/dist/query/validator.d.ts.map +1 -1
  98. package/dist/query/validator.js +70 -30
  99. package/dist/query/validator.js.map +1 -1
  100. package/package.json +1 -1
package/README.md CHANGED
@@ -8,9 +8,14 @@ A CLI tool for systematic literature searching across multiple academic database
8
8
  ## Features
9
9
 
10
10
  - **Multi-database search**: PubMed, ERIC, arXiv, Scopus (Web of Science, Embase planned)
11
- - **Unified query syntax**: YAML-based DSL with automatic translation
11
+ - **Unified query syntax**: YAML-based DSL with automatic translation and JSON Schema support
12
+ - **Controlled vocabulary validation**: Validates MeSH, ERIC descriptors, and Emtree terms with typo suggestions
12
13
  - **Reproducible searches**: Full session logging for PRISMA reporting
14
+ - **Result filtering**: Flexible query expressions (`-q`) to search and filter results by title, abstract, author, year, and more
15
+ - **Coverage verification**: Check whether known articles appear in search results for query quality validation
16
+ - **Session comparison**: Diff results between query iterations to track refinements
13
17
  - **Resume support**: Continue interrupted searches at DB or page level
18
+ - **Review workflow**: Multi-reviewer screening with agreement tracking and finalization
14
19
  - **Fulltext management**: OA discovery, automatic retrieval, PMC XML to Markdown conversion
15
20
  - **Reference manager integration**: Works with [reference-manager](https://github.com/ncukondo/reference-manager)
16
21
 
@@ -37,8 +42,15 @@ This creates config and data directories in platform-specific locations:
37
42
  | macOS | `~/Library/Preferences/search-hub/` | `~/Library/Application Support/search-hub/` |
38
43
  | Windows | `%APPDATA%/search-hub/Config/` | `%LOCALAPPDATA%/search-hub/Data/` |
39
44
 
40
- 2. Create a query file (`query.yaml`):
45
+ 2. Create a query file:
46
+ ```bash
47
+ search-hub query init -o query.yaml
48
+ ```
49
+
50
+ This generates a YAML template with JSON Schema support for editor autocompletion. Edit it to define your search:
51
+
41
52
  ```yaml
53
+ # yaml-language-server: $schema=./query.schema.json
42
54
  name: my_review
43
55
  description: "Literature search for scoping review"
44
56
 
@@ -56,12 +68,19 @@ filters:
56
68
  - en
57
69
  ```
58
70
 
59
- 3. Run search:
71
+ 3. Validate the query:
72
+ ```bash
73
+ search-hub query validate query.yaml
74
+ ```
75
+
76
+ This checks structure, validates controlled vocabulary terms (MeSH, ERIC descriptors, Emtree) against external APIs, and suggests corrections for typos.
77
+
78
+ 4. Run search:
60
79
  ```bash
61
80
  search-hub search query.yaml
62
81
  ```
63
82
 
64
- 4. Export results:
83
+ 5. Export results:
65
84
  ```bash
66
85
  search-hub export <session-id> --format ids
67
86
  ```
@@ -80,20 +99,26 @@ Developing an effective search query is iterative. Start broad, then refine base
80
99
  2. **Review initial results** - Check titles to assess quality:
81
100
  ```bash
82
101
  search-hub results <session-v1> --limit 50
102
+ search-hub results <session-v1> -q "title:diabetes year:2023-2025"
103
+ ```
104
+
105
+ 3. **Check coverage** - Verify known relevant articles are captured:
106
+ ```bash
107
+ search-hub check <session-v1> --file known-articles.txt
83
108
  ```
84
109
 
85
- 3. **Refine the query** - Copy and modify your query file:
110
+ 4. **Refine the query** - Copy and modify your query file:
86
111
  ```bash
87
112
  cp query-v1.yaml query-v2.yaml
88
113
  # Edit query-v2.yaml to add/remove terms, adjust filters
89
114
  ```
90
115
 
91
- 4. **Run the refined search**:
116
+ 5. **Run the refined search**:
92
117
  ```bash
93
118
  search-hub search query-v2.yaml --max-results 100
94
119
  ```
95
120
 
96
- 5. **Compare results with diff** - See what changed:
121
+ 6. **Compare results with diff** - See what changed:
97
122
  ```bash
98
123
  search-hub diff <session-v1> <session-v2> --show removed
99
124
  ```
@@ -106,6 +131,11 @@ Developing an effective search query is iterative. Start broad, then refine base
106
131
  search-hub search query.yaml --count-only
107
132
  ```
108
133
 
134
+ - **Use `--preview`** to see hit counts with sample titles:
135
+ ```bash
136
+ search-hub search query.yaml --preview
137
+ ```
138
+
109
139
  - **Use `--dry-run`** to preview translations: See exactly what query each database will receive.
110
140
  ```bash
111
141
  search-hub search query.yaml --dry-run
@@ -139,10 +169,10 @@ See [Fulltext Management Guide](./docs/fulltext.md) for details.
139
169
 
140
170
  ## Documentation
141
171
 
142
- - [Query Guide](./docs/query-guide.md) - How to write query files
172
+ - [Query Guide](./docs/query-guide.md) - How to write query files (DSL, JSON Schema, vocabulary validation)
143
173
  - [Command Reference](./docs/commands.md) - All CLI commands and options
144
174
  - [Configuration](./docs/configuration.md) - Setup and configuration
145
- - [Databases](./docs/databases.md) - Supported databases and tips
175
+ - [Databases](./docs/databases.md) - Supported databases, controlled vocabularies, and tips
146
176
  - [Fulltext Management](./docs/fulltext.md) - Fulltext retrieval and management
147
177
 
148
178
  ## Development
@@ -0,0 +1,34 @@
1
+ import { Article, ProviderName } from '../../providers/base/types.js';
2
+ export interface ParsedIdentifier {
3
+ type: 'doi' | 'pmid' | 'arxiv';
4
+ value: string;
5
+ raw: string;
6
+ }
7
+ export declare function parseIdentifierFile(content: string): ParsedIdentifier[];
8
+ export interface FoundItem {
9
+ query: string;
10
+ type: ParsedIdentifier['type'];
11
+ sources: ProviderName[];
12
+ title: string;
13
+ }
14
+ export interface MissingItem {
15
+ query: string;
16
+ type: ParsedIdentifier['type'];
17
+ }
18
+ export interface CheckResult {
19
+ total: number;
20
+ foundCount: number;
21
+ missingCount: number;
22
+ coverage: number;
23
+ found: FoundItem[];
24
+ missing: MissingItem[];
25
+ }
26
+ export declare function checkCoverage(articles: Article[], identifiers: ParsedIdentifier[]): CheckResult;
27
+ export interface FormatCheckOptions {
28
+ sessionId: string;
29
+ source: string;
30
+ missingOnly?: boolean | undefined;
31
+ }
32
+ export declare function formatCheckResult(result: CheckResult, options: FormatCheckOptions): string;
33
+ export declare function formatCheckResultJson(result: CheckResult, options: FormatCheckOptions): string;
34
+ //# sourceMappingURL=check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/check.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG3E,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAavE;AAuBD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC/B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,gBAAgB,EAAE,GAAG,WAAW,CA+C/F;AASD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACnC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,kBAAkB,GAAG,MAAM,CA0B1F;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAqB9F"}
@@ -0,0 +1,126 @@
1
+ import { getArticleKeys } from "./session-utils.js";
2
+ function parseIdentifierFile(content) {
3
+ const results = [];
4
+ const lines = content.split("\n");
5
+ for (let i = 0; i < lines.length; i++) {
6
+ const trimmed = lines[i].trim();
7
+ if (trimmed === "" || trimmed.startsWith("#")) continue;
8
+ const parsed = parseLine(trimmed, i + 1);
9
+ results.push(parsed);
10
+ }
11
+ return results;
12
+ }
13
+ function parseLine(line, lineNumber) {
14
+ const prefixMatch = line.match(/^(doi|pmid|arxiv):(.+)$/i);
15
+ if (prefixMatch) {
16
+ const prefix = prefixMatch[1].toLowerCase();
17
+ return { type: prefix, value: prefixMatch[2].trim(), raw: line };
18
+ }
19
+ if (line.startsWith("10.")) {
20
+ return { type: "doi", value: line, raw: line };
21
+ }
22
+ if (/^\d+$/.test(line)) {
23
+ return { type: "pmid", value: line, raw: line };
24
+ }
25
+ throw new Error(`Unrecognizable identifier at line ${lineNumber}: ${line}`);
26
+ }
27
+ function checkCoverage(articles, identifiers) {
28
+ if (identifiers.length === 0) {
29
+ return { total: 0, foundCount: 0, missingCount: 0, coverage: 0, found: [], missing: [] };
30
+ }
31
+ const keyToArticles = /* @__PURE__ */ new Map();
32
+ for (const article of articles) {
33
+ for (const key of getArticleKeys(article)) {
34
+ const existing = keyToArticles.get(key);
35
+ if (existing) {
36
+ existing.push(article);
37
+ } else {
38
+ keyToArticles.set(key, [article]);
39
+ }
40
+ }
41
+ }
42
+ const found = [];
43
+ const missing = [];
44
+ for (const id of identifiers) {
45
+ const key = identifierToKey(id);
46
+ const matched = keyToArticles.get(key);
47
+ if (matched && matched.length > 0) {
48
+ const sources = [...new Set(matched.map((a) => a.source))];
49
+ found.push({
50
+ query: id.raw,
51
+ type: id.type,
52
+ sources,
53
+ title: matched[0].title
54
+ });
55
+ } else {
56
+ missing.push({ query: id.raw, type: id.type });
57
+ }
58
+ }
59
+ const total = identifiers.length;
60
+ return {
61
+ total,
62
+ foundCount: found.length,
63
+ missingCount: missing.length,
64
+ coverage: found.length / total,
65
+ found,
66
+ missing
67
+ };
68
+ }
69
+ function identifierToKey(id) {
70
+ if (id.type === "doi") {
71
+ return `doi:${id.value.toLowerCase()}`;
72
+ }
73
+ return `${id.type}:${id.value}`;
74
+ }
75
+ function formatCheckResult(result, options) {
76
+ const lines = [];
77
+ const pct = result.total > 0 ? (result.coverage * 100).toFixed(1) : "0.0";
78
+ lines.push(`Coverage: ${options.sessionId}`);
79
+ lines.push(`Source: ${options.source} (${result.total} identifiers)`);
80
+ lines.push("");
81
+ lines.push(`Found: ${result.foundCount}/${result.total} (${pct}%)`);
82
+ if (result.missing.length > 0) {
83
+ lines.push("");
84
+ lines.push(`Missing (${result.missingCount}):`);
85
+ for (const m of result.missing) {
86
+ lines.push(` ${m.query}`);
87
+ }
88
+ }
89
+ if (!options.missingOnly && result.found.length > 0) {
90
+ lines.push("");
91
+ lines.push(`Found (${result.foundCount}):`);
92
+ for (const f of result.found) {
93
+ lines.push(` ${f.query} → ${f.title} (${f.sources.join(", ")})`);
94
+ }
95
+ }
96
+ return lines.join("\n");
97
+ }
98
+ function formatCheckResultJson(result, options) {
99
+ return JSON.stringify({
100
+ session: options.sessionId,
101
+ source: options.source,
102
+ total: result.total,
103
+ found: result.foundCount,
104
+ missing: result.missingCount,
105
+ coverage: result.coverage,
106
+ details: {
107
+ found: result.found.map((f) => ({
108
+ query: f.query,
109
+ type: f.type,
110
+ sources: f.sources,
111
+ title: f.title
112
+ })),
113
+ missing: result.missing.map((m) => ({
114
+ query: m.query,
115
+ type: m.type
116
+ }))
117
+ }
118
+ }, null, 2);
119
+ }
120
+ export {
121
+ checkCoverage,
122
+ formatCheckResult,
123
+ formatCheckResultJson,
124
+ parseIdentifierFile
125
+ };
126
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sources":["../../../src/cli/commands/check.ts"],"sourcesContent":["/**\n * Coverage check command - verifies known articles are present in session results.\n */\n\nimport type { Article, ProviderName } from '../../providers/base/types.js';\nimport { getArticleKeys } from './session-utils.js';\n\nexport interface ParsedIdentifier {\n type: 'doi' | 'pmid' | 'arxiv';\n value: string;\n raw: string;\n}\n\nexport function parseIdentifierFile(content: string): ParsedIdentifier[] {\n const results: ParsedIdentifier[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i]!.trim();\n if (trimmed === '' || trimmed.startsWith('#')) continue;\n\n const parsed = parseLine(trimmed, i + 1);\n results.push(parsed);\n }\n\n return results;\n}\n\nfunction parseLine(line: string, lineNumber: number): ParsedIdentifier {\n // Check for explicit prefix (case-insensitive)\n const prefixMatch = line.match(/^(doi|pmid|arxiv):(.+)$/i);\n if (prefixMatch) {\n const prefix = prefixMatch[1]!.toLowerCase() as 'doi' | 'pmid' | 'arxiv';\n return { type: prefix, value: prefixMatch[2]!.trim(), raw: line };\n }\n\n // Auto-detect: starts with \"10.\" → DOI\n if (line.startsWith('10.')) {\n return { type: 'doi', value: line, raw: line };\n }\n\n // Auto-detect: all digits → PMID\n if (/^\\d+$/.test(line)) {\n return { type: 'pmid', value: line, raw: line };\n }\n\n throw new Error(`Unrecognizable identifier at line ${lineNumber}: ${line}`);\n}\n\nexport interface FoundItem {\n query: string;\n type: ParsedIdentifier['type'];\n sources: ProviderName[];\n title: string;\n}\n\nexport interface MissingItem {\n query: string;\n type: ParsedIdentifier['type'];\n}\n\nexport interface CheckResult {\n total: number;\n foundCount: number;\n missingCount: number;\n coverage: number;\n found: FoundItem[];\n missing: MissingItem[];\n}\n\nexport function checkCoverage(articles: Article[], identifiers: ParsedIdentifier[]): CheckResult {\n if (identifiers.length === 0) {\n return { total: 0, foundCount: 0, missingCount: 0, coverage: 0, found: [], missing: [] };\n }\n\n // Build a lookup: key → list of articles with that key\n const keyToArticles = new Map<string, Article[]>();\n for (const article of articles) {\n for (const key of getArticleKeys(article)) {\n const existing = keyToArticles.get(key);\n if (existing) {\n existing.push(article);\n } else {\n keyToArticles.set(key, [article]);\n }\n }\n }\n\n const found: FoundItem[] = [];\n const missing: MissingItem[] = [];\n\n for (const id of identifiers) {\n const key = identifierToKey(id);\n const matched = keyToArticles.get(key);\n\n if (matched && matched.length > 0) {\n const sources = [...new Set(matched.map(a => a.source))];\n found.push({\n query: id.raw,\n type: id.type,\n sources,\n title: matched[0]!.title,\n });\n } else {\n missing.push({ query: id.raw, type: id.type });\n }\n }\n\n const total = identifiers.length;\n return {\n total,\n foundCount: found.length,\n missingCount: missing.length,\n coverage: found.length / total,\n found,\n missing,\n };\n}\n\nfunction identifierToKey(id: ParsedIdentifier): string {\n if (id.type === 'doi') {\n return `doi:${id.value.toLowerCase()}`;\n }\n return `${id.type}:${id.value}`;\n}\n\nexport interface FormatCheckOptions {\n sessionId: string;\n source: string;\n missingOnly?: boolean | undefined;\n}\n\nexport function formatCheckResult(result: CheckResult, options: FormatCheckOptions): string {\n const lines: string[] = [];\n const pct = result.total > 0 ? (result.coverage * 100).toFixed(1) : '0.0';\n\n lines.push(`Coverage: ${options.sessionId}`);\n lines.push(`Source: ${options.source} (${result.total} identifiers)`);\n lines.push('');\n lines.push(`Found: ${result.foundCount}/${result.total} (${pct}%)`);\n\n if (result.missing.length > 0) {\n lines.push('');\n lines.push(`Missing (${result.missingCount}):`);\n for (const m of result.missing) {\n lines.push(` ${m.query}`);\n }\n }\n\n if (!options.missingOnly && result.found.length > 0) {\n lines.push('');\n lines.push(`Found (${result.foundCount}):`);\n for (const f of result.found) {\n lines.push(` ${f.query} → ${f.title} (${f.sources.join(', ')})`);\n }\n }\n\n return lines.join('\\n');\n}\n\nexport function formatCheckResultJson(result: CheckResult, options: FormatCheckOptions): string {\n return JSON.stringify({\n session: options.sessionId,\n source: options.source,\n total: result.total,\n found: result.foundCount,\n missing: result.missingCount,\n coverage: result.coverage,\n details: {\n found: result.found.map(f => ({\n query: f.query,\n type: f.type,\n sources: f.sources,\n title: f.title,\n })),\n missing: result.missing.map(m => ({\n query: m.query,\n type: m.type,\n })),\n },\n }, null, 2);\n}\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { ProviderName, Article } from '../../providers/base/types.js';
1
+ import { Article } from '../../providers/base/types.js';
2
2
  export type ExportFormat = 'ids' | 'json' | 'jsonl' | 'csl-json';
3
3
  export type IdType = 'doi' | 'pmid' | 'all';
4
4
  export interface ExportFilter {
@@ -11,14 +11,16 @@ export interface ExportCommandOptions {
11
11
  sessionId: string;
12
12
  format: ExportFormat;
13
13
  outputPath?: string;
14
- providers?: ProviderName[];
15
14
  idType?: IdType;
16
15
  }
17
16
  export interface CommandLineOptions {
18
17
  format?: string | undefined;
19
18
  output?: string | undefined;
20
- db?: string | undefined;
21
19
  idType?: string | undefined;
20
+ query?: string | undefined;
21
+ filterYear?: string | undefined;
22
+ filterTitle?: string | undefined;
23
+ filterAbstract?: string | undefined;
22
24
  }
23
25
  export interface ValidationResult {
24
26
  valid: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAK7D,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;AACjE,MAAM,MAAM,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;AAE5C,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,kBAAkB,GAC1B,oBAAoB,CAmBtB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,gBAAgB,CAgCnF;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CA+BrE;AAeD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,QAAQ,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAuBrF;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAKvD;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAGzD;AAkBD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,mBAAmB,CAiD5E;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,EAAE,CAmCnF"}
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAI7D,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;AACjE,MAAM,MAAM,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;AAE5C,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,kBAAkB,GAC1B,oBAAoB,CAetB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,gBAAgB,CAgCnF;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CA+BrE;AAeD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,QAAQ,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAuBrF;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAKvD;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAGzD;AAkBD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,mBAAmB,CAiD5E;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,EAAE,CAmCnF"}
@@ -1,4 +1,3 @@
1
- import { parseProviderNames } from "../utils/validation.js";
2
1
  import { articlesToCslJson } from "../../integration/csl-json.js";
3
2
  import { getArticleKeys } from "./session-utils.js";
4
3
  const VALID_FORMATS = ["ids", "json", "jsonl", "csl-json"];
@@ -11,9 +10,6 @@ function parseExportOptions(sessionId, options) {
11
10
  if (options.output) {
12
11
  result.outputPath = options.output;
13
12
  }
14
- if (options.db) {
15
- result.providers = parseProviderNames(options.db);
16
- }
17
13
  if (options.idType) {
18
14
  result.idType = options.idType;
19
15
  }
@@ -1 +1 @@
1
- {"version":3,"file":"export.js","sources":["../../../src/cli/commands/export.ts"],"sourcesContent":["import type { ProviderName } from '../../providers/base/types.js';\nimport type { Article } from '../../providers/base/types.js';\nimport { parseProviderNames } from '../utils/validation.js';\nimport { articlesToCslJson } from '../../integration/csl-json.js';\nimport { getArticleKeys } from './session-utils.js';\n\nexport type ExportFormat = 'ids' | 'json' | 'jsonl' | 'csl-json';\nexport type IdType = 'doi' | 'pmid' | 'all';\n\nexport interface ExportFilter {\n yearFrom?: number;\n yearTo?: number;\n titleKeywords?: string[];\n abstractKeywords?: string[];\n}\n\nexport interface ExportCommandOptions {\n sessionId: string;\n format: ExportFormat;\n outputPath?: string;\n providers?: ProviderName[];\n idType?: IdType;\n}\n\nexport interface CommandLineOptions {\n format?: string | undefined;\n output?: string | undefined;\n db?: string | undefined;\n idType?: string | undefined;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n}\n\nconst VALID_FORMATS: ExportFormat[] = ['ids', 'json', 'jsonl', 'csl-json'];\nconst VALID_ID_TYPES: IdType[] = ['doi', 'pmid', 'all'];\n\nexport function parseExportOptions(\n sessionId: string,\n options: CommandLineOptions\n): ExportCommandOptions {\n const result: ExportCommandOptions = {\n sessionId,\n format: (options.format as ExportFormat) || 'jsonl',\n };\n\n if (options.output) {\n result.outputPath = options.output;\n }\n\n if (options.db) {\n result.providers = parseProviderNames(options.db);\n }\n\n if (options.idType) {\n result.idType = options.idType as IdType;\n }\n\n return result;\n}\n\nexport function validateExportInput(options: ExportCommandOptions): ValidationResult {\n if (!options.sessionId || options.sessionId.trim() === '') {\n return {\n valid: false,\n error: 'A session ID is required',\n };\n }\n\n if (!VALID_FORMATS.includes(options.format)) {\n return {\n valid: false,\n error: `Invalid format: ${options.format}. Valid formats are: ${VALID_FORMATS.join(', ')}`,\n };\n }\n\n if (options.idType) {\n if (!VALID_ID_TYPES.includes(options.idType)) {\n return {\n valid: false,\n error: `Invalid id-type: ${options.idType}. Valid types are: ${VALID_ID_TYPES.join(', ')}`,\n };\n }\n\n if (options.format !== 'ids') {\n return {\n valid: false,\n error: '--id-type can only be used with --format ids',\n };\n }\n }\n\n return { valid: true };\n}\n\nexport function formatIds(articles: Article[], idType: IdType): string {\n if (idType === 'all') {\n const groups: string[] = [];\n for (const article of articles) {\n const ids: string[] = [];\n if (article.pmid) ids.push(`pmid:${article.pmid}`);\n if (article.doi) ids.push(`doi:${article.doi}`);\n if (article.arxivId) ids.push(`arxiv:${article.arxivId}`);\n if (article.scopusId) ids.push(`scopus:${article.scopusId}`);\n if (article.ericId) ids.push(`eric:${article.ericId}`);\n if (ids.length > 0) {\n groups.push(ids.join('\\n'));\n }\n }\n return groups.join('\\n\\n');\n }\n\n const ids: string[] = [];\n for (const article of articles) {\n if (idType === 'doi') {\n if (article.doi) {\n ids.push(article.doi);\n }\n } else if (idType === 'pmid') {\n if (article.pmid) {\n ids.push(article.pmid);\n }\n }\n }\n\n return ids.join('\\n');\n}\n\nfunction extractYear(publicationDate: string | undefined): number | null {\n if (!publicationDate) return null;\n const year = parseInt(publicationDate.substring(0, 4), 10);\n return Number.isNaN(year) ? null : year;\n}\n\nfunction addYearField(articles: Article[]): (Article & { year: number | null })[] {\n return articles.map((article) => ({\n ...article,\n year: extractYear(article.publicationDate),\n }));\n}\n\nexport interface JsonExportMetadata {\n sessionId: string;\n sessionName: string;\n createdAt: string;\n databases: Record<string, number>;\n}\n\nexport function formatJson(articles: Article[], metadata?: JsonExportMetadata): string {\n const articlesWithYear = addYearField(articles);\n\n if (!metadata) {\n return JSON.stringify(articlesWithYear, null, 2);\n }\n\n return JSON.stringify(\n {\n session: {\n id: metadata.sessionId,\n name: metadata.sessionName,\n createdAt: metadata.createdAt,\n },\n summary: {\n totalResults: articles.length,\n databases: metadata.databases,\n },\n results: articlesWithYear,\n },\n null,\n 2\n );\n}\n\nexport function formatJsonl(articles: Article[]): string {\n if (articles.length === 0) {\n return '';\n }\n return addYearField(articles).map((article) => JSON.stringify(article)).join('\\n');\n}\n\nexport function formatCslJson(articles: Article[]): string {\n const cslItems = articlesToCslJson(articles);\n return JSON.stringify(cslItems, null, 2);\n}\n\n\nconst METADATA_FIELDS: (keyof Article)[] = [\n 'doi', 'pmid', 'arxivId', 'scopusId', 'ericId',\n 'abstract', 'publicationDate', 'journal', 'volume', 'issue', 'pages',\n];\n\nfunction countMetadataFields(article: Article): number {\n let count = 0;\n for (const field of METADATA_FIELDS) {\n if (article[field] !== undefined && article[field] !== '') {\n count++;\n }\n }\n return count;\n}\n\nexport interface DeduplicationResult {\n articles: Article[];\n duplicatesRemoved: number;\n}\n\nexport function deduplicateArticles(articles: Article[]): DeduplicationResult {\n // Map from identifier key to index in the unique array\n const keyToIndex = new Map<string, number>();\n const unique: Article[] = [];\n let duplicatesRemoved = 0;\n\n for (const article of articles) {\n const keys = getArticleKeys(article);\n\n if (keys.length === 0) {\n // No identifiers - cannot deduplicate, keep the article\n unique.push(article);\n continue;\n }\n\n // Check if any identifier has been seen before\n let existingIndex: number | undefined;\n for (const key of keys) {\n const idx = keyToIndex.get(key);\n if (idx !== undefined) {\n existingIndex = idx;\n break;\n }\n }\n\n if (existingIndex !== undefined) {\n // Duplicate found - compare metadata richness\n const existing = unique[existingIndex]!;\n if (countMetadataFields(article) > countMetadataFields(existing)) {\n // Replace with the richer record\n unique[existingIndex] = article;\n // Update all keys to point to the same index\n const newKeys = getArticleKeys(article);\n for (const key of newKeys) {\n keyToIndex.set(key, existingIndex);\n }\n }\n duplicatesRemoved++;\n } else {\n const index = unique.length;\n unique.push(article);\n // Map all identifiers to this index\n for (const key of keys) {\n keyToIndex.set(key, index);\n }\n }\n }\n\n return { articles: unique, duplicatesRemoved };\n}\n\nexport function filterArticles(articles: Article[], filter: ExportFilter): Article[] {\n const hasYearFilter = filter.yearFrom !== undefined || filter.yearTo !== undefined;\n const hasTitleFilter = filter.titleKeywords !== undefined && filter.titleKeywords.length > 0;\n const hasAbstractFilter = filter.abstractKeywords !== undefined && filter.abstractKeywords.length > 0;\n\n if (!hasYearFilter && !hasTitleFilter && !hasAbstractFilter) {\n return articles;\n }\n\n return articles.filter((article) => {\n // Year filter (AND with other filters)\n if (hasYearFilter) {\n const year = extractYear(article.publicationDate);\n if (year === null) return false;\n if (filter.yearFrom !== undefined && year < filter.yearFrom) return false;\n if (filter.yearTo !== undefined && year > filter.yearTo) return false;\n }\n\n // Title keyword filter (OR within keywords, AND with other filters)\n if (hasTitleFilter) {\n const titleLower = article.title.toLowerCase();\n const matched = filter.titleKeywords!.some((kw) => titleLower.includes(kw.toLowerCase()));\n if (!matched) return false;\n }\n\n // Abstract keyword filter (OR within keywords, AND with other filters)\n if (hasAbstractFilter) {\n if (!article.abstract) return false;\n const abstractLower = article.abstract.toLowerCase();\n const matched = filter.abstractKeywords!.some((kw) => abstractLower.includes(kw.toLowerCase()));\n if (!matched) return false;\n }\n\n return true;\n });\n}\n"],"names":["ids"],"mappings":"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"}
1
+ {"version":3,"file":"export.js","sources":["../../../src/cli/commands/export.ts"],"sourcesContent":["import type { Article } from '../../providers/base/types.js';\nimport { articlesToCslJson } from '../../integration/csl-json.js';\nimport { getArticleKeys } from './session-utils.js';\n\nexport type ExportFormat = 'ids' | 'json' | 'jsonl' | 'csl-json';\nexport type IdType = 'doi' | 'pmid' | 'all';\n\nexport interface ExportFilter {\n yearFrom?: number;\n yearTo?: number;\n titleKeywords?: string[];\n abstractKeywords?: string[];\n}\n\nexport interface ExportCommandOptions {\n sessionId: string;\n format: ExportFormat;\n outputPath?: string;\n idType?: IdType;\n}\n\nexport interface CommandLineOptions {\n format?: string | undefined;\n output?: string | undefined;\n idType?: string | undefined;\n query?: string | undefined;\n filterYear?: string | undefined;\n filterTitle?: string | undefined;\n filterAbstract?: string | undefined;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n}\n\nconst VALID_FORMATS: ExportFormat[] = ['ids', 'json', 'jsonl', 'csl-json'];\nconst VALID_ID_TYPES: IdType[] = ['doi', 'pmid', 'all'];\n\nexport function parseExportOptions(\n sessionId: string,\n options: CommandLineOptions\n): ExportCommandOptions {\n const result: ExportCommandOptions = {\n sessionId,\n format: (options.format as ExportFormat) || 'jsonl',\n };\n\n if (options.output) {\n result.outputPath = options.output;\n }\n\n if (options.idType) {\n result.idType = options.idType as IdType;\n }\n\n return result;\n}\n\nexport function validateExportInput(options: ExportCommandOptions): ValidationResult {\n if (!options.sessionId || options.sessionId.trim() === '') {\n return {\n valid: false,\n error: 'A session ID is required',\n };\n }\n\n if (!VALID_FORMATS.includes(options.format)) {\n return {\n valid: false,\n error: `Invalid format: ${options.format}. Valid formats are: ${VALID_FORMATS.join(', ')}`,\n };\n }\n\n if (options.idType) {\n if (!VALID_ID_TYPES.includes(options.idType)) {\n return {\n valid: false,\n error: `Invalid id-type: ${options.idType}. Valid types are: ${VALID_ID_TYPES.join(', ')}`,\n };\n }\n\n if (options.format !== 'ids') {\n return {\n valid: false,\n error: '--id-type can only be used with --format ids',\n };\n }\n }\n\n return { valid: true };\n}\n\nexport function formatIds(articles: Article[], idType: IdType): string {\n if (idType === 'all') {\n const groups: string[] = [];\n for (const article of articles) {\n const ids: string[] = [];\n if (article.pmid) ids.push(`pmid:${article.pmid}`);\n if (article.doi) ids.push(`doi:${article.doi}`);\n if (article.arxivId) ids.push(`arxiv:${article.arxivId}`);\n if (article.scopusId) ids.push(`scopus:${article.scopusId}`);\n if (article.ericId) ids.push(`eric:${article.ericId}`);\n if (ids.length > 0) {\n groups.push(ids.join('\\n'));\n }\n }\n return groups.join('\\n\\n');\n }\n\n const ids: string[] = [];\n for (const article of articles) {\n if (idType === 'doi') {\n if (article.doi) {\n ids.push(article.doi);\n }\n } else if (idType === 'pmid') {\n if (article.pmid) {\n ids.push(article.pmid);\n }\n }\n }\n\n return ids.join('\\n');\n}\n\nfunction extractYear(publicationDate: string | undefined): number | null {\n if (!publicationDate) return null;\n const year = parseInt(publicationDate.substring(0, 4), 10);\n return Number.isNaN(year) ? null : year;\n}\n\nfunction addYearField(articles: Article[]): (Article & { year: number | null })[] {\n return articles.map((article) => ({\n ...article,\n year: extractYear(article.publicationDate),\n }));\n}\n\nexport interface JsonExportMetadata {\n sessionId: string;\n sessionName: string;\n createdAt: string;\n databases: Record<string, number>;\n}\n\nexport function formatJson(articles: Article[], metadata?: JsonExportMetadata): string {\n const articlesWithYear = addYearField(articles);\n\n if (!metadata) {\n return JSON.stringify(articlesWithYear, null, 2);\n }\n\n return JSON.stringify(\n {\n session: {\n id: metadata.sessionId,\n name: metadata.sessionName,\n createdAt: metadata.createdAt,\n },\n summary: {\n totalResults: articles.length,\n databases: metadata.databases,\n },\n results: articlesWithYear,\n },\n null,\n 2\n );\n}\n\nexport function formatJsonl(articles: Article[]): string {\n if (articles.length === 0) {\n return '';\n }\n return addYearField(articles).map((article) => JSON.stringify(article)).join('\\n');\n}\n\nexport function formatCslJson(articles: Article[]): string {\n const cslItems = articlesToCslJson(articles);\n return JSON.stringify(cslItems, null, 2);\n}\n\n\nconst METADATA_FIELDS: (keyof Article)[] = [\n 'doi', 'pmid', 'arxivId', 'scopusId', 'ericId',\n 'abstract', 'publicationDate', 'journal', 'volume', 'issue', 'pages',\n];\n\nfunction countMetadataFields(article: Article): number {\n let count = 0;\n for (const field of METADATA_FIELDS) {\n if (article[field] !== undefined && article[field] !== '') {\n count++;\n }\n }\n return count;\n}\n\nexport interface DeduplicationResult {\n articles: Article[];\n duplicatesRemoved: number;\n}\n\nexport function deduplicateArticles(articles: Article[]): DeduplicationResult {\n // Map from identifier key to index in the unique array\n const keyToIndex = new Map<string, number>();\n const unique: Article[] = [];\n let duplicatesRemoved = 0;\n\n for (const article of articles) {\n const keys = getArticleKeys(article);\n\n if (keys.length === 0) {\n // No identifiers - cannot deduplicate, keep the article\n unique.push(article);\n continue;\n }\n\n // Check if any identifier has been seen before\n let existingIndex: number | undefined;\n for (const key of keys) {\n const idx = keyToIndex.get(key);\n if (idx !== undefined) {\n existingIndex = idx;\n break;\n }\n }\n\n if (existingIndex !== undefined) {\n // Duplicate found - compare metadata richness\n const existing = unique[existingIndex]!;\n if (countMetadataFields(article) > countMetadataFields(existing)) {\n // Replace with the richer record\n unique[existingIndex] = article;\n // Update all keys to point to the same index\n const newKeys = getArticleKeys(article);\n for (const key of newKeys) {\n keyToIndex.set(key, existingIndex);\n }\n }\n duplicatesRemoved++;\n } else {\n const index = unique.length;\n unique.push(article);\n // Map all identifiers to this index\n for (const key of keys) {\n keyToIndex.set(key, index);\n }\n }\n }\n\n return { articles: unique, duplicatesRemoved };\n}\n\nexport function filterArticles(articles: Article[], filter: ExportFilter): Article[] {\n const hasYearFilter = filter.yearFrom !== undefined || filter.yearTo !== undefined;\n const hasTitleFilter = filter.titleKeywords !== undefined && filter.titleKeywords.length > 0;\n const hasAbstractFilter = filter.abstractKeywords !== undefined && filter.abstractKeywords.length > 0;\n\n if (!hasYearFilter && !hasTitleFilter && !hasAbstractFilter) {\n return articles;\n }\n\n return articles.filter((article) => {\n // Year filter (AND with other filters)\n if (hasYearFilter) {\n const year = extractYear(article.publicationDate);\n if (year === null) return false;\n if (filter.yearFrom !== undefined && year < filter.yearFrom) return false;\n if (filter.yearTo !== undefined && year > filter.yearTo) return false;\n }\n\n // Title keyword filter (OR within keywords, AND with other filters)\n if (hasTitleFilter) {\n const titleLower = article.title.toLowerCase();\n const matched = filter.titleKeywords!.some((kw) => titleLower.includes(kw.toLowerCase()));\n if (!matched) return false;\n }\n\n // Abstract keyword filter (OR within keywords, AND with other filters)\n if (hasAbstractFilter) {\n if (!article.abstract) return false;\n const abstractLower = article.abstract.toLowerCase();\n const matched = filter.abstractKeywords!.some((kw) => abstractLower.includes(kw.toLowerCase()));\n if (!matched) return false;\n }\n\n return true;\n });\n}\n"],"names":["ids"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/query/init.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAiDzD;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAkCjD"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/query/init.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AA2DzD;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAkCjD"}
@@ -7,7 +7,8 @@ name: my_search
7
7
  description: ""
8
8
 
9
9
  query:
10
- - field: title_abstract # title, abstract, title_abstract, author, keyword, all
10
+ - id: concept-1 # Unique block identifier (for provider replacements)
11
+ field: title_abstract # title, abstract, title_abstract, author, keyword, all
11
12
  terms:
12
13
  keywords:
13
14
  - "search term 1"
@@ -24,7 +25,8 @@ query:
24
25
  operator: OR # How to combine terms within this block
25
26
 
26
27
  # Add more blocks — blocks are AND'd together
27
- # - field: title_abstract
28
+ # - id: concept-2
29
+ # field: title_abstract
28
30
  # terms:
29
31
  # keywords:
30
32
  # - "another term"
@@ -40,12 +42,20 @@ query:
40
42
  # - "Review"
41
43
  # - "Comment"
42
44
 
43
- # overrides: # Optional: database-specific settings
45
+ # providers: # Optional: per-database block replacements & filter additions
44
46
  # pubmed:
45
- # filters:
46
- # publication_types:
47
- # exclude:
48
- # - "Letter"
47
+ # replaces:
48
+ # concept-1: # Replace block by id
49
+ # field: keyword
50
+ # terms:
51
+ # mesh:
52
+ # - "MeSH Heading"
53
+ # operator: OR
54
+ # adds:
55
+ # filters:
56
+ # publication_types:
57
+ # exclude:
58
+ # - "Letter"
49
59
  `;
50
60
  function generateQueryTemplate() {
51
61
  return QUERY_TEMPLATE;
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sources":["../../../../src/cli/commands/query/init.ts"],"sourcesContent":["/**\n * Query init command implementation.\n *\n * Generates a skeleton YAML query file with helpful comments.\n */\nimport { writeFile as fsWriteFile, access } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { generateQueryJSONSchema } from \"../../../query/json-schema.js\";\n\n/**\n * The YAML template string with comments preserved.\n * This is a raw string (not generated by a YAML library) so comments are kept.\n */\nexport const QUERY_SCHEMA_FILENAME = \"query.schema.json\";\n\n// prettier-ignore\nconst QUERY_TEMPLATE =\n `# yaml-language-server: $schema=./${QUERY_SCHEMA_FILENAME}\\n` +\n \"name: my_search\\n\" +\n \"description: \\\"\\\"\\n\" +\n \"\\n\" +\n \"query:\\n\" +\n \" - field: title_abstract # title, abstract, title_abstract, author, keyword, all\\n\" +\n \" terms:\\n\" +\n \" keywords:\\n\" +\n \" - \\\"search term 1\\\"\\n\" +\n \" - \\\"search term 2\\\"\\n\" +\n \" # mesh: # PubMed MeSH terms (optional)\\n\" +\n \" # - \\\"MeSH Heading\\\"\\n\" +\n \" # eric: # ERIC Descriptors (optional, ERIC only)\\n\" +\n \" # - \\\"ERIC Descriptor\\\"\\n\" +\n \" exclude: [] # Terms to exclude (NOT operator)\\n\" +\n \" # Tip: Use exclude to filter out false matches from short keywords/acronyms\\n\" +\n \" # exclude:\\n\" +\n \" # - \\\"unwanted term\\\"\\n\" +\n \" # - \\\"irrelevant topic\\\"\\n\" +\n \" operator: OR # How to combine terms within this block\\n\" +\n \"\\n\" +\n \" # Add more blocks — blocks are AND'd together\\n\" +\n \" # - field: title_abstract\\n\" +\n \" # terms:\\n\" +\n \" # keywords:\\n\" +\n \" # - \\\"another term\\\"\\n\" +\n \" # operator: OR\\n\" +\n \"\\n\" +\n \"# filters: # Optional: apply to all databases\\n\" +\n \"# year_from: 2020\\n\" +\n \"# year_to: 2026\\n\" +\n \"# language:\\n\" +\n \"# - en\\n\" +\n \"# publication_types:\\n\" +\n \"# exclude:\\n\" +\n \"# - \\\"Review\\\"\\n\" +\n \"# - \\\"Comment\\\"\\n\" +\n \"\\n\" +\n \"# overrides: # Optional: database-specific settings\\n\" +\n \"# pubmed:\\n\" +\n \"# filters:\\n\" +\n \"# publication_types:\\n\" +\n \"# exclude:\\n\" +\n \"# - \\\"Letter\\\"\\n\";\n\n/**\n * Generate the query template YAML string.\n *\n * @returns The YAML template string with comments\n */\nexport function generateQueryTemplate(): string {\n return QUERY_TEMPLATE;\n}\n\n/**\n * Write the query template to a file or return it as a message.\n *\n * @param options - Output options\n * @param options.output - File path to write to (if omitted, returns template in message)\n * @param options.force - Whether to overwrite existing files\n * @returns Result with success status and message\n */\nexport async function writeQueryTemplate(options: {\n output?: string;\n force?: boolean;\n}): Promise<{ success: boolean; message: string }> {\n const template = generateQueryTemplate();\n\n if (!options.output) {\n // No output file specified, return template as message\n return { success: true, message: template };\n }\n\n // Check if file exists (unless force is set)\n if (!options.force) {\n try {\n await access(options.output);\n // File exists and force is not set\n return {\n success: false,\n message: `File already exists: ${options.output}. Use --force to overwrite.`,\n };\n } catch {\n // File does not exist, proceed\n }\n }\n\n // Write to file\n await fsWriteFile(options.output, template, \"utf-8\");\n\n // Generate JSON Schema file alongside output\n const schemaPath = join(dirname(options.output), QUERY_SCHEMA_FILENAME);\n const jsonSchema = generateQueryJSONSchema();\n await fsWriteFile(schemaPath, JSON.stringify(jsonSchema, null, 2) + \"\\n\", \"utf-8\");\n\n return {\n success: true,\n message: `Template written to ${options.output}`,\n };\n}\n"],"names":["fsWriteFile"],"mappings":";;;AAaO,MAAM,wBAAwB;AAGrC,MAAM,iBACJ,qCAAqC,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkDrD,SAAS,wBAAgC;AAC9C,SAAO;AACT;AAUA,eAAsB,mBAAmB,SAGU;AACjD,QAAM,WAAW,sBAAA;AAEjB,MAAI,CAAC,QAAQ,QAAQ;AAEnB,WAAO,EAAE,SAAS,MAAM,SAAS,SAAA;AAAA,EACnC;AAGA,MAAI,CAAC,QAAQ,OAAO;AAClB,QAAI;AACF,YAAM,OAAO,QAAQ,MAAM;AAE3B,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,wBAAwB,QAAQ,MAAM;AAAA,MAAA;AAAA,IAEnD,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,QAAMA,UAAY,QAAQ,QAAQ,UAAU,OAAO;AAGnD,QAAM,aAAa,KAAK,QAAQ,QAAQ,MAAM,GAAG,qBAAqB;AACtE,QAAM,aAAa,wBAAA;AACnB,QAAMA,UAAY,YAAY,KAAK,UAAU,YAAY,MAAM,CAAC,IAAI,MAAM,OAAO;AAEjF,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS,uBAAuB,QAAQ,MAAM;AAAA,EAAA;AAElD;"}
1
+ {"version":3,"file":"init.js","sources":["../../../../src/cli/commands/query/init.ts"],"sourcesContent":["/**\n * Query init command implementation.\n *\n * Generates a skeleton YAML query file with helpful comments.\n */\nimport { writeFile as fsWriteFile, access } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { generateQueryJSONSchema } from \"../../../query/json-schema.js\";\n\n/**\n * The YAML template string with comments preserved.\n * This is a raw string (not generated by a YAML library) so comments are kept.\n */\nexport const QUERY_SCHEMA_FILENAME = \"query.schema.json\";\n\n// prettier-ignore\nconst QUERY_TEMPLATE =\n `# yaml-language-server: $schema=./${QUERY_SCHEMA_FILENAME}\\n` +\n \"name: my_search\\n\" +\n \"description: \\\"\\\"\\n\" +\n \"\\n\" +\n \"query:\\n\" +\n \" - id: concept-1 # Unique block identifier (for provider replacements)\\n\" +\n \" field: title_abstract # title, abstract, title_abstract, author, keyword, all\\n\" +\n \" terms:\\n\" +\n \" keywords:\\n\" +\n \" - \\\"search term 1\\\"\\n\" +\n \" - \\\"search term 2\\\"\\n\" +\n \" # mesh: # PubMed MeSH terms (optional)\\n\" +\n \" # - \\\"MeSH Heading\\\"\\n\" +\n \" # eric: # ERIC Descriptors (optional, ERIC only)\\n\" +\n \" # - \\\"ERIC Descriptor\\\"\\n\" +\n \" exclude: [] # Terms to exclude (NOT operator)\\n\" +\n \" # Tip: Use exclude to filter out false matches from short keywords/acronyms\\n\" +\n \" # exclude:\\n\" +\n \" # - \\\"unwanted term\\\"\\n\" +\n \" # - \\\"irrelevant topic\\\"\\n\" +\n \" operator: OR # How to combine terms within this block\\n\" +\n \"\\n\" +\n \" # Add more blocks — blocks are AND'd together\\n\" +\n \" # - id: concept-2\\n\" +\n \" # field: title_abstract\\n\" +\n \" # terms:\\n\" +\n \" # keywords:\\n\" +\n \" # - \\\"another term\\\"\\n\" +\n \" # operator: OR\\n\" +\n \"\\n\" +\n \"# filters: # Optional: apply to all databases\\n\" +\n \"# year_from: 2020\\n\" +\n \"# year_to: 2026\\n\" +\n \"# language:\\n\" +\n \"# - en\\n\" +\n \"# publication_types:\\n\" +\n \"# exclude:\\n\" +\n \"# - \\\"Review\\\"\\n\" +\n \"# - \\\"Comment\\\"\\n\" +\n \"\\n\" +\n \"# providers: # Optional: per-database block replacements & filter additions\\n\" +\n \"# pubmed:\\n\" +\n \"# replaces:\\n\" +\n \"# concept-1: # Replace block by id\\n\" +\n \"# field: keyword\\n\" +\n \"# terms:\\n\" +\n \"# mesh:\\n\" +\n \"# - \\\"MeSH Heading\\\"\\n\" +\n \"# operator: OR\\n\" +\n \"# adds:\\n\" +\n \"# filters:\\n\" +\n \"# publication_types:\\n\" +\n \"# exclude:\\n\" +\n \"# - \\\"Letter\\\"\\n\";\n\n/**\n * Generate the query template YAML string.\n *\n * @returns The YAML template string with comments\n */\nexport function generateQueryTemplate(): string {\n return QUERY_TEMPLATE;\n}\n\n/**\n * Write the query template to a file or return it as a message.\n *\n * @param options - Output options\n * @param options.output - File path to write to (if omitted, returns template in message)\n * @param options.force - Whether to overwrite existing files\n * @returns Result with success status and message\n */\nexport async function writeQueryTemplate(options: {\n output?: string;\n force?: boolean;\n}): Promise<{ success: boolean; message: string }> {\n const template = generateQueryTemplate();\n\n if (!options.output) {\n // No output file specified, return template as message\n return { success: true, message: template };\n }\n\n // Check if file exists (unless force is set)\n if (!options.force) {\n try {\n await access(options.output);\n // File exists and force is not set\n return {\n success: false,\n message: `File already exists: ${options.output}. Use --force to overwrite.`,\n };\n } catch {\n // File does not exist, proceed\n }\n }\n\n // Write to file\n await fsWriteFile(options.output, template, \"utf-8\");\n\n // Generate JSON Schema file alongside output\n const schemaPath = join(dirname(options.output), QUERY_SCHEMA_FILENAME);\n const jsonSchema = generateQueryJSONSchema();\n await fsWriteFile(schemaPath, JSON.stringify(jsonSchema, null, 2) + \"\\n\", \"utf-8\");\n\n return {\n success: true,\n message: `Template written to ${options.output}`,\n };\n}\n"],"names":["fsWriteFile"],"mappings":";;;AAaO,MAAM,wBAAwB;AAGrC,MAAM,iBACJ,qCAAqC,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4DrD,SAAS,wBAAgC;AAC9C,SAAO;AACT;AAUA,eAAsB,mBAAmB,SAGU;AACjD,QAAM,WAAW,sBAAA;AAEjB,MAAI,CAAC,QAAQ,QAAQ;AAEnB,WAAO,EAAE,SAAS,MAAM,SAAS,SAAA;AAAA,EACnC;AAGA,MAAI,CAAC,QAAQ,OAAO;AAClB,QAAI;AACF,YAAM,OAAO,QAAQ,MAAM;AAE3B,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,wBAAwB,QAAQ,MAAM;AAAA,MAAA;AAAA,IAEnD,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,QAAMA,UAAY,QAAQ,QAAQ,UAAU,OAAO;AAGnD,QAAM,aAAa,KAAK,QAAQ,QAAQ,MAAM,GAAG,qBAAqB;AACtE,QAAM,aAAa,wBAAA;AACnB,QAAMA,UAAY,YAAY,KAAK,UAAU,YAAY,MAAM,CAAC,IAAI,MAAM,OAAO;AAEjF,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS,uBAAuB,QAAQ,MAAM;AAAA,EAAA;AAElD;"}
@@ -0,0 +1,36 @@
1
+ import { QueryAST } from '../../../query/types.js';
2
+ import { ProviderName } from '../../../providers/base/types.js';
3
+ export interface BlockInspectRow {
4
+ id: string;
5
+ status: Partial<Record<ProviderName, 'default' | 'replaced'>>;
6
+ }
7
+ export interface FilterInspectRow {
8
+ filterKey: string;
9
+ values: Partial<Record<ProviderName, string>>;
10
+ }
11
+ export interface InspectResult {
12
+ name: string;
13
+ providers: ProviderName[];
14
+ blocks: BlockInspectRow[];
15
+ addedFilters: FilterInspectRow[];
16
+ }
17
+ export interface InspectCommandResult {
18
+ success: boolean;
19
+ error?: string;
20
+ result?: InspectResult;
21
+ }
22
+ /**
23
+ * Inspect a QueryAST to determine block resolution and filter additions per provider.
24
+ */
25
+ export declare function inspectQuery(ast: QueryAST, enabledProviders: ProviderName[]): InspectResult;
26
+ /**
27
+ * Format InspectResult as an aligned table string.
28
+ */
29
+ export declare function formatInspectOutput(result: InspectResult): string;
30
+ /**
31
+ * Run the inspect command on a query file.
32
+ */
33
+ export declare function inspectQueryCommand(filePath: string, options?: {
34
+ providers?: ProviderName[];
35
+ }): Promise<InspectCommandResult>;
36
+ //# sourceMappingURL=inspect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspect.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/query/inspect.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAW,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAYrE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC;CAC/D;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,YAAY,EAAE,gBAAgB,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAYD;;GAEG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,QAAQ,EACb,gBAAgB,EAAE,YAAY,EAAE,GAC/B,aAAa,CA4Cf;AAiCD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CA+BjE;AAgDD;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,YAAY,EAAE,CAAA;CAAO,GAC3C,OAAO,CAAC,oBAAoB,CAAC,CAuB/B"}