@jbrowse/plugin-bed 4.3.0 → 5.0.0-beta.10

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.
Files changed (64) hide show
  1. package/esm/BedAdapter/BedAdapter.d.ts +5 -22
  2. package/esm/BedAdapter/BedAdapter.js +29 -93
  3. package/esm/BedAdapter/configSchema.d.ts +59 -32
  4. package/esm/BedAdapter/configSchema.js +64 -14
  5. package/esm/BedAdapter/index.js +4 -2
  6. package/esm/BedGraphAdapter/BedGraphAdapter.d.ts +5 -12
  7. package/esm/BedGraphAdapter/BedGraphAdapter.js +33 -89
  8. package/esm/BedGraphAdapter/configSchema.d.ts +35 -11
  9. package/esm/BedGraphAdapter/configSchema.js +43 -13
  10. package/esm/BedGraphAdapter/index.js +3 -2
  11. package/esm/BedGraphTabixAdapter/BedGraphTabixAdapter.d.ts +5 -5
  12. package/esm/BedGraphTabixAdapter/BedGraphTabixAdapter.js +65 -57
  13. package/esm/BedGraphTabixAdapter/configSchema.d.ts +46 -22
  14. package/esm/BedGraphTabixAdapter/configSchema.js +50 -34
  15. package/esm/BedGraphTabixAdapter/index.js +3 -2
  16. package/esm/BedTabixAdapter/BedTabixAdapter.d.ts +34 -38
  17. package/esm/BedTabixAdapter/BedTabixAdapter.js +82 -72
  18. package/esm/BedTabixAdapter/configSchema.d.ts +55 -31
  19. package/esm/BedTabixAdapter/configSchema.js +58 -35
  20. package/esm/BedTabixAdapter/index.js +4 -2
  21. package/esm/BedpeAdapter/BedpeAdapter.d.ts +6 -13
  22. package/esm/BedpeAdapter/BedpeAdapter.js +26 -74
  23. package/esm/BedpeAdapter/configSchema.d.ts +31 -12
  24. package/esm/BedpeAdapter/configSchema.js +37 -13
  25. package/esm/BedpeAdapter/index.js +4 -2
  26. package/esm/BedpeAdapter/util.js +25 -25
  27. package/esm/BigBedAdapter/BigBedAdapter.d.ts +8 -7
  28. package/esm/BigBedAdapter/BigBedAdapter.js +99 -107
  29. package/esm/BigBedAdapter/configSchema.d.ts +38 -16
  30. package/esm/BigBedAdapter/configSchema.js +46 -13
  31. package/esm/BigBedAdapter/index.js +3 -2
  32. package/esm/BigBedTextSearchAdapter/BigBedTextSearchAdapter.d.ts +27 -0
  33. package/esm/BigBedTextSearchAdapter/BigBedTextSearchAdapter.js +128 -0
  34. package/esm/BigBedTextSearchAdapter/configSchema.d.ts +51 -0
  35. package/esm/BigBedTextSearchAdapter/configSchema.js +57 -0
  36. package/esm/BigBedTextSearchAdapter/index.d.ts +2 -0
  37. package/esm/BigBedTextSearchAdapter/index.js +11 -0
  38. package/esm/StarFusionAdapter/StarFusionAdapter.d.ts +14 -0
  39. package/esm/StarFusionAdapter/StarFusionAdapter.js +66 -0
  40. package/esm/StarFusionAdapter/configSchema.d.ts +30 -0
  41. package/esm/StarFusionAdapter/configSchema.js +43 -0
  42. package/esm/StarFusionAdapter/index.d.ts +2 -0
  43. package/esm/StarFusionAdapter/index.js +12 -0
  44. package/esm/adapterUtil.d.ts +5 -0
  45. package/esm/adapterUtil.js +43 -0
  46. package/esm/bedGraphUtil.d.ts +11 -0
  47. package/esm/bedGraphUtil.js +20 -0
  48. package/esm/copyExcept.d.ts +1 -0
  49. package/esm/copyExcept.js +11 -0
  50. package/esm/generateBedMethylFeature.d.ts +1 -1
  51. package/esm/generateBedMethylFeature.js +24 -6
  52. package/esm/generateRastairMethylFeature.d.ts +28 -0
  53. package/esm/generateRastairMethylFeature.js +38 -0
  54. package/esm/generateRepeatMaskerFeature.d.ts +17 -51
  55. package/esm/generateRepeatMaskerFeature.js +27 -37
  56. package/esm/generateUcscTranscript.d.ts +15 -7
  57. package/esm/generateUcscTranscript.js +106 -29
  58. package/esm/index.d.ts +2 -0
  59. package/esm/index.js +12 -8
  60. package/esm/util.d.ts +32 -27
  61. package/esm/util.js +139 -98
  62. package/package.json +10 -13
  63. package/esm/GuessAdapter/index.d.ts +0 -2
  64. package/esm/GuessAdapter/index.js +0 -71
