@jbrowse/bandage-core 0.1.20 → 4.0.25

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.
@@ -1,38 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- cutWindowGFA,
3
+ HPRC_GBZ,
4
+ cutGbzRegion,
5
+ featuresOnBackbone,
4
6
  figureSvg,
5
7
  forceLayout,
6
- haplotypeWanted,
8
+ genesFromBed,
9
+ genesFromGff3Lines,
10
+ genesFromText,
11
+ graphBackbone,
7
12
  layoutModeByValue,
8
13
  loadBandage,
9
14
  loadGraph,
10
- referencePathQuery,
11
- referenceSamplesOf,
12
- resolveReferenceSample
13
- } from "../chunks/chunk-3XDR3ZBS.js";
15
+ openGbz,
16
+ parseRegion
17
+ } from "../chunks/chunk-B5ZHLU4N.js";
14
18
 
15
19
  // src/cli/figure.ts
16
20
  import { open, readFile, writeFile } from "node:fs/promises";
17
21
  import path from "node:path";
18
22
  import { parseArgs } from "node:util";
19
23
  import { gunzipSync } from "node:zlib";
20
- import { GBZBase } from "@gmod/gbz-base";
21
- var HPRC = {
22
- db: "https://s3-us-west-2.amazonaws.com/human-pangenomics/pangenomes/freeze/release2/minigraph-cactus/v2.1/hprc-v2.1-mc-grch38/hprc-v2.1-mc-grch38.gbz.db",
23
- index: "https://jbrowse.org/demos/hprc/hprc-v2.1-mc-grch38.haplotype-index.anchored.db"
24
- };
25
- var WALK_LIMIT = 1e5;
26
- function parseRegion(text) {
27
- const m = /^\s*([^:\s]+):([\d,]+)-([\d,]+)\s*$/.exec(text);
28
- if (!m) {
29
- throw new Error(
30
- `"${text}" is not a region like chr6:160,614,798-160,647,758`
31
- );
32
- }
33
- const n = (s) => Number(s.replaceAll(",", ""));
34
- return { refName: m[1], start: n(m[2]), end: n(m[3]) };
35
- }
24
+ import { TabixIndexedFile } from "@gmod/tabix";
36
25
  var isUrl = (s) => /^https?:\/\//.test(s);
37
26
  function byteSource(location) {
38
27
  if (isUrl(location)) {
@@ -64,36 +53,40 @@ function byteSource(location) {
64
53
  }
65
54
  };
66
55
  }
