jbrowse-plugin-msaview 2.7.1 → 2.7.3
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/dist/AddHighlightModel/MsaToGenomeHighlight.js +6 -5
- package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.js +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastPanel.js +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/useCachedBlastResults.d.ts +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/useCachedBlastResults.js +5 -2
- package/dist/LaunchMsaView/components/calculateProteinSequence.d.ts +3 -2
- package/dist/LaunchMsaView/components/calculateProteinSequence.js +31 -15
- package/dist/LaunchMsaView/components/geneticCodes.d.ts +15 -0
- package/dist/LaunchMsaView/components/geneticCodes.js +227 -0
- package/dist/LaunchMsaView/components/types.d.ts +1 -0
- package/dist/LaunchMsaView/index.js +32 -26
- package/dist/MsaViewPanel/doLaunchBlast.js +10 -22
- package/dist/MsaViewPanel/genomeToMSA.js +13 -6
- package/dist/MsaViewPanel/genomeToMSA.test.js +35 -6
- package/dist/MsaViewPanel/model.d.ts +68 -66
- package/dist/MsaViewPanel/model.js +14 -12
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.d.ts +32 -13
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.js +43 -14
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.js +108 -18
- package/dist/MsaViewPanel/msaDataStore.js +8 -17
- package/dist/MsaViewPanel/syncGenomeHoverToMsaColumn.test.js +8 -6
- package/dist/jbrowse-plugin-msaview.umd.production.min.js +31 -31
- package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
- package/dist/utils/blastCache.d.ts +1 -2
- package/dist/utils/blastCache.js +9 -22
- package/dist/utils/domainCache.js +6 -15
- package/dist/utils/idb.d.ts +12 -0
- package/dist/utils/idb.js +21 -0
- package/dist/utils/taxonomyNames.js +13 -18
- package/dist/utils/useLocalStorage.d.ts +1 -0
- package/dist/utils/useLocalStorage.js +29 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -3
- package/src/AddHighlightModel/MsaToGenomeHighlight.tsx +6 -8
- package/src/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.tsx +1 -1
- package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastPanel.tsx +1 -1
- package/src/LaunchMsaView/components/NCBIBlastQuery/useCachedBlastResults.ts +4 -2
- package/src/LaunchMsaView/components/calculateProteinSequence.ts +35 -20
- package/src/LaunchMsaView/components/geneticCodes.ts +298 -0
- package/src/LaunchMsaView/components/types.ts +3 -0
- package/src/LaunchMsaView/index.ts +44 -35
- package/src/MsaViewPanel/doLaunchBlast.ts +20 -29
- package/src/MsaViewPanel/genomeToMSA.test.ts +38 -6
- package/src/MsaViewPanel/genomeToMSA.ts +14 -6
- package/src/MsaViewPanel/model.ts +17 -12
- package/src/MsaViewPanel/msaCoordToGenomeCoord.test.ts +117 -18
- package/src/MsaViewPanel/msaCoordToGenomeCoord.ts +70 -26
- package/src/MsaViewPanel/msaDataStore.ts +17 -17
- package/src/MsaViewPanel/syncGenomeHoverToMsaColumn.test.ts +8 -6
- package/src/utils/blastCache.ts +20 -23
- package/src/utils/domainCache.ts +14 -18
- package/src/utils/idb.ts +28 -0
- package/src/utils/taxonomyNames.ts +22 -24
- package/src/utils/useLocalStorage.ts +31 -0
- package/src/version.ts +1 -1
- package/dist/MsaViewPanel/blosum62.d.ts +0 -2
- package/dist/MsaViewPanel/blosum62.js +0 -627
- package/src/MsaViewPanel/blosum62.ts +0 -628
|
@@ -29,6 +29,5 @@ export declare function saveBlastResult({ proteinSequence, blastDatabase, blastP
|
|
|
29
29
|
transcriptName?: string;
|
|
30
30
|
geneName?: string;
|
|
31
31
|
}): Promise<CachedBlastResult>;
|
|
32
|
-
export declare function getAllCachedResults(): Promise<
|
|
32
|
+
export declare function getAllCachedResults(): Promise<CachedBlastResult[]>;
|
|
33
33
|
export declare function deleteCachedResult(id: string): Promise<void>;
|
|
34
|
-
export declare function clearAllCachedResults(): Promise<void>;
|
package/dist/utils/blastCache.js
CHANGED
|
@@ -1,24 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createDbOpener } from './idb';
|
|
2
2
|
const DB_NAME = 'jbrowse-msaview-blast-cache';
|
|
3
3
|
const STORE_NAME = 'blast-results';
|
|
4
4
|
const DB_VERSION = 2;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
db.createObjectStore(STORE_NAME, { keyPath: 'id' });
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
}).catch((e) => {
|
|
17
|
-
dbPromise = undefined;
|
|
18
|
-
throw e;
|
|
19
|
-
});
|
|
20
|
-
return dbPromise;
|
|
21
|
-
}
|
|
5
|
+
const getDB = createDbOpener(DB_NAME, DB_VERSION, (db, oldVersion) => {
|
|
6
|
+
if (oldVersion < 2 && db.objectStoreNames.contains(STORE_NAME)) {
|
|
7
|
+
db.deleteObjectStore(STORE_NAME);
|
|
8
|
+
}
|
|
9
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
10
|
+
db.createObjectStore(STORE_NAME, { keyPath: 'id' });
|
|
11
|
+
}
|
|
12
|
+
});
|
|
22
13
|
function createCacheKey(proteinSequence, blastDatabase, blastProgram, msaAlgorithm, transcriptId) {
|
|
23
14
|
const idPart = transcriptId ? `:${transcriptId}` : '';
|
|
24
15
|
// msaAlgorithm is part of the key because the stored msa/tree are produced by
|
|
@@ -57,7 +48,3 @@ export async function deleteCachedResult(id) {
|
|
|
57
48
|
const db = await getDB();
|
|
58
49
|
await db.delete(STORE_NAME, id);
|
|
59
50
|
}
|
|
60
|
-
export async function clearAllCachedResults() {
|
|
61
|
-
const db = await getDB();
|
|
62
|
-
await db.clear(STORE_NAME);
|
|
63
|
-
}
|
|
@@ -1,21 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createDbOpener } from './idb';
|
|
2
2
|
const DB_NAME = 'jbrowse-msaview-domain-cache';
|
|
3
3
|
const STORE_NAME = 'domains';
|
|
4
4
|
const DB_VERSION = 1;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
db.createObjectStore(STORE_NAME, { keyPath: 'accession' });
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
}).catch((e) => {
|
|
14
|
-
dbPromise = undefined;
|
|
15
|
-
throw e;
|
|
16
|
-
});
|
|
17
|
-
return dbPromise;
|
|
18
|
-
}
|
|
5
|
+
const getDB = createDbOpener(DB_NAME, DB_VERSION, db => {
|
|
6
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
7
|
+
db.createObjectStore(STORE_NAME, { keyPath: 'accession' });
|
|
8
|
+
}
|
|
9
|
+
});
|
|
19
10
|
export async function getCachedDomains(accessions) {
|
|
20
11
|
const db = await getDB();
|
|
21
12
|
const tx = db.transaction(STORE_NAME, 'readonly');
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DBSchema, IDBPDatabase, OpenDBCallbacks } from 'idb';
|
|
2
|
+
/**
|
|
3
|
+
* Memoized `openDB` for a typed schema, shared by this plugin's caches.
|
|
4
|
+
*
|
|
5
|
+
* The connection promise is cached so callers share one connection, and dropped
|
|
6
|
+
* again if the open fails, so a later call retries instead of replaying the same
|
|
7
|
+
* rejection forever (IndexedDB is unavailable in some private-browsing modes).
|
|
8
|
+
*
|
|
9
|
+
* Passing a DBSchema is what keeps `get`/`getAll` from returning `any`: with an
|
|
10
|
+
* untyped database every cached record reaches the UI unchecked.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createDbOpener<T extends DBSchema>(name: string, version: number, upgrade: OpenDBCallbacks<T>['upgrade']): () => Promise<IDBPDatabase<T>>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { openDB } from 'idb';
|
|
2
|
+
/**
|
|
3
|
+
* Memoized `openDB` for a typed schema, shared by this plugin's caches.
|
|
4
|
+
*
|
|
5
|
+
* The connection promise is cached so callers share one connection, and dropped
|
|
6
|
+
* again if the open fails, so a later call retries instead of replaying the same
|
|
7
|
+
* rejection forever (IndexedDB is unavailable in some private-browsing modes).
|
|
8
|
+
*
|
|
9
|
+
* Passing a DBSchema is what keeps `get`/`getAll` from returning `any`: with an
|
|
10
|
+
* untyped database every cached record reaches the UI unchecked.
|
|
11
|
+
*/
|
|
12
|
+
export function createDbOpener(name, version, upgrade) {
|
|
13
|
+
let dbPromise;
|
|
14
|
+
return () => {
|
|
15
|
+
dbPromise ??= openDB(name, version, { upgrade }).catch((e) => {
|
|
16
|
+
dbPromise = undefined;
|
|
17
|
+
throw e;
|
|
18
|
+
});
|
|
19
|
+
return dbPromise;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -1,23 +1,15 @@
|
|
|
1
|
-
import { openDB } from 'idb';
|
|
2
1
|
import { efetchUrl } from './eutils';
|
|
2
|
+
import { textfetch } from './fetch';
|
|
3
|
+
import { createDbOpener } from './idb';
|
|
3
4
|
const DB_NAME = 'jbrowse-msaview-taxonomy-cache';
|
|
4
5
|
const STORE_NAME = 'common-names';
|
|
5
6
|
const DB_VERSION = 2;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
db.createObjectStore(STORE_NAME, { keyPath: 'taxid' });
|
|
14
|
-
},
|
|
15
|
-
}).catch((e) => {
|
|
16
|
-
dbPromise = undefined;
|
|
17
|
-
throw e;
|
|
18
|
-
});
|
|
19
|
-
return dbPromise;
|
|
20
|
-
}
|
|
7
|
+
const getDB = createDbOpener(DB_NAME, DB_VERSION, db => {
|
|
8
|
+
if (db.objectStoreNames.contains(STORE_NAME)) {
|
|
9
|
+
db.deleteObjectStore(STORE_NAME);
|
|
10
|
+
}
|
|
11
|
+
db.createObjectStore(STORE_NAME, { keyPath: 'taxid' });
|
|
12
|
+
});
|
|
21
13
|
async function getCachedTaxonomies(taxids) {
|
|
22
14
|
const db = await getDB();
|
|
23
15
|
const tx = db.transaction(STORE_NAME, 'readonly');
|
|
@@ -59,8 +51,11 @@ export async function fetchTaxonomyInfo(taxids) {
|
|
|
59
51
|
const batch = uncachedTaxids.slice(i, i + batchSize);
|
|
60
52
|
const idsParam = batch.join(',');
|
|
61
53
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
// textfetch rather than a bare fetch: an NCBI 429/5xx returns an HTML
|
|
55
|
+
// error body that the regexes below silently find nothing in, so without
|
|
56
|
+
// the status check a throttled batch looks like "these taxa have no
|
|
57
|
+
// names" instead of reporting why
|
|
58
|
+
const text = await textfetch(efetchUrl({ db: 'taxonomy', id: idsParam, retmode: 'xml' }));
|
|
64
59
|
// Build a map of taxid -> taxon block by finding Taxon elements.
|
|
65
60
|
// Prefer entries with <LineageEx> (full top-level entries) over nested
|
|
66
61
|
// entries inside another taxon's LineageEx
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useLocalStorage<T>(key: string, initialValue: T): readonly [T, (value: T) => void];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
// Vendored rather than imported from `@jbrowse/core/util`: that barrel is
|
|
3
|
+
// host-provided, and a barrel split dropped this export, making the BLAST panel
|
|
4
|
+
// throw "(0, PR.useLocalStorage) is not a function" on hosts built during that
|
|
5
|
+
// window. Same failure mode as `defaultCodonTable`; keeping our own copy takes
|
|
6
|
+
// this plugin out of the whack-a-mole.
|
|
7
|
+
function readLocalStorage(key, initialValue) {
|
|
8
|
+
try {
|
|
9
|
+
const item = globalThis.localStorage.getItem(key);
|
|
10
|
+
return item === null ? initialValue : JSON.parse(item);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
console.error(error);
|
|
14
|
+
return initialValue;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function useLocalStorage(key, initialValue) {
|
|
18
|
+
const [storedValue, setStoredValue] = useState(() => readLocalStorage(key, initialValue));
|
|
19
|
+
const setValue = (value) => {
|
|
20
|
+
setStoredValue(value);
|
|
21
|
+
try {
|
|
22
|
+
globalThis.localStorage.setItem(key, JSON.stringify(value));
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
console.error(error);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
return [storedValue, setValue];
|
|
29
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "2.7.
|
|
1
|
+
export declare const version = "2.7.3";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '2.7.
|
|
1
|
+
export const version = '2.7.3';
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.7.
|
|
2
|
+
"version": "2.7.3",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"name": "jbrowse-plugin-msaview",
|
|
5
5
|
"repository": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"puppeteer": "^25.3.0",
|
|
52
52
|
"react": "^19.2.8",
|
|
53
53
|
"react-dom": "^19.2.8",
|
|
54
|
-
"react-msaview": "^5.
|
|
54
|
+
"react-msaview": "^5.7.0",
|
|
55
55
|
"rimraf": "^6.1.3",
|
|
56
56
|
"rxjs": "^7.8.2",
|
|
57
57
|
"serve": "^14.2.6",
|
|
@@ -74,7 +74,9 @@
|
|
|
74
74
|
"test:setup:version": "node scripts/test-versions.mjs setup",
|
|
75
75
|
"test:versions": "node scripts/test-versions.mjs run",
|
|
76
76
|
"test:version": "node scripts/test-versions.mjs run",
|
|
77
|
-
"
|
|
77
|
+
"host-compat": "node scripts/host-compat-probe.mjs --bundle dist/jbrowse-plugin-msaview.umd.production.min.js",
|
|
78
|
+
"check-ci": "node scripts/require-green-ci.mjs",
|
|
79
|
+
"preversion": "pnpm check-ci && pnpm lint && pnpm build && pnpm host-compat",
|
|
78
80
|
"version": "node -e \"console.log('export const version = \\'' + require('./package.json').version + '\\'')\" > src/version.ts && git add src/version.ts",
|
|
79
81
|
"postversion": "git push --follow-tags"
|
|
80
82
|
}
|
|
@@ -23,14 +23,12 @@ const MsaToGenomeHighlight = observer(function MsaToGenomeHighlight2({
|
|
|
23
23
|
// The persistent click selection always shows. The hover codon is suppressed
|
|
24
24
|
// while hovering the LGV — GenomeMouseoverHighlight handles the single-bp
|
|
25
25
|
// display in that case, so we don't stack a wider codon band on top of it.
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
r !== undefined,
|
|
33
|
-
)
|
|
26
|
+
const highlights = [
|
|
27
|
+
...(msaView?.connectedClickHighlights ?? []),
|
|
28
|
+
...(hasHoverPosition(hovered)
|
|
29
|
+
? []
|
|
30
|
+
: (msaView?.connectedHoverHighlights ?? [])),
|
|
31
|
+
]
|
|
34
32
|
|
|
35
33
|
return highlights.length ? (
|
|
36
34
|
<MsaToGenomeHighlightRenderer model={model} highlights={highlights} />
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
|
|
3
|
-
import { useLocalStorage } from '@jbrowse/core/util'
|
|
4
3
|
import SettingsIcon from '@mui/icons-material/Settings'
|
|
5
4
|
import { IconButton } from '@mui/material'
|
|
6
5
|
import { makeStyles } from 'tss-react/mui'
|
|
@@ -11,6 +10,7 @@ import NCBIBlastMethodSelector from './NCBIBlastMethodSelector'
|
|
|
11
10
|
import NCBIBlastRIDPanel from './NCBIBlastRIDPanel'
|
|
12
11
|
import NCBISettingsDialog from './NCBISettingsDialog'
|
|
13
12
|
import { BASE_BLAST_URL } from './consts'
|
|
13
|
+
import { useLocalStorage } from '../../../utils/useLocalStorage'
|
|
14
14
|
|
|
15
15
|
import type { AbstractTrackModel, Feature } from '@jbrowse/core/util'
|
|
16
16
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import useSWR from 'swr'
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
clearAllCachedResults,
|
|
5
4
|
deleteCachedResult,
|
|
6
5
|
getAllCachedResults,
|
|
7
6
|
} from '../../../utils/blastCache'
|
|
@@ -30,8 +29,11 @@ export function useCachedBlastResults(geneIds: string[]) {
|
|
|
30
29
|
)
|
|
31
30
|
}
|
|
32
31
|
|
|
32
|
+
// deletes only what this hook listed, i.e. the results for these gene ids.
|
|
33
|
+
// The list the user is looking at is gene-scoped, so a store-wide clear here
|
|
34
|
+
// would silently throw away every other gene's cached alignments too
|
|
33
35
|
const handleClearAll = async () => {
|
|
34
|
-
await
|
|
36
|
+
await Promise.all((results ?? []).map(r => deleteCachedResult(r.id)))
|
|
35
37
|
await mutate([], false)
|
|
36
38
|
}
|
|
37
39
|
|
|
@@ -1,35 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
revcom,
|
|
6
|
-
} from '@jbrowse/core/util'
|
|
1
|
+
import { dedupe, revcom } from '@jbrowse/core/util'
|
|
2
|
+
import { convertCodingSequenceToPeptides } from '@jbrowse/core/util/convertCodingSequenceToPeptides'
|
|
3
|
+
|
|
4
|
+
import { getGeneticCode, parseTranslTable } from './geneticCodes'
|
|
7
5
|
|
|
8
6
|
import type { Feat } from './types'
|
|
9
7
|
import type { Feature } from '@jbrowse/core/util'
|
|
10
8
|
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
// `@jbrowse/core/util/convertCodingSequenceToPeptides` is a deep path, so unlike
|
|
10
|
+
// the `@jbrowse/core/util` barrel it is absent from ReExports and gets bundled
|
|
11
|
+
// rather than resolved out of the host's JBrowseExports. That is what makes
|
|
12
|
+
// reusing core's translation safe across every host a config names: this module
|
|
13
|
+
// previously built its codon table at module scope from the barrel's
|
|
14
|
+
// `defaultCodonTable`, and a core build that dropped that export turned it into
|
|
15
|
+
// `Object.keys(undefined)` while the UMD was still evaluating -- the plugin
|
|
16
|
+
// global was never assigned and PluginLoader error-paged the whole app.
|
|
18
17
|
|
|
19
18
|
export function calculateProteinSequence({
|
|
20
19
|
cds,
|
|
21
20
|
sequence,
|
|
21
|
+
geneticCodeId,
|
|
22
22
|
}: {
|
|
23
23
|
cds: Feat[]
|
|
24
24
|
sequence: string
|
|
25
|
+
geneticCodeId?: number
|
|
25
26
|
}) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
// `starts` is deliberately not passed: @jbrowse/core 4.3.0's signature has no
|
|
28
|
+
// such parameter, so alternative initiators (GTG under table 11, ATA under
|
|
29
|
+
// table 2) render as their internal residue rather than M. Core main added it;
|
|
30
|
+
// pass it here when msaview's @jbrowse/core floor reaches that release.
|
|
31
|
+
const { codonTable } = getGeneticCode(geneticCodeId)
|
|
32
|
+
return convertCodingSequenceToPeptides({
|
|
33
|
+
cds,
|
|
34
|
+
sequence,
|
|
35
|
+
codonTable,
|
|
36
|
+
})
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
export function revlist(list: Feat[], seqlen: number) {
|
|
@@ -62,8 +66,19 @@ export function getProteinSequenceFromFeature({
|
|
|
62
66
|
feat => `${feat.start}-${feat.end}`,
|
|
63
67
|
)
|
|
64
68
|
|
|
69
|
+
// a mitochondrial gene declares e.g. transl_table=2, so it translates with
|
|
70
|
+
// NCBI table 2 rather than the standard code. GFF3 usually carries the
|
|
71
|
+
// attribute on the CDS rather than the transcript, so check both.
|
|
72
|
+
const cdsSubfeature = feature
|
|
73
|
+
.get('subfeatures')
|
|
74
|
+
?.find((f: Feature) => f.get('type')?.toLowerCase() === 'cds')
|
|
75
|
+
const geneticCodeId =
|
|
76
|
+
parseTranslTable(feature.get('transl_table')) ??
|
|
77
|
+
parseTranslTable(cdsSubfeature?.get('transl_table'))
|
|
78
|
+
|
|
65
79
|
return calculateProteinSequence({
|
|
66
80
|
cds: strand === -1 ? revlist(cds, seq.length) : cds,
|
|
67
81
|
sequence: strand === -1 ? revcom(seq) : seq,
|
|
82
|
+
geneticCodeId,
|
|
68
83
|
})
|
|
69
84
|
}
|