@jbrowse/cli 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 (71) hide show
  1. package/README.md +451 -72
  2. package/dist/base.js +0 -3
  3. package/dist/bin.js +2 -1
  4. package/dist/cliFetch.js +3 -0
  5. package/dist/commands/add-assembly/index.js +46 -19
  6. package/dist/commands/add-assembly/utils.js +204 -290
  7. package/dist/commands/add-connection.js +73 -79
  8. package/dist/commands/add-track-json.js +24 -12
  9. package/dist/commands/add-track-utils/adapter-utils.js +189 -288
  10. package/dist/commands/add-track-utils/file-operations.js +27 -11
  11. package/dist/commands/add-track-utils/multiwig.js +47 -0
  12. package/dist/commands/add-track-utils/track-config.js +66 -25
  13. package/dist/commands/add-track-utils/validators.js +36 -20
  14. package/dist/commands/add-track.js +200 -62
  15. package/dist/commands/admin-server/index.js +39 -50
  16. package/dist/commands/admin-server/utils.js +238 -126
  17. package/dist/commands/create.js +18 -34
  18. package/dist/commands/make-density/density-generator.js +180 -0
  19. package/dist/commands/make-density/index.js +139 -0
  20. package/dist/commands/make-pif/index.js +133 -11
  21. package/dist/commands/make-pif/pif-generator.js +243 -43
  22. package/dist/commands/process-utils.js +13 -2
  23. package/dist/commands/remove-track.js +20 -13
  24. package/dist/commands/set-default-session.js +35 -31
  25. package/dist/commands/shared/config-operations.js +3 -10
  26. package/dist/commands/shared/density.js +12 -0
  27. package/dist/commands/shared/sort-utils.js +67 -10
  28. package/dist/commands/shared/validators.js +38 -3
  29. package/dist/commands/sort-bed.js +2 -33
  30. package/dist/commands/sort-gff.js +2 -33
  31. package/dist/commands/text-index/adapter-utils.js +19 -26
  32. package/dist/commands/text-index/aggregate.js +40 -34
  33. package/dist/commands/text-index/command.js +37 -7
  34. package/dist/commands/text-index/config-utils.js +62 -85
  35. package/dist/commands/text-index/file-list.js +32 -18
  36. package/dist/commands/text-index/index.js +4 -4
  37. package/dist/commands/text-index/indexing-utils.js +44 -96
  38. package/dist/commands/text-index/per-track.js +44 -31
  39. package/dist/commands/text-index/validators.js +1 -1
  40. package/dist/commands/upgrade.js +20 -32
  41. package/dist/commands/validate/configManifest.generated.js +7613 -0
  42. package/dist/commands/validate/configSchema.generated.js +20400 -0
  43. package/dist/commands/validate/displayDefaultKeys.js +28 -0
  44. package/dist/commands/validate/index.js +113 -0
  45. package/dist/commands/validate/liftConfig.js +34 -0
  46. package/dist/commands/validate/markRules/aggregateFieldName.js +9 -0
  47. package/dist/commands/validate/markRules/autoBin.js +27 -0
  48. package/dist/commands/validate/markRules/colorScale.js +244 -0
  49. package/dist/commands/validate/markRules/cssColorsLevel4.js +162 -0
  50. package/dist/commands/validate/markRules/jexlStrings.js +23 -0
  51. package/dist/commands/validate/markRules/markProblems.js +532 -0
  52. package/dist/commands/validate/markRules/markRuleFacts.js +6 -0
  53. package/dist/commands/validate/markRules/markSpecs.js +96 -0
  54. package/dist/commands/validate/markRules/markVocabulary.js +57 -0
  55. package/dist/commands/validate/markRules/shapeNameList.js +7 -0
  56. package/dist/commands/validate/markRules/stepChannels.js +55 -0
  57. package/dist/commands/validate/schemaValidate.js +425 -0
  58. package/dist/commands/validate/suggest.js +35 -0
  59. package/dist/commands/validate/types.js +6 -0
  60. package/dist/commands/validate/validateConfig.js +502 -0
  61. package/dist/index.js +104 -50
  62. package/dist/types/common.js +3 -27
  63. package/dist/utils.js +309 -128
  64. package/dist/version.js +1 -1
  65. package/package.json +8 -14
  66. package/bin/run +0 -4
  67. package/bundle/index.js +0 -3686
  68. package/dist/commands/make-pif/cigar-utils.js +0 -24
  69. package/dist/commands/make-pif/file-utils.js +0 -30
  70. package/dist/commands/track-utils.js +0 -74
  71. package/dist/util.js +0 -35
