@zenalexa/unicli 0.225.2 → 0.225.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.
Files changed (184) hide show
  1. package/AGENTS.md +2 -2
  2. package/README.md +3 -3
  3. package/README.zh-CN.md +3 -3
  4. package/dist/adapters/acl-anthology/papers.d.ts +16 -9
  5. package/dist/adapters/acl-anthology/papers.d.ts.map +1 -1
  6. package/dist/adapters/acl-anthology/papers.js +322 -58
  7. package/dist/adapters/acl-anthology/papers.js.map +1 -1
  8. package/dist/adapters/arxiv/papers.d.ts +22 -4
  9. package/dist/adapters/arxiv/papers.d.ts.map +1 -1
  10. package/dist/adapters/arxiv/papers.js +202 -4
  11. package/dist/adapters/arxiv/papers.js.map +1 -1
  12. package/dist/adapters/baidu-scholar/search.d.ts +15 -1
  13. package/dist/adapters/baidu-scholar/search.d.ts.map +1 -1
  14. package/dist/adapters/baidu-scholar/search.js +72 -8
  15. package/dist/adapters/baidu-scholar/search.js.map +1 -1
  16. package/dist/adapters/biorxiv/preprints.d.ts +9 -0
  17. package/dist/adapters/biorxiv/preprints.d.ts.map +1 -0
  18. package/dist/adapters/biorxiv/preprints.js +78 -0
  19. package/dist/adapters/biorxiv/preprints.js.map +1 -0
  20. package/dist/adapters/cnki/search.d.ts +82 -0
  21. package/dist/adapters/cnki/search.d.ts.map +1 -0
  22. package/dist/adapters/cnki/search.js +236 -0
  23. package/dist/adapters/cnki/search.js.map +1 -0
  24. package/dist/adapters/cvf/papers.d.ts +12 -7
  25. package/dist/adapters/cvf/papers.d.ts.map +1 -1
  26. package/dist/adapters/cvf/papers.js +210 -27
  27. package/dist/adapters/cvf/papers.js.map +1 -1
  28. package/dist/adapters/dblp/publications.d.ts +12 -5
  29. package/dist/adapters/dblp/publications.d.ts.map +1 -1
  30. package/dist/adapters/dblp/publications.js +31 -8
  31. package/dist/adapters/dblp/publications.js.map +1 -1
  32. package/dist/adapters/google-scholar/search.d.ts +22 -1
  33. package/dist/adapters/google-scholar/search.d.ts.map +1 -1
  34. package/dist/adapters/google-scholar/search.js +129 -14
  35. package/dist/adapters/google-scholar/search.js.map +1 -1
  36. package/dist/adapters/hf/paper.d.ts +12 -3
  37. package/dist/adapters/hf/paper.d.ts.map +1 -1
  38. package/dist/adapters/hf/paper.js +65 -5
  39. package/dist/adapters/hf/paper.js.map +1 -1
  40. package/dist/adapters/medrxiv/preprints.d.ts +9 -0
  41. package/dist/adapters/medrxiv/preprints.d.ts.map +1 -0
  42. package/dist/adapters/medrxiv/preprints.js +78 -0
  43. package/dist/adapters/medrxiv/preprints.js.map +1 -0
  44. package/dist/adapters/neurips/proceedings.d.ts +8 -7
  45. package/dist/adapters/neurips/proceedings.d.ts.map +1 -1
  46. package/dist/adapters/neurips/proceedings.js +209 -21
  47. package/dist/adapters/neurips/proceedings.js.map +1 -1
  48. package/dist/adapters/openalex/works.d.ts +21 -5
  49. package/dist/adapters/openalex/works.d.ts.map +1 -1
  50. package/dist/adapters/openalex/works.js +108 -8
  51. package/dist/adapters/openalex/works.js.map +1 -1
  52. package/dist/adapters/openreview/papers.d.ts +10 -4
  53. package/dist/adapters/openreview/papers.d.ts.map +1 -1
  54. package/dist/adapters/openreview/papers.js +351 -24
  55. package/dist/adapters/openreview/papers.js.map +1 -1
  56. package/dist/adapters/pmlr/proceedings.d.ts +6 -6
  57. package/dist/adapters/pmlr/proceedings.d.ts.map +1 -1
  58. package/dist/adapters/pmlr/proceedings.js +92 -12
  59. package/dist/adapters/pmlr/proceedings.js.map +1 -1
  60. package/dist/adapters/pubmed/articles.d.ts +8 -4
  61. package/dist/adapters/pubmed/articles.d.ts.map +1 -1
  62. package/dist/adapters/pubmed/articles.js +272 -39
  63. package/dist/adapters/pubmed/articles.js.map +1 -1
  64. package/dist/adapters/rxiv/preprints.d.ts +75 -0
  65. package/dist/adapters/rxiv/preprints.d.ts.map +1 -0
  66. package/dist/adapters/rxiv/preprints.js +651 -0
  67. package/dist/adapters/rxiv/preprints.js.map +1 -0
  68. package/dist/adapters/scholar-artifacts/pdf-read.d.ts +49 -0
  69. package/dist/adapters/scholar-artifacts/pdf-read.d.ts.map +1 -0
  70. package/dist/adapters/scholar-artifacts/pdf-read.js +204 -0
  71. package/dist/adapters/scholar-artifacts/pdf-read.js.map +1 -0
  72. package/dist/adapters/scholar-artifacts/pdf.d.ts +16 -0
  73. package/dist/adapters/scholar-artifacts/pdf.d.ts.map +1 -0
  74. package/dist/adapters/scholar-artifacts/pdf.js +122 -0
  75. package/dist/adapters/scholar-artifacts/pdf.js.map +1 -0
  76. package/dist/adapters/semantic-scholar/papers.d.ts +6 -6
  77. package/dist/adapters/semantic-scholar/papers.d.ts.map +1 -1
  78. package/dist/adapters/semantic-scholar/papers.js +80 -6
  79. package/dist/adapters/semantic-scholar/papers.js.map +1 -1
  80. package/dist/adapters/unpaywall/works.d.ts +7 -7
  81. package/dist/adapters/unpaywall/works.d.ts.map +1 -1
  82. package/dist/adapters/unpaywall/works.js +104 -12
  83. package/dist/adapters/unpaywall/works.js.map +1 -1
  84. package/dist/adapters/wanfang/search.d.ts +14 -0
  85. package/dist/adapters/wanfang/search.d.ts.map +1 -1
  86. package/dist/adapters/wanfang/search.js +56 -7
  87. package/dist/adapters/wanfang/search.js.map +1 -1
  88. package/dist/browser/page.d.ts +2 -0
  89. package/dist/browser/page.d.ts.map +1 -1
  90. package/dist/browser/page.js +12 -0
  91. package/dist/browser/page.js.map +1 -1
  92. package/dist/commands/browser/actions.d.ts.map +1 -1
  93. package/dist/commands/browser/actions.js +59 -3
  94. package/dist/commands/browser/actions.js.map +1 -1
  95. package/dist/commands/scholar.d.ts +77 -5
  96. package/dist/commands/scholar.d.ts.map +1 -1
  97. package/dist/commands/scholar.js +2945 -83
  98. package/dist/commands/scholar.js.map +1 -1
  99. package/dist/core/command-contract.d.ts.map +1 -1
  100. package/dist/core/command-contract.js +5 -0
  101. package/dist/core/command-contract.js.map +1 -1
  102. package/dist/core/schema-v2.d.ts +1 -0
  103. package/dist/core/schema-v2.d.ts.map +1 -1
  104. package/dist/core/schema-v2.js +1 -0
  105. package/dist/core/schema-v2.js.map +1 -1
  106. package/dist/discovery/aliases.d.ts.map +1 -1
  107. package/dist/discovery/aliases.js +208 -0
  108. package/dist/discovery/aliases.js.map +1 -1
  109. package/dist/discovery/core-catalog.d.ts +2 -0
  110. package/dist/discovery/core-catalog.d.ts.map +1 -1
  111. package/dist/discovery/core-catalog.js +487 -0
  112. package/dist/discovery/core-catalog.js.map +1 -1
  113. package/dist/discovery/intents.d.ts.map +1 -1
  114. package/dist/discovery/intents.js +273 -2
  115. package/dist/discovery/intents.js.map +1 -1
  116. package/dist/discovery/loader.d.ts.map +1 -1
  117. package/dist/discovery/loader.js +3 -0
  118. package/dist/discovery/loader.js.map +1 -1
  119. package/dist/engine/capability-policy.d.ts.map +1 -1
  120. package/dist/engine/capability-policy.js +30 -4
  121. package/dist/engine/capability-policy.js.map +1 -1
  122. package/dist/engine/kernel/stages.d.ts.map +1 -1
  123. package/dist/engine/kernel/stages.js +3 -0
  124. package/dist/engine/kernel/stages.js.map +1 -1
  125. package/dist/engine/operation-policy.d.ts +4 -1
  126. package/dist/engine/operation-policy.d.ts.map +1 -1
  127. package/dist/engine/operation-policy.js +23 -0
  128. package/dist/engine/operation-policy.js.map +1 -1
  129. package/dist/fast-path/manifest.d.ts +3 -0
  130. package/dist/fast-path/manifest.d.ts.map +1 -1
  131. package/dist/fast-path/manifest.js.map +1 -1
  132. package/dist/fast-path/policy.d.ts.map +1 -1
  133. package/dist/fast-path/policy.js +3 -0
  134. package/dist/fast-path/policy.js.map +1 -1
  135. package/dist/manifest-compact.txt +1 -1
  136. package/dist/manifest.json +6804 -1002
  137. package/dist/registry.d.ts +2 -0
  138. package/dist/registry.d.ts.map +1 -1
  139. package/dist/registry.js +1 -0
  140. package/dist/registry.js.map +1 -1
  141. package/dist/types/scholarly.d.ts +19 -4
  142. package/dist/types/scholarly.d.ts.map +1 -1
  143. package/dist/types/scholarly.js +4 -4
  144. package/dist/types.d.ts +8 -0
  145. package/dist/types.d.ts.map +1 -1
  146. package/dist/types.js.map +1 -1
  147. package/package.json +1 -1
  148. package/server.json +2 -2
  149. package/skills/unicli/SKILL.md +1 -1
  150. package/skills/unicli-claude-code/SKILL.md +1 -1
  151. package/skills/unicli-hermes/SKILL.md +1 -1
  152. package/src/adapters/acl-anthology/papers.test.ts +111 -0
  153. package/src/adapters/acl-anthology/papers.ts +379 -71
  154. package/src/adapters/arxiv/papers.test.ts +46 -0
  155. package/src/adapters/arxiv/papers.ts +251 -4
  156. package/src/adapters/baidu-scholar/search.ts +74 -11
  157. package/src/adapters/biorxiv/preprints.ts +112 -0
  158. package/src/adapters/cnki/search.ts +357 -0
  159. package/src/adapters/cvf/papers.ts +260 -27
  160. package/src/adapters/dblp/publications.test.ts +9 -0
  161. package/src/adapters/dblp/publications.ts +31 -8
  162. package/src/adapters/google-scholar/search.ts +165 -17
  163. package/src/adapters/hf/paper.test.ts +23 -0
  164. package/src/adapters/hf/paper.ts +89 -5
  165. package/src/adapters/hf/top.yaml +34 -2
  166. package/src/adapters/huggingface-papers/daily.yaml +37 -3
  167. package/src/adapters/huggingface-papers/search.yaml +43 -9
  168. package/src/adapters/medrxiv/preprints.ts +112 -0
  169. package/src/adapters/neurips/proceedings.ts +266 -22
  170. package/src/adapters/openalex/works.test.ts +15 -4
  171. package/src/adapters/openalex/works.ts +136 -8
  172. package/src/adapters/openreview/papers.test.ts +31 -0
  173. package/src/adapters/openreview/papers.ts +407 -29
  174. package/src/adapters/pmlr/proceedings.ts +102 -12
  175. package/src/adapters/pubmed/articles.test.ts +88 -1
  176. package/src/adapters/pubmed/articles.ts +343 -44
  177. package/src/adapters/rxiv/preprints.test.ts +233 -0
  178. package/src/adapters/rxiv/preprints.ts +849 -0
  179. package/src/adapters/scholar-artifacts/pdf-read.ts +277 -0
  180. package/src/adapters/scholar-artifacts/pdf.ts +133 -0
  181. package/src/adapters/semantic-scholar/papers.ts +98 -6
  182. package/src/adapters/unpaywall/works.ts +141 -12
  183. package/src/adapters/wanfang/search.ts +57 -7
  184. package/src/adapters/cnki/search.yaml +0 -49
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * @owner src::adapters::pmlr::proceedings
3
- * @does Registers Proceedings of Machine Learning Research volume search using official citeproc.yaml metadata.
4
- * @needs proceedings.mlr.press citeproc.yaml files, js-yaml, src/registry.ts
5
- * @feeds src/commands/scholar.ts via scholar.search, scholar.get, scholar.pdf, and scholar.venue
6
- * @breaks Missing volume metadata or citeproc drift surfaces as explicit adapter errors.
3
+ * @does Registers Proceedings of Machine Learning Research volume search, paper lookup, and PDF text reading using official citeproc.yaml metadata.
4
+ * @needs proceedings.mlr.press citeproc.yaml/PDF assets, js-yaml, src/adapters/scholar-artifacts/pdf-read.ts, src/registry.ts
5
+ * @feeds src/commands/scholar.ts via scholar.search, scholar.get, scholar.pdf, scholar.fulltext, and scholar.venue
6
+ * @breaks Missing volume metadata, citeproc/PDF drift, denied downloads, or missing pdftotext surfaces as explicit adapter errors.
7
7
  * @invariants Volume is explicit; rows are filtered locally from official YAML metadata, not scraped from rendered cards.
