jbrowse-plugin-msaview 3.9.0 → 4.0.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.
Files changed (156) hide show
  1. package/dist/AddHighlightModel/GenomeMouseoverHighlight.js +1 -1
  2. package/dist/AddHighlightModel/index.js +1 -1
  3. package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.d.ts +2 -8
  4. package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +10 -3
  5. package/dist/LaunchMsaView/cleanProteinSequence.test.js +36 -2
  6. package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.js +8 -10
  7. package/dist/LaunchMsaView/components/BlastQuery/BlastManualPanel.js +9 -11
  8. package/dist/LaunchMsaView/components/BlastQuery/CachedBlastResults.js +3 -1
  9. package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +14 -16
  10. package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.js +16 -18
  11. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +17 -19
  12. package/dist/LaunchMsaView/components/launchConnectedView.d.ts +2 -2
  13. package/dist/LaunchMsaView/components/launchConnectedView.js +4 -8
  14. package/dist/LaunchMsaView/components/useFeatureSequence.js +3 -6
  15. package/dist/LaunchMsaView/index.js +2 -1
  16. package/dist/LaunchMsaView/launchTarget.test.js +1 -1
  17. package/dist/LaunchMsaViewExtensionPoint/index.js +3 -3
  18. package/dist/LaunchMsaViewExtensionPoint/index.test.js +24 -21
  19. package/dist/MsaViewPanel/index.js +3 -3
  20. package/dist/MsaViewPanel/isMsaView.d.ts +4 -0
  21. package/dist/MsaViewPanel/isMsaView.js +3 -0
  22. package/dist/MsaViewPanel/model.d.ts +638 -212
  23. package/dist/MsaViewPanel/model.js +0 -3
  24. package/dist/MsaViewPanel/resolveConnectedTranscript.js +7 -13
  25. package/dist/MsaViewPanel/resolveConnectedTranscript.test.js +2 -2
  26. package/dist/MsaViewPanel/transcriptMap.js +27 -8
  27. package/dist/MsaViewPanel/transcriptMap.test.js +19 -0
  28. package/dist/chunks/AboutDialog-WT6GXMXU.js +2 -0
  29. package/dist/chunks/AboutDialog-WT6GXMXU.js.map +7 -0
  30. package/dist/chunks/AnnotationFileDialog-34R7LFHB.js +2 -0
  31. package/dist/chunks/AnnotationFileDialog-34R7LFHB.js.map +7 -0
  32. package/dist/chunks/BgzipFastaMsaAdapter-ZMUARSUQ.js +4 -0
  33. package/dist/chunks/BgzipFastaMsaAdapter-ZMUARSUQ.js.map +7 -0
  34. package/dist/chunks/ExportSVGDialog-NMVUHFGY.js +2 -0
  35. package/dist/chunks/ExportSVGDialog-NMVUHFGY.js.map +7 -0
  36. package/dist/chunks/FeatureDialog-XNHIA5PS.js +2 -0
  37. package/dist/chunks/FeatureDialog-XNHIA5PS.js.map +7 -0
  38. package/dist/chunks/HelpDialog-DF4Y536X.js +2 -0
  39. package/dist/chunks/HelpDialog-DF4Y536X.js.map +7 -0
  40. package/dist/chunks/LaunchMsaViewDialog-54LBEZHK.js +5 -0
  41. package/dist/chunks/LaunchMsaViewDialog-54LBEZHK.js.map +7 -0
  42. package/dist/chunks/MetadataDialog-2WT6EX4M.js +2 -0
  43. package/dist/chunks/MetadataDialog-2WT6EX4M.js.map +7 -0
  44. package/dist/chunks/MsaViewPanel-YYP4CYYO.js +2 -0
  45. package/dist/chunks/MsaViewPanel-YYP4CYYO.js.map +7 -0
  46. package/dist/chunks/TrackInfoDialog-TYCMLUKG.js +2 -0
  47. package/dist/chunks/TrackInfoDialog-TYCMLUKG.js.map +7 -0
  48. package/dist/chunks/TreeNodeInfoDialog-BFF6JRCO.js +2 -0
  49. package/dist/chunks/TreeNodeInfoDialog-BFF6JRCO.js.map +7 -0
  50. package/dist/chunks/chunk-2IWD4OO4.js +2 -0
  51. package/dist/chunks/chunk-2IWD4OO4.js.map +7 -0
  52. package/dist/chunks/chunk-5ORPTTMR.js +2 -0
  53. package/dist/chunks/chunk-5ORPTTMR.js.map +7 -0
  54. package/dist/chunks/chunk-6YFCSBEE.js +2 -0
  55. package/dist/chunks/chunk-6YFCSBEE.js.map +7 -0
  56. package/dist/chunks/chunk-73KAPCJ2.js +2 -0
  57. package/dist/chunks/chunk-73KAPCJ2.js.map +7 -0
  58. package/dist/chunks/chunk-BX4Z274Q.js +2 -0
  59. package/dist/chunks/chunk-BX4Z274Q.js.map +7 -0
  60. package/dist/chunks/chunk-CVLB4VFP.js +2 -0
  61. package/dist/chunks/chunk-CVLB4VFP.js.map +7 -0
  62. package/dist/chunks/chunk-DNFD5II2.js +2 -0
  63. package/dist/chunks/chunk-DNFD5II2.js.map +7 -0
  64. package/dist/chunks/chunk-ENTWPX2K.js +6 -0
  65. package/dist/chunks/chunk-ENTWPX2K.js.map +7 -0
  66. package/dist/chunks/chunk-EO7URJUB.js +2 -0
  67. package/dist/chunks/chunk-EO7URJUB.js.map +7 -0
  68. package/dist/chunks/chunk-FCNT6B47.js +2 -0
  69. package/dist/chunks/chunk-FCNT6B47.js.map +7 -0
  70. package/dist/chunks/chunk-FCWXYIFC.js +2 -0
  71. package/dist/chunks/chunk-FCWXYIFC.js.map +7 -0
  72. package/dist/chunks/chunk-FY3BTKY4.js +2 -0
  73. package/dist/chunks/chunk-FY3BTKY4.js.map +7 -0
  74. package/dist/chunks/chunk-IBBQUV2I.js +2 -0
  75. package/dist/chunks/chunk-IBBQUV2I.js.map +7 -0
  76. package/dist/chunks/chunk-J6KYLXRD.js +2 -0
  77. package/dist/chunks/chunk-J6KYLXRD.js.map +7 -0
  78. package/dist/chunks/chunk-MBI4TMKR.js +2 -0
  79. package/dist/chunks/chunk-MBI4TMKR.js.map +7 -0
  80. package/dist/chunks/chunk-NSASZ5FM.js +2 -0
  81. package/dist/chunks/chunk-NSASZ5FM.js.map +7 -0
  82. package/dist/chunks/chunk-O7JNDIKD.js +2 -0
  83. package/dist/chunks/chunk-O7JNDIKD.js.map +7 -0
  84. package/dist/chunks/chunk-P77DPK6B.js +2 -0
  85. package/dist/chunks/chunk-P77DPK6B.js.map +7 -0
  86. package/dist/chunks/chunk-PJZ3XTMG.js +6 -0
  87. package/dist/chunks/chunk-PJZ3XTMG.js.map +7 -0
  88. package/dist/chunks/chunk-QAC6JSDZ.js +5 -0
  89. package/dist/chunks/chunk-QAC6JSDZ.js.map +7 -0
  90. package/dist/chunks/chunk-QMLPITS4.js +18 -0
  91. package/dist/chunks/chunk-QMLPITS4.js.map +7 -0
  92. package/dist/chunks/chunk-RKVHQK52.js +3 -0
  93. package/dist/chunks/chunk-RKVHQK52.js.map +7 -0
  94. package/dist/chunks/chunk-RTKNCGDF.js +8 -0
  95. package/dist/chunks/chunk-RTKNCGDF.js.map +7 -0
  96. package/dist/chunks/chunk-TITP6LCV.js +2 -0
  97. package/dist/chunks/chunk-TITP6LCV.js.map +7 -0
  98. package/dist/chunks/chunk-YUKJI2AH.js +2 -0
  99. package/dist/chunks/chunk-YUKJI2AH.js.map +7 -0
  100. package/dist/chunks/chunk-ZTQYZDWK.js +2 -0
  101. package/dist/chunks/chunk-ZTQYZDWK.js.map +7 -0
  102. package/dist/chunks/dist-2T65ZS4E.js +2 -0
  103. package/dist/chunks/dist-2T65ZS4E.js.map +7 -0
  104. package/dist/chunks/dist-UX5CKLQJ.js +2 -0
  105. package/dist/chunks/dist-UX5CKLQJ.js.map +7 -0
  106. package/dist/chunks/model-KEZXRCK6.js +12 -0
  107. package/dist/chunks/model-KEZXRCK6.js.map +7 -0
  108. package/dist/chunks/renderToSvg-B7RCHRO3.js +2 -0
  109. package/dist/chunks/renderToSvg-B7RCHRO3.js.map +7 -0
  110. package/dist/config.json +1 -1
  111. package/dist/index.d.ts +11 -8
  112. package/dist/index.js +4 -1
  113. package/dist/jbrowse-plugin-msaview.esm.js +3 -0
  114. package/dist/jbrowse-plugin-msaview.esm.js.map +7 -0
  115. package/dist/utils/launchMsaView.d.ts +1 -1
  116. package/dist/utils/launchMsaView.js +5 -2
  117. package/dist/utils/workspaces.test.js +5 -5
  118. package/dist/version.d.ts +1 -1
  119. package/dist/version.js +1 -1
  120. package/package.json +19 -20
  121. package/src/AddHighlightModel/GenomeMouseoverHighlight.tsx +1 -1
  122. package/src/AddHighlightModel/index.tsx +1 -1
  123. package/src/LaunchMsaView/cleanProteinSequence.test.ts +37 -2
  124. package/src/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.tsx +3 -3
  125. package/src/LaunchMsaView/components/BlastQuery/BlastManualPanel.tsx +3 -3
  126. package/src/LaunchMsaView/components/BlastQuery/CachedBlastResults.tsx +3 -1
  127. package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +3 -3
  128. package/src/LaunchMsaView/components/OrthologQuery/OrthologPanel.tsx +3 -3
  129. package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +3 -3
  130. package/src/LaunchMsaView/components/launchConnectedView.ts +5 -8
  131. package/src/LaunchMsaView/components/useFeatureSequence.ts +3 -6
  132. package/src/LaunchMsaView/index.ts +6 -1
  133. package/src/LaunchMsaView/launchTarget.test.ts +1 -1
  134. package/src/LaunchMsaView/launchTarget.ts +2 -3
  135. package/src/LaunchMsaViewExtensionPoint/index.test.ts +26 -21
  136. package/src/LaunchMsaViewExtensionPoint/index.ts +3 -4
  137. package/src/MsaViewPanel/index.ts +3 -4
  138. package/src/MsaViewPanel/isMsaView.ts +7 -0
  139. package/src/MsaViewPanel/model.ts +0 -6
  140. package/src/MsaViewPanel/resolveConnectedTranscript.test.ts +2 -2
  141. package/src/MsaViewPanel/resolveConnectedTranscript.ts +7 -17
  142. package/src/MsaViewPanel/transcriptMap.test.ts +23 -0
  143. package/src/MsaViewPanel/transcriptMap.ts +36 -9
  144. package/src/index.ts +4 -1
  145. package/src/utils/launchMsaView.ts +5 -2
  146. package/src/utils/useFetch.ts +1 -1
  147. package/src/utils/workspaces.test.ts +5 -5
  148. package/src/version.ts +1 -1
  149. package/dist/LaunchMsaView/components/calculateProteinSequence.d.ts +0 -21
  150. package/dist/LaunchMsaView/components/calculateProteinSequence.js +0 -71
  151. package/dist/LaunchMsaView/components/types.d.ts +0 -6
  152. package/dist/LaunchMsaView/components/types.js +0 -1
  153. package/dist/jbrowse-plugin-msaview.umd.production.min.js +0 -143
  154. package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +0 -7
  155. package/src/LaunchMsaView/components/calculateProteinSequence.ts +0 -113
  156. package/src/LaunchMsaView/components/types.ts +0 -8
