jbrowse-plugin-msaview 5.14.0 → 5.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/MsaViewPanel/components/MsaViewPanel.js +3 -14
- package/dist/MsaViewPanel/isLaunching.d.ts +22 -0
- package/dist/MsaViewPanel/isLaunching.js +15 -0
- package/dist/MsaViewPanel/model.d.ts +7 -0
- package/dist/MsaViewPanel/model.js +13 -0
- package/dist/MsaViewPanel/model.test.js +25 -0
- package/dist/chunks/MsaViewPanel-MYCLHDTC.js +2 -0
- package/dist/chunks/MsaViewPanel-MYCLHDTC.js.map +7 -0
- package/dist/chunks/chunk-2MRW3C7A.js +2 -0
- package/dist/chunks/chunk-2MRW3C7A.js.map +7 -0
- package/dist/chunks/{model-ZXS5732V.js → model-RLH5FCVU.js} +7 -7
- package/dist/chunks/model-RLH5FCVU.js.map +7 -0
- package/dist/jbrowse-plugin-msaview.esm.js +1 -1
- package/dist/jbrowse-plugin-msaview.esm.js.map +1 -1
- package/dist/readinessConformance.test.d.ts +1 -0
- package/dist/readinessConformance.test.js +51 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/MsaViewPanel/components/MsaViewPanel.tsx +3 -14
- package/src/MsaViewPanel/isLaunching.ts +27 -0
- package/src/MsaViewPanel/model.test.ts +28 -0
- package/src/MsaViewPanel/model.ts +15 -0
- package/src/readinessConformance.test.ts +59 -0
- package/src/version.ts +1 -1
- package/dist/chunks/MsaViewPanel-4PHVMXIL.js +0 -2
- package/dist/chunks/MsaViewPanel-4PHVMXIL.js.map +0 -7
- package/dist/chunks/chunk-KYQ2SO7B.js +0 -2
- package/dist/chunks/chunk-KYQ2SO7B.js.map +0 -7
- package/dist/chunks/model-ZXS5732V.js.map +0 -7
|
@@ -3,6 +3,7 @@ import { LoadingEllipses } from '@jbrowse/core/ui';
|
|
|
3
3
|
import { observer } from 'mobx-react';
|
|
4
4
|
import { MSAView } from 'react-msaview';
|
|
5
5
|
import { makeStyles } from 'tss-react/mui';
|
|
6
|
+
import { isLaunching } from '../isLaunching';
|
|
6
7
|
import LaunchProgress from './LaunchProgress';
|
|
7
8
|
const useStyles = makeStyles()({
|
|
8
9
|
loadingContainer: {
|
|
@@ -11,20 +12,8 @@ const useStyles = makeStyles()({
|
|
|
11
12
|
});
|
|
12
13
|
const MsaViewPanel = observer(function MsaViewPanel2({ model, }) {
|
|
13
14
|
const { classes } = useStyles();
|
|
14
|
-
const {
|
|
15
|
-
|
|
16
|
-
// three gate the same panel -- see LaunchProgress. An indexed view keeps its
|
|
17
|
-
// init for the life of the view (it is how the block is refetched), so that
|
|
18
|
-
// one is only "launching" until the alignment arrives.
|
|
19
|
-
const request = blastParams ?? orthologParams;
|
|
20
|
-
const pending = !!request && !model.launchCompleted;
|
|
21
|
-
// a request that finished and then lost its alignment -- browser storage
|
|
22
|
-
// expires one after 7 days -- is a request again, and LaunchProgress draws it
|
|
23
|
-
// with the Retry that runs it. An error over an alignment still in hand is
|
|
24
|
-
// react-msaview's to draw.
|
|
25
|
-
const hasAlignment = !!(model.data.msa || model.data.tree);
|
|
26
|
-
const expired = !!request && !!model.error && !hasAlignment;
|
|
27
|
-
const launching = pending || expired || (!!init && !model.dataInitialized);
|
|
15
|
+
const { loadingStoredData } = model;
|
|
16
|
+
const launching = isLaunching(model);
|
|
28
17
|
return (React.createElement("div", null, launching ? (React.createElement(LaunchProgress, { model: model })) : loadingStoredData && !model.dataInitialized ? (React.createElement("div", { className: classes.loadingContainer },
|
|
29
18
|
React.createElement(LoadingEllipses, { message: "Loading MSA data", variant: "h6" }))) : (React.createElement(MSAView, { model: model }))));
|
|
30
19
|
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
interface LaunchState {
|
|
2
|
+
blastParams?: unknown;
|
|
3
|
+
orthologParams?: unknown;
|
|
4
|
+
launchCompleted?: boolean;
|
|
5
|
+
error?: unknown;
|
|
6
|
+
init?: unknown;
|
|
7
|
+
dataInitialized?: boolean;
|
|
8
|
+
data: {
|
|
9
|
+
msa?: string;
|
|
10
|
+
tree?: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Whether the panel draws LaunchProgress in place of the alignment. It does for
|
|
15
|
+
* a launch request still running, for a request whose alignment browser
|
|
16
|
+
* storage expired after 7 days (LaunchProgress offers a Retry), and for an init
|
|
17
|
+
* not yet fetched. An indexed view keeps its init for the life of the view, so
|
|
18
|
+
* it only launches until the alignment arrives. react-msaview draws an error
|
|
19
|
+
* over an alignment still in hand.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isLaunching(model: LaunchState): boolean;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the panel draws LaunchProgress in place of the alignment. It does for
|
|
3
|
+
* a launch request still running, for a request whose alignment browser
|
|
4
|
+
* storage expired after 7 days (LaunchProgress offers a Retry), and for an init
|
|
5
|
+
* not yet fetched. An indexed view keeps its init for the life of the view, so
|
|
6
|
+
* it only launches until the alignment arrives. react-msaview draws an error
|
|
7
|
+
* over an alignment still in hand.
|
|
8
|
+
*/
|
|
9
|
+
export function isLaunching(model) {
|
|
10
|
+
const request = model.blastParams ?? model.orthologParams;
|
|
11
|
+
const hasAlignment = !!(model.data.msa || model.data.tree);
|
|
12
|
+
return ((!!request && !model.launchCompleted) ||
|
|
13
|
+
(!!request && !!model.error && !hasAlignment) ||
|
|
14
|
+
(!!model.init && !model.dataInitialized));
|
|
15
|
+
}
|
|
@@ -1033,6 +1033,13 @@ export default function stateModelFactory(): import("@jbrowse/mobx-state-tree").
|
|
|
1033
1033
|
* from. IndexedDB holds exactly these.
|
|
1034
1034
|
*/
|
|
1035
1035
|
readonly unsavedDocuments: MsaDataPayload;
|
|
1036
|
+
/**
|
|
1037
|
+
* #getter
|
|
1038
|
+
* true while a launch, stored data or a file is still loading and no
|
|
1039
|
+
* error has ended it. jb.waitReady, the app's ready marker and
|
|
1040
|
+
* screenshot tools read it for a view.
|
|
1041
|
+
*/
|
|
1042
|
+
readonly showLoading: boolean;
|
|
1036
1043
|
} & {
|
|
1037
1044
|
/**
|
|
1038
1045
|
* #action
|
|
@@ -6,6 +6,7 @@ import { MSAModelF } from 'react-msaview';
|
|
|
6
6
|
import { autoLoadProteinDomains, launchBlastIfNeeded, launchOrthologsIfNeeded, loadStoredData, observeProteinHighlights, processInit, runCleanup, storeDataToIndexedDB, showDomainsOnGenomeIfAsked, syncGenomeHoverToMsaColumn, } from './afterCreateAutoruns';
|
|
7
7
|
import { connectedDomains } from './connectedDomains';
|
|
8
8
|
import { domainTrackConf } from './domainTrack';
|
|
9
|
+
import { isLaunching } from './isLaunching';
|
|
9
10
|
import { msaCoordToGenomeCoord, msaCoordToGenomeRegions, } from './msaCoordToGenomeCoord';
|
|
10
11
|
import { resolveConnectedTranscriptIfNeeded } from './resolveConnectedTranscript';
|
|
11
12
|
import { syncSessionHighlights } from './sessionHighlights';
|
|
@@ -264,6 +265,18 @@ export default function stateModelFactory() {
|
|
|
264
265
|
gff: unsaved('gff', self.gffFilehandle),
|
|
265
266
|
};
|
|
266
267
|
},
|
|
268
|
+
/**
|
|
269
|
+
* #getter
|
|
270
|
+
* true while a launch, stored data or a file is still loading and no
|
|
271
|
+
* error has ended it. jb.waitReady, the app's ready marker and
|
|
272
|
+
* screenshot tools read it for a view.
|
|
273
|
+
*/
|
|
274
|
+
get showLoading() {
|
|
275
|
+
return (!self.error &&
|
|
276
|
+
(isLaunching(self) ||
|
|
277
|
+
(self.loadingStoredData && !self.dataInitialized) ||
|
|
278
|
+
self.isLoading));
|
|
279
|
+
},
|
|
267
280
|
}))
|
|
268
281
|
.actions(self => ({
|
|
269
282
|
/**
|
|
@@ -265,3 +265,28 @@ describe('connectedDomains', () => {
|
|
|
265
265
|
});
|
|
266
266
|
});
|
|
267
267
|
});
|
|
268
|
+
// jb.waitReady and the app's ready marker read showLoading, and read a view
|
|
269
|
+
// without one as ready while its alignment is still being built
|
|
270
|
+
describe('showLoading', () => {
|
|
271
|
+
test('a fresh view with nothing to load is not loading', () => {
|
|
272
|
+
expect(view().showLoading).toBe(false);
|
|
273
|
+
});
|
|
274
|
+
test('a running launch is loading until it fails', () => {
|
|
275
|
+
const model = view();
|
|
276
|
+
model.setBlastParams({ ...BLAST_PARAMS });
|
|
277
|
+
expect(model.showLoading).toBe(true);
|
|
278
|
+
model.setError(new Error('EBI said no'));
|
|
279
|
+
expect(model.showLoading).toBe(false);
|
|
280
|
+
});
|
|
281
|
+
test('a finished launch is not loading', async () => {
|
|
282
|
+
vi.mocked(doLaunchBlast).mockResolvedValue({
|
|
283
|
+
msa: '>a\nMK\n>b\nMK',
|
|
284
|
+
tree: '(a,b);',
|
|
285
|
+
treeMetadata: '{}',
|
|
286
|
+
});
|
|
287
|
+
const model = view();
|
|
288
|
+
model.setBlastParams({ ...BLAST_PARAMS });
|
|
289
|
+
await new Promise(res => setTimeout(res, 0));
|
|
290
|
+
expect(model.showLoading).toBe(false);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as P,b,d as k,e as J}from"./chunk-2MRW3C7A.js";import{d as M}from"./chunk-RTIGCYCA.js";import"./chunk-MDZDMDUF.js";import"./chunk-YTIKINP5.js";import"./chunk-IBVOKJV3.js";import"./chunk-YXL36I7H.js";import"./chunk-FCWXYIFC.js";import{a as B}from"./chunk-M2AAIXY5.js";import"./chunk-6GRHBYHB.js";import"./chunk-QWXSEUZI.js";import"./chunk-K55ZVN73.js";import{a as y}from"./chunk-NSASZ5FM.js";import"./chunk-EKTRLQV7.js";import"./chunk-W5VCM3IV.js";import"./chunk-4IRL4OLU.js";import"./chunk-73KAPCJ2.js";import"./chunk-Y7M36762.js";import{a as v}from"./chunk-CVLB4VFP.js";import{a as L}from"./chunk-DNFD5II2.js";import{a as w}from"./chunk-BX4Z274Q.js";import{a as p}from"./chunk-J6KYLXRD.js";import{b as i}from"./chunk-FY3BTKY4.js";var a=i(p()),E=i(L()),T=i(v());var z=i(y());var o=i(p()),t=i(L()),n=i(w()),C=i(v()),V=i(y());var u=i(p()),S=i(w());function A({jobId:s}){let r=J(s)?k(s):b(s);return u.default.createElement(S.Typography,null,"Job ",s," (",u.default.createElement(B,{href:r},"see status"),")")}var d=A;var N=(0,V.makeStyles)()({margin:{padding:20},progressRow:{display:"flex",alignItems:"center",gap:12}}),U=(0,C.observer)(function({model:r}){let{progress:m,rid:e,error:l}=r,{classes:g}=N(),I=!r.launchCompleted,{blastParams:f,orthologParams:h}=r,c=f?`Running EBI ${f.searchProgram==="phmmer"?"phmmer":"BLAST"}`:h?h.source==="uniref"?"Building UniRef homolog alignment":"Building ortholog alignment":"Loading alignment";return o.default.createElement("div",{className:g.margin},l?o.default.createElement(o.default.Fragment,null,o.default.createElement(n.Typography,{variant:"h5"},I?`${c} failed`:"Alignment no longer available"),e?o.default.createElement(d,{jobId:e}):null,o.default.createElement(t.ErrorMessage,{error:l}),o.default.createElement("div",{className:g.progressRow},o.default.createElement(n.Button,{variant:"outlined",size:"small",onClick:()=>{r.retryLaunch()}},"Retry"),o.default.createElement(n.Button,{variant:"outlined",size:"small",onClick:()=>{r.cancelLaunch()}},"Dismiss"))):o.default.createElement(o.default.Fragment,null,o.default.createElement(t.LoadingEllipses,{message:c,variant:"h5"}),e?o.default.createElement(d,{jobId:e}):null,o.default.createElement("div",{className:g.progressRow},o.default.createElement(n.Typography,null,m||"Initializing"),o.default.createElement(n.Button,{variant:"outlined",size:"small",onClick:()=>{r.cancelLaunch()}},"Cancel"))))}),x=U;var D=(0,z.makeStyles)()({loadingContainer:{padding:20}}),$=(0,T.observer)(function({model:r}){let{classes:m}=D(),{loadingStoredData:e}=r,l=P(r);return a.default.createElement("div",null,l?a.default.createElement(x,{model:r}):e&&!r.dataInitialized?a.default.createElement("div",{className:m.loadingContainer},a.default.createElement(E.LoadingEllipses,{message:"Loading MSA data",variant:"h6"})):a.default.createElement(M,{model:r}))}),X=$;export{X as default};
|
|
2
|
+
//# sourceMappingURL=MsaViewPanel-MYCLHDTC.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/MsaViewPanel/components/MsaViewPanel.tsx", "../../src/MsaViewPanel/components/LaunchProgress.tsx", "../../src/MsaViewPanel/components/JobLink.tsx"],
|
|
4
|
+
"sourcesContent": ["import React from 'react'\n\nimport { LoadingEllipses } from '@jbrowse/core/ui'\nimport { observer } from 'mobx-react'\nimport { MSAView } from 'react-msaview'\nimport { makeStyles } from 'tss-react/mui'\n\nimport { isLaunching } from '../isLaunching'\nimport LaunchProgress from './LaunchProgress'\n\nimport type { JBrowsePluginMsaViewModel } from '../model'\n\nconst useStyles = makeStyles()({\n loadingContainer: {\n padding: 20,\n },\n})\n\nconst MsaViewPanel = observer(function MsaViewPanel2({\n model,\n}: {\n model: JBrowsePluginMsaViewModel\n}) {\n const { classes } = useStyles()\n const { loadingStoredData } = model\n const launching = isLaunching(model)\n return (\n <div>\n {launching ? (\n <LaunchProgress model={model} />\n ) : loadingStoredData && !model.dataInitialized ? (\n <div className={classes.loadingContainer}>\n <LoadingEllipses message=\"Loading MSA data\" variant=\"h6\" />\n </div>\n ) : (\n <MSAView model={model} />\n )}\n </div>\n )\n})\n\nexport default MsaViewPanel\n", "import React from 'react'\n\nimport { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui'\nimport { Button, Typography } from '@mui/material'\nimport { observer } from 'mobx-react'\nimport { makeStyles } from 'tss-react/mui'\n\nimport JobLink from './JobLink'\n\nimport type { JBrowsePluginMsaViewModel } from '../model'\n\nconst useStyles = makeStyles()({\n margin: {\n padding: 20,\n },\n progressRow: {\n display: 'flex',\n alignItems: 'center',\n gap: 12,\n },\n})\n\n/**\n * What a view shows while it is still building its alignment, and what it shows\n * when that fails.\n *\n * Every launch that resolves something states its request on the model --\n * `blastParams`, `orthologParams`, `init` -- and one not yet marked\n * `launchCompleted` IS \"no alignment yet\", as is a completed one whose stored\n * alignment has since expired. The error a failed launch records is only\n * readable here. This used to key on `blastParams` alone, which left an ortholog\n * launch rendering an empty MSAView for the minutes its alignment takes and, on\n * failure, forever: the error was set and nothing drew it.\n */\nconst LaunchProgress = observer(function LaunchProgress2({\n model,\n}: {\n model: JBrowsePluginMsaViewModel\n}) {\n const { progress, rid, error } = model\n const { classes } = useStyles()\n // the request stays on the model after it succeeds, so an expired\n // alignment's panel is still named after the search that built it\n const pending = !model.launchCompleted\n const { blastParams, orthologParams } = model\n const message = blastParams\n ? `Running EBI ${blastParams.searchProgram === 'phmmer' ? 'phmmer' : 'BLAST'}`\n : orthologParams\n ? orthologParams.source === 'uniref'\n ? 'Building UniRef homolog alignment'\n : 'Building ortholog alignment'\n : 'Loading alignment'\n return (\n <div className={classes.margin}>\n {error ? (\n <>\n <Typography variant=\"h5\">\n {pending ? `${message} failed` : 'Alignment no longer available'}\n </Typography>\n {/* the job outlives the browser, so its link is worth keeping next to\n the failure -- EBI's own page says more about a job than we can */}\n {rid ? <JobLink jobId={rid} /> : null}\n <ErrorMessage error={error} />\n {/* the request stays on the model after a failure, so without these\n the view is a dead end that resubmits the job on every reload */}\n <div className={classes.progressRow}>\n <Button\n variant=\"outlined\"\n size=\"small\"\n onClick={() => {\n model.retryLaunch()\n }}\n >\n Retry\n </Button>\n <Button\n variant=\"outlined\"\n size=\"small\"\n onClick={() => {\n model.cancelLaunch()\n }}\n >\n Dismiss\n </Button>\n </div>\n </>\n ) : (\n <>\n <LoadingEllipses message={message} variant=\"h5\" />\n {rid ? <JobLink jobId={rid} /> : null}\n <div className={classes.progressRow}>\n <Typography>{progress || 'Initializing'}</Typography>\n {/* stops the polling and returns to the import form; the EBI job\n itself runs on regardless, which is what its JobLink is for */}\n <Button\n variant=\"outlined\"\n size=\"small\"\n onClick={() => {\n model.cancelLaunch()\n }}\n >\n Cancel\n </Button>\n </div>\n </>\n )}\n </div>\n )\n})\n\nexport default LaunchProgress\n", "import React from 'react'\n\nimport { Typography } from '@mui/material'\n\nimport ExternalLink from '../../components/ExternalLink'\nimport { ebiBlastResultUrl } from '../../utils/ebiBlast'\nimport { isPhmmerJobId, phmmerResultUrl } from '../../utils/phmmer'\n\nfunction JobLink({ jobId }: { jobId: string }) {\n // read off the job id rather than the launch params, so a link rebuilt for an\n // old cached job still points at the tool that actually ran it\n const url = isPhmmerJobId(jobId)\n ? phmmerResultUrl(jobId)\n : ebiBlastResultUrl(jobId)\n return (\n <Typography>\n Job {jobId} (<ExternalLink href={url}>see status</ExternalLink>)\n </Typography>\n )\n}\n\nexport default JobLink\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["import_react", "import_ui", "import_mobx_react", "import_mui", "import_react", "import_ui", "import_material", "import_mobx_react", "import_mui", "import_react", "import_material", "JobLink", "jobId", "url", "isPhmmerJobId", "phmmerResultUrl", "ebiBlastResultUrl", "React", "ExternalLink", "JobLink_default", "useStyles", "LaunchProgress", "model", "progress", "rid", "error", "classes", "pending", "blastParams", "orthologParams", "message", "React", "JobLink_default", "LaunchProgress_default", "useStyles", "MsaViewPanel", "model", "classes", "loadingStoredData", "launching", "isLaunching", "React", "LaunchProgress_default", "Loading_default", "MsaViewPanel_default"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{j as u,p as l,q as h,u as p}from"./chunk-6GRHBYHB.js";function q(t){let n=t.blastParams??t.orthologParams,e=!!(t.data.msa||t.data.tree);return!!n&&!t.launchCompleted||!!n&&!!t.error&&!e||!!t.init&&!t.dataInitialized}var g="ncbiblast";function f(t){return(t.hits??[]).map(n=>{let e=Number.parseInt(n.hit_uni_ox??"",10);return{accession:n.hit_acc??"unknown",id:n.hit_id??n.hit_acc??"unknown",sciname:n.hit_uni_os??n.hit_os??"unknown",taxid:Number.isNaN(e)?void 0:e,title:n.hit_uni_de??n.hit_desc,sequence:(n.hit_hsps?.[0]?.hsp_hseq??"").replaceAll("-","")}})}function A(t){return`https://www.ebi.ac.uk/jdispatcher/sss/${g}/summary?jobId=${t}`}async function w({query:t,blastDatabase:n,maxHits:e,onProgress:s,onRid:r,signal:i}){let a=String(h(e??l)),o=await u({tool:g,label:"BLAST",params:{program:"blastp",stype:"protein",database:n,sequence:t,alignments:a,scores:a},onProgress:s,onRid:r,signal:i});return{rid:o.jobId,hits:f(JSON.parse(await o.result("json")))}}var I=({database:t,...n})=>w({blastDatabase:t,...n});var m="hmmer3_phmmer";function H(t){return`https://www.ebi.ac.uk/jdispatcher/sss/${m}/summary?jobId=${t}`}function N(t){return t.startsWith(`${m}-`)}function x({rf:t,query:n}){let e=0,s=Array.from(t,r=>r==="x"?n[e++]??"-":"-").join("");if(e!==n.length)throw new Error(`phmmer alignment has ${e} match columns for a query of ${n.length} residues, so the query row cannot be placed`);return s}function _(t){let n=(t??"").replace("[subseq from] ","");if(n.includes("^|^")){let r=n.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 e=/OS=(.*?)\s+(?:OX|GN|PE|SV)=/.exec(n)?.[1],s=/OX=(\d+)/.exec(n)?.[1];return{id:void 0,sciname:e??"unknown",taxid:s?Number.parseInt(s,10):void 0,title:n.split(" OS=")[0]||void 0}}function S(t){let n=t.lastIndexOf("/"),e=n===-1?void 0:/^\d+-\d+$/.exec(t.slice(n+1))?.[0],s=e===void 0?t:t.slice(0,n),r=s.split("|");return r.length===3?{accession:r[1],id:r[2],range:e}:{accession:s,id:s,range:e}}function y({stockholm:t,query:n}){let{gc:e,gs:s,seqdata:r,seqname:i}=new p(t,0).getMSA(),a=e.RF;if(!a)throw new Error("phmmer alignment has no #=GC RF line");return{queryRow:x({rf:a,query:n}),rows:i.map(o=>{let{id:d,...b}=_(s.DE?.[o]?.[0]),c=S(o);return{...c,id:c.id===c.accession&&d?d:c.id,...b,aligned:(r[o]??"").replaceAll(".","-").toUpperCase()}})}}async function P({query:t,database:n,maxHits:e,onProgress:s,onRid:r,signal:i}){let a=await u({tool:m,label:"phmmer",params:{database:n,sequence:t,alignView:"true",...e?{nhits:String(e)}:{}},onProgress:s,onRid:r,signal:i});return{rid:a.jobId,...y({stockholm:await a.result("sto"),query:t})}}function k(t){return t.map(({aligned:n,...e})=>({...e,sequence:n}))}var $=async({database:t,...n})=>{let{rows:e,queryRow:s,rid:r}=await P({database:t,...n});return{rid:r,queryRow:s,hits:k(e)}};export{q as a,A as b,I as c,H as d,N as e,$ as f};
|
|
2
|
+
//# sourceMappingURL=chunk-2MRW3C7A.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/MsaViewPanel/isLaunching.ts", "../../src/utils/ebiBlast.ts", "../../src/utils/phmmer.ts"],
|
|
4
|
+
"sourcesContent": ["interface LaunchState {\n blastParams?: unknown\n orthologParams?: unknown\n launchCompleted?: boolean\n error?: unknown\n init?: unknown\n dataInitialized?: boolean\n data: { msa?: string; tree?: string }\n}\n\n/**\n * Whether the panel draws LaunchProgress in place of the alignment. It does for\n * a launch request still running, for a request whose alignment browser\n * storage expired after 7 days (LaunchProgress offers a Retry), and for an init\n * not yet fetched. An indexed view keeps its init for the life of the view, so\n * it only launches until the alignment arrives. react-msaview draws an error\n * over an alignment still in hand.\n */\nexport function isLaunching(model: LaunchState) {\n const request = model.blastParams ?? model.orthologParams\n const hasAlignment = !!(model.data.msa || model.data.tree)\n return (\n (!!request && !model.launchCompleted) ||\n (!!request && !!model.error && !hasAlignment) ||\n (!!model.init && !model.dataInitialized)\n )\n}\n", "import {\n defaultMaxHits,\n snapBlastHitCount,\n} from '../LaunchMsaView/components/BlastQuery/consts'\nimport { runEbiJob } from './ebiJobDispatcher'\n\nimport type { BlastDatabase } from '../LaunchMsaView/components/BlastQuery/consts'\nimport type { SearchBackend, SearchHit } from './homologSearch'\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 * EBI's hits as search hits. BLAST's alignments are pairwise, one hit at a\n * time, so the gaps come back off and an aligner gets bare sequences. Exported\n * for testing against a captured response: nothing else in CI would notice if\n * EBI renamed a field.\n */\nexport function normalizeEbiBlastHits(result: EbiBlastJson): SearchHit[] {\n return (result.hits ?? []).map(hit => {\n const taxid = Number.parseInt(hit.hit_uni_ox ?? '', 10)\n return {\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 sequence: (hit.hit_hsps?.[0]?.hsp_hseq ?? '').replaceAll('-', ''),\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 */\n maxHits?: number\n onProgress: (arg: string) => void\n onRid: (arg: string) => void\n signal?: AbortSignal\n}) {\n const hitCount = String(snapBlastHitCount(maxHits ?? defaultMaxHits))\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 alignments: hitCount,\n scores: hitCount,\n },\n onProgress,\n onRid,\n signal,\n })\n return {\n rid: job.jobId,\n hits: normalizeEbiBlastHits(\n JSON.parse(await job.result('json')) as EbiBlastJson,\n ),\n }\n}\n\nexport const searchEbiBlast: SearchBackend = ({ database, ...request }) =>\n queryEbiBlast({ blastDatabase: database as BlastDatabase, ...request })\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 return {\n rid: job.jobId,\n ...parsePhmmerAlignment({ stockholm: await job.result('sto'), query }),\n }\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": ["isLaunching", "model", "request", "hasAlignment", "TOOL", "normalizeEbiBlastHits", "result", "hit", "taxid", "ebiBlastResultUrl", "jobId", "queryEbiBlast", "query", "blastDatabase", "maxHits", "onProgress", "onRid", "signal", "hitCount", "snapBlastHitCount", "defaultMaxHits", "job", "runEbiJob", "searchEbiBlast", "database", "request", "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", "toSearchHits", "rows", "aligned", "rest", "searchEbiPhmmer", "request", "queryRow", "rid"]
|
|
7
|
+
}
|