@apollo-annotation/cli 0.1.19 → 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 (52) hide show
  1. package/README.md +283 -106
  2. package/dist/baseCommand.d.ts +1 -1
  3. package/dist/baseCommand.js +3 -4
  4. package/dist/commands/assembly/add-from-fasta.d.ts +23 -0
  5. package/dist/commands/assembly/add-from-fasta.js +165 -0
  6. package/dist/commands/assembly/{add-gff.d.ts → add-from-gff.d.ts} +5 -3
  7. package/dist/commands/assembly/{add-gff.js → add-from-gff.js} +20 -21
  8. package/dist/commands/assembly/check.js +9 -9
  9. package/dist/commands/assembly/delete.js +4 -4
  10. package/dist/commands/assembly/get.js +4 -4
  11. package/dist/commands/assembly/sequence.js +4 -4
  12. package/dist/commands/change/get.js +7 -7
  13. package/dist/commands/config.js +3 -4
  14. package/dist/commands/feature/add-child.js +6 -5
  15. package/dist/commands/feature/check.js +6 -6
  16. package/dist/commands/feature/copy.js +5 -4
  17. package/dist/commands/feature/delete.js +4 -4
  18. package/dist/commands/feature/edit-attribute.js +6 -5
  19. package/dist/commands/feature/edit-coords.js +5 -5
  20. package/dist/commands/feature/edit-type.js +5 -5
  21. package/dist/commands/feature/edit.js +5 -5
  22. package/dist/commands/feature/get-id.js +5 -5
  23. package/dist/commands/feature/get.js +3 -3
  24. package/dist/commands/feature/import.d.ts +2 -2
  25. package/dist/commands/feature/import.js +6 -6
  26. package/dist/commands/feature/search.js +6 -6
  27. package/dist/commands/file/delete.d.ts +13 -0
  28. package/dist/commands/file/delete.js +58 -0
  29. package/dist/commands/file/download.d.ts +14 -0
  30. package/dist/commands/file/download.js +45 -0
  31. package/dist/commands/file/get.d.ts +13 -0
  32. package/dist/commands/file/get.js +38 -0
  33. package/dist/commands/file/upload.d.ts +16 -0
  34. package/dist/commands/file/upload.js +88 -0
  35. package/dist/commands/jbrowse/get-config.d.ts +10 -0
  36. package/dist/commands/jbrowse/get-config.js +21 -0
  37. package/dist/commands/jbrowse/set-config.d.ts +13 -0
  38. package/dist/commands/jbrowse/set-config.js +51 -0
  39. package/dist/commands/login.js +11 -15
  40. package/dist/commands/logout.js +2 -2
  41. package/dist/commands/refseq/add-alias.js +3 -3
  42. package/dist/commands/refseq/get.js +8 -8
  43. package/dist/commands/status.js +4 -3
  44. package/dist/commands/user/get.js +3 -3
  45. package/dist/fileCommand.d.ts +5 -0
  46. package/dist/fileCommand.js +76 -0
  47. package/dist/utils.d.ts +20 -9
  48. package/dist/utils.js +25 -103
  49. package/oclif.manifest.json +445 -69
  50. package/package.json +50 -43
  51. package/dist/commands/assembly/add-fasta.d.ts +0 -15
  52. package/dist/commands/assembly/add-fasta.js +0 -87
@@ -0,0 +1,51 @@
1
+ import * as fs from 'node:fs';
2
+ import { Args } from '@oclif/core';
3
+ import { Agent, fetch } from 'undici';
4
+ import { ConfigError } from '../../ApolloConf.js';
5
+ import { BaseCommand } from '../../baseCommand.js';
6
+ import { localhostToAddress, createFetchErrorMessage } from '../../utils.js';
7
+ export default class SetConfig extends BaseCommand {
8
+ static summary = 'Set JBrowse configuration';
9
+ static description = 'Set JBrowse configuration in Apollo collaboration server';
10
+ static examples = [
11
+ {
12
+ description: 'Add JBrowse configuration:',
13
+ command: '<%= config.bin %> <%= command.id %> config.json',
14
+ },
15
+ ];
16
+ static args = {
17
+ inputFile: Args.string({
18
+ description: 'JBrowse configuration file',
19
+ required: true,
20
+ }),
21
+ };
22
+ async run() {
23
+ const { args } = await this.parse(SetConfig);
24
+ if (!fs.existsSync(args.inputFile)) {
25
+ this.error(`File ${args.inputFile} does not exist`);
26
+ }
27
+ const access = await this.getAccess();
28
+ const filehandle = await fs.promises.open(args.inputFile);
29
+ const fileContent = await filehandle.readFile({ encoding: 'utf8' });
30
+ await filehandle.close();
31
+ const change = {
32
+ typeName: 'ImportJBrowseConfigChange',
33
+ newJBrowseConfig: JSON.parse(fileContent),
34
+ };
35
+ const auth = {
36
+ method: 'POST',
37
+ body: JSON.stringify(change),
38
+ headers: {
39
+ Authorization: `Bearer ${access.accessToken}`,
40
+ 'Content-Type': 'application/json',
41
+ },
42
+ dispatcher: new Agent({ headersTimeout: 60 * 60 * 1000 }),
43
+ };
44
+ const url = new URL(localhostToAddress(`${access.address}/changes`));
45
+ const response = await fetch(url, auth);
46
+ if (!response.ok) {
47
+ const errorMessage = await createFetchErrorMessage(response, 'Failed to add JBrowse configuration');
48
+ throw new ConfigError(errorMessage);
49
+ }
50
+ }
51
+ }
@@ -8,21 +8,21 @@ import * as querystring from 'node:querystring';
8
8
  import { Errors, Flags, ux } from '@oclif/core';
