@apollo-annotation/cli 0.1.18 → 0.1.20

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 (59) hide show
  1. package/README.md +374 -135
  2. package/bin/dev.js +2 -2
  3. package/bin/run.js +2 -2
  4. package/dist/ApolloConf.js +1 -0
  5. package/dist/baseCommand.d.ts +1 -1
  6. package/dist/baseCommand.js +7 -7
  7. package/dist/commands/assembly/add-from-fasta.d.ts +23 -0
  8. package/dist/commands/assembly/add-from-fasta.js +165 -0
  9. package/dist/commands/assembly/{add-gff.d.ts → add-from-gff.d.ts} +5 -3
  10. package/dist/commands/assembly/{add-gff.js → add-from-gff.js} +20 -22
  11. package/dist/commands/assembly/check.js +9 -9
  12. package/dist/commands/assembly/delete.js +4 -4
  13. package/dist/commands/assembly/get.js +4 -4
  14. package/dist/commands/assembly/get.test.js +3 -3
  15. package/dist/commands/assembly/sequence.js +8 -14
  16. package/dist/commands/change/get.js +7 -7
  17. package/dist/commands/change/get.test.js +1 -1
  18. package/dist/commands/config.js +4 -5
  19. package/dist/commands/feature/add-child.js +18 -19
  20. package/dist/commands/feature/check.js +11 -11
  21. package/dist/commands/feature/copy.js +12 -14
  22. package/dist/commands/feature/delete.js +13 -19
  23. package/dist/commands/feature/edit-attribute.js +18 -11
  24. package/dist/commands/feature/edit-coords.js +36 -21
  25. package/dist/commands/feature/edit-type.js +7 -11
  26. package/dist/commands/feature/edit.js +5 -6
  27. package/dist/commands/feature/get-id.js +5 -6
  28. package/dist/commands/feature/get.js +3 -4
  29. package/dist/commands/feature/import.d.ts +2 -2
  30. package/dist/commands/feature/import.js +26 -39
  31. package/dist/commands/feature/search.js +7 -7
  32. package/dist/commands/file/delete.d.ts +13 -0
  33. package/dist/commands/file/delete.js +58 -0
  34. package/dist/commands/file/download.d.ts +14 -0
  35. package/dist/commands/file/download.js +45 -0
  36. package/dist/commands/file/get.d.ts +13 -0
  37. package/dist/commands/file/get.js +38 -0
  38. package/dist/commands/file/upload.d.ts +16 -0
  39. package/dist/commands/file/upload.js +88 -0
  40. package/dist/commands/jbrowse/get-config.d.ts +10 -0
  41. package/dist/commands/jbrowse/get-config.js +21 -0
  42. package/dist/commands/jbrowse/set-config.d.ts +13 -0
  43. package/dist/commands/jbrowse/set-config.js +51 -0
  44. package/dist/commands/login.js +12 -16
  45. package/dist/commands/logout.js +3 -3
  46. package/dist/commands/{assembly/add-fasta.d.ts → refseq/add-alias.d.ts} +3 -4
  47. package/dist/commands/refseq/add-alias.js +72 -0
  48. package/dist/commands/refseq/get.js +8 -8
  49. package/dist/commands/status.js +5 -4
  50. package/dist/commands/user/get.js +3 -3
  51. package/dist/commands/user/get.test.js +1 -1
  52. package/dist/fileCommand.d.ts +5 -0
  53. package/dist/fileCommand.js +76 -0
  54. package/dist/test/fixtures.js +2 -2
  55. package/dist/utils.d.ts +20 -9
  56. package/dist/utils.js +33 -80
  57. package/oclif.manifest.json +495 -58
  58. package/package.json +56 -42
  59. package/dist/commands/assembly/add-fasta.js +0 -88
