p2s_mapper 1.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.
- package/LICENSE +21 -0
- package/README.md +148 -0
- package/dist/alignmentQuality.d.ts +58 -0
- package/dist/alignmentQuality.js +108 -0
- package/dist/alignmentQuality.js.map +1 -0
- package/dist/alphaFoldModels.d.ts +29 -0
- package/dist/alphaFoldModels.js +60 -0
- package/dist/alphaFoldModels.js.map +1 -0
- package/dist/caCoordsToPdb.d.ts +18 -0
- package/dist/caCoordsToPdb.js +95 -0
- package/dist/caCoordsToPdb.js.map +1 -0
- package/dist/chooseMappedEntity.d.ts +55 -0
- package/dist/chooseMappedEntity.js +140 -0
- package/dist/chooseMappedEntity.js.map +1 -0
- package/dist/coordinates.d.ts +66 -0
- package/dist/coordinates.js +66 -0
- package/dist/coordinates.js.map +1 -0
- package/dist/dbIds.d.ts +16 -0
- package/dist/dbIds.js +42 -0
- package/dist/dbIds.js.map +1 -0
- package/dist/extractPerResidueConfidence.d.ts +49 -0
- package/dist/extractPerResidueConfidence.js +42 -0
- package/dist/extractPerResidueConfidence.js.map +1 -0
- package/dist/extractStructureSequences.d.ts +142 -0
- package/dist/extractStructureSequences.js +217 -0
- package/dist/extractStructureSequences.js.map +1 -0
- package/dist/fetchUtils.d.ts +34 -0
- package/dist/fetchUtils.js +91 -0
- package/dist/fetchUtils.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/invertMap.d.ts +2 -0
- package/dist/invertMap.js +8 -0
- package/dist/invertMap.js.map +1 -0
- package/dist/isoformRanking.d.ts +47 -0
- package/dist/isoformRanking.js +91 -0
- package/dist/isoformRanking.js.map +1 -0
- package/dist/mappings.d.ts +58 -0
- package/dist/mappings.js +193 -0
- package/dist/mappings.js.map +1 -0
- package/dist/package.json +1 -0
- package/dist/pairwiseAlignment.d.ts +35 -0
- package/dist/pairwiseAlignment.js +337 -0
- package/dist/pairwiseAlignment.js.map +1 -0
- package/dist/pdbUniProtMapping.d.ts +75 -0
- package/dist/pdbUniProtMapping.js +241 -0
- package/dist/pdbUniProtMapping.js.map +1 -0
- package/dist/pdbeBestStructures.d.ts +23 -0
- package/dist/pdbeBestStructures.js +79 -0
- package/dist/pdbeBestStructures.js.map +1 -0
- package/dist/pdbeEntryMolecules.d.ts +7 -0
- package/dist/pdbeEntryMolecules.js +39 -0
- package/dist/pdbeEntryMolecules.js.map +1 -0
- package/dist/siftsNumbering.d.ts +23 -0
- package/dist/siftsNumbering.js +44 -0
- package/dist/siftsNumbering.js.map +1 -0
- package/dist/stripStopCodon.d.ts +15 -0
- package/dist/stripStopCodon.js +24 -0
- package/dist/stripStopCodon.js.map +1 -0
- package/dist/structureFormat.d.ts +40 -0
- package/dist/structureFormat.js +54 -0
- package/dist/structureFormat.js.map +1 -0
- package/dist/structureSources.d.ts +28 -0
- package/dist/structureSources.js +76 -0
- package/dist/structureSources.js.map +1 -0
- package/dist/structureUrls.d.ts +54 -0
- package/dist/structureUrls.js +147 -0
- package/dist/structureUrls.js.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.js +27 -0
- package/dist/types.js.map +1 -0
- package/dist/uniProtSearch.d.ts +32 -0
- package/dist/uniProtSearch.js +114 -0
- package/dist/uniProtSearch.js.map +1 -0
- package/esm/alignmentQuality.d.ts +58 -0
- package/esm/alignmentQuality.js +100 -0
- package/esm/alignmentQuality.js.map +1 -0
- package/esm/alphaFoldModels.d.ts +29 -0
- package/esm/alphaFoldModels.js +55 -0
- package/esm/alphaFoldModels.js.map +1 -0
- package/esm/caCoordsToPdb.d.ts +18 -0
- package/esm/caCoordsToPdb.js +91 -0
- package/esm/caCoordsToPdb.js.map +1 -0
- package/esm/chooseMappedEntity.d.ts +55 -0
- package/esm/chooseMappedEntity.js +134 -0
- package/esm/chooseMappedEntity.js.map +1 -0
- package/esm/coordinates.d.ts +66 -0
- package/esm/coordinates.js +58 -0
- package/esm/coordinates.js.map +1 -0
- package/esm/dbIds.d.ts +16 -0
- package/esm/dbIds.js +35 -0
- package/esm/dbIds.js.map +1 -0
- package/esm/extractPerResidueConfidence.d.ts +49 -0
- package/esm/extractPerResidueConfidence.js +38 -0
- package/esm/extractPerResidueConfidence.js.map +1 -0
- package/esm/extractStructureSequences.d.ts +142 -0
- package/esm/extractStructureSequences.js +205 -0
- package/esm/extractStructureSequences.js.map +1 -0
- package/esm/fetchUtils.d.ts +34 -0
- package/esm/fetchUtils.js +82 -0
- package/esm/fetchUtils.js.map +1 -0
- package/esm/index.d.ts +23 -0
- package/esm/index.js +30 -0
- package/esm/index.js.map +1 -0
- package/esm/invertMap.d.ts +2 -0
- package/esm/invertMap.js +5 -0
- package/esm/invertMap.js.map +1 -0
- package/esm/isoformRanking.d.ts +47 -0
- package/esm/isoformRanking.js +86 -0
- package/esm/isoformRanking.js.map +1 -0
- package/esm/mappings.d.ts +58 -0
- package/esm/mappings.js +179 -0
- package/esm/mappings.js.map +1 -0
- package/esm/pairwiseAlignment.d.ts +35 -0
- package/esm/pairwiseAlignment.js +328 -0
- package/esm/pairwiseAlignment.js.map +1 -0
- package/esm/pdbUniProtMapping.d.ts +75 -0
- package/esm/pdbUniProtMapping.js +231 -0
- package/esm/pdbUniProtMapping.js.map +1 -0
- package/esm/pdbeBestStructures.d.ts +23 -0
- package/esm/pdbeBestStructures.js +73 -0
- package/esm/pdbeBestStructures.js.map +1 -0
- package/esm/pdbeEntryMolecules.d.ts +7 -0
- package/esm/pdbeEntryMolecules.js +35 -0
- package/esm/pdbeEntryMolecules.js.map +1 -0
- package/esm/siftsNumbering.d.ts +23 -0
- package/esm/siftsNumbering.js +40 -0
- package/esm/siftsNumbering.js.map +1 -0
- package/esm/stripStopCodon.d.ts +15 -0
- package/esm/stripStopCodon.js +20 -0
- package/esm/stripStopCodon.js.map +1 -0
- package/esm/structureFormat.d.ts +40 -0
- package/esm/structureFormat.js +48 -0
- package/esm/structureFormat.js.map +1 -0
- package/esm/structureSources.d.ts +28 -0
- package/esm/structureSources.js +71 -0
- package/esm/structureSources.js.map +1 -0
- package/esm/structureUrls.d.ts +54 -0
- package/esm/structureUrls.js +134 -0
- package/esm/structureUrls.js.map +1 -0
- package/esm/types.d.ts +14 -0
- package/esm/types.js +23 -0
- package/esm/types.js.map +1 -0
- package/esm/uniProtSearch.d.ts +32 -0
- package/esm/uniProtSearch.js +110 -0
- package/esm/uniProtSearch.js.map +1 -0
- package/package.json +63 -0
- package/src/alignmentQuality.ts +132 -0
- package/src/alphaFoldModels.ts +91 -0
- package/src/caCoordsToPdb.ts +111 -0
- package/src/chooseMappedEntity.ts +198 -0
- package/src/coordinates.ts +119 -0
- package/src/dbIds.ts +46 -0
- package/src/extractPerResidueConfidence.ts +88 -0
- package/src/extractStructureSequences.ts +342 -0
- package/src/fetchUtils.ts +110 -0
- package/src/index.ts +34 -0
- package/src/invertMap.ts +4 -0
- package/src/isoformRanking.ts +139 -0
- package/src/mappings.ts +230 -0
- package/src/pairwiseAlignment.ts +423 -0
- package/src/pdbUniProtMapping.ts +320 -0
- package/src/pdbeBestStructures.ts +94 -0
- package/src/pdbeEntryMolecules.ts +37 -0
- package/src/siftsNumbering.ts +61 -0
- package/src/stripStopCodon.ts +20 -0
- package/src/structureFormat.ts +70 -0
- package/src/structureSources.ts +101 -0
- package/src/structureUrls.ts +171 -0
- package/src/types.ts +30 -0
- package/src/uniProtSearch.ts +212 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stripStopCodon = stripStopCodon;
|
|
4
|
+
exports.stripAllStopCodons = stripAllStopCodons;
|
|
5
|
+
/**
|
|
6
|
+
* Drop the terminal stop codon(s) from a translated protein sequence.
|
|
7
|
+
*
|
|
8
|
+
* Only trailing `*` go: an interior stop (mis-annotated CDS, selenoprotein
|
|
9
|
+
* read-through) occupies a real codon position, and deleting it would shift
|
|
10
|
+
* every later residue out of step with g2p's codon-indexed transcript
|
|
11
|
+
* coordinates — offsetting every genome↔structure hover past that point.
|
|
12
|
+
*/
|
|
13
|
+
function stripStopCodon(seq) {
|
|
14
|
+
return seq.replace(/\*+$/, '');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Strip every stop codon, including interior ones. Only for sequences handed to
|
|
18
|
+
* an external similarity search (Foldseek, AlphaFold), where `*` is not a valid
|
|
19
|
+
* query character and no coordinate depends on the result's positions.
|
|
20
|
+
*/
|
|
21
|
+
function stripAllStopCodons(seq) {
|
|
22
|
+
return seq.replaceAll('*', '');
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=stripStopCodon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stripStopCodon.js","sourceRoot":"","sources":["../src/stripStopCodon.ts"],"names":[],"mappings":";;AAQA,wCAEC;AAOD,gDAEC;AAnBD;;;;;;;GAOG;AACH,SAAgB,cAAc,CAAC,GAAW;IACxC,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;AAChC,CAAC;AAED;;;;GAIG;AACH,SAAgB,kBAAkB,CAAC,GAAW;IAC5C,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;AAChC,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which molstar trajectory parser a structure needs.
|
|
3
|
+
*
|
|
4
|
+
* Getting this wrong is not a graceful failure in either direction: parsing a
|
|
5
|
+
* PDB file as mmCIF throws ("Unexpected token. Expected data_, loop_, or data
|
|
6
|
+
* name"), and — worse — parsing an mmCIF as PDB *succeeds* and yields a model
|
|
7
|
+
* with zero polymer entities and thousands of misread atoms, so the view loads
|
|
8
|
+
* with no sequence, no alignment and no genome mapping, and nothing reports an
|
|
9
|
+
* error.
|
|
10
|
+
*
|
|
11
|
+
* So detection lives here and is applied as the default at every entry point,
|
|
12
|
+
* rather than at one of them.
|
|
13
|
+
*
|
|
14
|
+
* The two names are the subset of molstar's `BuiltInTrajectoryFormat` a
|
|
15
|
+
* structure loaded from a file or a url can be, spelled out rather than
|
|
16
|
+
* imported so nothing here depends on molstar.
|
|
17
|
+
*/
|
|
18
|
+
export type StructureFormat = 'pdb' | 'mmcif';
|
|
19
|
+
/** Trailing extension of a filename or URL, lowercased, ignoring any query or
|
|
20
|
+
* fragment and any `.gz` wrapper (`…/pdb1tup.ent.gz` -> `ent`). */
|
|
21
|
+
export declare function structureFileExtension(nameOrUrl: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Format for a filename or URL. Unknown extensions fall back to mmCIF, which is
|
|
24
|
+
* what every URL this plugin generates itself serves (AlphaFold and RCSB both
|
|
25
|
+
* hand out `.cif`).
|
|
26
|
+
*/
|
|
27
|
+
export declare function structureFormatFromName(nameOrUrl: string): StructureFormat;
|
|
28
|
+
/** Whether a URL points at a binary-encoded structure, which molstar must be
|
|
29
|
+
* told to download as bytes rather than text. */
|
|
30
|
+
export declare function isBinaryStructureUrl(url: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Format for structure text that arrived without a filename — an inline `data`
|
|
33
|
+
* snapshot, a pasted file, or the PDB the plugin synthesizes from Foldseek Cα
|
|
34
|
+
* coordinates. Sniffs the content, which is strictly more reliable than a name
|
|
35
|
+
* and is the only signal available here.
|
|
36
|
+
*
|
|
37
|
+
* mmCIF is line-oriented and its first meaningful line is a `data_` block
|
|
38
|
+
* header (comments and blank lines may precede it); PDB has none.
|
|
39
|
+
*/
|
|
40
|
+
export declare function structureFormatFromContent(data: string): StructureFormat;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.structureFileExtension = structureFileExtension;
|
|
4
|
+
exports.structureFormatFromName = structureFormatFromName;
|
|
5
|
+
exports.isBinaryStructureUrl = isBinaryStructureUrl;
|
|
6
|
+
exports.structureFormatFromContent = structureFormatFromContent;
|
|
7
|
+
// Only the PDB set is enumerated: mmCIF is the fallback (see below), so listing
|
|
8
|
+
// its extensions would be a second source of truth that could disagree.
|
|
9
|
+
const PDB_EXTENSIONS = new Set(['pdb', 'ent', 'pdb1', 'pdb2', 'pdb3']);
|
|
10
|
+
/** Trailing extension of a filename or URL, lowercased, ignoring any query or
|
|
11
|
+
* fragment and any `.gz` wrapper (`…/pdb1tup.ent.gz` -> `ent`). */
|
|
12
|
+
function structureFileExtension(nameOrUrl) {
|
|
13
|
+
const path = nameOrUrl.split(/[?#]/)[0] ?? '';
|
|
14
|
+
const name = path.split('/').pop() ?? '';
|
|
15
|
+
const parts = name.toLowerCase().split('.');
|
|
16
|
+
if (parts.at(-1) === 'gz') {
|
|
17
|
+
parts.pop();
|
|
18
|
+
}
|
|
19
|
+
return parts.length > 1 ? (parts.at(-1) ?? '') : '';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Format for a filename or URL. Unknown extensions fall back to mmCIF, which is
|
|
23
|
+
* what every URL this plugin generates itself serves (AlphaFold and RCSB both
|
|
24
|
+
* hand out `.cif`).
|
|
25
|
+
*/
|
|
26
|
+
function structureFormatFromName(nameOrUrl) {
|
|
27
|
+
return PDB_EXTENSIONS.has(structureFileExtension(nameOrUrl)) ? 'pdb' : 'mmcif';
|
|
28
|
+
}
|
|
29
|
+
/** Whether a URL points at a binary-encoded structure, which molstar must be
|
|
30
|
+
* told to download as bytes rather than text. */
|
|
31
|
+
function isBinaryStructureUrl(url) {
|
|
32
|
+
return structureFileExtension(url) === 'bcif';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Format for structure text that arrived without a filename — an inline `data`
|
|
36
|
+
* snapshot, a pasted file, or the PDB the plugin synthesizes from Foldseek Cα
|
|
37
|
+
* coordinates. Sniffs the content, which is strictly more reliable than a name
|
|
38
|
+
* and is the only signal available here.
|
|
39
|
+
*
|
|
40
|
+
* mmCIF is line-oriented and its first meaningful line is a `data_` block
|
|
41
|
+
* header (comments and blank lines may precede it); PDB has none.
|
|
42
|
+
*/
|
|
43
|
+
function structureFormatFromContent(data) {
|
|
44
|
+
// a data_ header can only appear in the first few lines; don't scan a 30MB
|
|
45
|
+
// structure to find out
|
|
46
|
+
for (const line of data.slice(0, 4096).split('\n')) {
|
|
47
|
+
const trimmed = line.trim();
|
|
48
|
+
if (trimmed !== '' && !trimmed.startsWith('#')) {
|
|
49
|
+
return trimmed.toLowerCase().startsWith('data_') ? 'mmcif' : 'pdb';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return 'pdb';
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=structureFormat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structureFormat.js","sourceRoot":"","sources":["../src/structureFormat.ts"],"names":[],"mappings":";;AAyBA,wDAQC;AAOD,0DAEC;AAID,oDAEC;AAWD,gEAUC;AAlDD,gFAAgF;AAChF,wEAAwE;AACxE,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AAEtE;mEACmE;AACnE,SAAgB,sBAAsB,CAAC,SAAiB;IACtD,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAA;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC3C,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC1B,KAAK,CAAC,GAAG,EAAE,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AACrD,CAAC;AAED;;;;GAIG;AACH,SAAgB,uBAAuB,CAAC,SAAiB;IACvD,OAAO,cAAc,CAAC,GAAG,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAA;AAChF,CAAC;AAED;iDACiD;AACjD,SAAgB,oBAAoB,CAAC,GAAW;IAC9C,OAAO,sBAAsB,CAAC,GAAG,CAAC,KAAK,MAAM,CAAA;AAC/C,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,0BAA0B,CAAC,IAAY;IACrD,2EAA2E;IAC3E,wBAAwB;IACxB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QAC3B,IAAI,OAAO,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/C,OAAO,OAAO,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAA;QACpE,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FetchOptions } from './fetchUtils.ts';
|
|
2
|
+
export interface ExperimentalStructure {
|
|
3
|
+
pdbId: string;
|
|
4
|
+
method: string;
|
|
5
|
+
/** Å; absent for NMR and some EM entries */
|
|
6
|
+
resolution?: number;
|
|
7
|
+
/** 1-based inclusive UniProt residues the entry covers */
|
|
8
|
+
start: number;
|
|
9
|
+
end: number;
|
|
10
|
+
/** fraction of the UniProt sequence the entry covers, 0..1 */
|
|
11
|
+
coverage: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function beaconsSummaryUrl(uniprotId: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* PDBe entries only, best-covering first and sharpest within a tie.
|
|
16
|
+
*
|
|
17
|
+
* Predicted entries (AlphaFold, SWISS-MODEL, AlphaFill) are left out because a
|
|
18
|
+
* prediction comes from AlphaFold's own API, which answers per model. And
|
|
19
|
+
* 3D-Beacons files SASBDB's small-angle-scattering fits under "experimentally
|
|
20
|
+
* determined" too, with numeric ids and near-full coverage — dystrophin's best
|
|
21
|
+
* "structure" by coverage was SASBDB 436 — which are not entries a residue
|
|
22
|
+
* mapping exists for. Hence the `provider === 'PDBe'` gate rather than the
|
|
23
|
+
* category alone.
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseExperimentalStructures(json: unknown): ExperimentalStructure[];
|
|
26
|
+
/** Best-effort: an unreachable API reads as "no entries", which costs a
|
|
27
|
+
* structure suggestion and nothing else. */
|
|
28
|
+
export declare function fetchExperimentalStructures(uniprotId: string, opts?: FetchOptions): Promise<ExperimentalStructure[]>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// The experimental structures 3D-Beacons knows for a UniProt entry: every PDBe
|
|
3
|
+
// entry mapped to the accession, with the UniProt range it covers and its
|
|
4
|
+
// resolution, in one CORS-enabled call. That is what lets a page offer a
|
|
5
|
+
// crystal structure beside a prediction without a PDB search of its own; take
|
|
6
|
+
// the entry id from here and ask `fetchUniProtStructureMappings` for its
|
|
7
|
+
// residue mapping.
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.beaconsSummaryUrl = beaconsSummaryUrl;
|
|
10
|
+
exports.parseExperimentalStructures = parseExperimentalStructures;
|
|
11
|
+
exports.fetchExperimentalStructures = fetchExperimentalStructures;
|
|
12
|
+
const fetchUtils_ts_1 = require("./fetchUtils.js");
|
|
13
|
+
function beaconsSummaryUrl(uniprotId) {
|
|
14
|
+
return `https://www.ebi.ac.uk/pdbe/pdbe-kb/3dbeacons/api/uniprot/summary/${encodeURIComponent(uniprotId)}.json?provider=pdbe`;
|
|
15
|
+
}
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
18
|
+
}
|
|
19
|
+
function finiteNumber(value) {
|
|
20
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* PDBe entries only, best-covering first and sharpest within a tie.
|
|
24
|
+
*
|
|
25
|
+
* Predicted entries (AlphaFold, SWISS-MODEL, AlphaFill) are left out because a
|
|
26
|
+
* prediction comes from AlphaFold's own API, which answers per model. And
|
|
27
|
+
* 3D-Beacons files SASBDB's small-angle-scattering fits under "experimentally
|
|
28
|
+
* determined" too, with numeric ids and near-full coverage — dystrophin's best
|
|
29
|
+
* "structure" by coverage was SASBDB 436 — which are not entries a residue
|
|
30
|
+
* mapping exists for. Hence the `provider === 'PDBe'` gate rather than the
|
|
31
|
+
* category alone.
|
|
32
|
+
*/
|
|
33
|
+
function parseExperimentalStructures(json) {
|
|
34
|
+
const structures = isRecord(json) ? json.structures : undefined;
|
|
35
|
+
if (!Array.isArray(structures)) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
return structures
|
|
39
|
+
.flatMap(entry => {
|
|
40
|
+
const s = isRecord(entry) ? entry.summary : undefined;
|
|
41
|
+
if (!isRecord(s)) {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
const start = finiteNumber(s.uniprot_start);
|
|
45
|
+
const end = finiteNumber(s.uniprot_end);
|
|
46
|
+
const coverage = finiteNumber(s.coverage);
|
|
47
|
+
return typeof s.model_identifier === 'string' &&
|
|
48
|
+
s.provider === 'PDBe' &&
|
|
49
|
+
s.model_category === 'EXPERIMENTALLY DETERMINED' &&
|
|
50
|
+
start !== undefined &&
|
|
51
|
+
end !== undefined &&
|
|
52
|
+
coverage !== undefined
|
|
53
|
+
? [
|
|
54
|
+
{
|
|
55
|
+
pdbId: s.model_identifier.toLowerCase(),
|
|
56
|
+
method: typeof s.experimental_method === 'string'
|
|
57
|
+
? s.experimental_method
|
|
58
|
+
: 'experimental',
|
|
59
|
+
resolution: finiteNumber(s.resolution),
|
|
60
|
+
start,
|
|
61
|
+
end,
|
|
62
|
+
coverage,
|
|
63
|
+
},
|
|
64
|
+
]
|
|
65
|
+
: [];
|
|
66
|
+
})
|
|
67
|
+
.sort((a, b) => b.coverage - a.coverage ||
|
|
68
|
+
(a.resolution ?? Infinity) - (b.resolution ?? Infinity));
|
|
69
|
+
}
|
|
70
|
+
/** Best-effort: an unreachable API reads as "no entries", which costs a
|
|
71
|
+
* structure suggestion and nothing else. */
|
|
72
|
+
async function fetchExperimentalStructures(uniprotId, opts) {
|
|
73
|
+
const res = await (0, fetchUtils_ts_1.rawfetch)(beaconsSummaryUrl(uniprotId), opts).catch(() => undefined);
|
|
74
|
+
return res?.ok ? parseExperimentalStructures(await res.json()) : [];
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=structureSources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structureSources.js","sourceRoot":"","sources":["../src/structureSources.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export declare function uniprotGffUrl(uniprotId: string): string;
|
|
2
|
+
export declare function uniprotFastaUrl(uniprotId: string): string;
|
|
3
|
+
export declare function uniprotEntryUrl(uniprotId: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* The guessed filename of an accession's canonical AlphaFold model. A guess:
|
|
6
|
+
* the F1 fragment does not exist past the length cap and the version moves, so
|
|
7
|
+
* this is the last resort when the prediction API cannot be reached. Ask
|
|
8
|
+
* `fetchAlphaFoldModels` for the real files.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getAlphaFoldStructureUrl(uniprotId: string, version?: string): string;
|
|
11
|
+
export declare function getPdbStructureUrl(pdbId: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the `{ pdbId }` shorthand to a concrete structure URL, so a
|
|
14
|
+
* hand-authored snapshot or a short launch URL doesn't have to know RCSB's
|
|
15
|
+
* filename format. An explicit `url` (or inline `data`) always wins.
|
|
16
|
+
* Idempotent: re-resolving an already-resolved spec returns the same url.
|
|
17
|
+
*
|
|
18
|
+
* `uniprotId` deliberately resolves to no url here, and still outranks a
|
|
19
|
+
* `pdbId`. An accession's files are whatever AlphaFold DB currently holds — an
|
|
20
|
+
* isoform model, several fragments, a new model version — so the structure
|
|
21
|
+
* keeps the accession and the structure loader asks the prediction API which
|
|
22
|
+
* file to open.
|
|
23
|
+
*
|
|
24
|
+
* Shared by the Structure model's snapshot preprocessor and the
|
|
25
|
+
* LaunchView-ProteinView extension point, which would otherwise each carry
|
|
26
|
+
* their own copy of the same precedence rule.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveStructureUrl({ url, data, uniprotId, pdbId, }: {
|
|
29
|
+
url?: string;
|
|
30
|
+
data?: string;
|
|
31
|
+
uniprotId?: string;
|
|
32
|
+
pdbId?: string;
|
|
33
|
+
}): string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* The PDB id of a structure URL from the PDB archive, lowercased, or undefined
|
|
36
|
+
* for any other URL. Accepts the filename forms those hosts serve, e.g.
|
|
37
|
+
* `1TUP.cif`, `pdb1tup.ent.gz`, `1tup_updated.cif`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function getPdbIdFromUrl(url: string): string | undefined;
|
|
40
|
+
/** The accession of an AlphaFold model id or url, with its isoform suffix:
|
|
41
|
+
* `AF-P16442-F1-model_v6` gives P16442, `AF-P04637-2-F1-model_v6` P04637-2. */
|
|
42
|
+
export declare function getUniprotIdFromAlphaFoldTarget(target: string): string | undefined;
|
|
43
|
+
export declare function getStructureUrlFromTarget(target: string, db: string): string | undefined;
|
|
44
|
+
export declare function getConfidenceUrlFromTarget(target: string): string | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* What a structure is called in the UI: its PDB id, its AlphaFold accession,
|
|
47
|
+
* else the file's name. Shared by the view title, the alignment panel heading
|
|
48
|
+
* and the hover readout, so one structure reads the same everywhere.
|
|
49
|
+
*/
|
|
50
|
+
export declare function structureDisplayLabel({ url, data, uniprotId, }: {
|
|
51
|
+
url?: string;
|
|
52
|
+
data?: string;
|
|
53
|
+
uniprotId?: string;
|
|
54
|
+
}): string;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Pure structure-URL builders and target parsers. Kept dependency-free so both
|
|
3
|
+
// the launch flow and the ProteinView model (e.g. resolving a `uniprotId`/`pdbId`
|
|
4
|
+
// shorthand at hydration) can import them without pulling in heavy launch code.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.uniprotGffUrl = uniprotGffUrl;
|
|
7
|
+
exports.uniprotFastaUrl = uniprotFastaUrl;
|
|
8
|
+
exports.uniprotEntryUrl = uniprotEntryUrl;
|
|
9
|
+
exports.getAlphaFoldStructureUrl = getAlphaFoldStructureUrl;
|
|
10
|
+
exports.getPdbStructureUrl = getPdbStructureUrl;
|
|
11
|
+
exports.resolveStructureUrl = resolveStructureUrl;
|
|
12
|
+
exports.getPdbIdFromUrl = getPdbIdFromUrl;
|
|
13
|
+
exports.getUniprotIdFromAlphaFoldTarget = getUniprotIdFromAlphaFoldTarget;
|
|
14
|
+
exports.getStructureUrlFromTarget = getStructureUrlFromTarget;
|
|
15
|
+
exports.getConfidenceUrlFromTarget = getConfidenceUrlFromTarget;
|
|
16
|
+
exports.structureDisplayLabel = structureDisplayLabel;
|
|
17
|
+
const ALPHAFOLD_VERSION = 'v6';
|
|
18
|
+
// UniProt endpoints. rest.* is the machine API (the GFF the feature tracks and
|
|
19
|
+
// the alignment's annotation rows both read, the FASTA the temporary protein
|
|
20
|
+
// assembly is built from); www.*/entry is the human-facing entry page the UI
|
|
21
|
+
// links to. Centralized so a path change is one edit rather than a grep.
|
|
22
|
+
function uniprotGffUrl(uniprotId) {
|
|
23
|
+
return `https://rest.uniprot.org/uniprotkb/${uniprotId}.gff`;
|
|
24
|
+
}
|
|
25
|
+
function uniprotFastaUrl(uniprotId) {
|
|
26
|
+
return `https://rest.uniprot.org/uniprotkb/${uniprotId}.fasta`;
|
|
27
|
+
}
|
|
28
|
+
function uniprotEntryUrl(uniprotId) {
|
|
29
|
+
return `https://www.uniprot.org/uniprotkb/${uniprotId}/entry`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The guessed filename of an accession's canonical AlphaFold model. A guess:
|
|
33
|
+
* the F1 fragment does not exist past the length cap and the version moves, so
|
|
34
|
+
* this is the last resort when the prediction API cannot be reached. Ask
|
|
35
|
+
* `fetchAlphaFoldModels` for the real files.
|
|
36
|
+
*/
|
|
37
|
+
function getAlphaFoldStructureUrl(uniprotId, version = ALPHAFOLD_VERSION) {
|
|
38
|
+
return `https://alphafold.ebi.ac.uk/files/AF-${uniprotId}-F1-model_${version}.cif`;
|
|
39
|
+
}
|
|
40
|
+
function getPdbStructureUrl(pdbId) {
|
|
41
|
+
return `https://files.rcsb.org/download/${pdbId}.cif`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolve the `{ pdbId }` shorthand to a concrete structure URL, so a
|
|
45
|
+
* hand-authored snapshot or a short launch URL doesn't have to know RCSB's
|
|
46
|
+
* filename format. An explicit `url` (or inline `data`) always wins.
|
|
47
|
+
* Idempotent: re-resolving an already-resolved spec returns the same url.
|
|
48
|
+
*
|
|
49
|
+
* `uniprotId` deliberately resolves to no url here, and still outranks a
|
|
50
|
+
* `pdbId`. An accession's files are whatever AlphaFold DB currently holds — an
|
|
51
|
+
* isoform model, several fragments, a new model version — so the structure
|
|
52
|
+
* keeps the accession and the structure loader asks the prediction API which
|
|
53
|
+
* file to open.
|
|
54
|
+
*
|
|
55
|
+
* Shared by the Structure model's snapshot preprocessor and the
|
|
56
|
+
* LaunchView-ProteinView extension point, which would otherwise each carry
|
|
57
|
+
* their own copy of the same precedence rule.
|
|
58
|
+
*/
|
|
59
|
+
function resolveStructureUrl({ url, data, uniprotId, pdbId, }) {
|
|
60
|
+
if (url !== undefined || data !== undefined) {
|
|
61
|
+
return url;
|
|
62
|
+
}
|
|
63
|
+
return uniprotId !== undefined || pdbId === undefined
|
|
64
|
+
? undefined
|
|
65
|
+
: getPdbStructureUrl(pdbId);
|
|
66
|
+
}
|
|
67
|
+
// Hosts that serve the PDB archive, so a 4-character filename there really is
|
|
68
|
+
// that PDB entry. Restricted deliberately: a user-uploaded model happening to be
|
|
69
|
+
// named 1abc.cif must not pick up 1ABC's UniProt annotations.
|
|
70
|
+
const PDB_ARCHIVE_HOSTS = ['files.rcsb.org', 'www.ebi.ac.uk', 'ftp.ebi.ac.uk'];
|
|
71
|
+
/**
|
|
72
|
+
* The PDB id of a structure URL from the PDB archive, lowercased, or undefined
|
|
73
|
+
* for any other URL. Accepts the filename forms those hosts serve, e.g.
|
|
74
|
+
* `1TUP.cif`, `pdb1tup.ent.gz`, `1tup_updated.cif`.
|
|
75
|
+
*/
|
|
76
|
+
function getPdbIdFromUrl(url) {
|
|
77
|
+
const { host, file } = (() => {
|
|
78
|
+
try {
|
|
79
|
+
const parsed = new URL(url);
|
|
80
|
+
return { host: parsed.hostname, file: parsed.pathname.split('/').pop() };
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return { host: undefined, file: undefined };
|
|
84
|
+
}
|
|
85
|
+
})();
|
|
86
|
+
if (host === undefined || !PDB_ARCHIVE_HOSTS.includes(host)) {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
const match = /^(?:pdb)?([1-9][a-z0-9]{3})(?:_updated)?\.(?:cif|bcif|pdb|ent)(?:\.gz)?$/i.exec(file ?? '');
|
|
90
|
+
return match?.[1]?.toLowerCase();
|
|
91
|
+
}
|
|
92
|
+
// Foldseek targets may contain a description after the ID separated by a
|
|
93
|
+
// space, e.g. "AF-P16442-F1-model_v6 Histo-blood group ABO transferase".
|
|
94
|
+
function extractTargetId(target) {
|
|
95
|
+
return target.split(' ')[0];
|
|
96
|
+
}
|
|
97
|
+
/** The accession of an AlphaFold model id or url, with its isoform suffix:
|
|
98
|
+
* `AF-P16442-F1-model_v6` gives P16442, `AF-P04637-2-F1-model_v6` P04637-2. */
|
|
99
|
+
function getUniprotIdFromAlphaFoldTarget(target) {
|
|
100
|
+
const match = /AF-([A-Z0-9]+(?:-\d+)?)-F\d+/.exec(extractTargetId(target));
|
|
101
|
+
return match?.[1];
|
|
102
|
+
}
|
|
103
|
+
// Foldseek names a pdb100 hit by the assembly file and chain it searched,
|
|
104
|
+
// `8f2h-assembly1.cif.gz_A`; the entry id is the first four characters.
|
|
105
|
+
function getStructureUrlFromTarget(target, db) {
|
|
106
|
+
const targetId = extractTargetId(target);
|
|
107
|
+
if (targetId.startsWith('AF-')) {
|
|
108
|
+
return `https://alphafold.ebi.ac.uk/files/${targetId}.cif`;
|
|
109
|
+
}
|
|
110
|
+
const pdbId = db === 'pdb100'
|
|
111
|
+
? /^([1-9][a-z0-9]{3})(?:[-_.]|$)/i.exec(targetId)?.[1]
|
|
112
|
+
: undefined;
|
|
113
|
+
return pdbId ? getPdbStructureUrl(pdbId) : undefined;
|
|
114
|
+
}
|
|
115
|
+
function getConfidenceUrlFromTarget(target) {
|
|
116
|
+
const targetId = extractTargetId(target);
|
|
117
|
+
if (targetId.startsWith('AF-')) {
|
|
118
|
+
const confidenceId = targetId.replace('-model_', '-confidence_');
|
|
119
|
+
return `https://alphafold.ebi.ac.uk/files/${confidenceId}.json`;
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* What a structure is called in the UI: its PDB id, its AlphaFold accession,
|
|
125
|
+
* else the file's name. Shared by the view title, the alignment panel heading
|
|
126
|
+
* and the hover readout, so one structure reads the same everywhere.
|
|
127
|
+
*/
|
|
128
|
+
function structureDisplayLabel({ url, data, uniprotId, }) {
|
|
129
|
+
if (url === undefined) {
|
|
130
|
+
return data !== undefined
|
|
131
|
+
? 'Uploaded structure'
|
|
132
|
+
: uniprotId
|
|
133
|
+
? `AlphaFold ${uniprotId}`
|
|
134
|
+
: '';
|
|
135
|
+
}
|
|
136
|
+
const pdbId = getPdbIdFromUrl(url);
|
|
137
|
+
if (pdbId) {
|
|
138
|
+
return pdbId.toUpperCase();
|
|
139
|
+
}
|
|
140
|
+
const urlAccession = getUniprotIdFromAlphaFoldTarget(url);
|
|
141
|
+
if (urlAccession) {
|
|
142
|
+
return `AlphaFold ${urlAccession}`;
|
|
143
|
+
}
|
|
144
|
+
const name = url.split('/').pop()?.split('?')[0];
|
|
145
|
+
return name === undefined || name === '' ? url : name;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=structureUrls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structureUrls.js","sourceRoot":"","sources":["../src/structureUrls.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type AlignmentAlgorithm = 'needleman_wunsch' | 'smith_waterman';
|
|
2
|
+
export declare const ALIGNMENT_ALGORITHMS: {
|
|
3
|
+
readonly NEEDLEMAN_WUNSCH: "needleman_wunsch";
|
|
4
|
+
readonly SMITH_WATERMAN: "smith_waterman";
|
|
5
|
+
};
|
|
6
|
+
export declare const DEFAULT_ALIGNMENT_ALGORITHM: AlignmentAlgorithm;
|
|
7
|
+
export declare const ALIGNMENT_ALGORITHM_VALUES: AlignmentAlgorithm[];
|
|
8
|
+
/**
|
|
9
|
+
* Parses an untrusted algorithm name (a URL session-spec param) into the union.
|
|
10
|
+
* The only place a coercion is needed: the model property is an enumeration, so
|
|
11
|
+
* everything downstream of hydration is already typed.
|
|
12
|
+
*/
|
|
13
|
+
export declare function coerceAlignmentAlgorithm(value: string): AlignmentAlgorithm;
|
|
14
|
+
export declare const ALIGNMENT_ALGORITHM_LABELS: Record<AlignmentAlgorithm, string>;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ALIGNMENT_ALGORITHM_LABELS = exports.ALIGNMENT_ALGORITHM_VALUES = exports.DEFAULT_ALIGNMENT_ALGORITHM = exports.ALIGNMENT_ALGORITHMS = void 0;
|
|
4
|
+
exports.coerceAlignmentAlgorithm = coerceAlignmentAlgorithm;
|
|
5
|
+
exports.ALIGNMENT_ALGORITHMS = {
|
|
6
|
+
NEEDLEMAN_WUNSCH: 'needleman_wunsch',
|
|
7
|
+
SMITH_WATERMAN: 'smith_waterman',
|
|
8
|
+
};
|
|
9
|
+
exports.DEFAULT_ALIGNMENT_ALGORITHM = 'smith_waterman';
|
|
10
|
+
exports.ALIGNMENT_ALGORITHM_VALUES = [
|
|
11
|
+
'needleman_wunsch',
|
|
12
|
+
'smith_waterman',
|
|
13
|
+
];
|
|
14
|
+
/**
|
|
15
|
+
* Parses an untrusted algorithm name (a URL session-spec param) into the union.
|
|
16
|
+
* The only place a coercion is needed: the model property is an enumeration, so
|
|
17
|
+
* everything downstream of hydration is already typed.
|
|
18
|
+
*/
|
|
19
|
+
function coerceAlignmentAlgorithm(value) {
|
|
20
|
+
return (exports.ALIGNMENT_ALGORITHM_VALUES.find(v => v === value) ??
|
|
21
|
+
exports.DEFAULT_ALIGNMENT_ALGORITHM);
|
|
22
|
+
}
|
|
23
|
+
exports.ALIGNMENT_ALGORITHM_LABELS = {
|
|
24
|
+
needleman_wunsch: 'Needleman-Wunsch',
|
|
25
|
+
smith_waterman: 'Smith-Waterman',
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AAmBA,4DAKC;AAtBY,QAAA,oBAAoB,GAAG;IAClC,gBAAgB,EAAE,kBAAkB;IACpC,cAAc,EAAE,gBAAgB;CACxB,CAAA;AAEG,QAAA,2BAA2B,GAAuB,gBAAgB,CAAA;AAElE,QAAA,0BAA0B,GAAyB;IAC9D,kBAAkB;IAClB,gBAAgB;CACjB,CAAA;AAED;;;;GAIG;AACH,SAAgB,wBAAwB,CAAC,KAAa;IACpD,OAAO,CACL,kCAA0B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC;QACjD,mCAA2B,CAC5B,CAAA;AACH,CAAC;AAEY,QAAA,0BAA0B,GAAuC;IAC5E,gBAAgB,EAAE,kBAAkB;IACpC,cAAc,EAAE,gBAAgB;CACjC,CAAA"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { FetchOptions } from './fetchUtils.ts';
|
|
2
|
+
export interface UniProtEntry {
|
|
3
|
+
accession: string;
|
|
4
|
+
id?: string;
|
|
5
|
+
geneName?: string;
|
|
6
|
+
organismName?: string;
|
|
7
|
+
proteinName?: string;
|
|
8
|
+
isReviewed: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* `gene_exact` rather than `gene`, which also matches synonyms and returns
|
|
12
|
+
* paralogs. Without a taxon the query runs across every species, so a mouse
|
|
13
|
+
* assembly whose tracks carry no taxId lists mouse beside human instead of
|
|
14
|
+
* silently answering with the human entry.
|
|
15
|
+
*
|
|
16
|
+
* The symbol is quoted because a bare one containing `:` — an HLA allele, a
|
|
17
|
+
* name straight out of an annotation file — reads as another field to
|
|
18
|
+
* UniProt's parser, which answers 400.
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildGeneNameQuery(geneName: string, organismId?: number): string;
|
|
21
|
+
export interface UniProtSearchResult {
|
|
22
|
+
entries: UniProtEntry[];
|
|
23
|
+
attemptedCount: number;
|
|
24
|
+
failedCount: number;
|
|
25
|
+
}
|
|
26
|
+
export declare function searchUniProtEntries({ recognizedIds, geneId, geneName, organismId, }: {
|
|
27
|
+
recognizedIds?: string[];
|
|
28
|
+
geneId?: string;
|
|
29
|
+
geneName?: string;
|
|
30
|
+
/** NCBI taxon id; undefined searches every species */
|
|
31
|
+
organismId?: number;
|
|
32
|
+
}, opts?: FetchOptions): Promise<UniProtSearchResult>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildGeneNameQuery = buildGeneNameQuery;
|
|
4
|
+
exports.searchUniProtEntries = searchUniProtEntries;
|
|
5
|
+
const fetchUtils_ts_1 = require("./fetchUtils.js");
|
|
6
|
+
const dbIds_ts_1 = require("./dbIds.js");
|
|
7
|
+
const UNIPROT_FIELDS = 'accession,id,gene_names,organism_name,protein_name,reviewed';
|
|
8
|
+
function mapApiResultToEntry(result) {
|
|
9
|
+
return {
|
|
10
|
+
accession: result.primaryAccession,
|
|
11
|
+
id: result.uniProtkbId,
|
|
12
|
+
geneName: result.genes?.[0]?.geneName?.value,
|
|
13
|
+
organismName: result.organism?.commonName ?? result.organism?.scientificName,
|
|
14
|
+
proteinName: result.proteinDescription?.recommendedName?.fullName?.value,
|
|
15
|
+
isReviewed: result.entryType === 'UniProtKB reviewed (Swiss-Prot)',
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async function searchUniProt(query, size, opts) {
|
|
19
|
+
const url = `https://rest.uniprot.org/uniprotkb/search?query=${encodeURIComponent(query)}&fields=${UNIPROT_FIELDS}&size=${size}`;
|
|
20
|
+
const data = await (0, fetchUtils_ts_1.jsonfetch)(url, opts);
|
|
21
|
+
return data.results.map(mapApiResultToEntry);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* `gene_exact` rather than `gene`, which also matches synonyms and returns
|
|
25
|
+
* paralogs. Without a taxon the query runs across every species, so a mouse
|
|
26
|
+
* assembly whose tracks carry no taxId lists mouse beside human instead of
|
|
27
|
+
* silently answering with the human entry.
|
|
28
|
+
*
|
|
29
|
+
* The symbol is quoted because a bare one containing `:` — an HLA allele, a
|
|
30
|
+
* name straight out of an annotation file — reads as another field to
|
|
31
|
+
* UniProt's parser, which answers 400.
|
|
32
|
+
*/
|
|
33
|
+
function buildGeneNameQuery(geneName, organismId) {
|
|
34
|
+
return [
|
|
35
|
+
`gene_exact:"${geneName.replaceAll('"', '\\"')}"`,
|
|
36
|
+
organismId ? `organism_id:${organismId}` : undefined,
|
|
37
|
+
'reviewed:true',
|
|
38
|
+
]
|
|
39
|
+
.filter(s => s !== undefined)
|
|
40
|
+
.join(' AND ');
|
|
41
|
+
}
|
|
42
|
+
async function searchByXref(id, opts) {
|
|
43
|
+
const query = (0, dbIds_ts_1.buildUniProtXrefQuery)(id);
|
|
44
|
+
if (!query) {
|
|
45
|
+
return { entries: [], error: undefined };
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
return { entries: await searchUniProt(query, 10, opts), error: undefined };
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
console.error(`xref search failed for ${id}:`, e);
|
|
52
|
+
return { entries: [], error: e };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function searchByGeneName(geneName, organismId, opts) {
|
|
56
|
+
try {
|
|
57
|
+
const entries = await searchUniProt(buildGeneNameQuery(geneName, organismId), organismId ? 5 : 10, opts);
|
|
58
|
+
return { entries, error: undefined };
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
console.error(`gene name search failed for ${geneName}:`, e);
|
|
62
|
+
return { entries: [], error: e };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function deduplicateEntries(entries) {
|
|
66
|
+
const seen = new Set();
|
|
67
|
+
const result = [];
|
|
68
|
+
for (const entry of entries) {
|
|
69
|
+
if (!seen.has(entry.accession)) {
|
|
70
|
+
seen.add(entry.accession);
|
|
71
|
+
result.push(entry);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
async function searchUniProtEntries({ recognizedIds = [], geneId, geneName, organismId, }, opts) {
|
|
77
|
+
const idsToSearch = new Set(recognizedIds);
|
|
78
|
+
const strippedGeneId = geneId ? (0, dbIds_ts_1.stripTrailingVersion)(geneId) : undefined;
|
|
79
|
+
if (strippedGeneId && (0, dbIds_ts_1.isRecognizedDatabaseId)(strippedGeneId)) {
|
|
80
|
+
idsToSearch.add(strippedGeneId);
|
|
81
|
+
}
|
|
82
|
+
// The gene-name query runs alongside the xrefs rather than after them: it is
|
|
83
|
+
// only consulted when no xref found a reviewed entry, but waiting for that
|
|
84
|
+
// answer before starting it doubled the latency of the commonest case.
|
|
85
|
+
const [xrefResults, geneResult] = await Promise.all([
|
|
86
|
+
Promise.all([...idsToSearch].map(id => searchByXref(id, opts))),
|
|
87
|
+
geneName ? searchByGeneName(geneName, organismId, opts) : undefined,
|
|
88
|
+
]);
|
|
89
|
+
let entries = deduplicateEntries(xrefResults.flatMap(r => r.entries));
|
|
90
|
+
// The gene-name answer is consulted only where no xref found a reviewed
|
|
91
|
+
// entry. Counting it otherwise reported "failed for 1 of 2 identifiers" over
|
|
92
|
+
// a gene that had in fact resolved, off a query nothing was waiting for.
|
|
93
|
+
const consultedGeneResult = geneResult && !entries.some(e => e.isReviewed) ? geneResult : undefined;
|
|
94
|
+
if (consultedGeneResult) {
|
|
95
|
+
entries = deduplicateEntries([...entries, ...consultedGeneResult.entries]);
|
|
96
|
+
}
|
|
97
|
+
const attemptedCount = idsToSearch.size + (consultedGeneResult ? 1 : 0);
|
|
98
|
+
const failedCount = xrefResults.filter(r => r.error !== undefined).length +
|
|
99
|
+
(consultedGeneResult?.error === undefined ? 0 : 1);
|
|
100
|
+
// Every attempt failing is a network problem, not an empty result. Throwing
|
|
101
|
+
// it stops consumers reporting "No UniProt ID found" over a dead connection.
|
|
102
|
+
if (entries.length === 0 &&
|
|
103
|
+
attemptedCount > 0 &&
|
|
104
|
+
attemptedCount === failedCount) {
|
|
105
|
+
throw (consultedGeneResult?.error ??
|
|
106
|
+
xrefResults.find(r => r.error !== undefined)?.error);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
entries: entries.toSorted((a, b) => Number(b.isReviewed) - Number(a.isReviewed)),
|
|
110
|
+
attemptedCount,
|
|
111
|
+
failedCount,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=uniProtSearch.js.map
|