@@ -1,29 +1,26 @@
1
- import path from 'path';
2
- import { parseArgs } from 'util';
3
- import parseJSON from 'json-parse-better-errors';
4
- import fetch from "../cliFetch.js";
5
- import { debug, printHelp, readJsonFile, resolveConfigPath, writeJsonFile, } from "../utils.js";
6
- async function resolveURL(location, check = true) {
7
- let locationUrl;
1
+ import { parseArgs } from 'node:util';
2
+ import { debug, parseCommaSeparatedString, printHelp, readConfigFile, requirePositional, resolveConfigPath, } from '../utils.js';
3
+ import { parseConfigFlag } from './add-track-utils/validators.js';
4
+ import { findAndUpdateOrAdd, saveConfigAndReport, } from './shared/config-operations.js';
5
+ function resolveURL(location) {
8
6
  try {
9
- locationUrl = new URL(location);
7
+ return new URL(location);
10
8
  }
11
9
  catch (error) {
12
- throw new Error(`The location ${location} provided is not a valid URL`);
13
- }
14
- if (check) {
15
- const response = await fetch(`${locationUrl}`, { method: 'HEAD' });
16
- if (!response.ok) {
17
- throw new Error(`HTTP ${response.status} fetching ${locationUrl}`);
18
- }
10
+ throw new Error(`The location ${location} provided is not a valid URL`, {
11
+ cause: error,
12
+ });
19
13
  }
20
- return locationUrl.href;
21
14
  }
15
+ // a hub.txt filename and a jbrowse/data path are the two conventions we can
16
+ // recognize. Both are read off the URL's path rather than the whole href, so a
17
+ // query string can't fake one and a host that happens to be named hub.txt can't
18
+ // be mistaken for a hub file.
22
19
  function determineConnectionType(url) {
23
- if (path.basename(url) === 'hub.txt') {
20
+ if (url.pathname.endsWith('/hub.txt')) {
24
21
  return 'UCSCTrackHubConnection';
25
22
  }
26
- if (url.includes('jbrowse/data')) {
23
+ if (url.pathname.includes('jbrowse/data')) {
27
24
  return 'JBrowse1Connection';
28
25
  }
29
26
  return 'custom';
@@ -66,18 +63,10 @@ export async function run(args) {
66
63
  type: 'string',
67
64
  description: 'Synonym for target',
68
65
  },
69
- skipCheck: {
70
- type: 'boolean',
71
- description: "Don't check whether the data directory URL exists",
72
- },
73
- overwrite: {
74
- type: 'boolean',
75
- description: 'Overwrites any existing connections if same connection id',
76
- },
77
66
  force: {
78
67
  type: 'boolean',
79
68
  short: 'f',
80
- description: 'Equivalent to --skipCheck --overwrite',
69
+ description: 'Overwrite existing connection if one with the same id exists',
81
70
  },
82
71
  };
83
72
  const { values: flags, positionals } = parseArgs({
@@ -86,81 +75,86 @@ export async function run(args) {
86
75
  allowPositionals: true,
87
76
  });
88
77
  const description = 'Add a connection to a JBrowse 2 configuration';
78
+ const usage = 'jbrowse add-connection <connectionUrlOrPath> [options]';
89
79
  const examples = [
90
- '$ jbrowse add-connection http://mysite.com/jbrowse/data/ -a hg19',
91
- '$ jbrowse add-connection http://mysite.com/jbrowse/custom_data_folder/ --type JBrowse1Connection -a hg38',
92
- '$ jbrowse add-connection http://mysite.com/path/to/hub.txt',
93
- '$ jbrowse add-connection http://mysite.com/path/to/custom_hub_name.txt --type UCSCTrackHubConnection',
94
- `$ jbrowse add-connection http://mysite.com/path/to/custom --type custom --config '{"uri":{"url":"https://mysite.com/path/to/custom"}, "locationType": "UriLocation"}' -a hg19`,
80
+ '# add a JBrowse 1 data directory connection (type inferred from the jbrowse/data path)',
81
+ '$ jbrowse add-connection https://mysite.com/jbrowse/data/ -a hg19',
82
+ '',
83
+ '# force the JBrowse1Connection type for a non-standard data folder path',
84
+ '$ jbrowse add-connection https://mysite.com/jbrowse/custom_data_folder/ --type JBrowse1Connection -a hg38',
85
+ '',
86
+ '# add a UCSC track hub (type inferred from the hub.txt filename)',
87
+ '$ jbrowse add-connection https://mysite.com/path/to/hub.txt',
88
+ '',
89
+ '# force the UCSCTrackHubConnection type for a hub file not named hub.txt',
90
+ '$ jbrowse add-connection https://mysite.com/path/to/custom_hub_name.txt --type UCSCTrackHubConnection',
91
+ '',
92
+ '# add a custom connection type with extra config',
93
+ `$ jbrowse add-connection https://mysite.com/path/to/custom --type custom --config '{"uri":{"url":"https://mysite.com/path/to/custom"}, "locationType": "UriLocation"}' -a hg19`,
94
+ '',
95
+ '# set an explicit id/name and write to a specific config file',
95
96
  '$ jbrowse add-connection https://mysite.com/path/to/hub.txt --connectionId newId --name newName --target /path/to/jb2/installation/config.json',
96
97
  ];
97
98
  if (flags.help) {
98
99
  printHelp({
99
100
  description,
100
101
  examples,
101
- usage: 'jbrowse add-connection <connectionUrlOrPath> [options]',
102
+ usage,
102
103
  options,
103
104
  });
104
105
  return;
105
106
  }
106
107
  const connectionUrlOrPath = positionals[0];
107
- if (!connectionUrlOrPath) {
108
- throw new Error('Missing required argument: connectionUrlOrPath\nUsage: jbrowse add-connection <connectionUrlOrPath> [options]');
109
- }
108
+ requirePositional(connectionUrlOrPath, 'connectionUrlOrPath', usage);
109
+ const { assemblyNames, type, name, config, connectionId, force } = flags;
110
+ const configObj = config ? parseConfigFlag(config) : undefined;
110
111
  const target = await resolveConfigPath(flags.target, flags.out);
111
- const { assemblyNames, type, name, config, connectionId, skipCheck, force } = flags;
112
- const url = await resolveURL(connectionUrlOrPath, !(skipCheck || force));
113
- const configContents = await readJsonFile(target);
112
+ const url = resolveURL(connectionUrlOrPath);
113
+ const configContents = await readConfigFile(target);
114
114
  debug(`Using config file ${target}`);
115
115
  if (!configContents.assemblies?.length) {
116
116
  throw new Error('No assemblies found. Please add one before adding connections');
117
117
  }
118
118
  const configType = type || determineConnectionType(url);
119
119
  const id = connectionId ||
120
- [configType, assemblyNames, Date.now()].filter(f => !!f).join('-');
120
+ [configType, assemblyNames, Date.now()].filter(Boolean).join('-');
121
+ // each known connection type stores the URL under its own location slot; a
122
+ // custom type carries no location, so its --config must supply one
123
+ const locationField = {
124
+ UCSCTrackHubConnection: 'hubTxtLocation',
125
+ JBrowse1Connection: 'dataDirLocation',
126
+ }[configType];
127
+ // a JBrowse 1 connection serves exactly one assembly, so it falls back to the
128
+ // config's first (the assemblies list is known non-empty by here)
129
+ const defaultAssemblyNames = configType === 'JBrowse1Connection'
130
+ ? [configContents.assemblies[0].name]
131
+ : undefined;
121
132
  const connectionConfig = {
122
133
  type: configType,
123
134
  name: name || id,
124
- ...(configType === 'UCSCTrackHubConnection'
125
- ? {
126
- hubTxtLocation: {
127
- uri: url,
128
- locationType: 'UriLocation',
129
- },
130
- }
131
- : {}),
132
- ...(configType === 'JBrowse1Connection'
133
- ? {
134
- dataDirLocation: {
135
- uri: url,
136
- locationType: 'UriLocation',
137
- },
138
- }
139
- : {}),
135
+ ...(locationField && {
136
+ [locationField]: { uri: url.href, locationType: 'UriLocation' },
137
+ }),
140
138
  connectionId: id,
141
139
  assemblyNames: assemblyNames
142
- ? assemblyNames.split(',')
143
- : type === 'JBrowse1Connection'
144
- ? [configContents.assemblies[0]?.name]
145
- : undefined,
146
- ...(config ? parseJSON(config) : {}),
140
+ ? parseCommaSeparatedString(assemblyNames)
141
+ : defaultAssemblyNames,
142
+ ...configObj,
147
143
  };
148
- if (!configContents.connections) {
149
- configContents.connections = [];
150
- }
151
- const idx = configContents.connections.findIndex(c => c.connectionId === connectionId);
152
- if (idx !== -1) {
153
- if (force || flags.overwrite) {
154
- configContents.connections[idx] = connectionConfig;
155
- }
156
- else {
157
- throw new Error(`Cannot add connection with id ${connectionId}, a connection with that id already exists.\nUse --overwrite if you would like to replace the existing connection`);
158
- }
159
- }
160
- else {
161
- configContents.connections.push(connectionConfig);
162
- }
163
- debug(`Writing configuration to file ${target}`);
164
- await writeJsonFile(target, configContents);
165
- console.log(`${idx !== -1 ? 'Overwrote' : 'Added'} connection "${name || id}" ${idx !== -1 ? 'in' : 'to'} ${target}`);
144
+ const { updatedItems: connections, wasOverwritten } = findAndUpdateOrAdd({
145
+ items: configContents.connections ?? [],
146
+ newItem: connectionConfig,
147
+ idField: 'connectionId',
148
+ getId: item => item.connectionId,
149
+ force: force ?? false,
150
+ itemType: 'connection',
151
+ });
152
+ await saveConfigAndReport({
153
+ config: { ...configContents, connections },
154
+ target,
155
+ itemType: 'connection',
156
+ itemName: name || id,
157
+ itemId: id,
158
+ wasOverwritten,
159
+ });
166
160
  }
@@ -1,6 +1,6 @@
1
- import { parseArgs } from 'util';
2
- import { debug, printHelp, readInlineOrFileJson, readJsonFile, resolveConfigPath, } from "../utils.js";
3
- import { findAndUpdateOrAdd, saveConfigAndReport, } from "./shared/config-operations.js";
1
+ import { parseArgs } from 'node:util';
2
+ import { debug, printHelp, readInlineOrFileJson, readConfigFile, requirePositional, resolveConfigPath, } from '../utils.js';
3
+ import { findAndUpdateOrAdd, saveConfigAndReport, } from './shared/config-operations.js';
4
4
  export async function run(args) {
5
5
  const options = {
6
6
  help: {
@@ -27,38 +27,50 @@ export async function run(args) {
27
27
  allowPositionals: true,
28
28
  });
29
29
  const description = 'Add a track configuration directly from a JSON hunk to the JBrowse 2 configuration';
30
+ const usage = 'jbrowse add-track-json <track> [options]';
31
+ const notes = 'The track argument is a JSON file, inline JSON, or "-" to read the JSON ' +
32
+ 'from stdin, so a track config can be generated and piped straight in.';
30
33
  const examples = [
34
+ '# add a track from a JSON file',
31
35
  '$ jbrowse add-track-json track.json',
36
+ '',
37
+ '# update an existing track (matched by trackId) with new JSON contents',
32
38
  '$ jbrowse add-track-json track.json --update',
39
+ '',
40
+ '# pass the track config inline instead of via a file',
41
+ `$ jbrowse add-track-json '{"type":"FeatureTrack","trackId":"genes","assemblyNames":["hg38"],"adapter":{"type":"Gff3TabixAdapter","gffGzLocation":{"uri":"genes.gff.gz"}}}'`,
42
+ '',
43
+ '# pipe the track config in from another program',
44
+ `$ jq '.tracks[0]' other/config.json | jbrowse add-track-json -`,
45
+ '',
46
+ '# write to a config.json in a specific installation directory',
47
+ '$ jbrowse add-track-json track.json --out /path/to/jb2/',
33
48
  ];
34
49
  if (flags.help) {
35
50
  printHelp({
36
51
  description,
37
52
  examples,
38
- usage: 'jbrowse add-track-json <track> [options]',
53
+ notes,
54
+ usage,
39
55
  options,
40
56
  });
41
57
  return;
42
58
  }
43
59
  const track = positionals[0];
44
- if (!track) {
45
- throw new Error('Missing required argument: track\nUsage: jbrowse add-track-json <track> [options]');
46
- }
60
+ requirePositional(track, 'track', usage);
47
61
  const target = await resolveConfigPath(flags.target, flags.out);
48
62
  debug(`Sequence location is: ${track}`);
49
63
  const { update } = flags;
50
- const config = await readJsonFile(target);
64
+ const config = await readConfigFile(target);
51
65
  debug(`Found existing config file ${target}`);
52
66
  const trackConfig = await readInlineOrFileJson(track);
53
- if (!config.tracks) {
54
- config.tracks = [];
55
- }
67
+ config.tracks ??= [];
56
68
  const { updatedItems, wasOverwritten } = findAndUpdateOrAdd({
57
69
  items: config.tracks,
58
70
  newItem: trackConfig,
59
71
  idField: 'trackId',
60
72
  getId: item => item.trackId,
61
- allowOverwrite: update ?? false,
73
+ force: update ?? false,
62
74
  itemType: 'track',
63
75
  });
64
76
  config.tracks = updatedItems;