@jbrowse/core
Version:
JBrowse 2 core libraries used by plugins
305 lines (304 loc) • 11.3 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = assemblyFactory;
const abortable_promise_cache_1 = __importDefault(require("@gmod/abortable-promise-cache"));
const mobx_1 = require("mobx");
const mobx_state_tree_1 = require("mobx-state-tree");
const configuration_1 = require("../configuration");
const util_1 = require("../data_adapters/util");
const util_2 = require("../util");
const QuickLRU_1 = __importDefault(require("../util/QuickLRU"));
const refNameRegex = new RegExp('[0-9A-Za-z!#$%&+./:;?@^_|~-][0-9A-Za-z!#$%&*+./:;=?@^_|~-]*');
const refNameColors = [
'rgb(153, 102, 0)',
'rgb(102, 102, 0)',
'rgb(153, 153, 30)',
'rgb(204, 0, 0)',
'rgb(255, 0, 0)',
'rgb(255, 0, 204)',
'rgb(165, 132, 132)',
'rgb(204, 122, 0)',
'rgb(178, 142, 0)',
'rgb(153, 153, 0)',
'rgb(122, 153, 0)',
'rgb(0, 165, 0)',
'rgb(53, 128, 0)',
'rgb(0, 0, 204)',
'rgb(96, 145, 242)',
'rgb(107, 142, 178)',
'rgb(0, 165, 165)',
'rgb(122, 153, 153)',
'rgb(153, 0, 204)',
'rgb(204, 51, 255)',
'rgb(173, 130, 216)',
'rgb(102, 102, 102)',
'rgb(145, 145, 145)',
'rgb(142, 142, 142)',
'rgb(142, 142, 107)',
'rgb(96, 163, 48)',
];
async function loadRefNameMap(assembly, adapterConfig, options, stopToken) {
const { sessionId } = options;
await (0, util_2.when)(() => !!(assembly.regions && assembly.refNameAliases), {
name: 'when assembly ready',
});
const refNames = (await assembly.rpcManager.call(sessionId || 'assemblyRpc', 'CoreGetRefNames', {
adapterConfig,
stopToken,
...options,
}, { timeout: 1000000 }));
const { refNameAliases } = assembly;
if (!refNameAliases) {
throw new Error(`error loading assembly ${assembly.name}'s refNameAliases`);
}
const refNameMap = Object.fromEntries(refNames.map(name => {
checkRefName(name);
return [assembly.getCanonicalRefName(name), name];
}));
return {
forwardMap: refNameMap,
reverseMap: Object.fromEntries(Object.entries(refNameMap).map(([canonicalName, adapterName]) => [
adapterName,
canonicalName,
])),
};
}
function checkRefName(refName) {
if (!refNameRegex.test(refName)) {
throw new Error(`Encountered invalid refName: "${refName}"`);
}
}
function assemblyFactory(assemblyConfigType, pluginManager) {
const adapterLoads = new abortable_promise_cache_1.default({
cache: new QuickLRU_1.default({ maxSize: 1000 }),
async fill(args, _stopToken, statusCallback) {
const { adapterConf, self, options } = args;
return loadRefNameMap(self, adapterConf, { ...options, statusCallback });
},
});
return mobx_state_tree_1.types
.model({
configuration: mobx_state_tree_1.types.safeReference(assemblyConfigType),
})
.volatile(() => ({
error: undefined,
loadingP: undefined,
volatileRegions: undefined,
refNameAliases: undefined,
cytobands: undefined,
}))
.views(self => ({
getConf(arg) {
return self.configuration ? (0, configuration_1.getConf)(self, arg) : undefined;
},
get lowerCaseRefNameAliases() {
return self.refNameAliases
? Object.fromEntries(Object.entries(self.refNameAliases).map(([key, val]) => {
return [key.toLowerCase(), val];
}))
: undefined;
},
}))
.views(self => ({
get initialized() {
self.load();
return !!self.refNameAliases;
},
get name() {
return self.getConf('name') || '';
},
get regions() {
self.load();
return self.volatileRegions;
},
get aliases() {
return self.getConf('aliases') || [];
},
get displayName() {
return self.getConf('displayName') || self.getConf('name') || '';
},
hasName(name) {
return this.allAliases.includes(name);
},
get allAliases() {
return [this.name, ...this.aliases];
},
get allRefNames() {
return !self.refNameAliases
? undefined
: Object.keys(self.refNameAliases);
},
get lowerCaseRefNames() {
return !self.lowerCaseRefNameAliases
? undefined
: Object.keys(self.lowerCaseRefNameAliases);
},
get allRefNamesWithLowerCase() {
return this.allRefNames && this.lowerCaseRefNames
? [...new Set([...this.allRefNames, ...this.lowerCaseRefNames])]
: undefined;
},
get rpcManager() {
return (0, mobx_state_tree_1.getParent)(self, 2).rpcManager;
},
get refNameColors() {
const colors = self.getConf('refNameColors') || [];
return colors.length === 0 ? refNameColors : colors;
},
}))
.views(self => ({
get refNames() {
var _a;
return (_a = self.regions) === null || _a === void 0 ? void 0 : _a.map(region => region.refName);
},
}))
.views(self => ({
getCanonicalRefName(refName) {
if (!self.refNameAliases || !self.lowerCaseRefNameAliases) {
throw new Error('aliases not loaded, we expect them to be loaded before getCanonicalRefName can be called');
}
return (self.refNameAliases[refName] || self.lowerCaseRefNameAliases[refName]);
},
getRefNameColor(refName) {
if (!self.refNames) {
return undefined;
}
const idx = self.refNames.indexOf(refName);
return idx === -1
? undefined
: self.refNameColors[idx % self.refNameColors.length];
},
isValidRefName(refName) {
if (!self.refNameAliases) {
throw new Error('isValidRefName cannot be called yet, the assembly has not finished loading');
}
return !!this.getCanonicalRefName(refName);
},
}))
.actions(self => ({
setLoaded({ regions, refNameAliases, cytobands, }) {
this.setRegions(regions);
this.setRefNameAliases(refNameAliases);
this.setCytobands(cytobands);
},
setError(e) {
self.error = e;
},
setRegions(regions) {
self.volatileRegions = regions;
},
setRefNameAliases(aliases) {
self.refNameAliases = aliases;
},
setCytobands(cytobands) {
self.cytobands = cytobands;
},
setLoadingP(p) {
self.loadingP = p;
},
load() {
if (!self.loadingP) {
self.loadingP = this.loadPre().catch((e) => {
this.setLoadingP(undefined);
this.setError(e);
});
}
return self.loadingP;
},
async loadPre() {
var _a, _b;
var _c;
const conf = self.configuration;
const refNameAliasesAdapterConf = (_a = conf === null || conf === void 0 ? void 0 : conf.refNameAliases) === null || _a === void 0 ? void 0 : _a.adapter;
const cytobandAdapterConf = (_b = conf === null || conf === void 0 ? void 0 : conf.cytobands) === null || _b === void 0 ? void 0 : _b.adapter;
const sequenceAdapterConf = conf === null || conf === void 0 ? void 0 : conf.sequence.adapter;
const assemblyName = self.name;
const regions = await getAssemblyRegions({
config: sequenceAdapterConf,
pluginManager,
});
for (const r of regions) {
checkRefName(r.refName);
}
const refNameAliases = {};
const refNameAliasCollection = await getRefNameAliases({
config: refNameAliasesAdapterConf,
pluginManager,
});
for (const { refName, aliases, override } of refNameAliasCollection) {
for (const alias of aliases) {
checkRefName(alias);
refNameAliases[alias] = refName;
}
if (override) {
refNameAliases[refName] = refName;
}
}
for (const region of regions) {
refNameAliases[_c = region.refName] || (refNameAliases[_c] = region.refName);
}
this.setLoaded({
refNameAliases,
regions: regions.map(r => ({
...r,
refName: refNameAliases[r.refName] || r.refName,
assemblyName,
})),
cytobands: await getCytobands({
config: cytobandAdapterConf,
pluginManager,
}),
});
},
}))
.views(self => ({
getAdapterMapEntry(adapterConf, options) {
const { stopToken, statusCallback, ...rest } = options;
if (!options.sessionId) {
throw new Error('sessionId is required');
}
return adapterLoads.get((0, util_1.adapterConfigCacheKey)(adapterConf), {
adapterConf,
self,
options: rest,
}, undefined, statusCallback);
},
async getRefNameMapForAdapter(adapterConf, opts) {
if (!opts.sessionId) {
throw new Error('sessionId is required');
}
const map = await this.getAdapterMapEntry(adapterConf, opts);
return map.forwardMap;
},
async getReverseRefNameMapForAdapter(adapterConf, opts) {
const map = await this.getAdapterMapEntry(adapterConf, opts);
return map.reverseMap;
},
afterCreate() {
(0, mobx_state_tree_1.addDisposer)(self, (0, mobx_1.autorun)(() => {
self.allRefNamesWithLowerCase;
}));
},
}));
}
async function getRefNameAliases({ config, pluginManager, stopToken, }) {
const type = pluginManager.getAdapterType(config.type);
const CLASS = await type.getAdapterClass();
const adapter = new CLASS(config, undefined, pluginManager);
return adapter.getRefNameAliases({ stopToken });
}
async function getCytobands({ config, pluginManager, }) {
const type = pluginManager.getAdapterType(config.type);
const CLASS = await type.getAdapterClass();
const adapter = new CLASS(config, undefined, pluginManager);
return adapter.getData();
}
async function getAssemblyRegions({ config, pluginManager, stopToken, }) {
const type = pluginManager.getAdapterType(config.type);
const CLASS = await type.getAdapterClass();
const adapter = new CLASS(config, undefined, pluginManager);
return adapter.getRegions({ stopToken });
}