@jbrowse/graph-walks 0.0.0-stage → 0.1.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,90 @@
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 set and a region on its reference
8
+ - **Out:** the reference over the region, every haplotype's walk through it, and
9
+ the nodes and links they visit, as typed arrays
10
+ - Reads format 1 (`.walks.bed.gz` with `.nodes.bed.gz` and `.links.bed.gz`) and
11
+ format 2 (`.walks.bed.gz` alone), each with its `.tbi`
12
+ - Has no runtime dependencies: it reads through any object with
13
+ [@gmod/tabix](https://github.com/GMOD/tabix-js)'s `TabixIndexedFile` methods
14
+
15
+ The JBrowse graph plugin, BandageJS and the sequenceTubeMap fork read walk files
16
+ through it.
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ npm install @jbrowse/graph-walks @gmod/tabix
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ```ts
27
+ import { TabixIndexedFile } from '@gmod/tabix'
28
+ import {
29
+ WalkReader,
30
+ panSNHaplotype,
31
+ walkNameFilter,
32
+ } from '@jbrowse/graph-walks'
33
+ import { RemoteFile } from 'generic-filehandle2'
34
+
35
+ const file = (url: string) =>
36
+ new TabixIndexedFile({
37
+ filehandle: new RemoteFile(url),
38
+ tbiFilehandle: new RemoteFile(`${url}.tbi`),
39
+ })
40
+ const prefix = 'https://jbrowse.org/demos/hprc/hprc-v2.1-mc-grch38.GRCh38'
41
+ const reader = new WalkReader({
42
+ walks: file(`${prefix}.walks.bed.gz`),
43
+ nodes: file(`${prefix}.nodes.bed.gz`),
44
+ })
45
+
46
+ const refName = 'GRCh38#0#chr6'
47
+ const { graph, kept, fragments } = await reader.cut(
48
+ refName,
49
+ { start: 160_614_798, end: 160_647_758 },
50
+ { keep: walkNameFilter(['HG00097', 'HG00133'], panSNHaplotype(refName)) },
51
+ )
52
+ const tables = graph.tables(kept, fragments)
53
+ ```
54
+
55
+ - `tables` lists the nodes with their lengths and reference positions, the links
56
+ between them, and each walk's steps. `graphTablesGFA` in
57
+ [@jbrowse/bandage-core](https://github.com/GMOD/bandage-core) writes it as GFA
58
+ - `reader.header()` lists the reference sample and every haplotype with rows,
59
+ read from the walk file's first block
60
+ - `reader.referenceSequences()` lists the sequences a region can be on, in full
61
+ PanSN: `GRCh38#0#chr6`
62
+
63
+ ## Cut options
64
+
65
+ | Option | Effect |
66
+ | ------------ | ------------------------------------------------------------------------- |
67
+ | `keep` | Which walks to decode, by `sample#haplotype#contig`; every one by default |
68
+ | `context` | bp of reference kept past each end of the region; default 1000 |
69
+ | `stepBudget` | The most steps a cut keeps; no limit by default |
70
+ | `byteBudget` | The most compressed bytes a cut fetches; no limit by default |
71
+ | `links` | `false` leaves the link file unread |
72
+ | `sequences` | Keep each node's bases in `graph.sequences`, from `--sequences` files |
73
+ | `signal` | An `AbortSignal` that stops the reads |
74
+
75
+ A cut over a budget throws a `RegionTooLargeError`. Its `fitsBp` holds about how
76
+ wide a window would fit, and JBrowse shows it as a zoom-in notice.
77
+
78
+ ## How a cut works
79
+
80
+ - A cut reads every chunk under the region, and the chunks before it as far as
81
+ the header's `maxnode` reaches, since a node is filed under the chunk holding
82
+ its start
83
+ - A haplotype that leaves the reference near the region is followed to where it
84
+ rejoins, and the reference is cut out as far as those rejoins reach
85
+ - A walk the read cuts off goes on in the chunk its row names (`pv:`/`nx:`
86
+ tags); the cut reads on to that chunk, in up to three rounds
87
+
88
+ ## License
89
+
90
+ MIT
package/esm/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export { BUDGET_NAMES, WalkGraph, byteBudgetError, chunkQueryStart, decodeSteps, joinPieces, keptStepsError, lookbackChunks, parseWalkRow, stepBudgetError, walkCut, walkHeader, walkNameFilter, walkRowName, } from './walkRows.ts';
2
+ export type { BudgetNames, WalkChunk, WalkFragment, WalkHeader, WalkNode, WalkRow, WalkRun, WalkWindow, } from './walkRows.ts';
3
+ export { WalkReader } from './walkReader.ts';
4
+ export { referenceNodes } from './walkFormat2.ts';
5
+ export type { TabixSource, WalkCutOptions, WalkFileSet, 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, decodeSteps, joinPieces, keptStepsError, lookbackChunks, parseWalkRow, stepBudgetError, walkCut, walkHeader, walkNameFilter, walkRowName, } from "./walkRows.js";
2
+ export { WalkReader } from "./walkReader.js";
3
+ export { referenceNodes } from "./walkFormat2.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,64 @@
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 Format2Chunk {
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
+ rows: WalkRow[];
38
+ nodes: Map<number, PlacedNode>;
39
+ ends: {
40
+ name: string;
41
+ fragStart: number;
42
+ piece: number;
43
+ first: number;
44
+ last: number;
45
+ }[];
46
+ chunk: Format2Chunk;
47
+ }
48
+ /**
49
+ * Decodes one chunk's rows, each split on tabs. `keep` names the walk rows
50
+ * whose steps a cut wants, undefined for every one.
51
+ */
52
+ export declare function decodeChunk(rows: string[][], keep: ((name: string) => boolean) | undefined): DecodedChunk;
53
+ /**
54
+ * The positions of the cut's nodes off R, and the links among the cut's
55
+ * nodes, from the chunks a cut read: `kept` holds the node ids the cut draws.
56
+ * A node off R takes its position from the path whose name sorts first among
57
+ * those visiting it in the chunks read, at its earliest offset, as format 1's
58
+ * node rows had it. Links are R's and the routes', both ends in the cut.
59
+ */
60
+ export declare function placeAndLink(chunks: Format2Chunk[], kept: Set<number>, place: (id: number, node: PlacedNode) => void, link: (a: number, b: number) => void): void;
61
+ export declare function referenceNodes(rows: string[][]): (PlacedNode & {
62
+ id: number;
63
+ })[];
64
+ export declare function pieceLinks(ends: DecodedChunk['ends']): [number, number][];