@ncukondo/search-hub 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/fulltext/check.d.ts +4 -0
- package/dist/cli/commands/fulltext/check.d.ts.map +1 -1
- package/dist/cli/commands/fulltext/check.js +41 -4
- package/dist/cli/commands/fulltext/check.js.map +1 -1
- package/dist/cli/commands/fulltext/index.d.ts.map +1 -1
- package/dist/cli/commands/fulltext/index.js +10 -0
- package/dist/cli/commands/fulltext/index.js.map +1 -1
- package/dist/cli/commands/fulltext/verify-pmcid.d.ts +28 -0
- package/dist/cli/commands/fulltext/verify-pmcid.d.ts.map +1 -0
- package/dist/cli/commands/fulltext/verify-pmcid.js +51 -0
- package/dist/cli/commands/fulltext/verify-pmcid.js.map +1 -0
- package/dist/cli/commands/query/init.d.ts +0 -1
- package/dist/cli/commands/query/init.d.ts.map +1 -1
- package/dist/cli/commands/query/init.js +2 -3
- package/dist/cli/commands/query/init.js.map +1 -1
- package/dist/cli/commands/query/resolve.d.ts.map +1 -1
- package/dist/cli/commands/query/resolve.js +5 -2
- package/dist/cli/commands/query/resolve.js.map +1 -1
- package/dist/cli/commands/register.d.ts +4 -0
- package/dist/cli/commands/register.d.ts.map +1 -1
- package/dist/cli/commands/register.js +62 -13
- package/dist/cli/commands/register.js.map +1 -1
- package/dist/cli/commands/upgrade.d.ts +28 -0
- package/dist/cli/commands/upgrade.d.ts.map +1 -0
- package/dist/cli/commands/upgrade.js +96 -0
- package/dist/cli/commands/upgrade.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +12 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/suggestions/rules.js +1 -1
- package/dist/cli/suggestions/rules.js.map +1 -1
- package/dist/cli/utils/argv.d.ts +34 -0
- package/dist/cli/utils/argv.d.ts.map +1 -0
- package/dist/cli/utils/argv.js +63 -0
- package/dist/cli/utils/argv.js.map +1 -0
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/paths.d.ts +1 -1
- package/dist/config/paths.d.ts.map +1 -1
- package/dist/config/paths.js +6 -2
- package/dist/config/paths.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/integration/csl-json.d.ts +10 -0
- package/dist/integration/csl-json.d.ts.map +1 -1
- package/dist/integration/csl-json.js +12 -0
- package/dist/integration/csl-json.js.map +1 -1
- package/dist/integration/register.d.ts.map +1 -1
- package/dist/integration/register.js +1 -1
- package/dist/integration/register.js.map +1 -1
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/convert/jats-parser.js +25 -7
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/convert/jats-parser.js.map +1 -1
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/discovery/index.js +67 -25
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/discovery/index.js.map +1 -1
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/discovery/pmc.js +3 -3
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/discovery/pmc.js.map +1 -1
- package/dist/package.json.js +1 -1
- package/dist/upgrade/apply-binary.d.ts +37 -0
- package/dist/upgrade/apply-binary.d.ts.map +1 -0
- package/dist/upgrade/apply-binary.js +200 -0
- package/dist/upgrade/apply-binary.js.map +1 -0
- package/dist/upgrade/apply-npm.d.ts +23 -0
- package/dist/upgrade/apply-npm.d.ts.map +1 -0
- package/dist/upgrade/apply-npm.js +96 -0
- package/dist/upgrade/apply-npm.js.map +1 -0
- package/dist/upgrade/check.d.ts +21 -0
- package/dist/upgrade/check.d.ts.map +1 -0
- package/dist/upgrade/check.js +91 -0
- package/dist/upgrade/check.js.map +1 -0
- package/dist/upgrade/detect.d.ts +16 -0
- package/dist/upgrade/detect.d.ts.map +1 -0
- package/dist/upgrade/detect.js +65 -0
- package/dist/upgrade/detect.js.map +1 -0
- package/dist/upgrade/notifier.d.ts +26 -0
- package/dist/upgrade/notifier.d.ts.map +1 -0
- package/dist/upgrade/notifier.js +111 -0
- package/dist/upgrade/notifier.js.map +1 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jats-parser.js","sources":["../../../../../../node_modules/@ncukondo/academic-fulltext/dist/convert/jats-parser.js"],"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 */\nimport { XMLParser } from \"fast-xml-parser\";\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// ─── Navigation Helpers ──────────────────────────────────────────────\n/** Get the tag name of an ordered node (the first key that isn't \":@\" or \"#text\"). */\nfunction getTagName(node) {\n for (const key of Object.keys(node)) {\n if (key !== \":@\" && key !== \"#text\")\n return key;\n }\n return undefined;\n}\n/** Get the children array of an element node. */\nfunction getChildren(node) {\n const tag = getTagName(node);\n if (!tag)\n return [];\n const children = node[tag];\n return Array.isArray(children) ? children : [];\n}\n/** Get attributes of an element node. */\nfunction getAttr(node, attrName) {\n const attrs = node[\":@\"];\n if (!attrs)\n return undefined;\n const val = attrs[`@_${attrName}`];\n return val != null ? String(val) : undefined;\n}\n/** Get all attributes of an element node (strips @_ prefix for consistency with getAttr). */\nfunction getAttrs(node) {\n const attrs = node[\":@\"];\n if (!attrs)\n return {};\n const result = {};\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/** Find the first child element with the given tag name. */\nfunction findChild(children, tagName) {\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 : [],\n attrs: getAttrs(child),\n };\n }\n }\n return undefined;\n}\n/** Find all child elements with the given tag name. */\nfunction findChildren(children, tagName) {\n const results = [];\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 : [],\n attrs: getAttrs(child),\n });\n }\n }\n return results;\n}\n/** Get text content from a #text node. */\nfunction getTextContent(child) {\n if (\"#text\" in child) {\n const val = child[\"#text\"];\n return val != null ? String(val) : undefined;\n }\n return undefined;\n}\n/**\n * Find the <article> element, handling optional <pmc-articleset> wrapper\n * that appears in efetch responses.\n */\nfunction findArticle(parsed) {\n const direct = findChild(parsed, \"article\");\n if (direct)\n return direct;\n const wrapper = findChild(parsed, \"pmc-articleset\");\n if (wrapper)\n return findChild(wrapper.children, \"article\");\n return undefined;\n}\n// ─── Text Extraction ─────────────────────────────────────────────────\n/** Tags whose text content should be followed by a space when adjacent to other content. */\nconst SPACE_AFTER_TAGS = new Set([\"surname\", \"given-names\", \"name\", \"string-name\"]);\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) {\n if (node == null)\n return \"\";\n if (typeof node === \"string\")\n return node;\n if (typeof node === \"number\")\n return String(node);\n if (Array.isArray(node)) {\n return joinChildTexts(node);\n }\n if (typeof node === \"object\") {\n const obj = node;\n // Text node\n const text = getTextContent(obj);\n if (text != null)\n 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);\n }\n }\n }\n return \"\";\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) {\n const parts = [];\n for (const child of children) {\n const text = extractAllText(child);\n if (!text)\n continue;\n const tag = getTagName(child);\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.at(-1);\n if (prev && !/[\\s,;.:()\\-/]$/.test(prev)) {\n parts.push(\" \");\n }\n }\n parts.push(text);\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// ─── Metadata Parsing ────────────────────────────────────────────────\n/** Extract DOI, PMCID, and PMID from article-id elements. */\nfunction parseArticleIds(metaChildren) {\n const articleIds = findChildren(metaChildren, \"article-id\");\n const result = {};\n for (const idEntry of articleIds) {\n const idType = idEntry.attrs[\"pub-id-type\"];\n const idText = extractAllText(idEntry.children);\n if (idType === \"doi\")\n result.doi = idText;\n if (idType === \"pmc\" || idType === \"pmcid\") {\n result.pmcid = idText.replace(/^PMC/, \"\");\n }\n if (idType === \"pmid\")\n result.pmid = idText;\n }\n return result;\n}\n/** Extract authors from contrib-group. */\nfunction parseAuthors(metaChildren) {\n const authors = [];\n const contribGroup = findChild(metaChildren, \"contrib-group\");\n if (!contribGroup)\n return authors;\n const contribs = findChildren(contribGroup.children, \"contrib\");\n for (const contrib of contribs) {\n if (contrib.attrs[\"contrib-type\"] !== \"author\")\n continue;\n const nameNode = findChild(contrib.children, \"name\");\n if (!nameNode)\n continue;\n const surnameNode = findChild(nameNode.children, \"surname\");\n const givenNamesNode = findChild(nameNode.children, \"given-names\");\n const author = {\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 return authors;\n}\n/** Extract abstract text, handling both structured and simple formats. */\nfunction parseAbstract(metaChildren) {\n const abstractNode = findChild(metaChildren, \"abstract\");\n if (!abstractNode)\n return undefined;\n // Structured abstract with <sec> elements\n const sections = findChildren(abstractNode.children, \"sec\");\n if (sections.length > 0) {\n return parseStructuredAbstract(sections);\n }\n // Simple abstract with <p>\n const paragraphs = findChildren(abstractNode.children, \"p\");\n if (paragraphs.length > 0) {\n return paragraphs.map((p) => extractAllText(p.children)).join(\"\\n\\n\");\n }\n const text = extractAllText(abstractNode.children);\n return text || undefined;\n}\n/** Parse a structured abstract with <sec> elements. */\nfunction parseStructuredAbstract(sections) {\n const parts = [];\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 parts.push(secTitle ? `${secTitle}: ${text}` : text);\n }\n return parts.join(\"\\n\\n\");\n}\n/** Extract a date from a pub-date node. */\nfunction extractDateFromNode(pd) {\n const yearNode = findChild(pd.children, \"year\");\n if (!yearNode)\n return undefined;\n const year = extractAllText(yearNode.children);\n const monthNode = findChild(pd.children, \"month\");\n const dayNode = findChild(pd.children, \"day\");\n const date = { year };\n if (monthNode)\n date.month = extractAllText(monthNode.children);\n if (dayNode)\n date.day = extractAllText(dayNode.children);\n return date;\n}\n/**\n * Extract publication date with priority: epub > ppub > collection > any other.\n * Falls back to first available date if none match priority.\n */\nfunction parsePublicationDate(metaChildren) {\n const pubDates = findChildren(metaChildren, \"pub-date\");\n const datePriority = { epub: 0, ppub: 1, collection: 2 };\n let bestPriority = Number.POSITIVE_INFINITY;\n let publicationDate;\n for (const pd of pubDates) {\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 publicationDate = extractDateFromNode(pd);\n }\n }\n // If no prioritized date found, take first available\n if (!publicationDate && pubDates.length > 0) {\n const pd = pubDates.at(0);\n if (pd) {\n publicationDate = extractDateFromNode(pd);\n }\n }\n return publicationDate;\n}\n/** Extract license information from permissions. */\nfunction parseLicense(metaChildren) {\n const permissions = findChild(metaChildren, \"permissions\");\n if (!permissions)\n return undefined;\n const licenseNode = findChild(permissions.children, \"license\");\n if (!licenseNode)\n return undefined;\n // Prefer @xlink:href (standardized URL) over <license-p> (free-text)\n const href = licenseNode.attrs[\"xlink:href\"];\n if (href)\n return href;\n const licenseP = findChild(licenseNode.children, \"license-p\");\n if (licenseP)\n return extractAllText(licenseP.children).trim();\n return undefined;\n}\n/** Extract keywords from all kwd-group elements. */\nfunction parseKeywords(metaChildren) {\n const kwdGroups = findChildren(metaChildren, \"kwd-group\");\n const keywords = [];\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)\n keywords.push(text);\n }\n }\n return keywords;\n}\n/** Extract volume, issue, and pages from article meta. */\nfunction parseVolumeAndPages(metaChildren) {\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;\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 }\n else {\n const elocationNode = findChild(metaChildren, \"elocation-id\");\n if (elocationNode)\n pages = extractAllText(elocationNode.children);\n }\n const result = {};\n if (volume !== undefined)\n result.volume = volume;\n if (issue !== undefined)\n result.issue = issue;\n if (pages !== undefined)\n result.pages = pages;\n return result;\n}\n/** Extract journal name from front matter journal-meta. */\nfunction parseJournalName(frontChildren) {\n const journalMeta = findChild(frontChildren, \"journal-meta\");\n if (!journalMeta)\n return undefined;\n const titleGroup = findChild(journalMeta.children, \"journal-title-group\");\n if (titleGroup) {\n const jTitle = findChild(titleGroup.children, \"journal-title\");\n if (jTitle)\n return extractAllText(jTitle.children);\n }\n const jTitle = findChild(journalMeta.children, \"journal-title\");\n if (jTitle)\n return extractAllText(jTitle.children);\n return undefined;\n}\n/** Extract article title from title-group. */\nfunction parseArticleTitle(metaChildren) {\n const titleGroup = findChild(metaChildren, \"title-group\");\n const articleTitle = titleGroup ? findChild(titleGroup.children, \"article-title\") : undefined;\n return articleTitle ? extractAllText(articleTitle.children) : \"\";\n}\n/** Assemble optional metadata fields into a JatsMetadata result. */\nfunction assembleMetadata(base, fields) {\n const result = { ...base };\n if (fields.doi)\n result.doi = fields.doi;\n if (fields.pmcid)\n result.pmcid = fields.pmcid;\n if (fields.pmid)\n result.pmid = fields.pmid;\n if (fields.journal)\n result.journal = fields.journal;\n if (fields.publicationDate)\n result.publicationDate = fields.publicationDate;\n if (fields.volume)\n result.volume = fields.volume;\n if (fields.issue)\n result.issue = fields.issue;\n if (fields.pages)\n result.pages = fields.pages;\n if (fields.keywords.length > 0)\n result.keywords = fields.keywords;\n if (fields.articleType)\n result.articleType = fields.articleType;\n if (fields.license)\n result.license = fields.license;\n if (fields.abstract)\n result.abstract = fields.abstract;\n return result;\n}\n/**\n * Parse JATS XML front matter to extract article metadata.\n */\nexport function parseJatsMetadata(xml) {\n const parsed = parser.parse(xml);\n const article = findArticle(parsed);\n if (!article)\n return { title: \"\", authors: [] };\n const front = findChild(article.children, \"front\");\n if (!front)\n return { title: \"\", authors: [] };\n const articleMeta = findChild(front.children, \"article-meta\");\n if (!articleMeta)\n return { title: \"\", authors: [] };\n const metaChildren = articleMeta.children;\n const journal = parseJournalName(front.children);\n const publicationDate = parsePublicationDate(metaChildren);\n const articleType = article.attrs[\"article-type\"] || undefined;\n const license = parseLicense(metaChildren);\n const abstract = parseAbstract(metaChildren);\n return assembleMetadata({ title: parseArticleTitle(metaChildren), authors: parseAuthors(metaChildren) }, {\n ...parseArticleIds(metaChildren),\n ...(journal !== undefined ? { journal } : {}),\n ...(publicationDate !== undefined ? { publicationDate } : {}),\n ...parseVolumeAndPages(metaChildren),\n keywords: parseKeywords(metaChildren),\n ...(articleType !== undefined ? { articleType } : {}),\n ...(license !== undefined ? { license } : {}),\n ...(abstract !== undefined ? { abstract } : {}),\n });\n}\n// ─── Inline Content Parsing ──────────────────────────────────────────\n/** Handle <inline-formula> tag. */\nfunction handleInlineFormula(innerChildren) {\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 = {\n type: \"inline-formula\",\n text,\n };\n if (tex)\n entry.tex = tex;\n return entry;\n}\n/** Handle <ext-link> tag. */\nfunction handleExtLink(child, innerChildren) {\n const href = getAttr(child, \"xlink:href\");\n if (href) {\n return { type: \"link\", url: href, children: parseInlineContent(innerChildren) };\n }\n const linkText = extractAllText(innerChildren);\n if (linkText)\n return { type: \"text\", text: linkText };\n return null;\n}\n/** Handle <uri> tag. */\nfunction handleUri(child, innerChildren) {\n const href = getAttr(child, \"xlink:href\");\n const textContent = extractAllText(innerChildren);\n const url = href || textContent;\n if (url) {\n return { type: \"link\", url, children: parseInlineContent(innerChildren) };\n }\n return null;\n}\n/** Handle <xref> tag. */\nfunction handleXref(child, innerChildren) {\n const refType = getAttr(child, \"ref-type\");\n if (refType === \"bibr\") {\n return {\n type: \"citation\",\n refId: getAttr(child, \"rid\") ?? \"\",\n text: extractAllText(innerChildren),\n };\n }\n const xrefText = extractAllText(innerChildren);\n if (xrefText)\n return { type: \"text\", text: xrefText };\n return null;\n}\n/** Map of tag names to their inline content handlers. */\nconst inlineTagHandlers = {\n bold: (_child, innerChildren) => ({\n type: \"bold\",\n children: parseInlineContent(innerChildren),\n }),\n italic: (_child, innerChildren) => ({\n type: \"italic\",\n children: parseInlineContent(innerChildren),\n }),\n sup: (_child, innerChildren) => ({\n type: \"superscript\",\n text: extractAllText(innerChildren),\n }),\n sub: (_child, innerChildren) => ({\n type: \"subscript\",\n text: extractAllText(innerChildren),\n }),\n \"inline-formula\": (_child, innerChildren) => handleInlineFormula(innerChildren),\n monospace: (_child, innerChildren) => ({\n type: \"code\",\n text: extractAllText(innerChildren),\n }),\n \"ext-link\": (child, innerChildren) => handleExtLink(child, innerChildren),\n uri: (child, innerChildren) => handleUri(child, innerChildren),\n underline: (_child, innerChildren) => {\n const passText = extractAllText(innerChildren);\n return passText ? { type: \"text\", text: passText } : null;\n },\n sc: (_child, innerChildren) => {\n const passText = extractAllText(innerChildren);\n return passText ? { type: \"text\", text: passText } : null;\n },\n xref: (child, innerChildren) => handleXref(child, innerChildren),\n};\n/** Process a single inline child node into an InlineContent, or return null. */\nfunction processInlineChild(child) {\n // Text node\n const text = getTextContent(child);\n if (text != null) {\n return text ? { type: \"text\", text } : null;\n }\n const tag = getTagName(child);\n if (!tag)\n return null;\n const innerChildren = getChildren(child);\n const handler = inlineTagHandlers[tag];\n if (handler)\n return handler(child, innerChildren);\n // Unknown inline element -- extract text\n const unknownText = extractAllText(innerChildren);\n return unknownText ? { type: \"text\", text: unknownText } : null;\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) {\n const result = [];\n for (const child of children) {\n const content = processInlineChild(child);\n if (content)\n result.push(content);\n }\n return result;\n}\n// ─── Block Content Parsing ───────────────────────────────────────────\n/**\n * Parse a <list> element into a BlockElement.\n */\nfunction parseList(listNode) {\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 = [];\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 return { type: \"list\", ordered, items };\n}\n/**\n * Parse a table row into an array of cell text content.\n */\nfunction parseTableRow(trChildren) {\n const cells = [];\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(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 * Parse an already-parsed table-wrap node.\n */\nfunction parseTableWrap(tableWrapNode) {\n const children = getChildren(tableWrapNode);\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 const tableNode = findChild(children, \"table\");\n const result = {\n headers: [],\n rows: [],\n };\n if (captionStr)\n result.caption = captionStr;\n if (!tableNode)\n return result;\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 // 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 return result;\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) {\n const parsed = parser.parse(xml);\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 * Parse a <boxed-text> element into a boxed-text block.\n * Extracts optional title and recursively parses inner block content.\n */\nfunction parseBoxedText(node) {\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 = { type: \"boxed-text\", content };\n if (title)\n block.title = title;\n return block;\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) {\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 = [];\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 = { type: \"def-list\", items };\n if (title)\n block.title = title;\n return block;\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) {\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 // Try <alternatives> wrapper first\n const alternatives = findChild(children, \"alternatives\");\n const searchChildren = alternatives ? alternatives.children : children;\n const texMath = findChild(searchChildren, \"tex-math\");\n const block = { type: \"formula\" };\n if (id)\n block.id = id;\n if (label)\n block.label = label;\n if (texMath) {\n block.tex = extractAllText(texMath.children);\n }\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)\n block.text = text;\n }\n return block;\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 * Parse a <disp-quote> element into a blockquote block.\n * Extracts <p> children and concatenates their inline content.\n */\nfunction parseDispQuote(node) {\n const children = getChildren(node);\n const paragraphs = findChildren(children, \"p\");\n const content = [];\n for (let i = 0; i < paragraphs.length; i++) {\n if (i > 0)\n content.push({ type: \"text\", text: \"\\n\\n\" });\n const para = paragraphs[i];\n if (para)\n 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 * Parse a <table-wrap> node into a table block element.\n */\nfunction parseTableBlock(node) {\n const tableResult = parseTableWrap(node);\n const tableBlock = {\n type: \"table\",\n headers: tableResult.headers,\n rows: tableResult.rows,\n };\n if (tableResult.caption)\n tableBlock.caption = tableResult.caption;\n return tableBlock;\n}\n/**\n * Parse a <fig> node into a figure block element.\n */\nfunction parseFigBlock(node) {\n const innerChildren = getChildren(node);\n const figBlock = { type: \"figure\" };\n const figId = getAttr(node, \"id\");\n if (figId)\n figBlock.id = figId;\n const figLabel = findChild(innerChildren, \"label\");\n if (figLabel) {\n const labelText = extractAllText(figLabel.children);\n if (labelText)\n figBlock.label = labelText;\n }\n const figCaption = findChild(innerChildren, \"caption\");\n if (figCaption) {\n const captionText = extractAllText(figCaption.children);\n if (captionText)\n figBlock.caption = captionText;\n }\n return figBlock;\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) {\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 if (!hasNestedBlocks) {\n return [{ type: \"paragraph\", content: parseInlineContent(pChildren) }];\n }\n // Split into inline runs and block elements\n const blocks = [];\n let inlineBuffer = [];\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((c) => c.type !== \"text\" || c.text.trim() !== \"\");\n if (content.length > 0 && hasNonWhitespace) {\n blocks.push({ type: \"paragraph\", content });\n }\n inlineBuffer = [];\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 }\n else if (tag === \"fig\") {\n flushInline();\n blocks.push(parseFigBlock(child));\n }\n else if (tag === \"disp-quote\") {\n flushInline();\n blocks.push(parseDispQuote(child));\n }\n else if (tag === \"boxed-text\") {\n flushInline();\n blocks.push(parseBoxedText(child));\n }\n else {\n inlineBuffer.push(child);\n }\n }\n flushInline();\n return blocks;\n}\n/** Parse a <supplementary-material> element into a paragraph block. */\nfunction parseSupplementaryMaterial(child) {\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 return { type: \"paragraph\", content: [{ type: \"text\", text }] };\n }\n return null;\n}\n/** Map of tag names to their block content handlers. */\nconst blockTagHandlers = {\n p: (child) => parseParagraph(getChildren(child)),\n list: (child) => parseList(child),\n \"table-wrap\": (child) => parseTableBlock(child),\n fig: (child) => parseFigBlock(child),\n \"disp-quote\": (child) => parseDispQuote(child),\n \"boxed-text\": (child) => parseBoxedText(child),\n \"def-list\": (child) => parseDefList(child),\n \"disp-formula\": (child) => parseDispFormula(child),\n preformat: (child) => ({ type: \"preformat\", text: extractAllText(getChildren(child)) }),\n \"supplementary-material\": (child) => parseSupplementaryMaterial(child),\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) {\n const blocks = [];\n for (const child of sectionChildren) {\n const tag = getTagName(child);\n if (!tag)\n continue;\n const handler = blockTagHandlers[tag];\n if (!handler)\n continue;\n const result = handler(child);\n if (result == null)\n continue;\n if (Array.isArray(result)) {\n blocks.push(...result);\n }\n else {\n blocks.push(result);\n }\n }\n return blocks;\n}\n// ─── Section Parsing ─────────────────────────────────────────────────\n/**\n * Parse a <sec> element into a JatsSection, recursively handling subsections.\n */\nfunction parseSection(secChildren, level) {\n const titleNode = findChild(secChildren, \"title\");\n const title = titleNode ? extractAllText(titleNode.children) : \"\";\n const content = parseBlockContent(secChildren);\n // Nested sections\n const subsections = [];\n const nestedSecs = findChildren(secChildren, \"sec\");\n for (const sub of nestedSecs) {\n subsections.push(parseSection(sub.children, level + 1));\n }\n return { title, level, content, subsections };\n}\n/**\n * Parse JATS XML body to extract sections and content.\n */\nexport function parseJatsBody(xml) {\n const parsed = parser.parse(xml);\n const article = findArticle(parsed);\n if (!article)\n return [];\n const body = findChild(article.children, \"body\");\n if (!body)\n return [];\n const sections = [];\n const secs = findChildren(body.children, \"sec\");\n if (secs.length > 0) {\n for (const sec of secs) {\n sections.push(parseSection(sec.children, 2));\n }\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 return sections;\n}\n// ─── Reference Parsing ───────────────────────────────────────────────\n/** Extract authors from a <person-group> element. */\nfunction extractCitationAuthors(children) {\n const personGroup = findChild(children, \"person-group\");\n if (!personGroup)\n return undefined;\n const names = findChildren(personGroup.children, \"name\");\n const authorParts = [];\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 }\n else if (surnameText) {\n authorParts.push(surnameText);\n }\n }\n return authorParts.length > 0 ? authorParts.join(\", \") : undefined;\n}\n/** Format year, volume, and pages into a citation string part. */\nfunction formatYearVolumePage(children) {\n const year = findChild(children, \"year\");\n if (!year)\n return undefined;\n let yearStr = extractAllText(year.children);\n const volume = findChild(children, \"volume\");\n if (volume) {\n yearStr += `;${extractAllText(volume.children)}`;\n }\n const fpage = findChild(children, \"fpage\");\n if (fpage) {\n const fpageText = extractAllText(fpage.children);\n const lpage = findChild(children, \"lpage\");\n const lpageText = lpage ? extractAllText(lpage.children) : \"\";\n yearStr += `:${fpageText}${lpageText ? `-${lpageText}` : \"\"}`;\n }\n return yearStr;\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) {\n const parts = [];\n const authorsStr = extractCitationAuthors(children);\n if (authorsStr)\n parts.push(authorsStr);\n // Article title\n const articleTitle = findChild(children, \"article-title\");\n if (articleTitle) {\n parts.push(extractAllText(articleTitle.children));\n }\n // Source (journal name)\n const source = findChild(children, \"source\");\n if (source) {\n parts.push(extractAllText(source.children));\n }\n // Year, volume, pages\n const yearVolumePage = formatYearVolumePage(children);\n if (yearVolumePage)\n parts.push(yearVolumePage);\n return `${parts.join(\". \")}.`;\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) {\n // Collect pub-id values\n const pubIds = findChildren(children, \"pub-id\");\n const pubIdValues = pubIds.map((p) => extractAllText(p.children).trim()).filter(Boolean);\n if (pubIdValues.length === 0) {\n return extractAllText(children).trim();\n }\n // Extract full text\n const fullText = extractAllText(children).trim();\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 return result;\n}\n/**\n * Extract structured pub-id values (DOI, PMID, PMCID) from children nodes.\n */\nfunction extractPubIds(children) {\n const pubIds = findChildren(children, \"pub-id\");\n const result = {};\n for (const p of pubIds) {\n const idType = p.attrs[\"pub-id-type\"];\n const value = extractAllText(p.children).trim();\n if (!value)\n continue;\n if (idType === \"doi\")\n result.doi = value;\n if (idType === \"pmid\")\n result.pmid = value;\n if (idType === \"pmc\" || idType === \"pmcid\") {\n result.pmcid = value.replace(/^PMC/, \"\");\n }\n }\n return result;\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(text, pubIds) {\n let result = text;\n const values = [pubIds.doi, pubIds.pmid, pubIds.pmcid].filter(Boolean);\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/** Parse a single <ref> element into a JatsReference, or return null if invalid. */\nfunction parseSingleReference(ref) {\n const id = getAttr(ref.node, \"id\") ?? \"\";\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 // 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)\n return { id, text, ...pubIds };\n return null;\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)\n return { id, text, ...pubIds };\n return null;\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 return { id, text };\n return null;\n}\n/**\n * Parse JATS XML back matter to extract references.\n */\nexport function parseJatsReferences(xml) {\n const parsed = parser.parse(xml);\n const article = findArticle(parsed);\n if (!article)\n return [];\n const back = findChild(article.children, \"back\");\n if (!back)\n return [];\n const refList = findChild(back.children, \"ref-list\");\n if (!refList)\n return [];\n const refs = findChildren(refList.children, \"ref\");\n const references = [];\n for (const ref of refs) {\n const reference = parseSingleReference(ref);\n if (reference)\n references.push(reference);\n }\n return references;\n}\n/** Parse acknowledgments from <ack> element. */\nfunction parseAcknowledgments(backChildren) {\n const ack = findChild(backChildren, \"ack\");\n if (!ack)\n return undefined;\n const paragraphs = findChildren(ack.children, \"p\");\n if (paragraphs.length > 0) {\n return paragraphs.map((p) => extractAllText(p.children)).join(\"\\n\\n\");\n }\n return undefined;\n}\n/** Parse appendices from <app-group>/<app> elements. */\nfunction parseAppendices(backChildren) {\n const appGroup = findChild(backChildren, \"app-group\");\n if (!appGroup)\n return undefined;\n const apps = findChildren(appGroup.children, \"app\");\n if (apps.length === 0)\n return undefined;\n return apps.map((app) => parseSection(app.children, 2));\n}\n/** Parse a single footnote element into a JatsFootnote. */\nfunction parseSingleFootnote(fn) {\n const parts = [];\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)\n 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)\n parts.push(pText);\n }\n return {\n id: getAttr(fn.node, \"id\") ?? \"\",\n text: parts.join(\" \"),\n };\n}\n/** Parse footnotes from <fn-group>/<fn> elements. */\nfunction parseFootnotes(backChildren) {\n const fnGroup = findChild(backChildren, \"fn-group\");\n if (!fnGroup)\n return undefined;\n const fns = findChildren(fnGroup.children, \"fn\");\n if (fns.length === 0)\n return undefined;\n return fns.map(parseSingleFootnote);\n}\n/** Parse a single notes element into BackMatterNote entries. */\nfunction parseSingleNotesElement(note) {\n const notes = [];\n // Check if this <notes> contains <sec> or nested <notes> children\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.map((p) => extractAllText(p.children)).join(\"\\n\\n\");\n if (subTitle || subText) {\n notes.push({ title: subTitle, text: subText });\n }\n }\n return notes;\n }\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.map((p) => extractAllText(p.children)).join(\"\\n\\n\");\n if (title || text) {\n notes.push({ title, text });\n }\n return notes;\n}\n/** Parse notes from <notes> elements (author contributions, funding, etc.). */\nfunction parseNotes(backChildren) {\n const notesElements = findChildren(backChildren, \"notes\");\n if (notesElements.length === 0)\n return undefined;\n const notes = [];\n for (const note of notesElements) {\n notes.push(...parseSingleNotesElement(note));\n }\n return notes.length > 0 ? notes : undefined;\n}\n/** Parse glossary elements into BackMatterNote entries. */\nfunction parseGlossary(backChildren) {\n const glossaryElements = findChildren(backChildren, \"glossary\");\n const notes = [];\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 continue;\n const defItems = findChildren(defList.children, \"def-item\");\n const lines = [];\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 notes.push({ title, text: lines.join(\"\\n\") });\n }\n return notes;\n}\n/** Parse <floats-group> into block elements. */\nfunction parseFloatsGroup(articleChildren) {\n const floatsGroup = findChild(articleChildren, \"floats-group\");\n if (!floatsGroup)\n return undefined;\n const blocks = [];\n for (const child of floatsGroup.children) {\n const tag = getTagName(child);\n if (tag === \"fig\") {\n blocks.push(parseFigBlock(child));\n }\n else if (tag === \"table-wrap\") {\n blocks.push(parseTableBlock(child));\n }\n }\n return blocks.length > 0 ? blocks : undefined;\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) {\n const parsed = parser.parse(xml);\n const article = findArticle(parsed);\n if (!article)\n return {};\n const result = {};\n // Parse <back> children\n const back = findChild(article.children, \"back\");\n if (back) {\n const ack = parseAcknowledgments(back.children);\n if (ack)\n result.acknowledgments = ack;\n const app = parseAppendices(back.children);\n if (app)\n result.appendices = app;\n const fn = parseFootnotes(back.children);\n if (fn)\n result.footnotes = fn;\n const notes = parseNotes(back.children);\n const glossaryNotes = parseGlossary(back.children);\n if (notes || glossaryNotes.length > 0) {\n result.notes = [...(notes ?? []), ...glossaryNotes];\n }\n }\n // Floats-group: <floats-group> (sibling of <body> and <back>)\n const floats = parseFloatsGroup(article.children);\n if (floats)\n result.floats = floats;\n return result;\n}\n//# sourceMappingURL=jats-parser.js.map"],"names":["jTitle","text"],"mappings":"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","x_google_ignoreList":[0]}
|
|
1
|
+
{"version":3,"file":"jats-parser.js","sources":["../../../../../../node_modules/@ncukondo/academic-fulltext/dist/convert/jats-parser.js"],"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 */\nimport { XMLParser } from \"fast-xml-parser\";\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// ─── Navigation Helpers ──────────────────────────────────────────────\n/** Get the tag name of an ordered node (the first key that isn't \":@\" or \"#text\"). */\nfunction getTagName(node) {\n for (const key of Object.keys(node)) {\n if (key !== \":@\" && key !== \"#text\")\n return key;\n }\n return undefined;\n}\n/** Get the children array of an element node. */\nfunction getChildren(node) {\n const tag = getTagName(node);\n if (!tag)\n return [];\n const children = node[tag];\n return Array.isArray(children) ? children : [];\n}\n/** Get attributes of an element node. */\nfunction getAttr(node, attrName) {\n const attrs = node[\":@\"];\n if (!attrs)\n return undefined;\n const val = attrs[`@_${attrName}`];\n return val != null ? String(val) : undefined;\n}\n/** Get all attributes of an element node (strips @_ prefix for consistency with getAttr). */\nfunction getAttrs(node) {\n const attrs = node[\":@\"];\n if (!attrs)\n return {};\n const result = {};\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/** Find the first child element with the given tag name. */\nfunction findChild(children, tagName) {\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 : [],\n attrs: getAttrs(child),\n };\n }\n }\n return undefined;\n}\n/** Find all child elements with the given tag name. */\nfunction findChildren(children, tagName) {\n const results = [];\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 : [],\n attrs: getAttrs(child),\n });\n }\n }\n return results;\n}\n/** Get text content from a #text node. */\nfunction getTextContent(child) {\n if (\"#text\" in child) {\n const val = child[\"#text\"];\n return val != null ? String(val) : undefined;\n }\n return undefined;\n}\n/**\n * Find the <article> element, handling optional <pmc-articleset> wrapper\n * that appears in efetch responses.\n */\nfunction findArticle(parsed) {\n const direct = findChild(parsed, \"article\");\n if (direct)\n return direct;\n const wrapper = findChild(parsed, \"pmc-articleset\");\n if (wrapper)\n return findChild(wrapper.children, \"article\");\n return undefined;\n}\n// ─── Text Extraction ─────────────────────────────────────────────────\n/** Tags whose text content should be followed by a space when adjacent to other content. */\nconst SPACE_AFTER_TAGS = new Set([\"surname\", \"given-names\", \"name\", \"string-name\"]);\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) {\n if (node == null)\n return \"\";\n if (typeof node === \"string\")\n return node;\n if (typeof node === \"number\")\n return String(node);\n if (Array.isArray(node)) {\n return joinChildTexts(node);\n }\n if (typeof node === \"object\") {\n const obj = node;\n // Text node\n const text = getTextContent(obj);\n if (text != null)\n 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);\n }\n }\n }\n return \"\";\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) {\n const parts = [];\n for (const child of children) {\n const text = extractAllText(child);\n if (!text)\n continue;\n const tag = getTagName(child);\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.at(-1);\n if (prev && !/[\\s,;.:()\\-/]$/.test(prev)) {\n parts.push(\" \");\n }\n }\n parts.push(text);\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// ─── Metadata Parsing ────────────────────────────────────────────────\n/** Extract DOI, PMCID, and PMID from article-id elements. */\nfunction parseArticleIds(metaChildren) {\n const articleIds = findChildren(metaChildren, \"article-id\");\n const result = {};\n for (const idEntry of articleIds) {\n const idType = idEntry.attrs[\"pub-id-type\"];\n const idText = extractAllText(idEntry.children);\n if (idType === \"doi\")\n result.doi = idText;\n if (idType === \"pmc\" || idType === \"pmcid\") {\n result.pmcid = idText.replace(/^PMC/, \"\");\n }\n if (idType === \"pmid\")\n result.pmid = idText;\n }\n return result;\n}\n/** Extract authors from contrib-group. */\nfunction parseAuthors(metaChildren) {\n const authors = [];\n const contribGroup = findChild(metaChildren, \"contrib-group\");\n if (!contribGroup)\n return authors;\n const contribs = findChildren(contribGroup.children, \"contrib\");\n for (const contrib of contribs) {\n if (contrib.attrs[\"contrib-type\"] !== \"author\")\n continue;\n const nameNode = findChild(contrib.children, \"name\");\n if (!nameNode)\n continue;\n const surnameNode = findChild(nameNode.children, \"surname\");\n const givenNamesNode = findChild(nameNode.children, \"given-names\");\n const author = {\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 return authors;\n}\n/** Extract abstract text, handling both structured and simple formats. */\nfunction parseAbstract(metaChildren) {\n const abstractNode = findChild(metaChildren, \"abstract\");\n if (!abstractNode)\n return undefined;\n // Structured abstract with <sec> elements\n const sections = findChildren(abstractNode.children, \"sec\");\n if (sections.length > 0) {\n return parseStructuredAbstract(sections);\n }\n // Simple abstract with <p>\n const paragraphs = findChildren(abstractNode.children, \"p\");\n if (paragraphs.length > 0) {\n return paragraphs.map((p) => extractAllText(p.children)).join(\"\\n\\n\");\n }\n const text = extractAllText(abstractNode.children);\n return text || undefined;\n}\n/** Parse a structured abstract with <sec> elements. */\nfunction parseStructuredAbstract(sections) {\n const parts = [];\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 parts.push(secTitle ? `${secTitle}: ${text}` : text);\n }\n return parts.join(\"\\n\\n\");\n}\n/** Extract a date from a pub-date node. */\nfunction extractDateFromNode(pd) {\n const yearNode = findChild(pd.children, \"year\");\n if (!yearNode)\n return undefined;\n const year = extractAllText(yearNode.children);\n const monthNode = findChild(pd.children, \"month\");\n const dayNode = findChild(pd.children, \"day\");\n const date = { year };\n if (monthNode)\n date.month = extractAllText(monthNode.children);\n if (dayNode)\n date.day = extractAllText(dayNode.children);\n return date;\n}\n/**\n * Extract publication date with priority: epub > ppub > collection > any other.\n * Falls back to first available date if none match priority.\n */\nfunction parsePublicationDate(metaChildren) {\n const pubDates = findChildren(metaChildren, \"pub-date\");\n const datePriority = { epub: 0, ppub: 1, collection: 2 };\n let bestPriority = Number.POSITIVE_INFINITY;\n let publicationDate;\n for (const pd of pubDates) {\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 publicationDate = extractDateFromNode(pd);\n }\n }\n // If no prioritized date found, take first available\n if (!publicationDate && pubDates.length > 0) {\n const pd = pubDates.at(0);\n if (pd) {\n publicationDate = extractDateFromNode(pd);\n }\n }\n return publicationDate;\n}\n/** Extract license information from permissions. */\nfunction parseLicense(metaChildren) {\n const permissions = findChild(metaChildren, \"permissions\");\n if (!permissions)\n return undefined;\n const licenseNode = findChild(permissions.children, \"license\");\n if (!licenseNode)\n return undefined;\n // Prefer @xlink:href (standardized URL) over <license-p> (free-text)\n const href = licenseNode.attrs[\"xlink:href\"];\n if (href)\n return href;\n const licenseP = findChild(licenseNode.children, \"license-p\");\n if (licenseP)\n return extractAllText(licenseP.children).trim();\n return undefined;\n}\n/** Extract keywords from all kwd-group elements. */\nfunction parseKeywords(metaChildren) {\n const kwdGroups = findChildren(metaChildren, \"kwd-group\");\n const keywords = [];\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)\n keywords.push(text);\n }\n }\n return keywords;\n}\n/** Extract volume, issue, and pages from article meta. */\nfunction parseVolumeAndPages(metaChildren) {\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;\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 }\n else {\n const elocationNode = findChild(metaChildren, \"elocation-id\");\n if (elocationNode)\n pages = extractAllText(elocationNode.children);\n }\n const result = {};\n if (volume !== undefined)\n result.volume = volume;\n if (issue !== undefined)\n result.issue = issue;\n if (pages !== undefined)\n result.pages = pages;\n return result;\n}\n/** Extract journal name from front matter journal-meta. */\nfunction parseJournalName(frontChildren) {\n const journalMeta = findChild(frontChildren, \"journal-meta\");\n if (!journalMeta)\n return undefined;\n const titleGroup = findChild(journalMeta.children, \"journal-title-group\");\n if (titleGroup) {\n const jTitle = findChild(titleGroup.children, \"journal-title\");\n if (jTitle)\n return extractAllText(jTitle.children);\n }\n const jTitle = findChild(journalMeta.children, \"journal-title\");\n if (jTitle)\n return extractAllText(jTitle.children);\n return undefined;\n}\n/** Extract article title from title-group. */\nfunction parseArticleTitle(metaChildren) {\n const titleGroup = findChild(metaChildren, \"title-group\");\n const articleTitle = titleGroup ? findChild(titleGroup.children, \"article-title\") : undefined;\n return articleTitle ? extractAllText(articleTitle.children) : \"\";\n}\n/** Assemble optional metadata fields into a JatsMetadata result. */\nfunction assembleMetadata(base, fields) {\n const result = { ...base };\n if (fields.doi)\n result.doi = fields.doi;\n if (fields.pmcid)\n result.pmcid = fields.pmcid;\n if (fields.pmid)\n result.pmid = fields.pmid;\n if (fields.journal)\n result.journal = fields.journal;\n if (fields.publicationDate)\n result.publicationDate = fields.publicationDate;\n if (fields.volume)\n result.volume = fields.volume;\n if (fields.issue)\n result.issue = fields.issue;\n if (fields.pages)\n result.pages = fields.pages;\n if (fields.keywords.length > 0)\n result.keywords = fields.keywords;\n if (fields.articleType)\n result.articleType = fields.articleType;\n if (fields.license)\n result.license = fields.license;\n if (fields.abstract)\n result.abstract = fields.abstract;\n return result;\n}\n/**\n * Parse JATS XML front matter to extract article metadata.\n */\nexport function parseJatsMetadata(xml) {\n const parsed = parser.parse(xml);\n const article = findArticle(parsed);\n if (!article)\n return { title: \"\", authors: [] };\n const front = findChild(article.children, \"front\");\n if (!front)\n return { title: \"\", authors: [] };\n const articleMeta = findChild(front.children, \"article-meta\");\n if (!articleMeta)\n return { title: \"\", authors: [] };\n const metaChildren = articleMeta.children;\n const journal = parseJournalName(front.children);\n const publicationDate = parsePublicationDate(metaChildren);\n const articleType = article.attrs[\"article-type\"] || undefined;\n const license = parseLicense(metaChildren);\n const abstract = parseAbstract(metaChildren);\n return assembleMetadata({ title: parseArticleTitle(metaChildren), authors: parseAuthors(metaChildren) }, {\n ...parseArticleIds(metaChildren),\n ...(journal !== undefined ? { journal } : {}),\n ...(publicationDate !== undefined ? { publicationDate } : {}),\n ...parseVolumeAndPages(metaChildren),\n keywords: parseKeywords(metaChildren),\n ...(articleType !== undefined ? { articleType } : {}),\n ...(license !== undefined ? { license } : {}),\n ...(abstract !== undefined ? { abstract } : {}),\n });\n}\n// ─── Inline Content Parsing ──────────────────────────────────────────\n/** Handle <inline-formula> tag. */\nfunction handleInlineFormula(innerChildren) {\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 = {\n type: \"inline-formula\",\n text,\n };\n if (tex)\n entry.tex = tex;\n return entry;\n}\n/** Handle <ext-link> tag. */\nfunction handleExtLink(child, innerChildren) {\n const href = getAttr(child, \"xlink:href\");\n if (href) {\n return { type: \"link\", url: href, children: parseInlineContent(innerChildren) };\n }\n const linkText = extractAllText(innerChildren);\n if (linkText)\n return { type: \"text\", text: linkText };\n return null;\n}\n/** Handle <uri> tag. */\nfunction handleUri(child, innerChildren) {\n const href = getAttr(child, \"xlink:href\");\n const textContent = extractAllText(innerChildren);\n const url = href || textContent;\n if (url) {\n return { type: \"link\", url, children: parseInlineContent(innerChildren) };\n }\n return null;\n}\n/** Handle <xref> tag. */\nfunction handleXref(child, innerChildren) {\n const refType = getAttr(child, \"ref-type\");\n if (refType === \"bibr\") {\n return {\n type: \"citation\",\n refId: getAttr(child, \"rid\") ?? \"\",\n text: extractAllText(innerChildren),\n };\n }\n const xrefText = extractAllText(innerChildren);\n if (xrefText)\n return { type: \"text\", text: xrefText };\n return null;\n}\n/** Map of tag names to their inline content handlers. */\nconst inlineTagHandlers = {\n bold: (_child, innerChildren) => ({\n type: \"bold\",\n children: parseInlineContent(innerChildren),\n }),\n italic: (_child, innerChildren) => ({\n type: \"italic\",\n children: parseInlineContent(innerChildren),\n }),\n sup: (_child, innerChildren) => ({\n type: \"superscript\",\n text: extractAllText(innerChildren),\n }),\n sub: (_child, innerChildren) => ({\n type: \"subscript\",\n text: extractAllText(innerChildren),\n }),\n \"inline-formula\": (_child, innerChildren) => handleInlineFormula(innerChildren),\n monospace: (_child, innerChildren) => ({\n type: \"code\",\n text: extractAllText(innerChildren),\n }),\n \"ext-link\": (child, innerChildren) => handleExtLink(child, innerChildren),\n uri: (child, innerChildren) => handleUri(child, innerChildren),\n underline: (_child, innerChildren) => {\n const passText = extractAllText(innerChildren);\n return passText ? { type: \"text\", text: passText } : null;\n },\n sc: (_child, innerChildren) => {\n const passText = extractAllText(innerChildren);\n return passText ? { type: \"text\", text: passText } : null;\n },\n xref: (child, innerChildren) => handleXref(child, innerChildren),\n};\n/** Process a single inline child node into an InlineContent, or return null. */\nfunction processInlineChild(child) {\n // Text node\n const text = getTextContent(child);\n if (text != null) {\n return text ? { type: \"text\", text } : null;\n }\n const tag = getTagName(child);\n if (!tag)\n return null;\n const innerChildren = getChildren(child);\n const handler = inlineTagHandlers[tag];\n if (handler)\n return handler(child, innerChildren);\n // Unknown inline element -- extract text\n const unknownText = extractAllText(innerChildren);\n return unknownText ? { type: \"text\", text: unknownText } : null;\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) {\n const result = [];\n for (const child of children) {\n const content = processInlineChild(child);\n if (content)\n result.push(content);\n }\n return result;\n}\n// ─── Block Content Parsing ───────────────────────────────────────────\n/**\n * Parse a <list> element into a BlockElement.\n */\nfunction parseList(listNode) {\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 = [];\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 return { type: \"list\", ordered, items };\n}\n/**\n * Parse a table row into an array of cell text content.\n */\nfunction parseTableRow(trChildren) {\n const cells = [];\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(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 * Parse an already-parsed table-wrap node.\n */\nfunction parseTableWrap(tableWrapNode) {\n const children = getChildren(tableWrapNode);\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 const tableNode = findChild(children, \"table\");\n const result = {\n headers: [],\n rows: [],\n };\n if (captionStr)\n result.caption = captionStr;\n if (!tableNode)\n return result;\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 // 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 return result;\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) {\n const parsed = parser.parse(xml);\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 * Parse a <boxed-text> element into a boxed-text block.\n * Extracts optional title and recursively parses inner block content.\n */\nfunction parseBoxedText(node) {\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 = { type: \"boxed-text\", content };\n if (title)\n block.title = title;\n return block;\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) {\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 = [];\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 = { type: \"def-list\", items };\n if (title)\n block.title = title;\n return block;\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) {\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 // Try <alternatives> wrapper first\n const alternatives = findChild(children, \"alternatives\");\n const searchChildren = alternatives ? alternatives.children : children;\n const texMath = findChild(searchChildren, \"tex-math\");\n const block = { type: \"formula\" };\n if (id)\n block.id = id;\n if (label)\n block.label = label;\n if (texMath) {\n block.tex = extractAllText(texMath.children);\n }\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)\n block.text = text;\n }\n return block;\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 * Parse a <disp-quote> element into a blockquote block.\n * Extracts <p> children and concatenates their inline content.\n */\nfunction parseDispQuote(node) {\n const children = getChildren(node);\n const paragraphs = findChildren(children, \"p\");\n const content = [];\n for (let i = 0; i < paragraphs.length; i++) {\n if (i > 0)\n content.push({ type: \"text\", text: \"\\n\\n\" });\n const para = paragraphs[i];\n if (para)\n 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 * Parse a <table-wrap> node into a table block element.\n */\nfunction parseTableBlock(node) {\n const tableResult = parseTableWrap(node);\n const tableBlock = {\n type: \"table\",\n headers: tableResult.headers,\n rows: tableResult.rows,\n };\n if (tableResult.caption)\n tableBlock.caption = tableResult.caption;\n return tableBlock;\n}\n/**\n * Parse a <fig> node into a figure block element.\n */\nfunction parseFigBlock(node) {\n const innerChildren = getChildren(node);\n const figBlock = { type: \"figure\" };\n const figId = getAttr(node, \"id\");\n if (figId)\n figBlock.id = figId;\n const figLabel = findChild(innerChildren, \"label\");\n if (figLabel) {\n const labelText = extractAllText(figLabel.children);\n if (labelText)\n figBlock.label = labelText;\n }\n const figCaption = findChild(innerChildren, \"caption\");\n if (figCaption) {\n const captionText = extractAllText(figCaption.children);\n if (captionText)\n figBlock.caption = captionText;\n }\n return figBlock;\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) {\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 if (!hasNestedBlocks) {\n return [{ type: \"paragraph\", content: parseInlineContent(pChildren) }];\n }\n // Split into inline runs and block elements\n const blocks = [];\n let inlineBuffer = [];\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((c) => c.type !== \"text\" || c.text.trim() !== \"\");\n if (content.length > 0 && hasNonWhitespace) {\n blocks.push({ type: \"paragraph\", content });\n }\n inlineBuffer = [];\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 }\n else if (tag === \"fig\") {\n flushInline();\n blocks.push(parseFigBlock(child));\n }\n else if (tag === \"disp-quote\") {\n flushInline();\n blocks.push(parseDispQuote(child));\n }\n else if (tag === \"boxed-text\") {\n flushInline();\n blocks.push(parseBoxedText(child));\n }\n else {\n inlineBuffer.push(child);\n }\n }\n flushInline();\n return blocks;\n}\n/** Parse a <supplementary-material> element into a paragraph block. */\nfunction parseSupplementaryMaterial(child) {\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 return { type: \"paragraph\", content: [{ type: \"text\", text }] };\n }\n return null;\n}\n/** Map of tag names to their block content handlers. */\nconst blockTagHandlers = {\n p: (child) => parseParagraph(getChildren(child)),\n list: (child) => parseList(child),\n \"table-wrap\": (child) => parseTableBlock(child),\n fig: (child) => parseFigBlock(child),\n \"disp-quote\": (child) => parseDispQuote(child),\n \"boxed-text\": (child) => parseBoxedText(child),\n \"def-list\": (child) => parseDefList(child),\n \"disp-formula\": (child) => parseDispFormula(child),\n preformat: (child) => ({ type: \"preformat\", text: extractAllText(getChildren(child)) }),\n \"supplementary-material\": (child) => parseSupplementaryMaterial(child),\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) {\n const blocks = [];\n for (const child of sectionChildren) {\n const tag = getTagName(child);\n if (!tag)\n continue;\n const handler = blockTagHandlers[tag];\n if (!handler)\n continue;\n const result = handler(child);\n if (result == null)\n continue;\n if (Array.isArray(result)) {\n blocks.push(...result);\n }\n else {\n blocks.push(result);\n }\n }\n return blocks;\n}\n// ─── Section Parsing ─────────────────────────────────────────────────\n/**\n * Parse a <sec> element into a JatsSection, recursively handling subsections.\n */\nfunction parseSection(secChildren, level) {\n const titleNode = findChild(secChildren, \"title\");\n const title = titleNode ? extractAllText(titleNode.children) : \"\";\n const content = parseBlockContent(secChildren);\n // Nested sections\n const subsections = [];\n const nestedSecs = findChildren(secChildren, \"sec\");\n for (const sub of nestedSecs) {\n subsections.push(parseSection(sub.children, level + 1));\n }\n return { title, level, content, subsections };\n}\n/** Flush buffered block nodes as an untitled section if non-empty. */\nfunction flushBlockBuffer(buffer, sections) {\n const content = parseBlockContent(buffer);\n if (content.length > 0) {\n sections.push({ title: \"\", level: 2, content, subsections: [] });\n }\n}\n/** Parse mixed body children (interleaved <p> and <sec>) in document order. */\nfunction parseMixedBodyChildren(children, sections) {\n let blockBuffer = [];\n for (const child of children) {\n if (getTagName(child) === \"sec\") {\n if (blockBuffer.length > 0) {\n flushBlockBuffer(blockBuffer, sections);\n blockBuffer = [];\n }\n sections.push(parseSection(getChildren(child), 2));\n }\n else {\n blockBuffer.push(child);\n }\n }\n if (blockBuffer.length > 0) {\n flushBlockBuffer(blockBuffer, sections);\n }\n}\n/**\n * Parse JATS XML body to extract sections and content.\n */\nexport function parseJatsBody(xml) {\n const parsed = parser.parse(xml);\n const article = findArticle(parsed);\n if (!article)\n return [];\n const body = findChild(article.children, \"body\");\n if (!body)\n return [];\n const sections = [];\n const secs = findChildren(body.children, \"sec\");\n if (secs.length > 0) {\n parseMixedBodyChildren(body.children, sections);\n }\n else {\n flushBlockBuffer(body.children, sections);\n }\n return sections;\n}\n// ─── Reference Parsing ───────────────────────────────────────────────\n/** Extract authors from a <person-group> element. */\nfunction extractCitationAuthors(children) {\n const personGroup = findChild(children, \"person-group\");\n if (!personGroup)\n return undefined;\n const names = findChildren(personGroup.children, \"name\");\n const authorParts = [];\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 }\n else if (surnameText) {\n authorParts.push(surnameText);\n }\n }\n return authorParts.length > 0 ? authorParts.join(\", \") : undefined;\n}\n/** Format year, volume, and pages into a citation string part. */\nfunction formatYearVolumePage(children) {\n const year = findChild(children, \"year\");\n if (!year)\n return undefined;\n let yearStr = extractAllText(year.children);\n const volume = findChild(children, \"volume\");\n if (volume) {\n yearStr += `;${extractAllText(volume.children)}`;\n }\n const fpage = findChild(children, \"fpage\");\n if (fpage) {\n const fpageText = extractAllText(fpage.children);\n const lpage = findChild(children, \"lpage\");\n const lpageText = lpage ? extractAllText(lpage.children) : \"\";\n yearStr += `:${fpageText}${lpageText ? `-${lpageText}` : \"\"}`;\n }\n return yearStr;\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) {\n const parts = [];\n const authorsStr = extractCitationAuthors(children);\n if (authorsStr)\n parts.push(authorsStr);\n // Article title\n const articleTitle = findChild(children, \"article-title\");\n if (articleTitle) {\n parts.push(extractAllText(articleTitle.children));\n }\n // Source (journal name)\n const source = findChild(children, \"source\");\n if (source) {\n parts.push(extractAllText(source.children));\n }\n // Year, volume, pages\n const yearVolumePage = formatYearVolumePage(children);\n if (yearVolumePage)\n parts.push(yearVolumePage);\n return `${parts.join(\". \")}.`;\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) {\n // Collect pub-id values\n const pubIds = findChildren(children, \"pub-id\");\n const pubIdValues = pubIds.map((p) => extractAllText(p.children).trim()).filter(Boolean);\n if (pubIdValues.length === 0) {\n return extractAllText(children).trim();\n }\n // Extract full text\n const fullText = extractAllText(children).trim();\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 return result;\n}\n/**\n * Extract structured pub-id values (DOI, PMID, PMCID) from children nodes.\n */\nfunction extractPubIds(children) {\n const pubIds = findChildren(children, \"pub-id\");\n const result = {};\n for (const p of pubIds) {\n const idType = p.attrs[\"pub-id-type\"];\n const value = extractAllText(p.children).trim();\n if (!value)\n continue;\n if (idType === \"doi\")\n result.doi = value;\n if (idType === \"pmid\")\n result.pmid = value;\n if (idType === \"pmc\" || idType === \"pmcid\") {\n result.pmcid = value.replace(/^PMC/, \"\");\n }\n }\n return result;\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(text, pubIds) {\n let result = text;\n const values = [pubIds.doi, pubIds.pmid, pubIds.pmcid].filter(Boolean);\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/** Parse a single <ref> element into a JatsReference, or return null if invalid. */\nfunction parseSingleReference(ref) {\n const id = getAttr(ref.node, \"id\") ?? \"\";\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 // 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)\n return { id, text, ...pubIds };\n return null;\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)\n return { id, text, ...pubIds };\n return null;\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 return { id, text };\n return null;\n}\n/**\n * Parse JATS XML back matter to extract references.\n */\nexport function parseJatsReferences(xml) {\n const parsed = parser.parse(xml);\n const article = findArticle(parsed);\n if (!article)\n return [];\n const back = findChild(article.children, \"back\");\n if (!back)\n return [];\n const refList = findChild(back.children, \"ref-list\");\n if (!refList)\n return [];\n const refs = findChildren(refList.children, \"ref\");\n const references = [];\n for (const ref of refs) {\n const reference = parseSingleReference(ref);\n if (reference)\n references.push(reference);\n }\n return references;\n}\n/** Parse acknowledgments from <ack> element. */\nfunction parseAcknowledgments(backChildren) {\n const ack = findChild(backChildren, \"ack\");\n if (!ack)\n return undefined;\n const paragraphs = findChildren(ack.children, \"p\");\n if (paragraphs.length > 0) {\n return paragraphs.map((p) => extractAllText(p.children)).join(\"\\n\\n\");\n }\n return undefined;\n}\n/** Parse appendices from <app-group>/<app> elements. */\nfunction parseAppendices(backChildren) {\n const appGroup = findChild(backChildren, \"app-group\");\n if (!appGroup)\n return undefined;\n const apps = findChildren(appGroup.children, \"app\");\n if (apps.length === 0)\n return undefined;\n return apps.map((app) => parseSection(app.children, 2));\n}\n/** Parse a single footnote element into a JatsFootnote. */\nfunction parseSingleFootnote(fn) {\n const parts = [];\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)\n 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)\n parts.push(pText);\n }\n return {\n id: getAttr(fn.node, \"id\") ?? \"\",\n text: parts.join(\" \"),\n };\n}\n/** Parse footnotes from <fn-group>/<fn> elements. */\nfunction parseFootnotes(backChildren) {\n const fnGroup = findChild(backChildren, \"fn-group\");\n if (!fnGroup)\n return undefined;\n const fns = findChildren(fnGroup.children, \"fn\");\n if (fns.length === 0)\n return undefined;\n return fns.map(parseSingleFootnote);\n}\n/** Parse a single notes element into BackMatterNote entries. */\nfunction parseSingleNotesElement(note) {\n const notes = [];\n // Check if this <notes> contains <sec> or nested <notes> children\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.map((p) => extractAllText(p.children)).join(\"\\n\\n\");\n if (subTitle || subText) {\n notes.push({ title: subTitle, text: subText });\n }\n }\n return notes;\n }\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.map((p) => extractAllText(p.children)).join(\"\\n\\n\");\n if (title || text) {\n notes.push({ title, text });\n }\n return notes;\n}\n/** Parse notes from <notes> elements (author contributions, funding, etc.). */\nfunction parseNotes(backChildren) {\n const notesElements = findChildren(backChildren, \"notes\");\n if (notesElements.length === 0)\n return undefined;\n const notes = [];\n for (const note of notesElements) {\n notes.push(...parseSingleNotesElement(note));\n }\n return notes.length > 0 ? notes : undefined;\n}\n/** Parse glossary elements into BackMatterNote entries. */\nfunction parseGlossary(backChildren) {\n const glossaryElements = findChildren(backChildren, \"glossary\");\n const notes = [];\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 continue;\n const defItems = findChildren(defList.children, \"def-item\");\n const lines = [];\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 notes.push({ title, text: lines.join(\"\\n\") });\n }\n return notes;\n}\n/** Parse <floats-group> into block elements. */\nfunction parseFloatsGroup(articleChildren) {\n const floatsGroup = findChild(articleChildren, \"floats-group\");\n if (!floatsGroup)\n return undefined;\n const blocks = [];\n for (const child of floatsGroup.children) {\n const tag = getTagName(child);\n if (tag === \"fig\") {\n blocks.push(parseFigBlock(child));\n }\n else if (tag === \"table-wrap\") {\n blocks.push(parseTableBlock(child));\n }\n }\n return blocks.length > 0 ? blocks : undefined;\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) {\n const parsed = parser.parse(xml);\n const article = findArticle(parsed);\n if (!article)\n return {};\n const result = {};\n // Parse <back> children\n const back = findChild(article.children, \"back\");\n if (back) {\n const ack = parseAcknowledgments(back.children);\n if (ack)\n result.acknowledgments = ack;\n const app = parseAppendices(back.children);\n if (app)\n result.appendices = app;\n const fn = parseFootnotes(back.children);\n if (fn)\n result.footnotes = fn;\n const notes = parseNotes(back.children);\n const glossaryNotes = parseGlossary(back.children);\n if (notes || glossaryNotes.length > 0) {\n result.notes = [...(notes ?? []), ...glossaryNotes];\n }\n }\n // Floats-group: <floats-group> (sibling of <body> and <back>)\n const floats = parseFloatsGroup(article.children);\n if (floats)\n result.floats = floats;\n return result;\n}\n//# sourceMappingURL=jats-parser.js.map"],"names":["jTitle","text"],"mappings":"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","x_google_ignoreList":[0]}
|
|
@@ -4,28 +4,19 @@ import { resolveDoiToPmcid } from "./ncbi-id-converter.js";
|
|
|
4
4
|
import { checkPmc } from "./pmc.js";
|
|
5
5
|
import { checkUnpaywallDetailed } from "./unpaywall.js";
|
|
6
6
|
const DEFAULT_SOURCE_ORDER = ["pmc", "arxiv", "unpaywall", "core"];
|
|
7
|
-
async function checkPmcSource(article) {
|
|
8
|
-
if (!article.pmid && !article.pmcid)
|
|
9
|
-
return void 0;
|
|
10
|
-
const ids = {};
|
|
11
|
-
if (article.pmid)
|
|
12
|
-
ids.pmid = article.pmid;
|
|
13
|
-
if (article.pmcid)
|
|
14
|
-
ids.pmcid = article.pmcid;
|
|
15
|
-
return await checkPmc(ids);
|
|
16
|
-
}
|
|
17
7
|
function checkArxivSource(article) {
|
|
18
8
|
if (!article.arxivId)
|
|
19
|
-
return
|
|
9
|
+
return { skipped: "no arXiv ID available" };
|
|
20
10
|
return checkArxiv(article.arxivId);
|
|
21
11
|
}
|
|
22
12
|
async function checkCoreSource(article, config) {
|
|
23
|
-
if (!config.coreApiKey
|
|
24
|
-
return
|
|
13
|
+
if (!config.coreApiKey)
|
|
14
|
+
return { skipped: "coreApiKey not configured" };
|
|
15
|
+
if (!article.doi)
|
|
16
|
+
return { skipped: "no DOI available" };
|
|
25
17
|
return await checkCore(article.doi, config.coreApiKey);
|
|
26
18
|
}
|
|
27
19
|
const sourceCheckers = {
|
|
28
|
-
pmc: checkPmcSource,
|
|
29
20
|
arxiv: checkArxivSource,
|
|
30
21
|
core: checkCoreSource
|
|
31
22
|
};
|
|
@@ -66,9 +57,16 @@ async function enrichArticleIds(article, config) {
|
|
|
66
57
|
return { enriched: article, discoveredIds };
|
|
67
58
|
}
|
|
68
59
|
async function checkUnpaywallSource(enriched, config, state) {
|
|
69
|
-
if (!config.unpaywallEmail
|
|
60
|
+
if (!config.unpaywallEmail) {
|
|
61
|
+
state.skipped.push({ source: "unpaywall", reason: "unpaywallEmail not configured" });
|
|
70
62
|
return;
|
|
63
|
+
}
|
|
64
|
+
if (!enriched.doi) {
|
|
65
|
+
state.skipped.push({ source: "unpaywall", reason: "no DOI available" });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
71
68
|
state.sourcesChecked++;
|
|
69
|
+
state.checkedSources.push("unpaywall");
|
|
72
70
|
try {
|
|
73
71
|
const detailed = await checkUnpaywallDetailed(enriched.doi, config.unpaywallEmail);
|
|
74
72
|
if (!detailed)
|
|
@@ -81,14 +79,41 @@ async function checkUnpaywallSource(enriched, config, state) {
|
|
|
81
79
|
state.errors.push({ source: "unpaywall", error: String(err) });
|
|
82
80
|
}
|
|
83
81
|
}
|
|
82
|
+
async function checkPmcSourceWithIds(enriched, state, discoveredIds) {
|
|
83
|
+
if (!enriched.pmid && !enriched.pmcid) {
|
|
84
|
+
state.skipped.push({ source: "pmc", reason: "no PMCID or PMID available" });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const ids = {};
|
|
88
|
+
if (enriched.pmid)
|
|
89
|
+
ids.pmid = enriched.pmid;
|
|
90
|
+
if (enriched.pmcid)
|
|
91
|
+
ids.pmcid = enriched.pmcid;
|
|
92
|
+
state.sourcesChecked++;
|
|
93
|
+
state.checkedSources.push("pmc");
|
|
94
|
+
try {
|
|
95
|
+
const result = await checkPmc(ids);
|
|
96
|
+
if (!result)
|
|
97
|
+
return;
|
|
98
|
+
state.locations.push(...result.locations);
|
|
99
|
+
if (result.discoveredPmcid) {
|
|
100
|
+
discoveredIds.pmcid = discoveredIds.pmcid ?? result.discoveredPmcid;
|
|
101
|
+
}
|
|
102
|
+
} catch (err) {
|
|
103
|
+
state.errors.push({ source: "pmc", error: String(err) });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
84
106
|
async function checkGenericSource(source, enriched, config, state) {
|
|
85
107
|
const checker = sourceCheckers[source];
|
|
86
108
|
if (!checker)
|
|
87
109
|
return;
|
|
88
110
|
const result = await runSourceChecker(checker, enriched, config);
|
|
89
|
-
if (result.
|
|
111
|
+
if (result.skipReason) {
|
|
112
|
+
state.skipped.push({ source, reason: result.skipReason });
|
|
90
113
|
return;
|
|
114
|
+
}
|
|
91
115
|
state.sourcesChecked++;
|
|
116
|
+
state.checkedSources.push(source);
|
|
92
117
|
if (result.error) {
|
|
93
118
|
state.errors.push({ source, error: result.error });
|
|
94
119
|
} else if (result.locations) {
|
|
@@ -99,10 +124,11 @@ async function lazyPmcCheck(enriched, state, discoveredIds) {
|
|
|
99
124
|
if (!state.unpaywallPmcid || enriched.pmcid)
|
|
100
125
|
return;
|
|
101
126
|
discoveredIds.pmcid = discoveredIds.pmcid ?? state.unpaywallPmcid;
|
|
127
|
+
state.checkedSources.push("pmc-lazy");
|
|
102
128
|
try {
|
|
103
|
-
const
|
|
104
|
-
if (
|
|
105
|
-
state.locations.push(...
|
|
129
|
+
const pmcResult = await checkPmc({ pmcid: state.unpaywallPmcid });
|
|
130
|
+
if (pmcResult) {
|
|
131
|
+
state.locations.push(...pmcResult.locations);
|
|
106
132
|
}
|
|
107
133
|
} catch (err) {
|
|
108
134
|
state.errors.push({ source: "pmc-lazy", error: String(err) });
|
|
@@ -110,27 +136,43 @@ async function lazyPmcCheck(enriched, state, discoveredIds) {
|
|
|
110
136
|
}
|
|
111
137
|
async function discoverOA(article, config) {
|
|
112
138
|
const { enriched, discoveredIds } = await enrichArticleIds(article, config);
|
|
113
|
-
const state = {
|
|
139
|
+
const state = {
|
|
140
|
+
locations: [],
|
|
141
|
+
errors: [],
|
|
142
|
+
skipped: [],
|
|
143
|
+
checkedSources: [],
|
|
144
|
+
sourcesChecked: 0
|
|
145
|
+
};
|
|
114
146
|
const sourceOrder = config.preferSources.length > 0 ? config.preferSources : DEFAULT_SOURCE_ORDER;
|
|
115
147
|
for (const source of sourceOrder) {
|
|
116
148
|
if (source === "unpaywall") {
|
|
117
149
|
await checkUnpaywallSource(enriched, config, state);
|
|
150
|
+
} else if (source === "pmc") {
|
|
151
|
+
await checkPmcSourceWithIds(enriched, state, discoveredIds);
|
|
118
152
|
} else {
|
|
119
153
|
await checkGenericSource(source, enriched, config, state);
|
|
120
154
|
}
|
|
121
155
|
}
|
|
122
156
|
await lazyPmcCheck(enriched, state, discoveredIds);
|
|
123
157
|
const oaStatus = determineOAStatus(state.locations, state.errors, state.sourcesChecked);
|
|
124
|
-
return {
|
|
158
|
+
return {
|
|
159
|
+
oaStatus,
|
|
160
|
+
locations: state.locations,
|
|
161
|
+
errors: state.errors,
|
|
162
|
+
skipped: state.skipped,
|
|
163
|
+
checkedSources: state.checkedSources,
|
|
164
|
+
discoveredIds
|
|
165
|
+
};
|
|
125
166
|
}
|
|
126
167
|
async function runSourceChecker(checker, article, config) {
|
|
127
168
|
try {
|
|
128
169
|
const result = await checker(article, config);
|
|
129
|
-
if (result ===
|
|
130
|
-
return {
|
|
131
|
-
|
|
170
|
+
if (result !== null && typeof result === "object" && "skipped" in result) {
|
|
171
|
+
return { skipReason: result.skipped };
|
|
172
|
+
}
|
|
173
|
+
return { locations: result ?? [] };
|
|
132
174
|
} catch (err) {
|
|
133
|
-
return {
|
|
175
|
+
return { error: String(err) };
|
|
134
176
|
}
|
|
135
177
|
}
|
|
136
178
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../../../node_modules/@ncukondo/academic-fulltext/dist/discovery/index.js"],"sourcesContent":["/**\n * OA Discovery Aggregator.\n * Combines results from multiple OA discovery sources.\n */\nimport { checkArxiv } from \"./arxiv.js\";\nimport { checkCore } from \"./core.js\";\nimport { resolveDoiToPmcid } from \"./ncbi-id-converter.js\";\nimport { checkPmc } from \"./pmc.js\";\nimport { checkUnpaywallDetailed } from \"./unpaywall.js\";\n/** Default source order when preferSources is empty or not specified */\nconst DEFAULT_SOURCE_ORDER = [\"pmc\", \"arxiv\", \"unpaywall\", \"core\"];\nasync function checkPmcSource(article) {\n if (!article.pmid && !article.pmcid)\n return undefined;\n const ids = {};\n if (article.pmid)\n ids.pmid = article.pmid;\n if (article.pmcid)\n ids.pmcid = article.pmcid;\n return await checkPmc(ids);\n}\nfunction checkArxivSource(article) {\n if (!article.arxivId)\n return undefined;\n return checkArxiv(article.arxivId);\n}\nasync function checkCoreSource(article, config) {\n if (!config.coreApiKey || !article.doi)\n return undefined;\n return await checkCore(article.doi, config.coreApiKey);\n}\n/** Map of source name to its checker function (excluding unpaywall, handled specially). */\nconst sourceCheckers = {\n pmc: checkPmcSource,\n arxiv: checkArxivSource,\n core: checkCoreSource,\n};\n/** Determine OA status from collected locations and errors. */\nfunction determineOAStatus(locations, errors, sourcesChecked) {\n if (locations.length > 0)\n return \"open\";\n if (errors.length > 0 && errors.length >= sourcesChecked)\n return \"unknown\";\n return \"closed\";\n}\n/**\n * Pre-enrich article with PMCID from NCBI ID Converter when only DOI is available.\n */\nasync function enrichArticleIds(article, config) {\n const discoveredIds = {};\n // Only enrich if we have a DOI but no pmcid/pmid\n if (!article.doi || article.pmcid || article.pmid) {\n return { enriched: article, discoveredIds };\n }\n try {\n const email = config.ncbiEmail || config.unpaywallEmail;\n const options = {};\n if (config.ncbiTool)\n options.tool = config.ncbiTool;\n if (email)\n options.email = email;\n const result = await resolveDoiToPmcid(article.doi, options);\n if (result) {\n const enriched = { ...article };\n if (result.pmcid) {\n enriched.pmcid = result.pmcid;\n discoveredIds.pmcid = result.pmcid;\n }\n if (result.pmid) {\n enriched.pmid = result.pmid;\n discoveredIds.pmid = result.pmid;\n }\n return { enriched, discoveredIds };\n }\n }\n catch {\n // NCBI ID Converter failure is non-fatal; continue without enrichment\n }\n return { enriched: article, discoveredIds };\n}\n/** Check Unpaywall with PMCID extraction. */\nasync function checkUnpaywallSource(enriched, config, state) {\n if (!config.unpaywallEmail || !enriched.doi)\n return;\n state.sourcesChecked++;\n try {\n const detailed = await checkUnpaywallDetailed(enriched.doi, config.unpaywallEmail);\n if (!detailed)\n return;\n state.locations.push(...detailed.locations);\n if (detailed.pmcid) {\n state.unpaywallPmcid = detailed.pmcid;\n }\n }\n catch (err) {\n state.errors.push({ source: \"unpaywall\", error: String(err) });\n }\n}\n/** Check a single non-unpaywall source, updating state. */\nasync function checkGenericSource(source, enriched, config, state) {\n const checker = sourceCheckers[source];\n if (!checker)\n return;\n const result = await runSourceChecker(checker, enriched, config);\n if (result.skipped)\n return;\n state.sourcesChecked++;\n if (result.error) {\n state.errors.push({ source, error: result.error });\n }\n else if (result.locations) {\n state.locations.push(...result.locations);\n }\n}\n/** Perform lazy PMC check if Unpaywall revealed a PMCID not already known. */\nasync function lazyPmcCheck(enriched, state, discoveredIds) {\n if (!state.unpaywallPmcid || enriched.pmcid)\n return;\n discoveredIds.pmcid = discoveredIds.pmcid ?? state.unpaywallPmcid;\n try {\n const pmcLocations = await checkPmc({ pmcid: state.unpaywallPmcid });\n if (pmcLocations) {\n state.locations.push(...pmcLocations);\n }\n }\n catch (err) {\n state.errors.push({ source: \"pmc-lazy\", error: String(err) });\n }\n}\n/**\n * Discover OA availability for an article across all configured sources.\n * Checks sources in the order specified by config.preferSources, falling back\n * to the default order. Individual source errors are caught and reported\n * without failing the whole discovery.\n *\n * When only a DOI is provided, pre-enriches with NCBI ID Converter to resolve\n * PMCID/PMID, enabling PMC discovery. Also extracts PMCID from Unpaywall URLs\n * as a fallback, performing a lazy PMC check if a PMCID is discovered.\n */\nexport async function discoverOA(article, config) {\n const { enriched, discoveredIds } = await enrichArticleIds(article, config);\n const state = { locations: [], errors: [], sourcesChecked: 0 };\n const sourceOrder = config.preferSources.length > 0 ? config.preferSources : DEFAULT_SOURCE_ORDER;\n for (const source of sourceOrder) {\n if (source === \"unpaywall\") {\n await checkUnpaywallSource(enriched, config, state);\n }\n else {\n await checkGenericSource(source, enriched, config, state);\n }\n }\n await lazyPmcCheck(enriched, state, discoveredIds);\n const oaStatus = determineOAStatus(state.locations, state.errors, state.sourcesChecked);\n return { oaStatus, locations: state.locations, errors: state.errors, discoveredIds };\n}\n/** Run a single source checker with error handling. */\nasync function runSourceChecker(checker, article, config) {\n try {\n const result = await checker(article, config);\n if (result === undefined)\n return { skipped: true };\n return { skipped: false, locations: result ?? [] };\n }\n catch (err) {\n return { skipped: false, error: String(err) };\n }\n}\n//# sourceMappingURL=index.js.map"],"names":[],"mappings":"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","x_google_ignoreList":[0]}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../../../node_modules/@ncukondo/academic-fulltext/dist/discovery/index.js"],"sourcesContent":["/**\n * OA Discovery Aggregator.\n * Combines results from multiple OA discovery sources.\n */\nimport { checkArxiv } from \"./arxiv.js\";\nimport { checkCore } from \"./core.js\";\nimport { resolveDoiToPmcid } from \"./ncbi-id-converter.js\";\nimport { checkPmc } from \"./pmc.js\";\nimport { checkUnpaywallDetailed } from \"./unpaywall.js\";\n/** Default source order when preferSources is empty or not specified */\nconst DEFAULT_SOURCE_ORDER = [\"pmc\", \"arxiv\", \"unpaywall\", \"core\"];\nfunction checkArxivSource(article) {\n if (!article.arxivId)\n return { skipped: \"no arXiv ID available\" };\n return checkArxiv(article.arxivId);\n}\nasync function checkCoreSource(article, config) {\n if (!config.coreApiKey)\n return { skipped: \"coreApiKey not configured\" };\n if (!article.doi)\n return { skipped: \"no DOI available\" };\n return await checkCore(article.doi, config.coreApiKey);\n}\n/** Map of source name to its checker function (excluding unpaywall and pmc, handled specially). */\nconst sourceCheckers = {\n arxiv: checkArxivSource,\n core: checkCoreSource,\n};\n/** Determine OA status from collected locations and errors. */\nfunction determineOAStatus(locations, errors, sourcesChecked) {\n if (locations.length > 0)\n return \"open\";\n if (errors.length > 0 && errors.length >= sourcesChecked)\n return \"unknown\";\n return \"closed\";\n}\n/**\n * Pre-enrich article with PMCID from NCBI ID Converter when only DOI is available.\n */\nasync function enrichArticleIds(article, config) {\n const discoveredIds = {};\n // Only enrich if we have a DOI but no pmcid/pmid\n if (!article.doi || article.pmcid || article.pmid) {\n return { enriched: article, discoveredIds };\n }\n try {\n const email = config.ncbiEmail || config.unpaywallEmail;\n const options = {};\n if (config.ncbiTool)\n options.tool = config.ncbiTool;\n if (email)\n options.email = email;\n const result = await resolveDoiToPmcid(article.doi, options);\n if (result) {\n const enriched = { ...article };\n if (result.pmcid) {\n enriched.pmcid = result.pmcid;\n discoveredIds.pmcid = result.pmcid;\n }\n if (result.pmid) {\n enriched.pmid = result.pmid;\n discoveredIds.pmid = result.pmid;\n }\n return { enriched, discoveredIds };\n }\n }\n catch {\n // NCBI ID Converter failure is non-fatal; continue without enrichment\n }\n return { enriched: article, discoveredIds };\n}\n/** Check Unpaywall with PMCID extraction. */\nasync function checkUnpaywallSource(enriched, config, state) {\n if (!config.unpaywallEmail) {\n state.skipped.push({ source: \"unpaywall\", reason: \"unpaywallEmail not configured\" });\n return;\n }\n if (!enriched.doi) {\n state.skipped.push({ source: \"unpaywall\", reason: \"no DOI available\" });\n return;\n }\n state.sourcesChecked++;\n state.checkedSources.push(\"unpaywall\");\n try {\n const detailed = await checkUnpaywallDetailed(enriched.doi, config.unpaywallEmail);\n if (!detailed)\n return;\n state.locations.push(...detailed.locations);\n if (detailed.pmcid) {\n state.unpaywallPmcid = detailed.pmcid;\n }\n }\n catch (err) {\n state.errors.push({ source: \"unpaywall\", error: String(err) });\n }\n}\n/** Check PMC source with discovered-ID propagation. */\nasync function checkPmcSourceWithIds(enriched, state, discoveredIds) {\n if (!enriched.pmid && !enriched.pmcid) {\n state.skipped.push({ source: \"pmc\", reason: \"no PMCID or PMID available\" });\n return;\n }\n const ids = {};\n if (enriched.pmid)\n ids.pmid = enriched.pmid;\n if (enriched.pmcid)\n ids.pmcid = enriched.pmcid;\n state.sourcesChecked++;\n state.checkedSources.push(\"pmc\");\n try {\n const result = await checkPmc(ids);\n if (!result)\n return;\n state.locations.push(...result.locations);\n if (result.discoveredPmcid) {\n discoveredIds.pmcid = discoveredIds.pmcid ?? result.discoveredPmcid;\n }\n }\n catch (err) {\n state.errors.push({ source: \"pmc\", error: String(err) });\n }\n}\n/** Check a single non-unpaywall source, updating state. */\nasync function checkGenericSource(source, enriched, config, state) {\n const checker = sourceCheckers[source];\n if (!checker)\n return;\n const result = await runSourceChecker(checker, enriched, config);\n if (result.skipReason) {\n state.skipped.push({ source, reason: result.skipReason });\n return;\n }\n state.sourcesChecked++;\n state.checkedSources.push(source);\n if (result.error) {\n state.errors.push({ source, error: result.error });\n }\n else if (result.locations) {\n state.locations.push(...result.locations);\n }\n}\n/** Perform lazy PMC check if Unpaywall revealed a PMCID not already known. */\nasync function lazyPmcCheck(enriched, state, discoveredIds) {\n if (!state.unpaywallPmcid || enriched.pmcid)\n return;\n discoveredIds.pmcid = discoveredIds.pmcid ?? state.unpaywallPmcid;\n state.checkedSources.push(\"pmc-lazy\");\n try {\n const pmcResult = await checkPmc({ pmcid: state.unpaywallPmcid });\n if (pmcResult) {\n state.locations.push(...pmcResult.locations);\n }\n }\n catch (err) {\n state.errors.push({ source: \"pmc-lazy\", error: String(err) });\n }\n}\n/**\n * Discover OA availability for an article across all configured sources.\n * Checks sources in the order specified by config.preferSources, falling back\n * to the default order. Individual source errors are caught and reported\n * without failing the whole discovery.\n *\n * When only a DOI is provided, pre-enriches with NCBI ID Converter to resolve\n * PMCID/PMID, enabling PMC discovery. Also extracts PMCID from Unpaywall URLs\n * as a fallback, performing a lazy PMC check if a PMCID is discovered.\n */\nexport async function discoverOA(article, config) {\n const { enriched, discoveredIds } = await enrichArticleIds(article, config);\n const state = {\n locations: [],\n errors: [],\n skipped: [],\n checkedSources: [],\n sourcesChecked: 0,\n };\n const sourceOrder = config.preferSources.length > 0 ? config.preferSources : DEFAULT_SOURCE_ORDER;\n for (const source of sourceOrder) {\n if (source === \"unpaywall\") {\n await checkUnpaywallSource(enriched, config, state);\n }\n else if (source === \"pmc\") {\n await checkPmcSourceWithIds(enriched, state, discoveredIds);\n }\n else {\n await checkGenericSource(source, enriched, config, state);\n }\n }\n await lazyPmcCheck(enriched, state, discoveredIds);\n const oaStatus = determineOAStatus(state.locations, state.errors, state.sourcesChecked);\n return {\n oaStatus,\n locations: state.locations,\n errors: state.errors,\n skipped: state.skipped,\n checkedSources: state.checkedSources,\n discoveredIds,\n };\n}\n/** Run a single source checker with error handling. */\nasync function runSourceChecker(checker, article, config) {\n try {\n const result = await checker(article, config);\n if (result !== null && typeof result === \"object\" && \"skipped\" in result) {\n return { skipReason: result.skipped };\n }\n return { locations: result ?? [] };\n }\n catch (err) {\n return { error: String(err) };\n }\n}\n//# sourceMappingURL=index.js.map"],"names":[],"mappings":";;;;;AAUA,MAAM,uBAAuB,CAAC,OAAO,SAAS,aAAa,MAAM;AACjE,SAAS,iBAAiB,SAAS;AAC/B,MAAI,CAAC,QAAQ;AACT,WAAO,EAAE,SAAS,wBAAuB;AAC7C,SAAO,WAAW,QAAQ,OAAO;AACrC;AACA,eAAe,gBAAgB,SAAS,QAAQ;AAC5C,MAAI,CAAC,OAAO;AACR,WAAO,EAAE,SAAS,4BAA2B;AACjD,MAAI,CAAC,QAAQ;AACT,WAAO,EAAE,SAAS,mBAAkB;AACxC,SAAO,MAAM,UAAU,QAAQ,KAAK,OAAO,UAAU;AACzD;AAEA,MAAM,iBAAiB;AAAA,EACnB,OAAO;AAAA,EACP,MAAM;AACV;AAEA,SAAS,kBAAkB,WAAW,QAAQ,gBAAgB;AAC1D,MAAI,UAAU,SAAS;AACnB,WAAO;AACX,MAAI,OAAO,SAAS,KAAK,OAAO,UAAU;AACtC,WAAO;AACX,SAAO;AACX;AAIA,eAAe,iBAAiB,SAAS,QAAQ;AAC7C,QAAM,gBAAgB,CAAA;AAEtB,MAAI,CAAC,QAAQ,OAAO,QAAQ,SAAS,QAAQ,MAAM;AAC/C,WAAO,EAAE,UAAU,SAAS,cAAa;AAAA,EAC7C;AACA,MAAI;AACA,UAAM,QAAQ,OAAO,aAAa,OAAO;AACzC,UAAM,UAAU,CAAA;AAChB,QAAI,OAAO;AACP,cAAQ,OAAO,OAAO;AAC1B,QAAI;AACA,cAAQ,QAAQ;AACpB,UAAM,SAAS,MAAM,kBAAkB,QAAQ,KAAK,OAAO;AAC3D,QAAI,QAAQ;AACR,YAAM,WAAW,EAAE,GAAG,QAAO;AAC7B,UAAI,OAAO,OAAO;AACd,iBAAS,QAAQ,OAAO;AACxB,sBAAc,QAAQ,OAAO;AAAA,MACjC;AACA,UAAI,OAAO,MAAM;AACb,iBAAS,OAAO,OAAO;AACvB,sBAAc,OAAO,OAAO;AAAA,MAChC;AACA,aAAO,EAAE,UAAU,cAAa;AAAA,IACpC;AAAA,EACJ,QACM;AAAA,EAEN;AACA,SAAO,EAAE,UAAU,SAAS,cAAa;AAC7C;AAEA,eAAe,qBAAqB,UAAU,QAAQ,OAAO;AACzD,MAAI,CAAC,OAAO,gBAAgB;AACxB,UAAM,QAAQ,KAAK,EAAE,QAAQ,aAAa,QAAQ,iCAAiC;AACnF;AAAA,EACJ;AACA,MAAI,CAAC,SAAS,KAAK;AACf,UAAM,QAAQ,KAAK,EAAE,QAAQ,aAAa,QAAQ,oBAAoB;AACtE;AAAA,EACJ;AACA,QAAM;AACN,QAAM,eAAe,KAAK,WAAW;AACrC,MAAI;AACA,UAAM,WAAW,MAAM,uBAAuB,SAAS,KAAK,OAAO,cAAc;AACjF,QAAI,CAAC;AACD;AACJ,UAAM,UAAU,KAAK,GAAG,SAAS,SAAS;AAC1C,QAAI,SAAS,OAAO;AAChB,YAAM,iBAAiB,SAAS;AAAA,IACpC;AAAA,EACJ,SACO,KAAK;AACR,UAAM,OAAO,KAAK,EAAE,QAAQ,aAAa,OAAO,OAAO,GAAG,GAAG;AAAA,EACjE;AACJ;AAEA,eAAe,sBAAsB,UAAU,OAAO,eAAe;AACjE,MAAI,CAAC,SAAS,QAAQ,CAAC,SAAS,OAAO;AACnC,UAAM,QAAQ,KAAK,EAAE,QAAQ,OAAO,QAAQ,8BAA8B;AAC1E;AAAA,EACJ;AACA,QAAM,MAAM,CAAA;AACZ,MAAI,SAAS;AACT,QAAI,OAAO,SAAS;AACxB,MAAI,SAAS;AACT,QAAI,QAAQ,SAAS;AACzB,QAAM;AACN,QAAM,eAAe,KAAK,KAAK;AAC/B,MAAI;AACA,UAAM,SAAS,MAAM,SAAS,GAAG;AACjC,QAAI,CAAC;AACD;AACJ,UAAM,UAAU,KAAK,GAAG,OAAO,SAAS;AACxC,QAAI,OAAO,iBAAiB;AACxB,oBAAc,QAAQ,cAAc,SAAS,OAAO;AAAA,IACxD;AAAA,EACJ,SACO,KAAK;AACR,UAAM,OAAO,KAAK,EAAE,QAAQ,OAAO,OAAO,OAAO,GAAG,GAAG;AAAA,EAC3D;AACJ;AAEA,eAAe,mBAAmB,QAAQ,UAAU,QAAQ,OAAO;AAC/D,QAAM,UAAU,eAAe,MAAM;AACrC,MAAI,CAAC;AACD;AACJ,QAAM,SAAS,MAAM,iBAAiB,SAAS,UAAU,MAAM;AAC/D,MAAI,OAAO,YAAY;AACnB,UAAM,QAAQ,KAAK,EAAE,QAAQ,QAAQ,OAAO,YAAY;AACxD;AAAA,EACJ;AACA,QAAM;AACN,QAAM,eAAe,KAAK,MAAM;AAChC,MAAI,OAAO,OAAO;AACd,UAAM,OAAO,KAAK,EAAE,QAAQ,OAAO,OAAO,OAAO;AAAA,EACrD,WACS,OAAO,WAAW;AACvB,UAAM,UAAU,KAAK,GAAG,OAAO,SAAS;AAAA,EAC5C;AACJ;AAEA,eAAe,aAAa,UAAU,OAAO,eAAe;AACxD,MAAI,CAAC,MAAM,kBAAkB,SAAS;AAClC;AACJ,gBAAc,QAAQ,cAAc,SAAS,MAAM;AACnD,QAAM,eAAe,KAAK,UAAU;AACpC,MAAI;AACA,UAAM,YAAY,MAAM,SAAS,EAAE,OAAO,MAAM,gBAAgB;AAChE,QAAI,WAAW;AACX,YAAM,UAAU,KAAK,GAAG,UAAU,SAAS;AAAA,IAC/C;AAAA,EACJ,SACO,KAAK;AACR,UAAM,OAAO,KAAK,EAAE,QAAQ,YAAY,OAAO,OAAO,GAAG,GAAG;AAAA,EAChE;AACJ;AAWO,eAAe,WAAW,SAAS,QAAQ;AAC9C,QAAM,EAAE,UAAU,cAAa,IAAK,MAAM,iBAAiB,SAAS,MAAM;AAC1E,QAAM,QAAQ;AAAA,IACV,WAAW,CAAA;AAAA,IACX,QAAQ,CAAA;AAAA,IACR,SAAS,CAAA;AAAA,IACT,gBAAgB,CAAA;AAAA,IAChB,gBAAgB;AAAA,EACxB;AACI,QAAM,cAAc,OAAO,cAAc,SAAS,IAAI,OAAO,gBAAgB;AAC7E,aAAW,UAAU,aAAa;AAC9B,QAAI,WAAW,aAAa;AACxB,YAAM,qBAAqB,UAAU,QAAQ,KAAK;AAAA,IACtD,WACS,WAAW,OAAO;AACvB,YAAM,sBAAsB,UAAU,OAAO,aAAa;AAAA,IAC9D,OACK;AACD,YAAM,mBAAmB,QAAQ,UAAU,QAAQ,KAAK;AAAA,IAC5D;AAAA,EACJ;AACA,QAAM,aAAa,UAAU,OAAO,aAAa;AACjD,QAAM,WAAW,kBAAkB,MAAM,WAAW,MAAM,QAAQ,MAAM,cAAc;AACtF,SAAO;AAAA,IACH;AAAA,IACA,WAAW,MAAM;AAAA,IACjB,QAAQ,MAAM;AAAA,IACd,SAAS,MAAM;AAAA,IACf,gBAAgB,MAAM;AAAA,IACtB;AAAA,EACR;AACA;AAEA,eAAe,iBAAiB,SAAS,SAAS,QAAQ;AACtD,MAAI;AACA,UAAM,SAAS,MAAM,QAAQ,SAAS,MAAM;AAC5C,QAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,aAAa,QAAQ;AACtE,aAAO,EAAE,YAAY,OAAO,QAAO;AAAA,IACvC;AACA,WAAO,EAAE,WAAW,UAAU,GAAE;AAAA,EACpC,SACO,KAAK;AACR,WAAO,EAAE,OAAO,OAAO,GAAG,EAAC;AAAA,EAC/B;AACJ;","x_google_ignoreList":[0]}
|
|
@@ -36,7 +36,7 @@ async function lookupPmcid(pmid, options) {
|
|
|
36
36
|
const firstLinkset = linksets[0];
|
|
37
37
|
if (!firstLinkset?.linksetdbs)
|
|
38
38
|
return null;
|
|
39
|
-
const pmcLink = firstLinkset.linksetdbs.find((db) => db.dbto === "pmc");
|
|
39
|
+
const pmcLink = firstLinkset.linksetdbs.find((db) => db.dbto === "pmc" && db.linkname === "pubmed_pmc");
|
|
40
40
|
if (!pmcLink?.links || pmcLink.links.length === 0)
|
|
41
41
|
return null;
|
|
42
42
|
const pmcNumericId = pmcLink.links[0];
|
|
@@ -44,13 +44,13 @@ async function lookupPmcid(pmid, options) {
|
|
|
44
44
|
}
|
|
45
45
|
async function checkPmc(ids, options) {
|
|
46
46
|
if (ids.pmcid) {
|
|
47
|
-
return getPmcUrls(ids.pmcid);
|
|
47
|
+
return { locations: getPmcUrls(ids.pmcid) };
|
|
48
48
|
}
|
|
49
49
|
if (ids.pmid) {
|
|
50
50
|
const pmcid = await lookupPmcid(ids.pmid);
|
|
51
51
|
if (!pmcid)
|
|
52
52
|
return null;
|
|
53
|
-
return getPmcUrls(pmcid);
|
|
53
|
+
return { locations: getPmcUrls(pmcid), discoveredPmcid: pmcid };
|
|
54
54
|
}
|
|
55
55
|
return null;
|
|
56
56
|
}
|