@jbrowse/cli 4.0.4 → 4.1.3
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/README.md +269 -111
- package/bin/run +2 -1
- package/bundle/index.js +289 -538
- package/dist/base.js +1 -2
- package/dist/bin.js +2 -4
- package/dist/cliFetch.js +3 -0
- package/dist/commands/add-assembly/index.js +19 -22
- package/dist/commands/add-assembly/utils.js +75 -92
- package/dist/commands/add-connection.js +16 -22
- package/dist/commands/add-track-json.js +13 -16
- package/dist/commands/add-track-utils/adapter-utils.js +6 -13
- package/dist/commands/add-track-utils/file-operations.js +8 -14
- package/dist/commands/add-track-utils/track-config.js +10 -18
- package/dist/commands/add-track-utils/validators.js +15 -27
- package/dist/commands/add-track.js +27 -33
- package/dist/commands/admin-server/index.js +24 -30
- package/dist/commands/admin-server/utils.js +27 -38
- package/dist/commands/create.js +16 -22
- package/dist/commands/make-pif/cigar-utils.js +3 -8
- package/dist/commands/make-pif/file-utils.js +10 -18
- package/dist/commands/make-pif/index.js +13 -16
- package/dist/commands/make-pif/pif-generator.js +14 -23
- package/dist/commands/process-utils.js +1 -4
- package/dist/commands/remove-track.js +8 -11
- package/dist/commands/set-default-session.js +13 -19
- package/dist/commands/shared/config-operations.js +7 -11
- package/dist/commands/shared/sort-utils.js +7 -14
- package/dist/commands/shared/validators.js +4 -8
- package/dist/commands/sort-bed.js +14 -17
- package/dist/commands/sort-gff.js +14 -17
- package/dist/commands/text-index/adapter-utils.js +5 -10
- package/dist/commands/text-index/aggregate.js +12 -15
- package/dist/commands/text-index/command.js +9 -12
- package/dist/commands/text-index/config-utils.js +24 -38
- package/dist/commands/text-index/file-list.js +11 -17
- package/dist/commands/text-index/index.js +4 -11
- package/dist/commands/text-index/indexing-utils.js +59 -43
- package/dist/commands/text-index/per-track.js +13 -16
- package/dist/commands/text-index/validators.js +3 -8
- package/dist/commands/track-utils.js +22 -33
- package/dist/commands/upgrade.js +20 -26
- package/dist/index.js +31 -39
- package/dist/types/common.js +5 -107
- package/dist/util.js +13 -20
- package/dist/utils.js +82 -58
- package/dist/version.js +1 -0
- package/package.json +7 -6
- package/dist/fetchWithProxy.js +0 -12
- package/dist/types/gff3Adapter.js +0 -42
- package/dist/types/streamUtils.js +0 -66
- package/dist/types/vcfAdapter.js +0 -39
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.adapterTypesToTrackTypeMap = void 0;
|
|
4
|
-
exports.makeLocationProtocol = makeLocationProtocol;
|
|
5
|
-
exports.guessFileNames = guessFileNames;
|
|
6
|
-
exports.guessAdapter = guessAdapter;
|
|
7
|
-
exports.guessTrackType = guessTrackType;
|
|
8
|
-
function makeLocationProtocol(protocol) {
|
|
1
|
+
export function makeLocationProtocol(protocol) {
|
|
9
2
|
return (location) => {
|
|
10
3
|
if (protocol === 'uri') {
|
|
11
4
|
return {
|
|
@@ -22,7 +15,7 @@ function makeLocationProtocol(protocol) {
|
|
|
22
15
|
throw new Error(`invalid protocol ${protocol}`);
|
|
23
16
|
};
|
|
24
17
|
}
|
|
25
|
-
function guessFileNames({ location, index, bed1, bed2, }) {
|
|
18
|
+
export function guessFileNames({ location, index, bed1, bed2, }) {
|
|
26
19
|
if (/\.anchors(.simple)?$/i.test(location)) {
|
|
27
20
|
return {
|
|
28
21
|
file: location,
|
|
@@ -85,7 +78,7 @@ function guessFileNames({ location, index, bed1, bed2, }) {
|
|
|
85
78
|
}
|
|
86
79
|
return {};
|
|
87
80
|
}
|
|
88
|
-
function guessAdapter({ location, protocol, index, bed1, bed2, }) {
|
|
81
|
+
export function guessAdapter({ location, protocol, index, bed1, bed2, }) {
|
|
89
82
|
const makeLocation = makeLocationProtocol(protocol);
|
|
90
83
|
if (/\.bam$/i.test(location)) {
|
|
91
84
|
return {
|
|
@@ -279,7 +272,7 @@ function guessAdapter({ location, protocol, index, bed1, bed2, }) {
|
|
|
279
272
|
type: 'UNKNOWN',
|
|
280
273
|
};
|
|
281
274
|
}
|
|
282
|
-
|
|
275
|
+
export const adapterTypesToTrackTypeMap = {
|
|
283
276
|
BamAdapter: 'AlignmentsTrack',
|
|
284
277
|
CramAdapter: 'AlignmentsTrack',
|
|
285
278
|
BgzipFastaAdapter: 'ReferenceSequenceTrack',
|
|
@@ -299,6 +292,6 @@ exports.adapterTypesToTrackTypeMap = {
|
|
|
299
292
|
MCScanAnchorsAdapter: 'SyntenyTrack',
|
|
300
293
|
MCScanSimpleAnchorsAdapter: 'SyntenyTrack',
|
|
301
294
|
};
|
|
302
|
-
function guessTrackType(adapterType) {
|
|
303
|
-
return
|
|
295
|
+
export function guessTrackType(adapterType) {
|
|
296
|
+
return adapterTypesToTrackTypeMap[adapterType] || 'FeatureTrack';
|
|
304
297
|
}
|
|
@@ -1,24 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
exports.loadFile = loadFile;
|
|
7
|
-
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const { copyFile, rename, symlink, unlink } = fs_1.default.promises;
|
|
10
|
-
const { COPYFILE_EXCL } = fs_1.default.constants;
|
|
11
|
-
async function loadFile({ src, destDir, mode, subDir = '', force = false, }) {
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
const { copyFile, rename, symlink, unlink } = fs.promises;
|
|
4
|
+
const { COPYFILE_EXCL } = fs.constants;
|
|
5
|
+
export async function loadFile({ src, destDir, mode, subDir = '', force = false, }) {
|
|
12
6
|
if (mode === 'inPlace') {
|
|
13
7
|
return;
|
|
14
8
|
}
|
|
15
|
-
const dest =
|
|
9
|
+
const dest = path.join(destDir, subDir, path.basename(src));
|
|
16
10
|
if (force) {
|
|
17
11
|
try {
|
|
18
12
|
await unlink(dest);
|
|
19
13
|
}
|
|
20
14
|
catch (e) {
|
|
21
|
-
if (e.code !== 'ENOENT') {
|
|
15
|
+
if (e && typeof e === 'object' && 'code' in e && e.code !== 'ENOENT') {
|
|
22
16
|
throw e;
|
|
23
17
|
}
|
|
24
18
|
}
|
|
@@ -30,7 +24,7 @@ async function loadFile({ src, destDir, mode, subDir = '', force = false, }) {
|
|
|
30
24
|
return rename(src, dest);
|
|
31
25
|
}
|
|
32
26
|
if (mode === 'symlink') {
|
|
33
|
-
return symlink(
|
|
27
|
+
return symlink(path.resolve(src), dest);
|
|
34
28
|
}
|
|
35
29
|
return undefined;
|
|
36
30
|
}
|
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.mapLocationForFiles = mapLocationForFiles;
|
|
7
|
-
exports.buildTrackConfig = buildTrackConfig;
|
|
8
|
-
exports.addSyntenyAssemblyNames = addSyntenyAssemblyNames;
|
|
9
|
-
const path_1 = __importDefault(require("path"));
|
|
10
|
-
const json_parse_better_errors_1 = __importDefault(require("json-parse-better-errors"));
|
|
11
|
-
const common_ts_1 = require("../../types/common.js");
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import parseJSON from 'json-parse-better-errors';
|
|
3
|
+
import { isURL } from "../../types/common.js";
|
|
12
4
|
const SYNTENY_ADAPTERS = new Set([
|
|
13
5
|
'PAFAdapter',
|
|
14
6
|
'PairwiseIndexedPAFAdapter',
|
|
@@ -18,14 +10,14 @@ const SYNTENY_ADAPTERS = new Set([
|
|
|
18
10
|
'MCScanAnchorsAdapter',
|
|
19
11
|
'MCScanSimpleAnchorsAdapter',
|
|
20
12
|
]);
|
|
21
|
-
function mapLocationForFiles(p, load, subDir) {
|
|
22
|
-
return !p ||
|
|
13
|
+
export function mapLocationForFiles(p, load, subDir) {
|
|
14
|
+
return !p || isURL(p) || load === 'inPlace'
|
|
23
15
|
? p
|
|
24
|
-
:
|
|
16
|
+
: path.join(subDir || '', path.basename(p));
|
|
25
17
|
}
|
|
26
|
-
function buildTrackConfig({ location, trackType, trackId, name, assemblyNames, category, description, config, adapter, configContents, }) {
|
|
27
|
-
const configObj = config ? (
|
|
28
|
-
const finalTrackId = trackId ||
|
|
18
|
+
export function buildTrackConfig({ location, trackType, trackId, name, assemblyNames, category, description, config, adapter, configContents, }) {
|
|
19
|
+
const configObj = config ? parseJSON(config) : {};
|
|
20
|
+
const finalTrackId = trackId || path.basename(location, path.extname(location));
|
|
29
21
|
const finalName = name || finalTrackId;
|
|
30
22
|
const finalAssemblyNames = assemblyNames || configContents.assemblies?.[0]?.name || '';
|
|
31
23
|
const trackConfig = {
|
|
@@ -40,7 +32,7 @@ function buildTrackConfig({ location, trackType, trackId, name, assemblyNames, c
|
|
|
40
32
|
};
|
|
41
33
|
return trackConfig;
|
|
42
34
|
}
|
|
43
|
-
function addSyntenyAssemblyNames(adapter, assemblyNames) {
|
|
35
|
+
export function addSyntenyAssemblyNames(adapter, assemblyNames) {
|
|
44
36
|
if (SYNTENY_ADAPTERS.has(adapter.type)) {
|
|
45
37
|
return {
|
|
46
38
|
...adapter,
|
|
@@ -1,39 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.validateLoadOption = validateLoadOption;
|
|
7
|
-
exports.validateTrackArg = validateTrackArg;
|
|
8
|
-
exports.validateLoadAndLocation = validateLoadAndLocation;
|
|
9
|
-
exports.validateAdapterType = validateAdapterType;
|
|
10
|
-
exports.validateAssemblies = validateAssemblies;
|
|
11
|
-
exports.validateTrackId = validateTrackId;
|
|
12
|
-
exports.createTargetDirectory = createTargetDirectory;
|
|
13
|
-
const fs_1 = __importDefault(require("fs"));
|
|
14
|
-
const path_1 = __importDefault(require("path"));
|
|
15
|
-
const common_ts_1 = require("../../types/common.js");
|
|
16
|
-
function validateLoadOption(load) {
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { isURL } from "../../types/common.js";
|
|
4
|
+
export function validateLoadOption(load) {
|
|
17
5
|
if (load && !['copy', 'symlink', 'move', 'inPlace'].includes(load)) {
|
|
18
6
|
throw new Error('Error: --load must be one of: copy, symlink, move, inPlace');
|
|
19
7
|
}
|
|
20
8
|
}
|
|
21
|
-
function validateTrackArg(track) {
|
|
9
|
+
export function validateTrackArg(track) {
|
|
22
10
|
if (!track) {
|
|
23
11
|
throw new Error('Missing 1 required arg:\ntrack Track file or URL\nSee more help with --help');
|
|
24
12
|
}
|
|
25
13
|
}
|
|
26
|
-
function validateLoadAndLocation(location, load) {
|
|
27
|
-
if (
|
|
14
|
+
export function validateLoadAndLocation(location, load) {
|
|
15
|
+
if (isURL(location) && load) {
|
|
28
16
|
throw new Error('The --load flag is used for local files only, but a URL was provided');
|
|
29
17
|
}
|
|
30
|
-
else if (!
|
|
18
|
+
else if (!isURL(location) && !load) {
|
|
31
19
|
throw new Error(`The --load flag should be used if a local file is used, example --load
|
|
32
20
|
copy to copy the file into the config directory. Options for load are
|
|
33
21
|
copy/move/symlink/inPlace (inPlace for no file operations)`);
|
|
34
22
|
}
|
|
35
23
|
}
|
|
36
|
-
function validateAdapterType(adapterType) {
|
|
24
|
+
export function validateAdapterType(adapterType) {
|
|
37
25
|
if (adapterType === 'UNKNOWN') {
|
|
38
26
|
throw new Error('Track type is not recognized');
|
|
39
27
|
}
|
|
@@ -41,7 +29,7 @@ function validateAdapterType(adapterType) {
|
|
|
41
29
|
throw new Error('Track type is not supported');
|
|
42
30
|
}
|
|
43
31
|
}
|
|
44
|
-
function validateAssemblies(configContents, assemblyNames) {
|
|
32
|
+
export function validateAssemblies(configContents, assemblyNames) {
|
|
45
33
|
if (!configContents.assemblies?.length) {
|
|
46
34
|
throw new Error('No assemblies found. Please add one before adding tracks');
|
|
47
35
|
}
|
|
@@ -49,7 +37,7 @@ function validateAssemblies(configContents, assemblyNames) {
|
|
|
49
37
|
throw new Error('Too many assemblies, cannot default to one. Please specify the assembly with the --assemblyNames flag');
|
|
50
38
|
}
|
|
51
39
|
}
|
|
52
|
-
function validateTrackId(configContents, trackId, force, overwrite) {
|
|
40
|
+
export function validateTrackId(configContents, trackId, force, overwrite) {
|
|
53
41
|
if (!configContents.tracks) {
|
|
54
42
|
configContents.tracks = [];
|
|
55
43
|
}
|
|
@@ -59,11 +47,11 @@ function validateTrackId(configContents, trackId, force, overwrite) {
|
|
|
59
47
|
}
|
|
60
48
|
return idx;
|
|
61
49
|
}
|
|
62
|
-
function createTargetDirectory(configDir, subDir) {
|
|
50
|
+
export function createTargetDirectory(configDir, subDir) {
|
|
63
51
|
if (subDir) {
|
|
64
|
-
const dir =
|
|
65
|
-
if (!
|
|
66
|
-
|
|
52
|
+
const dir = path.join(configDir, subDir);
|
|
53
|
+
if (!fs.existsSync(dir)) {
|
|
54
|
+
fs.mkdirSync(dir);
|
|
67
55
|
}
|
|
68
56
|
}
|
|
69
57
|
}
|
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const utils_ts_1 = require("../utils.js");
|
|
10
|
-
const adapter_utils_ts_1 = require("./add-track-utils/adapter-utils.js");
|
|
11
|
-
const track_config_ts_1 = require("./add-track-utils/track-config.js");
|
|
12
|
-
const validators_ts_1 = require("./add-track-utils/validators.js");
|
|
13
|
-
const track_utils_ts_1 = require("./track-utils.js");
|
|
14
|
-
async function run(args) {
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { parseArgs } from 'util';
|
|
3
|
+
import { printHelp, resolveConfigPath } from "../utils.js";
|
|
4
|
+
import { guessAdapter } from "./add-track-utils/adapter-utils.js";
|
|
5
|
+
import { addSyntenyAssemblyNames, mapLocationForFiles, } from "./add-track-utils/track-config.js";
|
|
6
|
+
import { createTargetDirectory, validateAdapterType, validateAssemblies, validateLoadAndLocation, validateLoadOption, validateTrackArg, } from "./add-track-utils/validators.js";
|
|
7
|
+
import { addTrackToConfig, buildTrackParams, createTrackConfiguration, loadTrackConfig, processTrackFiles, saveTrackConfigAndReport, } from "./track-utils.js";
|
|
8
|
+
export async function run(args) {
|
|
15
9
|
const options = {
|
|
16
10
|
help: {
|
|
17
11
|
type: 'boolean',
|
|
@@ -96,7 +90,7 @@ async function run(args) {
|
|
|
96
90
|
description: 'Used only for mcscan anchors/simpleAnchors types',
|
|
97
91
|
},
|
|
98
92
|
};
|
|
99
|
-
const { values: flags, positionals } =
|
|
93
|
+
const { values: flags, positionals } = parseArgs({
|
|
100
94
|
args,
|
|
101
95
|
options,
|
|
102
96
|
allowPositionals: true,
|
|
@@ -122,7 +116,7 @@ async function run(args) {
|
|
|
122
116
|
'$ jbrowse add-track /url/relative/path.bam --load inPlace',
|
|
123
117
|
];
|
|
124
118
|
if (flags.help) {
|
|
125
|
-
|
|
119
|
+
printHelp({
|
|
126
120
|
description,
|
|
127
121
|
examples,
|
|
128
122
|
usage: 'jbrowse add-track <track> [options]',
|
|
@@ -130,48 +124,48 @@ async function run(args) {
|
|
|
130
124
|
});
|
|
131
125
|
return;
|
|
132
126
|
}
|
|
133
|
-
|
|
127
|
+
validateLoadOption(flags.load);
|
|
134
128
|
const track = positionals[0];
|
|
135
|
-
|
|
129
|
+
validateTrackArg(track);
|
|
136
130
|
const { config, force, overwrite, category, description: trackDescription, load, subDir = '', target, protocol = 'uri', out, indexFile: index, bed1, bed2, } = flags;
|
|
137
|
-
const targetConfigPath = await
|
|
138
|
-
const configDir =
|
|
139
|
-
|
|
131
|
+
const targetConfigPath = await resolveConfigPath(target, out);
|
|
132
|
+
const configDir = path.dirname(targetConfigPath);
|
|
133
|
+
createTargetDirectory(configDir, subDir);
|
|
140
134
|
const location = track;
|
|
141
|
-
const mapLoc = (p) =>
|
|
142
|
-
let adapter =
|
|
135
|
+
const mapLoc = (p) => mapLocationForFiles(p, load, subDir);
|
|
136
|
+
let adapter = guessAdapter({
|
|
143
137
|
protocol,
|
|
144
138
|
location: mapLoc(location),
|
|
145
139
|
index: index ? mapLoc(index) : undefined,
|
|
146
140
|
bed1: bed1 ? mapLoc(bed1) : undefined,
|
|
147
141
|
bed2: bed2 ? mapLoc(bed2) : undefined,
|
|
148
142
|
});
|
|
149
|
-
adapter =
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const configContents = await
|
|
153
|
-
|
|
154
|
-
const trackParams =
|
|
143
|
+
adapter = addSyntenyAssemblyNames(adapter, flags.assemblyNames);
|
|
144
|
+
validateLoadAndLocation(location, load);
|
|
145
|
+
validateAdapterType(adapter.type);
|
|
146
|
+
const configContents = await loadTrackConfig(targetConfigPath);
|
|
147
|
+
validateAssemblies(configContents, flags.assemblyNames);
|
|
148
|
+
const trackParams = buildTrackParams({
|
|
155
149
|
flags,
|
|
156
150
|
location,
|
|
157
151
|
adapter,
|
|
158
152
|
configContents,
|
|
159
153
|
});
|
|
160
|
-
const trackConfig =
|
|
154
|
+
const trackConfig = createTrackConfiguration({
|
|
161
155
|
location,
|
|
162
156
|
trackParams,
|
|
163
157
|
flags: { category, description: trackDescription, config },
|
|
164
158
|
adapter,
|
|
165
159
|
configContents,
|
|
166
160
|
});
|
|
167
|
-
const { updatedConfig, wasOverwritten } =
|
|
161
|
+
const { updatedConfig, wasOverwritten } = addTrackToConfig({
|
|
168
162
|
configContents,
|
|
169
163
|
trackConfig,
|
|
170
164
|
trackId: trackParams.trackId,
|
|
171
165
|
force,
|
|
172
166
|
overwrite,
|
|
173
167
|
});
|
|
174
|
-
await
|
|
168
|
+
await processTrackFiles({
|
|
175
169
|
location,
|
|
176
170
|
index,
|
|
177
171
|
bed1,
|
|
@@ -181,7 +175,7 @@ async function run(args) {
|
|
|
181
175
|
subDir,
|
|
182
176
|
force,
|
|
183
177
|
});
|
|
184
|
-
await
|
|
178
|
+
await saveTrackConfigAndReport({
|
|
185
179
|
config: updatedConfig,
|
|
186
180
|
targetConfigPath,
|
|
187
181
|
name: trackParams.name,
|
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const util_1 = require("util");
|
|
11
|
-
const cors_1 = __importDefault(require("cors"));
|
|
12
|
-
const express_1 = __importDefault(require("express"));
|
|
13
|
-
const utils_ts_1 = require("./utils.js");
|
|
14
|
-
const utils_ts_2 = require("../../utils.js");
|
|
15
|
-
async function run(args) {
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { parseArgs } from 'util';
|
|
5
|
+
import cors from 'cors';
|
|
6
|
+
import express from 'express';
|
|
7
|
+
import { generateKey, parsePort, setupConfigFile, setupRoutes, startServer, } from "./utils.js";
|
|
8
|
+
import { debug, printHelp } from "../../utils.js";
|
|
9
|
+
export async function run(args) {
|
|
16
10
|
const options = {
|
|
17
11
|
help: {
|
|
18
12
|
type: 'boolean',
|
|
@@ -32,7 +26,7 @@ async function run(args) {
|
|
|
32
26
|
description: 'Size limit of the update message (default: 25mb)',
|
|
33
27
|
},
|
|
34
28
|
};
|
|
35
|
-
const { values: flags } =
|
|
29
|
+
const { values: flags } = parseArgs({
|
|
36
30
|
args,
|
|
37
31
|
options,
|
|
38
32
|
allowPositionals: true,
|
|
@@ -40,7 +34,7 @@ async function run(args) {
|
|
|
40
34
|
const description = 'Start up a small admin server for JBrowse configuration';
|
|
41
35
|
const examples = ['$ jbrowse admin-server', '$ jbrowse admin-server -p 8888'];
|
|
42
36
|
if (flags.help) {
|
|
43
|
-
|
|
37
|
+
printHelp({
|
|
44
38
|
description,
|
|
45
39
|
examples,
|
|
46
40
|
usage: 'jbrowse admin-server [options]',
|
|
@@ -49,20 +43,20 @@ async function run(args) {
|
|
|
49
43
|
return;
|
|
50
44
|
}
|
|
51
45
|
const { root, bodySizeLimit = '25mb' } = flags;
|
|
52
|
-
const { outFile, baseDir } = await
|
|
46
|
+
const { outFile, baseDir } = await setupConfigFile({ root });
|
|
53
47
|
// Parse and validate port
|
|
54
|
-
const port =
|
|
48
|
+
const port = parsePort({ portStr: flags.port });
|
|
55
49
|
// Set up the Express server
|
|
56
50
|
// const { app, key, keyPath, serverRef } = setupServer({
|
|
57
51
|
// baseDir,
|
|
58
52
|
// outFile,
|
|
59
53
|
// bodySizeLimit,
|
|
60
54
|
// })
|
|
61
|
-
const app = (
|
|
55
|
+
const app = express();
|
|
62
56
|
// Configure middleware
|
|
63
|
-
app.use(
|
|
64
|
-
app.use((
|
|
65
|
-
app.use(
|
|
57
|
+
app.use(express.static(baseDir));
|
|
58
|
+
app.use(cors());
|
|
59
|
+
app.use(express.json({ limit: bodySizeLimit }));
|
|
66
60
|
// Add error handling middleware
|
|
67
61
|
app.use((err, _req, res, next) => {
|
|
68
62
|
if (err) {
|
|
@@ -75,20 +69,20 @@ async function run(args) {
|
|
|
75
69
|
}
|
|
76
70
|
});
|
|
77
71
|
// Generate admin key and store it
|
|
78
|
-
const key =
|
|
79
|
-
const keyPath =
|
|
72
|
+
const key = generateKey();
|
|
73
|
+
const keyPath = path.join(os.tmpdir(), `jbrowse-admin-${key}`);
|
|
80
74
|
try {
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
fs.writeFileSync(keyPath, key);
|
|
76
|
+
debug(`Admin key stored at ${keyPath}`);
|
|
83
77
|
}
|
|
84
78
|
catch (error) {
|
|
85
|
-
console.error(`Failed to write admin key to ${keyPath}:`, error.message);
|
|
79
|
+
console.error(`Failed to write admin key to ${keyPath}:`, error instanceof Error ? error.message : error);
|
|
86
80
|
// Continue anyway, as this is not critical
|
|
87
81
|
}
|
|
88
82
|
// Create server reference for shutdown route
|
|
89
83
|
const serverRef = { current: null };
|
|
90
84
|
// Set up routes
|
|
91
|
-
|
|
85
|
+
setupRoutes({
|
|
92
86
|
app,
|
|
93
87
|
baseDir,
|
|
94
88
|
outFile,
|
|
@@ -96,5 +90,5 @@ async function run(args) {
|
|
|
96
90
|
serverRef,
|
|
97
91
|
});
|
|
98
92
|
// Start the server and set up shutdown handlers
|
|
99
|
-
|
|
93
|
+
startServer({ app, port, key, outFile, keyPath, serverRef });
|
|
100
94
|
}
|
|
@@ -1,29 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
exports.isValidPort = isValidPort;
|
|
7
|
-
exports.parsePort = parsePort;
|
|
8
|
-
exports.generateKey = generateKey;
|
|
9
|
-
exports.setupConfigFile = setupConfigFile;
|
|
10
|
-
exports.setupRoutes = setupRoutes;
|
|
11
|
-
exports.startServer = startServer;
|
|
12
|
-
const crypto_1 = __importDefault(require("crypto"));
|
|
13
|
-
const fs_1 = __importDefault(require("fs"));
|
|
14
|
-
const path_1 = __importDefault(require("path"));
|
|
15
|
-
const utils_ts_1 = require("../../utils.js");
|
|
16
|
-
const utils_ts_2 = require("../add-assembly/utils.js");
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { debug, resolveConfigPath, writeJsonFile } from "../../utils.js";
|
|
5
|
+
import { createDefaultConfig } from "../add-assembly/utils.js";
|
|
17
6
|
/**
|
|
18
7
|
* Validates if a port number is in the valid range
|
|
19
8
|
*/
|
|
20
|
-
function isValidPort(port) {
|
|
9
|
+
export function isValidPort(port) {
|
|
21
10
|
return port > 0 && port < 65535;
|
|
22
11
|
}
|
|
23
12
|
/**
|
|
24
13
|
* Parses and validates a port string
|
|
25
14
|
*/
|
|
26
|
-
function parsePort({ portStr, defaultPort = 9090, }) {
|
|
15
|
+
export function parsePort({ portStr, defaultPort = 9090, }) {
|
|
27
16
|
if (!portStr) {
|
|
28
17
|
return defaultPort;
|
|
29
18
|
}
|
|
@@ -36,21 +25,21 @@ function parsePort({ portStr, defaultPort = 9090, }) {
|
|
|
36
25
|
/**
|
|
37
26
|
* Generates a random alphanumeric string to serve as admin key
|
|
38
27
|
*/
|
|
39
|
-
function generateKey() {
|
|
40
|
-
return
|
|
28
|
+
export function generateKey() {
|
|
29
|
+
return crypto.randomBytes(5).toString('hex');
|
|
41
30
|
}
|
|
42
31
|
/**
|
|
43
32
|
* Sets up the configuration file
|
|
44
33
|
*/
|
|
45
|
-
async function setupConfigFile({ root = '.', } = {}) {
|
|
46
|
-
const outFile = await
|
|
47
|
-
const baseDir =
|
|
48
|
-
if (
|
|
49
|
-
|
|
34
|
+
export async function setupConfigFile({ root = '.', } = {}) {
|
|
35
|
+
const outFile = await resolveConfigPath(root);
|
|
36
|
+
const baseDir = path.dirname(outFile);
|
|
37
|
+
if (fs.existsSync(outFile)) {
|
|
38
|
+
debug(`Found existing config file ${outFile}`);
|
|
50
39
|
}
|
|
51
40
|
else {
|
|
52
|
-
|
|
53
|
-
await
|
|
41
|
+
debug(`Creating config file ${outFile}`);
|
|
42
|
+
await writeJsonFile(outFile, createDefaultConfig());
|
|
54
43
|
}
|
|
55
44
|
return { outFile, baseDir };
|
|
56
45
|
}
|
|
@@ -71,13 +60,13 @@ function validateAndExtractParams({ req, key, baseDir, outFile, }) {
|
|
|
71
60
|
try {
|
|
72
61
|
// Normalize the config path
|
|
73
62
|
const configPath = configPathParam
|
|
74
|
-
?
|
|
63
|
+
? path.normalize(path.join(baseDir, configPathParam))
|
|
75
64
|
: outFile;
|
|
76
65
|
// Check for directory traversal attempts
|
|
77
|
-
const normalizedBaseDir =
|
|
78
|
-
const relPath =
|
|
66
|
+
const normalizedBaseDir = path.normalize(baseDir);
|
|
67
|
+
const relPath = path.relative(normalizedBaseDir, configPath);
|
|
79
68
|
// Ensure the config path is within the base directory and doesn't contain path traversal
|
|
80
|
-
if (relPath.startsWith('..') ||
|
|
69
|
+
if (relPath.startsWith('..') || path.isAbsolute(relPath)) {
|
|
81
70
|
return { isValid: false, error: 'Cannot perform directory traversal' };
|
|
82
71
|
}
|
|
83
72
|
return { isValid: true, configPath };
|
|
@@ -89,7 +78,7 @@ function validateAndExtractParams({ req, key, baseDir, outFile, }) {
|
|
|
89
78
|
/**
|
|
90
79
|
* Sets up the API routes for the server
|
|
91
80
|
*/
|
|
92
|
-
function setupRoutes({ app, baseDir, outFile, key, serverRef, }) {
|
|
81
|
+
export function setupRoutes({ app, baseDir, outFile, key, serverRef, }) {
|
|
93
82
|
// Root route
|
|
94
83
|
app.get('/', (_req, res) => {
|
|
95
84
|
res.setHeader('Content-Type', 'text/plain');
|
|
@@ -110,7 +99,7 @@ function setupRoutes({ app, baseDir, outFile, key, serverRef, }) {
|
|
|
110
99
|
delete body.adminKey;
|
|
111
100
|
}
|
|
112
101
|
try {
|
|
113
|
-
|
|
102
|
+
fs.writeFileSync(validation.configPath, JSON.stringify(config, null, 2));
|
|
114
103
|
res.setHeader('Content-Type', 'text/plain');
|
|
115
104
|
res.send('Config updated successfully');
|
|
116
105
|
}
|
|
@@ -128,8 +117,8 @@ function setupRoutes({ app, baseDir, outFile, key, serverRef, }) {
|
|
|
128
117
|
return;
|
|
129
118
|
}
|
|
130
119
|
try {
|
|
131
|
-
if (
|
|
132
|
-
const config =
|
|
120
|
+
if (fs.existsSync(validation.configPath)) {
|
|
121
|
+
const config = fs.readFileSync(validation.configPath, 'utf8');
|
|
133
122
|
res.setHeader('Content-Type', 'text/plain');
|
|
134
123
|
res.send(config);
|
|
135
124
|
}
|
|
@@ -166,7 +155,7 @@ function setupRoutes({ app, baseDir, outFile, key, serverRef, }) {
|
|
|
166
155
|
/**
|
|
167
156
|
* Starts the server and sets up shutdown handlers
|
|
168
157
|
*/
|
|
169
|
-
function startServer({ app, port, key, outFile, keyPath, serverRef, }) {
|
|
158
|
+
export function startServer({ app, port, key, outFile, keyPath, serverRef, }) {
|
|
170
159
|
// Start the server
|
|
171
160
|
const server = app.listen(port, () => {
|
|
172
161
|
console.log(`Admin server started on port ${port}\n\n` +
|
|
@@ -194,8 +183,8 @@ function startServer({ app, port, key, outFile, keyPath, serverRef, }) {
|
|
|
194
183
|
server.close(() => {
|
|
195
184
|
// Clean up admin key file
|
|
196
185
|
try {
|
|
197
|
-
|
|
198
|
-
|
|
186
|
+
fs.unlinkSync(keyPath);
|
|
187
|
+
debug(`Removed admin key file: ${keyPath}`);
|
|
199
188
|
}
|
|
200
189
|
catch (error) {
|
|
201
190
|
// Ignore errors when cleaning up
|
package/dist/commands/create.js
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const util_1 = require("util");
|
|
9
|
-
const decompress_1 = __importDefault(require("decompress"));
|
|
10
|
-
const fetchWithProxy_ts_1 = __importDefault(require("../fetchWithProxy.js"));
|
|
11
|
-
const utils_ts_1 = require("../utils.js");
|
|
12
|
-
const fsPromises = fs_1.default.promises;
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { parseArgs } from 'util';
|
|
3
|
+
import decompress from 'decompress';
|
|
4
|
+
import fetch from "../cliFetch.js";
|
|
5
|
+
import { fetchGithubVersions, getBranch, getLatest, getTag, printHelp, } from "../utils.js";
|
|
6
|
+
const fsPromises = fs.promises;
|
|
13
7
|
const description = 'Downloads and installs the latest JBrowse 2 release';
|
|
14
8
|
const examples = [
|
|
15
9
|
'# Download latest release from github, and put in specific path',
|
|
@@ -59,17 +53,17 @@ const options = {
|
|
|
59
53
|
tag: {
|
|
60
54
|
type: 'string',
|
|
61
55
|
short: 't',
|
|
62
|
-
description: 'Version of JBrowse 2 to install. Format is v1.0.0
|
|
56
|
+
description: 'Version of JBrowse 2 to install. Format is v1.0.0. Defaults to latest',
|
|
63
57
|
},
|
|
64
58
|
};
|
|
65
|
-
async function run(args) {
|
|
66
|
-
const { positionals, values: runFlags } =
|
|
59
|
+
export async function run(args) {
|
|
60
|
+
const { positionals, values: runFlags } = parseArgs({
|
|
67
61
|
options,
|
|
68
62
|
allowPositionals: true,
|
|
69
63
|
args,
|
|
70
64
|
});
|
|
71
65
|
if (runFlags.help) {
|
|
72
|
-
|
|
66
|
+
printHelp({
|
|
73
67
|
description,
|
|
74
68
|
examples,
|
|
75
69
|
usage: 'jbrowse create [localPath] [options]',
|
|
@@ -86,7 +80,7 @@ async function run(args) {
|
|
|
86
80
|
}
|
|
87
81
|
const { force, url, listVersions, tag, branch, nightly } = runFlags;
|
|
88
82
|
if (listVersions) {
|
|
89
|
-
const versions = (await
|
|
83
|
+
const versions = (await fetchGithubVersions()).map(version => version.tag_name);
|
|
90
84
|
console.log(`All JBrowse versions:\n${versions.join('\n')}`);
|
|
91
85
|
process.exit(0);
|
|
92
86
|
}
|
|
@@ -96,11 +90,11 @@ async function run(args) {
|
|
|
96
90
|
await checkPath(argsPath);
|
|
97
91
|
}
|
|
98
92
|
const locationUrl = url ||
|
|
99
|
-
(nightly ? await
|
|
100
|
-
(branch ? await
|
|
101
|
-
(tag ? await
|
|
93
|
+
(nightly ? await getBranch('main') : '') ||
|
|
94
|
+
(branch ? await getBranch(branch) : '') ||
|
|
95
|
+
(tag ? await getTag(tag) : await getLatest());
|
|
102
96
|
console.log(`Fetching ${locationUrl}...`);
|
|
103
|
-
const response = await (
|
|
97
|
+
const response = await fetch(locationUrl);
|
|
104
98
|
if (!response.ok) {
|
|
105
99
|
throw new Error(`HTTP ${response.status} fetching ${locationUrl}: ${response.statusText}`);
|
|
106
100
|
}
|
|
@@ -110,7 +104,7 @@ async function run(args) {
|
|
|
110
104
|
type !== 'application/octet-stream') {
|
|
111
105
|
throw new Error('The URL provided does not seem to be a JBrowse installation URL');
|
|
112
106
|
}
|
|
113
|
-
await (
|
|
107
|
+
await decompress(Buffer.from(await response.arrayBuffer()), argsPath);
|
|
114
108
|
console.log(`Unpacked ${locationUrl} at ${argsPath}`);
|
|
115
109
|
}
|
|
116
110
|
async function checkPath(userPath) {
|