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,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.interactionMatchesMappedEntity = interactionMatchesMappedEntity;
|
|
4
|
+
exports.explainedFraction = explainedFraction;
|
|
5
|
+
exports.alignTranscriptToEntity = alignTranscriptToEntity;
|
|
6
|
+
exports.chooseMappedEntity = chooseMappedEntity;
|
|
7
|
+
const pairwiseAlignment_ts_1 = require("./pairwiseAlignment.js");
|
|
8
|
+
const stripStopCodon_ts_1 = require("./stripStopCodon.js");
|
|
9
|
+
const mappings_ts_1 = require("./mappings.js");
|
|
10
|
+
/**
|
|
11
|
+
* Whether a structure interaction (hover/click) should drive genome navigation.
|
|
12
|
+
* Only the transcript's mapped entity may — a hover on any other chain carries
|
|
13
|
+
* that chain's own label_seq_id and would mis-map through the wrong alignment.
|
|
14
|
+
* A structure with no resolved mapping (`mappedEntityId` undefined, e.g. a
|
|
15
|
+
* standalone viewer with no transcript) stays fully interactive.
|
|
16
|
+
*/
|
|
17
|
+
function interactionMatchesMappedEntity(entityId, mappedEntityId) {
|
|
18
|
+
return mappedEntityId === undefined || entityId === mappedEntityId;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Identical residues over the shorter of the two sequences: how much of
|
|
22
|
+
* whichever is smaller, transcript or entity, the other reproduces.
|
|
23
|
+
*
|
|
24
|
+
* A raw match count favours whatever chain is longest, and a complex's partner
|
|
25
|
+
* usually is: on 1H26 the 11-residue p53 peptide matches 11 while CDK2 accrues
|
|
26
|
+
* 58 scattered identities, and on 4ZZJ SIRT1 beats the 7-residue p53 peptide
|
|
27
|
+
* 63 to 6. The Smith-Waterman score is no better there (56 vs 58). Dividing
|
|
28
|
+
* by length asks the question the picker actually has, which chain *is* this
|
|
29
|
+
* gene's product: the peptide scores 0.69 and 0.50, the partners 0.19 and
|
|
30
|
+
* 0.17, and across a ribosome's 55 chains no decoy passes 0.29.
|
|
31
|
+
*
|
|
32
|
+
* Dividing by the *entity's* length alone penalised a fusion construct: a
|
|
33
|
+
* 60-residue target fused to a 370-residue carrier scored 0.14, and a random
|
|
34
|
+
* 10-mer decoy chain with 3 identities beat it a third of the time (measured
|
|
35
|
+
* 2026-09-11). Dividing by the shorter sequence leaves every chain shorter
|
|
36
|
+
* than the transcript scored as before and lets a chain that contains the
|
|
37
|
+
* whole transcript score near 1 however long its tag is. The pseudocount
|
|
38
|
+
* keeps a two-residue fragment that happens to match (a tRNA end in 7K00)
|
|
39
|
+
* from scoring 1.0.
|
|
40
|
+
*/
|
|
41
|
+
const EXPLAINED_PSEUDOCOUNT = 5;
|
|
42
|
+
function explainedFraction(matches, transcriptLength, entityLength) {
|
|
43
|
+
return (matches / (Math.min(transcriptLength, entityLength) + EXPLAINED_PSEUDOCOUNT));
|
|
44
|
+
}
|
|
45
|
+
function countMatches(pa) {
|
|
46
|
+
const a = (0, mappings_ts_1.transcriptAlignedSeq)(pa);
|
|
47
|
+
const b = (0, mappings_ts_1.structureAlignedSeq)(pa);
|
|
48
|
+
let matches = 0;
|
|
49
|
+
for (let i = 0; i < a.length; i++) {
|
|
50
|
+
const ca = a[i];
|
|
51
|
+
const cb = b[i];
|
|
52
|
+
if (ca !== '-' && cb !== '-' && ca?.toUpperCase() === cb?.toUpperCase()) {
|
|
53
|
+
matches++;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return matches;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Align one transcript to one entity, stop codons stripped on both sides. An
|
|
60
|
+
* exact match skips the DP entirely; an oversized pair returns undefined rather
|
|
61
|
+
* than locking up the tab. Used both to score every entity of a structure and
|
|
62
|
+
* to honour a user's explicit chain choice.
|
|
63
|
+
*/
|
|
64
|
+
function alignTranscriptToEntity(transcript, entitySeq, algorithm) {
|
|
65
|
+
const t = (0, stripStopCodon_ts_1.stripStopCodon)(transcript);
|
|
66
|
+
const s = (0, stripStopCodon_ts_1.stripStopCodon)(entitySeq);
|
|
67
|
+
if (!t || !s || (0, pairwiseAlignment_ts_1.alignmentTooLarge)(t.length, s.length)) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
if (s === t) {
|
|
71
|
+
return {
|
|
72
|
+
alignment: {
|
|
73
|
+
consensus: '|'.repeat(t.length),
|
|
74
|
+
alns: [
|
|
75
|
+
{ id: 'seq1', seq: t },
|
|
76
|
+
{ id: 'seq2', seq: s },
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
matches: t.length,
|
|
80
|
+
explained: explainedFraction(t.length, t.length, s.length),
|
|
81
|
+
score: (0, pairwiseAlignment_ts_1.selfScore)(t),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const { alignment, score } = (0, pairwiseAlignment_ts_1.scoredAlignment)(t, s, algorithm);
|
|
85
|
+
const matches = countMatches(alignment);
|
|
86
|
+
return {
|
|
87
|
+
alignment,
|
|
88
|
+
matches,
|
|
89
|
+
explained: explainedFraction(matches, t.length, s.length),
|
|
90
|
+
score,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Pick which polymer entity of a structure corresponds to the transcript.
|
|
95
|
+
*
|
|
96
|
+
* The plugin historically hardcoded entity `[0]`, which silently mis-maps every
|
|
97
|
+
* heteromeric / protein-DNA / processed-peptide structure where the protein of
|
|
98
|
+
* interest is some other chain. Selecting by alignment makes the structure self-
|
|
99
|
+
* describe which entity is the gene's protein: an exact sequence match wins
|
|
100
|
+
* outright, otherwise the entity with the highest identity over the shorter
|
|
101
|
+
* sequence (see `explainedFraction`). Nucleic-acid entities are never
|
|
102
|
+
* candidates.
|
|
103
|
+
*
|
|
104
|
+
* Returns `undefined` only when there is nothing to map (no transcript or no
|
|
105
|
+
* protein entities) — never a silent fallback to the wrong entity.
|
|
106
|
+
*/
|
|
107
|
+
function chooseMappedEntity(transcript, entities, algorithm) {
|
|
108
|
+
const t = (0, stripStopCodon_ts_1.stripStopCodon)(transcript);
|
|
109
|
+
if (!t) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
const candidates = entities.map(e => typeof e === 'string'
|
|
113
|
+
? { seq: (0, stripStopCodon_ts_1.stripStopCodon)(e) }
|
|
114
|
+
: { ...e, seq: (0, stripStopCodon_ts_1.stripStopCodon)(e.seq) });
|
|
115
|
+
const exactIndex = candidates.findIndex(c => !c.nucleicAcid && c.seq.length > 0 && c.seq === t);
|
|
116
|
+
if (exactIndex !== -1) {
|
|
117
|
+
const exact = alignTranscriptToEntity(t, candidates[exactIndex].seq, algorithm);
|
|
118
|
+
return { index: exactIndex, ...exact };
|
|
119
|
+
}
|
|
120
|
+
// Each alignment is an O(len(t) * len(s)) main-thread DP, so a complex with
|
|
121
|
+
// many chains pays it once per chain. Homomers and repeated chains share a
|
|
122
|
+
// sequence, so align each distinct one once and reuse the result.
|
|
123
|
+
const bySeq = new Map();
|
|
124
|
+
let best;
|
|
125
|
+
for (let index = 0; index < candidates.length; index++) {
|
|
126
|
+
const { seq, nucleicAcid } = candidates[index];
|
|
127
|
+
if (nucleicAcid) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (!bySeq.has(seq)) {
|
|
131
|
+
bySeq.set(seq, alignTranscriptToEntity(t, seq, algorithm));
|
|
132
|
+
}
|
|
133
|
+
const scored = bySeq.get(seq);
|
|
134
|
+
if (scored && (!best || scored.explained > best.explained)) {
|
|
135
|
+
best = { index, ...scored };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return best;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=chooseMappedEntity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chooseMappedEntity.js","sourceRoot":"","sources":["../src/chooseMappedEntity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { PairwiseAlignment } from './mappings.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Branded coordinate spaces. Every position in this plugin is a bare `number`,
|
|
4
|
+
* but the *meaning* differs: a structure residue index is not interchangeable
|
|
5
|
+
* with a transcript residue index or an alignment column, and mixing them is
|
|
6
|
+
* the root cause of the off-by-one class of bugs. Branding makes the compiler
|
|
7
|
+
* reject cross-space mixing; construct values with the helpers below at the
|
|
8
|
+
* boundaries where raw numbers enter (molstar picks, hover events, features).
|
|
9
|
+
*
|
|
10
|
+
* Conventions: all are 0-based. StructurePos indexes the ungapped structure
|
|
11
|
+
* sequence; TranscriptPos the ungapped transcript sequence; AlignmentCol a
|
|
12
|
+
* column of the pairwise alignment (gaps included).
|
|
13
|
+
*/
|
|
14
|
+
type Branded<B extends string> = number & {
|
|
15
|
+
readonly __brand: B;
|
|
16
|
+
};
|
|
17
|
+
export type StructurePos = Branded<'StructurePos'>;
|
|
18
|
+
export type TranscriptPos = Branded<'TranscriptPos'>;
|
|
19
|
+
export type AlignmentCol = Branded<'AlignmentCol'>;
|
|
20
|
+
export declare const structurePos: (n: number) => StructurePos;
|
|
21
|
+
export declare const transcriptPos: (n: number) => TranscriptPos;
|
|
22
|
+
export declare const alignmentCol: (n: number) => AlignmentCol;
|
|
23
|
+
export interface CoordinateMaps {
|
|
24
|
+
structureSeqToTranscriptSeqPosition: Record<number, number>;
|
|
25
|
+
transcriptSeqToStructureSeqPosition: Record<number, number>;
|
|
26
|
+
structurePositionToAlignmentMap: Record<number, number>;
|
|
27
|
+
transcriptPositionToAlignmentMap: Record<number, number>;
|
|
28
|
+
alignmentToStructurePosition: Record<number, number>;
|
|
29
|
+
alignmentToTranscriptPosition: Record<number, number>;
|
|
30
|
+
}
|
|
31
|
+
export interface CoordinateMapper {
|
|
32
|
+
structureToTranscript: (p: StructurePos) => TranscriptPos | undefined;
|
|
33
|
+
transcriptToStructure: (p: TranscriptPos) => StructurePos | undefined;
|
|
34
|
+
structureToAlignment: (p: StructurePos) => AlignmentCol | undefined;
|
|
35
|
+
alignmentToStructure: (c: AlignmentCol) => StructurePos | undefined;
|
|
36
|
+
transcriptToAlignment: (p: TranscriptPos) => AlignmentCol | undefined;
|
|
37
|
+
alignmentToTranscript: (c: AlignmentCol) => TranscriptPos | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Raw lookup tables, for consumers that iterate the whole map rather than
|
|
40
|
+
* convert a single position (e.g. per-residue track rendering, selection of
|
|
41
|
+
* all aligned residues). Plain numbers — prefer the typed methods above for
|
|
42
|
+
* point conversions.
|
|
43
|
+
*/
|
|
44
|
+
readonly maps: CoordinateMaps;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Builds every coordinate conversion once from a pairwise alignment, replacing
|
|
48
|
+
* the scatter of per-getter map builds + repeated invertMap calls. The branded
|
|
49
|
+
* methods are the preferred API; `maps` stays unbranded for whole-map consumers.
|
|
50
|
+
*/
|
|
51
|
+
export declare function makeCoordinateMapper(pairwiseAlignment: PairwiseAlignment): CoordinateMapper;
|
|
52
|
+
/**
|
|
53
|
+
* The 0-based half-open structure-position range covering an inclusive range
|
|
54
|
+
* of 1-based transcript residues, the numbering a UniProt feature or a domain
|
|
55
|
+
* map uses. Residues the structure lacks are skipped, so a range that runs
|
|
56
|
+
* past a fragment's end clamps to the last modeled residue; undefined when none
|
|
57
|
+
* of the range aligns to the structure.
|
|
58
|
+
*/
|
|
59
|
+
export declare function transcriptRangeToStructureRange(mapper: CoordinateMapper, range: {
|
|
60
|
+
start: number;
|
|
61
|
+
end: number;
|
|
62
|
+
}): {
|
|
63
|
+
start: number;
|
|
64
|
+
end: number;
|
|
65
|
+
} | undefined;
|
|
66
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.alignmentCol = exports.transcriptPos = exports.structurePos = void 0;
|
|
4
|
+
exports.makeCoordinateMapper = makeCoordinateMapper;
|
|
5
|
+
exports.transcriptRangeToStructureRange = transcriptRangeToStructureRange;
|
|
6
|
+
const mappings_ts_1 = require("./mappings.js");
|
|
7
|
+
const invertMap_ts_1 = require("./invertMap.js");
|
|
8
|
+
const structurePos = (n) => n;
|
|
9
|
+
exports.structurePos = structurePos;
|
|
10
|
+
const transcriptPos = (n) => n;
|
|
11
|
+
exports.transcriptPos = transcriptPos;
|
|
12
|
+
const alignmentCol = (n) => n;
|
|
13
|
+
exports.alignmentCol = alignmentCol;
|
|
14
|
+
/**
|
|
15
|
+
* Builds every coordinate conversion once from a pairwise alignment, replacing
|
|
16
|
+
* the scatter of per-getter map builds + repeated invertMap calls. The branded
|
|
17
|
+
* methods are the preferred API; `maps` stays unbranded for whole-map consumers.
|
|
18
|
+
*/
|
|
19
|
+
function makeCoordinateMapper(pairwiseAlignment) {
|
|
20
|
+
const { structureSeqToTranscriptSeqPosition, transcriptSeqToStructureSeqPosition, } = (0, mappings_ts_1.structureSeqVsTranscriptSeqMap)(pairwiseAlignment);
|
|
21
|
+
const s2aln = (0, mappings_ts_1.structurePositionToAlignmentMap)(pairwiseAlignment);
|
|
22
|
+
const t2aln = (0, mappings_ts_1.transcriptPositionToAlignmentMap)(pairwiseAlignment);
|
|
23
|
+
const aln2s = (0, invertMap_ts_1.invertMap)(s2aln);
|
|
24
|
+
const aln2t = (0, invertMap_ts_1.invertMap)(t2aln);
|
|
25
|
+
// The `as` casts below are the single brand-crossing point: indexing a
|
|
26
|
+
// Record<number,number> yields a plain number that we re-brand to its output
|
|
27
|
+
// space. Isolated here so the rest of the codebase stays brand-clean.
|
|
28
|
+
return {
|
|
29
|
+
structureToTranscript: p => structureSeqToTranscriptSeqPosition[p],
|
|
30
|
+
transcriptToStructure: p => transcriptSeqToStructureSeqPosition[p],
|
|
31
|
+
structureToAlignment: p => s2aln[p],
|
|
32
|
+
alignmentToStructure: c => aln2s[c],
|
|
33
|
+
transcriptToAlignment: p => t2aln[p],
|
|
34
|
+
alignmentToTranscript: c => aln2t[c],
|
|
35
|
+
maps: {
|
|
36
|
+
structureSeqToTranscriptSeqPosition,
|
|
37
|
+
transcriptSeqToStructureSeqPosition,
|
|
38
|
+
structurePositionToAlignmentMap: s2aln,
|
|
39
|
+
transcriptPositionToAlignmentMap: t2aln,
|
|
40
|
+
alignmentToStructurePosition: aln2s,
|
|
41
|
+
alignmentToTranscriptPosition: aln2t,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The 0-based half-open structure-position range covering an inclusive range
|
|
47
|
+
* of 1-based transcript residues, the numbering a UniProt feature or a domain
|
|
48
|
+
* map uses. Residues the structure lacks are skipped, so a range that runs
|
|
49
|
+
* past a fragment's end clamps to the last modeled residue; undefined when none
|
|
50
|
+
* of the range aligns to the structure.
|
|
51
|
+
*/
|
|
52
|
+
function transcriptRangeToStructureRange(mapper, range) {
|
|
53
|
+
let first;
|
|
54
|
+
let last;
|
|
55
|
+
for (let residue = range.start; residue <= range.end; residue++) {
|
|
56
|
+
const pos = mapper.transcriptToStructure((0, exports.transcriptPos)(residue - 1));
|
|
57
|
+
if (pos !== undefined) {
|
|
58
|
+
first = first === undefined ? pos : Math.min(first, pos);
|
|
59
|
+
last = last === undefined ? pos : Math.max(last, pos);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return first === undefined || last === undefined
|
|
63
|
+
? undefined
|
|
64
|
+
: { start: first, end: last + 1 };
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=coordinates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinates.js","sourceRoot":"","sources":["../src/coordinates.ts"],"names":[],"mappings":";;;AA4DA,oDAiCC;AASD,0EAgBC;AAtHD,+CAIsB;AACtB,iDAA0C;AAqBnC,MAAM,YAAY,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAiB,CAAA;AAA/C,QAAA,YAAY,gBAAmC;AACrD,MAAM,aAAa,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAkB,CAAA;AAAjD,QAAA,aAAa,iBAAoC;AACvD,MAAM,YAAY,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAiB,CAAA;AAA/C,QAAA,YAAY,gBAAmC;AA2B5D;;;;GAIG;AACH,SAAgB,oBAAoB,CAClC,iBAAoC;IAEpC,MAAM,EACJ,mCAAmC,EACnC,mCAAmC,GACpC,GAAG,IAAA,4CAA8B,EAAC,iBAAiB,CAAC,CAAA;IACrD,MAAM,KAAK,GAAG,IAAA,6CAA+B,EAAC,iBAAiB,CAAC,CAAA;IAChE,MAAM,KAAK,GAAG,IAAA,8CAAgC,EAAC,iBAAiB,CAAC,CAAA;IACjE,MAAM,KAAK,GAAG,IAAA,wBAAS,EAAC,KAAK,CAAC,CAAA;IAC9B,MAAM,KAAK,GAAG,IAAA,wBAAS,EAAC,KAAK,CAAC,CAAA;IAE9B,uEAAuE;IACvE,6EAA6E;IAC7E,sEAAsE;IACtE,OAAO;QACL,qBAAqB,EAAE,CAAC,CAAC,EAAE,CACzB,mCAAmC,CAAC,CAAC,CAA8B;QACrE,qBAAqB,EAAE,CAAC,CAAC,EAAE,CACzB,mCAAmC,CAAC,CAAC,CAA6B;QACpE,oBAAoB,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAA6B;QAC/D,oBAAoB,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAA6B;QAC/D,qBAAqB,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAA6B;QAChE,qBAAqB,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAA8B;QACjE,IAAI,EAAE;YACJ,mCAAmC;YACnC,mCAAmC;YACnC,+BAA+B,EAAE,KAAK;YACtC,gCAAgC,EAAE,KAAK;YACvC,4BAA4B,EAAE,KAAK;YACnC,6BAA6B,EAAE,KAAK;SACrC;KACF,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,+BAA+B,CAC7C,MAAwB,EACxB,KAAqC;IAErC,IAAI,KAAyB,CAAA;IAC7B,IAAI,IAAwB,CAAA;IAC5B,KAAK,IAAI,OAAO,GAAG,KAAK,CAAC,KAAK,EAAE,OAAO,IAAI,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC;QAChE,MAAM,GAAG,GAAG,MAAM,CAAC,qBAAqB,CAAC,IAAA,qBAAa,EAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAA;QACpE,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,KAAK,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;YACxD,IAAI,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;QACvD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS;QAC9C,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,GAAG,CAAC,EAAE,CAAA;AACrC,CAAC"}
|
package/dist/dbIds.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type DbType = 'ensembl' | 'refseq' | 'ccds' | 'hgnc';
|
|
2
|
+
export declare function matchDbIdPattern(id: string): {
|
|
3
|
+
db: DbType;
|
|
4
|
+
pattern: RegExp;
|
|
5
|
+
label: string;
|
|
6
|
+
} | undefined;
|
|
7
|
+
/** Whether an ID is a database identifier UniProt can map. */
|
|
8
|
+
export declare function isRecognizedDatabaseId(id: string): boolean;
|
|
9
|
+
/** Human-readable label for an ID, e.g. "ENST00000123 (Ensembl transcript)".
|
|
10
|
+
* Unrecognized IDs are returned unadorned. */
|
|
11
|
+
export declare function getDbIdLabel(id: string): string;
|
|
12
|
+
/** The UniProt xref query fragment for a recognized ID, e.g.
|
|
13
|
+
* "xref:ensembl-ENST00000123". HGNC strips its redundant "HGNC:" prefix. */
|
|
14
|
+
export declare function buildUniProtXrefQuery(id: string): string | undefined;
|
|
15
|
+
export declare function stripTrailingVersion(s?: string): string | undefined;
|
|
16
|
+
export {};
|
package/dist/dbIds.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchDbIdPattern = matchDbIdPattern;
|
|
4
|
+
exports.isRecognizedDatabaseId = isRecognizedDatabaseId;
|
|
5
|
+
exports.getDbIdLabel = getDbIdLabel;
|
|
6
|
+
exports.buildUniProtXrefQuery = buildUniProtXrefQuery;
|
|
7
|
+
exports.stripTrailingVersion = stripTrailingVersion;
|
|
8
|
+
const DB_ID_PATTERNS = [
|
|
9
|
+
{ db: 'ensembl', pattern: /^ENS[A-Z]*G\d+/i, label: 'Ensembl gene' },
|
|
10
|
+
{ db: 'ensembl', pattern: /^ENS[A-Z]*T\d+/i, label: 'Ensembl transcript' },
|
|
11
|
+
{ db: 'ensembl', pattern: /^ENS[A-Z]*P\d+/i, label: 'Ensembl protein' },
|
|
12
|
+
{ db: 'refseq', pattern: /^[NX]M_\d+/i, label: 'RefSeq mRNA' },
|
|
13
|
+
{ db: 'refseq', pattern: /^[NX]R_\d+/i, label: 'RefSeq ncRNA' },
|
|
14
|
+
{ db: 'refseq', pattern: /^[NX]P_\d+/i, label: 'RefSeq protein' },
|
|
15
|
+
{ db: 'ccds', pattern: /^CCDS\d+/i, label: 'CCDS' },
|
|
16
|
+
{ db: 'hgnc', pattern: /^HGNC:\d+/i, label: 'HGNC' },
|
|
17
|
+
];
|
|
18
|
+
function matchDbIdPattern(id) {
|
|
19
|
+
return DB_ID_PATTERNS.find(p => p.pattern.test(id));
|
|
20
|
+
}
|
|
21
|
+
/** Whether an ID is a database identifier UniProt can map. */
|
|
22
|
+
function isRecognizedDatabaseId(id) {
|
|
23
|
+
return matchDbIdPattern(id) !== undefined;
|
|
24
|
+
}
|
|
25
|
+
/** Human-readable label for an ID, e.g. "ENST00000123 (Ensembl transcript)".
|
|
26
|
+
* Unrecognized IDs are returned unadorned. */
|
|
27
|
+
function getDbIdLabel(id) {
|
|
28
|
+
const match = matchDbIdPattern(id);
|
|
29
|
+
return match ? `${id} (${match.label})` : id;
|
|
30
|
+
}
|
|
31
|
+
/** The UniProt xref query fragment for a recognized ID, e.g.
|
|
32
|
+
* "xref:ensembl-ENST00000123". HGNC strips its redundant "HGNC:" prefix. */
|
|
33
|
+
function buildUniProtXrefQuery(id) {
|
|
34
|
+
const match = matchDbIdPattern(id);
|
|
35
|
+
return match
|
|
36
|
+
? `xref:${match.db}-${match.db === 'hgnc' ? id.replace('HGNC:', '') : id}`
|
|
37
|
+
: undefined;
|
|
38
|
+
}
|
|
39
|
+
function stripTrailingVersion(s) {
|
|
40
|
+
return s?.replace(/\.[^./]+$/, '');
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=dbIds.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dbIds.js","sourceRoot":"","sources":["../src/dbIds.ts"],"names":[],"mappings":";;AAkBA,4CAEC;AAGD,wDAEC;AAID,oCAGC;AAID,sDAKC;AAED,oDAEC;AAtCD,MAAM,cAAc,GAAqD;IACvE,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,cAAc,EAAE;IACpE,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,oBAAoB,EAAE;IAC1E,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,EAAE;IACvE,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,EAAE;IAC9D,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,cAAc,EAAE;IAC/D,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,gBAAgB,EAAE;IACjE,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE;IACnD,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE;CACrD,CAAA;AAED,SAAgB,gBAAgB,CAAC,EAAU;IACzC,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;AACrD,CAAC;AAED,8DAA8D;AAC9D,SAAgB,sBAAsB,CAAC,EAAU;IAC/C,OAAO,gBAAgB,CAAC,EAAE,CAAC,KAAK,SAAS,CAAA;AAC3C,CAAC;AAED;8CAC8C;AAC9C,SAAgB,YAAY,CAAC,EAAU;IACrC,MAAM,KAAK,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAA;IAClC,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;AAC9C,CAAC;AAED;4EAC4E;AAC5E,SAAgB,qBAAqB,CAAC,EAAU;IAC9C,MAAM,KAAK,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAA;IAClC,OAAO,KAAK;QACV,CAAC,CAAC,QAAQ,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;QAC1E,CAAC,CAAC,SAAS,CAAA;AACf,CAAC;AAED,SAAgB,oBAAoB,CAAC,CAAU;IAC7C,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;AACpC,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
interface ConfidenceModel {
|
|
2
|
+
obj?: {
|
|
3
|
+
data: {
|
|
4
|
+
atomicConformation: {
|
|
5
|
+
B_iso_or_equiv: {
|
|
6
|
+
value: (row: number) => number;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
atomicHierarchy: {
|
|
10
|
+
residueAtomSegments: {
|
|
11
|
+
offsets: ArrayLike<number>;
|
|
12
|
+
count: number;
|
|
13
|
+
};
|
|
14
|
+
chainAtomSegments: {
|
|
15
|
+
index: ArrayLike<number>;
|
|
16
|
+
};
|
|
17
|
+
chains: {
|
|
18
|
+
label_entity_id: {
|
|
19
|
+
value: (row: number) => string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
residues: {
|
|
23
|
+
label_seq_id: {
|
|
24
|
+
value: (row: number) => number;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Per-residue B-factor / pLDDT of one polymer entity, keyed by molstar's
|
|
33
|
+
* `label_seq_id`. Keyed by id rather than by residue order because the atomic
|
|
34
|
+
* hierarchy only holds *observed* residues: a chain with an unmodeled loop, or
|
|
35
|
+
* a chain that is not the first in the file, would otherwise plot every value
|
|
36
|
+
* against the wrong SEQRES position. Convert through `Entity.seqIds`.
|
|
37
|
+
*/
|
|
38
|
+
export interface EntityConfidence {
|
|
39
|
+
entityId: string;
|
|
40
|
+
byLabelSeqId: Map<number, number>;
|
|
41
|
+
}
|
|
42
|
+
export declare function extractPerResidueConfidence(model: ConfidenceModel): EntityConfidence[] | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* AlphaFold-style pLDDT lives in [0, 100] and varies across residues. A
|
|
45
|
+
* constant column (common when a PDB has no B-factors) or out-of-range values
|
|
46
|
+
* indicate the track wouldn't be meaningful as confidence.
|
|
47
|
+
*/
|
|
48
|
+
export declare function looksLikePlddt(values: number[] | undefined): values is number[];
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractPerResidueConfidence = extractPerResidueConfidence;
|
|
4
|
+
exports.looksLikePlddt = looksLikePlddt;
|
|
5
|
+
function extractPerResidueConfidence(model) {
|
|
6
|
+
const data = model.obj?.data;
|
|
7
|
+
if (!data) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const { B_iso_or_equiv } = data.atomicConformation;
|
|
11
|
+
const { residueAtomSegments, chainAtomSegments, chains, residues } = data.atomicHierarchy;
|
|
12
|
+
const byEntity = new Map();
|
|
13
|
+
for (let residue = 0; residue < residueAtomSegments.count; residue++) {
|
|
14
|
+
const atom = residueAtomSegments.offsets[residue];
|
|
15
|
+
const entityId = chains.label_entity_id.value(chainAtomSegments.index[atom]);
|
|
16
|
+
let values = byEntity.get(entityId);
|
|
17
|
+
if (!values) {
|
|
18
|
+
values = new Map();
|
|
19
|
+
byEntity.set(entityId, values);
|
|
20
|
+
}
|
|
21
|
+
const seqId = residues.label_seq_id.value(residue);
|
|
22
|
+
if (!values.has(seqId)) {
|
|
23
|
+
values.set(seqId, B_iso_or_equiv.value(atom));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return [...byEntity].map(([entityId, byLabelSeqId]) => ({
|
|
27
|
+
entityId,
|
|
28
|
+
byLabelSeqId,
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* AlphaFold-style pLDDT lives in [0, 100] and varies across residues. A
|
|
33
|
+
* constant column (common when a PDB has no B-factors) or out-of-range values
|
|
34
|
+
* indicate the track wouldn't be meaningful as confidence.
|
|
35
|
+
*/
|
|
36
|
+
function looksLikePlddt(values) {
|
|
37
|
+
return (!!values &&
|
|
38
|
+
values.length > 1 &&
|
|
39
|
+
values.every(v => v >= 0 && v <= 100) &&
|
|
40
|
+
new Set(values).size > 1);
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=extractPerResidueConfidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractPerResidueConfidence.js","sourceRoot":"","sources":["../src/extractPerResidueConfidence.ts"],"names":[],"mappings":";;AAyCA,kEA8BC;AAOD,wCASC;AA9CD,SAAgB,2BAA2B,CACzC,KAAsB;IAEtB,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,CAAA;IAC5B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,kBAAkB,CAAA;IAClD,MAAM,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,EAAE,QAAQ,EAAE,GAChE,IAAI,CAAC,eAAe,CAAA;IACtB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA+B,CAAA;IACvD,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC;QACrE,MAAM,IAAI,GAAG,mBAAmB,CAAC,OAAO,CAAC,OAAO,CAAE,CAAA;QAClD,MAAM,QAAQ,GAAG,MAAM,CAAC,eAAe,CAAC,KAAK,CAC3C,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAE,CAC/B,CAAA;QACD,IAAI,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACnC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,GAAG,IAAI,GAAG,EAAE,CAAA;YAClB,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QAChC,CAAC;QACD,MAAM,KAAK,GAAG,QAAQ,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAClD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;QAC/C,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC;QACtD,QAAQ;QACR,YAAY;KACb,CAAC,CAAC,CAAA;AACL,CAAC;AAED;;;;GAIG;AACH,SAAgB,cAAc,CAC5B,MAA4B;IAE5B,OAAO,CACL,CAAC,CAAC,MAAM;QACR,MAAM,CAAC,MAAM,GAAG,CAAC;QACjB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC;QACrC,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,GAAG,CAAC,CACzB,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/** A polymer entity of a loaded structure: its mmCIF entity id, its one-letter
|
|
2
|
+
* sequence, and the molstar `label_seq_id` of each of those residues.
|
|
3
|
+
*
|
|
4
|
+
* The entity id is what lets every downstream step talk about "the gene's
|
|
5
|
+
* protein" by identity instead of by the fragile entity-[0] position.
|
|
6
|
+
*
|
|
7
|
+
* `seqIds` is what lets it talk about a *residue* by identity. Structure
|
|
8
|
+
* positions in this plugin are 0-based indices into `seq`; molstar addresses
|
|
9
|
+
* residues by `label_seq_id`. Those are related by `+1` only when the entity's
|
|
10
|
+
* sequence covers every residue from 1 — true for any mmCIF with an
|
|
11
|
+
* `entity_poly_seq` category (all of RCSB and AlphaFold) and for PDB files
|
|
12
|
+
* carrying SEQRES records, because molstar synthesizes the category from them.
|
|
13
|
+
*
|
|
14
|
+
* A PDB-format file with no SEQRES has no such category, so molstar falls back
|
|
15
|
+
* to `StructureSequence.fromHierarchy` and takes `label_seq_id` straight from
|
|
16
|
+
* the observed residues' author numbering: a chain whose coordinates start at
|
|
17
|
+
* residue 94 yields seqIds 94.., and an unobserved loop leaves a hole. Deriving
|
|
18
|
+
* the id arithmetically there is off by the whole offset, in both directions —
|
|
19
|
+
* every hover reads the wrong residue and every highlight paints the wrong one.
|
|
20
|
+
* So carry the real ids and convert through them.
|
|
21
|
+
*/
|
|
22
|
+
export interface Entity {
|
|
23
|
+
entityId: string;
|
|
24
|
+
seq: string;
|
|
25
|
+
seqIds: number[];
|
|
26
|
+
/** author chain ids carrying this entity, e.g. ['A', 'B'] for a homodimer;
|
|
27
|
+
* what a user recognises from the PDB entry page, where the entity id is
|
|
28
|
+
* molstar's own bookkeeping */
|
|
29
|
+
chains: string[];
|
|
30
|
+
/** DNA, RNA or a hybrid, by molstar's entity subtype. Its one-letter
|
|
31
|
+
* sequence spells amino acids too (A, C, G, T, U), so it has to be kept
|
|
32
|
+
* out of the transcript alignment rather than merely scoring low. */
|
|
33
|
+
nucleicAcid?: boolean;
|
|
34
|
+
/** `auth_seq_id` per position: the numbering the depositors chose, which
|
|
35
|
+
* for an RCSB entry is the one papers and UniProt cite (1TUP's position 154
|
|
36
|
+
* is R248) and what Mol*'s own hover label shows. Display only; every
|
|
37
|
+
* coordinate the plugin computes with stays a 0-based position, and molstar
|
|
38
|
+
* is addressed through `seqIds`. Absent when the model has no atomic
|
|
39
|
+
* hierarchy to read it from. See `residueNumber`. */
|
|
40
|
+
authSeqIds?: number[];
|
|
41
|
+
}
|
|
42
|
+
interface Column<T> {
|
|
43
|
+
rowCount: number;
|
|
44
|
+
value(row: number): T;
|
|
45
|
+
}
|
|
46
|
+
interface StructureModel {
|
|
47
|
+
obj?: {
|
|
48
|
+
data: {
|
|
49
|
+
entities?: {
|
|
50
|
+
subtype: Column<string>;
|
|
51
|
+
getEntityIndex(id: string): number;
|
|
52
|
+
};
|
|
53
|
+
sourceData?: {
|
|
54
|
+
data: unknown;
|
|
55
|
+
};
|
|
56
|
+
sequence: {
|
|
57
|
+
sequences: readonly {
|
|
58
|
+
entityId: string;
|
|
59
|
+
sequence: {
|
|
60
|
+
code: {
|
|
61
|
+
toArray(): ArrayLike<string>;
|
|
62
|
+
};
|
|
63
|
+
seqId: {
|
|
64
|
+
toArray(): ArrayLike<number>;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
}[];
|
|
68
|
+
};
|
|
69
|
+
atomicHierarchy?: {
|
|
70
|
+
chains: {
|
|
71
|
+
label_entity_id: Column<string>;
|
|
72
|
+
auth_asym_id: Column<string>;
|
|
73
|
+
};
|
|
74
|
+
residues?: {
|
|
75
|
+
label_seq_id: Column<number>;
|
|
76
|
+
auth_seq_id: Column<number>;
|
|
77
|
+
};
|
|
78
|
+
residueAtomSegments?: {
|
|
79
|
+
offsets: ArrayLike<number>;
|
|
80
|
+
count: number;
|
|
81
|
+
};
|
|
82
|
+
chainAtomSegments?: {
|
|
83
|
+
index: ArrayLike<number>;
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Author numbering for every SEQRES position, observed or not. A residue with
|
|
91
|
+
* no atoms has no auth_seq_id of its own, so it takes the offset of the nearest
|
|
92
|
+
* observed residue before it (after it, at an unobserved N-terminus): a
|
|
93
|
+
* disordered loop keeps counting the way the paper does. With nothing observed
|
|
94
|
+
* the label numbering stands.
|
|
95
|
+
*/
|
|
96
|
+
export declare function fillAuthSeqIds(seqIds: number[], observed: Map<number, number> | undefined): number[];
|
|
97
|
+
/** The number a residue is called by: author numbering when the file carries
|
|
98
|
+
* it, else molstar's label_seq_id, which is the author numbering already for a
|
|
99
|
+
* SEQRES-less PDB and 1..N for everything else. */
|
|
100
|
+
export declare function residueNumber(entity: Entity | undefined, pos: number): number;
|
|
101
|
+
/**
|
|
102
|
+
* The 0-based half-open position range covering an inclusive range of author
|
|
103
|
+
* residue numbers, the form a paper or a spec names a site by ("R248",
|
|
104
|
+
* "residues 102-292"). Undefined when no residue of the entity carries a number
|
|
105
|
+
* in the range, so a typo selects nothing rather than something else.
|
|
106
|
+
*/
|
|
107
|
+
export declare function residueRangeToPositions(entity: Entity | undefined, range: {
|
|
108
|
+
start: number;
|
|
109
|
+
end: number;
|
|
110
|
+
}): {
|
|
111
|
+
start: number;
|
|
112
|
+
end: number;
|
|
113
|
+
} | undefined;
|
|
114
|
+
/**
|
|
115
|
+
* One letter per position, so `seq[pos]` and `seqIds[pos]` name the same
|
|
116
|
+
* residue. Molstar's `label` column is not that: it spells a residue with no
|
|
117
|
+
* one-letter code by its component id (MSE, TPO, ACE) and an alternate site as
|
|
118
|
+
* "(S|P)", which shifted every later position. `code` writes X for those; the
|
|
119
|
+
* mmCIF canonical sequence then supplies the parent residue (MSE is M) when it
|
|
120
|
+
* has one letter per position, which a chromophore spelled "TYG" does not.
|
|
121
|
+
*/
|
|
122
|
+
export declare function oneLetterSequence(codes: ArrayLike<string>, canonical: string | undefined): string;
|
|
123
|
+
export declare function extractEntities(model: StructureModel): Entity[] | undefined;
|
|
124
|
+
/** A user-facing name for an entity: its chains when known, else its id. */
|
|
125
|
+
export declare function entityLabel(entity: Entity): string;
|
|
126
|
+
/** The protein chains' sequences, for the launch dialog's isoform matching,
|
|
127
|
+
* which has no use for a DNA strand that happens to align. */
|
|
128
|
+
export declare function extractStructureSequences(model: StructureModel): string[] | undefined;
|
|
129
|
+
/**
|
|
130
|
+
* The molstar `label_seq_id`s for a set of 0-based structure positions. Unknown
|
|
131
|
+
* positions are dropped rather than guessed, so an out-of-range position paints
|
|
132
|
+
* nothing instead of painting something wrong.
|
|
133
|
+
*/
|
|
134
|
+
export declare function toLabelSeqIds(entity: Entity | undefined, positions: Iterable<number>): number[];
|
|
135
|
+
/** As toLabelSeqIds, for a half-open [start, end) structure-position range. */
|
|
136
|
+
export declare function rangeToLabelSeqIds(entity: Entity | undefined, range: {
|
|
137
|
+
start: number;
|
|
138
|
+
end: number;
|
|
139
|
+
} | undefined): number[];
|
|
140
|
+
/** Reverse of `seqIds`: molstar's label_seq_id -> 0-based structure position. */
|
|
141
|
+
export declare function makeLabelSeqIdIndex(entity: Entity | undefined): Map<number, number>;
|
|
142
|
+
export {};
|