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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Colin Diesh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# p2s_mapper
|
|
2
|
+
|
|
3
|
+
Map a transcript's translation onto a protein structure: the pairwise alignment
|
|
4
|
+
between them, the coordinate maps that alignment implies, which chain of the
|
|
5
|
+
structure is the gene's product, SIFTS residue numbering, and the AlphaFold,
|
|
6
|
+
PDBe and UniProt lookups that find a structure to map in the first place.
|
|
7
|
+
|
|
8
|
+
Extracted from
|
|
9
|
+
[jbrowse-plugin-protein3d](https://github.com/GMOD/jbrowse-plugin-protein3d),
|
|
10
|
+
which is where the measurements behind the scoring rules were made. The package
|
|
11
|
+
has no React, no JBrowse and no Mol\* dependency: a loaded Mol\* model reaches
|
|
12
|
+
it through narrow structural interfaces, and `g2p_mapper` is the only runtime
|
|
13
|
+
dependency.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
npm install p2s_mapper
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Coordinate conventions
|
|
22
|
+
|
|
23
|
+
One residue has up to four numbers, and mixing them is the off-by-one class of
|
|
24
|
+
bug this package exists to prevent. **Every stored or computed coordinate is a
|
|
25
|
+
0-based position**: an index into the ungapped structure sequence or the
|
|
26
|
+
ungapped transcript sequence, which is the one numbering that is dense and
|
|
27
|
+
zero-based whatever the file. `coordinates.ts` brands the three internal spaces
|
|
28
|
+
— structure position, transcript position, alignment column — so the compiler
|
|
29
|
+
rejects mixing them. Two conversions leave that space, and each has one door.
|
|
30
|
+
**Mol\* is addressed by `label_seq_id`**, which is `position + 1` only for a
|
|
31
|
+
file carrying `entity_poly_seq` or SEQRES; a SEQRES-less PDB numbers its
|
|
32
|
+
observed residues by author numbering and leaves holes at unobserved loops, so
|
|
33
|
+
`Entity.seqIds` carries the real ids and every crossing goes through
|
|
34
|
+
`toLabelSeqIds`, `rangeToLabelSeqIds` or `makeLabelSeqIdIndex`. **The user is
|
|
35
|
+
shown `auth_seq_id`**, the depositors' numbering, which is what papers and
|
|
36
|
+
UniProt cite (1TUP position 154 reads 248) and what Mol\*'s own hover shows;
|
|
37
|
+
`Entity.authSeqIds` carries it and it is display-only. UniProt positions are
|
|
38
|
+
1-based, and `pdbUniProtMapping` converts them with SIFTS rather than assuming
|
|
39
|
+
any offset.
|
|
40
|
+
|
|
41
|
+
## Exports
|
|
42
|
+
|
|
43
|
+
### Alignment
|
|
44
|
+
|
|
45
|
+
`runLocalAlignment`, `scoredAlignment`, `needlemanWunsch`, `smithWaterman`,
|
|
46
|
+
`selfScore`, `alignmentTooLarge`, `MAX_ALIGNMENT_CELLS` — Smith-Waterman and
|
|
47
|
+
Needleman-Wunsch over BLOSUM62, with affine gaps and a packed traceback.
|
|
48
|
+
|
|
49
|
+
`PairwiseAlignment`, `transcriptAlignedSeq`, `structureAlignedSeq`,
|
|
50
|
+
`alignmentLength`, `pairwiseAlignmentProblem`,
|
|
51
|
+
`pairwiseAlignmentSequenceProblem`, `mappedStructureIdentity`,
|
|
52
|
+
`unmapStructurePositions` — the two-row alignment, whose **row order is a
|
|
53
|
+
contract**: row 0 is the transcript, row 1 is the structure. Read it through the
|
|
54
|
+
accessors, not by index.
|
|
55
|
+
|
|
56
|
+
`alignmentQuality`, `isLowSimilarity`, `describeAlignmentQuality`,
|
|
57
|
+
`describeTranscriptCoverage`, `describeCoveredRange` — identity over the shorter
|
|
58
|
+
sequence is what separates a real match from a chance local alignment; local
|
|
59
|
+
identity alone does not.
|
|
60
|
+
|
|
61
|
+
`AlignmentAlgorithm`, `coerceAlignmentAlgorithm`, `ALIGNMENT_ALGORITHM_LABELS`.
|
|
62
|
+
|
|
63
|
+
### Coordinates
|
|
64
|
+
|
|
65
|
+
`makeCoordinateMapper`, `CoordinateMapper`, `transcriptRangeToStructureRange`,
|
|
66
|
+
`structurePos`, `transcriptPos`, `alignmentCol` — every conversion built once
|
|
67
|
+
from an alignment, branded by space.
|
|
68
|
+
|
|
69
|
+
`structureSeqVsTranscriptSeqMap`, `structurePositionToAlignmentMap`,
|
|
70
|
+
`transcriptPositionToAlignmentMap`, `invertMap`, `codonGenomeSpan`,
|
|
71
|
+
`stripStopCodon`, `stripAllStopCodons`.
|
|
72
|
+
|
|
73
|
+
### Chain choice
|
|
74
|
+
|
|
75
|
+
`chooseMappedEntity`, `alignTranscriptToEntity`, `explainedFraction`,
|
|
76
|
+
`interactionMatchesMappedEntity` — which polymer entity is the transcript's
|
|
77
|
+
product, ranked by identical residues over the **shorter** of the two sequences.
|
|
78
|
+
A raw match count picks the large partner of every bound peptide, and the
|
|
79
|
+
alignment score does not separate them either.
|
|
80
|
+
|
|
81
|
+
`extractEntities`, `extractStructureSequences`, `entityLabel`, `residueNumber`,
|
|
82
|
+
`residueRangeToPositions`, `fillAuthSeqIds`, `oneLetterSequence`,
|
|
83
|
+
`toLabelSeqIds`, `rangeToLabelSeqIds`, `makeLabelSeqIdIndex` — read a loaded
|
|
84
|
+
Mol\* model through a structural interface. Read `sequence.code`, never
|
|
85
|
+
`sequence.label`: `label` spells MSE, TPO and ACE by component id, so the string
|
|
86
|
+
outgrows `seqId` and every later position addresses the wrong residue.
|
|
87
|
+
|
|
88
|
+
`extractPerResidueConfidence`, `looksLikePlddt` — AlphaFold's pLDDT out of the
|
|
89
|
+
B-factor column, keyed by `label_seq_id` because the hierarchy holds only
|
|
90
|
+
observed residues.
|
|
91
|
+
|
|
92
|
+
`classifyIsoforms`, `selectBestTranscript`, `pickStructureSequence` — rank plain
|
|
93
|
+
`{ id, seq }` isoform records against a chain.
|
|
94
|
+
|
|
95
|
+
### SIFTS
|
|
96
|
+
|
|
97
|
+
`fetchUniProtStructureMappings`, `parseUniProtStructureMappings`,
|
|
98
|
+
`pdbeSiftsUrl`, `chooseUniProtMappingForEntity`, `makeUniProtPositionMap`,
|
|
99
|
+
`identityUniProtPositionMap`, `fusionPartnerPositions` — the authoritative
|
|
100
|
+
UniProt ↔ structure alignment. `fusionPartnerPositions` unmaps the residues
|
|
101
|
+
SIFTS gives another protein on a fusion construct, which local alignment
|
|
102
|
+
otherwise bridges (2RH1 scatters 33 receptor residues onto T4 lysozyme).
|
|
103
|
+
|
|
104
|
+
`toAuthorRange`, `segmentsForAccession` — the author-numbered range a UniProt
|
|
105
|
+
range is cited by. Haemoglobin numbers from the mature protein, so author
|
|
106
|
+
residue 1 is UniProt residue 2.
|
|
107
|
+
|
|
108
|
+
### Structure sources
|
|
109
|
+
|
|
110
|
+
`fetchAlphaFoldModels`, `parseAlphaFoldModels`, `pickAlphaFoldModel` — asked
|
|
111
|
+
rather than derived: a protein past AlphaFold's length cap has no F1 model at
|
|
112
|
+
all, and the model version moves.
|
|
113
|
+
|
|
114
|
+
`fetchExperimentalStructures`, `parseExperimentalStructures` — 3D-Beacons'
|
|
115
|
+
experimental entries, PDBe only, because SASBDB's scattering fits file under the
|
|
116
|
+
same category with near-full coverage and no residue mapping.
|
|
117
|
+
|
|
118
|
+
`pdbeBestStructuresUrl`, `parseBestStructures`, `isPdbId`,
|
|
119
|
+
`pdbeEntryMoleculesUrl`, `parseEntryMolecules`, `searchUniProtEntries`,
|
|
120
|
+
`buildGeneNameQuery`, `isRecognizedDatabaseId`, `buildUniProtXrefQuery`,
|
|
121
|
+
`getDbIdLabel`, `stripTrailingVersion`.
|
|
122
|
+
|
|
123
|
+
Every request takes its `fetch` from the call —
|
|
124
|
+
`{ fetch?: typeof fetch, signal? }`, defaulting to `globalThis.fetch` — so a
|
|
125
|
+
host can hand in an instrumented one and a test can hand in a double.
|
|
126
|
+
|
|
127
|
+
### Urls and formats
|
|
128
|
+
|
|
129
|
+
`getPdbStructureUrl`, `getAlphaFoldStructureUrl`, `resolveStructureUrl`,
|
|
130
|
+
`getPdbIdFromUrl`, `getUniprotIdFromAlphaFoldTarget`,
|
|
131
|
+
`getStructureUrlFromTarget`, `getConfidenceUrlFromTarget`,
|
|
132
|
+
`structureDisplayLabel`, `uniprotGffUrl`, `uniprotFastaUrl`, `uniprotEntryUrl`,
|
|
133
|
+
`rcsbEntryUrl`.
|
|
134
|
+
|
|
135
|
+
`structureFormatFromContent`, `structureFormatFromName`,
|
|
136
|
+
`structureFileExtension`, `isBinaryStructureUrl` — which parser a structure
|
|
137
|
+
needs, sniffed from **content** rather than a filename. Getting it wrong fails
|
|
138
|
+
asymmetrically: PDB read as mmCIF throws, while mmCIF read as PDB succeeds and
|
|
139
|
+
yields a model with thousands of misread atoms and zero polymer entities.
|
|
140
|
+
|
|
141
|
+
`caCoordsToPdb`, `hasValidCaCoords` — a PDB file from Foldseek Cα coordinates.
|
|
142
|
+
|
|
143
|
+
`rawfetch`, `myfetch`, `jsonfetch`, `httpError`, `networkError`, `abortError`,
|
|
144
|
+
`timeout`, `FetchOptions`.
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { PairwiseAlignment } from './mappings.ts';
|
|
2
|
+
export interface AlignmentQuality {
|
|
3
|
+
/** columns carrying a residue on both rows, i.e. mapped positions */
|
|
4
|
+
aligned: number;
|
|
5
|
+
identical: number;
|
|
6
|
+
transcriptLength: number;
|
|
7
|
+
structureLength: number;
|
|
8
|
+
/** identical over aligned; 0 when nothing aligned */
|
|
9
|
+
identity: number;
|
|
10
|
+
/** identical over the shorter of the two sequences: the statistic that
|
|
11
|
+
* separates a real match from a chance local alignment, see below */
|
|
12
|
+
identityOverShorter: number;
|
|
13
|
+
/** aligned over the structure's length */
|
|
14
|
+
structureCoverage: number;
|
|
15
|
+
/** first and last transcript residue the alignment covers, 1-based
|
|
16
|
+
* inclusive; 0 when nothing aligned. A structure is usually a fragment, and
|
|
17
|
+
* which part of the protein it holds is the reader's first question. */
|
|
18
|
+
transcriptStart: number;
|
|
19
|
+
transcriptEnd: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Below either floor the mapped positions are as likely to be chance as
|
|
23
|
+
* homology. Local alignment picks the best-scoring stretch of two sequences,
|
|
24
|
+
* so identity over the aligned columns is inflated whatever the inputs:
|
|
25
|
+
* measured 2026-09-11 on random sequences, Smith-Waterman with BLOSUM62 gave
|
|
26
|
+
* 37% identity over 27 columns at 300 × 300 and 30% over 100 columns at
|
|
27
|
+
* 500 × 150, two thirds of the shorter chain, and p53 against a ribosomal
|
|
28
|
+
* protein (7K00 RPS11) gives 31 identities at a third identity. Neither local
|
|
29
|
+
* identity nor coverage alone separates those from a real match. Identical
|
|
30
|
+
* residues over the shorter sequence does: 0.03, 0.20 and 0.24 for the chance
|
|
31
|
+
* cases against 0.9 or better for any structure of the transcript's protein
|
|
32
|
+
* and about 0.6 for an ortholog.
|
|
33
|
+
*
|
|
34
|
+
* A short alignment has to be nearly perfect instead: a 10-residue decoy with
|
|
35
|
+
* 3 identities sits at the ratio floor, while a real bound peptide (1YCR's 15
|
|
36
|
+
* p53 residues, 4ZZJ's 7) is identical or one off. So under
|
|
37
|
+
* SHORT_ALIGNMENT_RESIDUES identical residues the ratio floor rises to
|
|
38
|
+
* SHORT_ALIGNMENT_IDENTITY, and under MIN_IDENTICAL_RESIDUES nothing passes,
|
|
39
|
+
* since a tetrapeptide is never the gene's product.
|
|
40
|
+
*/
|
|
41
|
+
export declare const LOW_IDENTITY_OVER_SHORTER = 0.3;
|
|
42
|
+
export declare const SHORT_ALIGNMENT_RESIDUES = 20;
|
|
43
|
+
export declare const SHORT_ALIGNMENT_IDENTITY = 0.8;
|
|
44
|
+
export declare const MIN_IDENTICAL_RESIDUES = 5;
|
|
45
|
+
export declare function alignmentQuality(pa: PairwiseAlignment): AlignmentQuality;
|
|
46
|
+
export declare function isLowSimilarity(q: AlignmentQuality): boolean;
|
|
47
|
+
/** One line for the alignment header: "87% identity over 219 of 393
|
|
48
|
+
* structure residues". */
|
|
49
|
+
export declare function describeAlignmentQuality(q: AlignmentQuality): string;
|
|
50
|
+
/**
|
|
51
|
+
* The same counted from the transcript's side, for the always-visible header:
|
|
52
|
+
* how much of the gene's protein this structure speaks for is what decides
|
|
53
|
+
* whether a codon has a residue at all.
|
|
54
|
+
*/
|
|
55
|
+
export declare function describeTranscriptCoverage(q: AlignmentQuality): string;
|
|
56
|
+
/** Which residues of the transcript a fragment holds, or undefined when it
|
|
57
|
+
* covers the whole translation. */
|
|
58
|
+
export declare function describeCoveredRange(q: AlignmentQuality): string | undefined;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MIN_IDENTICAL_RESIDUES = exports.SHORT_ALIGNMENT_IDENTITY = exports.SHORT_ALIGNMENT_RESIDUES = exports.LOW_IDENTITY_OVER_SHORTER = void 0;
|
|
4
|
+
exports.alignmentQuality = alignmentQuality;
|
|
5
|
+
exports.isLowSimilarity = isLowSimilarity;
|
|
6
|
+
exports.describeAlignmentQuality = describeAlignmentQuality;
|
|
7
|
+
exports.describeTranscriptCoverage = describeTranscriptCoverage;
|
|
8
|
+
exports.describeCoveredRange = describeCoveredRange;
|
|
9
|
+
const mappings_ts_1 = require("./mappings.js");
|
|
10
|
+
/**
|
|
11
|
+
* Below either floor the mapped positions are as likely to be chance as
|
|
12
|
+
* homology. Local alignment picks the best-scoring stretch of two sequences,
|
|
13
|
+
* so identity over the aligned columns is inflated whatever the inputs:
|
|
14
|
+
* measured 2026-09-11 on random sequences, Smith-Waterman with BLOSUM62 gave
|
|
15
|
+
* 37% identity over 27 columns at 300 × 300 and 30% over 100 columns at
|
|
16
|
+
* 500 × 150, two thirds of the shorter chain, and p53 against a ribosomal
|
|
17
|
+
* protein (7K00 RPS11) gives 31 identities at a third identity. Neither local
|
|
18
|
+
* identity nor coverage alone separates those from a real match. Identical
|
|
19
|
+
* residues over the shorter sequence does: 0.03, 0.20 and 0.24 for the chance
|
|
20
|
+
* cases against 0.9 or better for any structure of the transcript's protein
|
|
21
|
+
* and about 0.6 for an ortholog.
|
|
22
|
+
*
|
|
23
|
+
* A short alignment has to be nearly perfect instead: a 10-residue decoy with
|
|
24
|
+
* 3 identities sits at the ratio floor, while a real bound peptide (1YCR's 15
|
|
25
|
+
* p53 residues, 4ZZJ's 7) is identical or one off. So under
|
|
26
|
+
* SHORT_ALIGNMENT_RESIDUES identical residues the ratio floor rises to
|
|
27
|
+
* SHORT_ALIGNMENT_IDENTITY, and under MIN_IDENTICAL_RESIDUES nothing passes,
|
|
28
|
+
* since a tetrapeptide is never the gene's product.
|
|
29
|
+
*/
|
|
30
|
+
exports.LOW_IDENTITY_OVER_SHORTER = 0.3;
|
|
31
|
+
exports.SHORT_ALIGNMENT_RESIDUES = 20;
|
|
32
|
+
exports.SHORT_ALIGNMENT_IDENTITY = 0.8;
|
|
33
|
+
exports.MIN_IDENTICAL_RESIDUES = 5;
|
|
34
|
+
function alignmentQuality(pa) {
|
|
35
|
+
const t = (0, mappings_ts_1.transcriptAlignedSeq)(pa);
|
|
36
|
+
const s = (0, mappings_ts_1.structureAlignedSeq)(pa);
|
|
37
|
+
let aligned = 0;
|
|
38
|
+
let identical = 0;
|
|
39
|
+
let transcriptLength = 0;
|
|
40
|
+
let structureLength = 0;
|
|
41
|
+
let transcriptStart = 0;
|
|
42
|
+
let transcriptEnd = 0;
|
|
43
|
+
for (let i = 0; i < (0, mappings_ts_1.alignmentLength)(pa); i++) {
|
|
44
|
+
const a = t[i];
|
|
45
|
+
const b = s[i];
|
|
46
|
+
const ta = a !== '-';
|
|
47
|
+
const sb = b !== '-';
|
|
48
|
+
if (ta) {
|
|
49
|
+
transcriptLength++;
|
|
50
|
+
}
|
|
51
|
+
if (sb) {
|
|
52
|
+
structureLength++;
|
|
53
|
+
}
|
|
54
|
+
if (ta && sb) {
|
|
55
|
+
aligned++;
|
|
56
|
+
transcriptStart = transcriptStart || transcriptLength;
|
|
57
|
+
transcriptEnd = transcriptLength;
|
|
58
|
+
if (a.toUpperCase() === b.toUpperCase()) {
|
|
59
|
+
identical++;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const shorter = Math.min(transcriptLength, structureLength);
|
|
64
|
+
return {
|
|
65
|
+
aligned,
|
|
66
|
+
identical,
|
|
67
|
+
transcriptLength,
|
|
68
|
+
structureLength,
|
|
69
|
+
identity: aligned ? identical / aligned : 0,
|
|
70
|
+
identityOverShorter: shorter ? identical / shorter : 0,
|
|
71
|
+
structureCoverage: structureLength ? aligned / structureLength : 0,
|
|
72
|
+
transcriptStart,
|
|
73
|
+
transcriptEnd,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function isLowSimilarity(q) {
|
|
77
|
+
const floor = q.identical < exports.SHORT_ALIGNMENT_RESIDUES
|
|
78
|
+
? exports.SHORT_ALIGNMENT_IDENTITY
|
|
79
|
+
: exports.LOW_IDENTITY_OVER_SHORTER;
|
|
80
|
+
return q.identical < exports.MIN_IDENTICAL_RESIDUES || q.identityOverShorter < floor;
|
|
81
|
+
}
|
|
82
|
+
/** One line for the alignment header: "87% identity over 219 of 393
|
|
83
|
+
* structure residues". */
|
|
84
|
+
function describeAlignmentQuality(q) {
|
|
85
|
+
if (q.aligned === 0) {
|
|
86
|
+
return 'no residues aligned';
|
|
87
|
+
}
|
|
88
|
+
return `${Math.round(q.identity * 100)}% identity over ${q.aligned} of ${q.structureLength} structure residues`;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The same counted from the transcript's side, for the always-visible header:
|
|
92
|
+
* how much of the gene's protein this structure speaks for is what decides
|
|
93
|
+
* whether a codon has a residue at all.
|
|
94
|
+
*/
|
|
95
|
+
function describeTranscriptCoverage(q) {
|
|
96
|
+
if (q.aligned === 0) {
|
|
97
|
+
return 'no residues aligned';
|
|
98
|
+
}
|
|
99
|
+
return `${Math.round(q.identity * 100)}% identity, ${q.aligned} of ${q.transcriptLength} transcript residues covered`;
|
|
100
|
+
}
|
|
101
|
+
/** Which residues of the transcript a fragment holds, or undefined when it
|
|
102
|
+
* covers the whole translation. */
|
|
103
|
+
function describeCoveredRange(q) {
|
|
104
|
+
return q.aligned === 0 || q.aligned === q.transcriptLength
|
|
105
|
+
? undefined
|
|
106
|
+
: `covers transcript residues ${q.transcriptStart}–${q.transcriptEnd}`;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=alignmentQuality.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alignmentQuality.js","sourceRoot":"","sources":["../src/alignmentQuality.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { FetchOptions } from './fetchUtils.ts';
|
|
2
|
+
export interface AlphaFoldModel {
|
|
3
|
+
/** UniProt accession, with an isoform suffix on an isoform model */
|
|
4
|
+
accession: string;
|
|
5
|
+
url: string;
|
|
6
|
+
confidenceUrl?: string;
|
|
7
|
+
sequence: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function parseAlphaFoldModels(json: unknown): AlphaFoldModel[];
|
|
10
|
+
/**
|
|
11
|
+
* Every model AlphaFold DB has for an accession: the canonical one and any
|
|
12
|
+
* isoform models, each with its file url and sequence. Asked rather than
|
|
13
|
+
* derived from the accession, because a derived `AF-<acc>-F1-model_v6` does
|
|
14
|
+
* not exist past the length cap (dystrophin has fourteen isoform models and no
|
|
15
|
+
* F1) and moves with every model version. An accession AlphaFold has never
|
|
16
|
+
* folded answers 400 or 404, which is no models rather than an error.
|
|
17
|
+
*/
|
|
18
|
+
export declare function fetchAlphaFoldModels(uniprotId: string, opts?: FetchOptions): Promise<AlphaFoldModel[]>;
|
|
19
|
+
/**
|
|
20
|
+
* The model to open for a gene's transcripts: one folded from exactly a
|
|
21
|
+
* transcript's translation, canonical first, so the view maps it as an
|
|
22
|
+
* identity; else the canonical model; else the longest isoform model.
|
|
23
|
+
*
|
|
24
|
+
* Takes any record of sequences, since the structure loader knows one
|
|
25
|
+
* translation rather than a gene's isoforms.
|
|
26
|
+
*/
|
|
27
|
+
export declare function pickAlphaFoldModel(models: AlphaFoldModel[], isoformSequences: Record<string, {
|
|
28
|
+
seq: string;
|
|
29
|
+
}> | undefined): AlphaFoldModel | undefined;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseAlphaFoldModels = parseAlphaFoldModels;
|
|
4
|
+
exports.fetchAlphaFoldModels = fetchAlphaFoldModels;
|
|
5
|
+
exports.pickAlphaFoldModel = pickAlphaFoldModel;
|
|
6
|
+
const fetchUtils_ts_1 = require("./fetchUtils.js");
|
|
7
|
+
const stripStopCodon_ts_1 = require("./stripStopCodon.js");
|
|
8
|
+
function isPredictionEntry(value) {
|
|
9
|
+
return typeof value === 'object' && value !== null;
|
|
10
|
+
}
|
|
11
|
+
function parseAlphaFoldModels(json) {
|
|
12
|
+
return (Array.isArray(json) ? json : []).flatMap(e => isPredictionEntry(e) &&
|
|
13
|
+
typeof e.uniprotAccession === 'string' &&
|
|
14
|
+
typeof e.cifUrl === 'string' &&
|
|
15
|
+
typeof e.sequence === 'string'
|
|
16
|
+
? [
|
|
17
|
+
{
|
|
18
|
+
accession: e.uniprotAccession,
|
|
19
|
+
url: e.cifUrl,
|
|
20
|
+
confidenceUrl: typeof e.plddtDocUrl === 'string' ? e.plddtDocUrl : undefined,
|
|
21
|
+
sequence: e.sequence,
|
|
22
|
+
},
|
|
23
|
+
]
|
|
24
|
+
: []);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Every model AlphaFold DB has for an accession: the canonical one and any
|
|
28
|
+
* isoform models, each with its file url and sequence. Asked rather than
|
|
29
|
+
* derived from the accession, because a derived `AF-<acc>-F1-model_v6` does
|
|
30
|
+
* not exist past the length cap (dystrophin has fourteen isoform models and no
|
|
31
|
+
* F1) and moves with every model version. An accession AlphaFold has never
|
|
32
|
+
* folded answers 400 or 404, which is no models rather than an error.
|
|
33
|
+
*/
|
|
34
|
+
async function fetchAlphaFoldModels(uniprotId, opts) {
|
|
35
|
+
const url = `https://alphafold.ebi.ac.uk/api/prediction/${encodeURIComponent(uniprotId)}`;
|
|
36
|
+
const res = await (0, fetchUtils_ts_1.rawfetch)(url, opts);
|
|
37
|
+
if (res.status === 400 || res.status === 404) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
if (!res.ok) {
|
|
41
|
+
throw await (0, fetchUtils_ts_1.httpError)(res, url);
|
|
42
|
+
}
|
|
43
|
+
return parseAlphaFoldModels(await res.json());
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The model to open for a gene's transcripts: one folded from exactly a
|
|
47
|
+
* transcript's translation, canonical first, so the view maps it as an
|
|
48
|
+
* identity; else the canonical model; else the longest isoform model.
|
|
49
|
+
*
|
|
50
|
+
* Takes any record of sequences, since the structure loader knows one
|
|
51
|
+
* translation rather than a gene's isoforms.
|
|
52
|
+
*/
|
|
53
|
+
function pickAlphaFoldModel(models, isoformSequences) {
|
|
54
|
+
const translations = new Set(Object.values(isoformSequences ?? {}).map(v => (0, stripStopCodon_ts_1.stripStopCodon)(v.seq)));
|
|
55
|
+
const canonicalFirst = [...models].sort((a, b) => Number(a.accession.includes('-')) - Number(b.accession.includes('-')));
|
|
56
|
+
return (canonicalFirst.find(m => translations.has(m.sequence)) ??
|
|
57
|
+
canonicalFirst.find(m => !m.accession.includes('-')) ??
|
|
58
|
+
[...models].sort((a, b) => b.sequence.length - a.sequence.length)[0]);
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=alphaFoldModels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alphaFoldModels.js","sourceRoot":"","sources":["../src/alphaFoldModels.ts"],"names":[],"mappings":";;AAwBA,oDAiBC;AAUD,oDAaC;AAUD,gDAgBC;AA1FD,mDAAqD;AAGrD,2DAAoD;AAiBpD,SAAS,iBAAiB,CAAC,KAAc;IACvC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAA;AACpD,CAAC;AAED,SAAgB,oBAAoB,CAAC,IAAa;IAChD,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CACnD,iBAAiB,CAAC,CAAC,CAAC;QACpB,OAAO,CAAC,CAAC,gBAAgB,KAAK,QAAQ;QACtC,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;QAC5B,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ;QAC5B,CAAC,CAAC;YACE;gBACE,SAAS,EAAE,CAAC,CAAC,gBAAgB;gBAC7B,GAAG,EAAE,CAAC,CAAC,MAAM;gBACb,aAAa,EACX,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;gBAC/D,QAAQ,EAAE,CAAC,CAAC,QAAQ;aACrB;SACF;QACH,CAAC,CAAC,EAAE,CACP,CAAA;AACH,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,oBAAoB,CACxC,SAAiB,EACjB,IAAmB;IAEnB,MAAM,GAAG,GAAG,8CAA8C,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAA;IACzF,MAAM,GAAG,GAAG,MAAM,IAAA,wBAAQ,EAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IACrC,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC7C,OAAO,EAAE,CAAA;IACX,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,MAAM,IAAA,yBAAS,EAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IACjC,CAAC;IACD,OAAO,oBAAoB,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;AAC/C,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,kBAAkB,CAChC,MAAwB,EACxB,gBAA6D;IAE7D,MAAM,YAAY,GAAG,IAAI,GAAG,CAC1B,MAAM,CAAC,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAA,kCAAc,EAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CACtE,CAAA;IACD,MAAM,cAAc,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CACrC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CACxE,CAAA;IACD,OAAO,CACL,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACtD,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACpD,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CACrE,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert Foldseek tCa coordinates and tSeq to PDB format
|
|
3
|
+
* tCa is a comma-separated string of x,y,z triplets
|
|
4
|
+
* tSeq is the amino acid sequence
|
|
5
|
+
*/
|
|
6
|
+
export declare function caCoordsToPdb(tCa: string, tSeq: string, chainId?: string, title?: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Whether a Foldseek hit carries tCa data that can be converted to PDB. A type
|
|
9
|
+
* predicate rather than a boolean so a guarded caller can pass `hit.tCa`/
|
|
10
|
+
* `hit.tSeq` straight to caCoordsToPdb without re-asserting they're defined.
|
|
11
|
+
*/
|
|
12
|
+
export declare function hasValidCaCoords<T extends {
|
|
13
|
+
tCa?: string;
|
|
14
|
+
tSeq?: string;
|
|
15
|
+
}>(hit: T): hit is T & {
|
|
16
|
+
tCa: string;
|
|
17
|
+
tSeq: string;
|
|
18
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.caCoordsToPdb = caCoordsToPdb;
|
|
4
|
+
exports.hasValidCaCoords = hasValidCaCoords;
|
|
5
|
+
// 3-letter amino acid codes
|
|
6
|
+
const AA_3LETTER = {
|
|
7
|
+
A: 'ALA',
|
|
8
|
+
C: 'CYS',
|
|
9
|
+
D: 'ASP',
|
|
10
|
+
E: 'GLU',
|
|
11
|
+
F: 'PHE',
|
|
12
|
+
G: 'GLY',
|
|
13
|
+
H: 'HIS',
|
|
14
|
+
I: 'ILE',
|
|
15
|
+
K: 'LYS',
|
|
16
|
+
L: 'LEU',
|
|
17
|
+
M: 'MET',
|
|
18
|
+
N: 'ASN',
|
|
19
|
+
P: 'PRO',
|
|
20
|
+
Q: 'GLN',
|
|
21
|
+
R: 'ARG',
|
|
22
|
+
S: 'SER',
|
|
23
|
+
T: 'THR',
|
|
24
|
+
V: 'VAL',
|
|
25
|
+
W: 'TRP',
|
|
26
|
+
Y: 'TYR',
|
|
27
|
+
X: 'UNK', // Unknown
|
|
28
|
+
};
|
|
29
|
+
function padLeft(str, len) {
|
|
30
|
+
return str.padStart(len, ' ');
|
|
31
|
+
}
|
|
32
|
+
function padRight(str, len) {
|
|
33
|
+
return str.padEnd(len, ' ');
|
|
34
|
+
}
|
|
35
|
+
function formatCoord(val) {
|
|
36
|
+
return val.toFixed(3).padStart(8, ' ');
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Convert Foldseek tCa coordinates and tSeq to PDB format
|
|
40
|
+
* tCa is a comma-separated string of x,y,z triplets
|
|
41
|
+
* tSeq is the amino acid sequence
|
|
42
|
+
*/
|
|
43
|
+
function caCoordsToPdb(tCa, tSeq, chainId = 'A', title) {
|
|
44
|
+
const coords = tCa.split(',').map(Number);
|
|
45
|
+
const lines = [];
|
|
46
|
+
// Add header
|
|
47
|
+
if (title) {
|
|
48
|
+
lines.push(`TITLE ${title}`);
|
|
49
|
+
}
|
|
50
|
+
lines.push('REMARK 1 Generated from Foldseek Cα coordinates');
|
|
51
|
+
let atomNum = 1;
|
|
52
|
+
let resNum = 1;
|
|
53
|
+
for (let i = 0; i < coords.length - 2; i += 3) {
|
|
54
|
+
const x = coords[i];
|
|
55
|
+
const y = coords[i + 1];
|
|
56
|
+
const z = coords[i + 2];
|
|
57
|
+
const aa = tSeq[resNum - 1] ?? 'X';
|
|
58
|
+
const resName = AA_3LETTER[aa] ?? 'UNK';
|
|
59
|
+
if (x === undefined || y === undefined || z === undefined) {
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
// PDB ATOM record format
|
|
63
|
+
// Columns: 1-6 "ATOM ", 7-11 serial, 13-16 name, 17 altLoc, 18-20 resName,
|
|
64
|
+
// 22 chainID, 23-26 resSeq, 27 iCode, 31-38 x, 39-46 y, 47-54 z,
|
|
65
|
+
// 55-60 occupancy, 61-66 tempFactor, 77-78 element
|
|
66
|
+
const line = 'ATOM ' +
|
|
67
|
+
padLeft(String(atomNum), 5) +
|
|
68
|
+
' CA ' +
|
|
69
|
+
padRight(resName, 3) +
|
|
70
|
+
' ' +
|
|
71
|
+
chainId +
|
|
72
|
+
padLeft(String(resNum), 4) +
|
|
73
|
+
' ' +
|
|
74
|
+
formatCoord(x) +
|
|
75
|
+
formatCoord(y) +
|
|
76
|
+
formatCoord(z) +
|
|
77
|
+
' 1.00 0.00 C';
|
|
78
|
+
lines.push(line);
|
|
79
|
+
atomNum++;
|
|
80
|
+
resNum++;
|
|
81
|
+
}
|
|
82
|
+
lines.push('END');
|
|
83
|
+
return lines.join('\n');
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Whether a Foldseek hit carries tCa data that can be converted to PDB. A type
|
|
87
|
+
* predicate rather than a boolean so a guarded caller can pass `hit.tCa`/
|
|
88
|
+
* `hit.tSeq` straight to caCoordsToPdb without re-asserting they're defined.
|
|
89
|
+
*/
|
|
90
|
+
function hasValidCaCoords(hit) {
|
|
91
|
+
const { tCa, tSeq } = hit;
|
|
92
|
+
// Need at least 3 coordinates (x,y,z for one residue) and a sequence
|
|
93
|
+
return !!tCa && !!tSeq && tCa.split(',').length >= 3;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=caCoordsToPdb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caCoordsToPdb.js","sourceRoot":"","sources":["../src/caCoordsToPdb.ts"],"names":[],"mappings":";;AA0CA,sCAuDC;AAOD,4CAMC;AA9GD,4BAA4B;AAC5B,MAAM,UAAU,GAA2B;IACzC,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,KAAK,EAAE,UAAU;CACrB,CAAA;AAED,SAAS,OAAO,CAAC,GAAW,EAAE,GAAW;IACvC,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AAC/B,CAAC;AAED,SAAS,QAAQ,CAAC,GAAW,EAAE,GAAW;IACxC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AAC7B,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AACxC,CAAC;AAED;;;;GAIG;AACH,SAAgB,aAAa,CAC3B,GAAW,EACX,IAAY,EACZ,OAAO,GAAG,GAAG,EACb,KAAc;IAEd,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACzC,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,aAAa;IACb,IAAI,KAAK,EAAE,CAAC;QACV,KAAK,CAAC,IAAI,CAAC,aAAa,KAAK,EAAE,CAAC,CAAA;IAClC,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAA;IAE/D,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,IAAI,MAAM,GAAG,CAAC,CAAA;IAEd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9C,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;QACnB,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QACvB,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,GAAG,CAAA;QAClC,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,CAAC,IAAI,KAAK,CAAA;QAEvC,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;YAC1D,MAAK;QACP,CAAC;QAED,yBAAyB;QACzB,4EAA4E;QAC5E,iEAAiE;QACjE,mDAAmD;QACnD,MAAM,IAAI,GACR,QAAQ;YACR,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC3B,QAAQ;YACR,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;YACpB,GAAG;YACH,OAAO;YACP,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC1B,MAAM;YACN,WAAW,CAAC,CAAC,CAAC;YACd,WAAW,CAAC,CAAC,CAAC;YACd,WAAW,CAAC,CAAC,CAAC;YACd,0BAA0B,CAAA;QAE5B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAChB,OAAO,EAAE,CAAA;QACT,MAAM,EAAE,CAAA;IACV,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAEjB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC;AAED;;;;GAIG;AACH,SAAgB,gBAAgB,CAC9B,GAAM;IAEN,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,CAAA;IACzB,qEAAqE;IACrE,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAA;AACtD,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { PairwiseAlignment } from './mappings.ts';
|
|
2
|
+
import type { AlignmentAlgorithm } from './types.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Whether a structure interaction (hover/click) should drive genome navigation.
|
|
5
|
+
* Only the transcript's mapped entity may — a hover on any other chain carries
|
|
6
|
+
* that chain's own label_seq_id and would mis-map through the wrong alignment.
|
|
7
|
+
* A structure with no resolved mapping (`mappedEntityId` undefined, e.g. a
|
|
8
|
+
* standalone viewer with no transcript) stays fully interactive.
|
|
9
|
+
*/
|
|
10
|
+
export declare function interactionMatchesMappedEntity(entityId: string, mappedEntityId: string | undefined): boolean;
|
|
11
|
+
export interface EntitySelection extends ScoredAlignment {
|
|
12
|
+
/** index into the candidate array of the entity that is the transcript's */
|
|
13
|
+
index: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ScoredAlignment {
|
|
16
|
+
/** alignment of the transcript against the entity (stop codons stripped on
|
|
17
|
+
* both sides, matching the rest of the mapping pipeline) */
|
|
18
|
+
alignment: PairwiseAlignment;
|
|
19
|
+
/** identical aligned residues */
|
|
20
|
+
matches: number;
|
|
21
|
+
/** identical residues over the shorter sequence, the score that picks the
|
|
22
|
+
* entity; see `explainedFraction` */
|
|
23
|
+
explained: number;
|
|
24
|
+
/** the alignment's BLOSUM62 score, gap penalties included */
|
|
25
|
+
score: number;
|
|
26
|
+
}
|
|
27
|
+
/** A candidate entity: its sequence, and whether it is DNA/RNA, which is never
|
|
28
|
+
* the transcript's product however its letters happen to align. */
|
|
29
|
+
export interface EntityCandidate {
|
|
30
|
+
seq: string;
|
|
31
|
+
nucleicAcid?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export declare function explainedFraction(matches: number, transcriptLength: number, entityLength: number): number;
|
|
34
|
+
/**
|
|
35
|
+
* Align one transcript to one entity, stop codons stripped on both sides. An
|
|
36
|
+
* exact match skips the DP entirely; an oversized pair returns undefined rather
|
|
37
|
+
* than locking up the tab. Used both to score every entity of a structure and
|
|
38
|
+
* to honour a user's explicit chain choice.
|
|
39
|
+
*/
|
|
40
|
+
export declare function alignTranscriptToEntity(transcript: string, entitySeq: string, algorithm: AlignmentAlgorithm): ScoredAlignment | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Pick which polymer entity of a structure corresponds to the transcript.
|
|
43
|
+
*
|
|
44
|
+
* The plugin historically hardcoded entity `[0]`, which silently mis-maps every
|
|
45
|
+
* heteromeric / protein-DNA / processed-peptide structure where the protein of
|
|
46
|
+
* interest is some other chain. Selecting by alignment makes the structure self-
|
|
47
|
+
* describe which entity is the gene's protein: an exact sequence match wins
|
|
48
|
+
* outright, otherwise the entity with the highest identity over the shorter
|
|
49
|
+
* sequence (see `explainedFraction`). Nucleic-acid entities are never
|
|
50
|
+
* candidates.
|
|
51
|
+
*
|
|
52
|
+
* Returns `undefined` only when there is nothing to map (no transcript or no
|
|
53
|
+
* protein entities) — never a silent fallback to the wrong entity.
|
|
54
|
+
*/
|
|
55
|
+
export declare function chooseMappedEntity(transcript: string, entities: readonly (string | EntityCandidate)[], algorithm: AlignmentAlgorithm): EntitySelection | undefined;
|