@algolia/cli 4.0.7 → 5.10.0

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 ADDED
@@ -0,0 +1,42 @@
1
+ # Algolia CLI
2
+
3
+ The official Algolia CLI lets you manage your Algolia resources — indices, records, API keys, and synonyms — directly from the command line.
4
+
5
+ > This package installs a prebuilt Go binary. The npm wrapper still requires Node.js to launch the installed command.
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ npm install -g @algolia/cli
11
+ ```
12
+
13
+ Or run without installing:
14
+
15
+ ```sh
16
+ npx @algolia/cli --help
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ```sh
22
+ algolia --help
23
+ algolia search --index my-index --query "foo"
24
+ algolia indices list
25
+ algolia apikeys list
26
+ ```
27
+
28
+ ## Documentation
29
+
30
+ Full documentation: [algolia.com/doc/tools/cli](https://algolia.com/doc/tools/cli/)
31
+
32
+ ## Supported platforms
33
+
34
+ | OS | x64 | arm64 |
35
+ |---------|-----|-------|
36
+ | macOS | ✓ | ✓ |
37
+ | Linux | ✓ | ✓ |
38
+ | Windows | ✓ | ✓ |
39
+
40
+ ## Issues
41
+
42
+ [github.com/algolia/cli/issues](https://github.com/algolia/cli/issues)
package/bin/run.js ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { execFileSync } = require('child_process');
5
+
6
+ let binPath;
7
+ switch (`${process.platform}-${process.arch}`) {
8
+ case 'darwin-x64': ({ binPath } = require('@algolia/cli-darwin-x64')); break;
9
+ case 'darwin-arm64': ({ binPath } = require('@algolia/cli-darwin-arm64')); break;
10
+ case 'linux-x64': ({ binPath } = require('@algolia/cli-linux-x64')); break;
11
+ case 'linux-arm64': ({ binPath } = require('@algolia/cli-linux-arm64')); break;
12
+ case 'win32-x64': ({ binPath } = require('@algolia/cli-win32-x64')); break;
13
+ case 'win32-arm64': ({ binPath } = require('@algolia/cli-win32-arm64')); break;
14
+ default:
15
+ console.error(
16
+ `algolia: unsupported platform ${process.platform}/${process.arch}\n` +
17
+ `Install the appropriate platform package manually: npm install @algolia/cli-${process.platform}-${process.arch}`
18
+ );
19
+ process.exit(1);
20
+ }
21
+
22
+ try {
23
+ execFileSync(binPath, process.argv.slice(2), { stdio: 'inherit' });
24
+ } catch (e) {
25
+ process.exit(e.status ?? 1);
26
+ }
package/package.json CHANGED
@@ -1,90 +1,38 @@
1
1
  {
2
2
  "name": "@algolia/cli",
3
- "version": "4.0.7",
4
- "description": "A Node CLI tools for manipulating data. Handy for day-to-day Algolia SE work.",
5
- "license": "ISC",
6
- "author": "Algolia, Inc. (https://www.algolia.com)",
7
- "main": "index.js",
8
- "repository": {
9
- "type": "git",
10
- "url": "git://github.com/algolia/algolia-cli.git"
11
- },
3
+ "version": "5.10.0",
4
+ "description": "The official Algolia CLI manage indices, records, API keys, and synonyms from the command line",
12
5
  "bin": {
13
- "algolia": "./index.js"
14
- },
15
- "scripts": {
16
- "test": "jest --runInBand",
17
- "test:unit": "jest commands/",
18
- "test:unit:watch": "jest --watch commands/",
19
- "test:integration": "jest --runInBand tests/integration/",
20
- "lint": "eslint .",
21
- "lint:fix": "npm run lint -- --fix"
22
- },
23
- "engines": {
24
- "node": ">=8.9.1",
25
- "yarn": ">=1.10.1"
6
+ "algolia": "bin/run.js"
26
7
  },
27
8
  "files": [
28
- "commands",
29
- "commands.js",
30
- "index.js",
31
- "!commands/*.test.js"
9
+ "bin/run.js",
10
+ "README.md"
32
11
  ],
33
- "renovate": {
34
- "extends": [
35
- "config:js-app"
36
- ]
12
+ "optionalDependencies": {
13
+ "@algolia/cli-darwin-x64": "5.10.0",
14
+ "@algolia/cli-darwin-arm64": "5.10.0",
15
+ "@algolia/cli-linux-x64": "5.10.0",
16
+ "@algolia/cli-linux-arm64": "5.10.0",
17
+ "@algolia/cli-win32-x64": "5.10.0",
18
+ "@algolia/cli-win32-arm64": "5.10.0"
37
19
  },
38
20
  "keywords": [
39
- "data",
40
- "json",
41
- "csv",
42
- "manipulate",
43
- "transform",
44
- "process",
45
- "parse",
46
- "import",
47
- "index",
48
- "solutions",
49
- "se",
50
- "cli"
21
+ "algolia",
22
+ "cli",
23
+ "search",
24
+ "indices",
25
+ "records",
26
+ "api-keys",
27
+ "synonyms"
51
28
  ],
52
- "dependencies": {
53
- "JSONStream": "^1.3.5",
54
- "algoliasearch": "^3.31.0",
55
- "async": "^2.6.0",
56
- "batch-stream": "^0.1.3",
57
- "chalk": "^2.4.1",
58
- "commander": "^2.19.0",
59
- "csvtojson": "^2.0.8",
60
- "inquirer": "^6.2.2",
61
- "regex-parser": "^2.2.10",
62
- "speedtest-net": "^1.5.1",
63
- "stream-transform": "^1.0.7",
64
- "through": "^2.3.8"
65
- },
66
- "devDependencies": {
67
- "babel-eslint": "^10.0.1",
68
- "babel-jest": "^23.6.0",
69
- "dotenv": "^6.2.0",
70
- "eslint": "^5.9.0",
71
- "eslint-config-algolia": "^13.2.3",
72
- "eslint-config-prettier": "^3.3.0",
73
- "eslint-plugin-import": "^2.14.0",
74
- "eslint-plugin-jest": "^22.1.2",
75
- "eslint-plugin-prettier": "^3.0.0",
76
- "jest": "^23.6.0",
77
- "prettier": "^1.15.3",
78
- "randomatic": "^3.1.1",
79
- "rimraf": "^2.6.2",
80
- "striptags": "^3.1.1"
29
+ "license": "MIT",
30
+ "homepage": "https://algolia.com/doc/tools/cli/",
31
+ "bugs": {
32
+ "url": "https://github.com/algolia/cli/issues"
81
33
  },
82
- "jest": {
83
- "testEnvironment": "node",
84
- "verbose": true,
85
- "testURL": "http://localhost/",
86
- "setupFiles": [
87
- "./tests/config.js"
88
- ]
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/algolia/cli.git"
89
37
  }
90
38
  }
@@ -1,72 +0,0 @@
1
- const fs = require('fs');
2
- const algolia = require('algoliasearch');
3
- const Base = require('./Base.js');
4
-
5
- class AddRulesScript extends Base {
6
- constructor() {
7
- super();
8
- // Bind class methods
9
- this.getSource = this.getSource.bind(this);
10
- this.parseBatchRulesOptions = this.parseBatchRulesOptions.bind(this);
11
- this.start = this.start.bind(this);
12
- // Define validation constants
13
- this.message =
14
- '\nExample: $ algolia addrules -a algoliaappid -k algoliaapikey -n algoliaindexname -s sourcefilepath -p batchRulesParams\n\n';
15
- this.params = [
16
- 'algoliaappid',
17
- 'algoliaapikey',
18
- 'algoliaindexname',
19
- 'sourcefilepath',
20
- ];
21
- }
22
-
23
- getSource(path) {
24
- const filepath = this.normalizePath(path);
25
- if (!fs.lstatSync(filepath).isFile())
26
- throw new Error('Source filepath must target valid rules file.');
27
- return filepath;
28
- }
29
-
30
- parseBatchRulesOptions(params) {
31
- try {
32
- const options = { forwardToReplicas: false, clearExistingRules: false };
33
- if (params === null) return options;
34
- else return JSON.parse(params);
35
- } catch (e) {
36
- throw e;
37
- }
38
- }
39
-
40
- async start(program) {
41
- try {
42
- // Validate command; if invalid display help text and exit
43
- this.validate(program, this.message, this.params);
44
-
45
- // Config params
46
- const appId = program.algoliaappid;
47
- const apiKey = program.algoliaapikey;
48
- const indexName = program.algoliaindexname;
49
- const sourcefilepath = program.sourcefilepath;
50
- const params = program.params || null;
51
-
52
- // Get rules
53
- const rulesPath = this.getSource(sourcefilepath);
54
- const rulesFile = await fs.readFileSync(rulesPath);
55
- const rules = JSON.parse(rulesFile);
56
- // Get options
57
- const batchRulesOptions = this.parseBatchRulesOptions(params);
58
-
59
- // Instantiate Algolia index
60
- const client = algolia(appId, apiKey);
61
- const index = client.initIndex(indexName);
62
- // Add rules
63
- const result = await index.batchRules(rules, batchRulesOptions);
64
- return console.log(result);
65
- } catch (e) {
66
- throw e;
67
- }
68
- }
69
- }
70
-
71
- const addRulesScript = new AddRulesScript();
72
- module.exports = addRulesScript;
@@ -1,89 +0,0 @@
1
- const fs = require('fs');
2
- const algolia = require('algoliasearch');
3
- const Base = require('./Base.js');
4
-
5
- class AddSynonymsScript extends Base {
6
- constructor() {
7
- super();
8
- // Bind class methods
9
- this.getSource = this.getSource.bind(this);
10
- this.parseBatchSynonymsOptions = this.parseBatchSynonymsOptions.bind(this);
11
- this.convertCsvToJson = this.convertCsvToJson.bind(this);
12
- this.start = this.start.bind(this);
13
- // Define validation constants
14
- this.message =
15
- '\nExample: $ algolia addsynonyms -a algoliaappid -k algoliaapikey -n algoliaindexname -s sourcefilepath -p batchSynonymsParams\n\n';
16
- this.params = [
17
- 'algoliaappid',
18
- 'algoliaapikey',
19
- 'algoliaindexname',
20
- 'sourcefilepath',
21
- ];
22
- }
23
-
24
- getSource(path) {
25
- const filepath = this.normalizePath(path);
26
- if (!fs.lstatSync(filepath).isFile())
27
- throw new Error('Source filepath must target valid synonyms file.');
28
- return filepath;
29
- }
30
-
31
- parseBatchSynonymsOptions(params) {
32
- try {
33
- const options = {
34
- forwardToReplicas: false,
35
- clearExistingSynonyms: false,
36
- };
37
- if (params === null) return options;
38
- else return JSON.parse(params);
39
- } catch (e) {
40
- throw e;
41
- }
42
- }
43
-
44
- convertCsvToJson(synonymFile, filepath) {
45
- const synonyms = synonymFile.toString().split('\n');
46
- return synonyms.map((line, num) => ({
47
- type: 'synonym',
48
- objectID: `${filepath}-${num}`,
49
- synonyms: line.split(','),
50
- }));
51
- }
52
-
53
- async start(program) {
54
- try {
55
- // Validate command; if invalid display help text and exit
56
- this.validate(program, this.message, this.params);
57
-
58
- // Config params
59
- const appId = program.algoliaappid;
60
- const apiKey = program.algoliaapikey;
61
- const indexName = program.algoliaindexname;
62
- const sourcefilepath = program.sourcefilepath;
63
- const params = program.params || null;
64
- const isCsv = sourcefilepath.split('.').pop() === 'csv';
65
-
66
- // Get synonyms
67
- const synonymsPath = this.getSource(sourcefilepath);
68
- const synonymsFile = await fs.readFileSync(synonymsPath);
69
- const synonyms = isCsv
70
- ? this.convertCsvToJson(synonymsFile, sourcefilepath)
71
- : JSON.parse(synonymsFile);
72
-
73
- // Get options
74
- const batchSynonymsOptions = this.parseBatchSynonymsOptions(params);
75
-
76
- // Instantiate Algolia index
77
- const client = algolia(appId, apiKey);
78
- const index = client.initIndex(indexName);
79
- // Add rules
80
- const result = await index.batchSynonyms(synonyms, batchSynonymsOptions);
81
- return console.log(result);
82
- } catch (e) {
83
- throw e;
84
- }
85
- }
86
- }
87
-
88
- const addSynonymsScript = new AddSynonymsScript();
89
- module.exports = addSynonymsScript;
package/commands/Base.js DELETED
@@ -1,97 +0,0 @@
1
- const os = require('os');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const readLine = require('readline');
5
- const chalk = require('chalk');
6
- const speedTest = require('speedtest-net');
7
-
8
- class Base {
9
- constructor() {
10
- this.maxHeapMb = process.arch.includes('64') ? 1024 : 512;
11
- }
12
-
13
- validate(program, message, params) {
14
- let flag = false;
15
- let output = message;
16
- params.forEach(param => {
17
- if (!program[param]) {
18
- output += chalk.red(`Must specify ${param}\n`);
19
- flag = true;
20
- }
21
- });
22
- if (flag) return program.help(h => h + output);
23
- else return { flag, output };
24
- }
25
-
26
- writeProgress(message) {
27
- readLine.clearLine(process.stdout, 0);
28
- readLine.cursorTo(process.stdout, 0);
29
- process.stdout.write(message);
30
- }
31
-
32
- normalizePath(input) {
33
- // Convert path input param to valid system absolute path
34
- // Path is absolute, originating from system root
35
- if (path.isAbsolute(input)) return input;
36
- // Path is relative to user's home directory
37
- if (input[0] === '~') return path.join(os.homedir(), input.substr(1));
38
- // Path is relative to current directory
39
- return path.resolve(process.cwd(), input);
40
- }
41
-
42
- setSource(options) {
43
- // Set source directory and filenames array
44
- // Used to process path inputs that may either be a single file or a directory of files
45
- const source = this.normalizePath(options.sourceFilepath);
46
- if (fs.lstatSync(source).isDirectory()) {
47
- this.directory = source;
48
- this.filenames = fs.readdirSync(source);
49
- } else if (fs.lstatSync(source).isFile()) {
50
- this.directory = path.parse(source).dir;
51
- this.filenames = [path.parse(source).base];
52
- } else {
53
- throw new Error('Invalid sourcefilepath param');
54
- }
55
- }
56
-
57
- getMemoryUsage() {
58
- const used = process.memoryUsage().heapUsed / 1024 / 1024;
59
- const usedMb = Math.round(used * 100) / 100;
60
- const percentUsed = Math.floor((usedMb / this.maxHeapMb) * 100);
61
- return { usedMb, percentUsed };
62
- }
63
-
64
- getStringSizeMb(string) {
65
- const bytes = Buffer.byteLength(string, 'utf8');
66
- const mb = bytes / 1024 / 1024;
67
- return Math.ceil(mb);
68
- }
69
-
70
- getNetworkSpeed() {
71
- return new Promise((resolve, reject) => {
72
- this.writeProgress('Estimating network speed...');
73
- const test = speedTest({ maxTime: 5000 });
74
- let downloadSpeedMb = null;
75
- let uploadSpeedMb = null;
76
- test.on('error', e => {
77
- console.log(chalk.white.bgRed('Speed test error'), chalk.red(e));
78
- reject(e);
79
- });
80
- test.on('downloadspeed', speed => {
81
- downloadSpeedMb = ((speed * 125) / 1000).toFixed(2);
82
- });
83
- test.on('uploadspeed', speed => {
84
- uploadSpeedMb = ((speed * 125) / 1000).toFixed(2);
85
- });
86
- test.on('done', () => {
87
- console.log(
88
- chalk.blue(`\nDownload: ${downloadSpeedMb} MB/s`),
89
- chalk.blue(`\nUpload: ${uploadSpeedMb} MB/s`)
90
- );
91
- resolve(uploadSpeedMb);
92
- });
93
- });
94
- }
95
- }
96
-
97
- module.exports = Base;
@@ -1,104 +0,0 @@
1
- const algolia = require('algoliasearch');
2
- const Base = require('./Base.js');
3
-
4
- class DeleteIndicesPatternScript extends Base {
5
- constructor() {
6
- super();
7
- // Define validation constants
8
- this.message =
9
- "\nUsage: $ algolia deleteindices -a algoliaappid -k algoliaapikey -r 'regexp for filtering' -x\n\n";
10
- this.params = ['algoliaappid', 'algoliaapikey', 'regexp', 'dryrun'];
11
- }
12
-
13
- removeReplicas({ indices, regexp, dryRun }) {
14
- return Promise.all(
15
- indices.map(async ({ name: indexName }) => {
16
- const index = await this.client.initIndex(indexName);
17
- const indexSettings = await index.getSettings();
18
- const replicas = indexSettings.slaves || indexSettings.replicas;
19
- if (replicas !== undefined && replicas.length > 0) {
20
- const newReplicas = replicas.filter(
21
- replicaIndexName => regexp.test(replicaIndexName) === false
22
- );
23
-
24
- if (replicas.length !== newReplicas.length) {
25
- if (dryRun === false) {
26
- const { taskID } = await index.setSettings({
27
- [indexSettings.slaves !== undefined
28
- ? 'slaves'
29
- : 'replicas']: newReplicas,
30
- });
31
- await index.waitTask(taskID);
32
- } else {
33
- console.log(
34
- `[DRY RUN] Replicas change on index ${indexName}, \n- before: ${replicas.join(
35
- ','
36
- )}\n- after: ${newReplicas.join(',')}`
37
- );
38
- }
39
- }
40
- }
41
-
42
- return false;
43
- })
44
- );
45
- }
46
-
47
- deleteIndices({ indices, regexp, dryRun }) {
48
- let deletedIndices = 0;
49
- return Promise.all(
50
- indices
51
- .filter(({ name: indexName }) => regexp.test(indexName) === true)
52
- .map(async ({ name: indexName }) => {
53
- deletedIndices++;
54
-
55
- if (dryRun === false) {
56
- this.writeProgress(`Deleted indices: ${deletedIndices}`);
57
- const index = this.client.initIndex(indexName);
58
- const { taskID } = await this.client.deleteIndex(indexName);
59
- return index.waitTask(taskID);
60
- } else {
61
- console.log(`[DRY RUN] Delete index ${indexName}`);
62
- return false;
63
- }
64
- })
65
- ).then(() => {
66
- console.log('');
67
- if (dryRun === false) {
68
- console.log(`${deletedIndices} indices deleted`);
69
- } else {
70
- console.log(`[DRY RUN] ${deletedIndices} indices deleted`);
71
- }
72
- });
73
- }
74
-
75
- async deleteIndicesPattern(options) {
76
- this.client = algolia(options.appId, options.apiKey);
77
- const { items: indices } = await this.client.listIndexes();
78
- const regexp = new RegExp(options.regexp);
79
- await this.removeReplicas({ indices, regexp, dryRun: options.dryRun });
80
- await this.deleteIndices({ indices, regexp, dryRun: options.dryRun });
81
- }
82
-
83
- start(program) {
84
- try {
85
- // Validate command; if invalid display help text and exit
86
- this.validate(program, this.message, this.params);
87
-
88
- // Config params
89
- const options = {
90
- appId: program.algoliaappid,
91
- apiKey: program.algoliaapikey,
92
- regexp: program.regexp,
93
- dryRun: program.dryrun !== undefined ? program.dryrun === 'true' : true,
94
- };
95
-
96
- // Delete indices
97
- return this.deleteIndicesPattern(options);
98
- } catch (e) {
99
- throw e;
100
- }
101
- }
102
- }
103
-
104
- module.exports = new DeleteIndicesPatternScript();
@@ -1,117 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const algolia = require('algoliasearch');
4
- const Base = require('./Base.js');
5
-
6
- class ExportScript extends Base {
7
- constructor() {
8
- super();
9
- // Bind class methods
10
- this.getOutput = this.getOutput.bind(this);
11
- this.parseParams = this.parseParams.bind(this);
12
- this.writeFile = this.writeFile.bind(this);
13
- this.exportData = this.exportData.bind(this);
14
- this.start = this.start.bind(this);
15
- // Define validation constants
16
- this.message =
17
- '\nExample: $ algolia export -a algoliaappid -k algoliaapikey -n algoliaindexname -o outputpath -p params\n\n';
18
- this.params = ['algoliaappid', 'algoliaapikey', 'algoliaindexname'];
19
- }
20
-
21
- getOutput(outputPath) {
22
- // If no outputPath is provided, use directory from which command was invoked
23
- const outputDir =
24
- outputPath !== null ? this.normalizePath(outputPath) : process.cwd();
25
- // Ensure outputPath is a directory
26
- if (!fs.lstatSync(outputDir).isDirectory())
27
- throw new Error('Output path must be a directory.');
28
- return outputDir;
29
- }
30
-
31
- parseParams(params) {
32
- try {
33
- if (params === null) return { hitsPerPage: 1000 };
34
- return JSON.parse(params);
35
- } catch (e) {
36
- throw e;
37
- }
38
- }
39
-
40
- writeFile(hits, options, fileCount) {
41
- const filename = `algolia-index-${options.indexName}-${fileCount}.json`;
42
- const filePath = path.resolve(options.outputPath, filename);
43
- fs.writeFileSync(filePath, JSON.stringify(hits));
44
- return console.log(`\nDone writing ${filename}`);
45
- }
46
-
47
- exportData(options) {
48
- return new Promise((resolve, reject) => {
49
- // Instantiate Algolia index
50
- const client = algolia(options.appId, options.apiKey);
51
- const index = client.initIndex(options.indexName);
52
-
53
- // Export index
54
- const browse = index.browseAll('', options.params);
55
- let hits = [];
56
- let hitsCount = 0;
57
- let fileCount = 0;
58
-
59
- browse.on('result', result => {
60
- // Push 1000 new hits to array
61
- hits = hits.concat(result.hits);
62
- hitsCount += result.hits.length;
63
- this.writeProgress(`Records browsed: ${hitsCount}`);
64
- if (hits.length >= 10000) {
65
- // Write batch of 10,000 records to file
66
- fileCount++;
67
- this.writeFile(hits, options, fileCount);
68
- // Clear array
69
- hits = [];
70
- }
71
- });
72
-
73
- browse.on('end', () => {
74
- if (hits.length > 0) {
75
- // Write remaining records to file
76
- fileCount++;
77
- this.writeFile(hits, options, fileCount);
78
- }
79
- return resolve(
80
- `\nDone exporting index.\nSee your data here: ${options.outputPath}`
81
- );
82
- });
83
-
84
- browse.on('error', err => reject(err));
85
- });
86
- }
87
-
88
- async start(program) {
89
- try {
90
- // Validate command; if invalid display help text and exit
91
- this.validate(program, this.message, this.params);
92
-
93
- // Config params
94
- const options = {
95
- appId: program.algoliaappid,
96
- apiKey: program.algoliaapikey,
97
- indexName: program.algoliaindexname,
98
- outputPath: program.outputpath || null,
99
- params: program.params || null,
100
- };
101
-
102
- // Configure and validate output path
103
- options.outputPath = this.getOutput(options.outputPath);
104
- // Configure browseAll params
105
- options.params = this.parseParams(options.params);
106
-
107
- // Export data
108
- const result = await this.exportData(options);
109
- return console.log(result);
110
- } catch (e) {
111
- throw e;
112
- }
113
- }
114
- }
115
-
116
- const exportScript = new ExportScript();
117
- module.exports = exportScript;