p2s_mapper 2.0.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.
Files changed (87) hide show
  1. package/README.md +37 -38
  2. package/dist/alignmentQuality.d.ts +5 -5
  3. package/dist/alphaFoldModels.d.ts +4 -4
  4. package/dist/alphaFoldModels.js +4 -4
  5. package/dist/chooseMappedEntity.d.ts +11 -13
  6. package/dist/chooseMappedEntity.js +9 -12
  7. package/dist/chooseMappedEntity.js.map +1 -1
  8. package/dist/coordinates.d.ts +11 -13
  9. package/dist/coordinates.js.map +1 -1
  10. package/dist/extractStructureSequences.d.ts +21 -28
  11. package/dist/extractStructureSequences.js +2 -2
  12. package/dist/extractStructureSequences.js.map +1 -1
  13. package/dist/fetchUtils.d.ts +7 -7
  14. package/dist/fetchUtils.js +7 -7
  15. package/dist/isoformRanking.d.ts +1 -1
  16. package/dist/mappings.d.ts +9 -12
  17. package/dist/mappings.js +4 -5
  18. package/dist/mappings.js.map +1 -1
  19. package/dist/pairwiseAlignment.d.ts +4 -2
  20. package/dist/pairwiseAlignment.js +9 -7
  21. package/dist/pairwiseAlignment.js.map +1 -1
  22. package/dist/pdbUniProtMapping.d.ts +5 -6
  23. package/dist/pdbUniProtMapping.js +5 -6
  24. package/dist/pdbUniProtMapping.js.map +1 -1
  25. package/dist/structureFormat.d.ts +11 -18
  26. package/dist/structureFormat.js +4 -6
  27. package/dist/structureFormat.js.map +1 -1
  28. package/dist/structureSources.d.ts +1 -1
  29. package/dist/structureUrls.d.ts +11 -16
  30. package/dist/structureUrls.js +14 -23
  31. package/dist/structureUrls.js.map +1 -1
  32. package/dist/types.d.ts +2 -5
  33. package/dist/types.js +2 -5
  34. package/dist/types.js.map +1 -1
  35. package/dist/uniProtSearch.js +3 -8
  36. package/dist/uniProtSearch.js.map +1 -1
  37. package/esm/alignmentQuality.d.ts +5 -5
  38. package/esm/alphaFoldModels.d.ts +4 -4
  39. package/esm/alphaFoldModels.js +4 -4
  40. package/esm/chooseMappedEntity.d.ts +11 -13
  41. package/esm/chooseMappedEntity.js +9 -12
  42. package/esm/chooseMappedEntity.js.map +1 -1
  43. package/esm/coordinates.d.ts +11 -13
  44. package/esm/coordinates.js.map +1 -1
  45. package/esm/extractStructureSequences.d.ts +21 -28
  46. package/esm/extractStructureSequences.js +2 -2
  47. package/esm/extractStructureSequences.js.map +1 -1
  48. package/esm/fetchUtils.d.ts +7 -7
  49. package/esm/fetchUtils.js +7 -7
  50. package/esm/isoformRanking.d.ts +1 -1
  51. package/esm/mappings.d.ts +9 -12
  52. package/esm/mappings.js +4 -5
  53. package/esm/mappings.js.map +1 -1
  54. package/esm/pairwiseAlignment.d.ts +4 -2
  55. package/esm/pairwiseAlignment.js +7 -5
  56. package/esm/pairwiseAlignment.js.map +1 -1
  57. package/esm/pdbUniProtMapping.d.ts +5 -6
  58. package/esm/pdbUniProtMapping.js +5 -6
  59. package/esm/pdbUniProtMapping.js.map +1 -1
  60. package/esm/structureFormat.d.ts +11 -18
  61. package/esm/structureFormat.js +4 -6
  62. package/esm/structureFormat.js.map +1 -1
  63. package/esm/structureSources.d.ts +1 -1
  64. package/esm/structureUrls.d.ts +11 -16
  65. package/esm/structureUrls.js +14 -23
  66. package/esm/structureUrls.js.map +1 -1
  67. package/esm/types.d.ts +2 -5
  68. package/esm/types.js +2 -5
  69. package/esm/types.js.map +1 -1
  70. package/esm/uniProtSearch.js +3 -8
  71. package/esm/uniProtSearch.js.map +1 -1
  72. package/package.json +1 -1
  73. package/src/alignmentQuality.ts +5 -5
  74. package/src/alphaFoldModels.ts +4 -4
  75. package/src/chooseMappedEntity.ts +12 -15
  76. package/src/coordinates.ts +11 -13
  77. package/src/extractStructureSequences.ts +21 -28
  78. package/src/fetchUtils.ts +7 -7
  79. package/src/isoformRanking.ts +1 -1
  80. package/src/mappings.ts +9 -12
  81. package/src/pairwiseAlignment.ts +11 -8
  82. package/src/pdbUniProtMapping.ts +5 -6
  83. package/src/structureFormat.ts +11 -18
  84. package/src/structureSources.ts +1 -1
  85. package/src/structureUrls.ts +14 -23
  86. package/src/types.ts +2 -5
  87. package/src/uniProtSearch.ts +3 -8
