npm.io
2.7.0 • Published 3d agoCLI

@gmod/gbz-base

Licence
MIT
Version
2.7.0
Deps
1
Size
655 kB
Vulns
0
Weekly
0

@gmod/gbz-base

NPM version Build Status

A pure TypeScript reader for gbz-base pangenome databases (.gbz.db).

Install

npm install @gmod/gbz-base

Usage

import { RemoteFile } from 'generic-filehandle2'
import { GBZBase } from '@gmod/gbz-base'

const db = await GBZBase.open(
  new RemoteFile('https://example.org/graph.gbz.db'),
)

// one record per haplotype crossing the window
const alignments = await db.getAlignmentsForRange(
  'GRCh38#0#chr6',
  31500000,
  31501000,
)

// the same window as a subgraph, for a pangenome view
const subgraph = await db.getSubgraphForRange(
  'GRCh38#0#chr6',
  31500000,
  31501000,
)
const gfa = await subgraph?.toGFA({ names: 'resolved' })

Coordinates are 0-based half-open, and are offsets along the path you named, so ('GRCh38#0#chr6', 31500000, 31501000) is the same window gbz-base query --interval 31500000..31501000 gives. The path is a PanSN sample#haplotype#contig string, or a bare contig for a graph whose reference paths have no sample.

GBZBase reads only the SQLite pages a query touches, so it queries a multi-gigabyte database on an HTTP server through range requests without downloading it. Any generic-filehandle2 source works — LocalFile, RemoteFile, BlobFile — and there is a gbz-base-query command line that mirrors the upstream tool. Every option, method and query function: docs/api.md; what a query does between the call and the records, drawn: docs/dataflow.md.

Alignment records

for (const alignment of alignments) {
  const { refStart, refEnd, strand, cigar } = alignment
  if (alignment.resolved) {
    console.log(alignment.label, alignment.hapStart, alignment.hapEnd)
  }
}

A record is one haplotype's passage through the window, aligned to the reference path you queried. Where a haplotype leaves the subgraph and comes back — a private insertion, a bubble the window does not hold — the pieces are joined back into one record and the stretch between them becomes its insertion and deletion, so context changes what is read rather than how many records you get. The fields, the joining rules and what resolved means: docs/alignments.md.

subgraph.pairAlignments aligns one haplotype to another with the bases compared, so two haplotypes sharing sequence the reference lacks align through it, and gbz-base-query --stack prints each row of a stacked synteny view against the next as PAF: docs/alignments.md.

getAlignmentsForRange hands back data and spans path fragments; getSubgraphForRange hands back the Subgraph itself, because two disjoint fragments do not merge into one graph.

Naming haplotypes

Upstream gbz-base cannot determine which haplotype a subgraph path belongs to, so it emits unknown#N. This package adds that with two side tables that a small Rust tool (tools/haplotype-index/) writes into an existing database, or into a standalone companion beside a database someone else hosts:

const db = await GBZBase.open(new RemoteFile(graphUrl), {
  haplotypeIndex: new RemoteFile(indexUrl),
})

With names in hand, keep cuts a window to a chosen set of haplotypes — the reference walk, the walks the predicate accepts and the nodes those walks visit, so the drawing shows that set's private sequence and nothing else's. On a companion carrying anchors those haplotypes are walked from an anchor before the window, and nothing else is extracted or named, so the query's cost scales with the set rather than the graph: the eight haplotypes of the MHC class II tutorial window come back in 0.97 s against 9.16 s for all 464, both warm. docs/haplotype-index.md covers building the index and the two routes; docs/performance.md has the measurements.

Snarls

The snarls option uses the top-level chains a .gbz.db already stores, the way upstream's --snarls and --extend-snarls do — contained brings a window's variation back without widening it by a bp radius. docs/snarls.md.

Why not compile the Rust to wasm

gbwt-rs and simple-sds serialize usize at native width, so a wasm32 build misreads every file written on a 64-bit host, and the PR to fix that was closed unmerged — reasonably, since the same hazard sits on every other usize in the crate. wasm64 fixes the width but cannot carry gbz-base, whose bundled SQLite has no wasm64 libc. A TypeScript reader has neither problem, and it supports HTTP range access and runs in a JBrowse RPC worker without extra code for either: docs/why-not-wasm.md.

In JBrowse

jbrowse-plugin-graphgenomeviewer is the consumer this was written for. Its GbzBaseSyntenyAdapter opens a .gbz.db and its companion through JBrowse's own file access layer, and calls getSubgraphForRange for the graph view and getAlignmentsForRange for the haplotype lanes — in an RPC worker, like any other adapter.

Because the worker is where the subgraph is built and the main thread is where it is drawn, the representation it crosses in matters more than the representation it is built in. toSubgraphJson returns upstream's format, which uses an object and a stringified id for each of a human window's ~350,000 steps and costs about 295 ms to structured-clone; toCompactSubgraph returns the same subgraph as typed arrays of GBWT handles, which clones in 1.9 ms and can be transferred instead of copied. docs/api.md has both formats and the measurements.

Docs

Footnote

Started from ideas at MemPanG 26!

License

MIT Colin Diesh

Keywords