8
- * @side-effects HTTPS egress to proceedings.mlr.press only
9
- * @perf O(N) over one proceedings volume
8
+ * @side-effects HTTPS egress to proceedings.mlr.press/raw.githubusercontent.com; read writes one PDF artifact and executes pdftotext.
9
+ * @perf O(N) over one proceedings volume; read is O(PDF bytes + selected pages)
10
10
  * @concurrency safe
11
11
  * @test tests/unit/adapters/scholar-sources.test.ts
12
12
  * @stability experimental
@@ -16,6 +16,7 @@
16
16
  import yaml from "js-yaml";
17
17
  import { cli, Strategy } from "../../registry.js";
18
18
  import type { ScholarlyWorkRecord } from "../../types/scholarly.js";
19
+ import { readScholarPdf } from "../scholar-artifacts/pdf-read.js";
19
20
 
20
21
  interface PmlrEntry {
21
22
  title?: unknown;
@@ -102,6 +103,44 @@ async function fetchVolume(volume: string): Promise<PmlrEntry[]> {
102
103
  return parsePmlrCiteproc(await response.text());
103
104
  }
104
105
 
106
+ async function findPmlrPaper(
107
+ volume: string,
108
+ id: string,
109
+ ): Promise<ScholarlyWorkRecord> {
110
+ const row = (await fetchVolume(volume))
111
+ .map((entry) => mapPmlrEntry(entry, "pmlr"))
112
+ .find((entry) => entry.id === id);
113
+ if (!row) throw new Error(`No PMLR v${volume} paper found with id "${id}".`);
114
+ return row;
115
+ }
116
+
117
+ async function readPmlrPaperPdf(
118
+ row: ScholarlyWorkRecord,
119
+ kwargs: Record<string, unknown>,
120
+ ): Promise<Record<string, unknown>> {
121
+ if (!row.pdf_url) throw new Error(`PMLR paper ${row.id} has no PDF URL.`);
122
+ return readScholarPdf(
123
+ {
124
+ id: row.id,
125
+ title: row.title,
126
+ source_adapter: "pmlr",
127
+ source_url: row.source_url,
128
+ pdf_url: row.pdf_url,
129
+ output: kwargs.output,
130
+ filename: kwargs.filename,
131
+ "first-page": kwargs["first-page"] ?? kwargs.firstPage,
132
+ "last-page": kwargs["last-page"] ?? kwargs.lastPage,
133
+ "max-chars": kwargs["max-chars"] ?? kwargs.maxChars,
134
+ },
135
+ {
136
+ site: "pmlr",
137
+ command: "read",
138
+ defaultOutput: "./pmlr-downloads",
139
+ userAgent: "unicli-pmlr/1.0 (https://github.com/olo-dot-io/Uni-CLI)",
140
+ },
141
+ );
142
+ }
143
+
105
144
  cli({
106
145
  site: "pmlr",
107
146
  name: "search",
@@ -157,11 +196,62 @@ cli({
157
196
  const id = String(kwargs.id ?? kwargs.ref ?? "").trim();
158
197
  if (!id) throw new Error("pmlr paper id is required.");
159
198
  const volume = requireVolume(kwargs.volume);
160
- const row = (await fetchVolume(volume))
161
- .map((entry) => mapPmlrEntry(entry, "pmlr"))
162
- .find((entry) => entry.id === id);
163
- if (!row)
164
- throw new Error(`No PMLR v${volume} paper found with id "${id}".`);
165
- return [row];
199
+ return [await findPmlrPaper(volume, id)];
200
+ },
201
+ });
202
+
203
+ cli({
204
+ site: "pmlr",
205
+ name: "read",
206
+ description:
207
+ "Download a PMLR paper PDF by id inside a proceedings volume and extract text",
208
+ domain: "proceedings.mlr.press",
209
+ strategy: Strategy.PUBLIC,
210
+ args: [
211
+ { name: "id", type: "str", required: true, positional: true },
212
+ { name: "volume", type: "str", default: "235" },
213
+ {
214
+ name: "output",
215
+ type: "str",
216
+ default: "./pmlr-downloads",
217
+ description: "Output directory for the downloaded PDF",
218
+ "x-unicli-kind": "path",
219
+ },
220
+ { name: "filename", type: "str", description: "Output PDF filename" },
221
+ { name: "first-page", type: "int", default: 1, description: "First page" },
222
+ { name: "last-page", type: "int", default: 20, description: "Last page" },
223
+ {
224
+ name: "max-chars",
225
+ type: "int",
226
+ default: 40000,
227
+ description: "Maximum extracted text characters",
228
+ },
229
+ ],
230
+ columns: [
231
+ "id",
232
+ "title",
233
+ "source_adapter",
234
+ "source_url",
235
+ "pdf_url",
236
+ "path",
237
+ "text_source",
238
+ "text",
239
+ "text_chars",
240
+ "text_truncated",
241
+ ],
242
+ capabilities: [
243
+ "http.fetch",
244
+ "http.download",
245
+ "subprocess.exec",
246
+ "scholar.fulltext",
247
+ "scholar.pdf",
248
+ ],
249
+ executables: ["pdftotext"],
250
+ minimum_capability: "subprocess.exec",
251
+ func: async (_page, kwargs) => {
252
+ const id = String(kwargs.id ?? kwargs.ref ?? "").trim();
253
+ if (!id) throw new Error("pmlr paper id is required.");
254
+ const volume = requireVolume(kwargs.volume);
255
+ return [await readPmlrPaperPdf(await findPmlrPaper(volume, id), kwargs)];
166
256
  },
167
257
  });
@@ -1,9 +1,13 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import {
3
+ mapPmcFullTextRow,
4
+ mapPubMedArticleRecord,
3
5
  mapPubMedArticleRows,
4
6
  mapPubMedSummaryRows,
7
+ normalizePmcId,
5
8
  requirePmid,
6
9
  requirePubMedLimit,
10
+ requirePubMedMaxChars,
7
11
  requirePubMedText,
8
12
  } from "./articles.js";
9
13
 
@@ -13,9 +17,15 @@ describe("pubmed agent-facing commands", () => {
13
17
  expect(() => requirePubMedText("", "query")).toThrow("cannot be empty");
14
18
  expect(requirePmid("37780221")).toBe("37780221");
15
19
  expect(() => requirePmid("PMID:1")).toThrow("numeric PMID");
20
+ expect(normalizePmcId("8938715")).toBe("PMC8938715");
21
+ expect(normalizePmcId("PMC8938715")).toBe("PMC8938715");
22
+ expect(() => normalizePmcId("pmc:8938715")).toThrow("not valid");
16
23
  expect(requirePubMedLimit(undefined)).toBe(20);
17
24
  expect(requirePubMedLimit("100")).toBe(100);
18
25
  expect(() => requirePubMedLimit("0")).toThrow("pubmed limit must");
26
+ expect(requirePubMedMaxChars(undefined)).toBe(40000);
27
+ expect(requirePubMedMaxChars("1000")).toBe(1000);
28
+ expect(() => requirePubMedMaxChars("999")).toThrow("max-chars");
19
29
  });
20
30
 
21
31
  it("maps PubMed summary rows in PMID order", () => {
@@ -34,7 +44,10 @@ describe("pubmed agent-facing commands", () => {
34
44
  source: "Journal",
35
45
  pubdate: "2026 May",
36
46
  pubtype: ["Journal Article"],
37
- articleids: [{ idtype: "doi", value: "10.1/example" }],
47
+ articleids: [
48
+ { idtype: "doi", value: "10.1/example" },
49
+ { idtype: "pmc", value: "PMC123456" },
50
+ ],
38
51
  },
39
52
  {
40
53
  uid: "1",
@@ -51,6 +64,7 @@ describe("pubmed agent-facing commands", () => {
51
64
  ).toMatchObject([
52
65
  {
53
66
  rank: 1,
67
+ id: "1",
54
68
  pmid: "1",
55
69
  title: "First",
56
70
  authors: "Hopper G",
@@ -59,9 +73,12 @@ describe("pubmed agent-facing commands", () => {
59
73
  },
60
74
  {
61
75
  rank: 2,
76
+ id: "2",
62
77
  pmid: "2",
63
78
  authors: "Ada, Grace, Linus, et al.",
64
79
  doi: "10.1/example",
80
+ pmc_id: "PMC123456",
81
+ source_adapter: "pubmed",
65
82
  },
66
83
  ]);
67
84
  });
@@ -77,12 +94,14 @@ describe("pubmed agent-facing commands", () => {
77
94
  <PubDate><Year>2026</Year></PubDate>
78
95
  <PublicationType>Journal Article</PublicationType>
79
96
  <Language>eng</Language>
97
+ <ArticleId IdType="pmc">PMC123456</ArticleId>
80
98
  <ArticleId IdType="doi">10.1/example</ArticleId>
81
99
  </PubmedArticle>
82
100
  `,
83
101
  "123",
84
102
  );
85
103
  expect(rows).toContainEqual({ field: "PMID", value: "123" });
104
+ expect(rows).toContainEqual({ field: "PMCID", value: "PMC123456" });
86
105
  expect(rows).toContainEqual({ field: "Title", value: "Test & Treat" });
87
106
  expect(rows).toContainEqual({ field: "DOI", value: "10.1/example" });
88
107
  expect(rows).toContainEqual({
@@ -93,4 +112,72 @@ describe("pubmed agent-facing commands", () => {
93
112
  "did not include a title",
94
113
  );
95
114
  });
115
+
116
+ it("maps normalized PubMed article metadata rows", () => {
117
+ const row = mapPubMedArticleRecord(
118
+ `
119
+ <PubmedArticle>
120
+ <ArticleTitle>Normalized Article</ArticleTitle>
121
+ <Abstract><AbstractText>Abstract text</AbstractText></Abstract>
122
+ <Author><LastName>Ada</LastName><Initials>L</Initials></Author>
123
+ <Journal><Title>Journal</Title></Journal>
124
+ <PubDate><Year>2026</Year></PubDate>
125
+ <PublicationType>Journal Article</PublicationType>
126
+ <Language>eng</Language>
127
+ <ArticleId IdType="pmc">PMC123456</ArticleId>
128
+ <ArticleId IdType="doi">10.1/example</ArticleId>
129
+ </PubmedArticle>
130
+ `,
131
+ "123",
132
+ );
133
+
134
+ expect(row).toMatchObject({
135
+ id: "123",
136
+ pmid: "123",
137
+ title: "Normalized Article",
138
+ authors: ["Ada L"],
139
+ doi: "10.1/example",
140
+ pmc_id: "PMC123456",
141
+ source_adapter: "pubmed",
142
+ source_url: "https://pubmed.ncbi.nlm.nih.gov/123/",
143
+ });
144
+ });
145
+
146
+ it("maps PMC JATS XML into source-direct full text", () => {
147
+ const row = mapPmcFullTextRow(
148
+ `
149
+ <article>
150
+ <front>
151
+ <article-meta>
152
+ <article-id pub-id-type="pmcid">PMC123456</article-id>
153
+ <article-id pub-id-type="pmid">123</article-id>
154
+ <article-id pub-id-type="doi">10.1/example</article-id>
155
+ <title-group><article-title>Full Text Article</article-title></title-group>
156
+ </article-meta>
157
+ </front>
158
+ <abstract><p>Abstract paragraph.</p></abstract>
159
+ <body>
160
+ <sec><title>Introduction</title><p>First body paragraph.</p></sec>
161
+ <sec><title>Methods</title><p>Second body paragraph.</p></sec>
162
+ </body>
163
+ </article>
164
+ `,
165
+ "PMC123456",
166
+ 1000,
167
+ );
168
+
169
+ expect(row).toMatchObject({
170
+ id: "123",
171
+ title: "Full Text Article",
172
+ pmid: "123",
173
+ pmc_id: "PMC123456",
174
+ doi: "10.1/example",
175
+ source_adapter: "pubmed",
176
+ source_url: "https://pmc.ncbi.nlm.nih.gov/articles/PMC123456/",
177
+ text_truncated: false,
178
+ });
179
+ expect(row.text).toContain("## Abstract");
180
+ expect(row.text).toContain("## Introduction");
181
+ expect(row.text).toContain("First body paragraph.");
182
+ });
96
183
  });