@@ -8,16 +8,15 @@ import { invertMap } from './invertMap.ts'
8
8
  import type { PairwiseAlignment } from './mappings.ts'
9
9
 
10
10
  /**
11
- * Branded coordinate spaces. Every position in this plugin is a bare `number`,
12
- * but the *meaning* differs: a structure residue index is not interchangeable
13
- * with a transcript residue index or an alignment column, and mixing them is
14
- * the root cause of the off-by-one class of bugs. Branding makes the compiler
15
- * reject cross-space mixing; construct values with the helpers below at the
16
- * boundaries where raw numbers enter (molstar picks, hover events, features).
11
+ * Branded coordinate spaces. Every position is a plain `number` at runtime, but
12
+ * a structure residue index, a transcript residue index and an alignment column
13
+ * mean different things, and mixing them causes off-by-one bugs. The brands
14
+ * make the compiler reject mixing; wrap a raw number with the helpers below
15
+ * where it enters, such as a molstar pick, a hover event or a feature.
17
16
  *
18
- * Conventions: all are 0-based. StructurePos indexes the ungapped structure
19
- * sequence; TranscriptPos the ungapped transcript sequence; AlignmentCol a
20
- * column of the pairwise alignment (gaps included).
17
+ * All are 0-based. StructurePos indexes the ungapped structure sequence,
18
+ * TranscriptPos the ungapped transcript sequence, and AlignmentCol a column of
19
+ * the pairwise alignment, gaps included.
21
20
  */
22
21
  export type StructurePos = number & { readonly __brand: 'StructurePos' }
23
22
  export type TranscriptPos = number & { readonly __brand: 'TranscriptPos' }
@@ -44,10 +43,9 @@ export interface CoordinateMapper {
44
43
  transcriptToAlignment: (p: TranscriptPos) => AlignmentCol | undefined
45
44
  alignmentToTranscript: (c: AlignmentCol) => TranscriptPos | undefined
46
45
  /**
47
- * Raw lookup tables, for consumers that iterate the whole map rather than
48
- * convert a single position (e.g. per-residue track rendering, selection of
49
- * all aligned residues). Plain numbers — prefer the typed methods above for
50
- * point conversions.
46
+ * The lookup tables as plain numbers, for code that iterates a whole map,
47
+ * such as drawing a per-residue track or selecting every aligned residue.
48
+ * Use the typed methods above to convert one position.
51
49
  */
52
50
  readonly maps: CoordinateMaps
53
51
  }
@@ -1,42 +1,35 @@
1
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.
2
+ * sequence, and the molstar `label_seq_id` of each residue.
3
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.
4
+ * Structure positions are 0-based indices into `seq`, and molstar selects
5
+ * residues by `label_seq_id`. The two differ by 1 only when the entity's
6
+ * sequence covers every residue from 1, which holds for any mmCIF with an
7
+ * `entity_poly_seq` category (all of RCSB and AlphaFold) and for a PDB file
8
+ * with SEQRES records, from which molstar builds that category.
6
9
  *
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.
10
+ * For a PDB file without SEQRES, molstar falls back to
11
+ * `StructureSequence.fromHierarchy` and takes `label_seq_id` from the observed
12
+ * residues' author numbering: a chain whose coordinates start at residue 94 has
13
+ * seqIds 94.., and an unobserved loop leaves a gap. Adding 1 there is off by the
14
+ * whole offset, in both directions, so convert through `seqIds`.
21
15
  */
