@ncukondo/search-hub 0.12.1 → 0.13.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/_virtual/_commonjsHelpers.js +30 -0
- package/dist/_virtual/_commonjsHelpers.js.map +1 -0
- package/dist/_virtual/aliases.js +5 -0
- package/dist/_virtual/aliases.js.map +1 -0
- package/dist/_virtual/attributes.js +5 -0
- package/dist/_virtual/attributes.js.map +1 -0
- package/dist/_virtual/back.js +5 -0
- package/dist/_virtual/back.js.map +1 -0
- package/dist/_virtual/comment.js +5 -0
- package/dist/_virtual/comment.js.map +1 -0
- package/dist/_virtual/compile.js +5 -0
- package/dist/_virtual/compile.js.map +1 -0
- package/dist/_virtual/compile2.js +5 -0
- package/dist/_virtual/compile2.js.map +1 -0
- package/dist/_virtual/decode-data-html.js +5 -0
- package/dist/_virtual/decode-data-html.js.map +1 -0
- package/dist/_virtual/decode-data-xml.js +5 -0
- package/dist/_virtual/decode-data-xml.js.map +1 -0
- package/dist/_virtual/decode.js +5 -0
- package/dist/_virtual/decode.js.map +1 -0
- package/dist/_virtual/decode_codepoint.js +5 -0
- package/dist/_virtual/decode_codepoint.js.map +1 -0
- package/dist/_virtual/encode-html.js +5 -0
- package/dist/_virtual/encode-html.js.map +1 -0
- package/dist/_virtual/encode.js +5 -0
- package/dist/_virtual/encode.js.map +1 -0
- package/dist/_virtual/escape.js +5 -0
- package/dist/_virtual/escape.js.map +1 -0
- package/dist/_virtual/feeds.js +5 -0
- package/dist/_virtual/feeds.js.map +1 -0
- package/dist/_virtual/filters.js +5 -0
- package/dist/_virtual/filters.js.map +1 -0
- package/dist/_virtual/foreignNames.js +5 -0
- package/dist/_virtual/foreignNames.js.map +1 -0
- package/dist/_virtual/general.js +5 -0
- package/dist/_virtual/general.js.map +1 -0
- package/dist/_virtual/he.js +5 -0
- package/dist/_virtual/he.js.map +1 -0
- package/dist/_virtual/helpers.js +5 -0
- package/dist/_virtual/helpers.js.map +1 -0
- package/dist/_virtual/html.js +5 -0
- package/dist/_virtual/html.js.map +1 -0
- package/dist/_virtual/index.js +6 -0
- package/dist/_virtual/index.js.map +1 -0
- package/dist/_virtual/index10.js +5 -0
- package/dist/_virtual/index10.js.map +1 -0
- package/dist/_virtual/index11.js +5 -0
- package/dist/_virtual/index11.js.map +1 -0
- package/dist/_virtual/index2.js +5 -0
- package/dist/_virtual/index2.js.map +1 -0
- package/dist/_virtual/index3.js +5 -0
- package/dist/_virtual/index3.js.map +1 -0
- package/dist/_virtual/index4.js +5 -0
- package/dist/_virtual/index4.js.map +1 -0
- package/dist/_virtual/index5.js +7 -0
- package/dist/_virtual/index5.js.map +1 -0
- package/dist/_virtual/index6.js +5 -0
- package/dist/_virtual/index6.js.map +1 -0
- package/dist/_virtual/index7.js +5 -0
- package/dist/_virtual/index7.js.map +1 -0
- package/dist/_virtual/index8.js +5 -0
- package/dist/_virtual/index8.js.map +1 -0
- package/dist/_virtual/index9.js +5 -0
- package/dist/_virtual/index9.js.map +1 -0
- package/dist/_virtual/legacy.js +5 -0
- package/dist/_virtual/legacy.js.map +1 -0
- package/dist/_virtual/manipulation.js +5 -0
- package/dist/_virtual/manipulation.js.map +1 -0
- package/dist/_virtual/matcher.js +5 -0
- package/dist/_virtual/matcher.js.map +1 -0
- package/dist/_virtual/node.js +5 -0
- package/dist/_virtual/node.js.map +1 -0
- package/dist/_virtual/node2.js +5 -0
- package/dist/_virtual/node2.js.map +1 -0
- package/dist/_virtual/parse.js +5 -0
- package/dist/_virtual/parse.js.map +1 -0
- package/dist/_virtual/parse2.js +5 -0
- package/dist/_virtual/parse2.js.map +1 -0
- package/dist/_virtual/pseudos.js +5 -0
- package/dist/_virtual/pseudos.js.map +1 -0
- package/dist/_virtual/querying.js +5 -0
- package/dist/_virtual/querying.js.map +1 -0
- package/dist/_virtual/sort.js +5 -0
- package/dist/_virtual/sort.js.map +1 -0
- package/dist/_virtual/stringify.js +5 -0
- package/dist/_virtual/stringify.js.map +1 -0
- package/dist/_virtual/subselects.js +5 -0
- package/dist/_virtual/subselects.js.map +1 -0
- package/dist/_virtual/text.js +5 -0
- package/dist/_virtual/text.js.map +1 -0
- package/dist/_virtual/traversal.js +5 -0
- package/dist/_virtual/traversal.js.map +1 -0
- package/dist/_virtual/type.js +5 -0
- package/dist/_virtual/type.js.map +1 -0
- package/dist/_virtual/valid.js +5 -0
- package/dist/_virtual/valid.js.map +1 -0
- package/dist/_virtual/void-tag.js +5 -0
- package/dist/_virtual/void-tag.js.map +1 -0
- package/dist/cli/commands/diff.js +2 -2
- package/dist/cli/commands/diff.js.map +1 -1
- package/dist/cli/commands/fulltext/attach.js +1 -1
- package/dist/cli/commands/fulltext/attach.js.map +1 -1
- package/dist/cli/commands/fulltext/check.d.ts +1 -2
- package/dist/cli/commands/fulltext/check.d.ts.map +1 -1
- package/dist/cli/commands/fulltext/check.js +4 -2
- package/dist/cli/commands/fulltext/check.js.map +1 -1
- package/dist/cli/commands/fulltext/convert.d.ts.map +1 -1
- package/dist/cli/commands/fulltext/convert.js +8 -8
- package/dist/cli/commands/fulltext/convert.js.map +1 -1
- package/dist/cli/commands/fulltext/fetch.d.ts.map +1 -1
- package/dist/cli/commands/fulltext/fetch.js +10 -6
- package/dist/cli/commands/fulltext/fetch.js.map +1 -1
- package/dist/cli/commands/fulltext/index.d.ts.map +1 -1
- package/dist/cli/commands/fulltext/index.js +2 -0
- package/dist/cli/commands/fulltext/index.js.map +1 -1
- package/dist/cli/commands/fulltext/init.d.ts.map +1 -1
- package/dist/cli/commands/fulltext/init.js +6 -5
- package/dist/cli/commands/fulltext/init.js.map +1 -1
- package/dist/cli/commands/fulltext/pending.d.ts +1 -1
- package/dist/cli/commands/fulltext/pending.d.ts.map +1 -1
- package/dist/cli/commands/fulltext/pending.js +4 -2
- package/dist/cli/commands/fulltext/pending.js.map +1 -1
- package/dist/cli/commands/fulltext/status.d.ts.map +1 -1
- package/dist/cli/commands/fulltext/status.js +4 -2
- package/dist/cli/commands/fulltext/status.js.map +1 -1
- package/dist/cli/commands/fulltext/sync.d.ts.map +1 -1
- package/dist/cli/commands/fulltext/sync.js +6 -2
- package/dist/cli/commands/fulltext/sync.js.map +1 -1
- package/dist/cli/commands/query/init.d.ts +5 -0
- package/dist/cli/commands/query/init.d.ts.map +1 -1
- package/dist/cli/commands/query/init.js +9 -1
- package/dist/cli/commands/query/init.js.map +1 -1
- package/dist/cli/commands/query/translate.d.ts.map +1 -1
- package/dist/cli/commands/query/translate.js +5 -0
- package/dist/cli/commands/query/translate.js.map +1 -1
- package/dist/cli/commands/query/validate.d.ts +22 -1
- package/dist/cli/commands/query/validate.d.ts.map +1 -1
- package/dist/cli/commands/query/validate.js +65 -22
- package/dist/cli/commands/query/validate.js.map +1 -1
- package/dist/cli/commands/review/extract.d.ts.map +1 -1
- package/dist/cli/commands/review/extract.js +1 -2
- package/dist/cli/commands/review/extract.js.map +1 -1
- package/dist/cli/commands/review/finalize.d.ts.map +1 -1
- package/dist/cli/commands/review/finalize.js +1 -2
- package/dist/cli/commands/review/finalize.js.map +1 -1
- package/dist/cli/commands/review/init.d.ts.map +1 -1
- package/dist/cli/commands/review/init.js +2 -5
- package/dist/cli/commands/review/init.js.map +1 -1
- package/dist/cli/commands/review/merge.d.ts.map +1 -1
- package/dist/cli/commands/review/merge.js +1 -2
- package/dist/cli/commands/review/merge.js.map +1 -1
- package/dist/cli/commands/review/types.d.ts +1 -1
- package/dist/cli/commands/review/types.d.ts.map +1 -1
- package/dist/cli/commands/review/types.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +81 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/suggestions/index.d.ts.map +1 -1
- package/dist/cli/suggestions/index.js +10 -0
- package/dist/cli/suggestions/index.js.map +1 -1
- package/dist/cli/suggestions/rules.d.ts.map +1 -1
- package/dist/cli/suggestions/rules.js +21 -8
- package/dist/cli/suggestions/rules.js.map +1 -1
- package/dist/cli/suggestions/types.d.ts +11 -0
- package/dist/cli/suggestions/types.d.ts.map +1 -1
- package/dist/config/schema.d.ts +2 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +6 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/{fulltext → integration}/attach-shared.d.ts +2 -2
- package/dist/integration/attach-shared.d.ts.map +1 -0
- package/dist/integration/attach-shared.js.map +1 -0
- package/dist/integration/fulltext-attach.js +1 -1
- package/dist/integration/fulltext-attach.js.map +1 -1
- package/dist/{fulltext → node_modules/@ncukondo/academic-fulltext/dist}/citation-key.js +1 -1
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/citation-key.js.map +1 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/convert/arxiv-html-parser.js +434 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/convert/arxiv-html-parser.js.map +1 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/convert/index.js +93 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/convert/index.js.map +1 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/convert/jats-parser.js +1060 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/convert/jats-parser.js.map +1 -0
- package/dist/{fulltext → node_modules/@ncukondo/academic-fulltext/dist}/convert/markdown-writer.js +146 -117
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/convert/markdown-writer.js.map +1 -0
- package/dist/{fulltext → node_modules/@ncukondo/academic-fulltext/dist}/discovery/arxiv.js +8 -1
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/discovery/arxiv.js.map +1 -0
- package/dist/{fulltext → node_modules/@ncukondo/academic-fulltext/dist}/discovery/core.js +6 -3
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/discovery/core.js.map +1 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/discovery/index.js +139 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/discovery/index.js.map +1 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/discovery/ncbi-id-converter.js +46 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/discovery/ncbi-id-converter.js.map +1 -0
- package/dist/{fulltext → node_modules/@ncukondo/academic-fulltext/dist}/discovery/pmc.js +8 -4
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/discovery/pmc.js.map +1 -0
- package/dist/{fulltext → node_modules/@ncukondo/academic-fulltext/dist}/discovery/unpaywall.js +43 -9
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/discovery/unpaywall.js.map +1 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/download/arxiv-html.js +48 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/download/arxiv-html.js.map +1 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/download/downloader.js +64 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/download/downloader.js.map +1 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/download/orchestrator.js +236 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/download/orchestrator.js.map +1 -0
- package/dist/{fulltext → node_modules/@ncukondo/academic-fulltext/dist}/download/pmc-xml.js +2 -1
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/download/pmc-xml.js.map +1 -0
- package/dist/{fulltext → node_modules/@ncukondo/academic-fulltext/dist}/meta.js +15 -10
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/meta.js.map +1 -0
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/paths.js.map +1 -0
- package/dist/{fulltext → node_modules/@ncukondo/academic-fulltext/dist}/readme.js +8 -4
- package/dist/node_modules/@ncukondo/academic-fulltext/dist/readme.js.map +1 -0
- package/dist/node_modules/boolbase/index.js +19 -0
- package/dist/node_modules/boolbase/index.js.map +1 -0
- package/dist/node_modules/css-select/lib/attributes.js +203 -0
- package/dist/node_modules/css-select/lib/attributes.js.map +1 -0
- package/dist/node_modules/css-select/lib/compile.js +141 -0
- package/dist/node_modules/css-select/lib/compile.js.map +1 -0
- package/dist/node_modules/css-select/lib/general.js +154 -0
- package/dist/node_modules/css-select/lib/general.js.map +1 -0
- package/dist/node_modules/css-select/lib/index.js +128 -0
- package/dist/node_modules/css-select/lib/index.js.map +1 -0
- package/dist/node_modules/css-select/lib/pseudo-selectors/aliases.js +40 -0
- package/dist/node_modules/css-select/lib/pseudo-selectors/aliases.js.map +1 -0
- package/dist/node_modules/css-select/lib/pseudo-selectors/filters.js +163 -0
- package/dist/node_modules/css-select/lib/pseudo-selectors/filters.js.map +1 -0
- package/dist/node_modules/css-select/lib/pseudo-selectors/index.js +71 -0
- package/dist/node_modules/css-select/lib/pseudo-selectors/index.js.map +1 -0
- package/dist/node_modules/css-select/lib/pseudo-selectors/pseudos.js +93 -0
- package/dist/node_modules/css-select/lib/pseudo-selectors/pseudos.js.map +1 -0
- package/dist/node_modules/css-select/lib/pseudo-selectors/subselects.js +111 -0
- package/dist/node_modules/css-select/lib/pseudo-selectors/subselects.js.map +1 -0
- package/dist/node_modules/css-select/lib/sort.js +78 -0
- package/dist/node_modules/css-select/lib/sort.js.map +1 -0
- package/dist/node_modules/css-what/lib/es/index.js +12 -0
- package/dist/node_modules/css-what/lib/es/index.js.map +1 -0
- package/dist/node_modules/css-what/lib/es/parse.js +349 -0
- package/dist/node_modules/css-what/lib/es/parse.js.map +1 -0
- package/dist/node_modules/css-what/lib/es/stringify.js +102 -0
- package/dist/node_modules/css-what/lib/es/stringify.js.map +1 -0
- package/dist/node_modules/css-what/lib/es/types.js +37 -0
- package/dist/node_modules/css-what/lib/es/types.js.map +1 -0
- package/dist/node_modules/dom-serializer/lib/foreignNames.js +117 -0
- package/dist/node_modules/dom-serializer/lib/foreignNames.js.map +1 -0
- package/dist/node_modules/dom-serializer/lib/index.js +207 -0
- package/dist/node_modules/dom-serializer/lib/index.js.map +1 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/decode.js +368 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/decode.js.map +1 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/decode_codepoint.js +70 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/decode_codepoint.js.map +1 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/encode.js +61 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/encode.js.map +1 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/escape.js +79 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/escape.js.map +1 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/generated/decode-data-html.js +18 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/generated/decode-data-html.js.map +1 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/generated/decode-data-xml.js +18 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/generated/decode-data-xml.js.map +1 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/generated/encode-html.js +19 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/generated/encode-html.js.map +1 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/index.js +139 -0
- package/dist/node_modules/dom-serializer/node_modules/entities/lib/index.js.map +1 -0
- package/dist/node_modules/domelementtype/lib/index.js +40 -0
- package/dist/node_modules/domelementtype/lib/index.js.map +1 -0
- package/dist/node_modules/domhandler/lib/index.js +167 -0
- package/dist/node_modules/domhandler/lib/index.js.map +1 -0
- package/dist/node_modules/domhandler/lib/node.js +439 -0
- package/dist/node_modules/domhandler/lib/node.js.map +1 -0
- package/dist/node_modules/domutils/lib/feeds.js +146 -0
- package/dist/node_modules/domutils/lib/feeds.js.map +1 -0
- package/dist/node_modules/domutils/lib/helpers.js +97 -0
- package/dist/node_modules/domutils/lib/helpers.js.map +1 -0
- package/dist/node_modules/domutils/lib/index.js +65 -0
- package/dist/node_modules/domutils/lib/index.js.map +1 -0
- package/dist/node_modules/domutils/lib/legacy.js +124 -0
- package/dist/node_modules/domutils/lib/legacy.js.map +1 -0
- package/dist/node_modules/domutils/lib/manipulation.js +107 -0
- package/dist/node_modules/domutils/lib/manipulation.js.map +1 -0
- package/dist/node_modules/domutils/lib/querying.js +102 -0
- package/dist/node_modules/domutils/lib/querying.js.map +1 -0
- package/dist/node_modules/domutils/lib/stringify.js +65 -0
- package/dist/node_modules/domutils/lib/stringify.js.map +1 -0
- package/dist/node_modules/domutils/lib/traversal.js +69 -0
- package/dist/node_modules/domutils/lib/traversal.js.map +1 -0
- package/dist/node_modules/he/he.js +256 -0
- package/dist/node_modules/he/he.js.map +1 -0
- package/dist/node_modules/node-html-parser/dist/back.js +16 -0
- package/dist/node_modules/node-html-parser/dist/back.js.map +1 -0
- package/dist/node_modules/node-html-parser/dist/index.js +48 -0
- package/dist/node_modules/node-html-parser/dist/index.js.map +1 -0
- package/dist/node_modules/node-html-parser/dist/matcher.js +112 -0
- package/dist/node_modules/node-html-parser/dist/matcher.js.map +1 -0
- package/dist/node_modules/node-html-parser/dist/nodes/comment.js +41 -0
- package/dist/node_modules/node-html-parser/dist/nodes/comment.js.map +1 -0
- package/dist/node_modules/node-html-parser/dist/nodes/html.js +1048 -0
- package/dist/node_modules/node-html-parser/dist/nodes/html.js.map +1 -0
- package/dist/node_modules/node-html-parser/dist/nodes/node.js +49 -0
- package/dist/node_modules/node-html-parser/dist/nodes/node.js.map +1 -0
- package/dist/node_modules/node-html-parser/dist/nodes/text.js +106 -0
- package/dist/node_modules/node-html-parser/dist/nodes/text.js.map +1 -0
- package/dist/node_modules/node-html-parser/dist/nodes/type.js +19 -0
- package/dist/node_modules/node-html-parser/dist/nodes/type.js.map +1 -0
- package/dist/node_modules/node-html-parser/dist/parse.js +20 -0
- package/dist/node_modules/node-html-parser/dist/parse.js.map +1 -0
- package/dist/node_modules/node-html-parser/dist/valid.js +19 -0
- package/dist/node_modules/node-html-parser/dist/valid.js.map +1 -0
- package/dist/node_modules/node-html-parser/dist/void-tag.js +36 -0
- package/dist/node_modules/node-html-parser/dist/void-tag.js.map +1 -0
- package/dist/node_modules/nth-check/lib/compile.js +76 -0
- package/dist/node_modules/nth-check/lib/compile.js.map +1 -0
- package/dist/node_modules/nth-check/lib/index.js +36 -0
- package/dist/node_modules/nth-check/lib/index.js.map +1 -0
- package/dist/node_modules/nth-check/lib/parse.js +69 -0
- package/dist/node_modules/nth-check/lib/parse.js.map +1 -0
- package/dist/providers/arxiv/translator.d.ts.map +1 -1
- package/dist/providers/arxiv/translator.js +5 -2
- package/dist/providers/arxiv/translator.js.map +1 -1
- package/dist/providers/base/types.d.ts +2 -0
- package/dist/providers/base/types.d.ts.map +1 -1
- package/dist/providers/base/types.js.map +1 -1
- package/dist/providers/base/warnings.d.ts +14 -0
- package/dist/providers/base/warnings.d.ts.map +1 -0
- package/dist/providers/base/warnings.js +33 -0
- package/dist/providers/base/warnings.js.map +1 -0
- package/dist/providers/eric/translator.d.ts.map +1 -1
- package/dist/providers/eric/translator.js +5 -2
- package/dist/providers/eric/translator.js.map +1 -1
- package/dist/providers/pubmed/translator.d.ts.map +1 -1
- package/dist/providers/pubmed/translator.js +5 -2
- package/dist/providers/pubmed/translator.js.map +1 -1
- package/dist/providers/scopus/translator.d.ts.map +1 -1
- package/dist/providers/scopus/translator.js +22 -5
- package/dist/providers/scopus/translator.js.map +1 -1
- package/dist/query/__test-helpers__/mock-mesh-client.d.ts +12 -0
- package/dist/query/__test-helpers__/mock-mesh-client.d.ts.map +1 -0
- package/dist/query/index.d.ts +4 -0
- package/dist/query/index.d.ts.map +1 -1
- package/dist/query/json-schema.d.ts +3 -0
- package/dist/query/json-schema.d.ts.map +1 -0
- package/dist/query/json-schema.js +48 -0
- package/dist/query/json-schema.js.map +1 -0
- package/dist/query/mesh-lookup.d.ts +47 -0
- package/dist/query/mesh-lookup.d.ts.map +1 -0
- package/dist/query/mesh-lookup.js +151 -0
- package/dist/query/mesh-lookup.js.map +1 -0
- package/dist/query/parser.js +1 -1
- package/dist/query/parser.js.map +1 -1
- package/dist/query/types.d.ts +2 -2
- package/dist/query/types.d.ts.map +1 -1
- package/dist/query/validator.d.ts +5 -5
- package/dist/query/validator.d.ts.map +1 -1
- package/dist/query/validator.js +5 -2
- package/dist/query/validator.js.map +1 -1
- package/dist/query/vocab-cache.d.ts +15 -0
- package/dist/query/vocab-cache.d.ts.map +1 -0
- package/dist/query/vocab-cache.js +44 -0
- package/dist/query/vocab-cache.js.map +1 -0
- package/dist/query/vocab-validator.d.ts +71 -0
- package/dist/query/vocab-validator.d.ts.map +1 -0
- package/dist/query/vocab-validator.js +153 -0
- package/dist/query/vocab-validator.js.map +1 -0
- package/dist/utils/levenshtein.d.ts +6 -0
- package/dist/utils/levenshtein.d.ts.map +1 -0
- package/dist/utils/levenshtein.js +21 -0
- package/dist/utils/levenshtein.js.map +1 -0
- package/package.json +2 -2
- package/dist/fulltext/attach-shared.d.ts.map +0 -1
- package/dist/fulltext/attach-shared.js.map +0 -1
- package/dist/fulltext/citation-key.d.ts +0 -15
- package/dist/fulltext/citation-key.d.ts.map +0 -1
- package/dist/fulltext/citation-key.js.map +0 -1
- package/dist/fulltext/convert/index.d.ts +0 -20
- package/dist/fulltext/convert/index.d.ts.map +0 -1
- package/dist/fulltext/convert/index.js +0 -50
- package/dist/fulltext/convert/index.js.map +0 -1
- package/dist/fulltext/convert/jats-parser.d.ts +0 -36
- package/dist/fulltext/convert/jats-parser.d.ts.map +0 -1
- package/dist/fulltext/convert/jats-parser.js +0 -887
- package/dist/fulltext/convert/jats-parser.js.map +0 -1
- package/dist/fulltext/convert/markdown-writer.d.ts +0 -6
- package/dist/fulltext/convert/markdown-writer.d.ts.map +0 -1
- package/dist/fulltext/convert/markdown-writer.js.map +0 -1
- package/dist/fulltext/convert/types.d.ts +0 -141
- package/dist/fulltext/convert/types.d.ts.map +0 -1
- package/dist/fulltext/discovery/arxiv.d.ts +0 -11
- package/dist/fulltext/discovery/arxiv.d.ts.map +0 -1
- package/dist/fulltext/discovery/arxiv.js.map +0 -1
- package/dist/fulltext/discovery/core.d.ts +0 -11
- package/dist/fulltext/discovery/core.d.ts.map +0 -1
- package/dist/fulltext/discovery/core.js.map +0 -1
- package/dist/fulltext/discovery/index.d.ts +0 -28
- package/dist/fulltext/discovery/index.d.ts.map +0 -1
- package/dist/fulltext/discovery/index.js +0 -75
- package/dist/fulltext/discovery/index.js.map +0 -1
- package/dist/fulltext/discovery/pmc.d.ts +0 -19
- package/dist/fulltext/discovery/pmc.d.ts.map +0 -1
- package/dist/fulltext/discovery/pmc.js.map +0 -1
- package/dist/fulltext/discovery/unpaywall.d.ts +0 -11
- package/dist/fulltext/discovery/unpaywall.d.ts.map +0 -1
- package/dist/fulltext/discovery/unpaywall.js.map +0 -1
- package/dist/fulltext/download/downloader.d.ts +0 -21
- package/dist/fulltext/download/downloader.d.ts.map +0 -1
- package/dist/fulltext/download/downloader.js +0 -59
- package/dist/fulltext/download/downloader.js.map +0 -1
- package/dist/fulltext/download/orchestrator.d.ts +0 -33
- package/dist/fulltext/download/orchestrator.d.ts.map +0 -1
- package/dist/fulltext/download/orchestrator.js +0 -125
- package/dist/fulltext/download/orchestrator.js.map +0 -1
- package/dist/fulltext/download/pmc-xml.d.ts +0 -13
- package/dist/fulltext/download/pmc-xml.d.ts.map +0 -1
- package/dist/fulltext/download/pmc-xml.js.map +0 -1
- package/dist/fulltext/meta.d.ts +0 -25
- package/dist/fulltext/meta.d.ts.map +0 -1
- package/dist/fulltext/meta.js.map +0 -1
- package/dist/fulltext/paths.d.ts +0 -12
- package/dist/fulltext/paths.d.ts.map +0 -1
- package/dist/fulltext/paths.js.map +0 -1
- package/dist/fulltext/readme.d.ts +0 -4
- package/dist/fulltext/readme.d.ts.map +0 -1
- package/dist/fulltext/readme.js.map +0 -1
- package/dist/fulltext/types.d.ts +0 -90
- package/dist/fulltext/types.d.ts.map +0 -1
- /package/dist/{fulltext → integration}/attach-shared.js +0 -0
- /package/dist/{fulltext → node_modules/@ncukondo/academic-fulltext/dist}/paths.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-schema.d.ts","sourceRoot":"","sources":["../../src/query/json-schema.ts"],"names":[],"mappings":"AAsEA,6DAA6D;AAC7D,wBAAgB,uBAAuB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIjE"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { publicationTypeFilterSchema, operatorSchema, fieldTypeSchema } from "./validator.js";
|
|
3
|
+
const filtersInputSchema = z.object({
|
|
4
|
+
year_from: z.number().int().optional(),
|
|
5
|
+
year_to: z.number().int().optional(),
|
|
6
|
+
language: z.array(z.string()).optional(),
|
|
7
|
+
publication_types: publicationTypeFilterSchema.optional()
|
|
8
|
+
}).optional();
|
|
9
|
+
const overrideBlockInputSchema = z.object({
|
|
10
|
+
filters: filtersInputSchema,
|
|
11
|
+
categories: z.array(z.string()).optional(),
|
|
12
|
+
source_types: z.array(z.string()).optional()
|
|
13
|
+
}).optional();
|
|
14
|
+
const termBlockInputSchema = z.object({
|
|
15
|
+
keywords: z.array(z.string()).min(1).optional(),
|
|
16
|
+
mesh: z.array(z.string()).optional(),
|
|
17
|
+
emtree: z.array(z.string()).optional(),
|
|
18
|
+
eric: z.array(z.string()).optional(),
|
|
19
|
+
exclude: z.array(z.string()).optional()
|
|
20
|
+
});
|
|
21
|
+
const queryBlockInputSchema = z.object({
|
|
22
|
+
field: fieldTypeSchema,
|
|
23
|
+
terms: termBlockInputSchema,
|
|
24
|
+
operator: operatorSchema
|
|
25
|
+
});
|
|
26
|
+
const queryFileInputSchema = z.object({
|
|
27
|
+
name: z.string().min(1),
|
|
28
|
+
description: z.string().optional(),
|
|
29
|
+
query: z.array(queryBlockInputSchema).min(1),
|
|
30
|
+
filters: filtersInputSchema,
|
|
31
|
+
overrides: z.object({
|
|
32
|
+
pubmed: overrideBlockInputSchema,
|
|
33
|
+
scopus: overrideBlockInputSchema,
|
|
34
|
+
eric: overrideBlockInputSchema,
|
|
35
|
+
arxiv: overrideBlockInputSchema,
|
|
36
|
+
wos: overrideBlockInputSchema,
|
|
37
|
+
embase: overrideBlockInputSchema
|
|
38
|
+
}).optional()
|
|
39
|
+
});
|
|
40
|
+
function generateQueryJSONSchema() {
|
|
41
|
+
return z.toJSONSchema(queryFileInputSchema, {
|
|
42
|
+
target: "draft-2020-12"
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
generateQueryJSONSchema
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=json-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-schema.js","sources":["../../src/query/json-schema.ts"],"sourcesContent":["/**\n * JSON Schema generation for query YAML files.\n *\n * Defines an input-only schema (without transforms) that mirrors the structure\n * of queryFileSchema from validator.ts. This is necessary because Zod v4's\n * z.toJSONSchema() cannot handle .transform() calls.\n *\n * The generated JSON Schema enables editor autocompletion and validation\n * via the yaml-language-server $schema comment.\n */\nimport * as z from 'zod';\nimport {\n fieldTypeSchema,\n operatorSchema,\n publicationTypeFilterSchema,\n} from './validator.js';\n\n/** Filters input schema (without transform) */\nconst filtersInputSchema = z\n .object({\n year_from: z.number().int().optional(),\n year_to: z.number().int().optional(),\n language: z.array(z.string()).optional(),\n publication_types: publicationTypeFilterSchema.optional(),\n })\n .optional();\n\n/** Override block input schema (without transform) */\nconst overrideBlockInputSchema = z\n .object({\n filters: filtersInputSchema,\n categories: z.array(z.string()).optional(),\n source_types: z.array(z.string()).optional(),\n })\n .optional();\n\n/** Term block input schema (without refine) */\nconst termBlockInputSchema = z.object({\n keywords: z.array(z.string()).min(1).optional(),\n mesh: z.array(z.string()).optional(),\n emtree: z.array(z.string()).optional(),\n eric: z.array(z.string()).optional(),\n exclude: z.array(z.string()).optional(),\n});\n\n/** Query block input schema */\nconst queryBlockInputSchema = z.object({\n field: fieldTypeSchema,\n terms: termBlockInputSchema,\n operator: operatorSchema,\n});\n\n/** Query file input schema (without transform) - mirrors queryFileSchema input */\nconst queryFileInputSchema = z.object({\n name: z.string().min(1),\n description: z.string().optional(),\n query: z.array(queryBlockInputSchema).min(1),\n filters: filtersInputSchema,\n overrides: z\n .object({\n pubmed: overrideBlockInputSchema,\n scopus: overrideBlockInputSchema,\n eric: overrideBlockInputSchema,\n arxiv: overrideBlockInputSchema,\n wos: overrideBlockInputSchema,\n embase: overrideBlockInputSchema,\n })\n .optional(),\n});\n\n/** Generate a JSON Schema from the query file Zod schema. */\nexport function generateQueryJSONSchema(): Record<string, unknown> {\n return z.toJSONSchema(queryFileInputSchema, {\n target: 'draft-2020-12',\n });\n}\n"],"names":[],"mappings":";;AAkBA,MAAM,qBAAqB,EACxB,OAAO;AAAA,EACN,WAAW,EAAE,OAAA,EAAS,IAAA,EAAM,SAAA;AAAA,EAC5B,SAAS,EAAE,OAAA,EAAS,IAAA,EAAM,SAAA;AAAA,EAC1B,UAAU,EAAE,MAAM,EAAE,OAAA,CAAQ,EAAE,SAAA;AAAA,EAC9B,mBAAmB,4BAA4B,SAAA;AACjD,CAAC,EACA,SAAA;AAGH,MAAM,2BAA2B,EAC9B,OAAO;AAAA,EACN,SAAS;AAAA,EACT,YAAY,EAAE,MAAM,EAAE,OAAA,CAAQ,EAAE,SAAA;AAAA,EAChC,cAAc,EAAE,MAAM,EAAE,OAAA,CAAQ,EAAE,SAAA;AACpC,CAAC,EACA,SAAA;AAGH,MAAM,uBAAuB,EAAE,OAAO;AAAA,EACpC,UAAU,EAAE,MAAM,EAAE,OAAA,CAAQ,EAAE,IAAI,CAAC,EAAE,SAAA;AAAA,EACrC,MAAM,EAAE,MAAM,EAAE,OAAA,CAAQ,EAAE,SAAA;AAAA,EAC1B,QAAQ,EAAE,MAAM,EAAE,OAAA,CAAQ,EAAE,SAAA;AAAA,EAC5B,MAAM,EAAE,MAAM,EAAE,OAAA,CAAQ,EAAE,SAAA;AAAA,EAC1B,SAAS,EAAE,MAAM,EAAE,OAAA,CAAQ,EAAE,SAAA;AAC/B,CAAC;AAGD,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU;AACZ,CAAC;AAGD,MAAM,uBAAuB,EAAE,OAAO;AAAA,EACpC,MAAM,EAAE,SAAS,IAAI,CAAC;AAAA,EACtB,aAAa,EAAE,OAAA,EAAS,SAAA;AAAA,EACxB,OAAO,EAAE,MAAM,qBAAqB,EAAE,IAAI,CAAC;AAAA,EAC3C,SAAS;AAAA,EACT,WAAW,EACR,OAAO;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,IACL,QAAQ;AAAA,EAAA,CACT,EACA,SAAA;AACL,CAAC;AAGM,SAAS,0BAAmD;AACjE,SAAO,EAAE,aAAa,sBAAsB;AAAA,IAC1C,QAAQ;AAAA,EAAA,CACT;AACH;"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { RateLimiter } from '../providers/base/rate-limiter.js';
|
|
2
|
+
import { VocabCache } from './vocab-cache.js';
|
|
3
|
+
/**
|
|
4
|
+
* Result of a MeSH term lookup.
|
|
5
|
+
*/
|
|
6
|
+
export interface MeSHLookupResult {
|
|
7
|
+
/** The term that was looked up */
|
|
8
|
+
term: string;
|
|
9
|
+
/** Whether the term was found as a valid MeSH heading */
|
|
10
|
+
found: boolean;
|
|
11
|
+
/** Suggested terms if the lookup term was not found */
|
|
12
|
+
suggestions?: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Client for the NLM MeSH Lookup API.
|
|
16
|
+
*/
|
|
17
|
+
export declare class MeSHLookupClient {
|
|
18
|
+
private readonly rateLimiter;
|
|
19
|
+
private readonly timeoutMs;
|
|
20
|
+
private readonly cache;
|
|
21
|
+
constructor(options?: {
|
|
22
|
+
rateLimiter?: RateLimiter;
|
|
23
|
+
timeoutMs?: number;
|
|
24
|
+
cache?: VocabCache;
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* Look up a single MeSH term.
|
|
28
|
+
*
|
|
29
|
+
* Tries multiple match strategies in order:
|
|
30
|
+
* 1. exact — exact match
|
|
31
|
+
* 2. startsWith (full term) — prefix match
|
|
32
|
+
* 2b. startsWith (truncated) — suffix typo recovery (1-3 chars removed)
|
|
33
|
+
* 2c. startsWith (word1 + word2 prefix) — multi-word progressive prefix (max 3 calls)
|
|
34
|
+
* 3. contains (full term) — substring match
|
|
35
|
+
* 4. startsWith (first word, limit=25) — re-ranked by Levenshtein distance
|
|
36
|
+
*
|
|
37
|
+
* Returns on the first strategy that produces results.
|
|
38
|
+
* Results are cached when a VocabCache is provided.
|
|
39
|
+
*/
|
|
40
|
+
lookupTerm(term: string): Promise<MeSHLookupResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Look up multiple MeSH terms.
|
|
43
|
+
*/
|
|
44
|
+
lookupTerms(terms: string[]): Promise<MeSHLookupResult[]>;
|
|
45
|
+
private fetchLookup;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=mesh-lookup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mesh-lookup.d.ts","sourceRoot":"","sources":["../../src/query/mesh-lookup.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAMnD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,KAAK,EAAE,OAAO,CAAC;IACf,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAOD;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0B;IACtD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;gBAEnC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,WAAW,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,UAAU,CAAA;KAAE;IAM3F;;;;;;;;;;;;;OAaG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA4GzD;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;YAQjD,WAAW;CAoC1B"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { levenshteinDistance } from "../utils/levenshtein.js";
|
|
2
|
+
const MESH_LOOKUP_BASE_URL = "https://id.nlm.nih.gov/mesh/lookup/term";
|
|
3
|
+
const DEFAULT_TIMEOUT_MS = 1e4;
|
|
4
|
+
class MeSHLookupClient {
|
|
5
|
+
rateLimiter;
|
|
6
|
+
timeoutMs;
|
|
7
|
+
cache;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.rateLimiter = options?.rateLimiter;
|
|
10
|
+
this.timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
11
|
+
this.cache = options?.cache;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Look up a single MeSH term.
|
|
15
|
+
*
|
|
16
|
+
* Tries multiple match strategies in order:
|
|
17
|
+
* 1. exact — exact match
|
|
18
|
+
* 2. startsWith (full term) — prefix match
|
|
19
|
+
* 2b. startsWith (truncated) — suffix typo recovery (1-3 chars removed)
|
|
20
|
+
* 2c. startsWith (word1 + word2 prefix) — multi-word progressive prefix (max 3 calls)
|
|
21
|
+
* 3. contains (full term) — substring match
|
|
22
|
+
* 4. startsWith (first word, limit=25) — re-ranked by Levenshtein distance
|
|
23
|
+
*
|
|
24
|
+
* Returns on the first strategy that produces results.
|
|
25
|
+
* Results are cached when a VocabCache is provided.
|
|
26
|
+
*/
|
|
27
|
+
async lookupTerm(term) {
|
|
28
|
+
if (this.cache) {
|
|
29
|
+
const cached = this.cache.get("mesh", term);
|
|
30
|
+
if (cached) {
|
|
31
|
+
return cached;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const exactResults = await this.fetchLookup(term, "exact", 1);
|
|
35
|
+
if (exactResults.length > 0) {
|
|
36
|
+
const result2 = { term, found: true };
|
|
37
|
+
this.cache?.set("mesh", term, result2);
|
|
38
|
+
return result2;
|
|
39
|
+
}
|
|
40
|
+
const startsWithResults = await this.fetchLookup(term, "startswith", 5);
|
|
41
|
+
if (startsWithResults.length > 0) {
|
|
42
|
+
const result2 = {
|
|
43
|
+
term,
|
|
44
|
+
found: false,
|
|
45
|
+
suggestions: startsWithResults.map((s) => s.label)
|
|
46
|
+
};
|
|
47
|
+
this.cache?.set("mesh", term, result2);
|
|
48
|
+
return result2;
|
|
49
|
+
}
|
|
50
|
+
if (term.length > 3) {
|
|
51
|
+
for (let len = term.length - 1; len >= Math.max(term.length - 3, 3); len--) {
|
|
52
|
+
const truncated = term.slice(0, len);
|
|
53
|
+
const truncatedResults = await this.fetchLookup(truncated, "startswith", 5);
|
|
54
|
+
if (truncatedResults.length > 0) {
|
|
55
|
+
const result2 = {
|
|
56
|
+
term,
|
|
57
|
+
found: false,
|
|
58
|
+
suggestions: truncatedResults.map((s) => s.label)
|
|
59
|
+
};
|
|
60
|
+
this.cache?.set("mesh", term, result2);
|
|
61
|
+
return result2;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const words = term.split(/\s+/);
|
|
66
|
+
if (words.length >= 2 && words[1].length > 3) {
|
|
67
|
+
const startN = Math.min(words[1].length - 4, words[1].length - 1);
|
|
68
|
+
const endN = 3;
|
|
69
|
+
let iterations = 0;
|
|
70
|
+
for (let n = startN; n >= endN && iterations < 3; n--, iterations++) {
|
|
71
|
+
const prefix = words[0] + " " + words[1].slice(0, n);
|
|
72
|
+
const prefixResults = await this.fetchLookup(prefix, "startswith", 5);
|
|
73
|
+
if (prefixResults.length > 0) {
|
|
74
|
+
const result2 = {
|
|
75
|
+
term,
|
|
76
|
+
found: false,
|
|
77
|
+
suggestions: prefixResults.map((s) => s.label)
|
|
78
|
+
};
|
|
79
|
+
this.cache?.set("mesh", term, result2);
|
|
80
|
+
return result2;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const containsResults = await this.fetchLookup(term, "contains", 5);
|
|
85
|
+
if (containsResults.length > 0) {
|
|
86
|
+
const result2 = {
|
|
87
|
+
term,
|
|
88
|
+
found: false,
|
|
89
|
+
suggestions: containsResults.map((s) => s.label)
|
|
90
|
+
};
|
|
91
|
+
this.cache?.set("mesh", term, result2);
|
|
92
|
+
return result2;
|
|
93
|
+
}
|
|
94
|
+
if (words.length > 1) {
|
|
95
|
+
const firstWord = words[0];
|
|
96
|
+
const firstWordResults = await this.fetchLookup(firstWord, "startswith", 25);
|
|
97
|
+
if (firstWordResults.length > 0) {
|
|
98
|
+
const ranked = firstWordResults.map((s) => ({
|
|
99
|
+
label: s.label,
|
|
100
|
+
distance: levenshteinDistance(term.toLowerCase(), s.label.toLowerCase())
|
|
101
|
+
})).sort((a, b) => a.distance - b.distance).slice(0, 5).map((s) => s.label);
|
|
102
|
+
const result2 = { term, found: false, suggestions: ranked };
|
|
103
|
+
this.cache?.set("mesh", term, result2);
|
|
104
|
+
return result2;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const result = { term, found: false };
|
|
108
|
+
this.cache?.set("mesh", term, result);
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Look up multiple MeSH terms.
|
|
113
|
+
*/
|
|
114
|
+
async lookupTerms(terms) {
|
|
115
|
+
const results = [];
|
|
116
|
+
for (const term of terms) {
|
|
117
|
+
results.push(await this.lookupTerm(term));
|
|
118
|
+
}
|
|
119
|
+
return results;
|
|
120
|
+
}
|
|
121
|
+
async fetchLookup(label, match, limit) {
|
|
122
|
+
if (this.rateLimiter) {
|
|
123
|
+
await this.rateLimiter.acquire();
|
|
124
|
+
}
|
|
125
|
+
const params = new URLSearchParams({
|
|
126
|
+
label,
|
|
127
|
+
match,
|
|
128
|
+
limit: String(limit)
|
|
129
|
+
});
|
|
130
|
+
const url = `${MESH_LOOKUP_BASE_URL}?${params.toString()}`;
|
|
131
|
+
let response;
|
|
132
|
+
try {
|
|
133
|
+
response = await fetch(url, {
|
|
134
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
135
|
+
});
|
|
136
|
+
} catch (error) {
|
|
137
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
138
|
+
throw new Error(`MeSH lookup failed: ${message}`);
|
|
139
|
+
}
|
|
140
|
+
if (!response.ok) {
|
|
141
|
+
throw new Error(
|
|
142
|
+
`MeSH lookup failed: HTTP ${response.status} ${response.statusText}`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
return await response.json();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
export {
|
|
149
|
+
MeSHLookupClient
|
|
150
|
+
};
|
|
151
|
+
//# sourceMappingURL=mesh-lookup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mesh-lookup.js","sources":["../../src/query/mesh-lookup.ts"],"sourcesContent":["/**\n * MeSH Lookup API client.\n *\n * Validates MeSH (Medical Subject Headings) terms against the NLM MeSH Lookup API.\n * No API key required.\n *\n * API docs: https://id.nlm.nih.gov/mesh/lookup/term\n */\n\nimport type { RateLimiter } from '../providers/base/rate-limiter.js';\nimport type { VocabCache } from './vocab-cache.js';\nimport { levenshteinDistance } from '../utils/levenshtein.js';\n\nconst MESH_LOOKUP_BASE_URL = 'https://id.nlm.nih.gov/mesh/lookup/term';\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\n/**\n * Result of a MeSH term lookup.\n */\nexport interface MeSHLookupResult {\n /** The term that was looked up */\n term: string;\n /** Whether the term was found as a valid MeSH heading */\n found: boolean;\n /** Suggested terms if the lookup term was not found */\n suggestions?: string[];\n}\n\ninterface MeSHApiEntry {\n resource: string;\n label: string;\n}\n\n/**\n * Client for the NLM MeSH Lookup API.\n */\nexport class MeSHLookupClient {\n private readonly rateLimiter: RateLimiter | undefined;\n private readonly timeoutMs: number;\n private readonly cache: VocabCache | undefined;\n\n constructor(options?: { rateLimiter?: RateLimiter; timeoutMs?: number; cache?: VocabCache }) {\n this.rateLimiter = options?.rateLimiter;\n this.timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.cache = options?.cache;\n }\n\n /**\n * Look up a single MeSH term.\n *\n * Tries multiple match strategies in order:\n * 1. exact — exact match\n * 2. startsWith (full term) — prefix match\n * 2b. startsWith (truncated) — suffix typo recovery (1-3 chars removed)\n * 2c. startsWith (word1 + word2 prefix) — multi-word progressive prefix (max 3 calls)\n * 3. contains (full term) — substring match\n * 4. startsWith (first word, limit=25) — re-ranked by Levenshtein distance\n *\n * Returns on the first strategy that produces results.\n * Results are cached when a VocabCache is provided.\n */\n async lookupTerm(term: string): Promise<MeSHLookupResult> {\n // Check cache first\n if (this.cache) {\n const cached = this.cache.get('mesh', term);\n if (cached) {\n return cached;\n }\n }\n\n // 1. Try exact match first\n const exactResults = await this.fetchLookup(term, 'exact', 1);\n\n if (exactResults.length > 0) {\n const result: MeSHLookupResult = { term, found: true };\n this.cache?.set('mesh', term, result);\n return result;\n }\n\n // 2. Try startsWith (full term) for suggestions\n const startsWithResults = await this.fetchLookup(term, 'startswith', 5);\n\n if (startsWithResults.length > 0) {\n const result: MeSHLookupResult = {\n term,\n found: false,\n suggestions: startsWithResults.map((s) => s.label),\n };\n this.cache?.set('mesh', term, result);\n return result;\n }\n\n // 2b. Try startsWith with progressively shorter input (handles suffix typos)\n if (term.length > 3) {\n for (let len = term.length - 1; len >= Math.max(term.length - 3, 3); len--) {\n const truncated = term.slice(0, len);\n const truncatedResults = await this.fetchLookup(truncated, 'startswith', 5);\n if (truncatedResults.length > 0) {\n const result: MeSHLookupResult = {\n term,\n found: false,\n suggestions: truncatedResults.map((s) => s.label),\n };\n this.cache?.set('mesh', term, result);\n return result;\n }\n }\n }\n\n // 2c. Multi-word progressive prefix: try word1 + word2.slice(0, N)\n const words = term.split(/\\s+/);\n if (words.length >= 2 && words[1]!.length > 3) {\n const startN = Math.min(words[1]!.length - 4, words[1]!.length - 1);\n const endN = 3;\n let iterations = 0;\n for (let n = startN; n >= endN && iterations < 3; n--, iterations++) {\n const prefix = words[0]! + ' ' + words[1]!.slice(0, n);\n const prefixResults = await this.fetchLookup(prefix, 'startswith', 5);\n if (prefixResults.length > 0) {\n const result: MeSHLookupResult = {\n term,\n found: false,\n suggestions: prefixResults.map((s) => s.label),\n };\n this.cache?.set('mesh', term, result);\n return result;\n }\n }\n }\n\n // 3. Try contains (full term) for typos and variant spellings\n const containsResults = await this.fetchLookup(term, 'contains', 5);\n\n if (containsResults.length > 0) {\n const result: MeSHLookupResult = {\n term,\n found: false,\n suggestions: containsResults.map((s) => s.label),\n };\n this.cache?.set('mesh', term, result);\n return result;\n }\n\n // 4. Try startsWith with first word only (for multi-word terms)\n // Fetch up to 25 results and re-rank by Levenshtein distance\n if (words.length > 1) {\n const firstWord = words[0]!;\n const firstWordResults = await this.fetchLookup(firstWord, 'startswith', 25);\n\n if (firstWordResults.length > 0) {\n const ranked = firstWordResults\n .map((s) => ({\n label: s.label,\n distance: levenshteinDistance(term.toLowerCase(), s.label.toLowerCase()),\n }))\n .sort((a, b) => a.distance - b.distance)\n .slice(0, 5)\n .map((s) => s.label);\n const result: MeSHLookupResult = { term, found: false, suggestions: ranked };\n this.cache?.set('mesh', term, result);\n return result;\n }\n }\n\n const result: MeSHLookupResult = { term, found: false };\n this.cache?.set('mesh', term, result);\n return result;\n }\n\n /**\n * Look up multiple MeSH terms.\n */\n async lookupTerms(terms: string[]): Promise<MeSHLookupResult[]> {\n const results: MeSHLookupResult[] = [];\n for (const term of terms) {\n results.push(await this.lookupTerm(term));\n }\n return results;\n }\n\n private async fetchLookup(\n label: string,\n match: 'exact' | 'startswith' | 'contains',\n limit: number\n ): Promise<MeSHApiEntry[]> {\n if (this.rateLimiter) {\n await this.rateLimiter.acquire();\n }\n\n const params = new URLSearchParams({\n label,\n match,\n limit: String(limit),\n });\n\n const url = `${MESH_LOOKUP_BASE_URL}?${params.toString()}`;\n\n let response: Response;\n try {\n response = await fetch(url, {\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n } catch (error) {\n const message =\n error instanceof Error ? error.message : 'Unknown error';\n throw new Error(`MeSH lookup failed: ${message}`);\n }\n\n if (!response.ok) {\n throw new Error(\n `MeSH lookup failed: HTTP ${response.status} ${response.statusText}`\n );\n }\n\n return (await response.json()) as MeSHApiEntry[];\n }\n}\n"],"names":["result"],"mappings":"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"}
|
package/dist/query/parser.js
CHANGED
|
@@ -12,7 +12,7 @@ async function parseQueryFile(filePath) {
|
|
|
12
12
|
function detectShortKeywords(ast, threshold = 3) {
|
|
13
13
|
const shortKeywords = /* @__PURE__ */ new Set();
|
|
14
14
|
for (const block of ast.blocks) {
|
|
15
|
-
for (const keyword of block.terms.keywords) {
|
|
15
|
+
for (const keyword of block.terms.keywords ?? []) {
|
|
16
16
|
if (keyword.length <= threshold) {
|
|
17
17
|
shortKeywords.add(keyword);
|
|
18
18
|
}
|
package/dist/query/parser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.js","sources":["../../src/query/parser.ts"],"sourcesContent":["/**\n * Query YAML Parser\n *\n * Parses YAML query files into validated QueryAST.\n * See spec/models/query-dsl.md for the full specification.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { parse } from 'yaml';\nimport type { QueryAST } from './types.js';\nimport { validateQueryFile } from './validator.js';\n\n/**\n * Parse a YAML string into a validated QueryAST.\n *\n * @param yaml - YAML string to parse\n * @returns Validated QueryAST\n * @throws Error if YAML is invalid or doesn't match schema\n */\nexport function parseQueryString(yaml: string): QueryAST {\n const data = parse(yaml);\n return validateQueryFile(data);\n}\n\n/**\n * Parse a YAML file into a validated QueryAST.\n *\n * @param filePath - Path to the YAML file\n * @returns Promise resolving to validated QueryAST\n * @throws Error if file doesn't exist, YAML is invalid, or doesn't match schema\n */\nexport async function parseQueryFile(filePath: string): Promise<QueryAST> {\n const content = await readFile(filePath, 'utf-8');\n return parseQueryString(content);\n}\n\n\n/**\n * Detect short keywords (potential acronyms) in a query.\n *\n * Short keywords (3 characters or fewer by default) may match unrelated\n * acronyms in different fields, producing noisy results.\n *\n * @param ast - Parsed QueryAST\n * @param threshold - Maximum length to consider \"short\" (default: 3)\n * @returns Array of unique short keywords found\n */\nexport function detectShortKeywords(ast: QueryAST, threshold = 3): string[] {\n const shortKeywords = new Set<string>();\n\n for (const block of ast.blocks) {\n for (const keyword of block.terms.keywords) {\n if (keyword.length <= threshold) {\n shortKeywords.add(keyword);\n }\n }\n }\n\n return Array.from(shortKeywords);\n}\n"],"names":[],"mappings":";;;AAmBO,SAAS,iBAAiB,MAAwB;AACvD,QAAM,OAAO,MAAM,IAAI;AACvB,SAAO,kBAAkB,IAAI;AAC/B;AASA,eAAsB,eAAe,UAAqC;AACxE,QAAM,UAAU,MAAM,SAAS,UAAU,OAAO;AAChD,SAAO,iBAAiB,OAAO;AACjC;AAaO,SAAS,oBAAoB,KAAe,YAAY,GAAa;AAC1E,QAAM,oCAAoB,IAAA;AAE1B,aAAW,SAAS,IAAI,QAAQ;AAC9B,eAAW,WAAW,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"parser.js","sources":["../../src/query/parser.ts"],"sourcesContent":["/**\n * Query YAML Parser\n *\n * Parses YAML query files into validated QueryAST.\n * See spec/models/query-dsl.md for the full specification.\n */\n\nimport { readFile } from 'node:fs/promises';\nimport { parse } from 'yaml';\nimport type { QueryAST } from './types.js';\nimport { validateQueryFile } from './validator.js';\n\n/**\n * Parse a YAML string into a validated QueryAST.\n *\n * @param yaml - YAML string to parse\n * @returns Validated QueryAST\n * @throws Error if YAML is invalid or doesn't match schema\n */\nexport function parseQueryString(yaml: string): QueryAST {\n const data = parse(yaml);\n return validateQueryFile(data);\n}\n\n/**\n * Parse a YAML file into a validated QueryAST.\n *\n * @param filePath - Path to the YAML file\n * @returns Promise resolving to validated QueryAST\n * @throws Error if file doesn't exist, YAML is invalid, or doesn't match schema\n */\nexport async function parseQueryFile(filePath: string): Promise<QueryAST> {\n const content = await readFile(filePath, 'utf-8');\n return parseQueryString(content);\n}\n\n\n/**\n * Detect short keywords (potential acronyms) in a query.\n *\n * Short keywords (3 characters or fewer by default) may match unrelated\n * acronyms in different fields, producing noisy results.\n *\n * @param ast - Parsed QueryAST\n * @param threshold - Maximum length to consider \"short\" (default: 3)\n * @returns Array of unique short keywords found\n */\nexport function detectShortKeywords(ast: QueryAST, threshold = 3): string[] {\n const shortKeywords = new Set<string>();\n\n for (const block of ast.blocks) {\n for (const keyword of block.terms.keywords ?? []) {\n if (keyword.length <= threshold) {\n shortKeywords.add(keyword);\n }\n }\n }\n\n return Array.from(shortKeywords);\n}\n"],"names":[],"mappings":";;;AAmBO,SAAS,iBAAiB,MAAwB;AACvD,QAAM,OAAO,MAAM,IAAI;AACvB,SAAO,kBAAkB,IAAI;AAC/B;AASA,eAAsB,eAAe,UAAqC;AACxE,QAAM,UAAU,MAAM,SAAS,UAAU,OAAO;AAChD,SAAO,iBAAiB,OAAO;AACjC;AAaO,SAAS,oBAAoB,KAAe,YAAY,GAAa;AAC1E,QAAM,oCAAoB,IAAA;AAE1B,aAAW,SAAS,IAAI,QAAQ;AAC9B,eAAW,WAAW,MAAM,MAAM,YAAY,CAAA,GAAI;AAChD,UAAI,QAAQ,UAAU,WAAW;AAC/B,sBAAc,IAAI,OAAO;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,aAAa;AACjC;"}
|
package/dist/query/types.d.ts
CHANGED
|
@@ -15,10 +15,10 @@ export type Operator = 'AND' | 'OR';
|
|
|
15
15
|
*/
|
|
16
16
|
export interface TermBlock {
|
|
17
17
|
/** Free-text keywords (supported by all databases) */
|
|
18
|
-
keywords
|
|
18
|
+
keywords?: string[] | undefined;
|
|
19
19
|
/** MeSH terms (PubMed only) */
|
|
20
20
|
mesh?: string[] | undefined;
|
|
21
|
-
/** Emtree terms (Embase
|
|
21
|
+
/** Emtree terms (Embase/Scopus) */
|
|
22
22
|
emtree?: string[] | undefined;
|
|
23
23
|
/** ERIC Descriptors (ERIC only) */
|
|
24
24
|
eric?: string[] | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/query/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B;;;GAGG;AACH,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,UAAU,GACV,gBAAgB,GAChB,QAAQ,GACR,SAAS,GACT,KAAK,CAAC;AAEV;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,IAAI,CAAC;AAEpC;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/query/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B;;;GAGG;AACH,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,UAAU,GACV,gBAAgB,GAChB,QAAQ,GACR,SAAS,GACT,KAAK,CAAC;AAEV;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,IAAI,CAAC;AAEpC;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAChC,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5B,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC9B,mCAAmC;IACnC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5B,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,kCAAkC;IAClC,KAAK,EAAE,SAAS,CAAC;IACjB,mBAAmB;IACnB,KAAK,EAAE,SAAS,CAAC;IACjB,6CAA6C;IAC7C,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC/B,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACjC,+BAA+B;IAC/B,gBAAgB,CAAC,EAAE,qBAAqB,GAAG,SAAS,CAAC;CACtD;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAClC,iCAAiC;IACjC,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,4CAA4C;IAC5C,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,qBAAqB;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,kCAAkC;IAClC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,aAAa,GAAG,SAAS,CAAC,CAAC,CAAC;CACrE"}
|
|
@@ -15,7 +15,7 @@ export declare const fieldTypeSchema: z.ZodEnum<{
|
|
|
15
15
|
* Schema for term block containing search terms.
|
|
16
16
|
*/
|
|
17
17
|
export declare const termBlockSchema: z.ZodObject<{
|
|
18
|
-
keywords: z.ZodArray<z.ZodString
|
|
18
|
+
keywords: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
19
19
|
mesh: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
20
20
|
emtree: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
21
21
|
eric: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -41,7 +41,7 @@ export declare const queryBlockSchema: z.ZodObject<{
|
|
|
41
41
|
all: "all";
|
|
42
42
|
}>;
|
|
43
43
|
terms: z.ZodObject<{
|
|
44
|
-
keywords: z.ZodArray<z.ZodString
|
|
44
|
+
keywords: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
45
45
|
mesh: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
46
46
|
emtree: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
47
47
|
eric: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -156,7 +156,7 @@ export declare const queryFileSchema: z.ZodPipe<z.ZodObject<{
|
|
|
156
156
|
all: "all";
|
|
157
157
|
}>;
|
|
158
158
|
terms: z.ZodObject<{
|
|
159
|
-
keywords: z.ZodArray<z.ZodString
|
|
159
|
+
keywords: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
160
160
|
mesh: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
161
161
|
emtree: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
162
162
|
eric: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -422,7 +422,7 @@ export declare const queryFileSchema: z.ZodPipe<z.ZodObject<{
|
|
|
422
422
|
blocks: {
|
|
423
423
|
field: "title" | "abstract" | "title_abstract" | "author" | "keyword" | "all";
|
|
424
424
|
terms: {
|
|
425
|
-
keywords
|
|
425
|
+
keywords?: string[] | undefined;
|
|
426
426
|
mesh?: string[] | undefined;
|
|
427
427
|
emtree?: string[] | undefined;
|
|
428
428
|
eric?: string[] | undefined;
|
|
@@ -524,7 +524,7 @@ export declare const queryFileSchema: z.ZodPipe<z.ZodObject<{
|
|
|
524
524
|
query: {
|
|
525
525
|
field: "title" | "abstract" | "title_abstract" | "author" | "keyword" | "all";
|
|
526
526
|
terms: {
|
|
527
|
-
keywords
|
|
527
|
+
keywords?: string[] | undefined;
|
|
528
528
|
mesh?: string[] | undefined;
|
|
529
529
|
emtree?: string[] | undefined;
|
|
530
530
|
eric?: string[] | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/query/validator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;;EAO1B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/query/validator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;;EAO1B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;iBAS3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc;;;EAAwB,CAAC;AAEpD;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;iBAI3B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,2BAA2B;;;iBAGtC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;GAcrB,CAAC;AAEN;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwB3B,CAAC;AAEN;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;;;EAO7B,CAAC;AAiBH;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAcvB,CAAC;AAEN;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAEzD;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;aAEtB,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM;CAKlB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,OAAO,GAAG,eAAe,EAAE,CAWvE"}
|
package/dist/query/validator.js
CHANGED
|
@@ -8,12 +8,15 @@ const fieldTypeSchema = z.enum([
|
|
|
8
8
|
"all"
|
|
9
9
|
]);
|
|
10
10
|
const termBlockSchema = z.object({
|
|
11
|
-
keywords: z.array(z.string()).min(1),
|
|
11
|
+
keywords: z.array(z.string()).min(1).optional(),
|
|
12
12
|
mesh: z.array(z.string()).optional(),
|
|
13
13
|
emtree: z.array(z.string()).optional(),
|
|
14
14
|
eric: z.array(z.string()).optional(),
|
|
15
15
|
exclude: z.array(z.string()).optional()
|
|
16
|
-
})
|
|
16
|
+
}).refine(
|
|
17
|
+
(data) => data.keywords?.length || data.mesh?.length || data.emtree?.length || data.eric?.length,
|
|
18
|
+
{ message: "At least one of keywords, mesh, emtree, or eric is required" }
|
|
19
|
+
);
|
|
17
20
|
const operatorSchema = z.enum(["AND", "OR"]);
|
|
18
21
|
const queryBlockSchema = z.object({
|
|
19
22
|
field: fieldTypeSchema,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.js","sources":["../../src/query/validator.ts"],"sourcesContent":["/**\n * Query Schema Validation using Zod\n *\n * Validates query YAML structure against the query DSL schema.\n * See spec/models/query-dsl.md for the full specification.\n */\n\nimport { z } from 'zod';\nimport type { QueryAST } from './types.js';\n\n/**\n * Schema for field types.\n */\nexport const fieldTypeSchema = z.enum([\n 'title',\n 'abstract',\n 'title_abstract',\n 'author',\n 'keyword',\n 'all',\n]);\n\n/**\n * Schema for term block containing search terms.\n */\nexport const termBlockSchema = z.object({\n keywords: z.array(z.string()).min(1),\n mesh: z.array(z.string()).optional(),\n emtree: z.array(z.string()).optional(),\n eric: z.array(z.string()).optional(),\n exclude: z.array(z.string()).optional(),\n});\n\n/**\n * Schema for operator.\n */\nexport const operatorSchema = z.enum(['AND', 'OR']);\n\n/**\n * Schema for query block.\n */\nexport const queryBlockSchema = z.object({\n field: fieldTypeSchema,\n terms: termBlockSchema,\n operator: operatorSchema,\n});\n\n/**\n * Schema for publication type filter.\n */\nexport const publicationTypeFilterSchema = z.object({\n include: z.array(z.string()).optional(),\n exclude: z.array(z.string()).optional(),\n});\n\n/**\n * Schema for filters (YAML input format with snake_case).\n * Transforms to camelCase for internal use.\n */\nexport const filtersSchema = z\n .object({\n year_from: z.number().int().optional(),\n year_to: z.number().int().optional(),\n language: z.array(z.string()).optional(),\n publication_types: publicationTypeFilterSchema.optional(),\n })\n .optional()\n .default({})\n .transform((data) => ({\n yearFrom: data.year_from,\n yearTo: data.year_to,\n languages: data.language,\n publicationTypes: data.publication_types,\n }));\n\n/**\n * Schema for override block (YAML input format).\n */\nexport const overrideBlockSchema = z\n .object({\n filters: z\n .object({\n year_from: z.number().int().optional(),\n year_to: z.number().int().optional(),\n language: z.array(z.string()).optional(),\n publication_types: publicationTypeFilterSchema.optional(),\n })\n .optional(),\n categories: z.array(z.string()).optional(),\n source_types: z.array(z.string()).optional(),\n })\n .transform((data) => ({\n filters: data.filters\n ? {\n yearFrom: data.filters.year_from,\n yearTo: data.filters.year_to,\n languages: data.filters.language,\n publicationTypes: data.filters.publication_types,\n }\n : undefined,\n categories: data.categories,\n sourceTypes: data.source_types,\n }));\n\n/**\n * Provider names schema.\n */\nexport const providerNameSchema = z.enum([\n 'pubmed',\n 'scopus',\n 'eric',\n 'arxiv',\n 'wos',\n 'embase',\n]);\n\n/**\n * Schema for overrides object (partial record of provider -> override).\n */\nconst overridesSchema = z\n .object({\n pubmed: overrideBlockSchema.optional(),\n scopus: overrideBlockSchema.optional(),\n eric: overrideBlockSchema.optional(),\n arxiv: overrideBlockSchema.optional(),\n wos: overrideBlockSchema.optional(),\n embase: overrideBlockSchema.optional(),\n })\n .optional()\n .default({});\n\n/**\n * Schema for the complete query file (YAML input format).\n * Transforms to QueryAST for internal use.\n */\nexport const queryFileSchema = z\n .object({\n name: z.string().min(1),\n description: z.string().optional(),\n query: z.array(queryBlockSchema).min(1),\n filters: filtersSchema,\n overrides: overridesSchema,\n })\n .transform((data) => ({\n name: data.name,\n description: data.description,\n blocks: data.query,\n filters: data.filters,\n overrides: data.overrides,\n }));\n\n/**\n * Validate a parsed YAML object against the query schema.\n * Returns a validated QueryAST.\n */\nexport function validateQueryFile(data: unknown): QueryAST {\n return queryFileSchema.parse(data);\n}\n\n/**\n * Validation error with path information.\n */\nexport class ValidationError extends Error {\n constructor(\n public readonly path: string,\n message: string\n ) {\n super(message);\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Format Zod validation errors into an array of ValidationError objects.\n *\n * @param data - The data to validate\n * @returns Array of ValidationError objects (empty if valid)\n */\nexport function formatValidationErrors(data: unknown): ValidationError[] {\n const result = queryFileSchema.safeParse(data);\n\n if (result.success) {\n return [];\n }\n\n return result.error.issues.map((issue) => {\n const path = issue.path.join('.');\n return new ValidationError(path, issue.message);\n });\n}\n"],"names":[],"mappings":";AAaO,MAAM,kBAAkB,EAAE,KAAK;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAKM,MAAM,kBAAkB,EAAE,OAAO;AAAA,EACtC,UAAU,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"validator.js","sources":["../../src/query/validator.ts"],"sourcesContent":["/**\n * Query Schema Validation using Zod\n *\n * Validates query YAML structure against the query DSL schema.\n * See spec/models/query-dsl.md for the full specification.\n */\n\nimport { z } from 'zod';\nimport type { QueryAST } from './types.js';\n\n/**\n * Schema for field types.\n */\nexport const fieldTypeSchema = z.enum([\n 'title',\n 'abstract',\n 'title_abstract',\n 'author',\n 'keyword',\n 'all',\n]);\n\n/**\n * Schema for term block containing search terms.\n */\nexport const termBlockSchema = z.object({\n keywords: z.array(z.string()).min(1).optional(),\n mesh: z.array(z.string()).optional(),\n emtree: z.array(z.string()).optional(),\n eric: z.array(z.string()).optional(),\n exclude: z.array(z.string()).optional(),\n}).refine(\n (data) => data.keywords?.length || data.mesh?.length || data.emtree?.length || data.eric?.length,\n { message: 'At least one of keywords, mesh, emtree, or eric is required' }\n);\n\n/**\n * Schema for operator.\n */\nexport const operatorSchema = z.enum(['AND', 'OR']);\n\n/**\n * Schema for query block.\n */\nexport const queryBlockSchema = z.object({\n field: fieldTypeSchema,\n terms: termBlockSchema,\n operator: operatorSchema,\n});\n\n/**\n * Schema for publication type filter.\n */\nexport const publicationTypeFilterSchema = z.object({\n include: z.array(z.string()).optional(),\n exclude: z.array(z.string()).optional(),\n});\n\n/**\n * Schema for filters (YAML input format with snake_case).\n * Transforms to camelCase for internal use.\n */\nexport const filtersSchema = z\n .object({\n year_from: z.number().int().optional(),\n year_to: z.number().int().optional(),\n language: z.array(z.string()).optional(),\n publication_types: publicationTypeFilterSchema.optional(),\n })\n .optional()\n .default({})\n .transform((data) => ({\n yearFrom: data.year_from,\n yearTo: data.year_to,\n languages: data.language,\n publicationTypes: data.publication_types,\n }));\n\n/**\n * Schema for override block (YAML input format).\n */\nexport const overrideBlockSchema = z\n .object({\n filters: z\n .object({\n year_from: z.number().int().optional(),\n year_to: z.number().int().optional(),\n language: z.array(z.string()).optional(),\n publication_types: publicationTypeFilterSchema.optional(),\n })\n .optional(),\n categories: z.array(z.string()).optional(),\n source_types: z.array(z.string()).optional(),\n })\n .transform((data) => ({\n filters: data.filters\n ? {\n yearFrom: data.filters.year_from,\n yearTo: data.filters.year_to,\n languages: data.filters.language,\n publicationTypes: data.filters.publication_types,\n }\n : undefined,\n categories: data.categories,\n sourceTypes: data.source_types,\n }));\n\n/**\n * Provider names schema.\n */\nexport const providerNameSchema = z.enum([\n 'pubmed',\n 'scopus',\n 'eric',\n 'arxiv',\n 'wos',\n 'embase',\n]);\n\n/**\n * Schema for overrides object (partial record of provider -> override).\n */\nconst overridesSchema = z\n .object({\n pubmed: overrideBlockSchema.optional(),\n scopus: overrideBlockSchema.optional(),\n eric: overrideBlockSchema.optional(),\n arxiv: overrideBlockSchema.optional(),\n wos: overrideBlockSchema.optional(),\n embase: overrideBlockSchema.optional(),\n })\n .optional()\n .default({});\n\n/**\n * Schema for the complete query file (YAML input format).\n * Transforms to QueryAST for internal use.\n */\nexport const queryFileSchema = z\n .object({\n name: z.string().min(1),\n description: z.string().optional(),\n query: z.array(queryBlockSchema).min(1),\n filters: filtersSchema,\n overrides: overridesSchema,\n })\n .transform((data) => ({\n name: data.name,\n description: data.description,\n blocks: data.query,\n filters: data.filters,\n overrides: data.overrides,\n }));\n\n/**\n * Validate a parsed YAML object against the query schema.\n * Returns a validated QueryAST.\n */\nexport function validateQueryFile(data: unknown): QueryAST {\n return queryFileSchema.parse(data);\n}\n\n/**\n * Validation error with path information.\n */\nexport class ValidationError extends Error {\n constructor(\n public readonly path: string,\n message: string\n ) {\n super(message);\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Format Zod validation errors into an array of ValidationError objects.\n *\n * @param data - The data to validate\n * @returns Array of ValidationError objects (empty if valid)\n */\nexport function formatValidationErrors(data: unknown): ValidationError[] {\n const result = queryFileSchema.safeParse(data);\n\n if (result.success) {\n return [];\n }\n\n return result.error.issues.map((issue) => {\n const path = issue.path.join('.');\n return new ValidationError(path, issue.message);\n });\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { MeSHLookupResult } from './mesh-lookup.js';
|
|
2
|
+
export declare class VocabCache {
|
|
3
|
+
private store;
|
|
4
|
+
private readonly cachePath;
|
|
5
|
+
private readonly ttlMs;
|
|
6
|
+
constructor(options?: {
|
|
7
|
+
cachePath?: string;
|
|
8
|
+
ttlMs?: number;
|
|
9
|
+
});
|
|
10
|
+
load(): Promise<void>;
|
|
11
|
+
save(): Promise<void>;
|
|
12
|
+
get(vocabulary: string, term: string): MeSHLookupResult | undefined;
|
|
13
|
+
set(vocabulary: string, term: string, result: MeSHLookupResult): void;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=vocab-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vocab-cache.d.ts","sourceRoot":"","sources":["../../src/query/vocab-cache.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAWzD,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAOtD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAcrB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAanE,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,IAAI;CAItE"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { readFile, mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { getConfigDir } from "../config/paths.js";
|
|
4
|
+
const DEFAULT_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
5
|
+
class VocabCache {
|
|
6
|
+
store = {};
|
|
7
|
+
cachePath;
|
|
8
|
+
ttlMs;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.cachePath = options?.cachePath ?? join(getConfigDir(), "cache", "vocab-lookup.json");
|
|
11
|
+
this.ttlMs = options?.ttlMs ?? DEFAULT_TTL_MS;
|
|
12
|
+
}
|
|
13
|
+
async load() {
|
|
14
|
+
try {
|
|
15
|
+
const raw = await readFile(this.cachePath, "utf-8");
|
|
16
|
+
const parsed = JSON.parse(raw);
|
|
17
|
+
this.store = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
|
|
18
|
+
} catch {
|
|
19
|
+
this.store = {};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async save() {
|
|
23
|
+
await mkdir(dirname(this.cachePath), { recursive: true });
|
|
24
|
+
await writeFile(this.cachePath, JSON.stringify(this.store), "utf-8");
|
|
25
|
+
}
|
|
26
|
+
get(vocabulary, term) {
|
|
27
|
+
const key = `${vocabulary}:${term}`;
|
|
28
|
+
const entry = this.store[key];
|
|
29
|
+
if (!entry) return void 0;
|
|
30
|
+
if (Date.now() - entry.cachedAt > this.ttlMs) {
|
|
31
|
+
delete this.store[key];
|
|
32
|
+
return void 0;
|
|
33
|
+
}
|
|
34
|
+
return entry.result;
|
|
35
|
+
}
|
|
36
|
+
set(vocabulary, term, result) {
|
|
37
|
+
const key = `${vocabulary}:${term}`;
|
|
38
|
+
this.store[key] = { result, cachedAt: Date.now() };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
VocabCache
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=vocab-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vocab-cache.js","sources":["../../src/query/vocab-cache.ts"],"sourcesContent":["/**\n * File-based cache for vocabulary lookup results.\n *\n * Stores MeSH (and future vocabulary) lookup results on disk to avoid\n * redundant API calls. TTL defaults to 30 days.\n */\nimport { readFile, writeFile, mkdir } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { getConfigDir } from '../config/paths.js';\nimport type { MeSHLookupResult } from './mesh-lookup.js';\n\nconst DEFAULT_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days\n\ninterface VocabCacheEntry {\n result: MeSHLookupResult;\n cachedAt: number; // Unix ms\n}\n\ntype VocabCacheStore = Record<string, VocabCacheEntry>;\n\nexport class VocabCache {\n private store: VocabCacheStore = {};\n private readonly cachePath: string;\n private readonly ttlMs: number;\n\n constructor(options?: { cachePath?: string; ttlMs?: number }) {\n this.cachePath =\n options?.cachePath ??\n join(getConfigDir(), 'cache', 'vocab-lookup.json');\n this.ttlMs = options?.ttlMs ?? DEFAULT_TTL_MS;\n }\n\n async load(): Promise<void> {\n try {\n const raw = await readFile(this.cachePath, 'utf-8');\n const parsed: unknown = JSON.parse(raw);\n this.store =\n typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)\n ? (parsed as VocabCacheStore)\n : {};\n } catch {\n // File missing or corrupted JSON — start with empty cache\n this.store = {};\n }\n }\n\n async save(): Promise<void> {\n await mkdir(dirname(this.cachePath), { recursive: true });\n await writeFile(this.cachePath, JSON.stringify(this.store), 'utf-8');\n }\n\n get(vocabulary: string, term: string): MeSHLookupResult | undefined {\n const key = `${vocabulary}:${term}`;\n const entry = this.store[key];\n if (!entry) return undefined;\n\n if (Date.now() - entry.cachedAt > this.ttlMs) {\n delete this.store[key];\n return undefined;\n }\n\n return entry.result;\n }\n\n set(vocabulary: string, term: string, result: MeSHLookupResult): void {\n const key = `${vocabulary}:${term}`;\n this.store[key] = { result, cachedAt: Date.now() };\n }\n}\n"],"names":[],"mappings":";;;AAWA,MAAM,iBAAiB,KAAK,KAAK,KAAK,KAAK;AASpC,MAAM,WAAW;AAAA,EACd,QAAyB,CAAA;AAAA,EAChB;AAAA,EACA;AAAA,EAEjB,YAAY,SAAkD;AAC5D,SAAK,YACH,SAAS,aACT,KAAK,aAAA,GAAgB,SAAS,mBAAmB;AACnD,SAAK,QAAQ,SAAS,SAAS;AAAA,EACjC;AAAA,EAEA,MAAM,OAAsB;AAC1B,QAAI;AACF,YAAM,MAAM,MAAM,SAAS,KAAK,WAAW,OAAO;AAClD,YAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,WAAK,QACH,OAAO,WAAW,YAAY,WAAW,QAAQ,CAAC,MAAM,QAAQ,MAAM,IACjE,SACD,CAAA;AAAA,IACR,QAAQ;AAEN,WAAK,QAAQ,CAAA;AAAA,IACf;AAAA,EACF;AAAA,EAEA,MAAM,OAAsB;AAC1B,UAAM,MAAM,QAAQ,KAAK,SAAS,GAAG,EAAE,WAAW,MAAM;AACxD,UAAM,UAAU,KAAK,WAAW,KAAK,UAAU,KAAK,KAAK,GAAG,OAAO;AAAA,EACrE;AAAA,EAEA,IAAI,YAAoB,MAA4C;AAClE,UAAM,MAAM,GAAG,UAAU,IAAI,IAAI;AACjC,UAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,QAAI,CAAC,MAAO,QAAO;AAEnB,QAAI,KAAK,IAAA,IAAQ,MAAM,WAAW,KAAK,OAAO;AAC5C,aAAO,KAAK,MAAM,GAAG;AACrB,aAAO;AAAA,IACT;AAEA,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,IAAI,YAAoB,MAAc,QAAgC;AACpE,UAAM,MAAM,GAAG,UAAU,IAAI,IAAI;AACjC,SAAK,MAAM,GAAG,IAAI,EAAE,QAAQ,UAAU,KAAK,MAAI;AAAA,EACjD;AACF;"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { QueryAST } from './types.js';
|
|
2
|
+
import { MeSHLookupClient } from './mesh-lookup.js';
|
|
3
|
+
import { Provider } from '../providers/base/types.js';
|
|
4
|
+
import { VocabCache } from './vocab-cache.js';
|
|
5
|
+
/** Supported controlled vocabulary types. */
|
|
6
|
+
export type VocabType = 'mesh' | 'eric' | 'emtree';
|
|
7
|
+
/**
|
|
8
|
+
* A controlled vocabulary term extracted from a QueryAST.
|
|
9
|
+
*/
|
|
10
|
+
export interface VocabTerm {
|
|
11
|
+
term: string;
|
|
12
|
+
vocabulary: VocabType;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Result of validating a single controlled vocabulary term.
|
|
16
|
+
*/
|
|
17
|
+
export interface VocabTermResult {
|
|
18
|
+
term: string;
|
|
19
|
+
vocabulary: VocabType;
|
|
20
|
+
found: boolean;
|
|
21
|
+
suggestions?: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A controlled vocabulary term that failed due to an API error.
|
|
25
|
+
*/
|
|
26
|
+
export interface VocabTermError {
|
|
27
|
+
term: string;
|
|
28
|
+
vocabulary: VocabType;
|
|
29
|
+
error: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Result of validating all controlled vocabulary terms in a query.
|
|
33
|
+
*/
|
|
34
|
+
export interface VocabValidationResult {
|
|
35
|
+
valid: VocabTermResult[];
|
|
36
|
+
invalid: VocabTermResult[];
|
|
37
|
+
errors: VocabTermError[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Extract all controlled vocabulary terms from a QueryAST.
|
|
41
|
+
* Terms are deduplicated across blocks.
|
|
42
|
+
*/
|
|
43
|
+
export declare function extractControlledVocabTerms(ast: QueryAST): VocabTerm[];
|
|
44
|
+
/**
|
|
45
|
+
* A count-based vocabulary validator.
|
|
46
|
+
* Validates terms by executing a count-only search — hit count 0 means invalid.
|
|
47
|
+
*/
|
|
48
|
+
export interface CountVocabValidator {
|
|
49
|
+
vocabulary: VocabType;
|
|
50
|
+
countTerm: (term: string) => Promise<number>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Validate all controlled vocabulary terms in a QueryAST.
|
|
54
|
+
*
|
|
55
|
+
* MeSH terms are validated via the MeSH lookup API (exact match + suggestions).
|
|
56
|
+
* ERIC/Emtree terms are validated via count-only search when countValidators are provided.
|
|
57
|
+
* Terms whose vocabulary has no validator are skipped silently.
|
|
58
|
+
*
|
|
59
|
+
* Different vocabulary groups are validated in parallel; within each group,
|
|
60
|
+
* concurrency is limited to 3 requests at a time.
|
|
61
|
+
*/
|
|
62
|
+
export declare function validateControlledVocab(ast: QueryAST, meshClient: MeSHLookupClient, options?: {
|
|
63
|
+
countValidators?: CountVocabValidator[];
|
|
64
|
+
}): Promise<VocabValidationResult>;
|
|
65
|
+
export declare function createEricCountValidator(provider: Provider, options?: {
|
|
66
|
+
cache?: VocabCache;
|
|
67
|
+
}): CountVocabValidator;
|
|
68
|
+
export declare function createEmtreeCountValidator(provider: Provider, options?: {
|
|
69
|
+
cache?: VocabCache;
|
|
70
|
+
}): CountVocabValidator;
|
|
71
|
+
//# sourceMappingURL=vocab-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vocab-validator.d.ts","sourceRoot":"","sources":["../../src/query/vocab-validator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,gBAAgB,EAAoB,MAAM,kBAAkB,CAAC;AAC3E,OAAO,KAAK,EAAE,QAAQ,EAAiC,MAAM,4BAA4B,CAAC;AAC1F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,6CAA6C;AAC7C,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,SAAS,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,SAAS,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,SAAS,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,QAAQ,GAAG,SAAS,EAAE,CA0BtE;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,SAAS,CAAC;IACtB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9C;AAsBD;;;;;;;;;GASG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,QAAQ,EACb,UAAU,EAAE,gBAAgB,EAC5B,OAAO,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,mBAAmB,EAAE,CAAA;CAAE,GACpD,OAAO,CAAC,qBAAqB,CAAC,CA2FhC;AAgDD,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,UAAU,CAAA;CAAE,GAC/B,mBAAmB,CAErB;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,UAAU,CAAA;CAAE,GAC/B,mBAAmB,CAErB"}
|