@@ -0,0 +1,2 @@
1
+ import{a as t}from"./chunk-FY3BTKY4.js";var o=t((r,e)=>{e.exports=JBrowseExports["mobx-state-tree"]});export{o as a};
2
+ //# sourceMappingURL=chunk-FCWXYIFC.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["global-externals:@jbrowse/mobx-state-tree"],
4
+ "sourcesContent": ["module.exports = JBrowseExports[\"mobx-state-tree\"];"],
5
+ "mappings": "wCAAA,IAAAA,EAAAC,EAAA,CAAAC,EAAAC,IAAA,CAAAA,EAAO,QAAU,eAAe,iBAAiB",
6
+ "names": ["require_mobx_state_tree", "__commonJSMin", "exports", "module"]
7
+ }
@@ -0,0 +1,2 @@
1
+ var g=Object.create;var f=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var i=Object.getOwnPropertyNames;var j=Object.getPrototypeOf,k=Object.prototype.hasOwnProperty;var m=(b,a)=>()=>{try{return a||b((a={exports:{}}).exports,a),a.exports}catch(c){throw a=0,c}};var l=(b,a,c,e)=>{if(a&&typeof a=="object"||typeof a=="function")for(let d of i(a))!k.call(b,d)&&d!==c&&f(b,d,{get:()=>a[d],enumerable:!(e=h(a,d))||e.enumerable});return b};var n=(b,a,c)=>(c=b!=null?g(j(b)):{},l(a||!b||!b.__esModule?f(c,"default",{value:b,enumerable:!0}):c,b));export{m as a,n as b};
2
+ //# sourceMappingURL=chunk-FY3BTKY4.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,2 @@
1
+ import{a as e,b as x}from"./chunk-FY3BTKY4.js";var p=e((i,r)=>{r.exports=JBrowseExports["@jbrowse/core/util"]});var t=e((u,o)=>{o.exports=JBrowseExports["@mui/material/SvgIcon"]});var c=e((m,s)=>{s.exports=JBrowseExports["react/jsx-runtime"]});var a=x(t(),1);export{p as a,t as b,a as c,c as d};
2
+ //# sourceMappingURL=chunk-IBBQUV2I.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["global-externals:@jbrowse/core/util", "global-externals:@mui/material/SvgIcon", "global-externals:react/jsx-runtime", "../../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/utils/createSvgIcon.mjs"],
4
+ "sourcesContent": ["module.exports = JBrowseExports[\"@jbrowse/core/util\"];", "module.exports = JBrowseExports[\"@mui/material/SvgIcon\"];", "module.exports = JBrowseExports[\"react/jsx-runtime\"];", "'use client';\n\nexport { createSvgIcon as default } from '@mui/material/SvgIcon';"],
5
+ "mappings": "+CAAA,IAAAA,EAAAC,EAAA,CAAAC,EAAAC,IAAA,CAAAA,EAAO,QAAU,eAAe,oBAAoB,ICApD,IAAAC,EAAAC,EAAA,CAAAC,EAAAC,IAAA,CAAAA,EAAO,QAAU,eAAe,uBAAuB,ICAvD,IAAAC,EAAAC,EAAA,CAAAC,EAAAC,IAAA,CAAAA,EAAO,QAAU,eAAe,mBAAmB,ICEnD,IAAAC,EAAyC",
6
+ "names": ["require_util", "__commonJSMin", "exports", "module", "require_SvgIcon", "__commonJSMin", "exports", "module", "require_jsx_runtime", "__commonJSMin", "exports", "module", "import_SvgIcon"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{a as e}from"./chunk-FY3BTKY4.js";var o=e((s,r)=>{r.exports=JBrowseExports.react});export{o as a};
2
+ //# sourceMappingURL=chunk-J6KYLXRD.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["global-externals:react"],
4
+ "sourcesContent": ["module.exports = JBrowseExports[\"react\"];"],
5
+ "mappings": "wCAAA,IAAAA,EAAAC,EAAA,CAAAC,EAAAC,IAAA,CAAAA,EAAO,QAAU,eAAe",
6
+ "names": ["require_react", "__commonJSMin", "exports", "module"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{i as u,p,t as l}from"./chunk-PJZ3XTMG.js";function k(e,t){let n=e.sciname.replaceAll(" ","_");if(e.taxid&&t?.has(e.taxid)){let s=t.get(e.taxid);s.commonName&&(n=s.commonName.split(" ").map(r=>r.charAt(0).toUpperCase()+r.slice(1).toLowerCase()).join("_"))}return`${e.accession}-${n}`}function h(e){return e.replaceAll("-","")}var g="ncbiblast";function b(e){return(e.hits??[]).map(t=>{let n=Number.parseInt(t.hit_uni_ox??"",10);return{description:[{accession:t.hit_acc??"unknown",id:t.hit_id??t.hit_acc??"unknown",sciname:t.hit_uni_os??t.hit_os??"unknown",taxid:Number.isNaN(n)?void 0:n,title:t.hit_uni_de??t.hit_desc}],hsps:(t.hit_hsps??[]).flatMap(s=>s.hsp_hseq?[{hseq:s.hsp_hseq}]:[])}})}function R(e){return`https://www.ebi.ac.uk/jdispatcher/sss/${g}/summary?jobId=${e}`}async function w({query:e,blastDatabase:t,maxHits:n,onProgress:s,onRid:r,signal:i}){let o=n?String(p(n)):void 0,a=await u({tool:g,label:"BLAST",params:{program:"blastp",stype:"protein",database:t,sequence:e,...o?{alignments:o,scores:o}:{}},onProgress:s,onRid:r,signal:i}),c=b(JSON.parse(await a.result("json")));if(c.length===0)throw new Error("No hits found");return{rid:a.jobId,hits:c}}var j=async({database:e,...t})=>{let{hits:n,rid:s}=await w({blastDatabase:e,...t});return{rid:s,hits:n.map(r=>({...r.description[0]??{accession:"unknown",id:"unknown",sciname:"unknown"},sequence:h(r.hsps[0]?.hseq??"")}))}};var d="hmmer3_phmmer";function O(e){return`https://www.ebi.ac.uk/jdispatcher/sss/${d}/summary?jobId=${e}`}function H(e){return e.startsWith(`${d}-`)}function x({rf:e,query:t}){let n=0,s=Array.from(e,r=>r==="x"?t[n++]??"-":"-").join("");if(n!==t.length)throw new Error(`phmmer alignment has ${n} match columns for a query of ${t.length} residues, so the query row cannot be placed`);return s}function _(e){let t=(e??"").replace("[subseq from] ","");if(t.includes("^|^")){let r=t.split("^|^"),i=r[6];return{id:r[0]||void 0,sciname:r[5]||"unknown",taxid:i&&/^\d+$/.test(i)?Number.parseInt(i,10):void 0,title:r[2]||void 0}}let n=/OS=(.*?)\s+(?:OX|GN|PE|SV)=/.exec(t)?.[1],s=/OX=(\d+)/.exec(t)?.[1];return{id:void 0,sciname:n??"unknown",taxid:s?Number.parseInt(s,10):void 0,title:t.split(" OS=")[0]||void 0}}function y(e){let t=e.lastIndexOf("/"),n=t===-1?void 0:/^\d+-\d+$/.exec(e.slice(t+1))?.[0],s=n===void 0?e:e.slice(0,t),r=s.split("|");return r.length===3?{accession:r[1],id:r[2],range:n}:{accession:s,id:s,range:n}}function S({stockholm:e,query:t}){let{gc:n,gs:s,seqdata:r,seqname:i}=new l(e,0).getMSA(),o=n.RF;if(!o)throw new Error("phmmer alignment has no #=GC RF line");return{queryRow:x({rf:o,query:t}),rows:i.map(a=>{let{id:c,...f}=_(s.DE?.[a]?.[0]),m=y(a);return{...m,id:m.id===m.accession&&c?c:m.id,...f,aligned:(r[a]??"").replaceAll(".","-").toUpperCase()}})}}async function q({query:e,database:t,maxHits:n,onProgress:s,onRid:r,signal:i}){let o=await u({tool:d,label:"phmmer",params:{database:t,sequence:e,alignView:"true",...n?{nhits:String(n)}:{}},onProgress:s,onRid:r,signal:i}),a=S({stockholm:await o.result("sto"),query:e});if(a.rows.length===0)throw new Error("No hits found");return{rid:o.jobId,...a}}function B(e){return e.map(({aligned:t,...n})=>({...n,sequence:t}))}var J=async({database:e,...t})=>{let{rows:n,queryRow:s,rid:r}=await q({database:e,...t});return{rid:r,queryRow:s,hits:B(n)}};export{k as a,R as b,j as c,O as d,H as e,J as f};
2
+ //# sourceMappingURL=chunk-MBI4TMKR.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/LaunchMsaView/components/util.ts", "../../src/utils/ebiBlast.ts", "../../src/utils/phmmer.ts"],
4
+ "sourcesContent": ["import type { TaxonomyInfo } from '../../utils/taxonomyNames'\n\nexport function makeId(\n h: { accession: string; sciname: string; taxid?: number },\n taxonomyInfo?: Map<number, TaxonomyInfo>,\n) {\n let speciesName = h.sciname.replaceAll(' ', '_')\n if (h.taxid && taxonomyInfo?.has(h.taxid)) {\n const info = taxonomyInfo.get(h.taxid)!\n if (info.commonName) {\n speciesName = info.commonName\n .split(' ')\n .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join('_')\n }\n }\n return `${h.accession}-${speciesName}`\n}\n\nexport function strip(s: string) {\n return s.replaceAll('-', '')\n}\n", "import { snapBlastHitCount } from '../LaunchMsaView/components/BlastQuery/consts'\nimport { strip } from '../LaunchMsaView/components/util'\nimport { runEbiJob } from './ebiJobDispatcher'\n\nimport type { BlastDatabase } from '../LaunchMsaView/components/BlastQuery/consts'\nimport type { SearchBackend } from './homologSearch'\nimport type { BlastHit } from './types'\n\nconst TOOL = 'ncbiblast'\n\n/**\n * The subset of EBI's ncbiblast JSON result this plugin reads. The service\n * returns a great deal more per hit (urls, bit scores, e-values, the match\n * string); only what the MSA rows are built from is typed here.\n */\ninterface EbiBlastJson {\n hits?: {\n hit_acc?: string\n hit_id?: string\n hit_desc?: string\n /** the UniProt fields are absent on hits from non-UniProt databases */\n hit_os?: string\n hit_uni_de?: string\n hit_uni_os?: string\n /** NCBI taxon id, delivered as a string */\n hit_uni_ox?: string\n hit_hsps?: { hsp_hseq?: string }[]\n }[]\n}\n\n/**\n * Map EBI's hit shape onto the normalized one. Exported for testing against a\n * captured response \u2014 the field names are the whole risk here, and nothing else\n * in CI would notice if EBI renamed one.\n */\nexport function normalizeEbiBlastHits(result: EbiBlastJson): BlastHit[] {\n return (result.hits ?? []).map(hit => {\n const taxid = Number.parseInt(hit.hit_uni_ox ?? '', 10)\n return {\n description: [\n {\n accession: hit.hit_acc ?? 'unknown',\n id: hit.hit_id ?? hit.hit_acc ?? 'unknown',\n sciname: hit.hit_uni_os ?? hit.hit_os ?? 'unknown',\n taxid: Number.isNaN(taxid) ? undefined : taxid,\n // hit_uni_de is the bare protein name; hit_desc repeats it with the\n // OS=/OX=/GN= suffix that makeId already covers with real columns\n title: hit.hit_uni_de ?? hit.hit_desc,\n },\n ],\n hsps: (hit.hit_hsps ?? []).flatMap(hsp =>\n hsp.hsp_hseq ? [{ hseq: hsp.hsp_hseq }] : [],\n ),\n }\n })\n}\n\n/**\n * Human-facing link to a job, shown while it runs and on error \u2014 so it has to\n * be EBI's own results UI, not the REST result endpoint, which does not exist\n * yet at the moment the link is on screen.\n */\nexport function ebiBlastResultUrl(jobId: string) {\n return `https://www.ebi.ac.uk/jdispatcher/sss/${TOOL}/summary?jobId=${jobId}`\n}\n\nexport async function queryEbiBlast({\n query,\n blastDatabase,\n maxHits,\n onProgress,\n onRid,\n signal,\n}: {\n query: string\n blastDatabase: BlastDatabase\n /** rounded up to a count EBI accepts; their default of 50 when omitted */\n maxHits?: number\n onProgress: (arg: string) => void\n onRid: (arg: string) => void\n signal?: AbortSignal\n}) {\n const hitCount = maxHits ? String(snapBlastHitCount(maxHits)) : undefined\n const job = await runEbiJob({\n tool: TOOL,\n label: 'BLAST',\n params: {\n program: 'blastp',\n stype: 'protein',\n database: blastDatabase,\n sequence: query,\n ...(hitCount ? { alignments: hitCount, scores: hitCount } : {}),\n },\n onProgress,\n onRid,\n signal,\n })\n const hits = normalizeEbiBlastHits(\n JSON.parse(await job.result('json')) as EbiBlastJson,\n )\n if (hits.length === 0) {\n throw new Error('No hits found')\n }\n return { rid: job.jobId, hits }\n}\n\n/**\n * BLAST as a search backend. Its alignments are pairwise and one hit at a\n * time, so they are stripped back off and the hits go to an aligner as bare\n * sequences: no `queryRow`.\n */\nexport const searchEbiBlast: SearchBackend = async ({\n database,\n ...request\n}) => {\n const { hits, rid } = await queryEbiBlast({\n blastDatabase: database as BlastDatabase,\n ...request,\n })\n return {\n rid,\n hits: hits.map(hit => ({\n ...(hit.description[0] ?? {\n accession: 'unknown',\n id: 'unknown',\n sciname: 'unknown',\n }),\n sequence: strip(hit.hsps[0]?.hseq ?? ''),\n })),\n }\n}\n", "import { StockholmMSA } from 'msa-parsers'\n\nimport { runEbiJob } from './ebiJobDispatcher'\n\nimport type { PhmmerDatabase } from '../LaunchMsaView/components/BlastQuery/consts'\nimport type { SearchBackend, SearchHit } from './homologSearch'\n\nconst TOOL = 'hmmer3_phmmer'\n\nexport interface PhmmerRow {\n accession: string\n id: string\n sciname: string\n taxid?: number\n title?: string\n /** the matched envelope on the target, e.g. '503-912', absent if unparseable */\n range?: string\n /** the row as phmmer aligned it, uppercased with '.' inserts turned into '-' */\n aligned: string\n}\n\nexport interface PhmmerAlignment {\n rows: PhmmerRow[]\n /**\n * the query, placed into the same columns. phmmer does not put the query in\n * its own output, so this is derived \u2014 see buildQueryRow.\n */\n queryRow: string\n}\n\n/**\n * Human-facing link to a job, shown while it runs and on error.\n *\n * The category has to be sss: jdispatcher serves its shell with a 200 for any\n * category, so /pfa/ and /psa/ look fine to a fetch and render \"Page Not Found\"\n * in a browser.\n */\nexport function phmmerResultUrl(jobId: string) {\n return `https://www.ebi.ac.uk/jdispatcher/sss/${TOOL}/summary?jobId=${jobId}`\n}\n\n/** EBI job ids are prefixed with the tool that made them */\nexport function isPhmmerJobId(jobId: string) {\n return jobId.startsWith(`${TOOL}-`)\n}\n\n/**\n * phmmer aligns every hit to a profile built from the query, one match state\n * per query residue, and marks those columns 'x' in #=GC RF. So the query's own\n * row is exactly recoverable: walk RF, consume a query residue at each match\n * column, gap everywhere else.\n *\n * This is the one piece of real logic here rather than a library call, and it\n * is checked hard: if the match columns do not account for the query exactly,\n * the columns and the query have drifted apart, and a query row that is off by\n * even one residue would silently mis-map every column to the genome. Throwing\n * is much better than drawing that.\n */\nfunction buildQueryRow({ rf, query }: { rf: string; query: string }) {\n let consumed = 0\n const row = Array.from(rf, c =>\n c === 'x' ? (query[consumed++] ?? '-') : '-',\n ).join('')\n if (consumed !== query.length) {\n throw new Error(\n `phmmer alignment has ${consumed} match columns for a query of ${query.length} residues, so the query row cannot be placed`,\n )\n }\n return row\n}\n\n/**\n * '[subseq from] Albumin OS=Homo sapiens OX=9606 GN=ALB PE=1 SV=2' is what a\n * UniProt target's #=GS DE looks like. The representative-proteome databases\n * (rp15..rp75) write the same facts as caret-pipe fields instead --\n * 'P53_HUMAN^|^DNHU53^|^Cellular tumor antigen p53^|^...^|^Homo sapiens^|^9606^|^Euk/mammal^|^40674'\n * -- so both grammars are read. Hits from the non-UniProt databases phmmer\n * also offers (PDB, AlphaFold, MEROPS...) carry neither, so every field here\n * is optional.\n */\nfunction parseDescription(de: string | undefined) {\n const text = (de ?? '').replace('[subseq from] ', '')\n if (text.includes('^|^')) {\n const fields = text.split('^|^')\n const ox = fields[6]\n return {\n id: fields[0] || undefined,\n sciname: fields[5] || 'unknown',\n taxid: ox && /^\\d+$/.test(ox) ? Number.parseInt(ox, 10) : undefined,\n title: fields[2] || undefined,\n }\n }\n const sciname = /OS=(.*?)\\s+(?:OX|GN|PE|SV)=/.exec(text)?.[1]\n const ox = /OX=(\\d+)/.exec(text)?.[1]\n return {\n id: undefined,\n sciname: sciname ?? 'unknown',\n taxid: ox ? Number.parseInt(ox, 10) : undefined,\n title: text.split(' OS=')[0] || undefined,\n }\n}\n\n/**\n * Target names look like 'sp|P02768|ALBU_HUMAN/1-609' for UniProt databases and\n * like anything at all for the others, so an unrecognized name becomes its own\n * accession rather than being dropped.\n */\nfunction parseName(name: string) {\n const slash = name.lastIndexOf('/')\n const range =\n slash === -1 ? undefined : /^\\d+-\\d+$/.exec(name.slice(slash + 1))?.[0]\n const bare = range === undefined ? name : name.slice(0, slash)\n const parts = bare.split('|')\n return parts.length === 3\n ? { accession: parts[1]!, id: parts[2]!, range }\n : { accession: bare, id: bare, range }\n}\n\n/**\n * Exported for testing against a captured .sto \u2014 the annotation names (RF, the\n * DE line's OS=/OX=) are the whole risk in this mapping, and nothing else in CI\n * would notice if HMMER or EBI changed one.\n */\nexport function parsePhmmerAlignment({\n stockholm,\n query,\n}: {\n stockholm: string\n query: string\n}): PhmmerAlignment {\n const { gc, gs, seqdata, seqname } = new StockholmMSA(stockholm, 0).getMSA()\n const rf = gc.RF\n if (!rf) {\n throw new Error('phmmer alignment has no #=GC RF line')\n }\n\n return {\n queryRow: buildQueryRow({ rf, query }),\n rows: seqname.map(name => {\n const { id, ...description } = parseDescription(gs.DE?.[name]?.[0])\n const parsed = parseName(name)\n return {\n ...parsed,\n // the rp databases name a row by bare accession and put the mnemonic\n // id in the description instead\n id: parsed.id === parsed.accession && id ? id : parsed.id,\n ...description,\n // insert columns come back lowercase with '.' for gaps; the MSA renderer\n // looks colors up by the literal letter, so lowercase would draw\n // uncolored. The insert columns stay visible as gaps in the query row.\n aligned: (seqdata[name] ?? '').replaceAll('.', '-').toUpperCase(),\n }\n }),\n }\n}\n\nexport async function queryPhmmer({\n query,\n database,\n maxHits,\n onProgress,\n onRid,\n signal,\n}: {\n query: string\n database: PhmmerDatabase\n /** EBI's `nhits`, 100 when omitted */\n maxHits?: number\n onProgress: (arg: string) => void\n onRid: (arg: string) => void\n signal?: AbortSignal\n}) {\n const job = await runEbiJob({\n tool: TOOL,\n label: 'phmmer',\n params: {\n database,\n sequence: query,\n // the alignment is the whole point of using phmmer here\n alignView: 'true',\n ...(maxHits ? { nhits: String(maxHits) } : {}),\n },\n onProgress,\n onRid,\n signal,\n })\n const alignment = parsePhmmerAlignment({\n stockholm: await job.result('sto'),\n query,\n })\n if (alignment.rows.length === 0) {\n throw new Error('No hits found')\n }\n return { rid: job.jobId, ...alignment }\n}\n\n/** phmmer's rows as search hits: the aligned row is the hit's sequence. */\nexport function toSearchHits(rows: PhmmerRow[]): SearchHit[] {\n return rows.map(({ aligned, ...rest }) => ({ ...rest, sequence: aligned }))\n}\n\n/**\n * phmmer as a search backend. It aligns every hit to a profile of the query as\n * it searches, so the result carries `queryRow` and no aligner runs after it.\n */\nexport const searchEbiPhmmer: SearchBackend = async ({\n database,\n ...request\n}) => {\n const { rows, queryRow, rid } = await queryPhmmer({\n database: database as PhmmerDatabase,\n ...request,\n })\n return { rid, queryRow, hits: toSearchHits(rows) }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["makeId", "h", "taxonomyInfo", "speciesName", "info", "word", "strip", "s", "TOOL", "normalizeEbiBlastHits", "result", "hit", "taxid", "hsp", "ebiBlastResultUrl", "jobId", "queryEbiBlast", "query", "blastDatabase", "maxHits", "onProgress", "onRid", "signal", "hitCount", "snapBlastHitCount", "job", "runEbiJob", "hits", "searchEbiBlast", "database", "request", "rid", "strip", "TOOL", "phmmerResultUrl", "jobId", "isPhmmerJobId", "buildQueryRow", "rf", "query", "consumed", "row", "c", "parseDescription", "de", "text", "fields", "ox", "sciname", "parseName", "name", "slash", "range", "bare", "parts", "parsePhmmerAlignment", "stockholm", "gc", "gs", "seqdata", "seqname", "StockholmMSA", "id", "description", "parsed", "queryPhmmer", "database", "maxHits", "onProgress", "onRid", "signal", "job", "runEbiJob", "alignment", "toSearchHits", "rows", "aligned", "rest", "searchEbiPhmmer", "request", "queryRow", "rid"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{a as r}from"./chunk-FY3BTKY4.js";var t=r((e,s)=>{s.exports=JBrowseExports["tss-react/mui"]});export{t as a};
2
+ //# sourceMappingURL=chunk-NSASZ5FM.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["global-externals:tss-react/mui"],
4
+ "sourcesContent": ["module.exports = JBrowseExports[\"tss-react/mui\"];"],
5
+ "mappings": "wCAAA,IAAAA,EAAAC,EAAA,CAAAC,EAAAC,IAAA,CAAAA,EAAO,QAAU,eAAe,eAAe",
6
+ "names": ["require_mui", "__commonJSMin", "exports", "module"]
7
+ }
@@ -0,0 +1,2 @@
1
+ function n(c,o){let t=URL.createObjectURL(c),e=document.createElement("a");e.href=t,e.download=o,e.click(),setTimeout(()=>{URL.revokeObjectURL(t)},4e4)}export{n as a};
2
+ //# sourceMappingURL=chunk-O7JNDIKD.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../node_modules/.pnpm/react-msaview@8.4.1_35cd7d1fa06820f3dff395dec9803110/node_modules/react-msaview/src/vendor/fileSaver.ts"],
4
+ "sourcesContent": ["export function saveAs(blob: Blob, name: string) {\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = name\n a.click()\n setTimeout(() => {\n URL.revokeObjectURL(url)\n }, 40_000)\n}\n"],
5
+ "mappings": "AAAM,SAAUA,EAAOC,EAAYC,EAAY,CAC7C,IAAMC,EAAM,IAAI,gBAAgBF,CAAI,EAC9BG,EAAI,SAAS,cAAc,GAAG,EACpCA,EAAE,KAAOD,EACTC,EAAE,SAAWF,EACbE,EAAE,MAAK,EACP,WAAW,IAAK,CACd,IAAI,gBAAgBD,CAAG,CACzB,EAAG,GAAM,CACX",
6
+ "names": ["saveAs", "blob", "name", "url", "a"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{a as m}from"./chunk-BX4Z274Q.js";import{a}from"./chunk-J6KYLXRD.js";import{b as o}from"./chunk-FY3BTKY4.js";var e=o(a(),1),t=o(m(),1);function b({checked:r,label:l,disabled:n,onChange:c}){return e.default.createElement("div",null,e.default.createElement(t.FormControlLabel,{control:e.default.createElement(t.Checkbox,{disabled:n,checked:r,onChange:()=>{c()}}),label:l}))}export{b as a};
2
+ //# sourceMappingURL=chunk-P77DPK6B.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../node_modules/.pnpm/react-msaview@8.4.1_35cd7d1fa06820f3dff395dec9803110/node_modules/react-msaview/src/components/Checkbox2.tsx"],
4
+ "sourcesContent": ["import React from 'react'\n\nimport { Checkbox, FormControlLabel } from '@mui/material'\n\nexport default function Checkbox2({\n checked,\n label,\n disabled,\n onChange,\n}: {\n checked: boolean\n label: string\n disabled?: boolean\n onChange: () => void\n}) {\n return (\n <div>\n <FormControlLabel\n control={\n <Checkbox\n disabled={disabled}\n checked={checked}\n onChange={() => {\n onChange()\n }}\n />\n }\n label={label}\n />\n </div>\n )\n}\n"],
5
+ "mappings": "mHAAA,IAAAA,EAAkB,SAElBC,EAA2C,SAE7B,SAAPC,EAA2B,CAChC,QAAAC,EACA,MAAAC,EACA,SAAAC,EACA,SAAAC,CAAQ,EAMT,CACC,OACE,EAAAC,QAAA,cAAA,MAAA,KACE,EAAAA,QAAA,cAAC,mBAAgB,CACf,QACE,EAAAA,QAAA,cAAC,WAAQ,CACP,SAAUF,EACV,QAASF,EACT,SAAU,IAAK,CACbG,EAAQ,CACV,CAAC,CAAA,EAGL,MAAOF,CAAK,CAAA,CACZ,CAGR",
6
+ "names": ["import_react", "import_material", "Checkbox2", "checked", "label", "disabled", "onChange", "React"]
7
+ }
@@ -0,0 +1,6 @@
1
+ import{a as F,b as O}from"./chunk-QAC6JSDZ.js";import{a as $}from"./chunk-73KAPCJ2.js";import{a as z}from"./chunk-J6KYLXRD.js";import{b as W}from"./chunk-FY3BTKY4.js";var _=W(z());function R(e,t){try{let n=globalThis.localStorage.getItem(e);return n===null?t:JSON.parse(n)}catch(n){return console.error(n),t}}function Pt(e,t){let[n,r]=(0,_.useState)(()=>R(e,t));return[n,o=>{r(o);try{globalThis.localStorage.setItem(e,JSON.stringify(o))}catch(a){console.error(a)}}]}function J(e){try{return new URL(e).host}catch{return e}}async function P(e,t){let n;try{n=await fetch(e,t)}catch(r){throw r instanceof TypeError?new Error(`Could not reach ${J(e)} \u2014 the request failed at the network level. This is usually a CORS restriction (the server refused to let this site read the response), an offline connection, or a blocked request.`,{cause:r}):r}if(!n.ok)throw new Error(`HTTP ${n.status} fetching ${e} ${await n.text()}`);return n}async function w(e,t){return(await P(e,t)).text()}async function kt(e,t){return(await P(e,t)).json()}function N(e,t){return new Promise((n,r)=>{if(t?.aborted){r(new DOMException("Aborted","AbortError"));return}let s=()=>{clearTimeout(o),r(new DOMException("Aborted","AbortError"))},o=setTimeout(()=>{t?.removeEventListener("abort",s),n()},e);t?.addEventListener("abort",s,{once:!0})})}function k(e){return e instanceof Error&&e.name==="AbortError"}async function D({check:e,intervalSeconds:t,onCountdown:n,signal:r}){for(;;){if(r?.throwIfAborted(),await e())return;for(let s=t;s>0;s--)n(s),await N(1e3,r)}}var T="https://www.ebi.ac.uk/Tools/services/rest",V="msa-ebiContactEmail",q="colin.diesh@gmail.com";function X(){return R(V,q).trim()||q}var Y=new Set(["ERROR","FAILURE","NOT_FOUND"]);async function Q({tool:e,params:t,signal:n}){return(await w(`${T}/${e}/run`,{method:"POST",body:new URLSearchParams({email:X(),...t}),signal:n})).trim()}var I=900*1e3;async function Z({tool:e,jobId:t,intervalSeconds:n=10,onCountdown:r,signal:s}){let o;await D({intervalSeconds:n,onCountdown:r,signal:s,check:async()=>{let a;try{a=(await w(`${T}/${e}/status/${t}`,{signal:s})).trim()}catch(i){if(k(i))throw i;if(o??=Date.now(),Date.now()-o>=I)throw new Error(`Could not reach EBI to check ${e} job ${t} for ${Math.round(I/6e4)} minutes`,{cause:i});return console.warn("[msaview] EBI status check failed, retrying:",i),!1}if(o=void 0,a==="FINISHED")return!0;if(Y.has(a))throw new Error(`EBI ${e} job ${t} returned status ${a}`);return!1}})}async function Ut({tool:e,label:t,params:n,onProgress:r,onRid:s,signal:o}){r(`Submitting ${t} to EBI...`);let a=await Q({tool:e,params:n,signal:o});return s?.(a),await Z({tool:e,jobId:a,signal:o,onCountdown:i=>{r(`Re-checking ${t} status in... ${i}`)}}),{jobId:a,result:i=>w(`${T}/${e}/result/${a}/${i}`,{signal:o})}}var vt="https://blast.ncbi.nlm.nih.gov/Blast.cgi",tt=["clustalo","muscle","kalign","mafft"],Ht=[...tt,"browser"],Kt={clustalo:"Clustal Omega (EBI)",muscle:"MUSCLE (EBI)",kalign:"Kalign (EBI)",mafft:"MAFFT (EBI)",browser:"in browser, query-anchored"},et=["uniprotkb_swissprot","uniprotkb","pan_proteomes","uniprotkb_trembl"],rt="uniprotkb_swissprot",Wt=["blastp","phmmer"],zt={blastp:"blastp (sequence search)",phmmer:"phmmer (profile HMM search)"},nt=["swissprot","uniprotkb","uniprotrefprot","rp75","rp55","rp35","rp15"],st="swissprot",L=[5,10,20,50,100,150,200,250,500,750,1e3],Jt=100;function Vt(e){return L.find(t=>t>=e)??L.at(-1)}function Xt(e){return e==="phmmer"?{program:e,database:st}:{program:e,database:rt}}function Yt(e){return e==="phmmer"?nt:et}var ot={uniprotkb_swissprot:"UniProtKB/Swiss-Prot (curated)",swissprot:"UniProtKB/Swiss-Prot (curated)",uniprotkb:"UniProtKB (all entries)",uniprotkb_trembl:"UniProtKB/TrEMBL (unreviewed)",pan_proteomes:"Pan-proteomes",uniprotrefprot:"UniProt reference proteomes",rp75:"Representative proteomes, 75%",rp55:"Representative proteomes, 55%",rp35:"Representative proteomes, 35%",rp15:"Representative proteomes, 15% (widest spread)"};function Qt(e){return ot[e]}function M(e,t="node",n=0){let r=(a,i)=>({...a,id:i,name:a.name||i,children:[]}),s=r(e,`${t}-${n}`),o=[{src:e,out:s,depth:n}];for(;o.length>0;){let{src:a,out:i,depth:c}=o.pop();if(a.children){i.children=a.children.map((u,f)=>r(u,`${i.id}-${f}-${c+1}`));for(let[u,f]of a.children.entries())o.push({src:f,out:i.children[u],depth:c+1})}}return s}var p=class{rows=new Map;names;width;constructor(t){for(let[n,r]of t)this.rows.has(n)||this.rows.set(n,r);this.names=[...this.rows.keys()]}getRow(t){return this.rows.get(t)??""}getNames(){return this.names}getWidth(){return this.width??=this.getNames().reduce((t,n)=>Math.max(t,this.getRow(n).length),0),this.width}getTree(){return{id:"root",name:"root",noTree:!0,children:this.getNames().map(t=>({id:t,children:[],name:t}))}}getRowData(t){}getStructures(){return{}}getHeader(){return{}}get alignmentNames(){return[]}get seqConsensus(){}get secondaryStructureConsensus(){}get tracks(){return[]}};function S(e){return $(e,{postParenNumeric:"name"})}function B(){return{gf:Object.create(null),gc:Object.create(null),gs:Object.create(null),gr:Object.create(null),seqname:[],seqdata:Object.create(null)}}function b(e,t){if(e)return e;if(t)throw new Error("No format header: # STOCKHOLM 1.0");return B()}var j=/^# STOCKHOLM 1.0/,it=/^\/\/\s*$/,at=/^#=GF\s+(\S+)\s+(.*?)\s*$/,ct=/^#=GC\s+(\S+)\s+(.*?)\s*$/,ut=/^#=GS\s+(\S+)\s+(\S+)\s+(.*?)\s*$/,pt=/^#=GR\s+(\S+)\s+(\S+)\s+(.*?)\s*$/,ft=/^\s*([^#\s]\S*)\s+(\S+)\s*$/,lt=/\S/;function x(e){return j.test(e)}function U(e,t){let n=t??{},r=[],s=null,o=!1,a=e.split(`
2
+ `);for(let i of a){let c;if(j.test(i))s&&r.push(s),s=B(),o=!1;else if(it.test(i))s&&r.push(s),s=null;else if(c=at.exec(i)){s=b(s,!!n.strict);let u=c[1],f=s.gf[u]??=[];u==="NH"&&o&&f.length>0?f[f.length-1]+=c[2]:f.push(c[2]),o=u==="NH"}else if(c=ct.exec(i))s=b(s,!!n.strict),s.gc[c[1]]=(s.gc[c[1]]??"")+c[2];else if(c=ut.exec(i)){s=b(s,!!n.strict);let u=s.gs[c[2]]??=Object.create(null);(u[c[1]]??=[]).push(c[3])}else if(c=pt.exec(i)){s=b(s,!!n.strict);let u=s.gr[c[2]]??=Object.create(null);u[c[1]]=(u[c[1]]??"")+c[3]}else if(c=ft.exec(i)){s=b(s,!!n.strict);let u=c[1],f=c[2];s.seqdata[u]===void 0&&(s.seqdata[u]="",s.seqname.push(u)),s.seqdata[u]+=f}else if(lt.test(i)&&n.strict)throw new Error("Malformed line")}return s&&r.push(s),r}var m=class extends p{data;MSA;constructor(t,n){let r=U(t),s=r[n]??r[0];if(!s)throw new Error("No alignments found in Stockholm file");super(s.seqname.map(o=>[o,s.seqdata[o]??""])),this.data=r,this.MSA=s}getMSA(){return this.MSA}get alignmentNames(){return this.data.map((t,n)=>t.gf.DE?.[0]??`Alignment ${n+1}`)}getHeader(){let t=this.MSA.gs.AC??{},n=this.MSA.gs.DR??{};return{General:this.MSA.gf,Accessions:Object.fromEntries(Object.entries(t).map(([r,s])=>[r,s[0]])),Dbxref:Object.fromEntries(Object.entries(n).map(([r,s])=>[r,s.join("; ")]))}}getRowData(t){return{name:t,accession:this.MSA.gs.AC?.[t]?.[0],dbxref:this.MSA.gs.DR?.[t]?.join("; ")}}getStructures(){let t=/PDB; +(\S+) +([^;\s]+); *(-?[0-9]+)-(-?[0-9]+)/,n=this.MSA.gs.DR??{},r={};for(let[s,o]of Object.entries(n))for(let a of o){let i=t.exec(a);i&&(r[s]??=[]).push({pdb:i[1].toLowerCase(),chain:i[2],startPos:+i[3],endPos:+i[4]})}return r}getTree(){let t=this.MSA.gf.NH?.[0];return t?M(S(t)):super.getTree()}get seqConsensus(){return this.MSA.gc.seq_cons}get secondaryStructureConsensus(){return this.MSA.gc.SS_cons}get tracks(){let{seq_cons:t,SS_cons:n,...r}=this.MSA.gc;return[{id:"seqConsensus",name:"Sequence consensus",data:t,customColorScheme:{}},{id:"secondaryStruct",name:"Secondary-structure",data:n,customColorScheme:v},...Object.entries(r).map(([s,o])=>({id:`gc-${s}`,name:s,data:o,customColorScheme:G(s)})),...Object.entries(this.MSA.gr).flatMap(([s,o])=>Object.entries(o).map(([a,i])=>({id:`gr-${a}-${s}`,name:`${a} ${s}`,data:i,customColorScheme:G(s),defaultOff:!0})))]}},v={"<":"lightblue","(":"lightblue","[":"lightblue","{":"lightblue",">":"pink",")":"pink","]":"pink","}":"pink"};function G(e){return e==="SS"||e.endsWith("_SS")?v:{}}function E(e){return/^(#.*\n)*>/.test(e)}function C(e){let t=[],n=new Set,r,s=()=>{r&&!n.has(r.id)&&(n.add(r.id),t.push({id:r.id,seq:r.lines.join("").replaceAll(/\s/g,"")}))},o=0;for(let a of e.split(`
3
+ `))a.startsWith(">")?(s(),o++,r={id:/^>\s*(\S*)/.exec(a)[1]||`unnamed_${o}`,lines:[]}):r?.lines.push(a);return s(),t}function A(e){return typeof e=="string"?C(e):e}var mt=65,dt=90,ht=97,gt=122,St=45;function H(e){return e>=ht&&e<=gt}function K(e){return e>=mt&&e<=dt||e===St}function bt(e){let t=[],n=[""];for(let r of e){let s=r.charCodeAt(0);K(s)?(t.push(r),n.push("")):H(s)&&(n[n.length-1]+=r.toUpperCase())}return{matches:t,inserts:n}}var l=class extends p{constructor(t){let n=A(t),r=xt(n.map(s=>s.seq));super(n.map((s,o)=>[s.id,r[o]]))}static sniff(t){if(typeof t=="string"&&!E(t))return!1;let n=A(t).map(o=>o.seq).filter(o=>!!o);if(n.length<2||new Set(n.map(o=>o.length)).size===1)return!1;let r=!1,s;for(let o of n){let a=0;for(let i=0;i<o.length;i++){let c=o.charCodeAt(i);K(c)?a++:H(c)&&(r=!0)}if(s!==void 0&&a!==s)return!1;s=a}return r}};function xt(e){let t=e.map(bt),n=0;for(let{matches:s}of t)n=Math.max(n,s.length);let r=new Array(n+1).fill(0);for(let{inserts:s}of t)for(let[o,a]of s.entries())r[o]=Math.max(r[o],a.length);return t.map(({matches:s,inserts:o})=>{let a=[];for(let i=0;i<=n;i++){let c=o[i]??"";a.push(c,".".repeat(r[i]-c.length)),i<n&&a.push(s[i]??"-")}return a.join("")})}var d=class extends p{MSA;constructor(t){let n=F(t);super(n.alns.map(r=>[r.id,r.seq])),this.MSA=n}getHeader(){return this.MSA.header}get seqConsensus(){return this.MSA.consensus}};var h=class extends p{constructor(t){super(O(t).map(n=>[n.protein,n.seq]))}};var g=class extends p{colonNormalized=new Map;constructor(t){let n=A(t);super(n.map(r=>[r.id,r.seq]));for(let{id:r}of n)r.includes(":")&&this.colonNormalized.set(r.replaceAll(":","_"),r)}getRow(t){return this.rows.get(t)??this.rows.get(this.colonNormalized.get(t)??"")??""}};var wt=/^<(!doctype\s+html|html[\s>])/i,Et=/^(CLUSTAL|MUSCLE|PROBCONS|MSAPROBS|Kalign)/i;function Rt(e,t=0,n){let r=e.replace(/^\uFEFF/,"").replaceAll(/\r\n?/g,`
4
+ `).trimStart();if(wt.test(r))throw new Error("Received an HTML page, not an alignment. Check that the URL points at the file itself");if(n==="stockholm")return new m(r,t);if(n==="a3m")return new l(r);if(n==="fasta")return new g(r);if(n==="emf")return new h(r);if(n==="clustal")return new d(r);if(x(r))return new m(r,t);if(E(r)){let o=C(r);return l.sniff(o)?new l(o):new g(o)}if(r.startsWith("SEQ"))return new h(r);if(Et.test(r))return new d(r);let s=r.split(`
5
+ `,1)[0].slice(0,80);throw new Error(s?`Unrecognized alignment format: expected FASTA, A3M, Stockholm, Clustal or EMF, but the first line reads "${s}"`:"The alignment file is empty")}function Tt(e){return e.replaceAll(/[.-]/g,"")}export{P as a,w as b,kt as c,N as d,k as e,Pt as f,V as g,q as h,Ut as i,vt as j,Ht as k,Kt as l,Wt as m,zt as n,Jt as o,Vt as p,Xt as q,Yt as r,Qt as s,m as t,Rt as u,Tt as v};
6
+ //# sourceMappingURL=chunk-PJZ3XTMG.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/useLocalStorage.ts", "../../src/utils/fetch.ts", "../../src/utils/poll.ts", "../../src/utils/ebiJobDispatcher.ts", "../../src/LaunchMsaView/components/BlastQuery/consts.ts", "../../node_modules/.pnpm/msa-parsers@8.3.0/node_modules/msa-parsers/src/util.ts", "../../node_modules/.pnpm/msa-parsers@8.3.0/node_modules/msa-parsers/src/msa/BaseMSA.ts", "../../node_modules/.pnpm/msa-parsers@8.3.0/node_modules/msa-parsers/src/msa/parseNewick.ts", "../../node_modules/.pnpm/msa-parsers@8.3.0/node_modules/msa-parsers/src/msa/stockholmParser.ts", "../../node_modules/.pnpm/msa-parsers@8.3.0/node_modules/msa-parsers/src/msa/StockholmMSA.ts", "../../node_modules/.pnpm/msa-parsers@8.3.0/node_modules/msa-parsers/src/msa/fastaRecords.ts", "../../node_modules/.pnpm/msa-parsers@8.3.0/node_modules/msa-parsers/src/msa/A3mMSA.ts", "../../node_modules/.pnpm/msa-parsers@8.3.0/node_modules/msa-parsers/src/msa/ClustalMSA.ts", "../../node_modules/.pnpm/msa-parsers@8.3.0/node_modules/msa-parsers/src/msa/EmfMSA.ts", "../../node_modules/.pnpm/msa-parsers@8.3.0/node_modules/msa-parsers/src/msa/FastaMSA.ts", "../../node_modules/.pnpm/msa-parsers@8.3.0/node_modules/msa-parsers/src/msa/index.ts"],
4
+ "sourcesContent": ["import { useState } from 'react'\n\n// Vendored rather than imported from `@jbrowse/core/util`: that barrel is\n// host-provided, and a barrel split dropped this export, making the BLAST panel\n// throw \"(0, PR.useLocalStorage) is not a function\" on hosts built during that\n// window. Same failure mode as `defaultCodonTable`; keeping our own copy takes\n// this plugin out of the whack-a-mole.\nexport function readLocalStorage<T>(key: string, initialValue: T): T {\n try {\n const item = globalThis.localStorage.getItem(key)\n return item === null ? initialValue : (JSON.parse(item) as T)\n } catch (error) {\n console.error(error)\n return initialValue\n }\n}\n\nexport function useLocalStorage<T>(key: string, initialValue: T) {\n const [storedValue, setStoredValue] = useState<T>(() =>\n readLocalStorage(key, initialValue),\n )\n const setValue = (value: T) => {\n setStoredValue(value)\n try {\n globalThis.localStorage.setItem(key, JSON.stringify(value))\n } catch (error) {\n console.error(error)\n }\n }\n return [storedValue, setValue] as const\n}\n", "function hostOf(url: string) {\n try {\n return new URL(url).host\n } catch {\n // a relative url, e.g. a same-origin proxy path\n return url\n }\n}\n\nexport async function handleFetch(url: string, args?: RequestInit) {\n let response: Response\n try {\n response = await fetch(url, args)\n } catch (e) {\n // fetch rejects with a bare \"TypeError: Failed to fetch\" for every network\n // level failure, including a cross-origin response the browser refused to\n // hand over. That is not a hypothetical: NCBI's Blast.cgi stopped sending\n // Access-Control-Allow-Origin to third-party origins, and the raw TypeError\n // told users nothing at all about why. See docs/blast.md.\n if (e instanceof TypeError) {\n throw new Error(\n `Could not reach ${hostOf(url)} \u2014 the request failed at the network level. This is usually a CORS restriction (the server refused to let this site read the response), an offline connection, or a blocked request.`,\n { cause: e },\n )\n }\n throw e\n }\n\n if (!response.ok) {\n throw new Error(\n `HTTP ${response.status} fetching ${url} ${await response.text()}`,\n )\n }\n\n return response\n}\n\nexport async function textfetch(url: string, args?: RequestInit) {\n const response = await handleFetch(url, args)\n return response.text()\n}\n\nexport async function jsonfetch<T>(url: string, args?: RequestInit) {\n const response = await handleFetch(url, args)\n return response.json() as Promise<T>\n}\n\nexport function timeout(time: number, signal?: AbortSignal) {\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(new DOMException('Aborted', 'AbortError'))\n return\n }\n // the listener is removed on the natural path too: one signal serves every\n // tick of a poll, and a 10-minute job is ~600 sleeps -- leaving each\n // listener behind accumulates them all on that one signal\n const onAbort = () => {\n clearTimeout(id)\n reject(new DOMException('Aborted', 'AbortError'))\n }\n const id = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort)\n resolve()\n }, time)\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n\n/**\n * A cancelled launch has to be told apart from a failed one: it must not render\n * the \"failed\" panel, and it must not touch a model that may already be gone.\n * `fetch` and the sleep above both reject with a DOMException named AbortError,\n * which subclasses Error.\n */\nexport function isAbortError(e: unknown) {\n return e instanceof Error && e.name === 'AbortError'\n}\n", "import { timeout } from './fetch'\n\n/**\n * Poll a remote job until it reports done. `check` returns true when finished,\n * false when still pending, and throws on failure. Between checks it counts down\n * `intervalSeconds`, calling `onCountdown` each second so the UI can show\n * progress.\n *\n * `signal` ends the loop with an AbortError: the countdown rejects on the tick\n * it is aborted rather than at the end of the interval, so a cancelled launch\n * stops within a second instead of running for the job's lifetime.\n */\nexport async function pollLoop({\n check,\n intervalSeconds,\n onCountdown,\n signal,\n}: {\n check: () => Promise<boolean>\n intervalSeconds: number\n onCountdown: (secondsRemaining: number) => void\n signal?: AbortSignal\n}) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n while (true) {\n signal?.throwIfAborted()\n if (await check()) {\n return\n }\n for (let i = intervalSeconds; i > 0; i--) {\n onCountdown(i)\n await timeout(1000, signal)\n }\n }\n}\n", "import { isAbortError, textfetch } from './fetch'\nimport { pollLoop } from './poll'\nimport { readLocalStorage } from './useLocalStorage'\n\n/**\n * EBI's Job Dispatcher REST services (clustalo, muscle, ncbiblast, ...) all\n * speak the same run/status/result protocol, so the transport lives here and\n * each tool only supplies its own parameters and result types.\n *\n * Unlike NCBI's Blast.cgi these endpoints send `Access-Control-Allow-Origin: *`,\n * which is why the BLAST backend moved here \u2014 see docs/blast.md.\n */\nexport const EBI_BASE = 'https://www.ebi.ac.uk/Tools/services/rest'\n\n/**\n * EBI asks for a contact address on every submission so they can reach whoever\n * is generating the load. A deployment that sends real volume should point this\n * at its own maintainer via the BLAST settings dialog \u2014 otherwise every\n * msaview job in the world is attributed to one person.\n */\nexport const EBI_EMAIL_STORAGE_KEY = 'msa-ebiContactEmail'\nexport const DEFAULT_EBI_EMAIL = 'colin.diesh@gmail.com'\n\nexport function getEbiEmail() {\n const configured = readLocalStorage(\n EBI_EMAIL_STORAGE_KEY,\n DEFAULT_EBI_EMAIL,\n ).trim()\n return configured || DEFAULT_EBI_EMAIL\n}\n\n/** Statuses that mean the job is over and produced no result. */\nconst FAILED_STATUSES = new Set(['ERROR', 'FAILURE', 'NOT_FOUND'])\n\nasync function submitEbiJob({\n tool,\n params,\n signal,\n}: {\n tool: string\n params: Record<string, string>\n signal?: AbortSignal\n}) {\n const jobId = await textfetch(`${EBI_BASE}/${tool}/run`, {\n method: 'POST',\n body: new URLSearchParams({ email: getEbiEmail(), ...params }),\n signal,\n })\n return jobId.trim()\n}\n\n/**\n * A status check that could not reach EBI at all says nothing about the job, so\n * it is not a reason to abandon one. A job the server has accepted keeps running\n * whatever happens to the poller's connection, and EBI's cluster has answered\n * status checks with a 502 for ten minutes on jobs that then finished (see\n * docs/blast.md).\n *\n * So the poll gives up only once checks have failed without a break for this\n * long: an endpoint that has genuinely gone away must not be polled forever.\n */\nexport const STATUS_FAILURE_BUDGET_MS = 15 * 60 * 1000\n\nexport async function waitForEbiJob({\n tool,\n jobId,\n intervalSeconds = 10,\n onCountdown,\n signal,\n}: {\n tool: string\n jobId: string\n intervalSeconds?: number\n onCountdown: (secondsRemaining: number) => void\n signal?: AbortSignal\n}) {\n let failingSince: number | undefined\n await pollLoop({\n intervalSeconds,\n onCountdown,\n signal,\n check: async () => {\n let status: string\n try {\n status = (\n await textfetch(`${EBI_BASE}/${tool}/status/${jobId}`, { signal })\n ).trim()\n } catch (e) {\n // a cancelled request is the caller giving up, not EBI being\n // unreachable, so it must end the poll rather than be retried\n if (isAbortError(e)) {\n throw e\n }\n failingSince ??= Date.now()\n if (Date.now() - failingSince >= STATUS_FAILURE_BUDGET_MS) {\n throw new Error(\n `Could not reach EBI to check ${tool} job ${jobId} for ${Math.round(STATUS_FAILURE_BUDGET_MS / 60_000)} minutes`,\n { cause: e },\n )\n }\n console.warn('[msaview] EBI status check failed, retrying:', e)\n return false\n }\n failingSince = undefined\n // exact match, not includes(): a job whose status is ERROR must not be\n // able to poll forever waiting for a FINISHED that will never arrive\n if (status === 'FINISHED') {\n return true\n }\n if (FAILED_STATUSES.has(status)) {\n throw new Error(`EBI ${tool} job ${jobId} returned status ${status}`)\n }\n // RUNNING, QUEUED, PENDING and anything else EBI adds later\n return false\n },\n })\n}\n\nexport async function runEbiJob({\n tool,\n label,\n params,\n onProgress,\n onRid,\n signal,\n}: {\n tool: string\n label: string\n params: Record<string, string>\n onProgress: (arg: string) => void\n onRid?: (jobId: string) => void\n signal?: AbortSignal\n}) {\n onProgress(`Submitting ${label} to EBI...`)\n const jobId = await submitEbiJob({ tool, params, signal })\n onRid?.(jobId)\n await waitForEbiJob({\n tool,\n jobId,\n signal,\n onCountdown: s => {\n onProgress(`Re-checking ${label} status in... ${s}`)\n },\n })\n return {\n jobId,\n result: (type: string) =>\n textfetch(`${EBI_BASE}/${tool}/result/${jobId}/${type}`, { signal }),\n }\n}\n", "/**\n * Only used to build the link-out on the manual panel, which sends the user to\n * NCBI's own site to run BLAST there. Nothing fetches this url: NCBI stopped\n * sending Access-Control-Allow-Origin to third-party origins, so a browser\n * cannot read a response from it at all. See docs/blast.md.\n */\nexport const BASE_BLAST_URL = 'https://blast.ncbi.nlm.nih.gov/Blast.cgi'\n\n/** The aligners EBI's Job Dispatcher runs, each a tool name at their REST api. */\nexport const ebiMsaAlgorithms = [\n 'clustalo',\n 'muscle',\n 'kalign',\n 'mafft',\n] as const\nexport type EbiMsaAlgorithm = (typeof ebiMsaAlgorithms)[number]\n\n/**\n * `browser` is no job at all: each sequence is aligned to the query in the\n * page and the rows merged on the query (utils/browserAlign.ts), with the tree\n * built by react-msaview's neighbour joining. It is the aligner for a launch\n * that must not depend on EBI, and for one that wants to be quick -- a\n * hundred 400-residue rows take under a second, and a hundred 1400-residue\n * rows eight, against a Job Dispatcher queue that has been measured at\n * anything from ten seconds to fifteen minutes. Past the per-sequence limit in\n * browserAlign.ts it refuses and names an EBI aligner instead.\n */\nexport const msaAlgorithms = [...ebiMsaAlgorithms, 'browser'] as const\nexport type MsaAlgorithm = (typeof msaAlgorithms)[number]\n\nexport const msaAlgorithmLabels: Record<MsaAlgorithm, string> = {\n clustalo: 'Clustal Omega (EBI)',\n muscle: 'MUSCLE (EBI)',\n kalign: 'Kalign (EBI)',\n mafft: 'MAFFT (EBI)',\n browser: 'in browser, query-anchored',\n}\n\n/**\n * EBI rejects a submission naming a database outside its own list with a 400,\n * so every value here has to appear in\n * https://www.ebi.ac.uk/Tools/services/rest/ncbiblast/parameterdetails/database\n * -- `uniprotkb_reference_proteomes` did not, and 3.0.0 shipped it as a dead\n * menu entry.\n */\nexport const blastDatabaseOptions = [\n 'uniprotkb_swissprot',\n 'uniprotkb',\n 'pan_proteomes',\n 'uniprotkb_trembl',\n] as const\nexport type BlastDatabase = (typeof blastDatabaseOptions)[number]\n\n// curated, so it returns roughly one good sequence per species rather than the\n// many near-identical TrEMBL entries an alignment reads poorly\nconst defaultBlastDatabase: BlastDatabase = 'uniprotkb_swissprot'\n\nexport const searchPrograms = ['blastp', 'phmmer'] as const\nexport type SearchProgram = (typeof searchPrograms)[number]\n\nexport const searchProgramLabels: Record<SearchProgram, string> = {\n blastp: 'blastp (sequence search)',\n phmmer: 'phmmer (profile HMM search)',\n}\n\n/**\n * phmmer offers PDB, AlphaFold, Ensembl Genomes, MEROPS and ChEMBL too, but\n * targets outside UniProt carry no species in their description, so those rows\n * would lose their species and common name. Only the databases that label their\n * hits are offered.\n *\n * rp15..rp75 are the Representative Proteomes: UniProt's reference proteomes\n * thinned so that no two are more than 15% (35%, 55%, 75%) similar, which is\n * the widest taxonomic spread per hit that any of these databases gives. rp15\n * is the one to reach for when the question is \"what is this like across all\n * of life\"; swissprot when it is \"what is this like in the curated set\".\n */\nexport const phmmerDatabaseOptions = [\n 'swissprot',\n 'uniprotkb',\n 'uniprotrefprot',\n 'rp75',\n 'rp55',\n 'rp35',\n 'rp15',\n] as const\nexport type PhmmerDatabase = (typeof phmmerDatabaseOptions)[number]\n\nconst defaultPhmmerDatabase: PhmmerDatabase = 'swissprot'\n\n/**\n * The hit counts EBI's ncbiblast accepts for `alignments` and `scores`. A value\n * off this list is a 400 at submit time, so a request is rounded up to the next\n * one on it.\n */\nconst blastHitCounts = [5, 10, 20, 50, 100, 150, 200, 250, 500, 750, 1000]\n\nexport const defaultMaxHits = 100\n\nexport function snapBlastHitCount(maxHits: number) {\n return blastHitCounts.find(n => n >= maxHits) ?? blastHitCounts.at(-1)!\n}\n\n/**\n * A program together with a database that program actually has.\n *\n * The pair travels as one value because neither service knows the other's\n * database names \u2014 `swissprot` is a phmmer database and `uniprotkb_swissprot` a\n * blastp one \u2014 so a program held apart from its database can drift into a\n * combination EBI answers with a 400, minutes after the user pressed Submit.\n */\nexport type SearchChoice =\n | { program: 'blastp'; database: BlastDatabase }\n | { program: 'phmmer'; database: PhmmerDatabase }\n\nexport function defaultSearchFor(program: SearchProgram): SearchChoice {\n return program === 'phmmer'\n ? { program, database: defaultPhmmerDatabase }\n : { program, database: defaultBlastDatabase }\n}\n\nexport function databaseOptionsFor(program: SearchProgram) {\n return program === 'phmmer' ? phmmerDatabaseOptions : blastDatabaseOptions\n}\n\n/**\n * What each database is called in the menu. The keys are what EBI is sent and\n * are not negotiable; `uniprotkb_swissprot` and `swissprot` are the same\n * collection under each service's own name, so they read the same here.\n */\nconst databaseLabels: Record<BlastDatabase | PhmmerDatabase, string> = {\n uniprotkb_swissprot: 'UniProtKB/Swiss-Prot (curated)',\n swissprot: 'UniProtKB/Swiss-Prot (curated)',\n uniprotkb: 'UniProtKB (all entries)',\n uniprotkb_trembl: 'UniProtKB/TrEMBL (unreviewed)',\n pan_proteomes: 'Pan-proteomes',\n uniprotrefprot: 'UniProt reference proteomes',\n rp75: 'Representative proteomes, 75%',\n rp55: 'Representative proteomes, 55%',\n rp35: 'Representative proteomes, 35%',\n rp15: 'Representative proteomes, 15% (widest spread)',\n}\n\nexport function databaseLabel(database: BlastDatabase | PhmmerDatabase) {\n return databaseLabels[database]\n}\n", "import type { Node, NodeWithIds } from './types.ts'\n\n/**\n * Copy a parsed tree, giving every node a stable id derived from its path, and\n * falling back to that id for an unnamed node.\n *\n * Iterative, like the traversals in @gmod/newick and the Newick emitter in the\n * viewer's neighbor joining: a phylogeny can be a caterpillar whose depth equals\n * its leaf count, and the recursive form of this overflowed the stack somewhere\n * past 5000 deep -- which every tree reaches, since this is the last step of\n * parsing one.\n */\nexport function generateNodeIds(\n tree: Node,\n parent = 'node',\n depth = 0,\n): NodeWithIds {\n const withId = (node: Node, id: string): NodeWithIds => ({\n ...node,\n id,\n name: node.name || id,\n children: [],\n })\n\n const root = withId(tree, `${parent}-${depth}`)\n const stack = [{ src: tree, out: root, depth }]\n while (stack.length > 0) {\n const { src, out, depth: d } = stack.pop()!\n if (src.children) {\n out.children = src.children.map((child, i) =>\n withId(child, `${out.id}-${i}-${d + 1}`),\n )\n for (const [i, child] of src.children.entries()) {\n stack.push({ src: child, out: out.children[i]!, depth: d + 1 })\n }\n }\n }\n return root\n}\n", "import type { MSATrack, NodeWithIds } from '../types.ts'\n\nexport default abstract class BaseMSA {\n protected rows = new Map<string, string>()\n private names: string[]\n private width?: number\n\n /**\n * A repeated name keeps its first row: names index the rows, so listing the\n * name twice would render a phantom duplicate row.\n */\n constructor(rows: Iterable<readonly [string, string]>) {\n for (const [name, seq] of rows) {\n if (!this.rows.has(name)) {\n this.rows.set(name, seq)\n }\n }\n this.names = [...this.rows.keys()]\n }\n\n getRow(name: string): string {\n return this.rows.get(name) ?? ''\n }\n\n getNames(): string[] {\n return this.names\n }\n\n /**\n * Column count of the alignment: the length of its widest row, since a\n * hand-edited fasta or some aligners leave rows short. The viewer's gap\n * analysis counts columns the same way.\n */\n getWidth(): number {\n this.width ??= this.getNames().reduce(\n (max, name) => Math.max(max, this.getRow(name).length),\n 0,\n )\n return this.width\n }\n\n getTree(): NodeWithIds {\n return {\n id: 'root',\n name: 'root',\n noTree: true,\n children: this.getNames().map(name => ({\n id: name,\n children: [],\n name,\n })),\n }\n }\n\n getRowData(\n _name: string,\n ): { name?: string; accession?: string; dbxref?: string } | undefined {\n return undefined\n }\n\n getStructures(): Record<string, unknown> {\n return {}\n }\n\n getHeader(): Record<string, unknown> {\n return {}\n }\n\n get alignmentNames(): string[] {\n return []\n }\n\n get seqConsensus(): string | undefined {\n return undefined\n }\n\n get secondaryStructureConsensus(): string | undefined {\n return undefined\n }\n\n get tracks(): MSATrack[] {\n return []\n }\n}\n", "import { parseNewick } from '@gmod/newick'\n\nimport type { NewickNode } from '@gmod/newick'\n\n/**\n * Newick parsing for phylogenetic trees.\n *\n * `postParenNumeric: 'name'` is the whole reason this wrapper exists. A bare\n * number after a `)` is an internal node label in standard Newick -- typically a\n * bootstrap support value -- but `@gmod/hclust` reuses that slot for a cluster's\n * merge height, and the parser's default guesses between the two by whether the\n * tree carries any `:` branch length. Everything reaching this package is a\n * phylogeny (newick, the tree embedded in Stockholm `#=GF NH`, EMF, ASN.1), so\n * the guess is settled here instead: reading `((A,B)95,(C,D)80);` as lengths\n * would draw a phylogram scaled by support values.\n */\nexport default function parse(s: string): NewickNode {\n return parseNewick(s, { postParenNumeric: 'name' })\n}\n", "/**\n * Stockholm format parser\n * Based on stockholm-js by Ian Holmes\n * Rewritten in TypeScript\n */\n\nexport interface StockholmData {\n gf: Record<string, string[]>\n gc: Record<string, string>\n gs: Record<string, Record<string, string[]>>\n gr: Record<string, Record<string, string>>\n seqname: string[]\n seqdata: Record<string, string>\n}\n\nfunction createStockholm(): StockholmData {\n return {\n gf: Object.create(null),\n gc: Object.create(null),\n gs: Object.create(null),\n gr: Object.create(null),\n seqname: [],\n seqdata: Object.create(null),\n }\n}\n\n// records before a \"# STOCKHOLM 1.0\" header are an error in strict mode,\n// otherwise they implicitly start a new alignment\nfunction ensureStock(\n stock: StockholmData | null,\n strict: boolean,\n): StockholmData {\n if (stock) {\n return stock\n }\n if (strict) {\n throw new Error('No format header: # STOCKHOLM 1.0')\n }\n return createStockholm()\n}\n\nconst formatStartRegex = /^# STOCKHOLM 1.0/\nconst formatEndRegex = /^\\/\\/\\s*$/\nconst gfRegex = /^#=GF\\s+(\\S+)\\s+(.*?)\\s*$/\nconst gcRegex = /^#=GC\\s+(\\S+)\\s+(.*?)\\s*$/\nconst gsRegex = /^#=GS\\s+(\\S+)\\s+(\\S+)\\s+(.*?)\\s*$/\nconst grRegex = /^#=GR\\s+(\\S+)\\s+(\\S+)\\s+(.*?)\\s*$/\n// a sequence line is \"<name> <residues>\". The leading [^#\\s] guards against a\n// two-word comment (\"# note\") being taken as a sequence named '#'; every\n// meaningful '#' line is a #=GF/#=GC/#=GS/#=GR record already matched above.\nconst lineRegex = /^\\s*([^#\\s]\\S*)\\s+(\\S+)\\s*$/\nconst nonwhiteRegex = /\\S/\n\nexport function sniff(text: string): boolean {\n return formatStartRegex.test(text)\n}\n\nexport function parseAll(\n text: string,\n opts?: { strict?: boolean },\n): StockholmData[] {\n const options = opts ?? {}\n const db: StockholmData[] = []\n let stock: StockholmData | null = null\n // one tree spans consecutive `#=GF NH` lines, so a `#=GF TN` naming the\n // next tree, or any other #=GF line, ends it\n let treeContinues = false\n\n const lines = text.split('\\n')\n for (const line of lines) {\n let match: RegExpExecArray | null\n\n if (formatStartRegex.test(line)) {\n if (stock) {\n db.push(stock)\n }\n stock = createStockholm()\n treeContinues = false\n } else if (formatEndRegex.test(line)) {\n if (stock) {\n db.push(stock)\n }\n stock = null\n } else if ((match = gfRegex.exec(line))) {\n stock = ensureStock(stock, !!options.strict)\n const tag = match[1]!\n const values = (stock.gf[tag] ??= [])\n if (tag === 'NH' && treeContinues && values.length > 0) {\n values[values.length - 1] += match[2]!\n } else {\n values.push(match[2]!)\n }\n treeContinues = tag === 'NH'\n } else if ((match = gcRegex.exec(line))) {\n stock = ensureStock(stock, !!options.strict)\n stock.gc[match[1]!] = (stock.gc[match[1]!] ?? '') + match[2]!\n } else if ((match = gsRegex.exec(line))) {\n stock = ensureStock(stock, !!options.strict)\n const byName = (stock.gs[match[2]!] ??= Object.create(null))\n ;(byName[match[1]!] ??= []).push(match[3]!)\n } else if ((match = grRegex.exec(line))) {\n stock = ensureStock(stock, !!options.strict)\n const byName = (stock.gr[match[2]!] ??= Object.create(null))\n byName[match[1]!] = (byName[match[1]!] ?? '') + match[3]!\n } else if ((match = lineRegex.exec(line))) {\n stock = ensureStock(stock, !!options.strict)\n const seqname = match[1]!\n const seqdata = match[2]!\n if (stock.seqdata[seqname] === undefined) {\n stock.seqdata[seqname] = ''\n stock.seqname.push(seqname)\n }\n stock.seqdata[seqname] += seqdata\n } else if (nonwhiteRegex.test(line)) {\n if (options.strict) {\n throw new Error('Malformed line')\n }\n }\n }\n\n if (stock) {\n db.push(stock)\n }\n\n return db\n}\n\nexport function parse(\n text: string,\n opts?: { strict?: boolean },\n): StockholmData {\n const db = parseAll(text, opts)\n if (db.length === 0) {\n throw new Error('No alignments found')\n }\n if (db.length > 1) {\n throw new Error('More than one alignment found')\n }\n return db[0]!\n}\n", "import { generateNodeIds } from '../util.ts'\nimport BaseMSA from './BaseMSA.ts'\nimport parseNewick from './parseNewick.ts'\nimport { parseAll } from './stockholmParser.ts'\n\nimport type { MSATrack, NodeWithIds } from '../types.ts'\nimport type { StockholmData } from './stockholmParser.ts'\n\nexport default class StockholmMSA extends BaseMSA {\n private data: StockholmData[]\n private MSA: StockholmData\n\n constructor(text: string, currentAlignment: number) {\n const res = parseAll(text)\n const aln = res[currentAlignment] ?? res[0]\n if (!aln) {\n throw new Error('No alignments found in Stockholm file')\n }\n super(aln.seqname.map(name => [name, aln.seqdata[name] ?? '']))\n this.data = res\n this.MSA = aln\n }\n\n getMSA() {\n return this.MSA\n }\n\n get alignmentNames() {\n return this.data.map((aln, idx) => aln.gf.DE?.[0] ?? `Alignment ${idx + 1}`)\n }\n\n getHeader() {\n const acEntries = this.MSA.gs.AC ?? {}\n const drEntries = this.MSA.gs.DR ?? {}\n return {\n General: this.MSA.gf,\n Accessions: Object.fromEntries(\n Object.entries(acEntries).map(([k, v]) => [k, v[0]]),\n ),\n Dbxref: Object.fromEntries(\n Object.entries(drEntries).map(([k, v]) => [k, v.join('; ')]),\n ),\n }\n }\n\n getRowData(rowName: string) {\n return {\n name: rowName,\n accession: this.MSA.gs.AC?.[rowName]?.[0],\n dbxref: this.MSA.gs.DR?.[rowName]?.join('; '),\n }\n }\n\n getStructures() {\n const pdbRegex = /PDB; +(\\S+) +([^;\\s]+); *(-?[0-9]+)-(-?[0-9]+)/\n const drEntries = this.MSA.gs.DR ?? {}\n const result: Record<\n string,\n { pdb: string; chain: string; startPos: number; endPos: number }[]\n > = {}\n for (const [id, drList] of Object.entries(drEntries)) {\n for (const dr of drList) {\n const match = pdbRegex.exec(dr)\n if (match) {\n ;(result[id] ??= []).push({\n pdb: match[1]!.toLowerCase(),\n chain: match[2]!,\n startPos: +match[3]!,\n endPos: +match[4]!,\n })\n }\n }\n }\n return result\n }\n\n getTree(): NodeWithIds {\n const tree = this.MSA.gf.NH?.[0]\n return tree ? generateNodeIds(parseNewick(tree)) : super.getTree()\n }\n\n get seqConsensus() {\n return this.MSA.gc.seq_cons\n }\n\n get secondaryStructureConsensus() {\n return this.MSA.gc.SS_cons\n }\n\n /**\n * One text track per `#=GC` line, and one per `#=GR` line that stays hidden\n * until asked for: a Pfam seed carries a few per-row active-site lines, but\n * an Rfam family carries a structure line for every row with a PDB entry.\n */\n get tracks(): MSATrack[] {\n const { seq_cons, SS_cons, ...otherGc } = this.MSA.gc\n return [\n {\n id: 'seqConsensus',\n name: 'Sequence consensus',\n data: seq_cons,\n customColorScheme: {},\n },\n {\n id: 'secondaryStruct',\n name: 'Secondary-structure',\n data: SS_cons,\n customColorScheme: wussColors,\n },\n ...Object.entries(otherGc).map(([tag, data]) => ({\n id: `gc-${tag}`,\n name: tag,\n data,\n customColorScheme: structureColors(tag),\n })),\n ...Object.entries(this.MSA.gr).flatMap(([tag, byRow]) =>\n Object.entries(byRow).map(([row, data]) => ({\n id: `gr-${row}-${tag}`,\n name: `${row} ${tag}`,\n data,\n customColorScheme: structureColors(tag),\n defaultOff: true,\n })),\n ),\n ]\n }\n}\n\n// WUSS writes base-paired columns as open/close brackets of several nesting\n// types (<>, (), [], {}); coloring every open one way and every close the other\n// highlights each helix, including the tRNA acceptor stem written with parens\nconst wussColors: Record<string, string> = {\n '<': 'lightblue',\n '(': 'lightblue',\n '[': 'lightblue',\n '{': 'lightblue',\n '>': 'pink',\n ')': 'pink',\n ']': 'pink',\n '}': 'pink',\n}\n\n// `SS`, and Rfam's per-structure `2GIS_A_SS`\nfunction structureColors(tag: string) {\n return tag === 'SS' || tag.endsWith('_SS') ? wussColors : {}\n}\n\nexport { sniff as stockholmSniff } from './stockholmParser.ts'\n", "export interface FastaRecord {\n id: string\n seq: string\n}\n\n// a `>` record, optionally after `#` header lines such as ColabFold's `#8\\t1`\nexport function fastaSniff(text: string) {\n return /^(#.*\\n)*>/.test(text)\n}\n\n/**\n * Split FASTA-style text (shared by the FASTA and A3M parsers) into records.\n *\n * A record starts at a `>` that begins a line, so a `>` inside a defline\n * (`x->y`, HGVS `c.1799T>A`) stays part of it, and anything before the first\n * record (ColabFold's `#8\\t1` header) is skipped. The id is the first word of\n * the defline, so `> name` reads as `name`, and a defline with no word names\n * its record `unnamed_N` after its position in the file. A repeated id keeps\n * the first record: ids index the row data, so emitting the id twice would\n * render a phantom duplicate row showing the last record's residues.\n */\nexport function splitFastaRecords(text: string): FastaRecord[] {\n const records: FastaRecord[] = []\n const seen = new Set<string>()\n let current: { id: string; lines: string[] } | undefined\n\n const flush = () => {\n if (current && !seen.has(current.id)) {\n seen.add(current.id)\n records.push({\n id: current.id,\n seq: current.lines.join('').replaceAll(/\\s/g, ''),\n })\n }\n }\n\n let count = 0\n for (const line of text.split('\\n')) {\n if (line.startsWith('>')) {\n flush()\n count++\n const id = /^>\\s*(\\S*)/.exec(line)![1] || `unnamed_${count}`\n current = { id, lines: [] }\n } else {\n current?.lines.push(line)\n }\n }\n flush()\n\n return records\n}\n\nexport function toRecords(input: string | FastaRecord[]) {\n return typeof input === 'string' ? splitFastaRecords(input) : input\n}\n", "import BaseMSA from './BaseMSA.ts'\nimport { fastaSniff, toRecords } from './fastaRecords.ts'\n\nimport type { FastaRecord } from './fastaRecords.ts'\n\n/**\n * A3M Format Parser\n *\n * The A3M format consists of aligned FASTA, in which alignments are shown with:\n * - Inserts as lowercase characters\n * - Matches as uppercase characters\n * - Deletions as '-'\n * - Gaps aligned to inserts as '.'\n *\n * Note that gaps aligned to inserts can be omitted in the A3M format.\n *\n * Example:\n * >query\n * ETESMKTVRIREKIKKFLGDRPRNTAEILEHINSTMRHGTTSQQLGNVLSKDKDIVKVGYIKRSGILSGGYDICEWATRNWVAEHCPEWTE\n * >seq1\n * ----MRTTRLRQKIKKFLNERGeANTTEILEHVNSTMRHGTTPQQLGNVLSKDKDILKVATTKRGGALSGRYEICVWTLRP-----------\n *\n * In the above, 'e' after 'G' in seq1 is a lowercase insert.\n *\n * @see https://yanglab.qd.sdu.edu.cn/trRosetta/msa_format.html\n */\n\nconst CODE_A = 65 // 'A'\nconst CODE_Z = 90 // 'Z'\nconst CODE_a = 97 // 'a'\nconst CODE_z = 122 // 'z'\nconst CODE_DASH = 45 // '-'\n\nfunction isLower(code: number): boolean {\n return code >= CODE_a && code <= CODE_z\n}\n\nfunction isMatch(code: number): boolean {\n return (code >= CODE_A && code <= CODE_Z) || code === CODE_DASH\n}\n\ninterface ParsedRow {\n // one entry per match column, in order\n matches: string[]\n // inserts[k] is the run of insert residues sitting immediately BEFORE match\n // column k, already uppercased; inserts[matches.length] holds the trailing\n // run. An insert must never consume a match column, or a row that opens with\n // an insert falls a column out of register with every other row.\n inserts: string[]\n}\n\nfunction parseRow(seq: string): ParsedRow {\n const matches: string[] = []\n const inserts = ['']\n\n for (const ch of seq) {\n const code = ch.charCodeAt(0)\n if (isMatch(code)) {\n matches.push(ch)\n inserts.push('')\n } else if (isLower(code)) {\n inserts[inserts.length - 1] += ch.toUpperCase()\n }\n // '.' is padding aligned to some other row's insert, and anything else is\n // unrecognized -- neither carries alignment information\n }\n\n return { matches, inserts }\n}\n\nexport default class A3mMSA extends BaseMSA {\n constructor(input: string | FastaRecord[]) {\n const records = toRecords(input)\n const expanded = expandA3M(records.map(r => r.seq))\n super(records.map((r, i) => [r.id, expanded[i]!]))\n }\n\n static sniff(input: string | FastaRecord[]): boolean {\n if (typeof input === 'string' && !fastaSniff(input)) {\n return false\n }\n\n const seqs = toRecords(input)\n .map(r => r.seq)\n .filter(s => !!s)\n\n if (seqs.length < 2) {\n return false\n }\n\n // A true aligned FASTA (e.g. impg's fasta-aln output from soft-masked\n // references) has all rows the same total length. A3M's defining feature is\n // variable-length rows, since lowercase inserts live outside the fixed match\n // grid. So equal-length rows are an alignment, not A3M, even when lowercase\n // soft-masking is present.\n if (new Set(seqs.map(s => s.length)).size === 1) {\n return false\n }\n\n let hasLowercase = false\n let matchCount: number | undefined\n for (const seq of seqs) {\n let matches = 0\n for (let i = 0; i < seq.length; i++) {\n const code = seq.charCodeAt(i)\n if (isMatch(code)) {\n matches++\n } else if (isLower(code)) {\n hasLowercase = true\n }\n }\n if (matchCount !== undefined && matches !== matchCount) {\n return false\n }\n matchCount = matches\n }\n return hasLowercase\n }\n}\n\n/**\n * Expand A3M rows into a rectangular alignment: every insert slot is widened to\n * the longest insert any row places there, and rows without one are padded with\n * '.' so all rows stay in register.\n */\nfunction expandA3M(rawSeqs: string[]): string[] {\n const rows = rawSeqs.map(parseRow)\n // a loop, not Math.max(...): an a3m from an hhblits search routinely carries\n // six figures of hits, and spreading one argument per row throws\n // \"Maximum call stack size exceeded\" somewhere past ~125k of them\n let numPositions = 0\n for (const { matches } of rows) {\n numPositions = Math.max(numPositions, matches.length)\n }\n\n // one insert slot before each match column, plus one trailing slot\n const maxInserts = new Array<number>(numPositions + 1).fill(0)\n for (const { inserts } of rows) {\n for (const [pos, ins] of inserts.entries()) {\n maxInserts[pos] = Math.max(maxInserts[pos]!, ins.length)\n }\n }\n\n return rows.map(({ matches, inserts }) => {\n const result: string[] = []\n for (let pos = 0; pos <= numPositions; pos++) {\n const ins = inserts[pos] ?? ''\n result.push(ins, '.'.repeat(maxInserts[pos]! - ins.length))\n if (pos < numPositions) {\n // a row with fewer match columns than the widest is padded out\n result.push(matches[pos] ?? '-')\n }\n }\n return result.join('')\n })\n}\n", "import { parse } from 'clustal-js'\n\nimport BaseMSA from './BaseMSA.ts'\n\nexport default class ClustalMSA extends BaseMSA {\n private MSA: ReturnType<typeof parse>\n\n constructor(text: string) {\n const msa = parse(text)\n super(msa.alns.map(aln => [aln.id, aln.seq]))\n this.MSA = msa\n }\n\n getHeader() {\n return this.MSA.header\n }\n\n get seqConsensus() {\n return this.MSA.consensus\n }\n}\n", "import { parseEmfAln } from 'emf-js'\n\nimport BaseMSA from './BaseMSA.ts'\n\nexport default class EmfMSA extends BaseMSA {\n constructor(text: string) {\n super(parseEmfAln(text).map(aln => [aln.protein, aln.seq]))\n }\n}\n", "import BaseMSA from './BaseMSA.ts'\nimport { toRecords } from './fastaRecords.ts'\n\nimport type { FastaRecord } from './fastaRecords.ts'\n\nexport default class FastaMSA extends BaseMSA {\n // Newick tools write a colon in a name as an underscore\n private colonNormalized = new Map<string, string>()\n\n constructor(input: string | FastaRecord[]) {\n const records = toRecords(input)\n super(records.map(r => [r.id, r.seq]))\n for (const { id } of records) {\n if (id.includes(':')) {\n this.colonNormalized.set(id.replaceAll(':', '_'), id)\n }\n }\n }\n\n getRow(name: string) {\n return (\n this.rows.get(name) ??\n this.rows.get(this.colonNormalized.get(name) ?? '') ??\n ''\n )\n }\n}\n", "import A3mMSA from './A3mMSA.ts'\nimport ClustalMSA from './ClustalMSA.ts'\nimport EmfMSA from './EmfMSA.ts'\nimport FastaMSA from './FastaMSA.ts'\nimport StockholmMSA, { stockholmSniff } from './StockholmMSA.ts'\nimport { fastaSniff, splitFastaRecords } from './fastaRecords.ts'\n\nexport { parseEmfTree } from 'emf-js'\nexport { default as parseNewick } from './parseNewick.ts'\n\nexport type MSAParserType =\n | StockholmMSA\n | A3mMSA\n | FastaMSA\n | EmfMSA\n | ClustalMSA\n\nexport type MSAFormat = 'stockholm' | 'a3m' | 'fasta' | 'emf' | 'clustal'\n\nconst htmlPage = /^<(!doctype\\s+html|html[\\s>])/i\nconst clustalHeader = /^(CLUSTAL|MUSCLE|PROBCONS|MSAPROBS|Kalign)/i\n\n// A caller that knows the format, such as impg emitting fasta-aln, passes\n// `format` to skip sniffing, which cannot always tell FASTA from A3M\nexport function parseMSA(\n input: string,\n currentAlignment = 0,\n format?: MSAFormat,\n): MSAParserType {\n const text = input\n .replace(/^\\uFEFF/, '')\n .replaceAll(/\\r\\n?/g, '\\n')\n .trimStart()\n if (htmlPage.test(text)) {\n throw new Error(\n 'Received an HTML page, not an alignment. Check that the URL points at the file itself',\n )\n }\n if (format === 'stockholm') {\n return new StockholmMSA(text, currentAlignment)\n }\n if (format === 'a3m') {\n return new A3mMSA(text)\n }\n if (format === 'fasta') {\n return new FastaMSA(text)\n }\n if (format === 'emf') {\n return new EmfMSA(text)\n }\n if (format === 'clustal') {\n return new ClustalMSA(text)\n }\n if (stockholmSniff(text)) {\n return new StockholmMSA(text, currentAlignment)\n }\n if (fastaSniff(text)) {\n const records = splitFastaRecords(text)\n return A3mMSA.sniff(records) ? new A3mMSA(records) : new FastaMSA(records)\n }\n if (text.startsWith('SEQ')) {\n return new EmfMSA(text)\n }\n if (clustalHeader.test(text)) {\n return new ClustalMSA(text)\n }\n const firstLine = text.split('\\n', 1)[0]!.slice(0, 80)\n throw new Error(\n firstLine\n ? `Unrecognized alignment format: expected FASTA, A3M, Stockholm, Clustal or EMF, but the first line reads \"${firstLine}\"`\n : 'The alignment file is empty',\n )\n}\n\n/**\n * Get ungapped sequence from an aligned row\n */\nexport function getUngappedSequence(alignedSeq: string): string {\n return alignedSeq.replaceAll(/[.-]/g, '')\n}\n\nexport { default as A3mMSA } from './A3mMSA.ts'\nexport { default as ClustalMSA } from './ClustalMSA.ts'\nexport { default as EmfMSA } from './EmfMSA.ts'\nexport { default as FastaMSA } from './FastaMSA.ts'\nexport { default as StockholmMSA, stockholmSniff } from './StockholmMSA.ts'\n"],
5
+ "mappings": "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",
6
+ "names": ["import_react", "readLocalStorage", "key", "initialValue", "item", "error", "useLocalStorage", "storedValue", "setStoredValue", "value", "hostOf", "url", "handleFetch", "args", "response", "e", "textfetch", "jsonfetch", "timeout", "time", "signal", "resolve", "reject", "onAbort", "id", "isAbortError", "pollLoop", "check", "intervalSeconds", "onCountdown", "signal", "i", "timeout", "EBI_BASE", "EBI_EMAIL_STORAGE_KEY", "DEFAULT_EBI_EMAIL", "getEbiEmail", "readLocalStorage", "FAILED_STATUSES", "submitEbiJob", "tool", "params", "signal", "textfetch", "STATUS_FAILURE_BUDGET_MS", "waitForEbiJob", "jobId", "intervalSeconds", "onCountdown", "failingSince", "pollLoop", "status", "e", "isAbortError", "runEbiJob", "label", "onProgress", "onRid", "s", "type", "BASE_BLAST_URL", "ebiMsaAlgorithms", "msaAlgorithms", "msaAlgorithmLabels", "blastDatabaseOptions", "defaultBlastDatabase", "searchPrograms", "searchProgramLabels", "phmmerDatabaseOptions", "defaultPhmmerDatabase", "blastHitCounts", "defaultMaxHits", "snapBlastHitCount", "maxHits", "n", "defaultSearchFor", "program", "databaseOptionsFor", "databaseLabels", "databaseLabel", "database", "generateNodeIds", "tree", "parent", "depth", "withId", "node", "id", "root", "stack", "src", "out", "d", "child", "i", "BaseMSA", "rows", "name", "seq", "max", "_name", "parse", "s", "parseNewick", "createStockholm", "ensureStock", "stock", "strict", "formatStartRegex", "formatEndRegex", "gfRegex", "gcRegex", "gsRegex", "grRegex", "lineRegex", "nonwhiteRegex", "sniff", "text", "parseAll", "opts", "options", "db", "treeContinues", "lines", "line", "match", "tag", "values", "byName", "seqname", "seqdata", "StockholmMSA", "BaseMSA", "text", "currentAlignment", "res", "parseAll", "aln", "name", "idx", "acEntries", "drEntries", "k", "v", "rowName", "pdbRegex", "result", "id", "drList", "dr", "match", "tree", "generateNodeIds", "parse", "seq_cons", "SS_cons", "otherGc", "wussColors", "tag", "data", "structureColors", "byRow", "row", "fastaSniff", "text", "splitFastaRecords", "records", "seen", "current", "flush", "count", "line", "toRecords", "input", "CODE_A", "CODE_Z", "CODE_a", "CODE_z", "CODE_DASH", "isLower", "code", "isMatch", "parseRow", "seq", "matches", "inserts", "ch", "A3mMSA", "BaseMSA", "input", "records", "toRecords", "expanded", "expandA3M", "r", "i", "fastaSniff", "seqs", "s", "hasLowercase", "matchCount", "rawSeqs", "rows", "numPositions", "maxInserts", "pos", "ins", "result", "ClustalMSA", "BaseMSA", "text", "msa", "parse", "aln", "EmfMSA", "BaseMSA", "text", "parseEmfAln", "aln", "FastaMSA", "BaseMSA", "input", "records", "toRecords", "id", "name", "htmlPage", "clustalHeader", "parseMSA", "input", "currentAlignment", "format", "text", "StockholmMSA", "A3mMSA", "FastaMSA", "EmfMSA", "ClustalMSA", "sniff", "fastaSniff", "records", "splitFastaRecords", "firstLine", "getUngappedSequence", "alignedSeq"]
7
+ }
@@ -0,0 +1,5 @@
1
+ function k(t){let e=t.split(`
2
+ `).map(n=>n.trim()).filter(n=>!!n),r=[],s=[];for(let n of e)if(n.startsWith("SEQ"))r.push(n);else{if(n.startsWith("DATA"))continue;if(n.startsWith("//"))break;s.push(n)}let o=r.map(n=>{let[,i,c,p,a,u,l,f,h]=n.split(" ");return{species:i,protein:c,chr:p,start:+a,end:+u,strand:+l,geneId:f,unknown:h,seq:""}});for(let n of s)for(let i=0;i<r.length;i++)o[i].seq+=n[i];return o}function S(t){let e=t.split(`
3
+ `).map(n=>n.trim()).filter(n=>!!n),r=[],s="";for(let n of e)if(n.startsWith("SEQ"))r.push(n);else{if(n.startsWith("DATA"))continue;if(n.startsWith("//"))break;s=n}let o=r.map(n=>{let[,i,c,p,a,u,l,f,h]=n.split(" ");return{species:i,protein:c,chr:p,start:+a,end:+u,strand:+l,geneId:f,unknown:h}});return{tree:s,seqs:o}}function q(t){let e=/\(?(\d+(\.\d+)+)\)?/.exec(t);return e&&e.length>1?e[1]:""}function m(t){let e=["CLUSTAL","PROBCONS","MUSCLE","MSAPROBS","Kalign"];e.some(s=>t.startsWith(s))||console.warn(`${t} is not a known CLUSTAL header: ${e.join(",")}, proceeding but could indicate an issue`);let r=q(t);return{info:t,version:r}}function d(t){let e=t.next();for(;!e.done&&e.value.trim()==="";)e=t.next();return e.value}function x(t){let e=t.split(/\s+/),r=t.slice(e[0].length),s=e[0].length+r.indexOf(e[1]),o=s+e[1].length;return[s,o]}function g(t){let e=d(t),r=[],s="";if(!e)return;for(;e;)e.startsWith(" ")?s=e:r.push(e),e=t.next().value;let[o,n]=x(r[0]),c=r.map(l=>l.split(/\s+/)).map(l=>l[0]),p=r.map(l=>l.slice(o,n)),a=s.slice(o,n),u=p[0].length-a.length;return u&&(a+=" ".repeat(u)),{ids:c,seqs:p,consensus:a}}function w(t){let e,r=g(t);if(r!==void 0)for(;e=g(t);){for(let s=0;s<e.seqs.length;s++)r.seqs[s]+=e.seqs[s];r.consensus+=e.consensus}return r}function E(t){let e=d(t);if(!e)throw new Error("Empty file received");let r=m(e),s=w(t);if(s===void 0)throw new Error("No blocks parsed");let o=s.seqs.map((i,c)=>({id:s.ids[c],seq:i})),{consensus:n}=s;if(n.length!==o[0].seq.length)throw new Error(`Consensus length != sequence length. Con ${n.length} seq ${o[0].seq.length}`);return{consensus:n,alns:o,header:r}}function W(t){let e=t.split(`
4
+ `)[Symbol.iterator]();return E(e)}export{W as a,k as b,S as c};
5
+ //# sourceMappingURL=chunk-QAC6JSDZ.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../node_modules/.pnpm/emf-js@2.0.0/node_modules/emf-js/src/index.ts", "../../node_modules/.pnpm/clustal-js@2.0.18/node_modules/clustal-js/src/util.ts", "../../node_modules/.pnpm/clustal-js@2.0.18/node_modules/clustal-js/src/index.ts"],
4
+ "sourcesContent": ["export interface Row {\n id: string\n seq: string\n}\n\nexport function parseEmfAln(contents: string) {\n const lines = contents\n .split('\\n')\n .map(f => f.trim())\n .filter(f => !!f)\n const seqs = []\n const alns = []\n for (const line of lines) {\n if (line.startsWith('SEQ')) {\n seqs.push(line)\n } else if (line.startsWith('DATA')) {\n continue\n } else if (line.startsWith('//')) {\n break\n } else {\n alns.push(line)\n }\n }\n const arr = seqs.map(s => {\n const [, species, protein, chr, start, end, strand, geneId, unknown] =\n s.split(' ')\n return {\n species,\n protein,\n chr,\n start: +start,\n end: +end,\n strand: +strand,\n geneId,\n unknown,\n seq: '',\n }\n })\n for (const aln of alns) {\n for (let j = 0; j < seqs.length; j++) {\n arr[j].seq += aln[j]\n }\n }\n return arr\n}\n\nexport function parseEmfTree(contents: string) {\n const lines = contents\n .split('\\n')\n .map(f => f.trim())\n .filter(f => !!f)\n const seqs = []\n let tree = ''\n for (const line of lines) {\n if (line.startsWith('SEQ')) {\n seqs.push(line)\n } else if (line.startsWith('DATA')) {\n continue\n } else if (line.startsWith('//')) {\n break\n } else {\n tree = line\n }\n }\n const arr = seqs.map(s => {\n const [, species, protein, chr, start, end, strand, geneId, unknown] =\n s.split(' ')\n return {\n species,\n protein,\n chr,\n start: +start,\n end: +end,\n strand: +strand,\n geneId,\n unknown,\n }\n })\n\n return { tree, seqs: arr }\n}\n", "export function parseVersion(line: string) {\n const res = /\\(?(\\d+(\\.\\d+)+)\\)?/.exec(line)\n return res && res.length > 1 ? res[1] : ''\n}\nexport function parseHeader(info: string) {\n const knownHeaders = ['CLUSTAL', 'PROBCONS', 'MUSCLE', 'MSAPROBS', 'Kalign']\n\n if (!knownHeaders.some(l => info.startsWith(l))) {\n console.warn(\n `${info} is not a known CLUSTAL header: ${knownHeaders.join(\n ',',\n )}, proceeding but could indicate an issue`,\n )\n }\n const version = parseVersion(info)\n return { info, version }\n}\n\nexport function getFirstNonEmptyLine(arr: Iterator<string>): string {\n // There should be two blank lines after the header line\n let line = arr.next()\n while (!line.done && line.value.trim() === '') {\n line = arr.next()\n }\n return line.value!\n}\n\nexport function getSeqBounds(line: string) {\n const fields = line.split(/\\s+/)\n const temp = line.slice(fields[0]!.length)\n const s = fields[0]!.length + temp.indexOf(fields[1]!)\n const e = s + fields[1]!.length\n return [s, e] as const\n}\n\n// Use the first block to get the sequence identifiers\nexport function parseBlock(arr: Iterator<string>) {\n let line = getFirstNonEmptyLine(arr)\n const block = []\n let consensusLine = ''\n if (!line) {\n return undefined\n }\n\n while (line) {\n if (line.startsWith(' ')) {\n consensusLine = line\n } else {\n block.push(line)\n }\n line = arr.next().value\n }\n const [start, end] = getSeqBounds(block[0]!)\n const fields = block.map(s => s.split(/\\s+/))\n const ids = fields.map(s => s[0]!)\n const seqs = block.map(s => s.slice(start, end))\n let consensus = consensusLine.slice(start, end)\n\n // handle if the consensus trailing whitespace got trimmed\n const remainder = seqs[0]!.length - consensus.length\n if (remainder) {\n consensus += ' '.repeat(remainder)\n }\n return {\n ids,\n seqs,\n consensus,\n }\n}\n\nexport function parseBlocks(arr: Iterator<string>) {\n let block\n const res = parseBlock(arr)\n if (res !== undefined) {\n while ((block = parseBlock(arr))) {\n for (let i = 0; i < block.seqs.length; i++) {\n res.seqs[i] += block.seqs[i]!\n }\n res.consensus += block.consensus\n }\n }\n return res\n}\n", "import { parsePairwiseBlocks } from './pairwise.ts'\nimport { parseBlocks, parseHeader, getFirstNonEmptyLine } from './util.ts'\n\nexport function parseClustalIter(arr: Iterator<string>) {\n const line = getFirstNonEmptyLine(arr)\n if (!line) {\n throw new Error('Empty file received')\n }\n const header = parseHeader(line)\n\n const res = parseBlocks(arr)\n if (res === undefined) {\n throw new Error('No blocks parsed')\n }\n\n const alns = res.seqs.map((n, index) => ({ id: res.ids[index]!, seq: n }))\n const { consensus } = res\n if (consensus.length !== alns[0]!.seq.length) {\n throw new Error(\n `Consensus length != sequence length. Con ${consensus.length} seq ${alns[0]!.seq.length}`,\n )\n }\n\n return { consensus, alns, header }\n}\n\nexport interface Row {\n id: string\n seq: string\n}\n\nexport function parse(contents: string) {\n const iter = contents.split('\\n')[Symbol.iterator]()\n return parseClustalIter(iter)\n}\n\nexport function parsePairwise(contents: string) {\n const filtered = contents\n .split('\\n')\n .filter(f => !f.startsWith('#'))\n .join('\\n')\n\n const res = parsePairwiseBlocks(filtered.split('\\n')[Symbol.iterator]())\n if (res === undefined) {\n throw new Error('No blocks parsed')\n }\n\n const alns = res.seqs.map((n, index) => ({ id: res.ids[index]!, seq: n }))\n const { consensus } = res\n const firstAln = alns[0]\n if (!firstAln) {\n throw new Error('No alignments found')\n }\n if (consensus.length !== firstAln.seq.length) {\n throw new Error(\n `Consensus length !== sequence length. Con ${consensus.length} seq ${firstAln.seq.length}`,\n )\n }\n if (alns.length !== 2) {\n throw new Error(\n `Expected exactly 2 sequences in pairwise alignment, got ${alns.length}`,\n )\n }\n return {\n consensus,\n alns: alns as [Row, Row],\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["parseEmfAln", "contents", "lines", "f", "seqs", "alns", "line", "arr", "s", "species", "protein", "chr", "start", "end", "strand", "geneId", "unknown", "aln", "j", "parseEmfTree", "tree", "parseVersion", "line", "res", "parseHeader", "info", "knownHeaders", "l", "version", "getFirstNonEmptyLine", "arr", "getSeqBounds", "fields", "temp", "e", "parseBlock", "block", "consensusLine", "start", "end", "ids", "s", "seqs", "consensus", "remainder", "parseBlocks", "i", "parseClustalIter", "arr", "line", "getFirstNonEmptyLine", "header", "parseHeader", "res", "parseBlocks", "alns", "n", "index", "consensus", "parse", "contents", "iter"]
7
+ }