import { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAdapter' import { ObservableCreate } from '@jbrowse/core/util/rxjs' import { getSnapshot } from '@jbrowse/mobx-state-tree' import MafFeature from '../MafFeature' import { subscribeToObservable } from '../util/observableUtils' import { matchSampleId, parseAssemblyAndChrSimple, } from '../util/parseAssemblyName' import { getSamplesFromConfig } from '../util/getSamples' import type { AlignmentRecord, MafAdapterOptions } from '../types' import type { Feature, Region } from '@jbrowse/core/util' export default class BigMafAdapter extends BaseFeatureDataAdapter { public setupP?: Promise<{ adapter: BaseFeatureDataAdapter }> async setup() { if (!this.getSubAdapter) { throw new Error('no getSubAdapter available') } return { adapter: ( await this.getSubAdapter({ ...getSnapshot(this.config), type: 'BigBedAdapter', }) ).dataAdapter as BaseFeatureDataAdapter, } } async setupPre() { this.setupP ??= this.setup().catch((e: unknown) => { this.setupP = undefined throw e }) return this.setupP } async getRefNames() { const { adapter } = await this.setupPre() return adapter.getRefNames() } async getHeader() { const { adapter } = await this.setupPre() return adapter.getHeader() } getFeatures(query: Region, opts?: MafAdapterOptions) { const WHITESPACE_REGEX = / +/ return ObservableCreate(async observer => { const { adapter } = await this.setupPre() const sampleIds = opts?.samples ? new Set(opts.samples.map(s => s.id)) : undefined await subscribeToObservable(adapter.getFeatures(query, opts), feature => { const maf = feature.get('mafBlock') as string const blocks = maf.split(';') const alignments = {} as Record let referenceSeq: string | undefined for (const block of blocks) { if (block.startsWith('s')) { const parts = block.split(WHITESPACE_REGEX) const sequence = parts[6]! const organismChr = parts[1]! if (referenceSeq === undefined) { referenceSeq = sequence } // Known set → resolve the token against it so haplotype-suffixed // names (`Species1.1`) match exactly. No set → dot-position split. const parsed = sampleIds ? matchSampleId(organismChr, sampleIds) : parseAssemblyAndChrSimple(organismChr) if (parsed?.assemblyName) { alignments[parsed.assemblyName] = { chr: parsed.chr, start: +parts[2]!, srcSize: +parts[3]!, strand: parts[4] === '+' ? 1 : -1, unknown: +parts[5]!, seq: sequence, } } } } observer.next( new MafFeature( feature.id(), feature.get('start'), feature.get('end'), feature.get('refName'), 0, // strand not in BigMaf format alignments, referenceSeq ?? '', ), ) }) observer.complete() }, opts?.stopToken) } async getSamples(_query: Region) { return getSamplesFromConfig(this.getConf.bind(this)) } freeResources(): void {} }