9
9
  import open from 'open';
10
10
  import { fetch } from 'undici';
11
- import { ApolloConf } from '../ApolloConf.js';
11
+ import { ApolloConf, KEYS } from '../ApolloConf.js';
12
12
  import { BaseCommand } from '../baseCommand.js';
13
- import { basicCheckConfig, createFetchErrorMessage, getUserCredentials, localhostToAddress, waitFor, wrapLines, } from '../utils.js';
13
+ import { basicCheckConfig, createFetchErrorMessage, localhostToAddress, waitFor, } from '../utils.js';
14
14
  export default class Login extends BaseCommand {
15
15
  static summary = 'Login to Apollo';
16
- static description = wrapLines('Use the provided credentials to obtain and save the token to access Apollo. Once the token for \
17
- the given profile has been saved in the configuration file, users do not normally need to execute \
18
- this command again unless the token has expired. To setup a new profile use "apollo config"');
16
+ static description = 'Use the provided credentials to obtain and save the token to access Apollo. \
17
+ Once the token for the given profile has been saved in the configuration file, users do not normally \
18
+ need to execute this command again unless the token has expired. To setup a new profile use "apollo config"';
19
19
  static examples = [
20
20
  {
21
- description: wrapLines('The most basic and probably most typical usage is to login using the default profile in configuration file:'),
21
+ description: 'The most basic and probably most typical usage is to login using the default profile in configuration file:',
22
22
  command: '<%= config.bin %> <%= command.id %>',
23
23
  },
24
24
  {
25
- description: wrapLines('Login with a different profile:'),
25
+ description: 'Login with a different profile:',
26
26
  command: '<%= config.bin %> <%= command.id %> --profile my-profile',
27
27
  },
28
28
  ];
@@ -71,7 +71,7 @@ export default class Login extends BaseCommand {
71
71
  let userCredentials = { accessToken: '' };
72
72
  try {
73
73
  if (!flags.force) {
74
- await this.checkUserAlreadyLoggedIn();
74
+ await this.checkUserAlreadyLoggedIn(config, profileName);
75
75
  }
76
76
  if (accessType === 'root' || flags.username !== undefined) {
77
77
  const username = flags.username ??
@@ -104,13 +104,9 @@ export default class Login extends BaseCommand {
104
104
  }
105
105
  config.set(`${profileName}.accessToken`, userCredentials.accessToken);
106
106
  }
107
- async checkUserAlreadyLoggedIn() {
108
- const userCredentials = getUserCredentials();
109
- if (!userCredentials) {
110
- return;
111
- }
112
- const alreadyLoggedIn = Object.keys(userCredentials).every((key) => Boolean(userCredentials[key]));
113
- if (!alreadyLoggedIn) {
107
+ async checkUserAlreadyLoggedIn(userCredentials, profileName) {
108
+ const accessToken = userCredentials.get(`${profileName}.${KEYS.accessToken}`);
109
+ if (!accessToken) {
114
110
  return;
115
111
  }
116
112
  const reAuthenticate = await ux.confirm("You're already logged. Do you want to re-authenticate? (y/n)");
@@ -1,10 +1,10 @@
1
1
  import path from 'node:path';
2
2
  import { ApolloConf, KEYS } from '../ApolloConf.js';
3
3
  import { BaseCommand } from '../baseCommand.js';
4
- import { basicCheckConfig, wrapLines } from '../utils.js';
4
+ import { basicCheckConfig } from '../utils.js';
5
5
  export default class Logout extends BaseCommand {
6
6
  static summary = 'Logout of Apollo';
7
- static description = wrapLines('Logout by removing the access token from the selected profile');
7
+ static description = 'Logout by removing the access token from the selected profile';
8
8
  static examples = [
9
9
  {
10
10
  description: 'Logout default profile:',
@@ -2,11 +2,11 @@ import * as fs from 'node:fs';
2
2
  import { Agent, fetch } from 'undici';
3
3
  import { Flags } from '@oclif/core';
4
4
  import { BaseCommand } from '../../baseCommand.js';
5
- import { createFetchErrorMessage, localhostToAddress, queryApollo, wrapLines, } from '../../utils.js';
5
+ import { createFetchErrorMessage, localhostToAddress, queryApollo, } from '../../utils.js';
6
6
  import { ConfigError } from '../../ApolloConf.js';
7
7
  export default class AddRefNameAlias extends BaseCommand {
8
8
  static summary = 'Add reference name aliases from a file';
9
- static description = wrapLines('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.');
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
10
  static examples = [
11
11
  {
12
12
  description: 'Add reference name aliases:',
@@ -30,7 +30,7 @@ export default class AddRefNameAlias extends BaseCommand {
30
30
  if (!fs.existsSync(flags['input-file'])) {
31
31
  this.error(`File ${flags['input-file']} does not exist`);
32
32
  }
33
- const access = await this.getAccess(flags['config-file'], flags.profile);
33
+ const access = await this.getAccess();
34
34
  const filehandle = await fs.promises.open(flags['input-file']);
35
35
  const fileContent = await filehandle.readFile({ encoding: 'utf8' });
36
36
  await filehandle.close();
@@ -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;
@@ -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 = [];
@@ -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
+ }
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<never>;
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,17 +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 { Transform, pipeline, } from 'node:stream';
11
- import { SingleBar } from 'cli-progress';
3
+ import { stdin, stderr } from 'node:process';
12
4
  import { Agent, fetch } from 'undici';
13
5
  import { ApolloConf, ConfigError } from './ApolloConf.js';
14
- const CONFIG_PATH = path.resolve(os.homedir(), '.clirc');
15
6
  export const CLI_SERVER_ADDRESS = 'http://127.0.0.1:5657';
16
7
  export const CLI_SERVER_ADDRESS_CALLBACK = `${CLI_SERVER_ADDRESS}/auth/callback`;
17
8
  export class CheckError extends Error {
@@ -75,18 +66,16 @@ export async function deleteAssembly(address, accessToken, assemblyId) {
75
66
  export async function getAssembly(address, accessToken, assemblyNameOrId) {
76
67
  const assemblyId = await convertAssemblyNameToId(address, accessToken, [assemblyNameOrId]);
77
68
  if (assemblyId.length === 0) {
78
- return {};
69
+ throw new Error(`Assembly "${assemblyNameOrId}" not found`);
79
70
  }
80
71
  const res = await queryApollo(address, accessToken, 'assemblies');
81
72
  const assemblies = (await res.json());
82
- let assemblyObj = {};
83
73
  for (const x of assemblies) {
84
74
  if (x._id === assemblyId[0]) {
85
- assemblyObj = JSON.parse(JSON.stringify(x));
86
- break;
75
+ return JSON.parse(JSON.stringify(x));
87
76
  }
88
77
  }
89
- return assemblyObj;
78
+ throw new Error(`Assembly "${assemblyNameOrId}" not found`);
90
79
  }
91
80
  export async function getRefseqId(address, accessToken, refseqNameOrId, inAssemblyNameOrId) {
92
81
  if (refseqNameOrId === undefined && inAssemblyNameOrId === undefined) {
@@ -176,7 +165,7 @@ export async function convertAssemblyNameToId(address, accessToken, namesOrIds,
176
165
  ids.push(x);
177
166
  }
178
167
  else if (verbose) {
179
- process.stderr.write(`Warning: Omitting unknown assembly: "${x}"\n`);
168
+ stderr.write(`Warning: Omitting unknown assembly: "${x}"\n`);
180
169
  }
181
170
  }
182
171
  if (removeDuplicates) {
@@ -224,15 +213,6 @@ export function filterJsonList(json, keep, key) {
224
213
  }
225
214
  return results;
226
215
  }
227
- export const getUserCredentials = () => {
228
- try {
229
- const content = fs.readFileSync(CONFIG_PATH, { encoding: 'utf8' });
230
- return JSON.parse(content);
231
- }
232
- catch {
233
- return null;
234
- }
235
- };
236
216
  export const generatePkceChallenge = () => {
237
217
  const codeVerifier = crypto.randomBytes(64).toString('hex');
238
218
  const codeChallenge = crypto
@@ -259,7 +239,7 @@ export const waitFor = (eventName, emitter) => {
259
239
  return promise;
260
240
  };
261
241
  export async function submitAssembly(address, accessToken, body, force) {
262
- const assemblies = await queryApollo(address, accessToken, 'assemblies');
242
+ let assemblies = await queryApollo(address, accessToken, 'assemblies');
263
243
  for (const x of (await assemblies.json())) {
264
244
  if (x['name'] === body.assemblyName) {
265
245
  if (force) {
@@ -283,86 +263,29 @@ export async function submitAssembly(address, accessToken, body, force) {
283
263
  const response = await fetch(url, auth);
284
264
  if (!response.ok) {
285
265
  const errorMessage = await createFetchErrorMessage(response, 'submitAssembly failed');
286
- throw new ConfigError(errorMessage);
287
- }
288
- return response;
289
- }
290
- class ProgressTransform extends Transform {
291
- size = 0;
292
- progressBar;
293
- constructor(opts) {
294
- super(opts);
295
- this.progressBar = opts.progressBar;
296
- }
297
- _transform(chunk, _encoding, callback) {
298
- this.size += chunk.length;
299
- this.progressBar.update(this.size);
300
- callback(null, chunk);
266
+ throw new Error(errorMessage);
301
267
  }
302
- }
303
- export async function uploadFile(address, accessToken, file, type) {
304
- const filehandle = await fs.promises.open(file);
305
- const { size } = await filehandle.stat();
306
- const stream = filehandle.createReadStream();
307
- const progressBar = new SingleBar({ etaBuffer: 100_000_000 });
308
- const progressTransform = new ProgressTransform({ progressBar });
309
- const body = pipeline(stream, progressTransform, (error) => {
310
- if (error) {
311
- progressBar.stop();
312
- console.error('Error processing file.', error);
313
- throw error;
314
- }
315
- });
316
- const init = {
317
- method: 'POST',
318
- body,
319
- duplex: 'half',
320
- headers: {
321
- Authorization: `Bearer ${accessToken}`,
322
- 'Content-Type': type,
323
- 'Content-Length': String(size),
324
- },
325
- dispatcher: new Agent({ headersTimeout: 60 * 60 * 1000 }),
326
- };
327
- const fileName = path.basename(file);
328
- const url = new URL(localhostToAddress(`${address}/files`));
329
- url.searchParams.set('name', fileName);
330
- url.searchParams.set('type', type);
331
- progressBar.start(size, 0);
332
- try {
333
- const response = await fetch(url, init);
334
- if (!response.ok) {
335
- const errorMessage = await createFetchErrorMessage(response, 'uploadFile failed');
336
- 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;
337
272
  }
338
- const json = (await response.json());
339
- return json['_id'];
340
- }
341
- catch (error) {
342
- console.error(error);
343
- throw error;
344
- }
345
- finally {
346
- progressBar.stop();
347
273
  }
274
+ throw new Error(`Failed to retrieve assembly ${body.assemblyName}`);
348
275
  }
349
- /* Wrap text to max `length` per line */
350
- export function wrapLines(s, length) {
351
- if (length === undefined) {
352
- length = 80;
276
+ export async function readStdin() {
277
+ const chunks = [];
278
+ for await (const chunk of stdin) {
279
+ chunks.push(Buffer.from(chunk));
353
280
  }
354
- // Credit: https://stackoverflow.com/questions/14484787/wrap-text-in-javascript
355
- const re = new RegExp(`(?![^\\n]{1,${length}}$)([^\\n]{1,${length}})\\s`, 'g');
356
- s = s.replaceAll(/ +/g, ' ');
357
- const wr = s.replace(re, '$1\n');
358
- return wr;
281
+ return Buffer.concat(chunks).toString('utf8');
359
282
  }
360
- export function idReader(input, removeDuplicates = true) {
283
+ export async function idReader(input, removeDuplicates = true) {
361
284
  let ids = [];
362
285
  for (const xin of input) {
363
286
  let data;
364
287
  if (xin == '-') {
365
- data = fs.readFileSync('/dev/stdin').toString();
288
+ data = await readStdin();
366
289
  }
367
290
  else if (fs.existsSync(xin)) {
368
291
  data = fs.readFileSync(xin).toString();
@@ -371,14 +294,13 @@ export function idReader(input, removeDuplicates = true) {
371
294
  data = xin;
372
295
  }
373
296
  try {
374
- data = JSON.parse(data);
375
- if (data.length === undefined) {
376
- data = [data];
297
+ let parsedData = JSON.parse(data);
298
+ if (!Array.isArray(parsedData)) {
299
+ parsedData = [parsedData];
377
300
  }
378
- for (const x of data) {
379
- const id = x['_id'];
380
- if (id !== undefined) {
381
- ids.push(id);
301
+ for (const x of parsedData) {
302
+ if ('_id' in x && typeof x._id === 'string') {
303
+ ids.push(x._id);
382
304
  }
383
305
  }
384
306
  }