67
- async function gfaText(location) {
56
+ async function text(location) {
68
57
  const bytes = isUrl(location) ? new Uint8Array(await (await fetch(location)).arrayBuffer()) : await readFile(location);
69
58
  const gzipped = bytes[0] === 31 && bytes[1] === 139;
70
59
  return new TextDecoder().decode(gzipped ? gunzipSync(bytes) : bytes);
71
60
  }
72
- async function cutGbz(gbz) {
73
- const { db, index } = gbz.db === "hprc" ? HPRC : gbz;
74
- const base = await GBZBase.open(
75
- byteSource(db),
76
- index ? { haplotypeIndex: byteSource(index) } : {}
61
+ async function readGenes(genes, graph, resolve) {
62
+ const backbone = graphBackbone(graph);
63
+ if (!backbone) {
64
+ return [];
65
+ }
66
+ const file = resolve(genes.file);
67
+ const format = genes.format ?? (/\.bed(\.gz)?$/i.test(genes.file) ? "bed" : "gff3");
68
+ if (!genes.index) {
69
+ return featuresOnBackbone(genesFromText(await text(file)), backbone);
70
+ }
71
+ const index = resolve(genes.index);
72
+ const csi = index.endsWith(".csi");
73
+ const tabix = new TabixIndexedFile(
74
+ isUrl(file) ? { url: file, ...csi ? { csiUrl: index } : { tbiUrl: index } } : { path: file, ...csi ? { csiPath: index } : { tbiPath: index } }
77
75
  );
78
- const samples = await referenceSamplesOf(base);
79
- const sample = resolveReferenceSample({
80
- configured: "",
81
- anchorPrefix: gbz.referenceSample ?? samples[0] ?? "",
82
- referenceSamples: samples
83
- });
84
- const region = parseRegion(gbz.region);
85
- const query = await referencePathQuery(base, sample, region.refName);
86
- if (!query) {
87
- throw new Error(`${sample} has no indexed path named ${region.refName}`);
76
+ const indexed = new Set(await tabix.getReferenceSequenceNames());
77
+ const lines = [];
78
+ for (const contig of backbone.contigs) {
79
+ const refName = [contig.contig, contig.refName].find((n) => indexed.has(n));
80
+ if (refName !== void 0) {
81
+ await tabix.getLines(refName, contig.start, contig.end, {
82
+ lineCallback: (line) => lines.push(line)
83
+ });
84
+ }
88
85
  }
89
- const wanted = gbz.haplotypes?.length ? gbz.haplotypes : void 0;
90
- const text = await cutWindowGFA(base, query, region.start, region.end, {
91
- context: 1e3,
92
- snarls: "contained",
93
- limit: WALK_LIMIT,
94
- ...wanted ? { keep: (name) => haplotypeWanted(name, wanted) } : {}
95
- });
96
- return { text, region, name: `${sample} ${gbz.region}` };
86
+ return featuresOnBackbone(
87
+ format === "gff3" ? genesFromGff3Lines(lines) : genesFromBed(lines.join("\n")),
88
+ backbone
89
+ );
97
90
  }
98
91
  var engine = async (request) => {
99
92
  const bandage = await loadBandage();
@@ -101,15 +94,26 @@ var engine = async (request) => {
101
94
  const result = bandage.computeLayout(request.graph, request.options);
102
95
  return { result, duration: performance.now() - start };
103
96
  };
104
- async function renderSpec(spec, base = ".") {
105
- const source = spec.gbz ? await cutGbz(spec.gbz) : spec.gfa ? {
106
- text: await gfaText(
107
- isUrl(spec.gfa) ? spec.gfa : path.resolve(base, spec.gfa)
108
- ),
109
- region: spec.region ? parseRegion(spec.region) : void 0,
110
- name: path.basename(spec.gfa)
111
- } : void 0;
112
- if (!source) {
97
+ async function renderSpec(spec, base) {
98
+ const resolve = (location) => isUrl(location) ? location : path.resolve(base, location);
99
+ let source;
100
+ if (spec.gbz) {
101
+ const { db, index } = spec.gbz.db === "hprc" ? HPRC_GBZ : spec.gbz;
102
+ const cut = await cutGbzRegion(
103
+ await openGbz(
104
+ byteSource(resolve(db)),
105
+ index ? byteSource(resolve(index)) : void 0
106
+ ),
107
+ spec.gbz
108
+ );
109
+ source = { ...cut, name: `${cut.sample} ${spec.gbz.region}` };
110
+ } else if (spec.gfa) {
111
+ source = {
112
+ text: await text(resolve(spec.gfa)),
113
+ name: path.basename(spec.gfa),
114
+ region: spec.region ? parseRegion(spec.region) : void 0
115
+ };
116
+ } else {
113
117
  throw new Error("a spec names its graph as `gfa` or `gbz`");
114
118
  }
115
119
  const graph = loadGraph(source.text, source.name, {
@@ -128,7 +132,8 @@ async function renderSpec(spec, base = ".") {
128
132
  ...spec,
129
133
  walks: spec.walks?.map((w) => typeof w === "string" ? { walk: w } : w),
130
134
  region: source.region,
131
- metadata: JSON.stringify(spec)
135
+ genes: spec.genes ? await readGenes(spec.genes, graph, resolve) : void 0,
136
+ spec
132
137
  });
133
138
  }
134
139
  async function main() {