22
16
  export interface Entity {
23
- /** mmCIF entity id, molstar's own bookkeeping */
17
+ /** mmCIF entity id */
24
18
  entityId: string
25
19
  /** one letter per residue, observed or not */
26
20
  seq: string
27
21
  /** molstar `label_seq_id` per position of `seq` */
28
22
  seqIds: number[]
29
- /** author chain ids carrying this entity, e.g. ['A', 'B'] for a homodimer:
30
- * what a user recognises from the PDB entry page */
23
+ /** author chain ids of this entity, e.g. ['A', 'B'] for a homodimer, as
24
+ * the PDB entry page lists them */
31
25
  chains: string[]
32
- /** DNA, RNA or a hybrid, by molstar's entity subtype. Its letters spell
33
- * amino acids too (A, C, G, T, U), so it is kept out of the alignment
34
- * rather than left to score low. */
26
+ /** DNA, RNA or a hybrid, by molstar's entity subtype. A, C, G, T and U
27
+ * are amino-acid letters too, so chooseMappedEntity skips these. */
35
28
  nucleicAcid?: boolean
36
29
  /** `auth_seq_id` per position: the depositors' numbering, which papers cite
37
- * (1TUP's position 154 is R248) and Mol*'s hover shows. Display only;
38
- * address Mol* through `seqIds`. Absent when the model has no atomic
39
- * hierarchy. See `residueNumber`. */
30
+ * (1TUP's position 154 is R248) and Mol*'s hover shows. For display only;
31
+ * use `seqIds` to select residues in Mol*. Absent when the model has no
32
+ * atomic hierarchy. See `residueNumber`. */
40
33
  authSeqIds?: number[]
41
34
  }
42
35
 
@@ -284,8 +277,8 @@ export function entityLabel(entity: Entity) {
284
277
  return `${chains} (${entity.seq.length} ${entity.nucleicAcid ? 'nt' : 'aa'})`
285
278
  }
286
279
 
287
- /** The protein chains' sequences, for the launch dialog's isoform matching,
288
- * which has no use for a DNA strand that happens to align. */
280
+ /** The protein chains' sequences, for ranking isoforms against a structure.
281
+ * DNA chains are left out, because their letters align as amino acids. */
289
282
  export function extractStructureSequences(
290
283
  model: StructureModel,
291
284
  ): string[] | undefined {
package/src/fetchUtils.ts CHANGED
@@ -36,9 +36,9 @@ export function isDefinitiveFailure(e: unknown) {
36
36
  }
37
37
 
38
38
  /**
39
- * The error a failed response deserves: status and host, plus at most the first
40
- * line of the body. A whole body is an HTML error page or a megabyte of JSON,
41
- * and pasting it into the dialog buries the one line that says what went wrong.
39
+ * The error for a failed response: status and host, plus at most the first line
40
+ * of the body. A whole body is an HTML error page or a megabyte of JSON, which
41
+ * hides the line that says what went wrong.
42
42
  */
43
43
  export async function httpError(response: Response, url: string) {
44
44
  const detail = firstLine(await response.text().catch(() => ''))
@@ -127,10 +127,10 @@ export async function jsonfetch<T = unknown>(
127
127
  }
128
128
 
129
129
  /**
130
- * An AbortSignal's reason as a real Error. `signal.reason` is `any` — usually a
131
- * DOMException, but a caller can abort with anything at all — and throwing a
132
- * non-Error loses the stack and breaks `instanceof Error` checks in the UI's
133
- * error rendering. Normalize at every throw site.
130
+ * An AbortSignal's reason as a real Error. `signal.reason` is `any`, usually a
131
+ * DOMException, but a caller can abort with any value, and throwing a non-Error
132
+ * loses the stack and breaks `instanceof Error` checks. Use it at every throw
133
+ * site.
134
134
  */
135
135
  export function abortError(signal: AbortSignal) {
136
136
  return signal.reason instanceof Error
@@ -17,7 +17,7 @@ export interface RankedIsoform {
17
17
  /** identical residues against the structure, for an isoform that does not
18
18
  * match it exactly; undefined with no structure to compare to */
19
19
  identical?: number
20
- /** that alignment's score, which is what ranks non-matching isoforms */
20
+ /** that alignment's score, which ranks the non-matching isoforms */
21
21
  score?: number
22
22
  }
23
23
 
package/src/mappings.ts CHANGED
@@ -6,13 +6,11 @@ export interface AlignmentRow {
6
6
  }
7
7
 
8
8
  /**
9
- * A two-row alignment whose ROW ORDER IS A CONTRACT: row 0 is the transcript,
10
- * row 1 is the structure. Every producer honors it — runLocalAlignment is
11
- * always called as (transcript, structure), and the manual-import dialog tells
12
- * the user to paste them in that order — and every consumer depends on it, but
13
- * nothing in the type can enforce it, and swapping the two silently inverts
14
- * every coordinate map. Read the rows through the accessors below rather than
15
- * indexing, so a call site states which sequence it means.
9
+ * A two-row alignment: row 0 is the transcript and row 1 is the structure.
10
+ * alignSequences takes them in that order and every consumer depends on it,
11
+ * but the type cannot enforce it, and swapped rows invert every coordinate map
12
+ * without an error. Read the rows with the accessors below, so a call site
13
+ * names the sequence it means.
16
14
  */
17
15
  export interface PairwiseAlignment {
18
16
  consensus: string
@@ -28,11 +26,10 @@ export const alignmentLength = (pa: PairwiseAlignment) =>
28
26
  transcriptAlignedSeq(pa).length
29
27
 
30
28
  /**
31
- * Why a pairwise alignment can't be used, or undefined if it's usable. The two
32
- * rows must be the same non-zero length — every coordinate map walks them in
33
- * lockstep, so a ragged pair would map positions to nonsense. Callers that
34
- * accept alignments from outside (the manual-import dialog) check this and
35
- * report it; the internal map builders assert on it.
29
+ * Why a pairwise alignment can't be used, or undefined if it's usable. Both
30
+ * rows must have the same non-zero length, because every coordinate map walks
31
+ * them in step. Check an alignment from outside the package with this; the
32
+ * internal map builders throw on it.
36
33
  */
37
34
  export function pairwiseAlignmentProblem(pa: PairwiseAlignment) {
38
35
  const transcript = transcriptAlignedSeq(pa)
@@ -393,14 +393,14 @@ export function selfScore(seq: string) {
393
393
  }
394
394
 
395
395
  export function scoredAlignment(
396
- seq1: string,
397
- seq2: string,
396
+ transcript: string,
397
+ structure: string,
398
398
  algorithm: AlignmentAlgorithm,
399
399
  ): { alignment: PairwiseAlignment; score: number } {
400
400
  const { alignedSeq1, alignedSeq2, score } =
401
401
  algorithm === 'smith_waterman'
402
- ? smithWaterman(seq1, seq2)
403
- : needlemanWunsch(seq1, seq2)
402
+ ? smithWaterman(transcript, structure)
403
+ : needlemanWunsch(transcript, structure)
404
404
 
405
405
  return {
406
406
  alignment: {
@@ -414,10 +414,13 @@ export function scoredAlignment(
414
414
  }
415
415
  }
416
416
 
417
- export function runLocalAlignment(
418
- seq1: string,
419
- seq2: string,
417
+ export function alignSequences(
418
+ transcript: string,
419
+ structure: string,
420
420
  algorithm: AlignmentAlgorithm,
421
421
  ): PairwiseAlignment {
422
- return scoredAlignment(seq1, seq2, algorithm).alignment
422
+ return scoredAlignment(transcript, structure, algorithm).alignment
423
423
  }
424
+
425
+ /** @deprecated renamed to `alignSequences`, which runs either algorithm */
426
+ export const runLocalAlignment = alignSequences
@@ -70,12 +70,11 @@ function segmentsForEntity(
70
70
  }
71
71
 
72
72
  /**
73
- * Picks the UniProt entry that describes a given entity, with its segments
74
- * narrowed to that entity. Only the entity the plugin has mapped to the
75
- * transcript is relevant — a heteromer maps each of its chains to a different
76
- * accession, and using the wrong one would silently annotate the wrong protein.
77
- * When several accessions cover the same entity (a chimeric construct) the one
78
- * contributing the most residues wins.
73
+ * Picks the UniProt entry for a given entity, with its segments narrowed to
74
+ * that entity. Pass the entity mapped to the transcript: a heteromer's chains
75
+ * align to different accessions, and the wrong one would annotate the wrong
76
+ * protein. When several accessions cover one entity (a chimeric construct), the
77
+ * one covering the most residues wins.
79
78
  */
80
79
  export function chooseUniProtMappingForEntity(
81
80
  mappings: UniProtStructureMapping[],
@@ -1,19 +1,14 @@
1
1
  /**
2
2
  * Which molstar trajectory parser a structure needs.
3
3
  *
4
- * Getting this wrong is not a graceful failure in either direction: parsing a
5
- * PDB file as mmCIF throws ("Unexpected token. Expected data_, loop_, or data
6
- * name"), and — worse — parsing an mmCIF as PDB *succeeds* and yields a model
7
- * with zero polymer entities and thousands of misread atoms, so the view loads
8
- * with no sequence, no alignment and no genome mapping, and nothing reports an
9
- * error.
4
+ * A wrong choice fails either way. Parsing a PDB file as mmCIF throws
5
+ * ("Unexpected token. Expected data_, loop_, or data name"). Parsing an mmCIF
6
+ * as PDB succeeds and yields a model with zero polymer entities and thousands of
7
+ * misread atoms, so the structure loads with no sequence, no alignment and no
8
+ * genome mapping, and with no error.
10
9
  *
11
- * So detection lives here and is applied as the default at every entry point,
12
- * rather than at one of them.
13
- *
14
- * The two names are the subset of molstar's `BuiltInTrajectoryFormat` a
15
- * structure loaded from a file or a url can be, spelled out rather than
16
- * imported so nothing here depends on molstar.
10
+ * The two names are the subset of molstar's `BuiltInTrajectoryFormat` that a
11
+ * structure file or URL can be, written out so nothing here imports molstar.
17
12
  */
18
13
  export type StructureFormat = 'pdb' | 'mmcif'
19
14
 
@@ -35,8 +30,7 @@ export function structureFileExtension(nameOrUrl: string) {
35
30
 
36
31
  /**
37
32
  * Format for a filename or URL. Unknown extensions fall back to mmCIF, which is
38
- * what every URL this plugin generates itself serves (AlphaFold and RCSB both
39
- * hand out `.cif`).
33
+ * what AlphaFold and RCSB serve (`.cif`).
40
34
  */
41
35
  export function structureFormatFromName(nameOrUrl: string): StructureFormat {
42
36
  return PDB_EXTENSIONS.has(structureFileExtension(nameOrUrl)) ? 'pdb' : 'mmcif'
@@ -49,10 +43,9 @@ export function isBinaryStructureUrl(url: string) {
49
43
  }
50
44
 
51
45
  /**
52
- * Format for structure text that arrived without a filename — an inline `data`
53
- * snapshot, a pasted file, or the PDB the plugin synthesizes from Foldseek Cα
54
- * coordinates. Sniffs the content, which is strictly more reliable than a name
55
- * and is the only signal available here.
46
+ * Format for structure text with no filename, such as inline data, a pasted
47
+ * file, or a PDB built by `caCoordsToPdb`. The content is more reliable than a
48
+ * name, and here it is the only signal.
56
49
  *
57
50
  * mmCIF is line-oriented and its first meaningful line is a `data_` block
58
51
  * header (comments and blank lines may precede it); PDB has none.
@@ -24,7 +24,7 @@ export interface ExperimentalStructure {
24
24
  end: number
25
25
  /** fraction of the UniProt sequence the entry covers, 0..1 */
26
26
  coverage: number
27
- /** author chain ids carrying the protein */
27
+ /** author chain ids of the protein */
28
28
  chains: string[]
29
29
  }
30
30
 
@@ -1,13 +1,9 @@
1
- // Pure structure-URL builders and target parsers. Kept dependency-free so both
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 (the GFF the feature tracks and
8
- // the alignment's annotation rows both read, the FASTA the temporary protein
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
  }
@@ -49,20 +45,15 @@ export function rcsbEntryUrl(pdbId: string) {
49
45
  }
50
46
 
51
47
  /**
52
- * Resolve the `{ pdbId }` shorthand to a concrete structure URL, so a
53
- * hand-authored snapshot or a short launch URL doesn't have to know RCSB's
54
- * filename format. An explicit `url` (or inline `data`) always wins.
55
- * Idempotent: re-resolving an already-resolved spec returns the same url.
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.
56
52
  *
57
- * `uniprotId` deliberately resolves to no url here, and still outranks a
58
- * `pdbId`. An accession's files are whatever AlphaFold DB currently holds — an
59
- * isoform model, several fragments, a new model version — so the structure
60
- * keeps the accession and the structure loader asks the prediction API which
61
- * file to open.
62
- *
63
- * Shared by the Structure model's snapshot preprocessor and the
64
- * LaunchView-ProteinView extension point, which would otherwise each carry
65
- * their own copy of the same precedence rule.
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.
66
57
  */
67
58
  export function resolveStructureUrl({
68
59
  url,
@@ -149,9 +140,9 @@ export function getConfidenceUrlFromTarget(target: string) {
149
140
  }
150
141
 
151
142
  /**
152
- * What a structure is called in the UI: its PDB id, its AlphaFold accession,
153
- * else the file's name. Shared by the view title, the alignment panel heading
154
- * and the hover readout, so one structure reads the same everywhere.
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.
155
146
  */
156
147
  export function structureDisplayLabel({
157
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
- * Parses an untrusted algorithm name (a URL session-spec param) into the union.
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) ??
@@ -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
- // The taxon itself has answered, so the wider question failing leaves
136
- // that answer standing rather than turning an empty result into an error.
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((e: unknown) => {
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
  }