jbrowse-plugin-msaview 5.16.0 → 5.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.d.ts +19 -1
  2. package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.js +55 -3
  3. package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +10 -0
  4. package/dist/BgzipFastaMsaAdapter/configSchema.js +5 -0
  5. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +23 -13
  6. package/dist/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.d.ts +11 -4
  7. package/dist/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.js +4 -4
  8. package/dist/LaunchMsaView/components/TranscriptSelector.d.ts +2 -2
  9. package/dist/LaunchMsaView/components/TranscriptSelector.js +4 -6
  10. package/dist/LaunchMsaView/components/pickSelectedId.test.d.ts +1 -0
  11. package/dist/LaunchMsaView/components/pickSelectedId.test.js +17 -0
  12. package/dist/LaunchMsaView/components/useTranscriptSelection.d.ts +5 -3
  13. package/dist/LaunchMsaView/components/useTranscriptSelection.js +10 -11
  14. package/dist/LaunchMsaView/detectQueryRow.d.ts +1 -1
  15. package/dist/LaunchMsaView/detectQueryRow.js +37 -25
  16. package/dist/LaunchMsaView/detectQueryRow.test.js +9 -0
  17. package/dist/LaunchMsaView/featureMatchesId.test.d.ts +1 -0
  18. package/dist/LaunchMsaView/featureMatchesId.test.js +74 -0
  19. package/dist/LaunchMsaView/useQueryRowName.d.ts +1 -1
  20. package/dist/LaunchMsaView/useQueryRowName.js +2 -2
  21. package/dist/LaunchMsaView/util.d.ts +2 -0
  22. package/dist/LaunchMsaView/util.js +38 -0
  23. package/dist/MsaViewPanel/displayedRefName.d.ts +19 -0
  24. package/dist/MsaViewPanel/displayedRefName.js +16 -0
  25. package/dist/MsaViewPanel/fetchIndexedMsa.js +10 -23
  26. package/dist/MsaViewPanel/handleMsaClick.test.d.ts +1 -0
  27. package/dist/MsaViewPanel/handleMsaClick.test.js +59 -0
  28. package/dist/MsaViewPanel/model.js +13 -11
  29. package/dist/OrthoFinderMsaAdapter/OrthoFinderMsaAdapter.d.ts +22 -0
  30. package/dist/OrthoFinderMsaAdapter/OrthoFinderMsaAdapter.js +92 -0
  31. package/dist/OrthoFinderMsaAdapter/configSchema.d.ts +12 -0
  32. package/dist/OrthoFinderMsaAdapter/configSchema.js +13 -0
  33. package/dist/OrthoFinderMsaAdapter/index.d.ts +2 -0
  34. package/dist/OrthoFinderMsaAdapter/index.js +14 -0
  35. package/dist/OrthoFinderMsaAdapter/parseResults.d.ts +31 -0
  36. package/dist/OrthoFinderMsaAdapter/parseResults.js +81 -0
  37. package/dist/OrthoFinderMsaAdapter/parseResults.test.d.ts +1 -0
  38. package/dist/OrthoFinderMsaAdapter/parseResults.test.js +47 -0
  39. package/dist/chunks/BgzipFastaMsaAdapter-Y3LYQRQL.js +6 -0
  40. package/dist/chunks/BgzipFastaMsaAdapter-Y3LYQRQL.js.map +7 -0
  41. package/dist/chunks/LaunchMsaViewDialog-CZR76VIV.js +3 -0
  42. package/dist/chunks/LaunchMsaViewDialog-CZR76VIV.js.map +7 -0
  43. package/dist/chunks/MsaViewPanel-6ETJVX27.js +2 -0
  44. package/dist/chunks/{MsaViewPanel-UWT4FWLE.js.map → MsaViewPanel-6ETJVX27.js.map} +1 -1
  45. package/dist/chunks/OrthoFinderMsaAdapter-BAVVTZHO.js +2 -0
  46. package/dist/chunks/OrthoFinderMsaAdapter-BAVVTZHO.js.map +7 -0
  47. package/dist/chunks/chunk-3G2F2Y7O.js +2 -0
  48. package/dist/chunks/chunk-3G2F2Y7O.js.map +7 -0
  49. package/dist/chunks/chunk-CRESEEY4.js +2 -0
  50. package/dist/chunks/chunk-CRESEEY4.js.map +7 -0
  51. package/dist/chunks/chunk-ET6J3BQX.js +3 -0
  52. package/dist/chunks/chunk-ET6J3BQX.js.map +7 -0
  53. package/dist/chunks/{chunk-AO3YMCKS.js → chunk-TEQX6FF3.js} +6 -6
  54. package/dist/chunks/{chunk-AO3YMCKS.js.map → chunk-TEQX6FF3.js.map} +1 -1
  55. package/dist/chunks/chunk-WE35XW3J.js +3 -0
  56. package/dist/chunks/chunk-WE35XW3J.js.map +7 -0
  57. package/dist/chunks/chunk-YN7VVNZR.js +2 -0
  58. package/dist/chunks/chunk-YN7VVNZR.js.map +7 -0
  59. package/dist/chunks/dist-NZDQ46PF.js +2 -0
  60. package/dist/chunks/model-XMH3QSZV.js +11 -0
  61. package/dist/chunks/model-XMH3QSZV.js.map +7 -0
  62. package/dist/index.js +2 -0
  63. package/dist/jbrowse-plugin-msaview.esm.js +2 -2
  64. package/dist/jbrowse-plugin-msaview.esm.js.map +4 -4
  65. package/dist/utils/msaIndex.d.ts +14 -0
  66. package/dist/utils/msaIndex.js +13 -0
  67. package/dist/version.d.ts +1 -1
  68. package/dist/version.js +1 -1
  69. package/package.json +1 -1
  70. package/src/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.ts +74 -3
  71. package/src/BgzipFastaMsaAdapter/configSchema.ts +6 -0
  72. package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +31 -21
  73. package/src/LaunchMsaView/components/PreLoadedMSA/fetchMSAData.ts +19 -7
  74. package/src/LaunchMsaView/components/TranscriptSelector.tsx +4 -7
  75. package/src/LaunchMsaView/components/pickSelectedId.test.ts +24 -0
  76. package/src/LaunchMsaView/components/useTranscriptSelection.ts +13 -14
  77. package/src/LaunchMsaView/detectQueryRow.test.ts +11 -0
  78. package/src/LaunchMsaView/detectQueryRow.ts +49 -33
  79. package/src/LaunchMsaView/featureMatchesId.test.ts +97 -0
  80. package/src/LaunchMsaView/useQueryRowName.ts +7 -3
  81. package/src/LaunchMsaView/util.ts +52 -0
  82. package/src/MsaViewPanel/displayedRefName.ts +30 -0
  83. package/src/MsaViewPanel/fetchIndexedMsa.ts +15 -26
  84. package/src/MsaViewPanel/handleMsaClick.test.ts +75 -0
  85. package/src/MsaViewPanel/model.ts +17 -11
  86. package/src/OrthoFinderMsaAdapter/OrthoFinderMsaAdapter.ts +124 -0
  87. package/src/OrthoFinderMsaAdapter/configSchema.ts +19 -0
  88. package/src/OrthoFinderMsaAdapter/index.ts +19 -0
  89. package/src/OrthoFinderMsaAdapter/parseResults.test.ts +88 -0
  90. package/src/OrthoFinderMsaAdapter/parseResults.ts +97 -0
  91. package/src/index.ts +2 -0
  92. package/src/utils/msaIndex.ts +32 -0
  93. package/src/version.ts +1 -1
  94. package/dist/chunks/BgzipFastaMsaAdapter-LXM6BNRP.js +0 -4
  95. package/dist/chunks/BgzipFastaMsaAdapter-LXM6BNRP.js.map +0 -7
  96. package/dist/chunks/LaunchMsaViewDialog-CVUQSMRL.js +0 -5
  97. package/dist/chunks/LaunchMsaViewDialog-CVUQSMRL.js.map +0 -7
  98. package/dist/chunks/MsaViewPanel-UWT4FWLE.js +0 -2
  99. package/dist/chunks/chunk-LXN7NIHB.js +0 -2
  100. package/dist/chunks/chunk-LXN7NIHB.js.map +0 -7
  101. package/dist/chunks/chunk-YRZUKVPZ.js +0 -3
  102. package/dist/chunks/chunk-YRZUKVPZ.js.map +0 -7
  103. package/dist/chunks/dist-2VELCAWU.js +0 -2
  104. package/dist/chunks/model-DAIL6BNQ.js +0 -12
  105. package/dist/chunks/model-DAIL6BNQ.js.map +0 -7
  106. /package/dist/chunks/{dist-2VELCAWU.js.map → dist-NZDQ46PF.js.map} +0 -0
