@jbrowse/graph-walks 0.0.0-stage → 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Colin Diesh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,94 @@
1
- # Temporary Holding Version
1
+ # @jbrowse/graph-walks
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Cuts a window out of a pangenome graph stored as
4
+ [gfa-to-tabix](https://github.com/GMOD/gfa-to-tabix) walk files, by range
5
+ requests.
6
+
7
+ - **In:** a `gfa-to-tabix --walks` file, `<prefix>.<sample>.walks.bed.gz` with
8
+ its `.tbi`, and a region on its reference
9
+ - **Out:** the reference over the region, every haplotype's walk through it, and
10
+ the nodes and links they visit, as typed arrays
11
+ - Reads walk format 2, which gfa-to-tabix writes from 1.0. The reader refuses a
12
+ format 1 file, from gfa-to-tabix 0.12 or earlier, with a `WalkFormatError`
13
+ asking for a rebuild
14
+ - Has no runtime dependencies: it reads through any object with
15
+ [@gmod/tabix](https://github.com/GMOD/tabix-js)'s `TabixIndexedFile` methods
16
+
17
+ The JBrowse graph plugin, BandageJS and the sequenceTubeMap fork read walk files
18
+ through it.
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ npm install @jbrowse/graph-walks @gmod/tabix
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ```ts
29
+ import { TabixIndexedFile } from '@gmod/tabix'
30
+ import {
31
+ WalkReader,
32
+ panSNHaplotype,
33
+ walkNameFilter,
34
+ } from '@jbrowse/graph-walks'
35
+ import { RemoteFile } from 'generic-filehandle2'
36
+
37
+ const url = 'https://example.org/hprc-v2.1-mc-grch38.GRCh38.walks.bed.gz'
38
+ const reader = new WalkReader(
39
+ new TabixIndexedFile({
40
+ filehandle: new RemoteFile(url),
41
+ tbiFilehandle: new RemoteFile(`${url}.tbi`),
42
+ }),
43
+ )
44
+
45
+ const refName = 'GRCh38#0#chr6'
46
+ const { graph, kept, fragments } = await reader.cut(
47
+ refName,
48
+ { start: 160_614_798, end: 160_647_758 },
49
+ { keep: walkNameFilter(['HG00097', 'HG00133'], panSNHaplotype(refName)) },
50
+ )
51
+ const tables = graph.tables(kept, fragments)
52
+ ```
53
+
54
+ - `tables` lists the nodes with their lengths and reference positions, the links
55
+ between them, and each walk's steps. `graphTablesGFA` in
56
+ [@jbrowse/bandage-core](https://github.com/GMOD/bandage-core) writes it as GFA
57
+ - `reader.header()` lists the reference sample and every haplotype with rows,
58
+ read from the walk file's first block
59
+ - `reader.referenceSequences()` lists the sequences a region can be on, in full
60
+ PanSN: `GRCh38#0#chr6`
61
+
62
+ ## Cut options
63
+
64
+ | Option | Effect |
65
+ | ------------ | ------------------------------------------------------------------------- |
66
+ | `keep` | Which walks to decode, by `sample#haplotype#contig`; every one by default |
67
+ | `context` | bp of reference kept past each end of the region; default 1000 |
68
+ | `stepBudget` | The most steps a cut keeps; no limit by default |
69
+ | `byteBudget` | The most compressed bytes a cut fetches; no limit by default |
70
+ | `links` | `false` lists no links, for a caller that draws no edges |
71
+ | `sequences` | Keep each node's bases in `graph.sequences`, from a `--sequences` file |
72
+ | `signal` | An `AbortSignal` that stops the reads |
73
+
74
+ A cut over a budget throws a `RegionTooLargeError`. Its `fitsBp` holds about how
75
+ wide a window would fit, and JBrowse shows it as a zoom-in notice.
76
+
77
+ ## How a cut works
78
+
79
+ - A chunk holds the reference's path through it once, each way a haplotype
80
+ leaves that path once, and every other path's piece as the list of ways it
81
+ takes; gfa-to-tabix's
82
+ [walks-format.md](https://github.com/GMOD/gfa-to-tabix/blob/main/docs/walks-format.md)
83
+ describes the rows
84
+ - A cut reads every chunk under the region, and the chunks before it as far as
85
+ the header's `maxnode` reaches, since a node is filed under the chunk holding
86
+ its start, and decodes each chunk once
87
+ - A haplotype that leaves the reference near the region is followed to where it
88
+ rejoins, and the reference is cut out as far as those rejoins reach
89
+ - A walk the read cuts off goes on in the chunk its row names (`pv:`/`nx:`
90
+ tags); the cut reads on to that chunk, in up to three rounds
91
+
92
+ ## License
93
+
94
+ MIT
package/esm/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export { BUDGET_NAMES, WalkGraph, byteBudgetError, chunkQueryStart, joinPieces, keptStepsError, lookbackChunks, stepBudgetError, walkCut, walkHeader, walkNameFilter, } from './walkRows.ts';
2
+ export type { BudgetNames, WalkChunk, WalkFragment, WalkHeader, WalkNode, WalkRow, WalkRun, WalkWindow, } from './walkRows.ts';
3
+ export { WalkFormatError, WalkReader } from './walkReader.ts';
4
+ export { referenceNodes } from './walkChunk.ts';
5
+ export type { TabixSource, WalkCutOptions, WalkReaderHeader, } from './walkReader.ts';
6
+ export { RegionTooLargeError, bpString, panSNHaplotype, panSNMatchesPrefix, } from './util.ts';
7
+ export type { GraphTables } from './util.ts';
package/esm/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { BUDGET_NAMES, WalkGraph, byteBudgetError, chunkQueryStart, joinPieces, keptStepsError, lookbackChunks, stepBudgetError, walkCut, walkHeader, walkNameFilter, } from "./walkRows.js";
2
+ export { WalkFormatError, WalkReader } from "./walkReader.js";
3
+ export { referenceNodes } from "./walkChunk.js";
4
+ export { RegionTooLargeError, bpString, panSNHaplotype, panSNMatchesPrefix, } from "./util.js";
package/esm/util.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ export interface GraphTables {
2
+ nodes: {
3
+ names: string[];
4
+ lengths: Int32Array;
5
+ refs: Int32Array;
6
+ starts: Float64Array;
7
+ ranks: Int32Array;
8
+ refNames: string[];
9
+ };
10
+ links: {
11
+ from: Int32Array;
12
+ to: Int32Array;
13
+ strands: Uint8Array;
14
+ };
15
+ walks: {
16
+ names: string[];
17
+ starts: Float64Array;
18
+ ends: Float64Array;
19
+ offsets: Int32Array;
20
+ steps: Int32Array;
21
+ reversed: Uint8Array;
22
+ };
23
+ }
24
+ /**
25
+ * A window too large to cut. `regionTooLarge` and the name are what JBrowse
26
+ * and @jbrowse/bandage-core read a zoom-in notice by; `fitsBp` is about how
27
+ * wide a window would fit.
28
+ */
29
+ export declare class RegionTooLargeError extends Error {
30
+ readonly regionTooLarge = true;
31
+ name: string;
32
+ readonly limit: number;
33
+ readonly windowBp: number;
34
+ readonly fitsBp: number;
35
+ constructor(message: string, limit: number, windowBp: number, fitsBp: number);
36
+ }
37
+ export declare function panSNHaplotype(refName: string): string | undefined;
38
+ export declare function panSNMatchesPrefix(refName: string, prefix: string | undefined): boolean;
39
+ export declare function bpString(bp: number): string;
package/esm/util.js ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * A window too large to cut. `regionTooLarge` and the name are what JBrowse
3
+ * and @jbrowse/bandage-core read a zoom-in notice by; `fitsBp` is about how
4
+ * wide a window would fit.
5
+ */
6
+ export class RegionTooLargeError extends Error {
7
+ constructor(message, limit, windowBp, fitsBp) {
8
+ super(message);
9
+ this.regionTooLarge = true;
10
+ this.name = 'NodeLimitError';
11
+ this.limit = limit;
12
+ this.windowBp = windowBp;
13
+ this.fitsBp = fitsBp;
14
+ }
15
+ }
16
+ const SEP = '#';
17
+ export function panSNHaplotype(refName) {
18
+ const parts = refName.split(SEP);
19
+ return parts.length >= 3 ? `${parts[0]}${SEP}${parts[1]}` : undefined;
20
+ }
21
+ export function panSNMatchesPrefix(refName, prefix) {
22
+ return (prefix !== undefined &&
23
+ (refName === prefix || refName.startsWith(prefix + SEP)));
24
+ }
25
+ const precise = (n) => Number.parseFloat(n.toPrecision(3));
26
+ // `1.5Kbp`, `12Mbp`, `850bp`, as JBrowse writes a span
27
+ export function bpString(bp) {
28
+ const mb = precise(bp / 1000000);
29
+ return mb >= 1
30
+ ? `${mb.toLocaleString('en-US')}Mbp`
31
+ : bp >= 1000
32
+ ? `${precise(bp / 1000).toLocaleString('en-US')}Kbp`
33
+ : `${Math.floor(bp)}bp`;
34
+ }
@@ -0,0 +1,65 @@
1
+ import type { WalkRow } from './walkRows.ts';
2
+ export interface Route {
3
+ entry: number;
4
+ interior: number[];
5
+ exit: number;
6
+ }
7
+ export interface PlacedNode {
8
+ refName: string;
9
+ start: number;
10
+ end: number;
11
+ rank: number;
12
+ }
13
+ export declare function canonicalLink(a: number, b: number): [number, number];
14
+ export declare class RefPath {
15
+ readonly handles: number[];
16
+ readonly before: number[];
17
+ readonly occurrences: Map<number, number[]>;
18
+ add(handles: number[], lengths: number[]): void;
19
+ bp(i: number): number;
20
+ first(handle: number): number | undefined;
21
+ nearest(handle: number, i: number): number | undefined;
22
+ reach(i: number, h: number, target: number): number | undefined;
23
+ handleAt(i: number, forward: boolean): number;
24
+ }
25
+ export declare class WalkRowError extends Error {
26
+ }
27
+ export interface Chunk {
28
+ path: RefPath;
29
+ routes: Route[];
30
+ walks: {
31
+ cols: string[];
32
+ choices: number[];
33
+ }[];
34
+ lengths: Map<number, number>;
35
+ }
36
+ export interface DecodedChunk {
37
+ sequences: Map<number, string>;
38
+ rows: WalkRow[];
39
+ nodes: Map<number, PlacedNode>;
40
+ ends: {
41
+ name: string;
42
+ fragStart: number;
43
+ piece: number;
44
+ first: number;
45
+ last: number;
46
+ }[];
47
+ chunk: Chunk;
48
+ }
49
+ /**
50
+ * Decodes one chunk's rows, each split on tabs. `keep` names the walk rows
51
+ * whose steps a cut wants, undefined for every one.
52
+ */
53
+ export declare function decodeChunk(rows: string[][], keep: ((name: string) => boolean) | undefined, withSequences?: boolean): DecodedChunk;
54
+ /**
55
+ * The positions of the cut's nodes off R, and the links among the cut's
56
+ * nodes, from the chunks a cut read: `kept` holds the node ids the cut draws.
57
+ * A node off R takes its position from the path whose name sorts first among
58
+ * those visiting it in the chunks read, at its earliest offset, as format 1's
59
+ * node rows had it. Links are R's and the routes', both ends in the cut.
60
+ */
61
+ export declare function placeAndLink(chunks: Chunk[], kept: Set<number>, place: (id: number, node: PlacedNode) => void, link: (a: number, b: number) => void): void;
62
+ export declare function referenceNodes(rows: string[][]): (PlacedNode & {
63
+ id: number;
64
+ })[];
65
+ export declare function pieceLinks(ends: DecodedChunk['ends']): [number, number][];