@@ -0,0 +1,72 @@
1
+ import * as fs from 'node:fs';
2
+ import { Agent, fetch } from 'undici';
3
+ import { Flags } from '@oclif/core';
4
+ import { BaseCommand } from '../../baseCommand.js';
5
+ import { createFetchErrorMessage, localhostToAddress, queryApollo, } from '../../utils.js';
6
+ import { ConfigError } from '../../ApolloConf.js';
7
+ export default class AddRefNameAlias extends BaseCommand {
8
+ static summary = 'Add reference name aliases from a file';
9
+ static description = 'Reference name aliasing is a process to make chromosomes that are named slightly differently but which refer to the same thing render properly. This command reads a file with reference name aliases and adds them to the database.';
10
+ static examples = [
11
+ {
12
+ description: 'Add reference name aliases:',
13
+ command: '<%= config.bin %> <%= command.id %> -i alias.txt -a myAssembly',
14
+ },
15
+ ];
16
+ static flags = {
17
+ 'input-file': Flags.string({
18
+ char: 'i',
19
+ description: 'Input refname alias file',
20
+ required: true,
21
+ }),
22
+ assembly: Flags.string({
23
+ char: 'a',
24
+ description: 'Name for this assembly.',
25
+ required: true,
26
+ }),
27
+ };
28
+ async run() {
29
+ const { flags } = await this.parse(AddRefNameAlias);
30
+ if (!fs.existsSync(flags['input-file'])) {
31
+ this.error(`File ${flags['input-file']} does not exist`);
32
+ }
33
+ const access = await this.getAccess();
34
+ const filehandle = await fs.promises.open(flags['input-file']);
35
+ const fileContent = await filehandle.readFile({ encoding: 'utf8' });
36
+ await filehandle.close();
37
+ const lines = fileContent.split('\n');
38
+ const refNameAliases = [];
39
+ for (const line of lines) {
40
+ const [refName, ...aliases] = line.split('\t');
41
+ refNameAliases.push({ refName, aliases });
42
+ }
43
+ const assemblies = await queryApollo(access.address, access.accessToken, 'assemblies');
44
+ const json = (await assemblies.json());
45
+ const assembly = json.find((x) => 'name' in x && x.name === flags.assembly);
46
+ const assemblyId = assembly && '_id' in assembly ? assembly._id : undefined;
47
+ if (!assemblyId) {
48
+ this.error(`Assembly ${flags.assembly} not found`);
49
+ }
50
+ const change = {
51
+ typeName: 'AddRefSeqAliasesChange',
52
+ assembly: assemblyId,
53
+ refSeqAliases: refNameAliases,
54
+ };
55
+ const auth = {
56
+ method: 'POST',
57
+ body: JSON.stringify(change),
58
+ headers: {
59
+ Authorization: `Bearer ${access.accessToken}`,
60
+ 'Content-Type': 'application/json',
61
+ },
62
+ dispatcher: new Agent({ headersTimeout: 60 * 60 * 1000 }),
63
+ };
64
+ const url = new URL(localhostToAddress(`${access.address}/changes`));
65
+ const response = await fetch(url, auth);
66
+ if (!response.ok) {
67
+ const errorMessage = await createFetchErrorMessage(response, 'Failed to add reference name aliases');
68
+ throw new ConfigError(errorMessage);
69
+ }
70
+ this.log(`Reference name aliases added successfully to assembly ${flags.assembly}`);
71
+ }
72
+ }
@@ -1,18 +1,18 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import { BaseCommand } from '../../baseCommand.js';
3
- import { convertAssemblyNameToId, idReader, queryApollo, wrapLines, } from '../../utils.js';
3
+ import { convertAssemblyNameToId, idReader, queryApollo } from '../../utils.js';
4
4
  export default class Get extends BaseCommand {
5
5
  static summary = 'Get reference sequences';
6
- static description = wrapLines('Output the reference sequences in one or more assemblies in json format. \
7
- This command returns the sequence characteristics (e.g., name, ID, etc), not the DNA sequences. \
8
- Use `assembly sequence` for that.');
6
+ static description = 'Output the reference sequences in one or more assemblies in json format. \
7
+ This command returns the sequence characteristics (e.g., name, ID, etc), not the DNA sequences. \
8
+ Use `assembly sequence` for that.';
9
9
  static examples = [
10
10
  {
11
- description: wrapLines('All sequences in the database:'),
11
+ description: 'All sequences in the database:',
12
12
  command: '<%= config.bin %> <%= command.id %>',
13
13
  },
14
14
  {
15
- description: wrapLines('Only sequences for these assemblies:'),
15
+ description: 'Only sequences for these assemblies:',
16
16
  command: '<%= config.bin %> <%= command.id %> -a mm9 mm10',
17
17
  },
18
18
  ];
@@ -25,13 +25,13 @@ export default class Get extends BaseCommand {
25
25
  };
26
26
  async run() {
27
27
  const { flags } = await this.parse(Get);
28
- const access = await this.getAccess(flags['config-file'], flags.profile);
28
+ const access = await this.getAccess();
29
29
  const refSeqs = await queryApollo(access.address, access.accessToken, 'refSeqs');
30
30
  const json = (await refSeqs.json());
31
31
  let keep = json;
32
32
  if (flags.assembly !== undefined) {
33
33
  keep = [];
34
- const assembly = idReader(flags.assembly);
34
+ const assembly = await idReader(flags.assembly);
35
35
  const assemblyIds = await convertAssemblyNameToId(access.address, access.accessToken, assembly);
36
36
  for (const x of json) {
37
37
  if (assemblyIds.includes(x['assembly'])) {
@@ -2,11 +2,12 @@
2
2
  import path from 'node:path';
3
3
  import { ApolloConf, KEYS } from '../ApolloConf.js';
4
4
  import { BaseCommand } from '../baseCommand.js';
5
- import { basicCheckConfig, wrapLines } from '../utils.js';
5
+ import { basicCheckConfig } from '../utils.js';
6
6
  export default class Status extends BaseCommand {
7
7
  static summary = 'View authentication status';
8
- static description = wrapLines('This command returns "<profile>: Logged in" if the selected profile has an access token and "<profile>: Logged out" otherwise.\
9
- Note that this command does not check the validity of the access token.');
8
+ static description = 'This command returns "<profile>: Logged in" if the selected profile \
9
+ has an access token and "<profile>: Logged out" otherwise.\
10
+ Note that this command does not check the validity of the access token.';
10
11
  async run() {
11
12
  const { flags } = await this.parse(Status);
12
13
  let profileName = flags.profile;
@@ -15,7 +16,7 @@ export default class Status extends BaseCommand {
15
16
  }
16
17
  let configFile = flags['config-file'];
17
18
  if (configFile === undefined) {
18
- configFile = path.join(this.config.configDir, 'config.yaml');
19
+ configFile = path.join(this.config.configDir, 'config.yml');
19
20
  }
20
21
  basicCheckConfig(configFile, profileName);
21
22
  const config = new ApolloConf(configFile);
@@ -1,9 +1,9 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import { BaseCommand } from '../../baseCommand.js';
3
- import { queryApollo, wrapLines } from '../../utils.js';
3
+ import { queryApollo } from '../../utils.js';
4
4
  export default class Get extends BaseCommand {
5
5
  static summary = 'Get list of users';
6
- static description = wrapLines('If set, filters username and role must be both satisfied to return an entry');
6
+ static description = 'If set, filters username and role must be both satisfied to return an entry';
7
7
  static examples = [
8
8
  {
9
9
  description: 'By username:',
@@ -30,7 +30,7 @@ export default class Get extends BaseCommand {
30
30
  };
31
31
  async run() {
32
32
  const { flags } = await this.parse(Get);
33
- const access = await this.getAccess(flags['config-file'], flags.profile);
33
+ const access = await this.getAccess();
34
34
  const users = await queryApollo(access.address, access.accessToken, 'users');
35
35
  const json = (await users.json());
36
36
  const out = [];
@@ -7,7 +7,7 @@ const __dirname = fileURLToPath(new URL('.', import.meta.url));
7
7
  // TODO: Need valid token
8
8
  describe.skip('apollo user get: Get users as YAML string', () => {
9
9
  before(() => {
10
- copyFile(`${TEST_DATA_DIR}/complete_config.yaml`, CONFIG_FILE, VERBOSE);
10
+ copyFile(`${TEST_DATA_DIR}/complete_config.yml`, CONFIG_FILE, VERBOSE);
11
11
  });
12
12
  after(() => {
13
13
  fs.rmSync(CONFIG_FILE);
@@ -0,0 +1,5 @@
1
+ import { BaseCommand } from './baseCommand.js';
2
+ export declare abstract class FileCommand extends BaseCommand<typeof FileCommand> {
3
+ init(): Promise<void>;
4
+ uploadFile(address: string, accessToken: string, file: string, type: string, isGzip: boolean): Promise<string>;
5
+ }
@@ -0,0 +1,76 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { Transform, pipeline, } from 'node:stream';
4
+ import { SingleBar } from 'cli-progress';
5
+ import { Agent, fetch } from 'undici';
6
+ import { ConfigError } from './ApolloConf.js';
7
+ import { BaseCommand } from './baseCommand.js';
8
+ import { createFetchErrorMessage, localhostToAddress } from './utils.js';
9
+ class ProgressTransform extends Transform {
10
+ size = 0;
11
+ progressBar;
12
+ constructor(opts) {
13
+ super(opts);
14
+ this.progressBar = opts.progressBar;
15
+ }
16
+ _transform(chunk, _encoding, callback) {
17
+ this.size += chunk.length;
18
+ this.progressBar.update(this.size);
19
+ callback(null, chunk);
20
+ }
21
+ }
22
+ export class FileCommand extends BaseCommand {
23
+ async init() {
24
+ await super.init();
25
+ }
26
+ async uploadFile(address, accessToken, file, type, isGzip) {
27
+ const filehandle = await fs.promises.open(file);
28
+ const { size } = await filehandle.stat();
29
+ const stream = filehandle.createReadStream();
30
+ const progressBar = new SingleBar({ etaBuffer: 100_000_000 });
31
+ const progressTransform = new ProgressTransform({ progressBar });
32
+ const body = pipeline(stream, progressTransform, (error) => {
33
+ if (error) {
34
+ progressBar.stop();
35
+ console.error('Error processing file.', error);
36
+ throw error;
37
+ }
38
+ });
39
+ const headers = new Headers({
40
+ Authorization: `Bearer ${accessToken}`,
41
+ 'Content-Type': type,
42
+ 'Content-Length': String(size),
43
+ });
44
+ if (isGzip) {
45
+ headers.append('Content-Encoding', 'gzip');
46
+ }
47
+ const init = {
48
+ method: 'POST',
49
+ body,
50
+ duplex: 'half',
51
+ dispatcher: new Agent({ headersTimeout: 60 * 60 * 1000 }),
52
+ headers,
53
+ };
54
+ const fileName = path.basename(file);
55
+ const url = new URL(localhostToAddress(`${address}/files`));
56
+ url.searchParams.set('name', fileName);
57
+ url.searchParams.set('type', type);
58
+ progressBar.start(size, 0);
59
+ try {
60
+ const response = await fetch(url, init);
61
+ if (!response.ok) {
62
+ const errorMessage = await createFetchErrorMessage(response, 'uploadFile failed');
63
+ throw new ConfigError(errorMessage);
64
+ }
65
+ const json = (await response.json());
66
+ return json['_id'];
67
+ }
68
+ catch (error) {
69
+ console.error(error);
70
+ throw error;
71
+ }
72
+ finally {
73
+ progressBar.stop();
74
+ }
75
+ }
76
+ }
@@ -4,8 +4,8 @@ import path from 'node:path';
4
4
  export const TEST_DATA_DIR = path.resolve('test_data');
5
5
  export const VERBOSE = false;
6
6
  export const CONFIG_DIR = path.join(os.homedir(), '.config', 'apollo-cli');
7
- export const CONFIG_FILE = path.join(CONFIG_DIR, 'config.yaml');
8
- const CONFIG_BAK = path.join(TEST_DATA_DIR, 'original.config.yaml.bak');
7
+ export const CONFIG_FILE = path.join(CONFIG_DIR, 'config.yml');
8
+ const CONFIG_BAK = path.join(TEST_DATA_DIR, 'original.config.yml.bak');
9
9
  function renameFile(src, dest, verbose = true) {
10
10
  if (fs.existsSync(dest)) {
11
11
  throw new Error(`File ${dest} already exists`);
package/dist/utils.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import EventEmitter from 'node:events';
2
2
  import { Response } from 'undici';
3
3
  import { ApolloConf } from './ApolloConf.js';
4
+ import { ApolloAssemblySnapshot } from '@apollo-annotation/mst';
4
5
  export declare const CLI_SERVER_ADDRESS = "http://127.0.0.1:5657";
5
6
  export declare const CLI_SERVER_ADDRESS_CALLBACK = "http://127.0.0.1:5657/auth/callback";
6
7
  export declare class CheckError extends Error {
@@ -17,27 +18,38 @@ export declare function basicCheckConfig(configFile: string, profileName: string
17
18
  */
18
19
  export declare function localhostToAddress(url: string): string;
19
20
  export declare function deleteAssembly(address: string, accessToken: string, assemblyId: string): Promise<void>;
20
- export declare function getAssembly(address: string, accessToken: string, assemblyNameOrId: string): Promise<object>;
21
+ export declare function getAssembly(address: string, accessToken: string, assemblyNameOrId: string): Promise<ApolloAssemblySnapshot>;
21
22
  export declare function getRefseqId(address: string, accessToken: string, refseqNameOrId?: string, inAssemblyNameOrId?: string): Promise<string[]>;
22
23
  export declare function convertCheckNameToId(address: string, accessToken: string, namesOrIds: string[]): Promise<string[]>;
23
- export declare function assemblyNameToIdDict(address: string, accessToken: string): Promise<Record<string, string>>;
24
+ export declare function assemblyNameToIdDict(address: string, accessToken: string): Promise<Record<string, string | undefined>>;
24
25
  /** In input array namesOrIds, substitute common names with internal IDs */
25
26
  export declare function convertAssemblyNameToId(address: string, accessToken: string, namesOrIds: string[], verbose?: boolean, removeDuplicates?: boolean): Promise<string[]>;
26
27
  export declare function getFeatureById(address: string, accessToken: string, id: string): Promise<Response>;
27
28
  export declare function getAssemblyFromRefseq(address: string, accessToken: string, refSeq: string): Promise<string>;
28
29
  export declare function queryApollo(address: string, accessToken: string, endpoint: string): Promise<Response>;
29
30
  export declare function filterJsonList(json: object[], keep: string[], key: string): object[];
30
- export declare const getUserCredentials: () => UserCredentials | null;
31
31
  export declare const generatePkceChallenge: () => {
32
32
  state: string;
33
33
  codeVerifier: string;
34
34
  codeChallenge: string;
35
35
  };
36
36
  export declare const waitFor: <T>(eventName: string, emitter: EventEmitter) => Promise<T>;
37
- interface bodyLocalFile {
37
+ interface bodyFastaFile {
38
38
  assemblyName: string;
39
39
  typeName: string;
40
- fileId: string;
40
+ fileIds: {
41
+ fa: string;
42
+ };
43
+ assembly: string;
44
+ }
45
+ interface bodyIndexedFiles {
46
+ assemblyName: string;
47
+ typeName: string;
48
+ fileIds: {
49
+ fa: string;
50
+ fai: string;
51
+ gzi: string;
52
+ };
41
53
  }
42
54
  interface bodyExternalFile {
43
55
  assemblyName: string;
@@ -47,8 +59,7 @@ interface bodyExternalFile {
47
59
  fai: string;
48
60
  };
49
61
  }
50
- export declare function submitAssembly(address: string, accessToken: string, body: bodyLocalFile | bodyExternalFile, force: boolean): Promise<Response>;
51
- export declare function uploadFile(address: string, accessToken: string, file: string, type: string): Promise<string>;
52
- export declare function wrapLines(s: string, length?: number): string;
53
- export declare function idReader(input: string[], removeDuplicates?: boolean): string[];
62
+ export declare function submitAssembly(address: string, accessToken: string, body: bodyFastaFile | bodyExternalFile | bodyIndexedFiles, force: boolean): Promise<object>;
63
+ export declare function readStdin(): Promise<string>;
64
+ export declare function idReader(input: string[], removeDuplicates?: boolean): Promise<string[]>;
54
65
  export {};
package/dist/utils.js CHANGED
@@ -1,15 +1,8 @@
1
- /* eslint-disable @typescript-eslint/no-unnecessary-condition */
2
- /* eslint-disable @typescript-eslint/no-unsafe-call */
3
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
4
- /* eslint-disable @typescript-eslint/no-unsafe-assignment */
5
- /* eslint-disable @typescript-eslint/no-unsafe-return */
6
1
  import * as crypto from 'node:crypto';
7
2
  import * as fs from 'node:fs';
8
- import * as os from 'node:os';
9
- import * as path from 'node:path';
10
- import { Agent, FormData, Response, fetch } from 'undici';
3
+ import { stdin, stderr } from 'node:process';
4
+ import { Agent, fetch } from 'undici';
11
5
  import { ApolloConf, ConfigError } from './ApolloConf.js';
12
- const CONFIG_PATH = path.resolve(os.homedir(), '.clirc');
13
6
  export const CLI_SERVER_ADDRESS = 'http://127.0.0.1:5657';
14
7
  export const CLI_SERVER_ADDRESS_CALLBACK = `${CLI_SERVER_ADDRESS}/auth/callback`;
15
8
  export class CheckError extends Error {
@@ -61,6 +54,7 @@ export async function deleteAssembly(address, accessToken, assemblyId) {
61
54
  Authorization: `Bearer ${accessToken}`,
62
55
  'Content-Type': 'application/json',
63
56
  },
57
+ dispatcher: new Agent({ headersTimeout: 60 * 60 * 1000 }),
64
58
  };
65
59
  const url = new URL(localhostToAddress(`${address}/changes`));
66
60
  const response = await fetch(url, auth);
@@ -72,18 +66,16 @@ export async function deleteAssembly(address, accessToken, assemblyId) {
72
66
  export async function getAssembly(address, accessToken, assemblyNameOrId) {
73
67
  const assemblyId = await convertAssemblyNameToId(address, accessToken, [assemblyNameOrId]);
74
68
  if (assemblyId.length === 0) {
75
- return {};
69
+ throw new Error(`Assembly "${assemblyNameOrId}" not found`);
76
70
  }
77
71
  const res = await queryApollo(address, accessToken, 'assemblies');
78
72
  const assemblies = (await res.json());
79
- let assemblyObj = {};
80
73
  for (const x of assemblies) {
81
- if (x['_id'] === assemblyId[0]) {
82
- assemblyObj = JSON.parse(JSON.stringify(x));
83
- break;
74
+ if (x._id === assemblyId[0]) {
75
+ return JSON.parse(JSON.stringify(x));
84
76
  }
85
77
  }
86
- return assemblyObj;
78
+ throw new Error(`Assembly "${assemblyNameOrId}" not found`);
87
79
  }
88
80
  export async function getRefseqId(address, accessToken, refseqNameOrId, inAssemblyNameOrId) {
89
81
  if (refseqNameOrId === undefined && inAssemblyNameOrId === undefined) {
@@ -128,11 +120,11 @@ export async function getRefseqId(address, accessToken, refseqNameOrId, inAssemb
128
120
  }
129
121
  async function checkNameToIdDict(address, accessToken) {
130
122
  const asm = await queryApollo(address, accessToken, 'checks/types');
131
- const ja = (await asm.json());
123
+ const ja = (await asm.json()); // Not sure if CheckResultSnapshot is the right interface
132
124
  const nameToId = {};
133
125
  for (const x of ja) {
134
- const name = x['name'];
135
- nameToId[name] = x['_id'];
126
+ const { _id, name } = x; // x['name' as keyof typeof x]
127
+ nameToId[name] = _id; // x['_id' as keyof typeof x]
136
128
  }
137
129
  return nameToId;
138
130
  }
@@ -173,7 +165,7 @@ export async function convertAssemblyNameToId(address, accessToken, namesOrIds,
173
165
  ids.push(x);
174
166
  }
175
167
  else if (verbose) {
176
- process.stderr.write(`Warning: Omitting unknown assembly: "${x}"\n`);
168
+ stderr.write(`Warning: Omitting unknown assembly: "${x}"\n`);
177
169
  }
178
170
  }
179
171
  if (removeDuplicates) {
@@ -221,15 +213,6 @@ export function filterJsonList(json, keep, key) {
221
213
  }
222
214
  return results;
223
215
  }
224
- export const getUserCredentials = () => {
225
- try {
226
- const content = fs.readFileSync(CONFIG_PATH, { encoding: 'utf8' });
227
- return JSON.parse(content);
228
- }
229
- catch {
230
- return null;
231
- }
232
- };
233
216
  export const generatePkceChallenge = () => {
234
217
  const codeVerifier = crypto.randomBytes(64).toString('hex');
235
218
  const codeChallenge = crypto
@@ -256,7 +239,7 @@ export const waitFor = (eventName, emitter) => {
256
239
  return promise;
257
240
  };
258
241
  export async function submitAssembly(address, accessToken, body, force) {
259
- const assemblies = await queryApollo(address, accessToken, 'assemblies');
242
+ let assemblies = await queryApollo(address, accessToken, 'assemblies');
260
243
  for (const x of (await assemblies.json())) {
261
244
  if (x['name'] === body.assemblyName) {
262
245
  if (force) {
@@ -267,10 +250,6 @@ export async function submitAssembly(address, accessToken, body, force) {
267
250
  }
268
251
  }
269
252
  }
270
- const controller = new AbortController();
271
- setTimeout(() => {
272
- controller.abort();
273
- }, 24 * 60 * 60 * 1000);
274
253
  const auth = {
275
254
  method: 'POST',
276
255
  body: JSON.stringify(body),
@@ -278,60 +257,35 @@ export async function submitAssembly(address, accessToken, body, force) {
278
257
  Authorization: `Bearer ${accessToken}`,
279
258
  'Content-Type': 'application/json',
280
259
  },
281
- signal: controller.signal,
260
+ dispatcher: new Agent({ headersTimeout: 60 * 60 * 1000 }),
282
261
  };
283
262
  const url = new URL(localhostToAddress(`${address}/changes`));
284
263
  const response = await fetch(url, auth);
285
264
  if (!response.ok) {
286
265
  const errorMessage = await createFetchErrorMessage(response, 'submitAssembly failed');
287
- throw new ConfigError(errorMessage);
266
+ throw new Error(errorMessage);
288
267
  }
289
- return response;
290
- }
291
- export async function uploadFile(address, accessToken, file, type) {
292
- const stream = fs.createReadStream(file, 'utf8');
293
- const fileStream = new Response(stream);
294
- const fileBlob = await fileStream.blob();
295
- const formData = new FormData();
296
- formData.append('type', type);
297
- formData.append('file', fileBlob);
298
- const auth = {
299
- method: 'POST',
300
- body: formData,
301
- headers: {
302
- Authorization: `Bearer ${accessToken}`,
303
- },
304
- dispatcher: new Agent({
305
- keepAliveTimeout: 10 * 60 * 1000,
306
- keepAliveMaxTimeout: 10 * 60 * 1000, // 10 minutes
307
- }),
308
- };
309
- const url = new URL(localhostToAddress(`${address}/files`));
310
- const response = await fetch(url, auth);
311
- if (!response.ok) {
312
- const errorMessage = await createFetchErrorMessage(response, 'uploadFile failed');
313
- throw new ConfigError(errorMessage);
268
+ assemblies = await queryApollo(address, accessToken, 'assemblies');
269
+ for (const x of (await assemblies.json())) {
270
+ if (x['name'] === body.assemblyName) {
271
+ return x;
272
+ }
314
273
  }
315
- const json = (await response.json());
316
- return json['_id'];
274
+ throw new Error(`Failed to retrieve assembly ${body.assemblyName}`);
317
275
  }
318
- /* Wrap text to max `length` per line */
319
- export function wrapLines(s, length) {
320
- if (length === undefined) {
321
- length = 80;
276
+ export async function readStdin() {
277
+ const chunks = [];
278
+ for await (const chunk of stdin) {
279
+ chunks.push(Buffer.from(chunk));
322
280
  }
323
- // Credit: https://stackoverflow.com/questions/14484787/wrap-text-in-javascript
324
- const re = new RegExp(`(?![^\\n]{1,${length}}$)([^\\n]{1,${length}})\\s`, 'g');
325
- s = s.replaceAll(/ +/g, ' ');
326
- const wr = s.replace(re, '$1\n');
327
- return wr;
281
+ return Buffer.concat(chunks).toString('utf8');
328
282
  }
329
- export function idReader(input, removeDuplicates = true) {
283
+ export async function idReader(input, removeDuplicates = true) {
330
284
  let ids = [];
331
285
  for (const xin of input) {
332
286
  let data;
333
287
  if (xin == '-') {
334
- data = fs.readFileSync('/dev/stdin').toString();
288
+ data = await readStdin();
335
289
  }
336
290
  else if (fs.existsSync(xin)) {
337
291
  data = fs.readFileSync(xin).toString();
@@ -340,14 +294,13 @@ export function idReader(input, removeDuplicates = true) {
340
294
  data = xin;
341
295
  }
342
296
  try {
343
- data = JSON.parse(data);
344
- if (data.length === undefined) {
345
- data = [data];
297
+ let parsedData = JSON.parse(data);
298
+ if (!Array.isArray(parsedData)) {
299
+ parsedData = [parsedData];
346
300
  }
347
- for (const x of data) {
348
- const id = x['_id'];
349
- if (id !== undefined) {
350
- ids.push(id);
301
+ for (const x of parsedData) {
302
+ if ('_id' in x && typeof x._id === 'string') {
303
+ ids.push(x._id);
351
304
  }
352
305
  }
353
306
  }