package/esm/util.js CHANGED
@@ -1,31 +1,29 @@
1
- import { generateBedMethylFeature, isBedMethylFeature, } from "./generateBedMethylFeature.js";
2
- import { generateRepeatMaskerFeature, isRepeatMaskerDescriptionField, } from "./generateRepeatMaskerFeature.js";
3
- import { generateUcscTranscript, isUcscTranscript, } from "./generateUcscTranscript.js";
4
- function defaultParser(fields, splitLine) {
5
- const obj = {};
6
- let hasBlockCount = false;
7
- for (const [i, element] of splitLine.entries()) {
8
- const field = fields[i];
9
- if (field) {
10
- obj[field] = element;
11
- if (field === 'blockCount') {
12
- hasBlockCount = true;
13
- }
1
+ import BED from '@gmod/bed';
2
+ import { parseLineByLine } from '@jbrowse/core/util/parseLineByLine';
3
+ import { copyExcept } from './copyExcept.js';
4
+ import { generateBedMethylFeature, isBedMethylFeature, } from './generateBedMethylFeature.js';
5
+ import { generateRastairMethylFeature, isRastairMethylFeature, } from './generateRastairMethylFeature.js';
6
+ import { parseRepeatMaskerDescription } from './generateRepeatMaskerFeature.js';
7
+ import { generateUcscTranscript, isUcscTranscript, } from './generateUcscTranscript.js';
8
+ // Heuristically split features and header from a buffered BED file, bucketing
9
+ // data lines by the first tab-separated column (refName).
10
+ export function bucketBedLines(buffer, statusCallback) {
11
+ const features = {};
12
+ const headerLines = [];
13
+ parseLineByLine(buffer, line => {
14
+ if (line.startsWith('#')) {
15
+ headerLines.push(line);
14
16
  }
15
- }
16
- if (hasBlockCount) {
17
- const { blockStarts, blockCount, chromStarts, thickEnd, thickStart, blockSizes, ...rest } = obj;
18
- return {
19
- ...rest,
20
- blockStarts: arrayify(blockStarts),
21
- chromStarts: arrayify(chromStarts),
22
- blockSizes: arrayify(blockSizes),
23
- thickStart: thickStart ? +thickStart : undefined,
24
- thickEnd: thickEnd ? +thickEnd : undefined,
25
- blockCount: blockCount ? +blockCount : undefined,
26
- };
27
- }
28
- return obj;
17
+ else if (!line.startsWith('track') && !line.startsWith('browser')) {
18
+ // 'track'/'browser' are UCSC directive lines, not data (matches
19
+ // filterBedHeaderLines in spreadsheet-view)
20
+ const tab = line.indexOf('\t');
21
+ const refName = tab === -1 ? line : line.slice(0, tab);
22
+ (features[refName] ??= []).push(line);
23
+ }
24
+ return true;
25
+ }, statusCallback);
26
+ return { header: headerLines.join('\n'), features };
29
27
  }
30
28
  export function makeBlocks({ start, uniqueId, refName, chromStarts, blockCount, blockSizes, blockStarts, }) {
31
29
  const subfeatures = [];
@@ -45,24 +43,55 @@ export function makeBlocks({ start, uniqueId, refName, chromStarts, blockCount,
45
43
  }
46
44
  return subfeatures;
47
45
  }
48
- export function featureData({ line, colRef, colStart, colEnd, scoreColumn, parser, uniqueId, names, disableGeneHeuristic, }) {
49
- const splitLine = line.split('\t');
50
- const refName = splitLine[colRef];
51
- const start = Number.parseInt(splitLine[colStart], 10);
52
- const end = Number.parseInt(splitLine[colEnd], 10) + (colStart === colEnd ? 1 : 0);
53
- return featureData2({
54
- splitLine,
55
- refName,
46
+ export function parseNamesFromHeader(header) {
47
+ const defs = header.split(/\n|\r\n|\r/).filter(Boolean);
48
+ const defline = defs.at(-1);
49
+ // Strip the comment marker if there is one, rather than assuming it: a
50
+ // header kept for tabix with `-S N` instead of `#` is a bare row, and
51
+ // chopping its first character would rename its first column rather than
52
+ // fail to find one.
53
+ return defline?.includes('\t')
54
+ ? defline
55
+ .replace(/^#/, '')
56
+ .split('\t')
57
+ .map(f => f.trim())
58
+ : undefined;
59
+ }
60
+ // shared by BedAdapter/BedpeAdapter's getNames(): explicit columnNames config
61
+ // wins, else fall back to parsing the file's own header line
62
+ export async function resolveColumnNames(columnNames, getHeader) {
63
+ if (columnNames.length) {
64
+ return columnNames;
65
+ }
66
+ return parseNamesFromHeader(await getHeader());
67
+ }
68
+ // A header names the columns but says nothing about their types, so @gmod/bed
69
+ // takes the types from the standard BED columns of the same name — that is what
70
+ // splits `blockSizes` into numbers and leaves an unrecognized column a string.
71
+ // Names win over a configured autoSql, as they did when this file parsed the
72
+ // named case itself.
73
+ export function makeParser({ autoSql, columnNames, }) {
74
+ return columnNames?.length ? new BED({ columnNames }) : new BED({ autoSql });
75
+ }
76
+ // Decode a BED line's locus (0-based half-open) from its column layout, so the
77
+ // off-by-one rules live in one place instead of being re-derived at each call
78
+ // site. `oneBased` shifts a 1-based-closed start back a base. `hasEndColumn`
79
+ // false (tabix `-b` with no `-e`, e.g. point/GWAS data) means width-1 features.
80
+ // A start/end column collision in a 0-based file is also a point feature (+1).
81
+ export function bedFeatureLocus({ splitLine, colRef, colStart, colEnd, oneBased = false, hasEndColumn = true, }) {
82
+ const start = +splitLine[colStart] - (oneBased ? 1 : 0);
83
+ return {
84
+ refName: splitLine[colRef],
56
85
  start,
57
- end,
58
- parser,
59
- uniqueId,
60
- scoreColumn,
61
- names,
62
- disableGeneHeuristic,
63
- });
86
+ end: hasEndColumn
87
+ ? +splitLine[colEnd] + (colStart === colEnd && !oneBased ? 1 : 0)
88
+ : start + 1,
89
+ };
64
90
  }
65
- function parseStrand(strand) {
91
+ // Takes either form: `@gmod/bed`'s parseLine has already run its own
92
+ // parseStrand and hands over ±1/0, while the methylation and bedpe paths read
93
+ // the raw column. `.` and a missing column are unstranded, i.e. 0.
94
+ export function parseStrand(strand) {
66
95
  if (strand === '-' || strand === -1) {
67
96
  return -1;
68
97
  }
@@ -71,24 +100,19 @@ function parseStrand(strand) {
71
100
  }
72
101
  return 0;
73
102
  }
74
- export function featureData2({ splitLine, refName, start, end, parser, uniqueId, scoreColumn, names, disableGeneHeuristic, }) {
75
- const data = names
76
- ? defaultParser(names, splitLine)
77
- : parser.parseLine(splitLine, { uniqueId });
78
- const { strand: strand2, score: score2, chrom: _1, chromStart: _2, chromEnd: _3, ...rest } = data;
79
- const score = scoreColumn ? +data[scoreColumn] : score2 ? +score2 : undefined;
80
- const strand = parseStrand(strand2);
81
- const subfeatures = rest.blockCount
82
- ? makeBlocks({
83
- start,
84
- uniqueId,
85
- refName,
86
- chromStarts: rest.chromStarts,
87
- blockCount: rest.blockCount,
88
- blockSizes: rest.blockSizes,
89
- blockStarts: rest.blockStarts,
90
- })
91
- : undefined;
103
+ const BED_OWN = new Set(['strand', 'score', 'chrom', 'chromStart', 'chromEnd']);
104
+ const REPEAT_OWN = new Set([
105
+ ...BED_OWN,
106
+ 'description',
107
+ 'chromStarts',
108
+ 'blockSizes',
109
+ 'blockStarts',
110
+ 'blockCount',
111
+ 'thickStart',
112
+ 'thickEnd',
113
+ ]);
114
+ export function featureData({ splitLine, refName, start, end, parser, uniqueId, scoreColumn, names, disableGeneHeuristic, }) {
115
+ // bedMethyl detection runs on raw splitLine before parsing
92
116
  if (isBedMethylFeature({ splitLine, start, end })) {
93
117
  return generateBedMethylFeature({
94
118
  splitLine,
@@ -98,59 +122,76 @@ export function featureData2({ splitLine, refName, start, end, parser, uniqueId,
98
122
  end,
99
123
  });
100
124
  }
101
- else if (isRepeatMaskerDescriptionField(rest.description)) {
102
- const { chromStarts: _4, blockSizes: _5, blockStarts: _6, blockCount: _7, thickStart: _8, thickEnd: _9, description, ...rest2 } = rest;
103
- return generateRepeatMaskerFeature({
104
- ...rest2,
105
- description,
125
+ // rastair methylation is identified by its header column names
126
+ if (names !== undefined && isRastairMethylFeature(names)) {
127
+ return generateRastairMethylFeature({
128
+ splitLine,
129
+ names,
106
130
  uniqueId,
107
- score,
131
+ refName,
108
132
  start,
109
133
  end,
110
- strand,
111
- refName,
112
- subfeatures,
113
134
  });
114
135
  }
115
- else if (!disableGeneHeuristic &&
116
- subfeatures &&
117
- isUcscTranscript({
118
- strand,
119
- blockCount: rest.blockCount,
120
- thickStart: rest.thickStart,
121
- })) {
122
- return generateUcscTranscript({
123
- ...rest,
136
+ const data = parser.parseLine(splitLine, { uniqueId });
137
+ const strand = parseStrand(data.strand);
138
+ const rawScore = scoreColumn ? data[scoreColumn] : data.score;
139
+ const score = rawScore === undefined ? undefined : Number(rawScore);
140
+ const repeat = parseRepeatMaskerDescription(data.description);
141
+ if (repeat) {
142
+ return {
143
+ ...copyExcept(data, REPEAT_OWN),
144
+ ...repeat,
145
+ uniqueId,
124
146
  score,
125
147
  start,
126
148
  end,
127
149
  strand,
128
150
  refName,
129
- uniqueId,
130
- subfeatures,
131
- thickStart: rest.thickStart,
132
- thickEnd: rest.thickEnd,
133
- });
151
+ };
134
152
  }
135
- else {
136
- return {
137
- ...rest,
153
+ const rest = copyExcept(data, BED_OWN);
154
+ const subfeatures = data.blockCount
155
+ ? makeBlocks({
156
+ start,
138
157
  uniqueId,
158
+ refName,
159
+ chromStarts: data.chromStarts,
160
+ blockCount: data.blockCount,
161
+ blockSizes: data.blockSizes,
162
+ blockStarts: data.blockStarts,
163
+ })
164
+ : undefined;
165
+ const transcriptCheck = {
166
+ strand,
167
+ blockCount: data.blockCount,
168
+ thickStart: data.thickStart,
169
+ thickEnd: data.thickEnd,
170
+ };
171
+ if (!disableGeneHeuristic &&
172
+ subfeatures &&
173
+ isUcscTranscript(transcriptCheck)) {
174
+ return generateUcscTranscript({
175
+ ...rest,
139
176
  score,
140
177
  start,
141
178
  end,
142
- strand,
179
+ strand: transcriptCheck.strand,
143
180
  refName,
181
+ uniqueId,
144
182
  subfeatures,
145
- };
146
- }
147
- }
148
- export function arrayify(f) {
149
- if (f === undefined) {
150
- return undefined;
151
- }
152
- if (typeof f === 'string') {
153
- return f.split(',').map(s => +s);
183
+ thickStart: transcriptCheck.thickStart,
184
+ thickEnd: transcriptCheck.thickEnd,
185
+ });
154
186
  }
155
- return f;
187
+ return {
188
+ ...rest,
189
+ uniqueId,
190
+ score,
191
+ start,
192
+ end,
193
+ strand,
194
+ refName,
195
+ subfeatures,
196
+ };
156
197
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/plugin-bed",
3
- "version": "4.3.0",
3
+ "version": "5.0.0-beta.10",
4
4
  "type": "module",
5
5
  "description": "JBrowse 2 bed adapters, tracks, etc.",
6
6
  "keywords": [
@@ -21,14 +21,14 @@
21
21
  "esm"
22
22
  ],
23
23
  "dependencies": {
24
- "@flatten-js/interval-tree": "^2.0.3",
25
- "@gmod/bbi": "^9.0.9",
26
- "@gmod/bed": "^2.2.1",
27
- "@gmod/tabix": "^3.3.3",
28
- "@jbrowse/mobx-state-tree": "^5.6.0",
29
- "mobx": "^6.15.3",
24
+ "@gmod/bbi": "^11.3.0",
25
+ "@gmod/bed": "^2.3.1",
26
+ "@gmod/tabix": "^3.8.3",
27
+ "@gmod/trix": "^3.0.22",
28
+ "@jbrowse/mobx-state-tree": "^6.6.1",
29
+ "mobx": "^7.0.5",
30
30
  "rxjs": "^7.8.2",
31
- "@jbrowse/core": "^4.3.0"
31
+ "@jbrowse/core": "^5.0.0-beta.10"
32
32
  },
33
33
  "publishConfig": {
34
34
  "access": "public"
@@ -38,14 +38,11 @@
38
38
  "build": "pnpm run /^build:/",
39
39
  "test": "cd ../..; jest --passWithNoTests plugins/bed",
40
40
  "prebuild": "pnpm clean",
41
- "build:esm": "tsc -p tsconfig.build.esm.json",
41
+ "build:esm": "node ../../node_modules/typescript7/bin/tsc --build tsconfig.build.esm.json",
42
42
  "clean": "rimraf esm *.tsbuildinfo"
43
43
  },
44
44
  "types": "esm/index.d.ts",
45
45
  "exports": {
46
- ".": {
47
- "types": "./esm/index.d.ts",
48
- "import": "./esm/index.js"
49
- }
46
+ ".": "./esm/index.js"
50
47
  }
51
48
  }
@@ -1,2 +0,0 @@
1
- import type PluginManager from '@jbrowse/core/PluginManager';
2
- export default function GuessAdapterF(pluginManager: PluginManager): void;
@@ -1,71 +0,0 @@
1
- import { testAdapter } from '@jbrowse/core/util';
2
- import { getFileName, makeIndex, makeIndexType, } from '@jbrowse/core/util/tracks';
3
- export default function GuessAdapterF(pluginManager) {
4
- pluginManager.addToExtensionPoint('Core-guessAdapterForLocation', (adapterGuesser) => {
5
- return (file, index, adapterHint) => {
6
- const fileName = getFileName(file);
7
- const indexName = index && getFileName(index);
8
- if (testAdapter(fileName, /\.bedpe(\.gz)?$/i, adapterHint, 'BedpeAdapter')) {
9
- return {
10
- type: 'BedpeAdapter',
11
- bedpeLocation: file,
12
- };
13
- }
14
- else if (testAdapter(fileName, /\.bb$/i, adapterHint, 'BigBedAdapter') ||
15
- testAdapter(fileName, /\.bigbed$/i, adapterHint, 'BigBedAdapter')) {
16
- return {
17
- type: 'BigBedAdapter',
18
- bigBedLocation: file,
19
- };
20
- }
21
- else if (testAdapter(fileName, /\.bed$/i, adapterHint, 'BedAdapter')) {
22
- return {
23
- type: 'BedAdapter',
24
- bedLocation: file,
25
- };
26
- }
27
- else if (testAdapter(fileName, /\.bg$/i, adapterHint, 'BedGraphAdapter')) {
28
- return {
29
- type: 'BedGraphAdapter',
30
- bedGraphLocation: file,
31
- };
32
- }
33
- else if (testAdapter(fileName, /\.bg(\.gz)?$/i, adapterHint, 'BedGraphTabixAdapter')) {
34
- return {
35
- type: 'BedGraphTabixAdapter',
36
- bedGraphGzLocation: file,
37
- index: {
38
- location: index || makeIndex(file, '.tbi'),
39
- indexType: makeIndexType(indexName, 'CSI', 'TBI'),
40
- },
41
- };
42
- }
43
- else if (testAdapter(fileName, /\.bedmethyl\.gz$/i, adapterHint, 'BedTabixAdapter') ||
44
- testAdapter(fileName, /\.bed\.gz$/i, adapterHint, 'BedTabixAdapter')) {
45
- return {
46
- type: 'BedTabixAdapter',
47
- bedGzLocation: file,
48
- index: {
49
- location: index || makeIndex(file, '.tbi'),
50
- indexType: makeIndexType(indexName, 'CSI', 'TBI'),
51
- },
52
- };
53
- }
54
- else {
55
- return adapterGuesser(file, index, adapterHint);
56
- }
57
- };
58
- });
59
- pluginManager.addToExtensionPoint('Core-guessTrackTypeForLocation', (trackTypeGuesser) => (adapterName, file) => {
60
- if (adapterName === 'BedTabixAdapter' &&
61
- file &&
62
- /\.bedmethyl\.gz$/i.test(getFileName(file))) {
63
- return 'MultiQuantitativeTrack';
64
- }
65
- return ({
66
- BedpeAdapter: 'VariantTrack',
67
- BedGraphAdapter: 'QuantitativeTrack',
68
- BedGraphTabixAdapter: 'QuantitativeTrack',
69
- }[adapterName] || trackTypeGuesser(adapterName, file));
70
- });
71
- }