p2s_mapper 1.4.0 → 2.1.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/README.md +220 -39
- package/dist/alignmentQuality.d.ts +5 -5
- package/dist/alphaFoldModels.d.ts +4 -4
- package/dist/alphaFoldModels.js +4 -4
- package/dist/chooseMappedEntity.d.ts +11 -13
- package/dist/chooseMappedEntity.js +9 -12
- package/dist/chooseMappedEntity.js.map +1 -1
- package/dist/coordinates.d.ts +19 -19
- package/dist/coordinates.js.map +1 -1
- package/dist/dbIds.d.ts +1 -2
- package/dist/extractPerResidueConfidence.d.ts +1 -2
- package/dist/extractStructureSequences.d.ts +26 -34
- package/dist/extractStructureSequences.js +2 -2
- package/dist/extractStructureSequences.js.map +1 -1
- package/dist/fetchUtils.d.ts +7 -7
- package/dist/fetchUtils.js +26 -8
- package/dist/fetchUtils.js.map +1 -1
- package/dist/index.d.ts +1 -3
- package/dist/index.js +2 -4
- package/dist/index.js.map +1 -1
- package/dist/isoformRanking.d.ts +4 -1
- package/dist/mappings.d.ts +9 -12
- package/dist/mappings.js +4 -5
- package/dist/mappings.js.map +1 -1
- package/dist/pairwiseAlignment.d.ts +5 -4
- package/dist/pairwiseAlignment.js +9 -7
- package/dist/pairwiseAlignment.js.map +1 -1
- package/dist/pdbEntry.d.ts +32 -0
- package/dist/pdbEntry.js +182 -0
- package/dist/pdbEntry.js.map +1 -0
- package/dist/pdbUniProtMapping.d.ts +14 -39
- package/dist/pdbUniProtMapping.js +22 -140
- package/dist/pdbUniProtMapping.js.map +1 -1
- package/dist/structureFormat.d.ts +11 -18
- package/dist/structureFormat.js +4 -6
- package/dist/structureFormat.js.map +1 -1
- package/dist/structureSources.d.ts +37 -13
- package/dist/structureSources.js +144 -57
- package/dist/structureSources.js.map +1 -1
- package/dist/structureUrls.d.ts +13 -16
- package/dist/structureUrls.js +25 -23
- package/dist/structureUrls.js.map +1 -1
- package/dist/types.d.ts +2 -5
- package/dist/types.js +2 -5
- package/dist/types.js.map +1 -1
- package/dist/uniProtSearch.js +3 -8
- package/dist/uniProtSearch.js.map +1 -1
- package/esm/alignmentQuality.d.ts +5 -5
- package/esm/alphaFoldModels.d.ts +4 -4
- package/esm/alphaFoldModels.js +4 -4
- package/esm/chooseMappedEntity.d.ts +11 -13
- package/esm/chooseMappedEntity.js +9 -12
- package/esm/chooseMappedEntity.js.map +1 -1
- package/esm/coordinates.d.ts +19 -19
- package/esm/coordinates.js.map +1 -1
- package/esm/dbIds.d.ts +1 -2
- package/esm/extractPerResidueConfidence.d.ts +1 -2
- package/esm/extractStructureSequences.d.ts +26 -34
- package/esm/extractStructureSequences.js +2 -2
- package/esm/extractStructureSequences.js.map +1 -1
- package/esm/fetchUtils.d.ts +7 -7
- package/esm/fetchUtils.js +26 -8
- package/esm/fetchUtils.js.map +1 -1
- package/esm/index.d.ts +1 -3
- package/esm/index.js +2 -4
- package/esm/index.js.map +1 -1
- package/esm/isoformRanking.d.ts +4 -1
- package/esm/mappings.d.ts +9 -12
- package/esm/mappings.js +4 -5
- package/esm/mappings.js.map +1 -1
- package/esm/pairwiseAlignment.d.ts +5 -4
- package/esm/pairwiseAlignment.js +7 -5
- package/esm/pairwiseAlignment.js.map +1 -1
- package/esm/pdbEntry.d.ts +32 -0
- package/esm/pdbEntry.js +174 -0
- package/esm/pdbEntry.js.map +1 -0
- package/esm/pdbUniProtMapping.d.ts +14 -39
- package/esm/pdbUniProtMapping.js +22 -137
- package/esm/pdbUniProtMapping.js.map +1 -1
- package/esm/structureFormat.d.ts +11 -18
- package/esm/structureFormat.js +4 -6
- package/esm/structureFormat.js.map +1 -1
- package/esm/structureSources.d.ts +37 -13
- package/esm/structureSources.js +139 -55
- package/esm/structureSources.js.map +1 -1
- package/esm/structureUrls.d.ts +13 -16
- package/esm/structureUrls.js +23 -23
- package/esm/structureUrls.js.map +1 -1
- package/esm/types.d.ts +2 -5
- package/esm/types.js +2 -5
- package/esm/types.js.map +1 -1
- package/esm/uniProtSearch.js +3 -8
- package/esm/uniProtSearch.js.map +1 -1
- package/package.json +3 -2
- package/src/alignmentQuality.ts +5 -5
- package/src/alphaFoldModels.ts +4 -4
- package/src/chooseMappedEntity.ts +12 -15
- package/src/coordinates.ts +14 -17
- package/src/dbIds.ts +1 -1
- package/src/extractPerResidueConfidence.ts +1 -1
- package/src/extractStructureSequences.ts +26 -33
- package/src/fetchUtils.ts +30 -8
- package/src/index.ts +2 -4
- package/src/isoformRanking.ts +4 -4
- package/src/mappings.ts +9 -12
- package/src/pairwiseAlignment.ts +12 -9
- package/src/pdbEntry.ts +240 -0
- package/src/pdbUniProtMapping.ts +25 -164
- package/src/structureFormat.ts +11 -18
- package/src/structureSources.ts +194 -70
- package/src/structureUrls.ts +25 -23
- package/src/types.ts +2 -5
- package/src/uniProtSearch.ts +3 -8
- package/dist/pdbeBestStructures.d.ts +0 -23
- package/dist/pdbeBestStructures.js +0 -79
- package/dist/pdbeBestStructures.js.map +0 -1
- package/dist/pdbeEntryMolecules.d.ts +0 -7
- package/dist/pdbeEntryMolecules.js +0 -39
- package/dist/pdbeEntryMolecules.js.map +0 -1
- package/dist/siftsNumbering.d.ts +0 -23
- package/dist/siftsNumbering.js +0 -44
- package/dist/siftsNumbering.js.map +0 -1
- package/esm/pdbeBestStructures.d.ts +0 -23
- package/esm/pdbeBestStructures.js +0 -73
- package/esm/pdbeBestStructures.js.map +0 -1
- package/esm/pdbeEntryMolecules.d.ts +0 -7
- package/esm/pdbeEntryMolecules.js +0 -35
- package/esm/pdbeEntryMolecules.js.map +0 -1
- package/esm/siftsNumbering.d.ts +0 -23
- package/esm/siftsNumbering.js +0 -40
- package/esm/siftsNumbering.js.map +0 -1
- package/src/pdbeBestStructures.ts +0 -94
- package/src/pdbeEntryMolecules.ts +0 -37
- package/src/siftsNumbering.ts +0 -61
package/src/structureSources.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
// The experimental structures
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// crystal structure beside a prediction without a PDB search of
|
|
5
|
-
// the entry id from here and ask `
|
|
1
|
+
// The experimental structures RCSB aligns to a UniProt entry: every PDB entity
|
|
2
|
+
// carrying the accession, with the UniProt range it covers, then each entry's
|
|
3
|
+
// method, resolution and chains. Two CORS-enabled calls, which is what lets a
|
|
4
|
+
// page offer a crystal structure beside a prediction without a PDB search of
|
|
5
|
+
// its own; take the entry id from here and ask `fetchPdbEntry` for its
|
|
6
6
|
// residue mapping.
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { RCSB_DATA_GRAPHQL_URL, graphqlFetch } from './pdbEntry.ts'
|
|
9
|
+
import { isPdbId } from './structureUrls.ts'
|
|
9
10
|
|
|
10
11
|
import type { FetchOptions } from './fetchUtils.ts'
|
|
11
12
|
|
|
13
|
+
export const RCSB_SEQUENCE_COORDINATES_URL =
|
|
14
|
+
'https://sequence-coordinates.rcsb.org/graphql'
|
|
15
|
+
|
|
12
16
|
export interface ExperimentalStructure {
|
|
13
17
|
pdbId: string
|
|
18
|
+
/** RCSB's name for it: 'X-ray', 'EM', 'NMR', ... */
|
|
14
19
|
method: string
|
|
15
20
|
/** Å; absent for NMR and some EM entries */
|
|
16
21
|
resolution?: number
|
|
@@ -19,15 +24,45 @@ export interface ExperimentalStructure {
|
|
|
19
24
|
end: number
|
|
20
25
|
/** fraction of the UniProt sequence the entry covers, 0..1 */
|
|
21
26
|
coverage: number
|
|
27
|
+
/** author chain ids of the protein */
|
|
28
|
+
chains: string[]
|
|
22
29
|
}
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
/** One polymer entity aligned to the accession. */
|
|
32
|
+
export interface EntityAlignment {
|
|
33
|
+
/** RCSB's id, "1TUP_3" */
|
|
34
|
+
entityId: string
|
|
35
|
+
pdbId: string
|
|
36
|
+
start: number
|
|
37
|
+
end: number
|
|
38
|
+
coverage: number
|
|
29
39
|
}
|
|
30
40
|
|
|
41
|
+
export interface EntityDetails {
|
|
42
|
+
method: string
|
|
43
|
+
resolution?: number
|
|
44
|
+
chains: string[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function entityAlignmentsQuery(uniprotId: string) {
|
|
48
|
+
return `{ alignments(from: UNIPROT, to: PDB_ENTITY, queryId: "${uniprotId}") {
|
|
49
|
+
target_alignments {
|
|
50
|
+
target_id
|
|
51
|
+
coverage { query_coverage }
|
|
52
|
+
aligned_regions { query_begin query_end }
|
|
53
|
+
}
|
|
54
|
+
} }`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const ENTITY_DETAILS_QUERY = `query($ids: [String!]!) { polymer_entities(entity_ids: $ids) {
|
|
58
|
+
rcsb_id
|
|
59
|
+
rcsb_polymer_entity_container_identifiers { auth_asym_ids }
|
|
60
|
+
entry { rcsb_entry_info { experimental_method resolution_combined } }
|
|
61
|
+
} }`
|
|
62
|
+
|
|
63
|
+
// RCSB's cap on ids per batch request
|
|
64
|
+
const DETAILS_BATCH = 1000
|
|
65
|
+
|
|
31
66
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
32
67
|
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
33
68
|
}
|
|
@@ -36,77 +71,166 @@ function finiteNumber(value: unknown) {
|
|
|
36
71
|
return typeof value === 'number' && Number.isFinite(value) ? value : undefined
|
|
37
72
|
}
|
|
38
73
|
|
|
74
|
+
function strings(value: unknown) {
|
|
75
|
+
return Array.isArray(value)
|
|
76
|
+
? value.filter((v): v is string => typeof v === 'string')
|
|
77
|
+
: []
|
|
78
|
+
}
|
|
79
|
+
|
|
39
80
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* prediction comes from AlphaFold's own API, which answers per model. And
|
|
44
|
-
* 3D-Beacons files SASBDB's small-angle-scattering fits under "experimentally
|
|
45
|
-
* determined" too, with numeric ids and near-full coverage — dystrophin's best
|
|
46
|
-
* "structure" by coverage was SASBDB 436 — which are not entries a residue
|
|
47
|
-
* mapping exists for. Hence the `provider === 'PDBe'` gate rather than the
|
|
48
|
-
* category alone.
|
|
81
|
+
* PDB entities only: the same list carries RCSB's computed models
|
|
82
|
+
* (`AF_AFP04637F1_1`), which a residue mapping does not exist for. The range
|
|
83
|
+
* spans every aligned region, as 3D-Beacons reported it.
|
|
49
84
|
*/
|
|
50
|
-
export function
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
85
|
+
export function parseEntityAlignments(data: unknown): EntityAlignment[] {
|
|
86
|
+
const alignments = isRecord(data) ? data.alignments : undefined
|
|
87
|
+
const targets = isRecord(alignments)
|
|
88
|
+
? alignments.target_alignments
|
|
89
|
+
: undefined
|
|
90
|
+
if (!Array.isArray(targets)) {
|
|
55
91
|
return []
|
|
56
92
|
}
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
93
|
+
return targets.flatMap(target => {
|
|
94
|
+
if (!isRecord(target) || typeof target.target_id !== 'string') {
|
|
95
|
+
return []
|
|
96
|
+
}
|
|
97
|
+
const [pdbId] = target.target_id.split('_')
|
|
98
|
+
const coverage = isRecord(target.coverage)
|
|
99
|
+
? finiteNumber(target.coverage.query_coverage)
|
|
100
|
+
: undefined
|
|
101
|
+
const regions = Array.isArray(target.aligned_regions)
|
|
102
|
+
? target.aligned_regions.filter(isRecord)
|
|
103
|
+
: []
|
|
104
|
+
const begins = regions.map(r => finiteNumber(r.query_begin))
|
|
105
|
+
const ends = regions.map(r => finiteNumber(r.query_end))
|
|
106
|
+
return pdbId !== undefined &&
|
|
107
|
+
isPdbId(pdbId) &&
|
|
108
|
+
coverage !== undefined &&
|
|
109
|
+
begins.length > 0 &&
|
|
110
|
+
begins.every(b => b !== undefined) &&
|
|
111
|
+
ends.every(e => e !== undefined)
|
|
112
|
+
? [
|
|
113
|
+
{
|
|
114
|
+
entityId: target.target_id,
|
|
115
|
+
pdbId: pdbId.toLowerCase(),
|
|
116
|
+
start: Math.min(...begins),
|
|
117
|
+
end: Math.max(...ends),
|
|
118
|
+
coverage,
|
|
119
|
+
},
|
|
120
|
+
]
|
|
121
|
+
: []
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function parseEntityDetails(data: unknown): Map<string, EntityDetails> {
|
|
126
|
+
const entities = isRecord(data) ? data.polymer_entities : undefined
|
|
127
|
+
const details = new Map<string, EntityDetails>()
|
|
128
|
+
for (const entity of Array.isArray(entities) ? entities : []) {
|
|
129
|
+
if (!isRecord(entity) || typeof entity.rcsb_id !== 'string') {
|
|
130
|
+
continue
|
|
131
|
+
}
|
|
132
|
+
const ids = entity.rcsb_polymer_entity_container_identifiers
|
|
133
|
+
const info = isRecord(entity.entry)
|
|
134
|
+
? entity.entry.rcsb_entry_info
|
|
135
|
+
: undefined
|
|
136
|
+
const resolutions = isRecord(info) ? info.resolution_combined : undefined
|
|
137
|
+
const resolution = Array.isArray(resolutions)
|
|
138
|
+
? finiteNumber(resolutions[0])
|
|
139
|
+
: undefined
|
|
140
|
+
details.set(entity.rcsb_id, {
|
|
141
|
+
method:
|
|
142
|
+
isRecord(info) && typeof info.experimental_method === 'string'
|
|
143
|
+
? info.experimental_method
|
|
144
|
+
: 'experimental',
|
|
145
|
+
...(resolution === undefined ? {} : { resolution }),
|
|
146
|
+
chains: strings(isRecord(ids) ? ids.auth_asym_ids : undefined),
|
|
86
147
|
})
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
148
|
+
}
|
|
149
|
+
return details
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* One row per entry, best coverage first and sharpest within a tie. An entry
|
|
154
|
+
* with several entities of the protein (a construct crystallised beside a
|
|
155
|
+
* different fragment of itself) takes the best-covering entity's range and
|
|
156
|
+
* every entity's chains.
|
|
157
|
+
*/
|
|
158
|
+
export function experimentalStructures(
|
|
159
|
+
alignments: EntityAlignment[],
|
|
160
|
+
details: ReadonlyMap<string, EntityDetails>,
|
|
161
|
+
): ExperimentalStructure[] {
|
|
162
|
+
const byEntry = new Map<string, ExperimentalStructure>()
|
|
163
|
+
for (const a of alignments) {
|
|
164
|
+
const d = details.get(a.entityId)
|
|
165
|
+
if (!d) {
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
168
|
+
const existing = byEntry.get(a.pdbId)
|
|
169
|
+
if (!existing) {
|
|
170
|
+
byEntry.set(a.pdbId, {
|
|
171
|
+
pdbId: a.pdbId,
|
|
172
|
+
method: d.method,
|
|
173
|
+
...(d.resolution === undefined ? {} : { resolution: d.resolution }),
|
|
174
|
+
start: a.start,
|
|
175
|
+
end: a.end,
|
|
176
|
+
coverage: a.coverage,
|
|
177
|
+
chains: [...d.chains],
|
|
178
|
+
})
|
|
179
|
+
continue
|
|
180
|
+
}
|
|
181
|
+
for (const chain of d.chains) {
|
|
182
|
+
if (!existing.chains.includes(chain)) {
|
|
183
|
+
existing.chains.push(chain)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (a.coverage > existing.coverage) {
|
|
187
|
+
Object.assign(existing, {
|
|
188
|
+
start: a.start,
|
|
189
|
+
end: a.end,
|
|
190
|
+
coverage: a.coverage,
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return [...byEntry.values()].sort(
|
|
195
|
+
(a, b) =>
|
|
196
|
+
b.coverage - a.coverage ||
|
|
197
|
+
(a.resolution ?? Infinity) - (b.resolution ?? Infinity),
|
|
198
|
+
)
|
|
92
199
|
}
|
|
93
200
|
|
|
94
|
-
|
|
95
|
-
|
|
201
|
+
const ISOFORM_SUFFIX = /-\d+$/
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Every PDB entry aligned to the accession. An accession with none, or one
|
|
205
|
+
* RCSB does not know, is `[]`; an isoform accession is asked as its entry,
|
|
206
|
+
* which is what the alignment is numbered on.
|
|
207
|
+
*/
|
|
96
208
|
export async function fetchExperimentalStructures(
|
|
97
209
|
uniprotId: string,
|
|
98
210
|
opts?: FetchOptions,
|
|
99
211
|
): Promise<ExperimentalStructure[]> {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
212
|
+
const alignments = parseEntityAlignments(
|
|
213
|
+
await graphqlFetch(
|
|
214
|
+
RCSB_SEQUENCE_COORDINATES_URL,
|
|
215
|
+
entityAlignmentsQuery(uniprotId.replace(ISOFORM_SUFFIX, '')),
|
|
216
|
+
undefined,
|
|
217
|
+
opts,
|
|
218
|
+
),
|
|
219
|
+
)
|
|
220
|
+
const ids = alignments.map(a => a.entityId)
|
|
221
|
+
const details = new Map<string, EntityDetails>()
|
|
222
|
+
for (let i = 0; i < ids.length; i += DETAILS_BATCH) {
|
|
223
|
+
const batch = parseEntityDetails(
|
|
224
|
+
await graphqlFetch(
|
|
225
|
+
RCSB_DATA_GRAPHQL_URL,
|
|
226
|
+
ENTITY_DETAILS_QUERY,
|
|
227
|
+
{ ids: ids.slice(i, i + DETAILS_BATCH) },
|
|
228
|
+
opts,
|
|
229
|
+
),
|
|
103
230
|
)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
if (res?.status === 404 || opts?.signal?.aborted) {
|
|
108
|
-
return []
|
|
231
|
+
for (const [id, d] of batch) {
|
|
232
|
+
details.set(id, d)
|
|
109
233
|
}
|
|
110
234
|
}
|
|
111
|
-
return
|
|
235
|
+
return experimentalStructures(alignments, details)
|
|
112
236
|
}
|
package/src/structureUrls.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
//
|
|
2
|
-
// the launch flow and the ProteinView model (e.g. resolving a `uniprotId`/`pdbId`
|
|
3
|
-
// shorthand at hydration) can import them without pulling in heavy launch code.
|
|
1
|
+
// Structure URL builders and target parsers, with no dependencies.
|
|
4
2
|
|
|
5
3
|
const ALPHAFOLD_VERSION = 'v6'
|
|
6
4
|
|
|
7
|
-
// UniProt endpoints. rest.* is the machine API
|
|
8
|
-
// the
|
|
9
|
-
// assembly is built from); www.*/entry is the human-facing entry page the UI
|
|
10
|
-
// links to. Centralized so a path change is one edit rather than a grep.
|
|
5
|
+
// UniProt endpoints. rest.* is the machine API, serving the features as GFF
|
|
6
|
+
// and the sequence as FASTA; www.*/entry is the entry page for people.
|
|
11
7
|
export function uniprotGffUrl(uniprotId: string) {
|
|
12
8
|
return `https://rest.uniprot.org/uniprotkb/${uniprotId}.gff`
|
|
13
9
|
}
|
|
@@ -37,21 +33,27 @@ export function getPdbStructureUrl(pdbId: string) {
|
|
|
37
33
|
return `https://files.rcsb.org/download/${pdbId}.cif`
|
|
38
34
|
}
|
|
39
35
|
|
|
36
|
+
// A PDB id is four characters, the first a digit: 1TUP, 6ZIO. Checked before
|
|
37
|
+
// a typed one reaches a fetch, so three characters on the way to four are not
|
|
38
|
+
// a 404 apiece.
|
|
39
|
+
export function isPdbId(value: string) {
|
|
40
|
+
return /^[1-9][A-Za-z0-9]{3}$/.test(value)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function rcsbEntryUrl(pdbId: string) {
|
|
44
|
+
return `https://www.rcsb.org/structure/${pdbId.toUpperCase()}`
|
|
45
|
+
}
|
|
46
|
+
|
|
40
47
|
/**
|
|
41
|
-
* Resolve the `{ pdbId }` shorthand to a
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* `uniprotId` deliberately resolves to no url here, and still outranks a
|
|
47
|
-
* `pdbId`. An accession's files are whatever AlphaFold DB currently holds — an
|
|
48
|
-
* isoform model, several fragments, a new model version — so the structure
|
|
49
|
-
* keeps the accession and the structure loader asks the prediction API which
|
|
50
|
-
* file to open.
|
|
48
|
+
* Resolve the `{ pdbId }` shorthand to a structure URL, so a hand-written config
|
|
49
|
+
* or a short launch URL needs no RCSB filename. An explicit `url` (or inline
|
|
50
|
+
* `data`) always wins, and resolving an already-resolved spec returns the same
|
|
51
|
+
* url.
|
|
51
52
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
53
|
+
* `uniprotId` resolves to no url and still outranks a `pdbId`. AlphaFold DB's
|
|
54
|
+
* files for an accession change (an isoform model, several fragments, a new
|
|
55
|
+
* model version), so the caller keeps the accession and asks
|
|
56
|
+
* `fetchAlphaFoldModels` which file to open.
|
|
55
57
|
*/
|
|
56
58
|
export function resolveStructureUrl({
|
|
57
59
|
url,
|
|
@@ -138,9 +140,9 @@ export function getConfidenceUrlFromTarget(target: string) {
|
|
|
138
140
|
}
|
|
139
141
|
|
|
140
142
|
/**
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
143
|
+
* The name to show for a structure: its PDB id, its AlphaFold accession, else
|
|
144
|
+
* the file's name. Use it wherever a structure is named, so each place shows
|
|
145
|
+
* the same name.
|
|
144
146
|
*/
|
|
145
147
|
export function structureDisplayLabel({
|
|
146
148
|
url,
|
package/src/types.ts
CHANGED
|
@@ -12,11 +12,8 @@ export const ALIGNMENT_ALGORITHM_VALUES: AlignmentAlgorithm[] = [
|
|
|
12
12
|
'smith_waterman',
|
|
13
13
|
]
|
|
14
14
|
|
|
15
|
-
/**
|
|
16
|
-
*
|
|
17
|
-
* The only place a coercion is needed: the model property is an enumeration, so
|
|
18
|
-
* everything downstream of hydration is already typed.
|
|
19
|
-
*/
|
|
15
|
+
/** Parses an untrusted algorithm name, such as a URL parameter, falling back
|
|
16
|
+
* to Smith-Waterman. */
|
|
20
17
|
export function coerceAlignmentAlgorithm(value: string): AlignmentAlgorithm {
|
|
21
18
|
return (
|
|
22
19
|
ALIGNMENT_ALGORITHM_VALUES.find(v => v === value) ??
|
package/src/uniProtSearch.ts
CHANGED
|
@@ -111,7 +111,6 @@ async function searchByXref(
|
|
|
111
111
|
try {
|
|
112
112
|
return { entries: await searchUniProt(query, 10, opts), error: undefined }
|
|
113
113
|
} catch (e) {
|
|
114
|
-
console.error(`xref search failed for ${id}:`, e)
|
|
115
114
|
return { entries: [], error: e }
|
|
116
115
|
}
|
|
117
116
|
}
|
|
@@ -132,22 +131,18 @@ async function searchByGeneName(
|
|
|
132
131
|
// are 284812. The descendants are asked only on a miss, because for a
|
|
133
132
|
// species with reviewed subspecies entries (human and Neanderthal) they
|
|
134
133
|
// turn one answer into two.
|
|
135
|
-
//
|
|
136
|
-
//
|
|
134
|
+
// When the descendant search fails, the taxon's own empty result stands
|
|
135
|
+
// rather than becoming an error.
|
|
137
136
|
const entries =
|
|
138
137
|
exact.length === 0 && organismId
|
|
139
138
|
? await searchUniProt(
|
|
140
139
|
buildGeneNameQuery(geneName, organismId, 'taxonomy_id'),
|
|
141
140
|
5,
|
|
142
141
|
opts,
|
|
143
|
-
).catch((
|
|
144
|
-
console.error(`descendant-taxon search failed for ${geneName}:`, e)
|
|
145
|
-
return exact
|
|
146
|
-
})
|
|
142
|
+
).catch(() => exact)
|
|
147
143
|
: exact
|
|
148
144
|
return { entries, error: undefined }
|
|
149
145
|
} catch (e) {
|
|
150
|
-
console.error(`gene name search failed for ${geneName}:`, e)
|
|
151
146
|
return { entries: [], error: e }
|
|
152
147
|
}
|
|
153
148
|
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export interface PdbStructureEntry {
|
|
2
|
-
pdbId: string;
|
|
3
|
-
experimentalMethod: string;
|
|
4
|
-
/** Å; absent for NMR and some EM entries */
|
|
5
|
-
resolution?: number;
|
|
6
|
-
/** 1-based inclusive UniProt positions the entry covers */
|
|
7
|
-
unpStart: number;
|
|
8
|
-
unpEnd: number;
|
|
9
|
-
/** share of the UniProt sequence the entry covers, 0..1 */
|
|
10
|
-
coverage: number;
|
|
11
|
-
chains: string[];
|
|
12
|
-
}
|
|
13
|
-
export declare function pdbeBestStructuresUrl(uniprotId: string): string;
|
|
14
|
-
export declare function isPdbId(value: string): boolean;
|
|
15
|
-
export declare function rcsbEntryUrl(pdbId: string): string;
|
|
16
|
-
/**
|
|
17
|
-
* Groups the per-chain rows into one entry per PDB id, keeping PDBe's order.
|
|
18
|
-
* An entry whose chains cover different UniProt ranges (a construct
|
|
19
|
-
* crystallised beside a different fragment of itself) keeps its first row's
|
|
20
|
-
* range, which is the one PDBe ranked it by. The response is keyed by
|
|
21
|
-
* accession, so the single entry is taken whatever its key.
|
|
22
|
-
*/
|
|
23
|
-
export declare function parseBestStructures(json: unknown): PdbStructureEntry[];
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// PDBe's SIFTS-derived list of the experimental structures covering a UniProt
|
|
3
|
-
// entry, one row per chain, ranked by PDBe (coverage, then resolution). The
|
|
4
|
-
// dialog shows one row per entry, so the chains of a homomer collapse into it.
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.pdbeBestStructuresUrl = pdbeBestStructuresUrl;
|
|
7
|
-
exports.isPdbId = isPdbId;
|
|
8
|
-
exports.rcsbEntryUrl = rcsbEntryUrl;
|
|
9
|
-
exports.parseBestStructures = parseBestStructures;
|
|
10
|
-
function pdbeBestStructuresUrl(uniprotId) {
|
|
11
|
-
return `https://www.ebi.ac.uk/pdbe/api/mappings/best_structures/${uniprotId}`;
|
|
12
|
-
}
|
|
13
|
-
// A PDB id is four characters, the first a digit: 1TUP, 6ZIO. Checked before
|
|
14
|
-
// a typed one reaches a fetch, so three characters on the way to four are not
|
|
15
|
-
// a 404 apiece.
|
|
16
|
-
function isPdbId(value) {
|
|
17
|
-
return /^[1-9][A-Za-z0-9]{3}$/.test(value);
|
|
18
|
-
}
|
|
19
|
-
function rcsbEntryUrl(pdbId) {
|
|
20
|
-
return `https://www.rcsb.org/structure/${pdbId.toUpperCase()}`;
|
|
21
|
-
}
|
|
22
|
-
function isRecord(value) {
|
|
23
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
24
|
-
}
|
|
25
|
-
function finiteNumber(value) {
|
|
26
|
-
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Groups the per-chain rows into one entry per PDB id, keeping PDBe's order.
|
|
30
|
-
* An entry whose chains cover different UniProt ranges (a construct
|
|
31
|
-
* crystallised beside a different fragment of itself) keeps its first row's
|
|
32
|
-
* range, which is the one PDBe ranked it by. The response is keyed by
|
|
33
|
-
* accession, so the single entry is taken whatever its key.
|
|
34
|
-
*/
|
|
35
|
-
function parseBestStructures(json) {
|
|
36
|
-
const rows = isRecord(json) ? Object.values(json)[0] : undefined;
|
|
37
|
-
if (!Array.isArray(rows)) {
|
|
38
|
-
return [];
|
|
39
|
-
}
|
|
40
|
-
const byId = new Map();
|
|
41
|
-
for (const row of rows) {
|
|
42
|
-
if (!isRecord(row)) {
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
const pdbId = typeof row.pdb_id === 'string' ? row.pdb_id : undefined;
|
|
46
|
-
const chain = typeof row.chain_id === 'string' ? row.chain_id : undefined;
|
|
47
|
-
const unpStart = finiteNumber(row.unp_start);
|
|
48
|
-
const unpEnd = finiteNumber(row.unp_end);
|
|
49
|
-
const coverage = finiteNumber(row.coverage);
|
|
50
|
-
if (pdbId === undefined ||
|
|
51
|
-
chain === undefined ||
|
|
52
|
-
unpStart === undefined ||
|
|
53
|
-
unpEnd === undefined ||
|
|
54
|
-
coverage === undefined) {
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
const existing = byId.get(pdbId);
|
|
58
|
-
if (existing) {
|
|
59
|
-
if (!existing.chains.includes(chain)) {
|
|
60
|
-
existing.chains.push(chain);
|
|
61
|
-
}
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
const resolution = finiteNumber(row.resolution);
|
|
65
|
-
byId.set(pdbId, {
|
|
66
|
-
pdbId,
|
|
67
|
-
experimentalMethod: typeof row.experimental_method === 'string'
|
|
68
|
-
? row.experimental_method
|
|
69
|
-
: 'unknown',
|
|
70
|
-
...(resolution === undefined ? {} : { resolution }),
|
|
71
|
-
unpStart,
|
|
72
|
-
unpEnd,
|
|
73
|
-
coverage,
|
|
74
|
-
chains: [chain],
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
return [...byId.values()];
|
|
78
|
-
}
|
|
79
|
-
//# sourceMappingURL=pdbeBestStructures.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pdbeBestStructures.js","sourceRoot":"","sources":["../src/pdbeBestStructures.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export declare function pdbeEntryMoleculesUrl(pdbId: string): string;
|
|
2
|
-
/**
|
|
3
|
-
* One sequence per protein entity, in entity order. Nucleic-acid entities are
|
|
4
|
-
* left out: A, C, G, T and U are amino-acid letters too, so a DNA chain scores
|
|
5
|
-
* as a candidate rather than losing honestly.
|
|
6
|
-
*/
|
|
7
|
-
export declare function parseEntryMolecules(json: unknown): string[];
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// The polymer entities of a PDB entry, as PDBe already knows them. The dialog
|
|
3
|
-
// wants the entry's residues only to annotate the isoform list, and reading
|
|
4
|
-
// them out of the structure file meant downloading and parsing the whole
|
|
5
|
-
// mmCIF — minutes for a ribosome, for a label. This is a few kB of JSON, and
|
|
6
|
-
// the view downloads the file itself when the user actually launches.
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.pdbeEntryMoleculesUrl = pdbeEntryMoleculesUrl;
|
|
9
|
-
exports.parseEntryMolecules = parseEntryMolecules;
|
|
10
|
-
function pdbeEntryMoleculesUrl(pdbId) {
|
|
11
|
-
return `https://www.ebi.ac.uk/pdbe/api/pdb/entry/molecules/${pdbId.toLowerCase()}`;
|
|
12
|
-
}
|
|
13
|
-
function isRecord(value) {
|
|
14
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* One sequence per protein entity, in entity order. Nucleic-acid entities are
|
|
18
|
-
* left out: A, C, G, T and U are amino-acid letters too, so a DNA chain scores
|
|
19
|
-
* as a candidate rather than losing honestly.
|
|
20
|
-
*/
|
|
21
|
-
function parseEntryMolecules(json) {
|
|
22
|
-
const entities = isRecord(json) ? Object.values(json)[0] : undefined;
|
|
23
|
-
if (!Array.isArray(entities)) {
|
|
24
|
-
return [];
|
|
25
|
-
}
|
|
26
|
-
return entities.flatMap(entity => {
|
|
27
|
-
if (!isRecord(entity)) {
|
|
28
|
-
return [];
|
|
29
|
-
}
|
|
30
|
-
const { molecule_type: type, sequence } = entity;
|
|
31
|
-
return typeof type === 'string' &&
|
|
32
|
-
type.startsWith('polypeptide') &&
|
|
33
|
-
typeof sequence === 'string' &&
|
|
34
|
-
sequence.length > 0
|
|
35
|
-
? [sequence]
|
|
36
|
-
: [];
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
//# sourceMappingURL=pdbeEntryMolecules.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pdbeEntryMolecules.js","sourceRoot":"","sources":["../src/pdbeEntryMolecules.ts"],"names":[],"mappings":";AAAA,8EAA8E;AAC9E,4EAA4E;AAC5E,yEAAyE;AACzE,6EAA6E;AAC7E,sEAAsE;;AAEtE,sDAEC;AAWD,kDAiBC;AA9BD,SAAgB,qBAAqB,CAAC,KAAa;IACjD,OAAO,sDAAsD,KAAK,CAAC,WAAW,EAAE,EAAE,CAAA;AACpF,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;AAC7E,CAAC;AAED;;;;GAIG;AACH,SAAgB,mBAAmB,CAAC,IAAa;IAC/C,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACpE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAA;IACX,CAAC;IACD,OAAO,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;QAC/B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,CAAA;QACX,CAAC;QACD,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAA;QAChD,OAAO,OAAO,IAAI,KAAK,QAAQ;YAC7B,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;YAC9B,OAAO,QAAQ,KAAK,QAAQ;YAC5B,QAAQ,CAAC,MAAM,GAAG,CAAC;YACnB,CAAC,CAAC,CAAC,QAAQ,CAAC;YACZ,CAAC,CAAC,EAAE,CAAA;IACR,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
package/dist/siftsNumbering.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { UniProtStructureSegment } from './pdbUniProtMapping.ts';
|
|
2
|
-
export interface AuthorRange {
|
|
3
|
-
start: number;
|
|
4
|
-
end: number;
|
|
5
|
-
chain: string;
|
|
6
|
-
/** author number minus UniProt number in the chosen segment */
|
|
7
|
-
shift: number;
|
|
8
|
-
}
|
|
9
|
-
/** The segments SIFTS gives one accession, chain ids and author numbers
|
|
10
|
-
* included, out of a whole entry's mappings. */
|
|
11
|
-
export declare function segmentsForAccession(mappings: readonly {
|
|
12
|
-
accession: string;
|
|
13
|
-
segments: UniProtStructureSegment[];
|
|
14
|
-
}[], accession: string): UniProtStructureSegment[];
|
|
15
|
-
/**
|
|
16
|
-
* The author-numbered range a UniProt range is cited by in the entry: the
|
|
17
|
-
* segment overlapping most of the range decides the shift. Undefined when no
|
|
18
|
-
* chain of the entry covers any of it, or when SIFTS named no author numbers.
|
|
19
|
-
*/
|
|
20
|
-
export declare function toAuthorRange(segments: readonly UniProtStructureSegment[], range: {
|
|
21
|
-
start: number;
|
|
22
|
-
end: number;
|
|
23
|
-
}): AuthorRange | undefined;
|
package/dist/siftsNumbering.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// How a PDB entry numbers the residues of a UniProt sequence. Author numbering
|
|
3
|
-
// is what a paper cites and what a viewer lights a named residue by, and it is
|
|
4
|
-
// not the UniProt numbering for every entry: haemoglobin's chains number from
|
|
5
|
-
// the mature protein, so author residue 1 is UniProt residue 2 (2HHB, measured
|
|
6
|
-
// 2026-09-12). SIFTS records the correspondence per chain segment, and
|
|
7
|
-
// `fetchUniProtStructureMappings` is what asks PDBe for it.
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.segmentsForAccession = segmentsForAccession;
|
|
10
|
-
exports.toAuthorRange = toAuthorRange;
|
|
11
|
-
/** The segments SIFTS gives one accession, chain ids and author numbers
|
|
12
|
-
* included, out of a whole entry's mappings. */
|
|
13
|
-
function segmentsForAccession(mappings, accession) {
|
|
14
|
-
return mappings.find(m => m.accession === accession)?.segments ?? [];
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* The author-numbered range a UniProt range is cited by in the entry: the
|
|
18
|
-
* segment overlapping most of the range decides the shift. Undefined when no
|
|
19
|
-
* chain of the entry covers any of it, or when SIFTS named no author numbers.
|
|
20
|
-
*/
|
|
21
|
-
function toAuthorRange(segments, range) {
|
|
22
|
-
const overlap = (s) => Math.min(s.unpEnd, range.end) - Math.max(s.unpStart, range.start) + 1;
|
|
23
|
-
const best = segments
|
|
24
|
-
.flatMap(s => {
|
|
25
|
-
const { chainId, authorStart, unpStart, unpEnd } = s;
|
|
26
|
-
return chainId !== undefined &&
|
|
27
|
-
authorStart !== undefined &&
|
|
28
|
-
overlap(s) > 0
|
|
29
|
-
? [{ chainId, authorStart, unpStart, unpEnd, overlap: overlap(s) }]
|
|
30
|
-
: [];
|
|
31
|
-
})
|
|
32
|
-
.toSorted((a, b) => b.overlap - a.overlap)[0];
|
|
33
|
-
if (!best) {
|
|
34
|
-
return undefined;
|
|
35
|
-
}
|
|
36
|
-
const shift = best.authorStart - best.unpStart;
|
|
37
|
-
return {
|
|
38
|
-
start: Math.max(range.start, best.unpStart) + shift,
|
|
39
|
-
end: Math.min(range.end, best.unpEnd) + shift,
|
|
40
|
-
chain: best.chainId,
|
|
41
|
-
shift,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
//# sourceMappingURL=siftsNumbering.js.map
|