@nanalogue/node 0.1.1
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 +21 -0
- package/README.md +224 -0
- package/index.d.ts +239 -0
- package/index.js +320 -0
- package/nanalogue.darwin-arm64.node +0 -0
- package/nanalogue.darwin-x64.node +0 -0
- package/nanalogue.linux-arm64-gnu.node +0 -0
- package/nanalogue.linux-arm64-musl.node +0 -0
- package/nanalogue.linux-x64-gnu.node +0 -0
- package/nanalogue.linux-x64-musl.node +0 -0
- package/package.json +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sathish Thiyagarajan
|
|
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
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# `nanalogue-node`
|
|
2
|
+
|
|
3
|
+
Node.js bindings for Nanalogue: single-molecule BAM/Mod-BAM analysis.
|
|
4
|
+
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
Nanalogue is a tool to parse and analyse BAM/Mod-BAM files with a single-molecule focus.
|
|
8
|
+
This package exposes Nanalogue's functions through a Node.js/TypeScript interface using [NAPI-RS](https://napi.rs/).
|
|
9
|
+
|
|
10
|
+
A common pain point in genomics analyses is that BAM files are information-dense,
|
|
11
|
+
making it difficult to gain insight from them. Nanalogue-node helps extract and process
|
|
12
|
+
this information, with a particular focus on single-molecule aspects and DNA/RNA modifications.
|
|
13
|
+
|
|
14
|
+
We support any type of DNA/RNA modifications in any pattern (single/multiple mods,
|
|
15
|
+
spatially-isolated/non-isolated, etc.). All we require is that the data is stored
|
|
16
|
+
in a BAM file in the mod BAM format (using MM/ML tags as specified in the
|
|
17
|
+
[SAM tags specification](https://samtools.github.io/hts-specs/SAMtags.pdf)).
|
|
18
|
+
|
|
19
|
+
## Table of Contents
|
|
20
|
+
|
|
21
|
+
- [Requirements](#requirements)
|
|
22
|
+
- [Installation](#installation)
|
|
23
|
+
- [Functions](#functions)
|
|
24
|
+
- [peek](#peek)
|
|
25
|
+
- [readInfo](#readinfo)
|
|
26
|
+
- [bamMods](#bammods)
|
|
27
|
+
- [windowReads](#windowreads)
|
|
28
|
+
- [seqTable](#seqtable)
|
|
29
|
+
- [simulateModBam](#simulatemodbam)
|
|
30
|
+
- [TypeScript Support](#typescript-support)
|
|
31
|
+
- [Filtering Options](#filtering-options)
|
|
32
|
+
- [Further Documentation](#further-documentation)
|
|
33
|
+
- [Versioning](#versioning)
|
|
34
|
+
- [Acknowledgments](#acknowledgments)
|
|
35
|
+
|
|
36
|
+
## Requirements
|
|
37
|
+
|
|
38
|
+
- Node.js 22 or higher
|
|
39
|
+
- For building from source: Rust toolchain
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install @nanalogue/node
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Functions
|
|
48
|
+
|
|
49
|
+
All functions return Promises and support extensive filtering options.
|
|
50
|
+
|
|
51
|
+
### peek
|
|
52
|
+
|
|
53
|
+
Quickly extract BAM file metadata without processing all records.
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { peek } from '@nanalogue/node';
|
|
57
|
+
|
|
58
|
+
const metadata = await peek({ bamPath: 'example.bam' });
|
|
59
|
+
console.log(metadata);
|
|
60
|
+
// {
|
|
61
|
+
// contigs: { chr1: 248956422, chr2: 242193529, ... },
|
|
62
|
+
// modifications: [['T', '+', 'T'], ['C', '+', 'm']]
|
|
63
|
+
// }
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### readInfo
|
|
67
|
+
|
|
68
|
+
Get information about reads in the BAM file.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { readInfo } from '@nanalogue/node';
|
|
72
|
+
|
|
73
|
+
const reads = await readInfo({ bamPath: 'example.bam' });
|
|
74
|
+
// Returns array of read records with alignment info and modification counts
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### bamMods
|
|
78
|
+
|
|
79
|
+
Extract detailed modification data for each read.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { bamMods } from '@nanalogue/node';
|
|
83
|
+
|
|
84
|
+
const mods = await bamMods({ bamPath: 'example.bam' });
|
|
85
|
+
// Returns array of records with modification tables containing
|
|
86
|
+
// position, reference position, and probability for each modification
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### windowReads
|
|
90
|
+
|
|
91
|
+
Compute windowed modification densities across reads.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { windowReads } from '@nanalogue/node';
|
|
95
|
+
|
|
96
|
+
const tsv = await windowReads({
|
|
97
|
+
bamPath: 'example.bam',
|
|
98
|
+
win: 100, // window size in bases
|
|
99
|
+
step: 50 // step size
|
|
100
|
+
});
|
|
101
|
+
// Returns TSV string with windowed modification densities
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Supports `winOp: 'grad_density'` for gradient mode.
|
|
105
|
+
|
|
106
|
+
### seqTable
|
|
107
|
+
|
|
108
|
+
Extract sequences and qualities for a genomic region.
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { seqTable } from '@nanalogue/node';
|
|
112
|
+
|
|
113
|
+
const tsv = await seqTable({
|
|
114
|
+
bamPath: 'example.bam',
|
|
115
|
+
region: 'chr1:1000-2000' // region is required
|
|
116
|
+
});
|
|
117
|
+
// Returns TSV with read_id, sequence, qualities columns
|
|
118
|
+
// Sequence uses: . for deletion, lowercase for insertion, Z for modification
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### simulateModBam
|
|
122
|
+
|
|
123
|
+
Generate synthetic BAM files with defined modification patterns (useful for testing).
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { simulateModBam } from '@nanalogue/node';
|
|
127
|
+
|
|
128
|
+
const config = JSON.stringify({
|
|
129
|
+
contigs: { number: 2, len_range: [1000, 2000] },
|
|
130
|
+
reads: [{
|
|
131
|
+
number: 100,
|
|
132
|
+
len_range: [0.5, 0.9],
|
|
133
|
+
mods: [{
|
|
134
|
+
base: 'C',
|
|
135
|
+
is_strand_plus: true,
|
|
136
|
+
mod_code: 'm',
|
|
137
|
+
win: [5, 3],
|
|
138
|
+
mod_range: [[0.3, 0.7], [0.1, 0.5]]
|
|
139
|
+
}]
|
|
140
|
+
}]
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
await simulateModBam({
|
|
144
|
+
jsonConfig: config,
|
|
145
|
+
bamPath: 'output.bam',
|
|
146
|
+
fastaPath: 'output.fasta'
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## TypeScript Support
|
|
151
|
+
|
|
152
|
+
Full TypeScript definitions are included. The package uses discriminated unions
|
|
153
|
+
to enforce constraints at compile time (e.g., `fullRegion` can only be set when
|
|
154
|
+
`region` is specified).
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
import type { ReadOptions, BamModRecord, ReadInfoRecord } from '@nanalogue/node';
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Filtering Options
|
|
161
|
+
|
|
162
|
+
All read functions support extensive filtering:
|
|
163
|
+
|
|
164
|
+
| Option | Description |
|
|
165
|
+
|--------|-------------|
|
|
166
|
+
| `treatAsUrl` | Treat bamPath as URL instead of file path |
|
|
167
|
+
| `region` | Genomic region filter (e.g., "chr1:1000-2000") |
|
|
168
|
+
| `fullRegion` | Only include reads fully spanning the region |
|
|
169
|
+
| `readFilter` | Filter by alignment type (e.g., "primary_forward,primary_reverse") |
|
|
170
|
+
| `readIdSet` | Filter to specific read IDs |
|
|
171
|
+
| `minSeqLen` | Minimum sequence length |
|
|
172
|
+
| `minAlignLen` | Minimum alignment length |
|
|
173
|
+
| `mapqFilter` | Minimum mapping quality |
|
|
174
|
+
| `excludeMapqUnavail` | Exclude reads without mapping quality |
|
|
175
|
+
| `sampleFraction` | Subsample reads (0.0 to 1.0) |
|
|
176
|
+
| `threads` | Number of threads for BAM reading |
|
|
177
|
+
| `tag` | Filter by modification type |
|
|
178
|
+
| `modStrand` | Filter by modification strand ("bc" or "bc_comp") |
|
|
179
|
+
| `minModQual` | Minimum modification quality threshold |
|
|
180
|
+
| `rejectModQualNonInclusive` | Reject mods where low < prob < high |
|
|
181
|
+
| `trimReadEndsMod` | Trim modification info from read ends |
|
|
182
|
+
| `baseQualFilterMod` | Base quality filter for modifications |
|
|
183
|
+
| `modRegion` | Genomic region for modification filtering |
|
|
184
|
+
|
|
185
|
+
## Further Documentation
|
|
186
|
+
|
|
187
|
+
- [Nanalogue Core Documentation](https://docs.rs/nanalogue)
|
|
188
|
+
- [Nanalogue Cookbook](https://www.nanalogue.com)
|
|
189
|
+
- [pynanalogue](https://github.com/DNAReplicationLab/pynanalogue) - Python bindings
|
|
190
|
+
|
|
191
|
+
## Changelog
|
|
192
|
+
|
|
193
|
+
See [CHANGELOG.md](CHANGELOG.md) for version history.
|
|
194
|
+
|
|
195
|
+
## Versioning
|
|
196
|
+
|
|
197
|
+
We use [Semantic Versioning](https://semver.org/).
|
|
198
|
+
|
|
199
|
+
**Current Status: Pre-1.0 (0.x.y)**
|
|
200
|
+
|
|
201
|
+
While in 0.x.y versions:
|
|
202
|
+
- The API may change without notice
|
|
203
|
+
- Breaking changes can occur in minor version updates
|
|
204
|
+
|
|
205
|
+
After 1.0.0, we will guarantee backwards compatibility in minor/patch releases.
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
210
|
+
|
|
211
|
+
## Acknowledgments
|
|
212
|
+
|
|
213
|
+
This software was developed at the Earlham Institute in the UK.
|
|
214
|
+
This work was supported by the Biotechnology and Biological Sciences
|
|
215
|
+
Research Council (BBSRC), part of UK Research and Innovation,
|
|
216
|
+
through the Core Capability Grant BB/CCG2220/1 at the Earlham Institute
|
|
217
|
+
and the Earlham Institute Strategic Programme Grant Cellular Genomics
|
|
218
|
+
BBX011070/1 and its constituent work packages BBS/E/ER/230001B
|
|
219
|
+
(CellGen WP2 Consequences of somatic genome variation on traits).
|
|
220
|
+
The work was also supported by the following response-mode project grants:
|
|
221
|
+
BB/W006014/1 (Single molecule detection of DNA replication errors) and
|
|
222
|
+
BB/Y00549X/1 (Single molecule analysis of Human DNA replication).
|
|
223
|
+
This research was supported in part by NBI Research Computing
|
|
224
|
+
through use of the High-Performance Computing system and Isilon storage.
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// Node.js bindings for Nanalogue: single-molecule BAM/Mod-BAM analysis
|
|
2
|
+
// This file provides TypeScript type definitions for the native module.
|
|
3
|
+
|
|
4
|
+
export interface PeekResult {
|
|
5
|
+
contigs: Record<string, number>;
|
|
6
|
+
modifications: [string, string, string][];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface PeekOptions {
|
|
10
|
+
bamPath: string;
|
|
11
|
+
/** If true, treat bamPath as a URL. Otherwise treat as file path. */
|
|
12
|
+
treatAsUrl?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export declare function peek(options: PeekOptions): Promise<PeekResult>;
|
|
16
|
+
|
|
17
|
+
// Read info types
|
|
18
|
+
export interface MappedReadInfo {
|
|
19
|
+
read_id: string;
|
|
20
|
+
sequence_length: number;
|
|
21
|
+
contig: string;
|
|
22
|
+
reference_start: number;
|
|
23
|
+
reference_end: number;
|
|
24
|
+
alignment_length: number;
|
|
25
|
+
alignment_type: 'primary_forward' | 'primary_reverse' | 'secondary_forward' | 'secondary_reverse' | 'supplementary_forward' | 'supplementary_reverse';
|
|
26
|
+
mod_count: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface UnmappedReadInfo {
|
|
30
|
+
read_id: string;
|
|
31
|
+
sequence_length: number;
|
|
32
|
+
alignment_type: 'unmapped';
|
|
33
|
+
mod_count: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type ReadInfoRecord = MappedReadInfo | UnmappedReadInfo;
|
|
37
|
+
|
|
38
|
+
// Base options shared by ReadOptions (excluding region/fullRegion)
|
|
39
|
+
interface BaseReadOptionsCore {
|
|
40
|
+
/** Path to the BAM file (local path or URL). */
|
|
41
|
+
bamPath: string;
|
|
42
|
+
/** If true, treat bamPath as a URL. Otherwise treat as file path. */
|
|
43
|
+
treatAsUrl?: boolean;
|
|
44
|
+
/** Minimum sequence length filter. */
|
|
45
|
+
minSeqLen?: number;
|
|
46
|
+
/** Minimum alignment length filter. */
|
|
47
|
+
minAlignLen?: number;
|
|
48
|
+
/** Filter to a set of read IDs. */
|
|
49
|
+
readIdSet?: string[];
|
|
50
|
+
/** Number of threads for BAM reading. */
|
|
51
|
+
threads?: number;
|
|
52
|
+
/** Include zero-length sequences (may cause crashes). */
|
|
53
|
+
includeZeroLen?: boolean;
|
|
54
|
+
/** Comma-separated read filter (e.g., "primary_forward,primary_reverse"). */
|
|
55
|
+
readFilter?: string;
|
|
56
|
+
/** Subsample fraction (0.0 to 1.0). */
|
|
57
|
+
sampleFraction?: number;
|
|
58
|
+
/** Minimum mapping quality filter. */
|
|
59
|
+
mapqFilter?: number;
|
|
60
|
+
/** Exclude reads with unavailable mapping quality. */
|
|
61
|
+
excludeMapqUnavail?: boolean;
|
|
62
|
+
/** Filter to specific modification tag. */
|
|
63
|
+
tag?: string;
|
|
64
|
+
/** Filter by modification strand ("bc" or "bc_comp"). */
|
|
65
|
+
modStrand?: string;
|
|
66
|
+
/** Minimum modification quality threshold. */
|
|
67
|
+
minModQual?: number;
|
|
68
|
+
/**
|
|
69
|
+
* Reject modification calls where low < probability < high.
|
|
70
|
+
* Tuple of [low, high] where both are 0-255.
|
|
71
|
+
* If low > high, throws an error. If high - low <= 1, no rejection range is applied.
|
|
72
|
+
*/
|
|
73
|
+
rejectModQualNonInclusive?: [number, number];
|
|
74
|
+
/** Trim modification info from read ends (bp). */
|
|
75
|
+
trimReadEndsMod?: number;
|
|
76
|
+
/** Base quality filter for modifications. */
|
|
77
|
+
baseQualFilterMod?: number;
|
|
78
|
+
/** Genomic region for modification filtering. */
|
|
79
|
+
modRegion?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* ReadOptions with a region filter specified.
|
|
84
|
+
* When `region` is set, `fullRegion` can optionally be used to require reads
|
|
85
|
+
* to fully span the region.
|
|
86
|
+
*/
|
|
87
|
+
interface ReadOptionsWithRegion extends BaseReadOptionsCore {
|
|
88
|
+
/** Genomic region filter (e.g., "chr1:1000-2000"). */
|
|
89
|
+
region: string;
|
|
90
|
+
/**
|
|
91
|
+
* Only include reads fully spanning the region.
|
|
92
|
+
* Can only be set when `region` is specified.
|
|
93
|
+
*/
|
|
94
|
+
fullRegion?: boolean;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* ReadOptions without a region filter.
|
|
99
|
+
* When `region` is not set, `fullRegion` cannot be used.
|
|
100
|
+
*/
|
|
101
|
+
interface ReadOptionsWithoutRegion extends BaseReadOptionsCore {
|
|
102
|
+
region?: undefined;
|
|
103
|
+
fullRegion?: undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Options for read operations including BAM filtering and modification parameters.
|
|
108
|
+
*
|
|
109
|
+
* Note: `fullRegion` can only be set when `region` is specified.
|
|
110
|
+
*/
|
|
111
|
+
export type ReadOptions = ReadOptionsWithRegion | ReadOptionsWithoutRegion;
|
|
112
|
+
|
|
113
|
+
export declare function readInfo(options: ReadOptions): Promise<ReadInfoRecord[]>;
|
|
114
|
+
|
|
115
|
+
// Simulation types
|
|
116
|
+
export interface SimulateOptions {
|
|
117
|
+
jsonConfig: string;
|
|
118
|
+
bamPath: string;
|
|
119
|
+
fastaPath: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export declare function simulateModBam(options: SimulateOptions): Promise<void>;
|
|
123
|
+
|
|
124
|
+
// Detailed modification data types (bamMods)
|
|
125
|
+
export interface ModTableEntry {
|
|
126
|
+
base: string;
|
|
127
|
+
is_strand_plus: boolean;
|
|
128
|
+
mod_code: string;
|
|
129
|
+
data: [number, number, number][]; // [read_pos, ref_pos, probability]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface MappedBamModRecord {
|
|
133
|
+
alignment_type: 'primary_forward' | 'primary_reverse' | 'secondary_forward' | 'secondary_reverse' | 'supplementary_forward' | 'supplementary_reverse';
|
|
134
|
+
alignment: {
|
|
135
|
+
start: number;
|
|
136
|
+
end: number;
|
|
137
|
+
contig: string;
|
|
138
|
+
contig_id: number;
|
|
139
|
+
};
|
|
140
|
+
mod_table: ModTableEntry[];
|
|
141
|
+
read_id: string;
|
|
142
|
+
seq_len: number;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface UnmappedBamModRecord {
|
|
146
|
+
alignment_type: 'unmapped';
|
|
147
|
+
mod_table: ModTableEntry[];
|
|
148
|
+
read_id: string;
|
|
149
|
+
seq_len: number;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export type BamModRecord = MappedBamModRecord | UnmappedBamModRecord;
|
|
153
|
+
|
|
154
|
+
export declare function bamMods(options: ReadOptions): Promise<BamModRecord[]>;
|
|
155
|
+
|
|
156
|
+
// Base options shared by WindowOptions (excluding region/fullRegion)
|
|
157
|
+
interface BaseWindowOptionsCore {
|
|
158
|
+
/** Path to the BAM file (local path or URL). */
|
|
159
|
+
bamPath: string;
|
|
160
|
+
/** If true, treat bamPath as a URL. Otherwise treat as file path. */
|
|
161
|
+
treatAsUrl?: boolean;
|
|
162
|
+
/** Window size in number of bases. */
|
|
163
|
+
win: number;
|
|
164
|
+
/** Step size for sliding the window. */
|
|
165
|
+
step: number;
|
|
166
|
+
/** Type of windowing operation: "density" or "grad_density". */
|
|
167
|
+
winOp?: 'density' | 'grad_density';
|
|
168
|
+
/** Minimum sequence length filter. */
|
|
169
|
+
minSeqLen?: number;
|
|
170
|
+
/** Minimum alignment length filter. */
|
|
171
|
+
minAlignLen?: number;
|
|
172
|
+
/** Filter to a set of read IDs. */
|
|
173
|
+
readIdSet?: string[];
|
|
174
|
+
/** Number of threads for BAM reading. */
|
|
175
|
+
threads?: number;
|
|
176
|
+
/** Include zero-length sequences (may cause crashes). */
|
|
177
|
+
includeZeroLen?: boolean;
|
|
178
|
+
/** Comma-separated read filter. */
|
|
179
|
+
readFilter?: string;
|
|
180
|
+
/** Subsample fraction (0.0 to 1.0). */
|
|
181
|
+
sampleFraction?: number;
|
|
182
|
+
/** Minimum mapping quality filter. */
|
|
183
|
+
mapqFilter?: number;
|
|
184
|
+
/** Exclude reads with unavailable mapping quality. */
|
|
185
|
+
excludeMapqUnavail?: boolean;
|
|
186
|
+
/** Filter to specific modification tag. */
|
|
187
|
+
tag?: string;
|
|
188
|
+
/** Filter by modification strand. */
|
|
189
|
+
modStrand?: string;
|
|
190
|
+
/** Minimum modification quality threshold. */
|
|
191
|
+
minModQual?: number;
|
|
192
|
+
/**
|
|
193
|
+
* Reject modification calls where low < probability < high.
|
|
194
|
+
* Tuple of [low, high] where both are 0-255.
|
|
195
|
+
* If low > high, throws an error. If high - low <= 1, no rejection range is applied.
|
|
196
|
+
*/
|
|
197
|
+
rejectModQualNonInclusive?: [number, number];
|
|
198
|
+
/** Trim modification info from read ends (bp). */
|
|
199
|
+
trimReadEndsMod?: number;
|
|
200
|
+
/** Base quality filter for modifications. */
|
|
201
|
+
baseQualFilterMod?: number;
|
|
202
|
+
/** Genomic region for modification filtering. */
|
|
203
|
+
modRegion?: string;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* WindowOptions with a region filter specified.
|
|
208
|
+
* When `region` is set, `fullRegion` can optionally be used to require reads
|
|
209
|
+
* to fully span the region.
|
|
210
|
+
*/
|
|
211
|
+
interface WindowOptionsWithRegion extends BaseWindowOptionsCore {
|
|
212
|
+
/** Genomic region filter (e.g., "chr1:1000-2000"). */
|
|
213
|
+
region: string;
|
|
214
|
+
/**
|
|
215
|
+
* Only include reads fully spanning the region.
|
|
216
|
+
* Can only be set when `region` is specified.
|
|
217
|
+
*/
|
|
218
|
+
fullRegion?: boolean;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* WindowOptions without a region filter.
|
|
223
|
+
* When `region` is not set, `fullRegion` cannot be used.
|
|
224
|
+
*/
|
|
225
|
+
interface WindowOptionsWithoutRegion extends BaseWindowOptionsCore {
|
|
226
|
+
region?: undefined;
|
|
227
|
+
fullRegion?: undefined;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Options for windowed modification analysis.
|
|
232
|
+
*
|
|
233
|
+
* Note: `fullRegion` can only be set when `region` is specified.
|
|
234
|
+
*/
|
|
235
|
+
export type WindowOptions = WindowOptionsWithRegion | WindowOptionsWithoutRegion;
|
|
236
|
+
|
|
237
|
+
export declare function windowReads(options: WindowOptions): Promise<string>;
|
|
238
|
+
|
|
239
|
+
export declare function seqTable(options: ReadOptions): Promise<string>;
|
package/index.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
/* prettier-ignore */
|
|
4
|
+
|
|
5
|
+
/* auto-generated by NAPI-RS */
|
|
6
|
+
|
|
7
|
+
const { existsSync, readFileSync } = require('fs')
|
|
8
|
+
const { join } = require('path')
|
|
9
|
+
|
|
10
|
+
const { platform, arch } = process
|
|
11
|
+
|
|
12
|
+
let nativeBinding = null
|
|
13
|
+
let localFileExisted = false
|
|
14
|
+
let loadError = null
|
|
15
|
+
|
|
16
|
+
function isMusl() {
|
|
17
|
+
// For Node 10
|
|
18
|
+
if (!process.report || typeof process.report.getReport !== 'function') {
|
|
19
|
+
try {
|
|
20
|
+
const lddPath = require('child_process').execSync('which ldd').toString().trim()
|
|
21
|
+
return readFileSync(lddPath, 'utf8').includes('musl')
|
|
22
|
+
} catch (e) {
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
25
|
+
} else {
|
|
26
|
+
const { glibcVersionRuntime } = process.report.getReport().header
|
|
27
|
+
return !glibcVersionRuntime
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
switch (platform) {
|
|
32
|
+
case 'android':
|
|
33
|
+
switch (arch) {
|
|
34
|
+
case 'arm64':
|
|
35
|
+
localFileExisted = existsSync(join(__dirname, 'nanalogue.android-arm64.node'))
|
|
36
|
+
try {
|
|
37
|
+
if (localFileExisted) {
|
|
38
|
+
nativeBinding = require('./nanalogue.android-arm64.node')
|
|
39
|
+
} else {
|
|
40
|
+
nativeBinding = require('@nanalogue/node-android-arm64')
|
|
41
|
+
}
|
|
42
|
+
} catch (e) {
|
|
43
|
+
loadError = e
|
|
44
|
+
}
|
|
45
|
+
break
|
|
46
|
+
case 'arm':
|
|
47
|
+
localFileExisted = existsSync(join(__dirname, 'nanalogue.android-arm-eabi.node'))
|
|
48
|
+
try {
|
|
49
|
+
if (localFileExisted) {
|
|
50
|
+
nativeBinding = require('./nanalogue.android-arm-eabi.node')
|
|
51
|
+
} else {
|
|
52
|
+
nativeBinding = require('@nanalogue/node-android-arm-eabi')
|
|
53
|
+
}
|
|
54
|
+
} catch (e) {
|
|
55
|
+
loadError = e
|
|
56
|
+
}
|
|
57
|
+
break
|
|
58
|
+
default:
|
|
59
|
+
throw new Error(`Unsupported architecture on Android ${arch}`)
|
|
60
|
+
}
|
|
61
|
+
break
|
|
62
|
+
case 'win32':
|
|
63
|
+
switch (arch) {
|
|
64
|
+
case 'x64':
|
|
65
|
+
localFileExisted = existsSync(
|
|
66
|
+
join(__dirname, 'nanalogue.win32-x64-msvc.node')
|
|
67
|
+
)
|
|
68
|
+
try {
|
|
69
|
+
if (localFileExisted) {
|
|
70
|
+
nativeBinding = require('./nanalogue.win32-x64-msvc.node')
|
|
71
|
+
} else {
|
|
72
|
+
nativeBinding = require('@nanalogue/node-win32-x64-msvc')
|
|
73
|
+
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
loadError = e
|
|
76
|
+
}
|
|
77
|
+
break
|
|
78
|
+
case 'ia32':
|
|
79
|
+
localFileExisted = existsSync(
|
|
80
|
+
join(__dirname, 'nanalogue.win32-ia32-msvc.node')
|
|
81
|
+
)
|
|
82
|
+
try {
|
|
83
|
+
if (localFileExisted) {
|
|
84
|
+
nativeBinding = require('./nanalogue.win32-ia32-msvc.node')
|
|
85
|
+
} else {
|
|
86
|
+
nativeBinding = require('@nanalogue/node-win32-ia32-msvc')
|
|
87
|
+
}
|
|
88
|
+
} catch (e) {
|
|
89
|
+
loadError = e
|
|
90
|
+
}
|
|
91
|
+
break
|
|
92
|
+
case 'arm64':
|
|
93
|
+
localFileExisted = existsSync(
|
|
94
|
+
join(__dirname, 'nanalogue.win32-arm64-msvc.node')
|
|
95
|
+
)
|
|
96
|
+
try {
|
|
97
|
+
if (localFileExisted) {
|
|
98
|
+
nativeBinding = require('./nanalogue.win32-arm64-msvc.node')
|
|
99
|
+
} else {
|
|
100
|
+
nativeBinding = require('@nanalogue/node-win32-arm64-msvc')
|
|
101
|
+
}
|
|
102
|
+
} catch (e) {
|
|
103
|
+
loadError = e
|
|
104
|
+
}
|
|
105
|
+
break
|
|
106
|
+
default:
|
|
107
|
+
throw new Error(`Unsupported architecture on Windows: ${arch}`)
|
|
108
|
+
}
|
|
109
|
+
break
|
|
110
|
+
case 'darwin':
|
|
111
|
+
localFileExisted = existsSync(join(__dirname, 'nanalogue.darwin-universal.node'))
|
|
112
|
+
try {
|
|
113
|
+
if (localFileExisted) {
|
|
114
|
+
nativeBinding = require('./nanalogue.darwin-universal.node')
|
|
115
|
+
} else {
|
|
116
|
+
nativeBinding = require('@nanalogue/node-darwin-universal')
|
|
117
|
+
}
|
|
118
|
+
break
|
|
119
|
+
} catch {}
|
|
120
|
+
switch (arch) {
|
|
121
|
+
case 'x64':
|
|
122
|
+
localFileExisted = existsSync(join(__dirname, 'nanalogue.darwin-x64.node'))
|
|
123
|
+
try {
|
|
124
|
+
if (localFileExisted) {
|
|
125
|
+
nativeBinding = require('./nanalogue.darwin-x64.node')
|
|
126
|
+
} else {
|
|
127
|
+
nativeBinding = require('@nanalogue/node-darwin-x64')
|
|
128
|
+
}
|
|
129
|
+
} catch (e) {
|
|
130
|
+
loadError = e
|
|
131
|
+
}
|
|
132
|
+
break
|
|
133
|
+
case 'arm64':
|
|
134
|
+
localFileExisted = existsSync(
|
|
135
|
+
join(__dirname, 'nanalogue.darwin-arm64.node')
|
|
136
|
+
)
|
|
137
|
+
try {
|
|
138
|
+
if (localFileExisted) {
|
|
139
|
+
nativeBinding = require('./nanalogue.darwin-arm64.node')
|
|
140
|
+
} else {
|
|
141
|
+
nativeBinding = require('@nanalogue/node-darwin-arm64')
|
|
142
|
+
}
|
|
143
|
+
} catch (e) {
|
|
144
|
+
loadError = e
|
|
145
|
+
}
|
|
146
|
+
break
|
|
147
|
+
default:
|
|
148
|
+
throw new Error(`Unsupported architecture on macOS: ${arch}`)
|
|
149
|
+
}
|
|
150
|
+
break
|
|
151
|
+
case 'freebsd':
|
|
152
|
+
if (arch !== 'x64') {
|
|
153
|
+
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
|
|
154
|
+
}
|
|
155
|
+
localFileExisted = existsSync(join(__dirname, 'nanalogue.freebsd-x64.node'))
|
|
156
|
+
try {
|
|
157
|
+
if (localFileExisted) {
|
|
158
|
+
nativeBinding = require('./nanalogue.freebsd-x64.node')
|
|
159
|
+
} else {
|
|
160
|
+
nativeBinding = require('@nanalogue/node-freebsd-x64')
|
|
161
|
+
}
|
|
162
|
+
} catch (e) {
|
|
163
|
+
loadError = e
|
|
164
|
+
}
|
|
165
|
+
break
|
|
166
|
+
case 'linux':
|
|
167
|
+
switch (arch) {
|
|
168
|
+
case 'x64':
|
|
169
|
+
if (isMusl()) {
|
|
170
|
+
localFileExisted = existsSync(
|
|
171
|
+
join(__dirname, 'nanalogue.linux-x64-musl.node')
|
|
172
|
+
)
|
|
173
|
+
try {
|
|
174
|
+
if (localFileExisted) {
|
|
175
|
+
nativeBinding = require('./nanalogue.linux-x64-musl.node')
|
|
176
|
+
} else {
|
|
177
|
+
nativeBinding = require('@nanalogue/node-linux-x64-musl')
|
|
178
|
+
}
|
|
179
|
+
} catch (e) {
|
|
180
|
+
loadError = e
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
localFileExisted = existsSync(
|
|
184
|
+
join(__dirname, 'nanalogue.linux-x64-gnu.node')
|
|
185
|
+
)
|
|
186
|
+
try {
|
|
187
|
+
if (localFileExisted) {
|
|
188
|
+
nativeBinding = require('./nanalogue.linux-x64-gnu.node')
|
|
189
|
+
} else {
|
|
190
|
+
nativeBinding = require('@nanalogue/node-linux-x64-gnu')
|
|
191
|
+
}
|
|
192
|
+
} catch (e) {
|
|
193
|
+
loadError = e
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
break
|
|
197
|
+
case 'arm64':
|
|
198
|
+
if (isMusl()) {
|
|
199
|
+
localFileExisted = existsSync(
|
|
200
|
+
join(__dirname, 'nanalogue.linux-arm64-musl.node')
|
|
201
|
+
)
|
|
202
|
+
try {
|
|
203
|
+
if (localFileExisted) {
|
|
204
|
+
nativeBinding = require('./nanalogue.linux-arm64-musl.node')
|
|
205
|
+
} else {
|
|
206
|
+
nativeBinding = require('@nanalogue/node-linux-arm64-musl')
|
|
207
|
+
}
|
|
208
|
+
} catch (e) {
|
|
209
|
+
loadError = e
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
localFileExisted = existsSync(
|
|
213
|
+
join(__dirname, 'nanalogue.linux-arm64-gnu.node')
|
|
214
|
+
)
|
|
215
|
+
try {
|
|
216
|
+
if (localFileExisted) {
|
|
217
|
+
nativeBinding = require('./nanalogue.linux-arm64-gnu.node')
|
|
218
|
+
} else {
|
|
219
|
+
nativeBinding = require('@nanalogue/node-linux-arm64-gnu')
|
|
220
|
+
}
|
|
221
|
+
} catch (e) {
|
|
222
|
+
loadError = e
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
break
|
|
226
|
+
case 'arm':
|
|
227
|
+
if (isMusl()) {
|
|
228
|
+
localFileExisted = existsSync(
|
|
229
|
+
join(__dirname, 'nanalogue.linux-arm-musleabihf.node')
|
|
230
|
+
)
|
|
231
|
+
try {
|
|
232
|
+
if (localFileExisted) {
|
|
233
|
+
nativeBinding = require('./nanalogue.linux-arm-musleabihf.node')
|
|
234
|
+
} else {
|
|
235
|
+
nativeBinding = require('@nanalogue/node-linux-arm-musleabihf')
|
|
236
|
+
}
|
|
237
|
+
} catch (e) {
|
|
238
|
+
loadError = e
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
localFileExisted = existsSync(
|
|
242
|
+
join(__dirname, 'nanalogue.linux-arm-gnueabihf.node')
|
|
243
|
+
)
|
|
244
|
+
try {
|
|
245
|
+
if (localFileExisted) {
|
|
246
|
+
nativeBinding = require('./nanalogue.linux-arm-gnueabihf.node')
|
|
247
|
+
} else {
|
|
248
|
+
nativeBinding = require('@nanalogue/node-linux-arm-gnueabihf')
|
|
249
|
+
}
|
|
250
|
+
} catch (e) {
|
|
251
|
+
loadError = e
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
break
|
|
255
|
+
case 'riscv64':
|
|
256
|
+
if (isMusl()) {
|
|
257
|
+
localFileExisted = existsSync(
|
|
258
|
+
join(__dirname, 'nanalogue.linux-riscv64-musl.node')
|
|
259
|
+
)
|
|
260
|
+
try {
|
|
261
|
+
if (localFileExisted) {
|
|
262
|
+
nativeBinding = require('./nanalogue.linux-riscv64-musl.node')
|
|
263
|
+
} else {
|
|
264
|
+
nativeBinding = require('@nanalogue/node-linux-riscv64-musl')
|
|
265
|
+
}
|
|
266
|
+
} catch (e) {
|
|
267
|
+
loadError = e
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
localFileExisted = existsSync(
|
|
271
|
+
join(__dirname, 'nanalogue.linux-riscv64-gnu.node')
|
|
272
|
+
)
|
|
273
|
+
try {
|
|
274
|
+
if (localFileExisted) {
|
|
275
|
+
nativeBinding = require('./nanalogue.linux-riscv64-gnu.node')
|
|
276
|
+
} else {
|
|
277
|
+
nativeBinding = require('@nanalogue/node-linux-riscv64-gnu')
|
|
278
|
+
}
|
|
279
|
+
} catch (e) {
|
|
280
|
+
loadError = e
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
break
|
|
284
|
+
case 's390x':
|
|
285
|
+
localFileExisted = existsSync(
|
|
286
|
+
join(__dirname, 'nanalogue.linux-s390x-gnu.node')
|
|
287
|
+
)
|
|
288
|
+
try {
|
|
289
|
+
if (localFileExisted) {
|
|
290
|
+
nativeBinding = require('./nanalogue.linux-s390x-gnu.node')
|
|
291
|
+
} else {
|
|
292
|
+
nativeBinding = require('@nanalogue/node-linux-s390x-gnu')
|
|
293
|
+
}
|
|
294
|
+
} catch (e) {
|
|
295
|
+
loadError = e
|
|
296
|
+
}
|
|
297
|
+
break
|
|
298
|
+
default:
|
|
299
|
+
throw new Error(`Unsupported architecture on Linux: ${arch}`)
|
|
300
|
+
}
|
|
301
|
+
break
|
|
302
|
+
default:
|
|
303
|
+
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (!nativeBinding) {
|
|
307
|
+
if (loadError) {
|
|
308
|
+
throw loadError
|
|
309
|
+
}
|
|
310
|
+
throw new Error(`Failed to load native binding`)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const { peek, readInfo, simulateModBam, bamMods, windowReads, seqTable } = nativeBinding
|
|
314
|
+
|
|
315
|
+
module.exports.peek = peek
|
|
316
|
+
module.exports.readInfo = readInfo
|
|
317
|
+
module.exports.simulateModBam = simulateModBam
|
|
318
|
+
module.exports.bamMods = bamMods
|
|
319
|
+
module.exports.windowReads = windowReads
|
|
320
|
+
module.exports.seqTable = seqTable
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nanalogue/node",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Node.js bindings for Nanalogue: single-molecule BAM/Mod-BAM analysis",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"index.js",
|
|
9
|
+
"index.d.ts",
|
|
10
|
+
"*.node"
|
|
11
|
+
],
|
|
12
|
+
"napi": {
|
|
13
|
+
"name": "nanalogue",
|
|
14
|
+
"triples": {
|
|
15
|
+
"defaults": false,
|
|
16
|
+
"additional": [
|
|
17
|
+
"x86_64-apple-darwin",
|
|
18
|
+
"aarch64-apple-darwin",
|
|
19
|
+
"x86_64-unknown-linux-gnu",
|
|
20
|
+
"x86_64-unknown-linux-musl",
|
|
21
|
+
"aarch64-unknown-linux-gnu",
|
|
22
|
+
"aarch64-unknown-linux-musl"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">= 22"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "napi build --platform --release --dts native.d.ts",
|
|
31
|
+
"build:debug": "napi build --platform",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"lint": "biome check .",
|
|
35
|
+
"lint:fix": "biome check --write .",
|
|
36
|
+
"format": "biome format --write .",
|
|
37
|
+
"prepublishOnly": "napi pre-publish --npm-dir npm --no-gh-release"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@biomejs/biome": "^2.3.14",
|
|
41
|
+
"@napi-rs/cli": "^3.5.1",
|
|
42
|
+
"@types/node": "^22.0.0",
|
|
43
|
+
"typescript": "^5.4.0",
|
|
44
|
+
"vitest": "^4.0.18"
|
|
45
|
+
},
|
|
46
|
+
"keywords": [
|
|
47
|
+
"bioinformatics",
|
|
48
|
+
"genomics",
|
|
49
|
+
"bam",
|
|
50
|
+
"modifications",
|
|
51
|
+
"nanopore",
|
|
52
|
+
"pacbio",
|
|
53
|
+
"illumina",
|
|
54
|
+
"napi-rs",
|
|
55
|
+
"rust"
|
|
56
|
+
],
|
|
57
|
+
"license": "MIT",
|
|
58
|
+
"repository": {
|
|
59
|
+
"type": "git",
|
|
60
|
+
"url": "git+https://github.com/sathish-t/nanalogue-node.git"
|
|
61
|
+
},
|
|
62
|
+
"author": "Sathish Thiyagarajan <mail@unintegrable.com>",
|
|
63
|
+
"optionalDependencies": {
|
|
64
|
+
"@nanalogue/node-darwin-x64": "0.1.1",
|
|
65
|
+
"@nanalogue/node-darwin-arm64": "0.1.1",
|
|
66
|
+
"@nanalogue/node-linux-x64-gnu": "0.1.1",
|
|
67
|
+
"@nanalogue/node-linux-x64-musl": "0.1.1",
|
|
68
|
+
"@nanalogue/node-linux-arm64-gnu": "0.1.1",
|
|
69
|
+
"@nanalogue/node-linux-arm64-musl": "0.1.1"
|
|
70
|
+
}
|
|
71
|
+
}
|