@ncukondo/search-hub 0.9.2 → 0.9.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fulltext/convert/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC,CAoDxB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fulltext/convert/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC,CAqDxB"}
@@ -13,6 +13,7 @@ async function convertPmcXmlToMarkdown(xmlPath, mdPath, metaPath) {
13
13
  if (backMatter.appendices) doc.appendices = backMatter.appendices;
14
14
  if (backMatter.footnotes) doc.footnotes = backMatter.footnotes;
15
15
  if (backMatter.floats) doc.floats = backMatter.floats;
16
+ if (backMatter.notes) doc.notes = backMatter.notes;
16
17
  const md = writeMarkdown(doc);
17
18
  await writeFile(mdPath, md, "utf-8");
18
19
  if (metaPath) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/fulltext/convert/index.ts"],"sourcesContent":["/**\n * Conversion orchestrator for PMC XML to Markdown.\n *\n * Ties together the JATS parser and Markdown writer with file I/O.\n */\n\nimport { readFile, writeFile, stat } from 'node:fs/promises';\nimport { parseJatsMetadata, parseJatsBody, parseJatsReferences, parseJatsBackMatter } from './jats-parser.js';\nimport { writeMarkdown } from './markdown-writer.js';\nimport type { JatsDocument } from './types.js';\nimport type { FulltextMeta } from '../types.js';\n\nexport interface ConvertResult {\n success: boolean;\n error?: string;\n title?: string;\n sections?: number;\n references?: number;\n}\n\n/**\n * Convert a PMC JATS XML file to Markdown.\n *\n * Reads the XML, parses it into a JatsDocument, writes Markdown,\n * and optionally updates meta.json.\n */\nexport async function convertPmcXmlToMarkdown(\n xmlPath: string,\n mdPath: string,\n metaPath?: string,\n): Promise<ConvertResult> {\n try {\n const xml = await readFile(xmlPath, 'utf-8');\n\n // Parse\n const metadata = parseJatsMetadata(xml);\n const sections = parseJatsBody(xml);\n const references = parseJatsReferences(xml);\n const backMatter = parseJatsBackMatter(xml);\n\n const doc: JatsDocument = { metadata, sections, references };\n if (backMatter.acknowledgments) doc.acknowledgments = backMatter.acknowledgments;\n if (backMatter.appendices) doc.appendices = backMatter.appendices;\n if (backMatter.footnotes) doc.footnotes = backMatter.footnotes;\n if (backMatter.floats) doc.floats = backMatter.floats;\n\n // Write Markdown\n const md = writeMarkdown(doc);\n await writeFile(mdPath, md, 'utf-8');\n\n // Update meta.json if path provided and file exists\n if (metaPath) {\n try {\n await stat(metaPath);\n const metaRaw = await readFile(metaPath, 'utf-8');\n const meta = JSON.parse(metaRaw) as FulltextMeta;\n const mdStat = await stat(mdPath);\n\n meta.files.markdown = {\n filename: 'fulltext.md',\n source: 'conversion',\n retrievedAt: new Date().toISOString(),\n size: mdStat.size,\n convertedFrom: 'fulltext.xml',\n };\n\n await writeFile(metaPath, JSON.stringify(meta, null, 2) + '\\n', 'utf-8');\n } catch {\n // meta.json doesn't exist or can't be read, skip update\n }\n }\n\n const result: ConvertResult = { success: true };\n result.title = metadata.title;\n result.sections = sections.length;\n result.references = references.length;\n return result;\n } catch (err) {\n const result: ConvertResult = { success: false };\n result.error = err instanceof Error ? err.message : String(err);\n return result;\n }\n}\n"],"names":[],"mappings":";;;AA0BA,eAAsB,wBACpB,SACA,QACA,UACwB;AACxB,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,SAAS,OAAO;AAG3C,UAAM,WAAW,kBAAkB,GAAG;AACtC,UAAM,WAAW,cAAc,GAAG;AAClC,UAAM,aAAa,oBAAoB,GAAG;AAC1C,UAAM,aAAa,oBAAoB,GAAG;AAE1C,UAAM,MAAoB,EAAE,UAAU,UAAU,WAAA;AAChD,QAAI,WAAW,gBAAiB,KAAI,kBAAkB,WAAW;AACjE,QAAI,WAAW,WAAY,KAAI,aAAa,WAAW;AACvD,QAAI,WAAW,UAAW,KAAI,YAAY,WAAW;AACrD,QAAI,WAAW,OAAQ,KAAI,SAAS,WAAW;AAG/C,UAAM,KAAK,cAAc,GAAG;AAC5B,UAAM,UAAU,QAAQ,IAAI,OAAO;AAGnC,QAAI,UAAU;AACZ,UAAI;AACF,cAAM,KAAK,QAAQ;AACnB,cAAM,UAAU,MAAM,SAAS,UAAU,OAAO;AAChD,cAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,cAAM,SAAS,MAAM,KAAK,MAAM;AAEhC,aAAK,MAAM,WAAW;AAAA,UACpB,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,cAAa,oBAAI,KAAA,GAAO,YAAA;AAAA,UACxB,MAAM,OAAO;AAAA,UACb,eAAe;AAAA,QAAA;AAGjB,cAAM,UAAU,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,MAAM,OAAO;AAAA,MACzE,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,UAAM,SAAwB,EAAE,SAAS,KAAA;AACzC,WAAO,QAAQ,SAAS;AACxB,WAAO,WAAW,SAAS;AAC3B,WAAO,aAAa,WAAW;AAC/B,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,UAAM,SAAwB,EAAE,SAAS,MAAA;AACzC,WAAO,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,WAAO;AAAA,EACT;AACF;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/fulltext/convert/index.ts"],"sourcesContent":["/**\n * Conversion orchestrator for PMC XML to Markdown.\n *\n * Ties together the JATS parser and Markdown writer with file I/O.\n */\n\nimport { readFile, writeFile, stat } from 'node:fs/promises';\nimport { parseJatsMetadata, parseJatsBody, parseJatsReferences, parseJatsBackMatter } from './jats-parser.js';\nimport { writeMarkdown } from './markdown-writer.js';\nimport type { JatsDocument } from './types.js';\nimport type { FulltextMeta } from '../types.js';\n\nexport interface ConvertResult {\n success: boolean;\n error?: string;\n title?: string;\n sections?: number;\n references?: number;\n}\n\n/**\n * Convert a PMC JATS XML file to Markdown.\n *\n * Reads the XML, parses it into a JatsDocument, writes Markdown,\n * and optionally updates meta.json.\n */\nexport async function convertPmcXmlToMarkdown(\n xmlPath: string,\n mdPath: string,\n metaPath?: string,\n): Promise<ConvertResult> {\n try {\n const xml = await readFile(xmlPath, 'utf-8');\n\n // Parse\n const metadata = parseJatsMetadata(xml);\n const sections = parseJatsBody(xml);\n const references = parseJatsReferences(xml);\n const backMatter = parseJatsBackMatter(xml);\n\n const doc: JatsDocument = { metadata, sections, references };\n if (backMatter.acknowledgments) doc.acknowledgments = backMatter.acknowledgments;\n if (backMatter.appendices) doc.appendices = backMatter.appendices;\n if (backMatter.footnotes) doc.footnotes = backMatter.footnotes;\n if (backMatter.floats) doc.floats = backMatter.floats;\n if (backMatter.notes) doc.notes = backMatter.notes;\n\n // Write Markdown\n const md = writeMarkdown(doc);\n await writeFile(mdPath, md, 'utf-8');\n\n // Update meta.json if path provided and file exists\n if (metaPath) {\n try {\n await stat(metaPath);\n const metaRaw = await readFile(metaPath, 'utf-8');\n const meta = JSON.parse(metaRaw) as FulltextMeta;\n const mdStat = await stat(mdPath);\n\n meta.files.markdown = {\n filename: 'fulltext.md',\n source: 'conversion',\n retrievedAt: new Date().toISOString(),\n size: mdStat.size,\n convertedFrom: 'fulltext.xml',\n };\n\n await writeFile(metaPath, JSON.stringify(meta, null, 2) + '\\n', 'utf-8');\n } catch {\n // meta.json doesn't exist or can't be read, skip update\n }\n }\n\n const result: ConvertResult = { success: true };\n result.title = metadata.title;\n result.sections = sections.length;\n result.references = references.length;\n return result;\n } catch (err) {\n const result: ConvertResult = { success: false };\n result.error = err instanceof Error ? err.message : String(err);\n return result;\n }\n}\n"],"names":[],"mappings":";;;AA0BA,eAAsB,wBACpB,SACA,QACA,UACwB;AACxB,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,SAAS,OAAO;AAG3C,UAAM,WAAW,kBAAkB,GAAG;AACtC,UAAM,WAAW,cAAc,GAAG;AAClC,UAAM,aAAa,oBAAoB,GAAG;AAC1C,UAAM,aAAa,oBAAoB,GAAG;AAE1C,UAAM,MAAoB,EAAE,UAAU,UAAU,WAAA;AAChD,QAAI,WAAW,gBAAiB,KAAI,kBAAkB,WAAW;AACjE,QAAI,WAAW,WAAY,KAAI,aAAa,WAAW;AACvD,QAAI,WAAW,UAAW,KAAI,YAAY,WAAW;AACrD,QAAI,WAAW,OAAQ,KAAI,SAAS,WAAW;AAC/C,QAAI,WAAW,MAAO,KAAI,QAAQ,WAAW;AAG7C,UAAM,KAAK,cAAc,GAAG;AAC5B,UAAM,UAAU,QAAQ,IAAI,OAAO;AAGnC,QAAI,UAAU;AACZ,UAAI;AACF,cAAM,KAAK,QAAQ;AACnB,cAAM,UAAU,MAAM,SAAS,UAAU,OAAO;AAChD,cAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,cAAM,SAAS,MAAM,KAAK,MAAM;AAEhC,aAAK,MAAM,WAAW;AAAA,UACpB,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,cAAa,oBAAI,KAAA,GAAO,YAAA;AAAA,UACxB,MAAM,OAAO;AAAA,UACb,eAAe;AAAA,QAAA;AAGjB,cAAM,UAAU,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,MAAM,OAAO;AAAA,MACzE,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,UAAM,SAAwB,EAAE,SAAS,KAAA;AACzC,WAAO,QAAQ,SAAS;AACxB,WAAO,WAAW,SAAS;AAC3B,WAAO,aAAa,WAAW;AAC/B,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,UAAM,SAAwB,EAAE,SAAS,MAAA;AACzC,WAAO,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,WAAO;AAAA,EACT;AACF;"}
@@ -1,4 +1,4 @@
1
- import { JatsMetadata, JatsSection, JatsReference, JatsFootnote, BlockElement } from './types.js';
1
+ import { JatsMetadata, JatsSection, JatsReference, JatsFootnote, BackMatterNote, BlockElement } from './types.js';
2
2
  /**
3
3
  * Parse JATS XML front matter to extract article metadata.
4
4
  */
@@ -26,6 +26,7 @@ export interface BackMatterResult {
26
26
  appendices?: JatsSection[];
27
27
  footnotes?: JatsFootnote[];
28
28
  floats?: BlockElement[];
29
+ notes?: BackMatterNote[];
29
30
  }
30
31
  /**
31
32
  * Parse JATS XML back matter sections (ack, app-group, fn-group)
@@ -1 +1 @@
1
- {"version":3,"file":"jats-parser.d.ts","sourceRoot":"","sources":["../../../src/fulltext/convert/jats-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAEV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,YAAY,EACZ,YAAY,EAEb,MAAM,YAAY,CAAC;AAuMpB;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CA2M3D;AAuLD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;CAClB,CAQA;AA4PD;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,EAAE,CAwBxD;AAwGD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,EAAE,CA8ChE;AAID,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,WAAW,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CA+DjE"}
1
+ {"version":3,"file":"jats-parser.d.ts","sourceRoot":"","sources":["../../../src/fulltext/convert/jats-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAEV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,YAAY,EACZ,cAAc,EACd,YAAY,EAEb,MAAM,YAAY,CAAC;AAuMpB;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CA2M3D;AAuLD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;CAClB,CAQA;AA4PD;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,EAAE,CAwBxD;AA8JD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,EAAE,CAkDhE;AAID,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,WAAW,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAuIjE"}
@@ -695,6 +695,39 @@ function extractMixedCitationText(children) {
695
695
  }
696
696
  return result;
697
697
  }
698
+ function extractPubIds(children) {
699
+ const pubIds = findChildren(children, "pub-id");
700
+ const result = {};
701
+ for (const p of pubIds) {
702
+ const idType = p.attrs["pub-id-type"];
703
+ const value = extractAllText(p.children).trim();
704
+ if (!value) continue;
705
+ if (idType === "doi") result.doi = value;
706
+ if (idType === "pmid") result.pmid = value;
707
+ if (idType === "pmc" || idType === "pmcid") {
708
+ result.pmcid = value.replace(/^PMC/, "");
709
+ }
710
+ }
711
+ return result;
712
+ }
713
+ function stripPubIdValues(text, pubIds) {
714
+ let result = text;
715
+ const values = [pubIds.doi, pubIds.pmid, pubIds.pmcid].filter(Boolean);
716
+ for (const val of values) {
717
+ const escaped = val.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
718
+ result = result.replace(new RegExp(`(?:doi|PMID|pmid|PMC|pmc)[:\\s]*${escaped}`, "gi"), "");
719
+ result = result.replace(new RegExp(escaped, "g"), "");
720
+ }
721
+ if (pubIds.pmcid) {
722
+ const pmcFull = `PMC${pubIds.pmcid}`;
723
+ const escaped = pmcFull.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
724
+ result = result.replace(new RegExp(`(?:pmc|pmcid)[:\\s]*${escaped}`, "gi"), "");
725
+ result = result.replace(new RegExp(escaped, "g"), "");
726
+ }
727
+ result = result.replace(/\s{2,}/g, " ").trim();
728
+ result = result.replace(/\.\s*\.$/, ".");
729
+ return result;
730
+ }
698
731
  function parseJatsReferences(xml) {
699
732
  const parsed = parser.parse(xml);
700
733
  const article = findArticle(parsed);
@@ -711,14 +744,18 @@ function parseJatsReferences(xml) {
711
744
  const searchChildren = citationAlternatives ? citationAlternatives.children : ref.children;
712
745
  const mixedCitation = findChild(searchChildren, "mixed-citation");
713
746
  if (mixedCitation) {
714
- const text2 = extractMixedCitationText(mixedCitation.children);
715
- if (id && text2) references.push({ id, text: text2 });
747
+ const rawText = extractMixedCitationText(mixedCitation.children);
748
+ const pubIds = extractPubIds(mixedCitation.children);
749
+ const text2 = stripPubIdValues(rawText, pubIds);
750
+ if (id && text2) references.push({ id, text: text2, ...pubIds });
716
751
  continue;
717
752
  }
718
753
  const elementCitation = findChild(searchChildren, "element-citation");
719
754
  if (elementCitation) {
720
- const text2 = formatElementCitation(elementCitation.children);
721
- if (id && text2) references.push({ id, text: text2 });
755
+ const rawText = formatElementCitation(elementCitation.children);
756
+ const pubIds = extractPubIds(elementCitation.children);
757
+ const text2 = stripPubIdValues(rawText, pubIds);
758
+ if (id && text2) references.push({ id, text: text2, ...pubIds });
722
759
  continue;
723
760
  }
724
761
  const childrenWithoutLabel = ref.children.filter((c) => !("label" in c));
@@ -754,12 +791,73 @@ function parseJatsBackMatter(xml) {
754
791
  if (fnGroup) {
755
792
  const fns = findChildren(fnGroup.children, "fn");
756
793
  if (fns.length > 0) {
757
- result.footnotes = fns.map((fn) => ({
758
- id: fn.attrs["id"] ?? "",
759
- text: extractAllText(
760
- findChildren(fn.children, "p").flatMap((p) => p.children)
761
- )
762
- }));
794
+ result.footnotes = fns.map((fn) => {
795
+ const parts = [];
796
+ const titleNode = findChild(fn.children, "title");
797
+ if (titleNode) {
798
+ const titleText = extractAllText(titleNode.children).trim();
799
+ if (titleText) parts.push(titleText);
800
+ }
801
+ const paragraphs = findChildren(fn.children, "p");
802
+ for (const p of paragraphs) {
803
+ const pText = extractAllText(p.children).trim();
804
+ if (pText) parts.push(pText);
805
+ }
806
+ return {
807
+ id: fn.attrs["id"] ?? "",
808
+ text: parts.join(" ")
809
+ };
810
+ });
811
+ }
812
+ }
813
+ const notesElements = findChildren(back.children, "notes");
814
+ if (notesElements.length > 0) {
815
+ const notes = [];
816
+ for (const note of notesElements) {
817
+ const secs = findChildren(note.children, "sec");
818
+ const nestedNotes = findChildren(note.children, "notes");
819
+ const subItems = secs.length > 0 ? secs : nestedNotes;
820
+ if (subItems.length > 0) {
821
+ for (const sub of subItems) {
822
+ const subTitleNode = findChild(sub.children, "title");
823
+ const subTitle = subTitleNode ? extractAllText(subTitleNode.children) : "";
824
+ const subParagraphs = findChildren(sub.children, "p");
825
+ const subText = subParagraphs.map((p) => extractAllText(p.children)).join("\n\n");
826
+ if (subTitle || subText) {
827
+ notes.push({ title: subTitle, text: subText });
828
+ }
829
+ }
830
+ } else {
831
+ const titleNode = findChild(note.children, "title");
832
+ const title = titleNode ? extractAllText(titleNode.children) : "";
833
+ const paragraphs = findChildren(note.children, "p");
834
+ const text = paragraphs.map((p) => extractAllText(p.children)).join("\n\n");
835
+ if (title || text) {
836
+ notes.push({ title, text });
837
+ }
838
+ }
839
+ }
840
+ if (notes.length > 0) {
841
+ result.notes = notes;
842
+ }
843
+ }
844
+ const glossaryElements = findChildren(back.children, "glossary");
845
+ for (const glossary of glossaryElements) {
846
+ const titleNode = findChild(glossary.children, "title");
847
+ const title = titleNode ? extractAllText(titleNode.children) : "Glossary";
848
+ const defList = findChild(glossary.children, "def-list");
849
+ if (defList) {
850
+ const defItems = findChildren(defList.children, "def-item");
851
+ const lines = [];
852
+ for (const item of defItems) {
853
+ const termNode = findChild(item.children, "term");
854
+ const defNode = findChild(item.children, "def");
855
+ const term = termNode ? extractAllText(termNode.children) : "";
856
+ const definition = defNode ? extractAllText(defNode.children) : "";
857
+ lines.push(`${term}: ${definition}`);
858
+ }
859
+ if (!result.notes) result.notes = [];
860
+ result.notes.push({ title, text: lines.join("\n") });
763
861
  }
764
862
  }
765
863
  }
@@ -1 +1 @@
1
- {"version":3,"file":"jats-parser.js","sources":["../../../src/fulltext/convert/jats-parser.ts"],"sourcesContent":["/**\n * JATS XML parser for PMC articles.\n *\n * Parses JATS (Journal Article Tag Suite) XML into an intermediate\n * representation for Markdown conversion.\n *\n * Uses fast-xml-parser with `preserveOrder: true` to maintain document order\n * of interleaved elements (e.g. text, citations, formatting).\n */\n\nimport { XMLParser } from 'fast-xml-parser';\nimport type {\n JatsAuthor,\n JatsMetadata,\n JatsSection,\n JatsReference,\n JatsFootnote,\n BlockElement,\n InlineContent,\n} from './types.js';\n\n/**\n * A node in the preserveOrder output.\n * Either a text node `{ \"#text\": string | number }` or an element node\n * `{ tagName: OrderedNode[], \":@\"?: { \"@_attr\": value } }`.\n */\ntype OrderedNode = Record<string, unknown>;\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n textNodeName: '#text',\n trimValues: false,\n preserveOrder: true,\n processEntities: true,\n htmlEntities: true,\n});\n\n// ─── Navigation Helpers ──────────────────────────────────────────────\n\n/** Get the tag name of an ordered node (the first key that isn't \":@\" or \"#text\"). */\nfunction getTagName(node: OrderedNode): string | undefined {\n for (const key of Object.keys(node)) {\n if (key !== ':@' && key !== '#text') return key;\n }\n return undefined;\n}\n\n/** Get the children array of an element node. */\nfunction getChildren(node: OrderedNode): OrderedNode[] {\n const tag = getTagName(node);\n if (!tag) return [];\n const children = node[tag];\n return Array.isArray(children) ? (children as OrderedNode[]) : [];\n}\n\n/** Get attributes of an element node. */\nfunction getAttr(node: OrderedNode, attrName: string): string | undefined {\n const attrs = node[':@'] as Record<string, unknown> | undefined;\n if (!attrs) return undefined;\n const val = attrs[`@_${attrName}`];\n return val != null ? String(val) : undefined;\n}\n\n/** Get all attributes of an element node (strips @_ prefix for consistency with getAttr). */\nfunction getAttrs(node: OrderedNode): Record<string, string> {\n const attrs = node[':@'] as Record<string, unknown> | undefined;\n if (!attrs) return {};\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(attrs)) {\n if (key.startsWith('@_')) {\n result[key.slice(2)] = String(value);\n }\n }\n return result;\n}\n\n/** Find the first child element with the given tag name. */\nfunction findChild(\n children: OrderedNode[],\n tagName: string,\n): { node: OrderedNode; children: OrderedNode[]; attrs: Record<string, string> } | undefined {\n for (const child of children) {\n if (tagName in child) {\n const childArr = child[tagName];\n return {\n node: child,\n children: Array.isArray(childArr) ? (childArr as OrderedNode[]) : [],\n attrs: getAttrs(child),\n };\n }\n }\n return undefined;\n}\n\n/** Find all child elements with the given tag name. */\nfunction findChildren(\n children: OrderedNode[],\n tagName: string,\n): Array<{ node: OrderedNode; children: OrderedNode[]; attrs: Record<string, string> }> {\n const results: Array<{\n node: OrderedNode;\n children: OrderedNode[];\n attrs: Record<string, string>;\n }> = [];\n for (const child of children) {\n if (tagName in child) {\n const childArr = child[tagName];\n results.push({\n node: child,\n children: Array.isArray(childArr) ? (childArr as OrderedNode[]) : [],\n attrs: getAttrs(child),\n });\n }\n }\n return results;\n}\n\n/** Get text content from a #text node. */\nfunction getTextContent(child: OrderedNode): string | undefined {\n if ('#text' in child) {\n const val = child['#text'];\n return val != null ? String(val) : undefined;\n }\n return undefined;\n}\n\n/**\n * Find the <article> element, handling optional <pmc-articleset> wrapper\n * that appears in efetch responses.\n */\nfunction findArticle(\n parsed: OrderedNode[],\n): { node: OrderedNode; children: OrderedNode[]; attrs: Record<string, string> } | undefined {\n const direct = findChild(parsed, 'article');\n if (direct) return direct;\n const wrapper = findChild(parsed, 'pmc-articleset');\n if (wrapper) return findChild(wrapper.children, 'article');\n return undefined;\n}\n\n// ─── Text Extraction ─────────────────────────────────────────────────\n\n/** Tags whose text content should be followed by a space when adjacent to other content. */\nconst SPACE_AFTER_TAGS = new Set([\n 'surname',\n 'given-names',\n 'name',\n 'string-name',\n]);\n\n/**\n * Extract plain text from a node that may contain nested elements.\n * Recursively collects all text content from preserveOrder nodes.\n *\n * When extracting text from inline container elements (e.g. `<name>`,\n * `<string-name>`), inserts a space between adjacent child elements\n * that would otherwise concatenate without whitespace (e.g.\n * `<surname>McGuire</surname><given-names>N</given-names>` → `McGuire N`).\n */\nfunction extractAllText(node: unknown): string {\n if (node == null) return '';\n if (typeof node === 'string') return node;\n if (typeof node === 'number') return String(node);\n if (Array.isArray(node)) {\n return joinChildTexts(node);\n }\n if (typeof node === 'object') {\n const obj = node as OrderedNode;\n // Text node\n const text = getTextContent(obj);\n if (text != null) return text;\n // Element node — recurse into children\n const tag = getTagName(obj);\n if (tag) {\n const children = obj[tag];\n if (Array.isArray(children)) {\n return joinChildTexts(children as OrderedNode[]);\n }\n }\n }\n return '';\n}\n\n/**\n * Join extracted text from an array of child nodes, inserting spaces\n * between adjacent inline elements where no whitespace separator exists.\n */\nfunction joinChildTexts(children: OrderedNode[]): string {\n const parts: string[] = [];\n for (const child of children) {\n const text = extractAllText(child);\n if (!text) continue;\n\n const tag = getTagName(child as OrderedNode);\n\n // If this is a space-after tag and there's previous content that doesn't\n // end with whitespace or punctuation, insert a space before this text.\n if (tag && SPACE_AFTER_TAGS.has(tag) && parts.length > 0) {\n const prev = parts[parts.length - 1]!;\n if (prev && !/[\\s,;.:()\\-/]$/.test(prev)) {\n parts.push(' ');\n }\n }\n\n parts.push(text);\n\n // If this is a space-after tag, check if a space is needed after.\n // We handle this by peeking: space will be inserted before the next\n // element if needed (handled above). But we also need to handle\n // the case where the next sibling is a text node starting without space.\n // That's already handled since text nodes include their own whitespace.\n }\n return parts.join('');\n}\n\n// ─── Metadata Parsing ────────────────────────────────────────────────\n\n/**\n * Parse JATS XML front matter to extract article metadata.\n */\nexport function parseJatsMetadata(xml: string): JatsMetadata {\n const parsed = parser.parse(xml) as OrderedNode[];\n const article = findArticle(parsed);\n if (!article) return { title: '', authors: [] };\n\n const front = findChild(article.children, 'front');\n if (!front) return { title: '', authors: [] };\n\n const articleMeta = findChild(front.children, 'article-meta');\n if (!articleMeta) return { title: '', authors: [] };\n\n const metaChildren = articleMeta.children;\n\n // Title\n const titleGroup = findChild(metaChildren, 'title-group');\n const articleTitle = titleGroup ? findChild(titleGroup.children, 'article-title') : undefined;\n const title = articleTitle ? extractAllText(articleTitle.children) : '';\n\n // Article IDs\n const articleIds = findChildren(metaChildren, 'article-id');\n let doi: string | undefined;\n let pmcid: string | undefined;\n let pmid: string | undefined;\n for (const idEntry of articleIds) {\n const idType = idEntry.attrs['pub-id-type'];\n const idText = extractAllText(idEntry.children);\n if (idType === 'doi') doi = idText;\n if (idType === 'pmc' || idType === 'pmcid') {\n pmcid = idText.replace(/^PMC/, '');\n }\n if (idType === 'pmid') pmid = idText;\n }\n\n // Authors\n const authors: JatsAuthor[] = [];\n const contribGroup = findChild(metaChildren, 'contrib-group');\n if (contribGroup) {\n const contribs = findChildren(contribGroup.children, 'contrib');\n for (const contrib of contribs) {\n if (contrib.attrs['contrib-type'] !== 'author') continue;\n const nameNode = findChild(contrib.children, 'name');\n if (!nameNode) continue;\n const surnameNode = findChild(nameNode.children, 'surname');\n const givenNamesNode = findChild(nameNode.children, 'given-names');\n const author: JatsAuthor = {\n surname: surnameNode ? extractAllText(surnameNode.children) : '',\n };\n const givenNames = givenNamesNode ? extractAllText(givenNamesNode.children) : '';\n if (givenNames) {\n author.givenNames = givenNames;\n }\n authors.push(author);\n }\n }\n\n // Abstract\n const abstractNode = findChild(metaChildren, 'abstract');\n let abstract: string | undefined;\n if (abstractNode) {\n // Structured abstract with <sec> elements\n const sections = findChildren(abstractNode.children, 'sec');\n if (sections.length > 0) {\n const parts: string[] = [];\n for (const sec of sections) {\n const secTitleNode = findChild(sec.children, 'title');\n const secTitle = secTitleNode ? extractAllText(secTitleNode.children) : '';\n const secPs = findChildren(sec.children, 'p');\n const text = secPs.map((p) => extractAllText(p.children)).join(' ');\n if (secTitle) {\n parts.push(`${secTitle}: ${text}`);\n } else {\n parts.push(text);\n }\n }\n abstract = parts.join('\\n\\n');\n } else {\n // Simple abstract with <p>\n const paragraphs = findChildren(abstractNode.children, 'p');\n if (paragraphs.length > 0) {\n abstract = paragraphs.map((p) => extractAllText(p.children)).join('\\n\\n');\n } else {\n const text = extractAllText(abstractNode.children);\n if (text) abstract = text;\n }\n }\n }\n\n // Publication date (from <article-meta>/<pub-date>)\n // Priority: epub > ppub > collection > any other\n const pubDates = findChildren(metaChildren, 'pub-date');\n let publicationDate: { year: string; month?: string; day?: string } | undefined;\n const datePriority: Record<string, number> = { epub: 0, ppub: 1, collection: 2 };\n let bestPriority = Infinity;\n for (const pd of pubDates) {\n // Support both pub-type (NLM/early JATS) and date-type (JATS 1.2+)\n const dateType = pd.attrs['pub-type'] ?? pd.attrs['date-type'] ?? '';\n const priority = datePriority[dateType] ?? 3;\n if (priority < bestPriority) {\n bestPriority = priority;\n const yearNode = findChild(pd.children, 'year');\n if (yearNode) {\n const year = extractAllText(yearNode.children);\n const monthNode = findChild(pd.children, 'month');\n const dayNode = findChild(pd.children, 'day');\n const date: { year: string; month?: string; day?: string } = { year };\n if (monthNode) date.month = extractAllText(monthNode.children);\n if (dayNode) date.day = extractAllText(dayNode.children);\n publicationDate = date;\n }\n }\n }\n // If no prioritized date found, take first available\n if (!publicationDate && pubDates.length > 0) {\n const pd = pubDates[0]!;\n const yearNode = findChild(pd.children, 'year');\n if (yearNode) {\n const year = extractAllText(yearNode.children);\n const monthNode = findChild(pd.children, 'month');\n const dayNode = findChild(pd.children, 'day');\n const date: { year: string; month?: string; day?: string } = { year };\n if (monthNode) date.month = extractAllText(monthNode.children);\n if (dayNode) date.day = extractAllText(dayNode.children);\n publicationDate = date;\n }\n }\n\n // Article type (from root <article> element attribute)\n const articleType = article.attrs['article-type'] || undefined;\n\n // License (from <permissions>/<license>)\n let license: string | undefined;\n const permissions = findChild(metaChildren, 'permissions');\n if (permissions) {\n const licenseNode = findChild(permissions.children, 'license');\n if (licenseNode) {\n // Prefer @xlink:href (standardized URL) over <license-p> (free-text)\n const href = licenseNode.attrs['xlink:href'];\n if (href) {\n license = href;\n } else {\n const licenseP = findChild(licenseNode.children, 'license-p');\n if (licenseP) license = extractAllText(licenseP.children).trim();\n }\n }\n }\n\n // Keywords (from all <kwd-group> elements)\n const kwdGroups = findChildren(metaChildren, 'kwd-group');\n const keywords: string[] = [];\n for (const kwdGroup of kwdGroups) {\n const kwds = findChildren(kwdGroup.children, 'kwd');\n for (const kwd of kwds) {\n const text = extractAllText(kwd.children).trim();\n if (text) keywords.push(text);\n }\n }\n\n // Volume, issue, pages\n const volumeNode = findChild(metaChildren, 'volume');\n const volume = volumeNode ? extractAllText(volumeNode.children) : undefined;\n const issueNode = findChild(metaChildren, 'issue');\n const issue = issueNode ? extractAllText(issueNode.children) : undefined;\n let pages: string | undefined;\n const fpageNode = findChild(metaChildren, 'fpage');\n const lpageNode = findChild(metaChildren, 'lpage');\n if (fpageNode) {\n const fp = extractAllText(fpageNode.children);\n const lp = lpageNode ? extractAllText(lpageNode.children) : '';\n pages = lp ? `${fp}-${lp}` : fp;\n } else {\n const elocationNode = findChild(metaChildren, 'elocation-id');\n if (elocationNode) pages = extractAllText(elocationNode.children);\n }\n\n // Journal name (from <front>/<journal-meta>)\n const journalMeta = findChild(front.children, 'journal-meta');\n let journal: string | undefined;\n if (journalMeta) {\n const titleGroup = findChild(journalMeta.children, 'journal-title-group');\n if (titleGroup) {\n const jTitle = findChild(titleGroup.children, 'journal-title');\n if (jTitle) journal = extractAllText(jTitle.children);\n }\n if (!journal) {\n const jTitle = findChild(journalMeta.children, 'journal-title');\n if (jTitle) journal = extractAllText(jTitle.children);\n }\n }\n\n const result: JatsMetadata = { title, authors };\n if (doi) result.doi = doi;\n if (pmcid) result.pmcid = pmcid;\n if (pmid) result.pmid = pmid;\n if (journal) result.journal = journal;\n if (publicationDate) result.publicationDate = publicationDate;\n if (volume) result.volume = volume;\n if (issue) result.issue = issue;\n if (pages) result.pages = pages;\n if (keywords.length > 0) result.keywords = keywords;\n if (articleType) result.articleType = articleType;\n if (license) result.license = license;\n if (abstract) result.abstract = abstract;\n return result;\n}\n\n// ─── Inline Content Parsing ──────────────────────────────────────────\n\n/**\n * Parse inline content from a paragraph's children array.\n * Iterates in document order to preserve interleaving of text, citations,\n * and formatting elements.\n */\nfunction parseInlineContent(children: OrderedNode[]): InlineContent[] {\n const result: InlineContent[] = [];\n\n for (const child of children) {\n // Text node\n const text = getTextContent(child);\n if (text != null) {\n if (text) result.push({ type: 'text', text });\n continue;\n }\n\n const tag = getTagName(child);\n if (!tag) continue;\n\n const innerChildren = getChildren(child);\n\n if (tag === 'bold') {\n result.push({ type: 'bold', children: parseInlineContent(innerChildren) });\n } else if (tag === 'italic') {\n result.push({ type: 'italic', children: parseInlineContent(innerChildren) });\n } else if (tag === 'sup') {\n result.push({ type: 'superscript', text: extractAllText(innerChildren) });\n } else if (tag === 'sub') {\n result.push({ type: 'subscript', text: extractAllText(innerChildren) });\n } else if (tag === 'inline-formula') {\n // Try to find <tex-math> directly or inside <alternatives>\n let texMath = findChild(innerChildren, 'tex-math');\n if (!texMath) {\n const alternatives = findChild(innerChildren, 'alternatives');\n if (alternatives) {\n texMath = findChild(alternatives.children, 'tex-math');\n }\n }\n const tex = texMath ? extractAllText(texMath.children) : undefined;\n const text = tex || extractAllText(innerChildren);\n const entry: { type: 'inline-formula'; tex?: string; text: string } = {\n type: 'inline-formula',\n text,\n };\n if (tex) entry.tex = tex;\n result.push(entry);\n } else if (tag === 'monospace') {\n result.push({ type: 'code', text: extractAllText(innerChildren) });\n } else if (tag === 'ext-link') {\n const href = getAttr(child, 'xlink:href');\n if (href) {\n result.push({ type: 'link', url: href, children: parseInlineContent(innerChildren) });\n } else {\n const linkText = extractAllText(innerChildren);\n if (linkText) result.push({ type: 'text', text: linkText });\n }\n } else if (tag === 'uri') {\n const href = getAttr(child, 'xlink:href');\n const textContent = extractAllText(innerChildren);\n const url = href || textContent;\n if (url) {\n result.push({ type: 'link', url, children: parseInlineContent(innerChildren) });\n }\n } else if (tag === 'underline' || tag === 'sc') {\n // Pass-through: preserve text content without special formatting\n const passText = extractAllText(innerChildren);\n if (passText) result.push({ type: 'text', text: passText });\n } else if (tag === 'xref') {\n const refType = getAttr(child, 'ref-type');\n if (refType === 'bibr') {\n result.push({\n type: 'citation',\n refId: getAttr(child, 'rid') ?? '',\n text: extractAllText(innerChildren),\n });\n } else {\n const xrefText = extractAllText(innerChildren);\n if (xrefText) result.push({ type: 'text', text: xrefText });\n }\n } else {\n // Unknown inline element — extract text\n const unknownText = extractAllText(innerChildren);\n if (unknownText) result.push({ type: 'text', text: unknownText });\n }\n }\n\n return result;\n}\n\n// ─── Block Content Parsing ───────────────────────────────────────────\n\n/**\n * Parse a <list> element into a BlockElement.\n */\nfunction parseList(listNode: OrderedNode): BlockElement {\n const listType = getAttr(listNode, 'list-type');\n const ordered = listType === 'order';\n const listChildren = getChildren(listNode);\n const listItems = findChildren(listChildren, 'list-item');\n const items: InlineContent[][] = [];\n\n for (const item of listItems) {\n const pNodes = findChildren(item.children, 'p');\n const content = pNodes.flatMap((p) => parseInlineContent(p.children));\n items.push(content);\n }\n\n return { type: 'list', ordered, items };\n}\n\n/**\n * Parse a table row into an array of cell text content.\n */\nfunction parseTableRow(trChildren: OrderedNode[]): string[] {\n const cells: string[] = [];\n for (const child of trChildren) {\n const tag = getTagName(child);\n if (tag === 'th' || tag === 'td') {\n const cellChildren = getChildren(child);\n // Check if cell contains multiple <p> elements\n const paragraphs = findChildren(cellChildren, 'p');\n if (paragraphs.length > 1) {\n cells.push(\n paragraphs.map((p) => extractAllText(p.children)).join('<br>'),\n );\n } else {\n cells.push(extractAllText(cellChildren));\n }\n }\n }\n return cells;\n}\n\n/**\n * Parse an already-parsed table-wrap node.\n */\nfunction parseTableWrap(tableWrapNode: OrderedNode): {\n caption?: string;\n headers: string[];\n rows: string[][];\n} {\n const children = getChildren(tableWrapNode);\n\n // Caption\n const labelNode = findChild(children, 'label');\n const label = labelNode ? extractAllText(labelNode.children) : '';\n const captionNode = findChild(children, 'caption');\n const captionText = captionNode ? extractAllText(captionNode.children) : '';\n const captionStr = [label, captionText].filter(Boolean).join('. ');\n\n const tableNode = findChild(children, 'table');\n const result: { caption?: string; headers: string[]; rows: string[][] } = {\n headers: [],\n rows: [],\n };\n if (captionStr) result.caption = captionStr;\n if (!tableNode) return result;\n\n // Headers from thead\n const thead = findChild(tableNode.children, 'thead');\n if (thead) {\n const headRows = findChildren(thead.children, 'tr');\n if (headRows.length > 0) {\n result.headers.push(...parseTableRow(headRows[0]!.children));\n }\n }\n\n // Body rows\n const tbody = findChild(tableNode.children, 'tbody');\n if (tbody) {\n const bodyRows = findChildren(tbody.children, 'tr');\n for (const row of bodyRows) {\n result.rows.push(parseTableRow(row.children));\n }\n }\n\n return result;\n}\n\n/**\n * Parse a <table-wrap> element into a table block.\n * Exported for standalone use and used internally by parseBlockContent.\n */\nexport function parseJatsTable(xml: string): {\n caption?: string;\n headers: string[];\n rows: string[][];\n} {\n const parsed = parser.parse(xml) as OrderedNode[];\n const tableWrap = findChild(parsed, 'table-wrap');\n if (tableWrap) {\n return parseTableWrap(tableWrap.node);\n }\n // Fallback: if not wrapped, try to find table directly\n return { headers: [], rows: [] };\n}\n\n/**\n * Parse a <boxed-text> element into a boxed-text block.\n * Extracts optional title and recursively parses inner block content.\n */\nfunction parseBoxedText(node: OrderedNode): BlockElement {\n const children = getChildren(node);\n const titleNode = findChild(children, 'title');\n const title = titleNode ? extractAllText(titleNode.children) : undefined;\n const content = parseBlockContent(children);\n const block: BlockElement = { type: 'boxed-text', content };\n if (title) block.title = title;\n return block;\n}\n\n/**\n * Parse a <def-list> element into a def-list block.\n * Extracts optional title and <def-item> pairs with <term> and <def>.\n */\nfunction parseDefList(node: OrderedNode): BlockElement {\n const children = getChildren(node);\n const titleNode = findChild(children, 'title');\n const title = titleNode ? extractAllText(titleNode.children) : undefined;\n const defItems = findChildren(children, 'def-item');\n const items: { term: string; definition: string }[] = [];\n for (const item of defItems) {\n const termNode = findChild(item.children, 'term');\n const defNode = findChild(item.children, 'def');\n const term = termNode ? extractAllText(termNode.children) : '';\n const definition = defNode ? extractAllText(defNode.children) : '';\n items.push({ term, definition });\n }\n const block: BlockElement = { type: 'def-list', items };\n if (title) block.title = title;\n return block;\n}\n\n/**\n * Parse a <disp-formula> element into a formula block.\n * Extracts TeX content from <tex-math> preferentially (inside <alternatives> or direct),\n * falls back to extractAllText for plain text.\n */\nfunction parseDispFormula(node: OrderedNode): BlockElement {\n const children = getChildren(node);\n const id = getAttr(node, 'id');\n const labelNode = findChild(children, 'label');\n const label = labelNode ? extractAllText(labelNode.children) : undefined;\n\n // Try <alternatives> wrapper first\n const alternatives = findChild(children, 'alternatives');\n const searchChildren = alternatives ? alternatives.children : children;\n\n const texMath = findChild(searchChildren, 'tex-math');\n const block: BlockElement = { type: 'formula' };\n if (id) block.id = id;\n if (label) block.label = label;\n\n if (texMath) {\n block.tex = extractAllText(texMath.children);\n } else {\n // Fall back to plain text extraction (skip label)\n const textChildren = children.filter((c) => !('label' in c));\n const text = extractAllText(textChildren).trim();\n if (text) block.text = text;\n }\n\n return block;\n}\n\n/** Tags that represent block-level elements when nested inside <p>. */\nconst BLOCK_TAGS = new Set(['table-wrap', 'fig', 'disp-quote', 'boxed-text']);\n\n/**\n * Parse a <disp-quote> element into a blockquote block.\n * Extracts <p> children and concatenates their inline content.\n */\nfunction parseDispQuote(node: OrderedNode): BlockElement {\n const children = getChildren(node);\n const paragraphs = findChildren(children, 'p');\n const content: InlineContent[] = [];\n for (let i = 0; i < paragraphs.length; i++) {\n if (i > 0) content.push({ type: 'text', text: '\\n\\n' });\n const para = paragraphs[i];\n if (para) content.push(...parseInlineContent(para.children));\n }\n // If no <p> children, extract inline content directly\n if (paragraphs.length === 0) {\n content.push(...parseInlineContent(children));\n }\n return { type: 'blockquote', content };\n}\n\n/**\n * Parse a <table-wrap> node into a table block element.\n */\nfunction parseTableBlock(node: OrderedNode): BlockElement {\n const tableResult = parseTableWrap(node);\n const tableBlock: BlockElement = {\n type: 'table',\n headers: tableResult.headers,\n rows: tableResult.rows,\n };\n if (tableResult.caption) tableBlock.caption = tableResult.caption;\n return tableBlock;\n}\n\n/**\n * Parse a <fig> node into a figure block element.\n */\nfunction parseFigBlock(node: OrderedNode): BlockElement {\n const innerChildren = getChildren(node);\n const figBlock: BlockElement = { type: 'figure' };\n const figId = getAttr(node, 'id');\n if (figId) figBlock.id = figId;\n const figLabel = findChild(innerChildren, 'label');\n if (figLabel) {\n const labelText = extractAllText(figLabel.children);\n if (labelText) figBlock.label = labelText;\n }\n const figCaption = findChild(innerChildren, 'caption');\n if (figCaption) {\n const captionText = extractAllText(figCaption.children);\n if (captionText) figBlock.caption = captionText;\n }\n return figBlock;\n}\n\n/**\n * Parse a <p> element, splitting it if it contains nested block elements\n * (table-wrap, fig, disp-quote). Returns one or more block elements.\n */\nfunction parseParagraph(pChildren: OrderedNode[]): BlockElement[] {\n // Check if <p> contains any nested block elements\n const hasNestedBlocks = pChildren.some((child) => {\n const tag = getTagName(child);\n return tag != null && BLOCK_TAGS.has(tag);\n });\n\n if (!hasNestedBlocks) {\n return [{ type: 'paragraph', content: parseInlineContent(pChildren) }];\n }\n\n // Split into inline runs and block elements\n const blocks: BlockElement[] = [];\n let inlineBuffer: OrderedNode[] = [];\n\n const flushInline = () => {\n if (inlineBuffer.length > 0) {\n const content = parseInlineContent(inlineBuffer);\n // Skip whitespace-only paragraphs created by XML formatting\n const hasNonWhitespace = content.some(\n (c) => c.type !== 'text' || c.text.trim() !== '',\n );\n if (content.length > 0 && hasNonWhitespace) {\n blocks.push({ type: 'paragraph', content });\n }\n inlineBuffer = [];\n }\n };\n\n for (const child of pChildren) {\n const tag = getTagName(child);\n if (tag === 'table-wrap') {\n flushInline();\n blocks.push(parseTableBlock(child));\n } else if (tag === 'fig') {\n flushInline();\n blocks.push(parseFigBlock(child));\n } else if (tag === 'disp-quote') {\n flushInline();\n blocks.push(parseDispQuote(child));\n } else if (tag === 'boxed-text') {\n flushInline();\n blocks.push(parseBoxedText(child));\n } else {\n inlineBuffer.push(child);\n }\n }\n flushInline();\n\n return blocks;\n}\n\n/**\n * Parse block-level content from a section's children.\n * Iterates in document order to preserve ordering of paragraphs, lists,\n * tables, figures, and blockquotes.\n */\nfunction parseBlockContent(sectionChildren: OrderedNode[]): BlockElement[] {\n const blocks: BlockElement[] = [];\n\n for (const child of sectionChildren) {\n const tag = getTagName(child);\n if (!tag) continue;\n\n if (tag === 'p') {\n blocks.push(...parseParagraph(getChildren(child)));\n } else if (tag === 'list') {\n blocks.push(parseList(child));\n } else if (tag === 'table-wrap') {\n blocks.push(parseTableBlock(child));\n } else if (tag === 'fig') {\n blocks.push(parseFigBlock(child));\n } else if (tag === 'disp-quote') {\n blocks.push(parseDispQuote(child));\n } else if (tag === 'boxed-text') {\n blocks.push(parseBoxedText(child));\n } else if (tag === 'def-list') {\n blocks.push(parseDefList(child));\n } else if (tag === 'disp-formula') {\n blocks.push(parseDispFormula(child));\n } else if (tag === 'preformat') {\n const text = extractAllText(getChildren(child));\n blocks.push({ type: 'preformat', text });\n } else if (tag === 'supplementary-material') {\n const innerChildren = getChildren(child);\n const labelNode = findChild(innerChildren, 'label');\n const captionNode = findChild(innerChildren, 'caption');\n const labelText = labelNode ? extractAllText(labelNode.children) : '';\n const captionText = captionNode ? extractAllText(captionNode.children) : '';\n const text = [labelText, captionText].filter(Boolean).join(': ');\n if (text) {\n blocks.push({ type: 'paragraph', content: [{ type: 'text', text }] });\n }\n }\n // Skip title, sec, and other non-block elements\n }\n\n return blocks;\n}\n\n// ─── Section Parsing ─────────────────────────────────────────────────\n\n/**\n * Parse a <sec> element into a JatsSection, recursively handling subsections.\n */\nfunction parseSection(secChildren: OrderedNode[], level: number): JatsSection {\n const titleNode = findChild(secChildren, 'title');\n const title = titleNode ? extractAllText(titleNode.children) : '';\n const content = parseBlockContent(secChildren);\n\n // Nested sections\n const subsections: JatsSection[] = [];\n const nestedSecs = findChildren(secChildren, 'sec');\n for (const sub of nestedSecs) {\n subsections.push(parseSection(sub.children, level + 1));\n }\n\n return { title, level, content, subsections };\n}\n\n/**\n * Parse JATS XML body to extract sections and content.\n */\nexport function parseJatsBody(xml: string): JatsSection[] {\n const parsed = parser.parse(xml) as OrderedNode[];\n const article = findArticle(parsed);\n if (!article) return [];\n\n const body = findChild(article.children, 'body');\n if (!body) return [];\n\n const sections: JatsSection[] = [];\n const secs = findChildren(body.children, 'sec');\n\n if (secs.length > 0) {\n for (const sec of secs) {\n sections.push(parseSection(sec.children, 2));\n }\n } else {\n // Body has paragraphs without sections\n const content = parseBlockContent(body.children);\n if (content.length > 0) {\n sections.push({ title: '', level: 2, content, subsections: [] });\n }\n }\n\n return sections;\n}\n\n// ─── Reference Parsing ───────────────────────────────────────────────\n\n/**\n * Format a structured <element-citation> into a readable reference string.\n * Produces: \"Author1, Author2. Title. Source. Year;Volume:FirstPage-LastPage.\"\n */\nfunction formatElementCitation(children: OrderedNode[]): string {\n const parts: string[] = [];\n\n // Authors from person-group\n const personGroup = findChild(children, 'person-group');\n if (personGroup) {\n const names = findChildren(personGroup.children, 'name');\n const authorParts: string[] = [];\n for (const name of names) {\n const surname = findChild(name.children, 'surname');\n const givenNames = findChild(name.children, 'given-names');\n const surnameText = surname ? extractAllText(surname.children) : '';\n const givenText = givenNames ? extractAllText(givenNames.children) : '';\n if (surnameText && givenText) {\n authorParts.push(`${surnameText} ${givenText}`);\n } else if (surnameText) {\n authorParts.push(surnameText);\n }\n }\n if (authorParts.length > 0) {\n parts.push(authorParts.join(', '));\n }\n }\n\n // Article title\n const articleTitle = findChild(children, 'article-title');\n if (articleTitle) {\n parts.push(extractAllText(articleTitle.children));\n }\n\n // Source (journal name)\n const source = findChild(children, 'source');\n if (source) {\n parts.push(extractAllText(source.children));\n }\n\n // Year, volume, pages\n const year = findChild(children, 'year');\n const volume = findChild(children, 'volume');\n const fpage = findChild(children, 'fpage');\n const lpage = findChild(children, 'lpage');\n\n if (year) {\n let yearStr = extractAllText(year.children);\n if (volume) {\n yearStr += `;${extractAllText(volume.children)}`;\n }\n if (fpage) {\n const fpageText = extractAllText(fpage.children);\n const lpageText = lpage ? extractAllText(lpage.children) : '';\n yearStr += `:${fpageText}${lpageText ? `-${lpageText}` : ''}`;\n }\n parts.push(yearStr);\n }\n\n return parts.join('. ') + '.';\n}\n\n/**\n * Extract text from a <mixed-citation>'s children, deduplicating any\n * <pub-id> content that also appears as inline text.\n *\n * Some publishers include the DOI/PMID both as a text node and inside\n * a <pub-id> element, causing duplication like \"10.1234/x 10.1234/x\".\n */\nfunction extractMixedCitationText(children: OrderedNode[]): string {\n // Collect pub-id values\n const pubIds = findChildren(children, 'pub-id');\n const pubIdValues = pubIds\n .map((p) => extractAllText(p.children).trim())\n .filter(Boolean);\n\n if (pubIdValues.length === 0) {\n return extractAllText(children).trim();\n }\n\n // Extract full text\n const fullText = extractAllText(children).trim();\n\n // For each pub-id value, if it appears more than once, remove extra occurrences\n let result = fullText;\n for (const val of pubIdValues) {\n // Escape regex special characters\n const escaped = val.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const matches = result.match(new RegExp(escaped, 'g'));\n if (matches && matches.length > 1) {\n // Remove the first occurrence (typically the inline text), keep the last (pub-id element)\n result = result.replace(val, '');\n // Clean up any leftover extra whitespace\n result = result.replace(/\\s{2,}/g, ' ').trim();\n }\n }\n\n return result;\n}\n\n/**\n * Parse JATS XML back matter to extract references.\n */\nexport function parseJatsReferences(xml: string): JatsReference[] {\n const parsed = parser.parse(xml) as OrderedNode[];\n const article = findArticle(parsed);\n if (!article) return [];\n\n const back = findChild(article.children, 'back');\n if (!back) return [];\n\n const refList = findChild(back.children, 'ref-list');\n if (!refList) return [];\n\n const refs = findChildren(refList.children, 'ref');\n const references: JatsReference[] = [];\n\n for (const ref of refs) {\n const id = ref.attrs['id'] ?? '';\n\n // Determine the search scope: if <citation-alternatives> exists, search within it;\n // otherwise search direct children of <ref>\n const citationAlternatives = findChild(ref.children, 'citation-alternatives');\n const searchChildren = citationAlternatives ? citationAlternatives.children : ref.children;\n\n // Try mixed-citation first (already formatted), then element-citation (structured)\n const mixedCitation = findChild(searchChildren, 'mixed-citation');\n if (mixedCitation) {\n const text = extractMixedCitationText(mixedCitation.children);\n if (id && text) references.push({ id, text });\n continue;\n }\n\n const elementCitation = findChild(searchChildren, 'element-citation');\n if (elementCitation) {\n const text = formatElementCitation(elementCitation.children);\n if (id && text) references.push({ id, text });\n continue;\n }\n\n // Fallback: extract all text from ref, skipping <label>\n const childrenWithoutLabel = ref.children.filter((c) => !('label' in c));\n const text = extractAllText(childrenWithoutLabel).trim();\n if (id && text) {\n references.push({ id, text });\n }\n }\n\n return references;\n}\n\n// ─── Back Matter & Floats Parsing ────────────────────────────────────\n\n/** Result of parsing back matter and floats-group. */\nexport interface BackMatterResult {\n acknowledgments?: string;\n appendices?: JatsSection[];\n footnotes?: JatsFootnote[];\n floats?: BlockElement[];\n}\n\n/**\n * Parse JATS XML back matter sections (ack, app-group, fn-group)\n * and top-level floats-group.\n */\nexport function parseJatsBackMatter(xml: string): BackMatterResult {\n const parsed = parser.parse(xml) as OrderedNode[];\n const article = findArticle(parsed);\n if (!article) return {};\n\n const result: BackMatterResult = {};\n\n // Parse <back> children\n const back = findChild(article.children, 'back');\n if (back) {\n // Acknowledgments: <ack>\n const ack = findChild(back.children, 'ack');\n if (ack) {\n const paragraphs = findChildren(ack.children, 'p');\n if (paragraphs.length > 0) {\n result.acknowledgments = paragraphs\n .map((p) => extractAllText(p.children))\n .join('\\n\\n');\n }\n }\n\n // Appendices: <app-group>/<app>\n const appGroup = findChild(back.children, 'app-group');\n if (appGroup) {\n const apps = findChildren(appGroup.children, 'app');\n if (apps.length > 0) {\n result.appendices = apps.map((app) => parseSection(app.children, 2));\n }\n }\n\n // Footnotes: <fn-group>/<fn>\n const fnGroup = findChild(back.children, 'fn-group');\n if (fnGroup) {\n const fns = findChildren(fnGroup.children, 'fn');\n if (fns.length > 0) {\n result.footnotes = fns.map((fn) => ({\n id: fn.attrs['id'] ?? '',\n text: extractAllText(\n findChildren(fn.children, 'p').flatMap((p) => p.children),\n ),\n }));\n }\n }\n }\n\n // Floats-group: <floats-group> (sibling of <body> and <back>)\n const floatsGroup = findChild(article.children, 'floats-group');\n if (floatsGroup) {\n const blocks: BlockElement[] = [];\n for (const child of floatsGroup.children) {\n const tag = getTagName(child);\n if (tag === 'fig') {\n blocks.push(parseFigBlock(child));\n } else if (tag === 'table-wrap') {\n blocks.push(parseTableBlock(child));\n }\n }\n if (blocks.length > 0) {\n result.floats = blocks;\n }\n }\n\n return result;\n}\n"],"names":["titleGroup","text"],"mappings":"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"}
1
+ {"version":3,"file":"jats-parser.js","sources":["../../../src/fulltext/convert/jats-parser.ts"],"sourcesContent":["/**\n * JATS XML parser for PMC articles.\n *\n * Parses JATS (Journal Article Tag Suite) XML into an intermediate\n * representation for Markdown conversion.\n *\n * Uses fast-xml-parser with `preserveOrder: true` to maintain document order\n * of interleaved elements (e.g. text, citations, formatting).\n */\n\nimport { XMLParser } from 'fast-xml-parser';\nimport type {\n JatsAuthor,\n JatsMetadata,\n JatsSection,\n JatsReference,\n JatsFootnote,\n BackMatterNote,\n BlockElement,\n InlineContent,\n} from './types.js';\n\n/**\n * A node in the preserveOrder output.\n * Either a text node `{ \"#text\": string | number }` or an element node\n * `{ tagName: OrderedNode[], \":@\"?: { \"@_attr\": value } }`.\n */\ntype OrderedNode = Record<string, unknown>;\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n textNodeName: '#text',\n trimValues: false,\n preserveOrder: true,\n processEntities: true,\n htmlEntities: true,\n});\n\n// ─── Navigation Helpers ──────────────────────────────────────────────\n\n/** Get the tag name of an ordered node (the first key that isn't \":@\" or \"#text\"). */\nfunction getTagName(node: OrderedNode): string | undefined {\n for (const key of Object.keys(node)) {\n if (key !== ':@' && key !== '#text') return key;\n }\n return undefined;\n}\n\n/** Get the children array of an element node. */\nfunction getChildren(node: OrderedNode): OrderedNode[] {\n const tag = getTagName(node);\n if (!tag) return [];\n const children = node[tag];\n return Array.isArray(children) ? (children as OrderedNode[]) : [];\n}\n\n/** Get attributes of an element node. */\nfunction getAttr(node: OrderedNode, attrName: string): string | undefined {\n const attrs = node[':@'] as Record<string, unknown> | undefined;\n if (!attrs) return undefined;\n const val = attrs[`@_${attrName}`];\n return val != null ? String(val) : undefined;\n}\n\n/** Get all attributes of an element node (strips @_ prefix for consistency with getAttr). */\nfunction getAttrs(node: OrderedNode): Record<string, string> {\n const attrs = node[':@'] as Record<string, unknown> | undefined;\n if (!attrs) return {};\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(attrs)) {\n if (key.startsWith('@_')) {\n result[key.slice(2)] = String(value);\n }\n }\n return result;\n}\n\n/** Find the first child element with the given tag name. */\nfunction findChild(\n children: OrderedNode[],\n tagName: string,\n): { node: OrderedNode; children: OrderedNode[]; attrs: Record<string, string> } | undefined {\n for (const child of children) {\n if (tagName in child) {\n const childArr = child[tagName];\n return {\n node: child,\n children: Array.isArray(childArr) ? (childArr as OrderedNode[]) : [],\n attrs: getAttrs(child),\n };\n }\n }\n return undefined;\n}\n\n/** Find all child elements with the given tag name. */\nfunction findChildren(\n children: OrderedNode[],\n tagName: string,\n): Array<{ node: OrderedNode; children: OrderedNode[]; attrs: Record<string, string> }> {\n const results: Array<{\n node: OrderedNode;\n children: OrderedNode[];\n attrs: Record<string, string>;\n }> = [];\n for (const child of children) {\n if (tagName in child) {\n const childArr = child[tagName];\n results.push({\n node: child,\n children: Array.isArray(childArr) ? (childArr as OrderedNode[]) : [],\n attrs: getAttrs(child),\n });\n }\n }\n return results;\n}\n\n/** Get text content from a #text node. */\nfunction getTextContent(child: OrderedNode): string | undefined {\n if ('#text' in child) {\n const val = child['#text'];\n return val != null ? String(val) : undefined;\n }\n return undefined;\n}\n\n/**\n * Find the <article> element, handling optional <pmc-articleset> wrapper\n * that appears in efetch responses.\n */\nfunction findArticle(\n parsed: OrderedNode[],\n): { node: OrderedNode; children: OrderedNode[]; attrs: Record<string, string> } | undefined {\n const direct = findChild(parsed, 'article');\n if (direct) return direct;\n const wrapper = findChild(parsed, 'pmc-articleset');\n if (wrapper) return findChild(wrapper.children, 'article');\n return undefined;\n}\n\n// ─── Text Extraction ─────────────────────────────────────────────────\n\n/** Tags whose text content should be followed by a space when adjacent to other content. */\nconst SPACE_AFTER_TAGS = new Set([\n 'surname',\n 'given-names',\n 'name',\n 'string-name',\n]);\n\n/**\n * Extract plain text from a node that may contain nested elements.\n * Recursively collects all text content from preserveOrder nodes.\n *\n * When extracting text from inline container elements (e.g. `<name>`,\n * `<string-name>`), inserts a space between adjacent child elements\n * that would otherwise concatenate without whitespace (e.g.\n * `<surname>McGuire</surname><given-names>N</given-names>` → `McGuire N`).\n */\nfunction extractAllText(node: unknown): string {\n if (node == null) return '';\n if (typeof node === 'string') return node;\n if (typeof node === 'number') return String(node);\n if (Array.isArray(node)) {\n return joinChildTexts(node);\n }\n if (typeof node === 'object') {\n const obj = node as OrderedNode;\n // Text node\n const text = getTextContent(obj);\n if (text != null) return text;\n // Element node — recurse into children\n const tag = getTagName(obj);\n if (tag) {\n const children = obj[tag];\n if (Array.isArray(children)) {\n return joinChildTexts(children as OrderedNode[]);\n }\n }\n }\n return '';\n}\n\n/**\n * Join extracted text from an array of child nodes, inserting spaces\n * between adjacent inline elements where no whitespace separator exists.\n */\nfunction joinChildTexts(children: OrderedNode[]): string {\n const parts: string[] = [];\n for (const child of children) {\n const text = extractAllText(child);\n if (!text) continue;\n\n const tag = getTagName(child as OrderedNode);\n\n // If this is a space-after tag and there's previous content that doesn't\n // end with whitespace or punctuation, insert a space before this text.\n if (tag && SPACE_AFTER_TAGS.has(tag) && parts.length > 0) {\n const prev = parts[parts.length - 1]!;\n if (prev && !/[\\s,;.:()\\-/]$/.test(prev)) {\n parts.push(' ');\n }\n }\n\n parts.push(text);\n\n // If this is a space-after tag, check if a space is needed after.\n // We handle this by peeking: space will be inserted before the next\n // element if needed (handled above). But we also need to handle\n // the case where the next sibling is a text node starting without space.\n // That's already handled since text nodes include their own whitespace.\n }\n return parts.join('');\n}\n\n// ─── Metadata Parsing ────────────────────────────────────────────────\n\n/**\n * Parse JATS XML front matter to extract article metadata.\n */\nexport function parseJatsMetadata(xml: string): JatsMetadata {\n const parsed = parser.parse(xml) as OrderedNode[];\n const article = findArticle(parsed);\n if (!article) return { title: '', authors: [] };\n\n const front = findChild(article.children, 'front');\n if (!front) return { title: '', authors: [] };\n\n const articleMeta = findChild(front.children, 'article-meta');\n if (!articleMeta) return { title: '', authors: [] };\n\n const metaChildren = articleMeta.children;\n\n // Title\n const titleGroup = findChild(metaChildren, 'title-group');\n const articleTitle = titleGroup ? findChild(titleGroup.children, 'article-title') : undefined;\n const title = articleTitle ? extractAllText(articleTitle.children) : '';\n\n // Article IDs\n const articleIds = findChildren(metaChildren, 'article-id');\n let doi: string | undefined;\n let pmcid: string | undefined;\n let pmid: string | undefined;\n for (const idEntry of articleIds) {\n const idType = idEntry.attrs['pub-id-type'];\n const idText = extractAllText(idEntry.children);\n if (idType === 'doi') doi = idText;\n if (idType === 'pmc' || idType === 'pmcid') {\n pmcid = idText.replace(/^PMC/, '');\n }\n if (idType === 'pmid') pmid = idText;\n }\n\n // Authors\n const authors: JatsAuthor[] = [];\n const contribGroup = findChild(metaChildren, 'contrib-group');\n if (contribGroup) {\n const contribs = findChildren(contribGroup.children, 'contrib');\n for (const contrib of contribs) {\n if (contrib.attrs['contrib-type'] !== 'author') continue;\n const nameNode = findChild(contrib.children, 'name');\n if (!nameNode) continue;\n const surnameNode = findChild(nameNode.children, 'surname');\n const givenNamesNode = findChild(nameNode.children, 'given-names');\n const author: JatsAuthor = {\n surname: surnameNode ? extractAllText(surnameNode.children) : '',\n };\n const givenNames = givenNamesNode ? extractAllText(givenNamesNode.children) : '';\n if (givenNames) {\n author.givenNames = givenNames;\n }\n authors.push(author);\n }\n }\n\n // Abstract\n const abstractNode = findChild(metaChildren, 'abstract');\n let abstract: string | undefined;\n if (abstractNode) {\n // Structured abstract with <sec> elements\n const sections = findChildren(abstractNode.children, 'sec');\n if (sections.length > 0) {\n const parts: string[] = [];\n for (const sec of sections) {\n const secTitleNode = findChild(sec.children, 'title');\n const secTitle = secTitleNode ? extractAllText(secTitleNode.children) : '';\n const secPs = findChildren(sec.children, 'p');\n const text = secPs.map((p) => extractAllText(p.children)).join(' ');\n if (secTitle) {\n parts.push(`${secTitle}: ${text}`);\n } else {\n parts.push(text);\n }\n }\n abstract = parts.join('\\n\\n');\n } else {\n // Simple abstract with <p>\n const paragraphs = findChildren(abstractNode.children, 'p');\n if (paragraphs.length > 0) {\n abstract = paragraphs.map((p) => extractAllText(p.children)).join('\\n\\n');\n } else {\n const text = extractAllText(abstractNode.children);\n if (text) abstract = text;\n }\n }\n }\n\n // Publication date (from <article-meta>/<pub-date>)\n // Priority: epub > ppub > collection > any other\n const pubDates = findChildren(metaChildren, 'pub-date');\n let publicationDate: { year: string; month?: string; day?: string } | undefined;\n const datePriority: Record<string, number> = { epub: 0, ppub: 1, collection: 2 };\n let bestPriority = Infinity;\n for (const pd of pubDates) {\n // Support both pub-type (NLM/early JATS) and date-type (JATS 1.2+)\n const dateType = pd.attrs['pub-type'] ?? pd.attrs['date-type'] ?? '';\n const priority = datePriority[dateType] ?? 3;\n if (priority < bestPriority) {\n bestPriority = priority;\n const yearNode = findChild(pd.children, 'year');\n if (yearNode) {\n const year = extractAllText(yearNode.children);\n const monthNode = findChild(pd.children, 'month');\n const dayNode = findChild(pd.children, 'day');\n const date: { year: string; month?: string; day?: string } = { year };\n if (monthNode) date.month = extractAllText(monthNode.children);\n if (dayNode) date.day = extractAllText(dayNode.children);\n publicationDate = date;\n }\n }\n }\n // If no prioritized date found, take first available\n if (!publicationDate && pubDates.length > 0) {\n const pd = pubDates[0]!;\n const yearNode = findChild(pd.children, 'year');\n if (yearNode) {\n const year = extractAllText(yearNode.children);\n const monthNode = findChild(pd.children, 'month');\n const dayNode = findChild(pd.children, 'day');\n const date: { year: string; month?: string; day?: string } = { year };\n if (monthNode) date.month = extractAllText(monthNode.children);\n if (dayNode) date.day = extractAllText(dayNode.children);\n publicationDate = date;\n }\n }\n\n // Article type (from root <article> element attribute)\n const articleType = article.attrs['article-type'] || undefined;\n\n // License (from <permissions>/<license>)\n let license: string | undefined;\n const permissions = findChild(metaChildren, 'permissions');\n if (permissions) {\n const licenseNode = findChild(permissions.children, 'license');\n if (licenseNode) {\n // Prefer @xlink:href (standardized URL) over <license-p> (free-text)\n const href = licenseNode.attrs['xlink:href'];\n if (href) {\n license = href;\n } else {\n const licenseP = findChild(licenseNode.children, 'license-p');\n if (licenseP) license = extractAllText(licenseP.children).trim();\n }\n }\n }\n\n // Keywords (from all <kwd-group> elements)\n const kwdGroups = findChildren(metaChildren, 'kwd-group');\n const keywords: string[] = [];\n for (const kwdGroup of kwdGroups) {\n const kwds = findChildren(kwdGroup.children, 'kwd');\n for (const kwd of kwds) {\n const text = extractAllText(kwd.children).trim();\n if (text) keywords.push(text);\n }\n }\n\n // Volume, issue, pages\n const volumeNode = findChild(metaChildren, 'volume');\n const volume = volumeNode ? extractAllText(volumeNode.children) : undefined;\n const issueNode = findChild(metaChildren, 'issue');\n const issue = issueNode ? extractAllText(issueNode.children) : undefined;\n let pages: string | undefined;\n const fpageNode = findChild(metaChildren, 'fpage');\n const lpageNode = findChild(metaChildren, 'lpage');\n if (fpageNode) {\n const fp = extractAllText(fpageNode.children);\n const lp = lpageNode ? extractAllText(lpageNode.children) : '';\n pages = lp ? `${fp}-${lp}` : fp;\n } else {\n const elocationNode = findChild(metaChildren, 'elocation-id');\n if (elocationNode) pages = extractAllText(elocationNode.children);\n }\n\n // Journal name (from <front>/<journal-meta>)\n const journalMeta = findChild(front.children, 'journal-meta');\n let journal: string | undefined;\n if (journalMeta) {\n const titleGroup = findChild(journalMeta.children, 'journal-title-group');\n if (titleGroup) {\n const jTitle = findChild(titleGroup.children, 'journal-title');\n if (jTitle) journal = extractAllText(jTitle.children);\n }\n if (!journal) {\n const jTitle = findChild(journalMeta.children, 'journal-title');\n if (jTitle) journal = extractAllText(jTitle.children);\n }\n }\n\n const result: JatsMetadata = { title, authors };\n if (doi) result.doi = doi;\n if (pmcid) result.pmcid = pmcid;\n if (pmid) result.pmid = pmid;\n if (journal) result.journal = journal;\n if (publicationDate) result.publicationDate = publicationDate;\n if (volume) result.volume = volume;\n if (issue) result.issue = issue;\n if (pages) result.pages = pages;\n if (keywords.length > 0) result.keywords = keywords;\n if (articleType) result.articleType = articleType;\n if (license) result.license = license;\n if (abstract) result.abstract = abstract;\n return result;\n}\n\n// ─── Inline Content Parsing ──────────────────────────────────────────\n\n/**\n * Parse inline content from a paragraph's children array.\n * Iterates in document order to preserve interleaving of text, citations,\n * and formatting elements.\n */\nfunction parseInlineContent(children: OrderedNode[]): InlineContent[] {\n const result: InlineContent[] = [];\n\n for (const child of children) {\n // Text node\n const text = getTextContent(child);\n if (text != null) {\n if (text) result.push({ type: 'text', text });\n continue;\n }\n\n const tag = getTagName(child);\n if (!tag) continue;\n\n const innerChildren = getChildren(child);\n\n if (tag === 'bold') {\n result.push({ type: 'bold', children: parseInlineContent(innerChildren) });\n } else if (tag === 'italic') {\n result.push({ type: 'italic', children: parseInlineContent(innerChildren) });\n } else if (tag === 'sup') {\n result.push({ type: 'superscript', text: extractAllText(innerChildren) });\n } else if (tag === 'sub') {\n result.push({ type: 'subscript', text: extractAllText(innerChildren) });\n } else if (tag === 'inline-formula') {\n // Try to find <tex-math> directly or inside <alternatives>\n let texMath = findChild(innerChildren, 'tex-math');\n if (!texMath) {\n const alternatives = findChild(innerChildren, 'alternatives');\n if (alternatives) {\n texMath = findChild(alternatives.children, 'tex-math');\n }\n }\n const tex = texMath ? extractAllText(texMath.children) : undefined;\n const text = tex || extractAllText(innerChildren);\n const entry: { type: 'inline-formula'; tex?: string; text: string } = {\n type: 'inline-formula',\n text,\n };\n if (tex) entry.tex = tex;\n result.push(entry);\n } else if (tag === 'monospace') {\n result.push({ type: 'code', text: extractAllText(innerChildren) });\n } else if (tag === 'ext-link') {\n const href = getAttr(child, 'xlink:href');\n if (href) {\n result.push({ type: 'link', url: href, children: parseInlineContent(innerChildren) });\n } else {\n const linkText = extractAllText(innerChildren);\n if (linkText) result.push({ type: 'text', text: linkText });\n }\n } else if (tag === 'uri') {\n const href = getAttr(child, 'xlink:href');\n const textContent = extractAllText(innerChildren);\n const url = href || textContent;\n if (url) {\n result.push({ type: 'link', url, children: parseInlineContent(innerChildren) });\n }\n } else if (tag === 'underline' || tag === 'sc') {\n // Pass-through: preserve text content without special formatting\n const passText = extractAllText(innerChildren);\n if (passText) result.push({ type: 'text', text: passText });\n } else if (tag === 'xref') {\n const refType = getAttr(child, 'ref-type');\n if (refType === 'bibr') {\n result.push({\n type: 'citation',\n refId: getAttr(child, 'rid') ?? '',\n text: extractAllText(innerChildren),\n });\n } else {\n const xrefText = extractAllText(innerChildren);\n if (xrefText) result.push({ type: 'text', text: xrefText });\n }\n } else {\n // Unknown inline element — extract text\n const unknownText = extractAllText(innerChildren);\n if (unknownText) result.push({ type: 'text', text: unknownText });\n }\n }\n\n return result;\n}\n\n// ─── Block Content Parsing ───────────────────────────────────────────\n\n/**\n * Parse a <list> element into a BlockElement.\n */\nfunction parseList(listNode: OrderedNode): BlockElement {\n const listType = getAttr(listNode, 'list-type');\n const ordered = listType === 'order';\n const listChildren = getChildren(listNode);\n const listItems = findChildren(listChildren, 'list-item');\n const items: InlineContent[][] = [];\n\n for (const item of listItems) {\n const pNodes = findChildren(item.children, 'p');\n const content = pNodes.flatMap((p) => parseInlineContent(p.children));\n items.push(content);\n }\n\n return { type: 'list', ordered, items };\n}\n\n/**\n * Parse a table row into an array of cell text content.\n */\nfunction parseTableRow(trChildren: OrderedNode[]): string[] {\n const cells: string[] = [];\n for (const child of trChildren) {\n const tag = getTagName(child);\n if (tag === 'th' || tag === 'td') {\n const cellChildren = getChildren(child);\n // Check if cell contains multiple <p> elements\n const paragraphs = findChildren(cellChildren, 'p');\n if (paragraphs.length > 1) {\n cells.push(\n paragraphs.map((p) => extractAllText(p.children)).join('<br>'),\n );\n } else {\n cells.push(extractAllText(cellChildren));\n }\n }\n }\n return cells;\n}\n\n/**\n * Parse an already-parsed table-wrap node.\n */\nfunction parseTableWrap(tableWrapNode: OrderedNode): {\n caption?: string;\n headers: string[];\n rows: string[][];\n} {\n const children = getChildren(tableWrapNode);\n\n // Caption\n const labelNode = findChild(children, 'label');\n const label = labelNode ? extractAllText(labelNode.children) : '';\n const captionNode = findChild(children, 'caption');\n const captionText = captionNode ? extractAllText(captionNode.children) : '';\n const captionStr = [label, captionText].filter(Boolean).join('. ');\n\n const tableNode = findChild(children, 'table');\n const result: { caption?: string; headers: string[]; rows: string[][] } = {\n headers: [],\n rows: [],\n };\n if (captionStr) result.caption = captionStr;\n if (!tableNode) return result;\n\n // Headers from thead\n const thead = findChild(tableNode.children, 'thead');\n if (thead) {\n const headRows = findChildren(thead.children, 'tr');\n if (headRows.length > 0) {\n result.headers.push(...parseTableRow(headRows[0]!.children));\n }\n }\n\n // Body rows\n const tbody = findChild(tableNode.children, 'tbody');\n if (tbody) {\n const bodyRows = findChildren(tbody.children, 'tr');\n for (const row of bodyRows) {\n result.rows.push(parseTableRow(row.children));\n }\n }\n\n return result;\n}\n\n/**\n * Parse a <table-wrap> element into a table block.\n * Exported for standalone use and used internally by parseBlockContent.\n */\nexport function parseJatsTable(xml: string): {\n caption?: string;\n headers: string[];\n rows: string[][];\n} {\n const parsed = parser.parse(xml) as OrderedNode[];\n const tableWrap = findChild(parsed, 'table-wrap');\n if (tableWrap) {\n return parseTableWrap(tableWrap.node);\n }\n // Fallback: if not wrapped, try to find table directly\n return { headers: [], rows: [] };\n}\n\n/**\n * Parse a <boxed-text> element into a boxed-text block.\n * Extracts optional title and recursively parses inner block content.\n */\nfunction parseBoxedText(node: OrderedNode): BlockElement {\n const children = getChildren(node);\n const titleNode = findChild(children, 'title');\n const title = titleNode ? extractAllText(titleNode.children) : undefined;\n const content = parseBlockContent(children);\n const block: BlockElement = { type: 'boxed-text', content };\n if (title) block.title = title;\n return block;\n}\n\n/**\n * Parse a <def-list> element into a def-list block.\n * Extracts optional title and <def-item> pairs with <term> and <def>.\n */\nfunction parseDefList(node: OrderedNode): BlockElement {\n const children = getChildren(node);\n const titleNode = findChild(children, 'title');\n const title = titleNode ? extractAllText(titleNode.children) : undefined;\n const defItems = findChildren(children, 'def-item');\n const items: { term: string; definition: string }[] = [];\n for (const item of defItems) {\n const termNode = findChild(item.children, 'term');\n const defNode = findChild(item.children, 'def');\n const term = termNode ? extractAllText(termNode.children) : '';\n const definition = defNode ? extractAllText(defNode.children) : '';\n items.push({ term, definition });\n }\n const block: BlockElement = { type: 'def-list', items };\n if (title) block.title = title;\n return block;\n}\n\n/**\n * Parse a <disp-formula> element into a formula block.\n * Extracts TeX content from <tex-math> preferentially (inside <alternatives> or direct),\n * falls back to extractAllText for plain text.\n */\nfunction parseDispFormula(node: OrderedNode): BlockElement {\n const children = getChildren(node);\n const id = getAttr(node, 'id');\n const labelNode = findChild(children, 'label');\n const label = labelNode ? extractAllText(labelNode.children) : undefined;\n\n // Try <alternatives> wrapper first\n const alternatives = findChild(children, 'alternatives');\n const searchChildren = alternatives ? alternatives.children : children;\n\n const texMath = findChild(searchChildren, 'tex-math');\n const block: BlockElement = { type: 'formula' };\n if (id) block.id = id;\n if (label) block.label = label;\n\n if (texMath) {\n block.tex = extractAllText(texMath.children);\n } else {\n // Fall back to plain text extraction (skip label)\n const textChildren = children.filter((c) => !('label' in c));\n const text = extractAllText(textChildren).trim();\n if (text) block.text = text;\n }\n\n return block;\n}\n\n/** Tags that represent block-level elements when nested inside <p>. */\nconst BLOCK_TAGS = new Set(['table-wrap', 'fig', 'disp-quote', 'boxed-text']);\n\n/**\n * Parse a <disp-quote> element into a blockquote block.\n * Extracts <p> children and concatenates their inline content.\n */\nfunction parseDispQuote(node: OrderedNode): BlockElement {\n const children = getChildren(node);\n const paragraphs = findChildren(children, 'p');\n const content: InlineContent[] = [];\n for (let i = 0; i < paragraphs.length; i++) {\n if (i > 0) content.push({ type: 'text', text: '\\n\\n' });\n const para = paragraphs[i];\n if (para) content.push(...parseInlineContent(para.children));\n }\n // If no <p> children, extract inline content directly\n if (paragraphs.length === 0) {\n content.push(...parseInlineContent(children));\n }\n return { type: 'blockquote', content };\n}\n\n/**\n * Parse a <table-wrap> node into a table block element.\n */\nfunction parseTableBlock(node: OrderedNode): BlockElement {\n const tableResult = parseTableWrap(node);\n const tableBlock: BlockElement = {\n type: 'table',\n headers: tableResult.headers,\n rows: tableResult.rows,\n };\n if (tableResult.caption) tableBlock.caption = tableResult.caption;\n return tableBlock;\n}\n\n/**\n * Parse a <fig> node into a figure block element.\n */\nfunction parseFigBlock(node: OrderedNode): BlockElement {\n const innerChildren = getChildren(node);\n const figBlock: BlockElement = { type: 'figure' };\n const figId = getAttr(node, 'id');\n if (figId) figBlock.id = figId;\n const figLabel = findChild(innerChildren, 'label');\n if (figLabel) {\n const labelText = extractAllText(figLabel.children);\n if (labelText) figBlock.label = labelText;\n }\n const figCaption = findChild(innerChildren, 'caption');\n if (figCaption) {\n const captionText = extractAllText(figCaption.children);\n if (captionText) figBlock.caption = captionText;\n }\n return figBlock;\n}\n\n/**\n * Parse a <p> element, splitting it if it contains nested block elements\n * (table-wrap, fig, disp-quote). Returns one or more block elements.\n */\nfunction parseParagraph(pChildren: OrderedNode[]): BlockElement[] {\n // Check if <p> contains any nested block elements\n const hasNestedBlocks = pChildren.some((child) => {\n const tag = getTagName(child);\n return tag != null && BLOCK_TAGS.has(tag);\n });\n\n if (!hasNestedBlocks) {\n return [{ type: 'paragraph', content: parseInlineContent(pChildren) }];\n }\n\n // Split into inline runs and block elements\n const blocks: BlockElement[] = [];\n let inlineBuffer: OrderedNode[] = [];\n\n const flushInline = () => {\n if (inlineBuffer.length > 0) {\n const content = parseInlineContent(inlineBuffer);\n // Skip whitespace-only paragraphs created by XML formatting\n const hasNonWhitespace = content.some(\n (c) => c.type !== 'text' || c.text.trim() !== '',\n );\n if (content.length > 0 && hasNonWhitespace) {\n blocks.push({ type: 'paragraph', content });\n }\n inlineBuffer = [];\n }\n };\n\n for (const child of pChildren) {\n const tag = getTagName(child);\n if (tag === 'table-wrap') {\n flushInline();\n blocks.push(parseTableBlock(child));\n } else if (tag === 'fig') {\n flushInline();\n blocks.push(parseFigBlock(child));\n } else if (tag === 'disp-quote') {\n flushInline();\n blocks.push(parseDispQuote(child));\n } else if (tag === 'boxed-text') {\n flushInline();\n blocks.push(parseBoxedText(child));\n } else {\n inlineBuffer.push(child);\n }\n }\n flushInline();\n\n return blocks;\n}\n\n/**\n * Parse block-level content from a section's children.\n * Iterates in document order to preserve ordering of paragraphs, lists,\n * tables, figures, and blockquotes.\n */\nfunction parseBlockContent(sectionChildren: OrderedNode[]): BlockElement[] {\n const blocks: BlockElement[] = [];\n\n for (const child of sectionChildren) {\n const tag = getTagName(child);\n if (!tag) continue;\n\n if (tag === 'p') {\n blocks.push(...parseParagraph(getChildren(child)));\n } else if (tag === 'list') {\n blocks.push(parseList(child));\n } else if (tag === 'table-wrap') {\n blocks.push(parseTableBlock(child));\n } else if (tag === 'fig') {\n blocks.push(parseFigBlock(child));\n } else if (tag === 'disp-quote') {\n blocks.push(parseDispQuote(child));\n } else if (tag === 'boxed-text') {\n blocks.push(parseBoxedText(child));\n } else if (tag === 'def-list') {\n blocks.push(parseDefList(child));\n } else if (tag === 'disp-formula') {\n blocks.push(parseDispFormula(child));\n } else if (tag === 'preformat') {\n const text = extractAllText(getChildren(child));\n blocks.push({ type: 'preformat', text });\n } else if (tag === 'supplementary-material') {\n const innerChildren = getChildren(child);\n const labelNode = findChild(innerChildren, 'label');\n const captionNode = findChild(innerChildren, 'caption');\n const labelText = labelNode ? extractAllText(labelNode.children) : '';\n const captionText = captionNode ? extractAllText(captionNode.children) : '';\n const text = [labelText, captionText].filter(Boolean).join(': ');\n if (text) {\n blocks.push({ type: 'paragraph', content: [{ type: 'text', text }] });\n }\n }\n // Skip title, sec, and other non-block elements\n }\n\n return blocks;\n}\n\n// ─── Section Parsing ─────────────────────────────────────────────────\n\n/**\n * Parse a <sec> element into a JatsSection, recursively handling subsections.\n */\nfunction parseSection(secChildren: OrderedNode[], level: number): JatsSection {\n const titleNode = findChild(secChildren, 'title');\n const title = titleNode ? extractAllText(titleNode.children) : '';\n const content = parseBlockContent(secChildren);\n\n // Nested sections\n const subsections: JatsSection[] = [];\n const nestedSecs = findChildren(secChildren, 'sec');\n for (const sub of nestedSecs) {\n subsections.push(parseSection(sub.children, level + 1));\n }\n\n return { title, level, content, subsections };\n}\n\n/**\n * Parse JATS XML body to extract sections and content.\n */\nexport function parseJatsBody(xml: string): JatsSection[] {\n const parsed = parser.parse(xml) as OrderedNode[];\n const article = findArticle(parsed);\n if (!article) return [];\n\n const body = findChild(article.children, 'body');\n if (!body) return [];\n\n const sections: JatsSection[] = [];\n const secs = findChildren(body.children, 'sec');\n\n if (secs.length > 0) {\n for (const sec of secs) {\n sections.push(parseSection(sec.children, 2));\n }\n } else {\n // Body has paragraphs without sections\n const content = parseBlockContent(body.children);\n if (content.length > 0) {\n sections.push({ title: '', level: 2, content, subsections: [] });\n }\n }\n\n return sections;\n}\n\n// ─── Reference Parsing ───────────────────────────────────────────────\n\n/**\n * Format a structured <element-citation> into a readable reference string.\n * Produces: \"Author1, Author2. Title. Source. Year;Volume:FirstPage-LastPage.\"\n */\nfunction formatElementCitation(children: OrderedNode[]): string {\n const parts: string[] = [];\n\n // Authors from person-group\n const personGroup = findChild(children, 'person-group');\n if (personGroup) {\n const names = findChildren(personGroup.children, 'name');\n const authorParts: string[] = [];\n for (const name of names) {\n const surname = findChild(name.children, 'surname');\n const givenNames = findChild(name.children, 'given-names');\n const surnameText = surname ? extractAllText(surname.children) : '';\n const givenText = givenNames ? extractAllText(givenNames.children) : '';\n if (surnameText && givenText) {\n authorParts.push(`${surnameText} ${givenText}`);\n } else if (surnameText) {\n authorParts.push(surnameText);\n }\n }\n if (authorParts.length > 0) {\n parts.push(authorParts.join(', '));\n }\n }\n\n // Article title\n const articleTitle = findChild(children, 'article-title');\n if (articleTitle) {\n parts.push(extractAllText(articleTitle.children));\n }\n\n // Source (journal name)\n const source = findChild(children, 'source');\n if (source) {\n parts.push(extractAllText(source.children));\n }\n\n // Year, volume, pages\n const year = findChild(children, 'year');\n const volume = findChild(children, 'volume');\n const fpage = findChild(children, 'fpage');\n const lpage = findChild(children, 'lpage');\n\n if (year) {\n let yearStr = extractAllText(year.children);\n if (volume) {\n yearStr += `;${extractAllText(volume.children)}`;\n }\n if (fpage) {\n const fpageText = extractAllText(fpage.children);\n const lpageText = lpage ? extractAllText(lpage.children) : '';\n yearStr += `:${fpageText}${lpageText ? `-${lpageText}` : ''}`;\n }\n parts.push(yearStr);\n }\n\n return parts.join('. ') + '.';\n}\n\n/**\n * Extract text from a <mixed-citation>'s children, deduplicating any\n * <pub-id> content that also appears as inline text.\n *\n * Some publishers include the DOI/PMID both as a text node and inside\n * a <pub-id> element, causing duplication like \"10.1234/x 10.1234/x\".\n */\nfunction extractMixedCitationText(children: OrderedNode[]): string {\n // Collect pub-id values\n const pubIds = findChildren(children, 'pub-id');\n const pubIdValues = pubIds\n .map((p) => extractAllText(p.children).trim())\n .filter(Boolean);\n\n if (pubIdValues.length === 0) {\n return extractAllText(children).trim();\n }\n\n // Extract full text\n const fullText = extractAllText(children).trim();\n\n // For each pub-id value, if it appears more than once, remove extra occurrences\n let result = fullText;\n for (const val of pubIdValues) {\n // Escape regex special characters\n const escaped = val.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const matches = result.match(new RegExp(escaped, 'g'));\n if (matches && matches.length > 1) {\n // Remove the first occurrence (typically the inline text), keep the last (pub-id element)\n result = result.replace(val, '');\n // Clean up any leftover extra whitespace\n result = result.replace(/\\s{2,}/g, ' ').trim();\n }\n }\n\n return result;\n}\n\n/**\n * Extract structured pub-id values (DOI, PMID, PMCID) from children nodes.\n */\nfunction extractPubIds(children: OrderedNode[]): {\n doi?: string;\n pmid?: string;\n pmcid?: string;\n} {\n const pubIds = findChildren(children, 'pub-id');\n const result: { doi?: string; pmid?: string; pmcid?: string } = {};\n for (const p of pubIds) {\n const idType = p.attrs['pub-id-type'];\n const value = extractAllText(p.children).trim();\n if (!value) continue;\n if (idType === 'doi') result.doi = value;\n if (idType === 'pmid') result.pmid = value;\n if (idType === 'pmc' || idType === 'pmcid') {\n result.pmcid = value.replace(/^PMC/, '');\n }\n }\n return result;\n}\n\n/**\n * Strip extracted pub-id values from reference text to avoid duplication\n * when pub-ids are rendered separately as links.\n */\nfunction stripPubIdValues(\n text: string,\n pubIds: { doi?: string; pmid?: string; pmcid?: string },\n): string {\n let result = text;\n const values = [pubIds.doi, pubIds.pmid, pubIds.pmcid].filter(Boolean) as string[];\n for (const val of values) {\n // Strip common label prefixes (e.g. \"doi: \", \"PMID: \", \"DOI:\", \"pmid:\") followed by the value\n const escaped = val.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n result = result.replace(new RegExp(`(?:doi|PMID|pmid|PMC|pmc)[:\\\\s]*${escaped}`, 'gi'), '');\n // Also strip the bare value itself\n result = result.replace(new RegExp(escaped, 'g'), '');\n }\n // Also strip PMC-prefixed form of pmcid\n if (pubIds.pmcid) {\n const pmcFull = `PMC${pubIds.pmcid}`;\n const escaped = pmcFull.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n result = result.replace(new RegExp(`(?:pmc|pmcid)[:\\\\s]*${escaped}`, 'gi'), '');\n result = result.replace(new RegExp(escaped, 'g'), '');\n }\n // Clean up trailing/leading whitespace and extra spaces\n result = result.replace(/\\s{2,}/g, ' ').trim();\n // Clean up trailing period after stripped content (e.g. \"Title. .\" -> \"Title.\")\n result = result.replace(/\\.\\s*\\.$/, '.');\n return result;\n}\n\n/**\n * Parse JATS XML back matter to extract references.\n */\nexport function parseJatsReferences(xml: string): JatsReference[] {\n const parsed = parser.parse(xml) as OrderedNode[];\n const article = findArticle(parsed);\n if (!article) return [];\n\n const back = findChild(article.children, 'back');\n if (!back) return [];\n\n const refList = findChild(back.children, 'ref-list');\n if (!refList) return [];\n\n const refs = findChildren(refList.children, 'ref');\n const references: JatsReference[] = [];\n\n for (const ref of refs) {\n const id = ref.attrs['id'] ?? '';\n\n // Determine the search scope: if <citation-alternatives> exists, search within it;\n // otherwise search direct children of <ref>\n const citationAlternatives = findChild(ref.children, 'citation-alternatives');\n const searchChildren = citationAlternatives ? citationAlternatives.children : ref.children;\n\n // Try mixed-citation first (already formatted), then element-citation (structured)\n const mixedCitation = findChild(searchChildren, 'mixed-citation');\n if (mixedCitation) {\n const rawText = extractMixedCitationText(mixedCitation.children);\n const pubIds = extractPubIds(mixedCitation.children);\n const text = stripPubIdValues(rawText, pubIds);\n if (id && text) references.push({ id, text, ...pubIds });\n continue;\n }\n\n const elementCitation = findChild(searchChildren, 'element-citation');\n if (elementCitation) {\n const rawText = formatElementCitation(elementCitation.children);\n const pubIds = extractPubIds(elementCitation.children);\n const text = stripPubIdValues(rawText, pubIds);\n if (id && text) references.push({ id, text, ...pubIds });\n continue;\n }\n\n // Fallback: extract all text from ref, skipping <label>\n const childrenWithoutLabel = ref.children.filter((c) => !('label' in c));\n const text = extractAllText(childrenWithoutLabel).trim();\n if (id && text) {\n references.push({ id, text });\n }\n }\n\n return references;\n}\n\n// ─── Back Matter & Floats Parsing ────────────────────────────────────\n\n/** Result of parsing back matter and floats-group. */\nexport interface BackMatterResult {\n acknowledgments?: string;\n appendices?: JatsSection[];\n footnotes?: JatsFootnote[];\n floats?: BlockElement[];\n notes?: BackMatterNote[];\n}\n\n/**\n * Parse JATS XML back matter sections (ack, app-group, fn-group)\n * and top-level floats-group.\n */\nexport function parseJatsBackMatter(xml: string): BackMatterResult {\n const parsed = parser.parse(xml) as OrderedNode[];\n const article = findArticle(parsed);\n if (!article) return {};\n\n const result: BackMatterResult = {};\n\n // Parse <back> children\n const back = findChild(article.children, 'back');\n if (back) {\n // Acknowledgments: <ack>\n const ack = findChild(back.children, 'ack');\n if (ack) {\n const paragraphs = findChildren(ack.children, 'p');\n if (paragraphs.length > 0) {\n result.acknowledgments = paragraphs\n .map((p) => extractAllText(p.children))\n .join('\\n\\n');\n }\n }\n\n // Appendices: <app-group>/<app>\n const appGroup = findChild(back.children, 'app-group');\n if (appGroup) {\n const apps = findChildren(appGroup.children, 'app');\n if (apps.length > 0) {\n result.appendices = apps.map((app) => parseSection(app.children, 2));\n }\n }\n\n // Footnotes: <fn-group>/<fn>\n const fnGroup = findChild(back.children, 'fn-group');\n if (fnGroup) {\n const fns = findChildren(fnGroup.children, 'fn');\n if (fns.length > 0) {\n result.footnotes = fns.map((fn) => {\n const parts: string[] = [];\n // Include <title> if present\n const titleNode = findChild(fn.children, 'title');\n if (titleNode) {\n const titleText = extractAllText(titleNode.children).trim();\n if (titleText) parts.push(titleText);\n }\n // Extract text from each <p> separately and join with space\n const paragraphs = findChildren(fn.children, 'p');\n for (const p of paragraphs) {\n const pText = extractAllText(p.children).trim();\n if (pText) parts.push(pText);\n }\n return {\n id: fn.attrs['id'] ?? '',\n text: parts.join(' '),\n };\n });\n }\n }\n\n // Notes: <notes> (author contributions, funding, data availability, etc.)\n const notesElements = findChildren(back.children, 'notes');\n if (notesElements.length > 0) {\n const notes: BackMatterNote[] = [];\n for (const note of notesElements) {\n // Check if this <notes> contains <sec> or nested <notes> children (e.g. Declarations wrapper)\n const secs = findChildren(note.children, 'sec');\n const nestedNotes = findChildren(note.children, 'notes');\n const subItems = secs.length > 0 ? secs : nestedNotes;\n if (subItems.length > 0) {\n for (const sub of subItems) {\n const subTitleNode = findChild(sub.children, 'title');\n const subTitle = subTitleNode ? extractAllText(subTitleNode.children) : '';\n const subParagraphs = findChildren(sub.children, 'p');\n const subText = subParagraphs\n .map((p) => extractAllText(p.children))\n .join('\\n\\n');\n if (subTitle || subText) {\n notes.push({ title: subTitle, text: subText });\n }\n }\n } else {\n const titleNode = findChild(note.children, 'title');\n const title = titleNode ? extractAllText(titleNode.children) : '';\n const paragraphs = findChildren(note.children, 'p');\n const text = paragraphs\n .map((p) => extractAllText(p.children))\n .join('\\n\\n');\n if (title || text) {\n notes.push({ title, text });\n }\n }\n }\n if (notes.length > 0) {\n result.notes = notes;\n }\n }\n\n // Glossary: <glossary> (abbreviations, definitions)\n const glossaryElements = findChildren(back.children, 'glossary');\n for (const glossary of glossaryElements) {\n const titleNode = findChild(glossary.children, 'title');\n const title = titleNode ? extractAllText(titleNode.children) : 'Glossary';\n const defList = findChild(glossary.children, 'def-list');\n if (defList) {\n const defItems = findChildren(defList.children, 'def-item');\n const lines: string[] = [];\n for (const item of defItems) {\n const termNode = findChild(item.children, 'term');\n const defNode = findChild(item.children, 'def');\n const term = termNode ? extractAllText(termNode.children) : '';\n const definition = defNode ? extractAllText(defNode.children) : '';\n lines.push(`${term}: ${definition}`);\n }\n if (!result.notes) result.notes = [];\n result.notes.push({ title, text: lines.join('\\n') });\n }\n }\n }\n\n // Floats-group: <floats-group> (sibling of <body> and <back>)\n const floatsGroup = findChild(article.children, 'floats-group');\n if (floatsGroup) {\n const blocks: BlockElement[] = [];\n for (const child of floatsGroup.children) {\n const tag = getTagName(child);\n if (tag === 'fig') {\n blocks.push(parseFigBlock(child));\n } else if (tag === 'table-wrap') {\n blocks.push(parseTableBlock(child));\n }\n }\n if (blocks.length > 0) {\n result.floats = blocks;\n }\n }\n\n return result;\n}\n"],"names":["titleGroup","text"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-writer.d.ts","sourceRoot":"","sources":["../../../src/fulltext/convert/markdown-writer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,YAAY,EAMb,MAAM,YAAY,CAAC;AA6MpB;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CA6HvD"}
1
+ {"version":3,"file":"markdown-writer.d.ts","sourceRoot":"","sources":["../../../src/fulltext/convert/markdown-writer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,YAAY,EAMb,MAAM,YAAY,CAAC;AAiOpB;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAuIvD"}
@@ -53,6 +53,10 @@ function renderBlock(block) {
53
53
  if (block.headers.length > 0) {
54
54
  lines.push(`| ${block.headers.join(" | ")} |`);
55
55
  lines.push(`| ${block.headers.map(() => "---").join(" | ")} |`);
56
+ } else if (block.rows.length > 0) {
57
+ const colCount = block.rows[0].length;
58
+ lines.push(`| ${Array.from({ length: colCount }, () => "").join(" | ")} |`);
59
+ lines.push(`| ${Array.from({ length: colCount }, () => "---").join(" | ")} |`);
56
60
  }
57
61
  for (const row of block.rows) {
58
62
  lines.push(`| ${row.join(" | ")} |`);
@@ -123,11 +127,26 @@ function renderSection(section) {
123
127
  }
124
128
  return lines.join("\n");
125
129
  }
130
+ function formatRefPubIds(ref) {
131
+ const links = [];
132
+ if (ref.doi) {
133
+ links.push(`[doi:${ref.doi}](https://doi.org/${ref.doi})`);
134
+ }
135
+ if (ref.pmid) {
136
+ links.push(`[pmid:${ref.pmid}](https://pubmed.ncbi.nlm.nih.gov/${ref.pmid}/)`);
137
+ }
138
+ if (ref.pmcid) {
139
+ links.push(`[pmcid:PMC${ref.pmcid}](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC${ref.pmcid}/)`);
140
+ }
141
+ return links.join(" ");
142
+ }
126
143
  function renderReferences(references) {
127
144
  if (references.length === 0) return "";
128
145
  const lines = ["## References", ""];
129
146
  references.forEach((ref, i) => {
130
- lines.push(`${i + 1}. ${ref.text}`);
147
+ const pubIdLinks = formatRefPubIds(ref);
148
+ const line = pubIdLinks ? `${i + 1}. ${ref.text} ${pubIdLinks}` : `${i + 1}. ${ref.text}`;
149
+ lines.push(line);
131
150
  });
132
151
  lines.push("");
133
152
  return lines.join("\n");
@@ -221,6 +240,14 @@ function writeMarkdown(doc) {
221
240
  lines.push(doc.acknowledgments);
222
241
  lines.push("");
223
242
  }
243
+ if (doc.notes && doc.notes.length > 0) {
244
+ for (const note of doc.notes) {
245
+ lines.push(`## ${note.title}`);
246
+ lines.push("");
247
+ lines.push(note.text);
248
+ lines.push("");
249
+ }
250
+ }
224
251
  if (doc.references.length > 0) {
225
252
  lines.push(renderReferences(doc.references));
226
253
  }
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-writer.js","sources":["../../../src/fulltext/convert/markdown-writer.ts"],"sourcesContent":["/**\n * Markdown writer for JATS XML conversion.\n *\n * Converts the intermediate JatsDocument representation to Markdown text.\n */\n\nimport type {\n JatsDocument,\n JatsSection,\n JatsFootnote,\n BlockElement,\n InlineContent,\n JatsReference,\n} from './types.js';\n\n/**\n * Format an author's name in abbreviated form (e.g., \"Smith J\").\n */\nfunction formatAuthor(author: { surname: string; givenNames?: string }): string {\n if (!author.givenNames) return author.surname;\n const initials = author.givenNames\n .split(/[\\s.]+/)\n .filter(Boolean)\n .map((n) => n[0])\n .join('');\n return `${author.surname} ${initials}`;\n}\n\n/**\n * Render inline content to Markdown string.\n */\nfunction renderInline(content: InlineContent[]): string {\n return content\n .map((node) => {\n switch (node.type) {\n case 'text':\n return node.text;\n case 'bold':\n return `**${renderInline(node.children)}**`;\n case 'italic':\n return `*${renderInline(node.children)}*`;\n case 'superscript':\n return `^${node.text}^`;\n case 'subscript':\n return `~${node.text}~`;\n case 'citation':\n return node.text;\n case 'code':\n return `\\`${node.text}\\``;\n case 'inline-formula':\n return node.tex ? `$${node.tex}$` : node.text;\n case 'link': {\n const linkText = renderInline(node.children);\n if (linkText === node.url) return node.url;\n return `[${linkText}](${node.url})`;\n }\n }\n })\n .join('');\n}\n\n/**\n * Render a block element to Markdown lines.\n */\nfunction renderBlock(block: BlockElement): string {\n switch (block.type) {\n case 'paragraph':\n return renderInline(block.content);\n\n case 'blockquote': {\n const text = renderInline(block.content);\n return text\n .split('\\n')\n .map((line) => (line === '' ? '>' : `> ${line}`))\n .join('\\n');\n }\n\n case 'list': {\n return block.items\n .map((item, i) => {\n const prefix = block.ordered ? `${i + 1}. ` : '- ';\n return `${prefix}${renderInline(item)}`;\n })\n .join('\\n');\n }\n\n case 'table': {\n const lines: string[] = [];\n if (block.caption) {\n lines.push(`*${block.caption}*`);\n lines.push('');\n }\n if (block.headers.length > 0) {\n lines.push(`| ${block.headers.join(' | ')} |`);\n lines.push(`| ${block.headers.map(() => '---').join(' | ')} |`);\n }\n for (const row of block.rows) {\n lines.push(`| ${row.join(' | ')} |`);\n }\n return lines.join('\\n');\n }\n\n case 'figure': {\n const label = block.label ?? 'Figure';\n const altText = block.caption ? `${label}. ${block.caption}` : label;\n return `![${altText}]()`;\n }\n\n case 'preformat':\n return '```\\n' + block.text + '\\n```';\n\n case 'formula': {\n const lines: string[] = [];\n if (block.tex) {\n lines.push(`$$${block.tex}$$`);\n } else if (block.text) {\n lines.push('```');\n lines.push(block.text);\n lines.push('```');\n }\n if (block.label) {\n lines.push(block.label);\n }\n return lines.join('\\n');\n }\n\n case 'def-list': {\n const lines: string[] = [];\n if (block.title) {\n lines.push(`**${block.title}**`);\n lines.push('');\n }\n for (const item of block.items) {\n lines.push(`**${item.term}**: ${item.definition}`);\n }\n return lines.join('\\n');\n }\n\n case 'boxed-text': {\n const lines: string[] = [];\n if (block.title) {\n lines.push(`> **${block.title}**`);\n lines.push('>');\n }\n for (const inner of block.content) {\n const rendered = renderBlock(inner);\n rendered.split('\\n').forEach((line) => {\n lines.push(line === '' ? '>' : `> ${line}`);\n });\n }\n return lines.join('\\n');\n }\n }\n}\n\n/**\n * Render a section and its subsections to Markdown.\n */\nfunction renderSection(section: JatsSection): string {\n const lines: string[] = [];\n const heading = '#'.repeat(section.level);\n\n if (section.title.trim()) {\n lines.push(`${heading} ${section.title}`);\n lines.push('');\n }\n\n for (const block of section.content) {\n lines.push(renderBlock(block));\n lines.push('');\n }\n\n for (const sub of section.subsections) {\n lines.push(renderSection(sub));\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Render references section.\n */\nfunction renderReferences(references: JatsReference[]): string {\n if (references.length === 0) return '';\n const lines: string[] = ['## References', ''];\n references.forEach((ref, i) => {\n lines.push(`${i + 1}. ${ref.text}`);\n });\n lines.push('');\n return lines.join('\\n');\n}\n\n/**\n * Render footnotes section.\n */\nfunction renderFootnotes(footnotes: JatsFootnote[]): string {\n if (footnotes.length === 0) return '';\n const lines: string[] = ['## Footnotes', ''];\n footnotes.forEach((fn, i) => {\n lines.push(`${i + 1}. ${fn.text}`);\n });\n lines.push('');\n return lines.join('\\n');\n}\n\n/**\n * Render floats (figures and tables) section.\n */\nfunction renderFloats(floats: BlockElement[]): string {\n if (floats.length === 0) return '';\n const lines: string[] = ['## Figures and Tables', ''];\n for (const block of floats) {\n lines.push(renderBlock(block));\n lines.push('');\n }\n return lines.join('\\n');\n}\n\n/**\n * Convert a parsed JATS document to Markdown string.\n */\nexport function writeMarkdown(doc: JatsDocument): string {\n const lines: string[] = [];\n\n // Title\n lines.push(`# ${doc.metadata.title}`);\n lines.push('');\n\n // Authors\n if (doc.metadata.authors.length > 0) {\n const authorStr = doc.metadata.authors.map(formatAuthor).join(', ');\n lines.push(`**Authors**: ${authorStr}`);\n }\n\n // DOI\n if (doc.metadata.doi) {\n lines.push(`**DOI**: ${doc.metadata.doi}`);\n }\n\n // PMC\n if (doc.metadata.pmcid) {\n lines.push(`**PMC**: PMC${doc.metadata.pmcid}`);\n }\n\n // PMID\n if (doc.metadata.pmid) {\n lines.push(`**PMID**: ${doc.metadata.pmid}`);\n }\n\n // Journal\n if (doc.metadata.journal) {\n lines.push(`**Journal**: ${doc.metadata.journal}`);\n }\n\n // Published date\n if (doc.metadata.publicationDate) {\n const d = doc.metadata.publicationDate;\n let dateStr = d.year;\n if (d.month) {\n dateStr += `-${d.month.padStart(2, '0')}`;\n if (d.day) dateStr += `-${d.day.padStart(2, '0')}`;\n }\n lines.push(`**Published**: ${dateStr}`);\n }\n\n // Citation (volume/issue/pages)\n if (doc.metadata.volume || doc.metadata.issue || doc.metadata.pages) {\n const parts: string[] = [];\n if (doc.metadata.volume) parts.push(`Vol. ${doc.metadata.volume}`);\n if (doc.metadata.issue) parts.push(`(${doc.metadata.issue})`);\n if (doc.metadata.pages) parts.push(`pp. ${doc.metadata.pages}`);\n // Join: \"Vol. 10(2), pp. 100-110\" or similar\n let citation = '';\n if (doc.metadata.volume && doc.metadata.issue) {\n citation = `Vol. ${doc.metadata.volume}(${doc.metadata.issue})`;\n if (doc.metadata.pages) citation += `, pp. ${doc.metadata.pages}`;\n } else {\n citation = parts.join(', ');\n }\n lines.push(`**Citation**: ${citation}`);\n }\n\n // Article type\n if (doc.metadata.articleType) {\n lines.push(`**Article Type**: ${doc.metadata.articleType}`);\n }\n\n // Keywords\n if (doc.metadata.keywords && doc.metadata.keywords.length > 0) {\n lines.push(`**Keywords**: ${doc.metadata.keywords.join(', ')}`);\n }\n\n // License\n if (doc.metadata.license) {\n lines.push(`**License**: ${doc.metadata.license}`);\n }\n\n const hasMetaLines = doc.metadata.authors.length > 0 || doc.metadata.doi || doc.metadata.pmcid || doc.metadata.pmid || doc.metadata.journal || doc.metadata.publicationDate || doc.metadata.volume || doc.metadata.pages || (doc.metadata.keywords && doc.metadata.keywords.length > 0) || doc.metadata.articleType || doc.metadata.license;\n if (hasMetaLines) {\n lines.push('');\n }\n\n // Abstract\n if (doc.metadata.abstract) {\n lines.push('## Abstract');\n lines.push('');\n lines.push(doc.metadata.abstract);\n lines.push('');\n }\n\n // Sections\n for (const section of doc.sections) {\n lines.push(renderSection(section));\n }\n\n // Acknowledgments (before References)\n if (doc.acknowledgments) {\n lines.push('## Acknowledgments');\n lines.push('');\n lines.push(doc.acknowledgments);\n lines.push('');\n }\n\n // References\n if (doc.references.length > 0) {\n lines.push(renderReferences(doc.references));\n }\n\n // Appendices (after References)\n if (doc.appendices && doc.appendices.length > 0) {\n for (const appendix of doc.appendices) {\n lines.push(renderSection(appendix));\n }\n }\n\n // Footnotes\n if (doc.footnotes && doc.footnotes.length > 0) {\n lines.push(renderFootnotes(doc.footnotes));\n }\n\n // Floats (Figures and Tables from floats-group)\n if (doc.floats && doc.floats.length > 0) {\n lines.push(renderFloats(doc.floats));\n }\n\n return lines.join('\\n').trimEnd() + '\\n';\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"markdown-writer.js","sources":["../../../src/fulltext/convert/markdown-writer.ts"],"sourcesContent":["/**\n * Markdown writer for JATS XML conversion.\n *\n * Converts the intermediate JatsDocument representation to Markdown text.\n */\n\nimport type {\n JatsDocument,\n JatsSection,\n JatsFootnote,\n BlockElement,\n InlineContent,\n JatsReference,\n} from './types.js';\n\n/**\n * Format an author's name in abbreviated form (e.g., \"Smith J\").\n */\nfunction formatAuthor(author: { surname: string; givenNames?: string }): string {\n if (!author.givenNames) return author.surname;\n const initials = author.givenNames\n .split(/[\\s.]+/)\n .filter(Boolean)\n .map((n) => n[0])\n .join('');\n return `${author.surname} ${initials}`;\n}\n\n/**\n * Render inline content to Markdown string.\n */\nfunction renderInline(content: InlineContent[]): string {\n return content\n .map((node) => {\n switch (node.type) {\n case 'text':\n return node.text;\n case 'bold':\n return `**${renderInline(node.children)}**`;\n case 'italic':\n return `*${renderInline(node.children)}*`;\n case 'superscript':\n return `^${node.text}^`;\n case 'subscript':\n return `~${node.text}~`;\n case 'citation':\n return node.text;\n case 'code':\n return `\\`${node.text}\\``;\n case 'inline-formula':\n return node.tex ? `$${node.tex}$` : node.text;\n case 'link': {\n const linkText = renderInline(node.children);\n if (linkText === node.url) return node.url;\n return `[${linkText}](${node.url})`;\n }\n }\n })\n .join('');\n}\n\n/**\n * Render a block element to Markdown lines.\n */\nfunction renderBlock(block: BlockElement): string {\n switch (block.type) {\n case 'paragraph':\n return renderInline(block.content);\n\n case 'blockquote': {\n const text = renderInline(block.content);\n return text\n .split('\\n')\n .map((line) => (line === '' ? '>' : `> ${line}`))\n .join('\\n');\n }\n\n case 'list': {\n return block.items\n .map((item, i) => {\n const prefix = block.ordered ? `${i + 1}. ` : '- ';\n return `${prefix}${renderInline(item)}`;\n })\n .join('\\n');\n }\n\n case 'table': {\n const lines: string[] = [];\n if (block.caption) {\n lines.push(`*${block.caption}*`);\n lines.push('');\n }\n if (block.headers.length > 0) {\n lines.push(`| ${block.headers.join(' | ')} |`);\n lines.push(`| ${block.headers.map(() => '---').join(' | ')} |`);\n } else if (block.rows.length > 0) {\n const colCount = block.rows[0]!.length;\n lines.push(`| ${Array.from({ length: colCount }, () => '').join(' | ')} |`);\n lines.push(`| ${Array.from({ length: colCount }, () => '---').join(' | ')} |`);\n }\n for (const row of block.rows) {\n lines.push(`| ${row.join(' | ')} |`);\n }\n return lines.join('\\n');\n }\n\n case 'figure': {\n const label = block.label ?? 'Figure';\n const altText = block.caption ? `${label}. ${block.caption}` : label;\n return `![${altText}]()`;\n }\n\n case 'preformat':\n return '```\\n' + block.text + '\\n```';\n\n case 'formula': {\n const lines: string[] = [];\n if (block.tex) {\n lines.push(`$$${block.tex}$$`);\n } else if (block.text) {\n lines.push('```');\n lines.push(block.text);\n lines.push('```');\n }\n if (block.label) {\n lines.push(block.label);\n }\n return lines.join('\\n');\n }\n\n case 'def-list': {\n const lines: string[] = [];\n if (block.title) {\n lines.push(`**${block.title}**`);\n lines.push('');\n }\n for (const item of block.items) {\n lines.push(`**${item.term}**: ${item.definition}`);\n }\n return lines.join('\\n');\n }\n\n case 'boxed-text': {\n const lines: string[] = [];\n if (block.title) {\n lines.push(`> **${block.title}**`);\n lines.push('>');\n }\n for (const inner of block.content) {\n const rendered = renderBlock(inner);\n rendered.split('\\n').forEach((line) => {\n lines.push(line === '' ? '>' : `> ${line}`);\n });\n }\n return lines.join('\\n');\n }\n }\n}\n\n/**\n * Render a section and its subsections to Markdown.\n */\nfunction renderSection(section: JatsSection): string {\n const lines: string[] = [];\n const heading = '#'.repeat(section.level);\n\n if (section.title.trim()) {\n lines.push(`${heading} ${section.title}`);\n lines.push('');\n }\n\n for (const block of section.content) {\n lines.push(renderBlock(block));\n lines.push('');\n }\n\n for (const sub of section.subsections) {\n lines.push(renderSection(sub));\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Render references section.\n */\nfunction formatRefPubIds(ref: JatsReference): string {\n const links: string[] = [];\n if (ref.doi) {\n links.push(`[doi:${ref.doi}](https://doi.org/${ref.doi})`);\n }\n if (ref.pmid) {\n links.push(`[pmid:${ref.pmid}](https://pubmed.ncbi.nlm.nih.gov/${ref.pmid}/)`);\n }\n if (ref.pmcid) {\n links.push(`[pmcid:PMC${ref.pmcid}](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC${ref.pmcid}/)`);\n }\n return links.join(' ');\n}\n\nfunction renderReferences(references: JatsReference[]): string {\n if (references.length === 0) return '';\n const lines: string[] = ['## References', ''];\n references.forEach((ref, i) => {\n const pubIdLinks = formatRefPubIds(ref);\n const line = pubIdLinks ? `${i + 1}. ${ref.text} ${pubIdLinks}` : `${i + 1}. ${ref.text}`;\n lines.push(line);\n });\n lines.push('');\n return lines.join('\\n');\n}\n\n/**\n * Render footnotes section.\n */\nfunction renderFootnotes(footnotes: JatsFootnote[]): string {\n if (footnotes.length === 0) return '';\n const lines: string[] = ['## Footnotes', ''];\n footnotes.forEach((fn, i) => {\n lines.push(`${i + 1}. ${fn.text}`);\n });\n lines.push('');\n return lines.join('\\n');\n}\n\n/**\n * Render floats (figures and tables) section.\n */\nfunction renderFloats(floats: BlockElement[]): string {\n if (floats.length === 0) return '';\n const lines: string[] = ['## Figures and Tables', ''];\n for (const block of floats) {\n lines.push(renderBlock(block));\n lines.push('');\n }\n return lines.join('\\n');\n}\n\n/**\n * Convert a parsed JATS document to Markdown string.\n */\nexport function writeMarkdown(doc: JatsDocument): string {\n const lines: string[] = [];\n\n // Title\n lines.push(`# ${doc.metadata.title}`);\n lines.push('');\n\n // Authors\n if (doc.metadata.authors.length > 0) {\n const authorStr = doc.metadata.authors.map(formatAuthor).join(', ');\n lines.push(`**Authors**: ${authorStr}`);\n }\n\n // DOI\n if (doc.metadata.doi) {\n lines.push(`**DOI**: ${doc.metadata.doi}`);\n }\n\n // PMC\n if (doc.metadata.pmcid) {\n lines.push(`**PMC**: PMC${doc.metadata.pmcid}`);\n }\n\n // PMID\n if (doc.metadata.pmid) {\n lines.push(`**PMID**: ${doc.metadata.pmid}`);\n }\n\n // Journal\n if (doc.metadata.journal) {\n lines.push(`**Journal**: ${doc.metadata.journal}`);\n }\n\n // Published date\n if (doc.metadata.publicationDate) {\n const d = doc.metadata.publicationDate;\n let dateStr = d.year;\n if (d.month) {\n dateStr += `-${d.month.padStart(2, '0')}`;\n if (d.day) dateStr += `-${d.day.padStart(2, '0')}`;\n }\n lines.push(`**Published**: ${dateStr}`);\n }\n\n // Citation (volume/issue/pages)\n if (doc.metadata.volume || doc.metadata.issue || doc.metadata.pages) {\n const parts: string[] = [];\n if (doc.metadata.volume) parts.push(`Vol. ${doc.metadata.volume}`);\n if (doc.metadata.issue) parts.push(`(${doc.metadata.issue})`);\n if (doc.metadata.pages) parts.push(`pp. ${doc.metadata.pages}`);\n // Join: \"Vol. 10(2), pp. 100-110\" or similar\n let citation = '';\n if (doc.metadata.volume && doc.metadata.issue) {\n citation = `Vol. ${doc.metadata.volume}(${doc.metadata.issue})`;\n if (doc.metadata.pages) citation += `, pp. ${doc.metadata.pages}`;\n } else {\n citation = parts.join(', ');\n }\n lines.push(`**Citation**: ${citation}`);\n }\n\n // Article type\n if (doc.metadata.articleType) {\n lines.push(`**Article Type**: ${doc.metadata.articleType}`);\n }\n\n // Keywords\n if (doc.metadata.keywords && doc.metadata.keywords.length > 0) {\n lines.push(`**Keywords**: ${doc.metadata.keywords.join(', ')}`);\n }\n\n // License\n if (doc.metadata.license) {\n lines.push(`**License**: ${doc.metadata.license}`);\n }\n\n const hasMetaLines = doc.metadata.authors.length > 0 || doc.metadata.doi || doc.metadata.pmcid || doc.metadata.pmid || doc.metadata.journal || doc.metadata.publicationDate || doc.metadata.volume || doc.metadata.pages || (doc.metadata.keywords && doc.metadata.keywords.length > 0) || doc.metadata.articleType || doc.metadata.license;\n if (hasMetaLines) {\n lines.push('');\n }\n\n // Abstract\n if (doc.metadata.abstract) {\n lines.push('## Abstract');\n lines.push('');\n lines.push(doc.metadata.abstract);\n lines.push('');\n }\n\n // Sections\n for (const section of doc.sections) {\n lines.push(renderSection(section));\n }\n\n // Acknowledgments (before References)\n if (doc.acknowledgments) {\n lines.push('## Acknowledgments');\n lines.push('');\n lines.push(doc.acknowledgments);\n lines.push('');\n }\n\n // Notes (between Acknowledgments and References)\n if (doc.notes && doc.notes.length > 0) {\n for (const note of doc.notes) {\n lines.push(`## ${note.title}`);\n lines.push('');\n lines.push(note.text);\n lines.push('');\n }\n }\n\n // References\n if (doc.references.length > 0) {\n lines.push(renderReferences(doc.references));\n }\n\n // Appendices (after References)\n if (doc.appendices && doc.appendices.length > 0) {\n for (const appendix of doc.appendices) {\n lines.push(renderSection(appendix));\n }\n }\n\n // Footnotes\n if (doc.footnotes && doc.footnotes.length > 0) {\n lines.push(renderFootnotes(doc.footnotes));\n }\n\n // Floats (Figures and Tables from floats-group)\n if (doc.floats && doc.floats.length > 0) {\n lines.push(renderFloats(doc.floats));\n }\n\n return lines.join('\\n').trimEnd() + '\\n';\n}\n"],"names":[],"mappings":"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"}
@@ -113,12 +113,20 @@ export interface JatsSection {
113
113
  export interface JatsReference {
114
114
  id: string;
115
115
  text: string;
116
+ doi?: string;
117
+ pmid?: string;
118
+ pmcid?: string;
116
119
  }
117
120
  /** A footnote from back matter fn-group. */
118
121
  export interface JatsFootnote {
119
122
  id: string;
120
123
  text: string;
121
124
  }
125
+ /** A notes section from back matter (e.g., author contributions, funding). */
126
+ export interface BackMatterNote {
127
+ title: string;
128
+ text: string;
129
+ }
122
130
  /** Complete parsed JATS document. */
123
131
  export interface JatsDocument {
124
132
  metadata: JatsMetadata;
@@ -128,5 +136,6 @@ export interface JatsDocument {
128
136
  appendices?: JatsSection[];
129
137
  footnotes?: JatsFootnote[];
130
138
  floats?: BlockElement[];
139
+ notes?: BackMatterNote[];
131
140
  }
132
141
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/fulltext/convert/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/fulltext/convert/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ncukondo/search-hub",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "A CLI tool for systematic literature searching across multiple academic databases",
5
5
  "type": "module",
6
6
  "engines": {