@@ -19,11 +19,11 @@ import { findQueryRow } from './detectQueryRow';
19
19
  export function resolveQueryRowName(queryRow, fallback) {
20
20
  return (queryRow.querySeqName || (queryRow.names.includes(fallback) ? fallback : ''));
21
21
  }
22
- export function useQueryRowName(msaText, proteinSequence) {
22
+ export function useQueryRowName(msaText, proteinSequence, preferredName) {
23
23
  const [override, setOverride] = useState();
24
24
  // parsing runs on every keystroke in the paste box otherwise, and an
25
25
  // alignment of a few hundred rows is not free
26
- const { names, match } = useMemo(() => findQueryRow(msaText, proteinSequence), [msaText, proteinSequence]);
26
+ const { names, match } = useMemo(() => findQueryRow(msaText, proteinSequence, preferredName), [msaText, proteinSequence, preferredName]);
27
27
  // a row picked from one alignment says nothing about the next one pasted
28
28
  const picked = override !== undefined && (names.length === 0 || names.includes(override))
29
29
  ? override
@@ -8,6 +8,8 @@ export declare function getTranscriptLength(feature: Feature): {
8
8
  };
9
9
  export declare function getId(val?: Feature): string;
10
10
  export declare function featureMatchesId(feature: Feature, id: string): boolean;
11
+ /** the dataset entry for this transcript, else for the gene it belongs to */
12
+ export declare function findDatasetId(transcript: Feature, gene: Feature, ids: ReadonlySet<string>): string | undefined;
11
13
  export declare function getTranscriptDisplayName(val?: Feature): string;
12
14
  export declare function getGeneDisplayName(val?: Feature): string;
13
15
  export declare function getBlastViewTitle(feature: Feature, transcript: Feature): string;
@@ -39,6 +39,13 @@ export function getTranscriptLength(feature) {
39
39
  export function getId(val) {
40
40
  return val?.id() ?? '';
41
41
  }
42
+ // a proteome downloaded from NCBI or Ensembl names each protein by the CDS's
43
+ // protein_id rather than by its transcript
44
+ function getCdsProteinIds(transcript) {
45
+ return (transcript.get('subfeatures') ?? [])
46
+ .filter(f => f.get('type') === 'CDS')
47
+ .map(f => f.get('protein_id'));
48
+ }
42
49
  function getMatchableIds(val) {
43
50
  return val
44
51
  ? uniqueDefined([
@@ -46,12 +53,43 @@ function getMatchableIds(val) {
46
53
  val.get('name'),
47
54
  val.get('id'),
48
55
  val.get('transcript_id'),
56
+ val.get('gene_name'),
57
+ val.get('locus_tag'),
58
+ ...getCdsProteinIds(val),
49
59
  ])
50
60
  : [];
51
61
  }
52
62
  export function featureMatchesId(feature, id) {
53
63
  return getMatchableIds(feature).includes(id);
54
64
  }
65
+ const geneIdPrefix = /^gene[-:]/;
66
+ // A set keyed by gene, such as an OrthoFinder run over one protein per gene,
67
+ // names the gene by its GFF3 ID without NCBI's `gene-` or Ensembl's `gene:`
68
+ function getGeneMatchableIds(gene) {
69
+ const id = gene.get('id');
70
+ return uniqueDefined([
71
+ id,
72
+ id?.replace(geneIdPrefix, ''),
73
+ gene.get('gene_id'),
74
+ gene.get('locus_tag'),
75
+ ]);
76
+ }
77
+ // OrthoFinder writes every gene name with each character that Newick or a TSV
78
+ // cannot hold replaced by `_` (its utils/gene_ids.py clean_name), so NCBI's
79
+ // gene `E(spl)m2-BFM` is `E_spl_m2-BFM` in its results. It also replaces control
80
+ // characters, which a GFF3 attribute cannot carry unescaped.
81
+ const orthoFinderUnsafe = /[\s:;(),[\]='"]/g;
82
+ function findIn(candidates, ids) {
83
+ return (candidates.find(id => ids.has(id)) ??
84
+ candidates
85
+ .map(id => id.replace(orthoFinderUnsafe, '_'))
86
+ .find(id => ids.has(id)));
87
+ }
88
+ /** the dataset entry for this transcript, else for the gene it belongs to */
89
+ export function findDatasetId(transcript, gene, ids) {
90
+ return (findIn(getMatchableIds(transcript), ids) ??
91
+ findIn(getGeneMatchableIds(gene), ids));
92
+ }
55
93
  export function getTranscriptDisplayName(val) {
56
94
  return val ? joinDefined(' ', [val.get('name'), val.get('id')]) : '';
57
95
  }
@@ -0,0 +1,19 @@
1
+ interface Resolver {
2
+ initialized?: boolean;
3
+ getCanonicalRefName: (refName: string) => string | undefined;
4
+ }
5
+ /**
6
+ * The name a view's displayed regions use for `refName`'s chromosome. core's
7
+ * bpToPx, and so centerAt, matches region names exactly and does nothing when
8
+ * the feature says `chr1` and the view displays `1`. The raw name is kept when
9
+ * a region already carries it, and when no region resolves to the same
10
+ * chromosome, so a view this cannot place behaves as it did before.
11
+ */
12
+ export declare function displayedRefName({ displayedRegions, assembly, refName, }: {
13
+ displayedRegions: readonly {
14
+ refName: string;
15
+ }[];
16
+ assembly?: Resolver;
17
+ refName: string;
18
+ }): string;
19
+ export {};
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The name a view's displayed regions use for `refName`'s chromosome. core's
3
+ * bpToPx, and so centerAt, matches region names exactly and does nothing when
4
+ * the feature says `chr1` and the view displays `1`. The raw name is kept when
5
+ * a region already carries it, and when no region resolves to the same
6
+ * chromosome, so a view this cannot place behaves as it did before.
7
+ */
8
+ export function displayedRefName({ displayedRegions, assembly, refName, }) {
9
+ const canonical = (name) => assembly?.initialized ? assembly.getCanonicalRefName(name) : undefined;
10
+ const wanted = canonical(refName);
11
+ return displayedRegions.some(r => r.refName === refName) ||
12
+ wanted === undefined
13
+ ? refName
14
+ : (displayedRegions.find(r => canonical(r.refName) === wanted)?.refName ??
15
+ refName);
16
+ }
@@ -1,36 +1,23 @@
1
- import { BgzfFilehandle } from '@gmod/bgzf-filehandle';
2
1
  import { openLocation } from '@jbrowse/core/util/io';
2
+ import { parseMsaIndex, readMsaBlock } from '../utils/msaIndex';
3
3
  // Pull one transcript's whole multiple-alignment out of a single bgzip file
4
4
  // keyed by NAME. Given the `.fa.gz` uri, the bgzip index (`.gzi`) and name index
5
- // (`.idx`, a TSV `name<TAB>offset<TAB>length` of each block's uncompressed byte
6
- // offset + length) are found by suffix. We fetch the small `.idx` once, look up
7
- // the name, and random-read just that block — already valid FASTA
5
+ // (`.idx`) are found by suffix. We fetch the small `.idx` once, look up the
6
+ // name, and random-read just that block — already valid FASTA
8
7
  // (`>hg38\nSEQ\n>panTro4\nSEQ\n...`). One genome-scale alignment serves any gene
9
8
  // with no per-gene files or coordinates.
10
9
  export async function fetchIndexedMsa({ location, name, }) {
11
10
  const open = (uri) => openLocation({ uri, locationType: 'UriLocation' });
12
- const idxText = await open(`${location.uri}.idx`).readFile('utf8');
13
- const entry = lookup(idxText, name);
14
- if (entry) {
15
- const fh = new BgzfFilehandle({
11
+ const want = versionless(name);
12
+ const entry = parseMsaIndex(await open(`${location.uri}.idx`).readFile('utf8')).find(e => versionless(e.name) === want);
13
+ return entry
14
+ ? readMsaBlock({
16
15
  filehandle: open(location.uri),
17
16
  gziFilehandle: open(`${location.uri}.gzi`),
18
- });
19
- const bytes = await fh.read(entry.length, entry.offset);
20
- return new TextDecoder().decode(bytes).trim();
21
- }
22
- return undefined;
17
+ entry,
18
+ })
19
+ : undefined;
23
20
  }
24
21
  // ids are matched versionless, so version drift between the alignment build and
25
22
  // the live annotation never breaks the lookup
26
23
  const versionless = (s) => s.replace(/\.\d+$/, '');
27
- function lookup(idxText, name) {
28
- const want = versionless(name);
29
- for (const line of idxText.split('\n')) {
30
- const [id, offset, length] = line.split('\t');
31
- if (id && versionless(id) === want) {
32
- return { offset: Number(offset), length: Number(length) };
33
- }
34
- }
35
- return undefined;
36
- }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,59 @@
1
+ import { describe, expect, test, vi } from 'vitest';
2
+ import stateModelFactory from './model';
3
+ const aliases = { chr1: '1' };
4
+ vi.mock('@jbrowse/core/util', async (importOriginal) => ({
5
+ ...(await importOriginal()),
6
+ getSession: () => session,
7
+ }));
8
+ const centerAtCalls = [];
9
+ const connectedView = (displayedRefName) => ({
10
+ id: 'lgv1',
11
+ type: 'LinearGenomeView',
12
+ assemblyNames: ['hg38'],
13
+ displayedRegions: [{ refName: displayedRefName, start: 0, end: 1000 }],
14
+ centerAt: (coord, refName) => {
15
+ centerAtCalls.push([coord, refName]);
16
+ },
17
+ navTo: vi.fn(),
18
+ });
19
+ let session = {
20
+ views: [connectedView('1')],
21
+ hovered: undefined,
22
+ assemblyManager: {
23
+ get: () => ({
24
+ initialized: true,
25
+ getCanonicalRefName: (refName) => aliases[refName] ?? refName,
26
+ }),
27
+ },
28
+ };
29
+ function clickColumn({ displayed, refName, }) {
30
+ centerAtCalls.length = 0;
31
+ session = { ...session, views: [connectedView(displayed)] };
32
+ const model = stateModelFactory().create({
33
+ type: 'MsaView',
34
+ id: 'msaview1',
35
+ connectedViewId: 'lgv1',
36
+ querySeqName: 'hg38.chr1',
37
+ mafRegion: { refName, start: 500, end: 510, assemblyName: 'hg38' },
38
+ });
39
+ model.setMSA('>hg38.chr1\nACGTACGTAC');
40
+ model.handleMsaClick(3);
41
+ return centerAtCalls;
42
+ }
43
+ describe('clicking an alignment column recenters the connected view', () => {
44
+ test('on the name the view displays when the feature uses an alias', () => {
45
+ expect(clickColumn({ displayed: '1', refName: 'chr1' })).toEqual([
46
+ [503, '1'],
47
+ ]);
48
+ });
49
+ test('on the feature name when it is already a displayed region', () => {
50
+ expect(clickColumn({ displayed: 'chr1', refName: 'chr1' })).toEqual([
51
+ [503, 'chr1'],
52
+ ]);
53
+ });
54
+ test('on the feature name when no displayed region is the same chromosome', () => {
55
+ expect(clickColumn({ displayed: '2', refName: 'chr1' })).toEqual([
56
+ [503, 'chr1'],
57
+ ]);
58
+ });
59
+ });
@@ -5,6 +5,7 @@ import { autorun } from 'mobx';
5
5
  import { MSAModelF } from 'react-msaview';
6
6
  import { autoLoadProteinDomains, launchAwaitsTranscript, launchBlastIfNeeded, launchOrthologsIfNeeded, loadStoredData, observeProteinHighlights, processInit, runCleanup, storeDataToIndexedDB, showDomainsOnGenomeIfAsked, syncGenomeHoverToMsaColumn, } from './afterCreateAutoruns';
7
7
  import { connectedDomains } from './connectedDomains';
8
+ import { displayedRefName } from './displayedRefName';
8
9
  import { domainTrackConf } from './domainTrack';
9
10
  import { isLaunching } from './isLaunching';
10
11
  import { msaCoordToGenomeCoord, msaCoordToGenomeRegions, } from './msaCoordToGenomeCoord';
@@ -609,18 +610,19 @@ export default function stateModelFactory() {
609
610
  */
610
611
  handleMsaClick(coord) {
611
612
  const { connectedView, zoomToBaseLevel } = self;
613
+ const { assemblyManager } = getSession(self);
612
614
  const r2 = msaCoordToGenomeCoord({ model: self, coord });
613
- if (!r2 || !connectedView) {
614
- return;
615
- }
616
- // Use the genome coord's own refName for both nav paths — it matches the
617
- // connected view's displayed regions. Canonicalizing (e.g. "chr17"->"17")
618
- // can miss a view whose regions are an alias (same as the bpToPx path).
619
- if (zoomToBaseLevel) {
620
- connectedView.navTo(r2);
621
- }
622
- else {
623
- connectedView.centerAt(r2.start, r2.refName);
615
+ if (r2 && connectedView) {
616
+ if (zoomToBaseLevel) {
617
+ connectedView.navTo(r2);
618
+ }
619
+ else {
620
+ connectedView.centerAt(r2.start, displayedRefName({
621
+ displayedRegions: connectedView.displayedRegions,
622
+ assembly: assemblyManager.get(connectedView.assemblyNames[0] ?? ''),
623
+ refName: r2.refName,
624
+ }));
625
+ }
624
626
  }
625
627
  },
626
628
  }))
@@ -0,0 +1,22 @@
1
+ import { BaseAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
2
+ import type { ProteinEntry } from './parseResults';
3
+ /**
4
+ * Reads the `Results_*` folder of an OrthoFinder run as a dataset: each protein
5
+ * id it saw names its orthogroup's alignment and gene tree.
6
+ */
7
+ export default class OrthoFinderMsaAdapter extends BaseAdapter {
8
+ orthogroupsP: Promise<Map<string, Map<string, ProteinEntry>>> | undefined;
9
+ resolvedTreesP: Promise<Map<string, string> | 'per-orthogroup'> | undefined;
10
+ readResultsFile(path: string): Promise<string>;
11
+ getOrthogroups(): Promise<Map<string, Map<string, ProteinEntry>>>;
12
+ getResolvedTrees(): Promise<Map<string, string> | "per-orthogroup">;
13
+ readAlignment(orthogroup: string): Promise<string>;
14
+ readTree(orthogroup: string): Promise<string | undefined>;
15
+ speciesFor(assemblyName?: string): Promise<string[]>;
16
+ getMSAList(assemblyName?: string): Promise<string[]>;
17
+ getMSA(proteinId: string, assemblyName?: string): Promise<{
18
+ msa: string;
19
+ tree: string | undefined;
20
+ queryRowName: string;
21
+ }>;
22
+ }
@@ -0,0 +1,92 @@
1
+ import { BaseAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
2
+ import { openLocation } from '@jbrowse/core/util/io';
3
+ import { alignmentRowName, isMissingFile, parseOrthogroups, parseResolvedGeneTrees, resultsFile, } from './parseResults';
4
+ /**
5
+ * Reads the `Results_*` folder of an OrthoFinder run as a dataset: each protein
6
+ * id it saw names its orthogroup's alignment and gene tree.
7
+ */
8
+ export default class OrthoFinderMsaAdapter extends BaseAdapter {
9
+ readResultsFile(path) {
10
+ return openLocation(resultsFile(this.getConf('resultsLocation'), path), this.pluginManager).readFile('utf8');
11
+ }
12
+ getOrthogroups() {
13
+ this.orthogroupsP ??= this.readResultsFile('Orthogroups/Orthogroups.tsv')
14
+ .then(parseOrthogroups)
15
+ .catch((e) => {
16
+ this.orthogroupsP = undefined;
17
+ throw e;
18
+ });
19
+ return this.orthogroupsP;
20
+ }
21
+ getResolvedTrees() {
22
+ this.resolvedTreesP ??= this.readResultsFile('Resolved_Gene_Trees/Resolved_Gene_Trees.txt').then(parseResolvedGeneTrees, (e) => {
23
+ if (isMissingFile(e)) {
24
+ return 'per-orthogroup';
25
+ }
26
+ else {
27
+ this.resolvedTreesP = undefined;
28
+ throw e;
29
+ }
30
+ });
31
+ return this.resolvedTreesP;
32
+ }
33
+ async readAlignment(orthogroup) {
34
+ try {
35
+ return await this.readResultsFile(`MultipleSequenceAlignments/${orthogroup}.fa`);
36
+ }
37
+ catch (e) {
38
+ throw isMissingFile(e)
39
+ ? new Error(`OrthoFinder wrote no alignment for ${orthogroup}. It skips a few small orthogroups, and writes none at all when run with -og, or as OrthoFinder 2 without -M msa`, { cause: e })
40
+ : e;
41
+ }
42
+ }
43
+ // only orthogroups of four or more genes have a tree
44
+ async readTree(orthogroup) {
45
+ const resolvedTrees = await this.getResolvedTrees();
46
+ return resolvedTrees === 'per-orthogroup'
47
+ ? this.readResultsFile(`Resolved_Gene_Trees/${orthogroup}_tree.txt`).catch((e) => {
48
+ if (isMissingFile(e)) {
49
+ return undefined;
50
+ }
51
+ else {
52
+ throw e;
53
+ }
54
+ })
55
+ : resolvedTrees.get(orthogroup);
56
+ }
57
+ // the column named after the assembly when there is one, else every column,
58
+ // for a run whose proteome files are named some other way
59
+ async speciesFor(assemblyName) {
60
+ const bySpecies = await this.getOrthogroups();
61
+ return assemblyName && bySpecies.has(assemblyName)
62
+ ? [assemblyName]
63
+ : [...bySpecies.keys()];
64
+ }
65
+ async getMSAList(assemblyName) {
66
+ const bySpecies = await this.getOrthogroups();
67
+ const species = await this.speciesFor(assemblyName);
68
+ return [
69
+ ...new Set(species.flatMap(s => [...(bySpecies.get(s)?.keys() ?? [])])),
70
+ ];
71
+ }
72
+ async getMSA(proteinId, assemblyName) {
73
+ const bySpecies = await this.getOrthogroups();
74
+ const found = (await this.speciesFor(assemblyName))
75
+ .map(species => ({
76
+ species,
77
+ entry: bySpecies.get(species)?.get(proteinId),
78
+ }))
79
+ .find(({ entry }) => entry);
80
+ if (found?.entry) {
81
+ const { species, entry } = found;
82
+ const [msa, tree] = await Promise.all([
83
+ this.readAlignment(entry.orthogroup),
84
+ this.readTree(entry.orthogroup),
85
+ ]);
86
+ return { msa, tree, queryRowName: alignmentRowName(species, entry.id) };
87
+ }
88
+ else {
89
+ throw new Error(`${proteinId} is in no OrthoFinder orthogroup`);
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,12 @@
1
+ export default function configSchemaF(): import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
2
+ readonly resultsLocation: {
3
+ type: "fileLocation";
4
+ } & {
5
+ defaultValue: unknown;
6
+ };
7
+ type: {
8
+ type: "string";
9
+ } & {
10
+ defaultValue: string;
11
+ };
12
+ }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, true, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>, any>>;
@@ -0,0 +1,13 @@
1
+ import { ConfigurationSchema } from '@jbrowse/core/configuration';
2
+ export default function configSchemaF() {
3
+ return ConfigurationSchema('OrthoFinderMsaAdapter', {
4
+ resultsLocation: {
5
+ type: 'fileLocation',
6
+ description: 'the Results_* folder of an OrthoFinder run, holding Orthogroups/, MultipleSequenceAlignments/ and Resolved_Gene_Trees/',
7
+ defaultValue: {
8
+ uri: '/path/to/Results',
9
+ locationType: 'UriLocation',
10
+ },
11
+ },
12
+ }, { explicitlyTyped: true });
13
+ }
@@ -0,0 +1,2 @@
1
+ import type PluginManager from '@jbrowse/core/PluginManager';
2
+ export default function OrthoFinderMsaAdapterF(pluginManager: PluginManager): PluginManager;
@@ -0,0 +1,14 @@
1
+ import { AdapterType } from '@jbrowse/core/pluggableElementTypes';
2
+ import configSchemaF from './configSchema';
3
+ export default function OrthoFinderMsaAdapterF(pluginManager) {
4
+ return pluginManager.addAdapterType(() => {
5
+ return new AdapterType({
6
+ name: 'OrthoFinderMsaAdapter',
7
+ configSchema: configSchemaF(),
8
+ adapterMetadata: {
9
+ hiddenFromGUI: true,
10
+ },
11
+ getAdapterClass: () => import('./OrthoFinderMsaAdapter').then(t => t.default),
12
+ });
13
+ });
14
+ }
@@ -0,0 +1,31 @@
1
+ import type { FileLocation } from '@jbrowse/core/util/types';
2
+ export interface ProteinEntry {
3
+ orthogroup: string;
4
+ /** the id as OrthoFinder wrote it, which names the protein's alignment row */
5
+ id: string;
6
+ }
7
+ /**
8
+ * OrthoFinder's `Orthogroups/Orthogroups.tsv`, one map per species column from
9
+ * protein id to its orthogroup. Per species because OrthoFinder leaves a name
10
+ * two species share unprefixed there: human's `ADAM32` and chicken's are
11
+ * different genes in different orthogroups. Each id is also listed without a
12
+ * trailing `.N` version, since a proteome FASTA usually versions its ids where
13
+ * the matching GFF does not (Ensembl's `ENSP00000269305.4` against
14
+ * `protein_id=ENSP00000269305`).
15
+ */
16
+ export declare function parseOrthogroups(tsv: string): Map<string, Map<string, ProteinEntry>>;
17
+ /**
18
+ * The alignment and tree name each protein `<species>_<id>`, the species with
19
+ * `.` and space made `_` (OrthoFinder's species_prefixes).
20
+ */
21
+ export declare function alignmentRowName(species: string, id: string): string;
22
+ /** a file the results folder does not hold, as opposed to one it failed to serve */
23
+ export declare function isMissingFile(e: unknown): boolean;
24
+ /**
25
+ * OrthoFinder 3 writes every gene tree into one
26
+ * `Resolved_Gene_Trees/Resolved_Gene_Trees.txt`, a line per orthogroup of the
27
+ * form `OG0000055: (newick);`
28
+ */
29
+ export declare function parseResolvedGeneTrees(text: string): Map<string, string>;
30
+ /** a file inside the results folder, addressed the same way the folder is */
31
+ export declare function resultsFile(results: FileLocation, path: string): FileLocation;
@@ -0,0 +1,81 @@
1
+ import { isLocalPathLocation, isUriLocation } from '@jbrowse/core/util/types';
2
+ const versionSuffix = /\.\d+$/;
3
+ /**
4
+ * OrthoFinder's `Orthogroups/Orthogroups.tsv`, one map per species column from
5
+ * protein id to its orthogroup. Per species because OrthoFinder leaves a name
6
+ * two species share unprefixed there: human's `ADAM32` and chicken's are
7
+ * different genes in different orthogroups. Each id is also listed without a
8
+ * trailing `.N` version, since a proteome FASTA usually versions its ids where
9
+ * the matching GFF does not (Ensembl's `ENSP00000269305.4` against
10
+ * `protein_id=ENSP00000269305`).
11
+ */
12
+ export function parseOrthogroups(tsv) {
13
+ const [header = '', ...rows] = tsv.split(/\r?\n/);
14
+ const species = header.split('\t').slice(1);
15
+ const bySpecies = new Map(species.map(name => [name, new Map()]));
16
+ for (const row of rows) {
17
+ const [orthogroup, ...cells] = row.split('\t');
18
+ if (orthogroup) {
19
+ cells.forEach((cell, i) => {
20
+ const proteins = bySpecies.get(species[i] ?? '');
21
+ for (const geneId of cell.split(',')) {
22
+ const id = geneId.trim();
23
+ if (proteins && id) {
24
+ const entry = { orthogroup, id };
25
+ proteins.set(id, entry);
26
+ const unversioned = id.replace(versionSuffix, '');
27
+ if (!proteins.has(unversioned)) {
28
+ proteins.set(unversioned, entry);
29
+ }
30
+ }
31
+ }
32
+ });
33
+ }
34
+ }
35
+ return bySpecies;
36
+ }
37
+ /**
38
+ * The alignment and tree name each protein `<species>_<id>`, the species with
39
+ * `.` and space made `_` (OrthoFinder's species_prefixes).
40
+ */
41
+ export function alignmentRowName(species, id) {
42
+ return `${species.replace(/[. ]/g, '_')}_${id}`;
43
+ }
44
+ /** a file the results folder does not hold, as opposed to one it failed to serve */
45
+ export function isMissingFile(e) {
46
+ return e instanceof Error && /HTTP 40[34]\b|ENOENT/.test(e.message);
47
+ }
48
+ /**
49
+ * OrthoFinder 3 writes every gene tree into one
50
+ * `Resolved_Gene_Trees/Resolved_Gene_Trees.txt`, a line per orthogroup of the
51
+ * form `OG0000055: (newick);`
52
+ */
53
+ export function parseResolvedGeneTrees(text) {
54
+ const trees = new Map();
55
+ for (const line of text.split(/\r?\n/)) {
56
+ const separator = line.indexOf(': ');
57
+ if (separator > 0) {
58
+ trees.set(line.slice(0, separator), line.slice(separator + 2).trim());
59
+ }
60
+ }
61
+ return trees;
62
+ }
63
+ /** a file inside the results folder, addressed the same way the folder is */
64
+ export function resultsFile(results, path) {
65
+ if (isLocalPathLocation(results)) {
66
+ return {
67
+ ...results,
68
+ localPath: `${results.localPath.replace(/\/+$/, '')}/${path}`,
69
+ };
70
+ }
71
+ else if (isUriLocation(results)) {
72
+ const folder = new URL(results.uri, results.baseUri).href;
73
+ return {
74
+ ...results,
75
+ uri: new URL(path, folder.endsWith('/') ? folder : `${folder}/`).href,
76
+ };
77
+ }
78
+ else {
79
+ throw new Error('An OrthoFinder results folder must be given as a URL or a local path');
80
+ }
81
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
1
+ import { expect, test } from 'vitest';
2
+ import { alignmentRowName, isMissingFile, parseOrthogroups, parseResolvedGeneTrees, resultsFile, } from './parseResults';
3
+ test('a versioned protein id also answers to its unversioned form', () => {
4
+ const human = parseOrthogroups('Orthogroup\tHuman\tMouse\nOG0000001\tENSP00000269305.4\tENSMUSP00000104298.3, ENSMUSP00000005371\n').get('Human');
5
+ expect(human?.get('ENSP00000269305.4')?.orthogroup).toBe('OG0000001');
6
+ expect(human?.get('ENSP00000269305')).toEqual({
7
+ orthogroup: 'OG0000001',
8
+ id: 'ENSP00000269305.4',
9
+ });
10
+ });
11
+ test('an unversioned alias never displaces a real id of the same name', () => {
12
+ const groups = parseOrthogroups('Orthogroup\tA\nOG0000001\tP1.2\nOG0000002\tP1\n');
13
+ expect(groups.get('A')?.get('P1')?.orthogroup).toBe('OG0000002');
14
+ const reversed = parseOrthogroups('Orthogroup\tA\nOG0000002\tP1\nOG0000001\tP1.2\n');
15
+ expect(reversed.get('A')?.get('P1')?.orthogroup).toBe('OG0000002');
16
+ });
17
+ test("a name two species share stays each species' own", () => {
18
+ const groups = parseOrthogroups('Orthogroup\tchicken\thuman\r\nOG0013180\t\tADAM32\r\nOG0015286\tADAM32\t\r\n');
19
+ expect(groups.get('human')?.get('ADAM32')?.orthogroup).toBe('OG0013180');
20
+ expect(groups.get('chicken')?.get('ADAM32')?.orthogroup).toBe('OG0015286');
21
+ });
22
+ test("an alignment row is the species, made OrthoFinder's prefix, and the id", () => {
23
+ expect(alignmentRowName('melanogaster', 'Dmel_CG2671')).toBe('melanogaster_Dmel_CG2671');
24
+ expect(alignmentRowName('Homo sapiens.v2', 'P1')).toBe('Homo_sapiens_v2_P1');
25
+ });
26
+ test('only a 403, 404 or absent local file reads as missing', () => {
27
+ expect(isMissingFile(new Error('HTTP 404 fetching https://x/a'))).toBe(true);
28
+ expect(isMissingFile(new Error('HTTP 403 fetching https://x/a'))).toBe(true);
29
+ expect(isMissingFile(new Error("ENOENT: no such file, open '/a'"))).toBe(true);
30
+ expect(isMissingFile(new Error('HTTP 503 fetching https://x/a'))).toBe(false);
31
+ expect(isMissingFile(new TypeError('Failed to fetch'))).toBe(false);
32
+ });
33
+ test('Resolved_Gene_Trees.txt splits on the first colon-space only', () => {
34
+ const trees = parseResolvedGeneTrees('OG0000001: (A_x:0.1,B_y:0.2)n0;\nOG0000002: (C:1,D:2);\n');
35
+ expect(trees.get('OG0000001')).toBe('(A_x:0.1,B_y:0.2)n0;');
36
+ expect(trees.size).toBe(2);
37
+ });
38
+ test('a results file resolves inside the folder however the folder is written', () => {
39
+ const file = 'Orthogroups/Orthogroups.tsv';
40
+ expect(resultsFile({ uri: 'https://x.org/run/Results_Nov05', locationType: 'UriLocation' }, file)).toMatchObject({ uri: `https://x.org/run/Results_Nov05/${file}` });
41
+ expect(resultsFile({
42
+ uri: 'Results_Nov05/',
43
+ baseUri: 'https://x.org/run/config.json',
44
+ locationType: 'UriLocation',
45
+ }, file)).toMatchObject({ uri: `https://x.org/run/Results_Nov05/${file}` });
46
+ expect(resultsFile({ localPath: '/data/Results_Nov05/', locationType: 'LocalPathLocation' }, file)).toMatchObject({ localPath: `/data/Results_Nov05/${file}` });
47
+ });
@@ -0,0 +1,6 @@
1
+ import{a as V,b as q}from"./chunk-WE35XW3J.js";import{a as B}from"./chunk-3G2F2Y7O.js";import{a as v}from"./chunk-YN7VVNZR.js";import{c as k}from"./chunk-QBRNXRNU.js";import"./chunk-KVMUXFPB.js";var T=function(t,r){return T=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,o){e.__proto__=o}||function(e,o){for(var n in o)Object.prototype.hasOwnProperty.call(o,n)&&(e[n]=o[n])},T(t,r)};function b(t,r){if(typeof r!="function"&&r!==null)throw new TypeError("Class extends value "+String(r)+" is not a constructor or null");T(t,r);function e(){this.constructor=t}t.prototype=r===null?Object.create(r):(e.prototype=r.prototype,new e)}function j(t){var r=typeof Symbol=="function"&&Symbol.iterator,e=r&&t[r],o=0;if(e)return e.call(t);if(t&&typeof t.length=="number")return{next:function(){return t&&o>=t.length&&(t=void 0),{value:t&&t[o++],done:!t}}};throw new TypeError(r?"Object is not iterable.":"Symbol.iterator is not defined.")}function d(t,r){var e=typeof Symbol=="function"&&t[Symbol.iterator];if(!e)return t;var o=e.call(t),n,i=[],a;try{for(;(r===void 0||r-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(f){a={error:f}}finally{try{n&&!n.done&&(e=o.return)&&e.call(o)}finally{if(a)throw a.error}}return i}function h(t,r,e){if(e||arguments.length===2)for(var o=0,n=r.length,i;o<n;o++)(i||!(o in r))&&(i||(i=Array.prototype.slice.call(r,0,o)),i[o]=r[o]);return t.concat(i||Array.prototype.slice.call(r))}function p(t){return typeof t=="function"}function _(t){var r=function(o){Error.call(o),o.stack=new Error().stack},e=t(r);return e.prototype=Object.create(Error.prototype),e.prototype.constructor=e,e}var E=_(function(t){return function(e){t(this),this.message=e?e.length+` errors occurred during unsubscription:
2
+ `+e.map(function(o,n){return n+1+") "+o.toString()}).join(`
3
+ `):"",this.name="UnsubscriptionError",this.errors=e}});function A(t,r){if(t){var e=t.indexOf(r);0<=e&&t.splice(e,1)}}var S=(function(){function t(r){this.initialTeardown=r,this.closed=!1,this._parentage=null,this._finalizers=null}return t.prototype.unsubscribe=function(){var r,e,o,n,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var f=j(a),s=f.next();!s.done;s=f.next()){var c=s.value;c.remove(this)}}catch(u){r={error:u}}finally{try{s&&!s.done&&(e=f.return)&&e.call(f)}finally{if(r)throw r.error}}else a.remove(this);var l=this.initialTeardown;if(p(l))try{l()}catch(u){i=u instanceof E?u.errors:[u]}var w=this._finalizers;if(w){this._finalizers=null;try{for(var g=j(w),y=g.next();!y.done;y=g.next()){var er=y.value;try{$(er)}catch(u){i=i??[],u instanceof E?i=h(h([],d(i)),d(u.errors)):i.push(u)}}}catch(u){o={error:u}}finally{try{y&&!y.done&&(n=g.return)&&n.call(g)}finally{if(o)throw o.error}}}if(i)throw new E(i)}},t.prototype.add=function(r){var e;if(r&&r!==this)if(this.closed)$(r);else{if(r instanceof t){if(r.closed||r._hasParent(this))return;r._addParent(this)}(this._finalizers=(e=this._finalizers)!==null&&e!==void 0?e:[]).push(r)}},t.prototype._hasParent=function(r){var e=this._parentage;return e===r||Array.isArray(e)&&e.includes(r)},t.prototype._addParent=function(r){var e=this._parentage;this._parentage=Array.isArray(e)?(e.push(r),e):e?[e,r]:r},t.prototype._removeParent=function(r){var e=this._parentage;e===r?this._parentage=null:Array.isArray(e)&&A(e,r)},t.prototype.remove=function(r){var e=this._finalizers;e&&A(e,r),r instanceof t&&r._removeParent(this)},t.EMPTY=(function(){var r=new t;return r.closed=!0,r})(),t})();var gr=S.EMPTY;function z(t){return t instanceof S||t&&"closed"in t&&p(t.remove)&&p(t.add)&&p(t.unsubscribe)}function $(t){p(t)?t():t.unsubscribe()}var m={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var x={setTimeout:function(t,r){for(var e=[],o=2;o<arguments.length;o++)e[o-2]=arguments[o];var n=x.delegate;return n?.setTimeout?n.setTimeout.apply(n,h([t,r],d(e))):setTimeout.apply(void 0,h([t,r],d(e)))},clearTimeout:function(t){var r=x.delegate;return(r?.clearTimeout||clearTimeout)(t)},delegate:void 0};function H(t){x.setTimeout(function(){var r=m.onUnhandledError;if(r)r(t);else throw t})}function M(){}var Y=(function(){return I("C",void 0,void 0)})();function K(t){return I("E",void 0,t)}function G(t){return I("N",t,void 0)}function I(t,r,e){return{kind:t,value:r,error:e}}var R=null;function J(t){m.useDeprecatedSynchronousErrorHandling&&R&&(R.errorThrown=!0,R.error=t)}var L=(function(t){b(r,t);function r(e){var o=t.call(this)||this;return o.isStopped=!1,e?(o.destination=e,z(e)&&e.add(o)):o.destination=ir,o}return r.create=function(e,o,n){return new U(e,o,n)},r.prototype.next=function(e){this.isStopped?F(G(e),this):this._next(e)},r.prototype.error=function(e){this.isStopped?F(K(e),this):(this.isStopped=!0,this._error(e))},r.prototype.complete=function(){this.isStopped?F(Y,this):(this.isStopped=!0,this._complete())},r.prototype.unsubscribe=function(){this.closed||(this.isStopped=!0,t.prototype.unsubscribe.call(this),this.destination=null)},r.prototype._next=function(e){this.destination.next(e)},r.prototype._error=function(e){try{this.destination.error(e)}finally{this.unsubscribe()}},r.prototype._complete=function(){try{this.destination.complete()}finally{this.unsubscribe()}},r})(S);var tr=Function.prototype.bind;function C(t,r){return tr.call(t,r)}var or=(function(){function t(r){this.partialObserver=r}return t.prototype.next=function(r){var e=this.partialObserver;if(e.next)try{e.next(r)}catch(o){P(o)}},t.prototype.error=function(r){var e=this.partialObserver;if(e.error)try{e.error(r)}catch(o){P(o)}else P(r)},t.prototype.complete=function(){var r=this.partialObserver;if(r.complete)try{r.complete()}catch(e){P(e)}},t})(),U=(function(t){b(r,t);function r(e,o,n){var i=t.call(this)||this,a;if(p(e)||!e)a={next:e??void 0,error:o??void 0,complete:n??void 0};else{var f;i&&m.useDeprecatedNextContext?(f=Object.create(e),f.unsubscribe=function(){return i.unsubscribe()},a={next:e.next&&C(e.next,f),error:e.error&&C(e.error,f),complete:e.complete&&C(e.complete,f)}):a=e}return i.destination=new or(a),i}return r})(L);function P(t){m.useDeprecatedSynchronousErrorHandling?J(t):H(t)}function nr(t){throw t}function F(t,r){var e=m.onStoppedNotification;e&&x.setTimeout(function(){return e(t,r)})}var ir={closed:!0,next:M,error:nr,complete:M};function ar(t){return p(t?.lift)}function O(t){return function(r){if(ar(r))return r.lift(function(e){try{return t(e,this)}catch(o){this.error(o)}});throw new TypeError("Unable to lift unknown Observable type")}}function Q(t,r,e,o,n){return new fr(t,r,e,o,n)}var fr=(function(t){b(r,t);function r(e,o,n,i,a,f){var s=t.call(this,e)||this;return s.onFinalize=a,s.shouldUnsubscribe=f,s._next=o?function(c){try{o(c)}catch(l){e.error(l)}}:t.prototype._next,s._error=i?function(c){try{i(c)}catch(l){e.error(l)}finally{this.unsubscribe()}}:t.prototype._error,s._complete=n?function(){try{n()}catch(c){e.error(c)}finally{this.unsubscribe()}}:t.prototype._complete,s}return r.prototype.unsubscribe=function(){var e;if(!this.shouldUnsubscribe||this.shouldUnsubscribe()){var o=this.closed;t.prototype.unsubscribe.call(this),!o&&((e=this.onFinalize)===null||e===void 0||e.call(this))}},r})(L);var X=_(function(t){return function(){t(this),this.name="EmptyError",this.message="no elements in sequence"}});function D(t,r){var e=typeof r=="object";return new Promise(function(o,n){var i=new U({next:function(a){o(a),i.unsubscribe()},error:n,complete:function(){e?o(r.defaultValue):n(new X)}});t.subscribe(i)})}function Z(t,r,e,o,n){return function(i,a){var f=e,s=r,c=0;i.subscribe(Q(a,function(l){var w=c++;s=f?t(s,l,w):(f=!0,l),o&&a.next(s)},n&&(function(){f&&a.next(s),a.complete()})))}}function rr(t,r){return O(Z(t,r,arguments.length>=2,!1,!0))}var sr=function(t,r){return t.push(r),t};function N(){return O(function(t,r){rr(sr,[])(t).subscribe(r)})}var W=class extends B{async configurePre(){let r=this.getSubAdapter;if(r)return(await r({...k(this.config),type:"BgzipFastaAdapter"})).dataAdapter;throw new Error("no get subadapter")}configure(){return this.configureP??=this.configurePre().catch(r=>{throw this.configureP=void 0,r}),this.configureP}async readMsaIndex(r){let e=await v(r,this.pluginManager).readFile("utf8");return new Map(V(e).map(o=>[o.name,o]))}async loadMsaIndex(){let r=this.getConf("msaIndexLocation"),e=await this.configure(),o=ur(e.getConf("fastaLocation"),".idx");return"uri"in r&&!r.uri?o?this.readMsaIndex(o).catch(()=>{}):void 0:this.readMsaIndex(r)}getMsaIndex(){return this.msaIndexP??=this.loadMsaIndex().catch(r=>{throw this.msaIndexP=void 0,r}),this.msaIndexP}async groupRows(){let r=await this.configure(),e=new RegExp(this.getConf("msaRegex")),o=new Map;for(let n of await r.getRefNames()){let i=n.split(e)[0],a=o.get(i);a?a.push(n):o.set(i,[n])}return o}getMSARows(){return this.msaRowsP??=this.groupRows().catch(r=>{throw this.msaRowsP=void 0,r}),this.msaRowsP}async getMSAList(){return[...(await this.getMsaIndex()??await this.getMSARows()).keys()]}async readIndexedMSA(r,e){let o=await this.configure(),n=r.get(e);return n?q({filehandle:v(o.getConf("fastaLocation"),this.pluginManager),gziFilehandle:v(o.getConf("gziLocation"),this.pluginManager),entry:n}):""}async readRowsMSA(r){let e=await this.configure(),o=(await this.getMSARows()).get(r)??[];return(await D(e.getFeaturesInMultipleRegions(o.map(i=>({refName:i,start:0,end:1e9,assemblyName:""}))).pipe(N()))).map(i=>`>${i.get("refName")}
4
+ ${i.get("seq")}`).join(`
5
+ `)}async getMSA(r){let e=await this.getMsaIndex();return{msa:await(e?this.readIndexedMSA(e,r):this.readRowsMSA(r))}}};function ur(t,r){return"uri"in t?{...t,uri:`${t.uri}${r}`}:"localPath"in t?{...t,localPath:`${t.localPath}${r}`}:void 0}export{W as default};
6
+ //# sourceMappingURL=BgzipFastaMsaAdapter-Y3LYQRQL.js.map