From 38d11fb9d5cda97d3927c9aab4c271c4173dc5be Mon Sep 17 00:00:00 2001 From: Aschen Date: Fri, 30 Oct 2020 16:20:57 +0100 Subject: [PATCH 01/15] Add Quine example --- README.md | 17 +++++++++++++++++ src/commands/sdk/execute.ts | 5 ++++- src/common.ts | 19 ++++++++++++++++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fb800e04..8b88b1a9 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ The CLI that helps you manage your Kuzzle instances. * [Usage](#usage) * [Commands](#commands) * [Where does this weird name come from?](#where-does-this-weird-name-come-from) +* [Have fun with quine](#have-fun-with-quine) :warning: This project is currently in beta and breaking changes may occur until the 1.0.0 @@ -1358,3 +1359,19 @@ _See code: [src/commands/vault/test.ts](src/commands/vault/test.ts)_ # Where does this weird name come from? We liked the idea that this CLI is like a launchpad for the Kuzzle rocket. The place where you launch and pilot your Kuzzle instance. The place where the European Space Agency launches their rockets is in the country near the city of [Kourou](https://www.wikiwand.com/en/Kourou), in French Guiana, so we liked the idea that the Kuzzle rockets would take off from there. + +# Have fun with quine + +[Quine](https://en.wikipedia.org/wiki/Quine_(computing)) are programs able to print their own source code. + +```bash +$ kourou sdk:execute --code '( + function quine() { + const sq = String.fromCharCode(39); + const lp = String.fromCharCode(40); + const rp = String.fromCharCode(41); + + console.log("kourou sdk:execute --code " + sq + lp + quine.toString() + rp + lp + rp + ";" + sq) + } +)()' +``` \ No newline at end of file diff --git a/src/commands/sdk/execute.ts b/src/commands/sdk/execute.ts index 67534eb8..ddb01306 100644 --- a/src/commands/sdk/execute.ts +++ b/src/commands/sdk/execute.ts @@ -52,6 +52,9 @@ Other 'keep-alive': flags.boolean({ description: 'Keep the connection running (websocket only)' }), + 'print-raw': flags.boolean({ + description: 'Print only the script result to stdout' + }), ...kuzzleFlags, }; @@ -120,7 +123,7 @@ ${variables} this.logOk('Successfully executed SDK code') if (result !== undefined) { - this.log(JSON.stringify(result, null, 2)) + console.log(JSON.stringify(result, null, 2)) } } diff --git a/src/common.ts b/src/common.ts index 6cf358cb..91aa743a 100644 --- a/src/common.ts +++ b/src/common.ts @@ -37,30 +37,47 @@ export abstract class Kommand extends Command { } public log(message?: string): void { + if (this.flags['print-raw']) { + return; + } + super.log(` ${message}`) } public logOk(message: string): void { + if (this.flags['print-raw']) { + return; + } + this.log(chalk.green(`[✔] ${message}`)) } public logInfo(message: string): void { + if (this.flags['print-raw']) { + return; + } + this.log(chalk.yellow(`[ℹ] ${message}`)) } public logKo(message?: string): void { + if (this.flags['print-raw']) { + return; + } + this.exitCode = 1 this.log(chalk.red(`[X] ${message}`)) } async run() { - this.printCommand() const kommand = (this.constructor as unknown) as any const result = this.parse(kommand) this.args = result.args this.flags = result.flags + this.printCommand() + if (kommand.readStdin) { this.stdin = this.fromStdin() From 43c0b3ba18b68dc80e0f68281c54888cea659fdc Mon Sep 17 00:00:00 2001 From: Aschen Date: Fri, 30 Oct 2020 16:27:07 +0100 Subject: [PATCH 02/15] lint --- README.md | 7 ++++--- src/commands/sdk/execute.ts | 1 + src/common.ts | 10 +++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8b88b1a9..32581c92 100644 --- a/README.md +++ b/README.md @@ -939,6 +939,7 @@ OPTIONS --keep-alive Keep the connection running (websocket only) --password=password Kuzzle user password --port=port [default: 7512] Kuzzle server port + --print-raw Print only the script result to stdout --protocol=protocol [default: ws] Kuzzle protocol (http or ws) --ssl Use SSL to connect to Kuzzle --username=username [default: anonymous] Kuzzle username (local strategy) @@ -1365,13 +1366,13 @@ We liked the idea that this CLI is like a launchpad for the Kuzzle rocket. The p [Quine](https://en.wikipedia.org/wiki/Quine_(computing)) are programs able to print their own source code. ```bash -$ kourou sdk:execute --code '( +$ kourou-dev sdk:execute --print-raw --code '( function quine() { const sq = String.fromCharCode(39); const lp = String.fromCharCode(40); const rp = String.fromCharCode(41); - console.log("kourou sdk:execute --code " + sq + lp + quine.toString() + rp + lp + rp + ";" + sq) + console.log("kourou-dev sdk:execute --print-raw --code " + sq + lp + quine.toString() + rp + lp + rp + ";" + sq) } )()' -``` \ No newline at end of file +``` diff --git a/src/commands/sdk/execute.ts b/src/commands/sdk/execute.ts index ddb01306..6fac1cb9 100644 --- a/src/commands/sdk/execute.ts +++ b/src/commands/sdk/execute.ts @@ -123,6 +123,7 @@ ${variables} this.logOk('Successfully executed SDK code') if (result !== undefined) { + // eslint-disable-next-line no-console console.log(JSON.stringify(result, null, 2)) } } diff --git a/src/common.ts b/src/common.ts index 91aa743a..c22be966 100644 --- a/src/common.ts +++ b/src/common.ts @@ -38,7 +38,7 @@ export abstract class Kommand extends Command { public log(message?: string): void { if (this.flags['print-raw']) { - return; + return } super.log(` ${message}`) @@ -46,7 +46,7 @@ export abstract class Kommand extends Command { public logOk(message: string): void { if (this.flags['print-raw']) { - return; + return } this.log(chalk.green(`[✔] ${message}`)) @@ -54,7 +54,7 @@ export abstract class Kommand extends Command { public logInfo(message: string): void { if (this.flags['print-raw']) { - return; + return } this.log(chalk.yellow(`[ℹ] ${message}`)) @@ -62,7 +62,7 @@ export abstract class Kommand extends Command { public logKo(message?: string): void { if (this.flags['print-raw']) { - return; + return } this.exitCode = 1 @@ -158,6 +158,6 @@ export abstract class Kommand extends Command { } // eslint-disable-next-line no-eval - return eval(`var o = ${input}; o`) + return eval(`var o = ${input} o`) } } From d5b3e47c0db109d42c2323365f62d962519fdf78 Mon Sep 17 00:00:00 2001 From: Aschen Date: Fri, 30 Oct 2020 16:27:42 +0100 Subject: [PATCH 03/15] lint --- src/common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common.ts b/src/common.ts index c22be966..20b79bc8 100644 --- a/src/common.ts +++ b/src/common.ts @@ -158,6 +158,6 @@ export abstract class Kommand extends Command { } // eslint-disable-next-line no-eval - return eval(`var o = ${input} o`) + return eval(`var o = ${input}; o`) } } From 424b1064b6cfe922288fe8c8ab5234805cc44a2c Mon Sep 17 00:00:00 2001 From: Aschen Date: Mon, 2 Nov 2020 15:02:57 +0100 Subject: [PATCH 04/15] nit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 32581c92..17160af3 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ The CLI that helps you manage your Kuzzle instances. * [Usage](#usage) * [Commands](#commands) * [Where does this weird name come from?](#where-does-this-weird-name-come-from) -* [Have fun with quine](#have-fun-with-quine) +* [Have fun with a quine](#have-fun-with-a-quine) :warning: This project is currently in beta and breaking changes may occur until the 1.0.0 @@ -1361,7 +1361,7 @@ _See code: [src/commands/vault/test.ts](src/commands/vault/test.ts)_ We liked the idea that this CLI is like a launchpad for the Kuzzle rocket. The place where you launch and pilot your Kuzzle instance. The place where the European Space Agency launches their rockets is in the country near the city of [Kourou](https://www.wikiwand.com/en/Kourou), in French Guiana, so we liked the idea that the Kuzzle rockets would take off from there. -# Have fun with quine +# Have fun with a quine [Quine](https://en.wikipedia.org/wiki/Quine_(computing)) are programs able to print their own source code. From 92ae6084059326f69f362671622da83e70a5c4df Mon Sep 17 00:00:00 2001 From: Alexandre Bouthinon Date: Fri, 13 Nov 2020 09:59:46 +0100 Subject: [PATCH 05/15] Add es:migrate command (#81) ## What does this PR do? Add a new command to the `es` module to migrate data and mappings from an Elasticsearch server to another using bulk API: ![kourou](https://user-images.githubusercontent.com/7868838/98674476-25e8b000-2359-11eb-9b0e-e020d54505f3.gif) --- src/commands/es/migrate.ts | 135 +++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 src/commands/es/migrate.ts diff --git a/src/commands/es/migrate.ts b/src/commands/es/migrate.ts new file mode 100644 index 00000000..2847f759 --- /dev/null +++ b/src/commands/es/migrate.ts @@ -0,0 +1,135 @@ +import { flags } from '@oclif/command' +import { Client } from '@elastic/elasticsearch' +import cli from 'cli-ux' +import chalk from 'chalk' +import emoji from 'node-emoji' + +import { Kommand } from '../../common' + +export default class EsMigrate extends Kommand { + static initSdk = false + + static description = 'Migrate all the index from one Elasticsearch server to another' + + static flags = { + help: flags.help(), + src: flags.string({ + description: 'Source Elasticsearch server URL', + env: 'KUZZLE_ES_SRC', + required: true + }), + dest: flags.string({ + description: 'Destination Elasticsearch server URL', + env: 'KUZZLE_ES_DEST', + required: true + }), + reset: flags.boolean({ + description: 'Reset destination Elasticsearch server', + default: false, + }), + 'batch-size': flags.integer({ + description: 'How many documents to move in batch per operation', + default: 1000, + }), + 'no-interactive': flags.boolean({ + description: 'Skip confirmation interactive prompts (perfect for scripting)', + default: false, + }), + } + + static examples = [ + 'kourou es:migrate --src http://elasticsearch:9200 --dest http://otherElasticsearch:9200 --reset --limit 2000', + 'kourou es:migrate --src http://elasticsearch:9200 --dest http://otherElasticsearch:9200 --reset --limit 2000 --no-interactive' + ] + + private src: any + + private dest: any + + private async migrateMappings(index: string) { + const { body } = await this.src.indices.getMapping({ index, format: 'json' }) + const mappings = body[index] + await this.dest.indices.create({ index, body: mappings }) + } + + private async migrateData(index: string, batch_size: number) { + const queue = [] + const { body } = await this.src.search({ + index, + scroll: '20s', + size: batch_size, + body: { + query: { + match_all: {} + } + } + }) + const scrollId = body._scroll_id + const total = body.hits.total.value + + if (total === 0) { + return 0 + } + + const progressBar = cli.progress({ format: chalk.blue(' [*] Importing |{bar}| {percentage}% || {value}/{total} documents') }) + progressBar.start(total, 0) + + let count = 0 + queue.push(body.hits) + while (queue.length && total !== count) { + const { hits: docs } = queue.pop() + const bulk = [] + for (const doc of docs) { + bulk.push({ index: { _index: index, _id: doc._id } }) + bulk.push(doc._source) + count++ + } + + await this.dest.bulk({ body: bulk }) + + progressBar.update(count) + + const { body: { hits } } = await this.src.scroll({ + scrollId, + scroll: '20s' + }) + queue.push(hits) + } + progressBar.stop() + + return total + } + + async runSafe() { + this.src = new Client({ node: this.flags.src }) + this.dest = new Client({ node: this.flags.dest }) + + if (this.flags.reset && !this.flags['no-interactive']) { + this.log(chalk.red(`${emoji.get('fire')} Are you sure you want to reset ${chalk.bold(this.flags.dest)}?`)) + await cli.confirm(chalk.redBright(` ${emoji.get('fire')} You will lose all the data stored in it (Type "yes" to confirm)`)) + await this.dest.indices.delete({ index: '_all' }) + } + + this.logInfo(`Fetching indices list from ${chalk.bold(this.flags.src)}`) + const { body } = await this.src.cat.indices({ format: 'json' }) + const indices = body + .map(({ index }: { index: string }) => index) + .sort() + this.logOk(`Found ${chalk.bold(indices.length)} indices in the source Elasticsearch server\n`) + + this.logInfo(`Starting indices migration from ${chalk.bold(this.flags.src)} to ${chalk.bold(this.flags.dest)}`) + for (const index of indices) { + this.logInfo(`Importing ${chalk.bold(index)} to ${chalk.bold(this.flags.dest)}`) + + await this.migrateMappings(index) + this.logOk('Mappings successfully imported!') + + const count = await this.migrateData(index, this.flags['batch-size']) + if (count === 0) { + this.logInfo('No documents to import\n') + } else { + this.logOk(`${chalk.bold(count)} document(s) imported!\n`) + } + } + } +} From 2ade85d6a73b4bf51d748ea85a035304d33e10f7 Mon Sep 17 00:00:00 2001 From: Alexandre Bouthinon Date: Fri, 13 Nov 2020 11:33:31 +0100 Subject: [PATCH 06/15] Fix es:migrate examples --- src/commands/es/migrate.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/es/migrate.ts b/src/commands/es/migrate.ts index 2847f759..7eafae41 100644 --- a/src/commands/es/migrate.ts +++ b/src/commands/es/migrate.ts @@ -38,8 +38,8 @@ export default class EsMigrate extends Kommand { } static examples = [ - 'kourou es:migrate --src http://elasticsearch:9200 --dest http://otherElasticsearch:9200 --reset --limit 2000', - 'kourou es:migrate --src http://elasticsearch:9200 --dest http://otherElasticsearch:9200 --reset --limit 2000 --no-interactive' + 'kourou es:migrate --src http://elasticsearch:9200 --dest http://otherElasticsearch:9200 --reset --batch-size 2000', + 'kourou es:migrate --src http://elasticsearch:9200 --dest http://otherElasticsearch:9200 --reset --batch-size 2000 --no-interactive' ] private src: any From b63097b3c71f2923680de70e7b89ba5ef24057f9 Mon Sep 17 00:00:00 2001 From: Aschen Date: Tue, 24 Nov 2020 09:59:56 +0100 Subject: [PATCH 07/15] Expose ES in template --- templates/app-scaffold/ts/docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/app-scaffold/ts/docker-compose.yml b/templates/app-scaffold/ts/docker-compose.yml index e007f9cc..a836a97e 100644 --- a/templates/app-scaffold/ts/docker-compose.yml +++ b/templates/app-scaffold/ts/docker-compose.yml @@ -32,5 +32,7 @@ services: elasticsearch: image: kuzzleio/elasticsearch:7 + ports: + - "9200:9200" ulimits: nofile: 65536 From b259e3e05ced557a216ba0347f554380ae3af9fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Blondel?= Date: Tue, 24 Nov 2020 11:31:25 +0100 Subject: [PATCH 08/15] Add an option to export data in a format compatible with Kuzzle fixtures (#82) ## What does this PR do? Add option to export in JSON understandable for Kuzzle fixtures. fix #56 ### How should this be manually tested? ``` kourou index:export my-index --format=raw kourou collection:export my-index my-collection --format=raw ``` --- README.md | 25 +++++++++++++++++- src/commands/collection/export.ts | 10 ++++++-- src/commands/index/export.ts | 13 +++++++--- src/support/dump-collection.ts | 42 +++++++++++++++++++++++-------- 4 files changed, 74 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 0ec41173..1dc7db4d 100644 --- a/README.md +++ b/README.md @@ -355,14 +355,26 @@ OPTIONS --as=as Impersonate a user --batch-size=batch-size [default: 2000] Maximum batch size (see limits.documentsFetchCount config) --editor Open an editor (EDITOR env variable) to edit the query before sending + + --format=format [default: JSONL] "jsonl or kuzzle - kuzzle will export in Kuzzle format usable for internal + fixtures and jsonl allows to import that data back with kourou + --help show CLI help + --host=host [default: localhost] Kuzzle server host + --password=password Kuzzle user password + --path=path Dump root directory + --port=port [default: 7512] Kuzzle server port + --protocol=protocol [default: ws] Kuzzle protocol (http or websocket) + --query=query [default: {}] Only dump documents matching the query (JS or JSON format) + --ssl Use SSL to connect to Kuzzle + --username=username [default: anonymous] Kuzzle username (local strategy) EXAMPLES @@ -709,7 +721,7 @@ _See code: [src/commands/import.ts](src/commands/import.ts)_ ## `kourou index:export INDEX` -Exports an index (JSONL format) +Exports an index (JSONL or Kuzzle format) ``` USAGE @@ -722,13 +734,24 @@ OPTIONS --api-key=api-key Kuzzle user api-key --as=as Impersonate a user --batch-size=batch-size [default: 2000] Maximum batch size (see limits.documentsFetchCount config) + + --format=format [default: jsonl] "jsonl or kuzzle - kuzzle will export in Kuzzle format usable for internal + fixtures and jsonl allows to import that data back with kourou + --help show CLI help + --host=host [default: localhost] Kuzzle server host + --password=password Kuzzle user password + --path=path Dump root directory + --port=port [default: 7512] Kuzzle server port + --protocol=protocol [default: ws] Kuzzle protocol (http or websocket) + --ssl Use SSL to connect to Kuzzle + --username=username [default: anonymous] Kuzzle username (local strategy) ``` diff --git a/src/commands/collection/export.ts b/src/commands/collection/export.ts index 9d54996f..4c5aafc6 100644 --- a/src/commands/collection/export.ts +++ b/src/commands/collection/export.ts @@ -25,6 +25,10 @@ export default class CollectionExport extends Kommand { editor: flags.boolean({ description: 'Open an editor (EDITOR env variable) to edit the query before sending' }), + format: flags.string({ + description: '"jsonl or kuzzle - kuzzle will export in Kuzzle format usable for internal fixtures and jsonl allows to import that data back with kourou', + default: 'JSONL' + }), ...kuzzleFlags, protocol: flags.string({ description: 'Kuzzle protocol (http or websocket)', @@ -64,7 +68,8 @@ export default class CollectionExport extends Kommand { this.sdk, this.args.index, this.args.collection, - exportPath) + exportPath, + this.flags.format) await dumpCollectionData( this.sdk, @@ -72,7 +77,8 @@ export default class CollectionExport extends Kommand { this.args.collection, Number(this.flags['batch-size']), exportPath, - query) + query, + this.flags.format) this.logOk(`Collection ${this.args.index}:${this.args.collection} dumped`) } diff --git a/src/commands/index/export.ts b/src/commands/index/export.ts index 3da84eb3..1a2ddde3 100644 --- a/src/commands/index/export.ts +++ b/src/commands/index/export.ts @@ -8,7 +8,7 @@ import { kuzzleFlags } from '../../support/kuzzle' import { dumpCollectionData, dumpCollectionMappings } from '../../support/dump-collection' export default class IndexExport extends Kommand { - static description = 'Exports an index (JSONL format)' + static description = 'Exports an index (JSONL or Kuzzle format)' static flags = { help: flags.help({}), @@ -19,6 +19,10 @@ export default class IndexExport extends Kommand { description: 'Maximum batch size (see limits.documentsFetchCount config)', default: '2000' }), + format: flags.string({ + description: '"jsonl or kuzzle - kuzzle will export in Kuzzle format usable for internal fixtures and jsonl will be usable to import that data back with kourou', + default: 'jsonl' + }), ...kuzzleFlags, protocol: flags.string({ description: 'Kuzzle protocol (http or websocket)', @@ -48,14 +52,17 @@ export default class IndexExport extends Kommand { this.sdk, this.args.index, collection.name, - exportPath) + exportPath, + this.flags.format) await dumpCollectionData( this.sdk, this.args.index, collection.name, Number(this.flags['batch-size']), - exportPath) + exportPath, + {}, + this.flags.format) cli.action.stop() } diff --git a/src/support/dump-collection.ts b/src/support/dump-collection.ts index 381bba00..9493e331 100644 --- a/src/support/dump-collection.ts +++ b/src/support/dump-collection.ts @@ -4,9 +4,9 @@ import path from 'path' import cli from 'cli-ux' import ndjson from 'ndjson' -export async function dumpCollectionData(sdk: any, index: string, collection: string, batchSize: number, destPath: string, query: any = {}) { +export async function dumpCollectionData(sdk: any, index: string, collection: string, batchSize: number, destPath: string, query: any = {}, format = 'jsonl') { const collectionDir = path.join(destPath, collection) - const filename = path.join(collectionDir, 'documents.jsonl') + const filename = path.join(collectionDir, (format.toLowerCase() === 'jsonl' ? 'documents.jsonl' : 'documents.json')) const writeStream = fs.createWriteStream(filename) const waitWrite = new Promise(resolve => writeStream.on('finish', resolve)) const ndjsonStream = ndjson.stringify() @@ -34,7 +34,9 @@ export async function dumpCollectionData(sdk: any, index: string, collection: st fs.mkdirSync(collectionDir, { recursive: true }) - await writeLine({ type: 'collection', index, collection }) + if (format.toLowerCase() === 'jsonl') { + await writeLine({ type: 'collection', index, collection }) + } let results = await sdk.document.search(index, collection, { query }, options) @@ -43,19 +45,39 @@ export async function dumpCollectionData(sdk: any, index: string, collection: st }) progressBar.start(results.total, 0) + const rawDocuments: any = { + [index]: { + [collection]: [] + } + } + do { progressBar.update(results.fetched) for (const hit of results.hits) { - const document = { - _id: hit._id, - body: hit._source + let document = null + if (format.toLocaleLowerCase() === 'jsonl') { + document = { + _id: hit._id, + body: hit._source + } + + await writeLine(document) + } else { + rawDocuments[index][collection].push({ + index: { + _id: hit._id + } + }) + rawDocuments[index][collection].push(hit._source) } - - await writeLine(document) } } while ((results = await results.next())) + if (format.toLocaleLowerCase() === 'kuzzle') { + await writeLine(rawDocuments) + } + progressBar.stop() ndjsonStream.end() writeStream.end() @@ -63,7 +85,7 @@ export async function dumpCollectionData(sdk: any, index: string, collection: st return waitWrite } -export async function dumpCollectionMappings(sdk: any, index: string, collection: string, destPath: string) { +export async function dumpCollectionMappings(sdk: any, index: string, collection: string, destPath: string, format = 'jsonl') { const collectionDir = path.join(destPath, collection) const filename = path.join(collectionDir, 'mappings.json') @@ -80,5 +102,5 @@ export async function dumpCollectionMappings(sdk: any, index: string, collection } } - fs.writeFileSync(filename, JSON.stringify(dump, null, 2)) + fs.writeFileSync(filename, JSON.stringify((format.toLowerCase() === 'jsonl' ? dump : mappings), null, 2)) } From 1ed7155a8d6700272f8e37dde7ec471d58ca20af Mon Sep 17 00:00:00 2001 From: Aschen Date: Wed, 9 Dec 2020 08:49:37 +0100 Subject: [PATCH 09/15] update template --- templates/app-scaffold/ts/app.ts | 8 ++++---- templates/app-scaffold/ts/package.json | 18 ++++++++++-------- tsconfig.json | 1 + 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/templates/app-scaffold/ts/app.ts b/templates/app-scaffold/ts/app.ts index 84dd637d..0865e99a 100644 --- a/templates/app-scaffold/ts/app.ts +++ b/templates/app-scaffold/ts/app.ts @@ -1,9 +1,9 @@ -import { Backend } from 'kuzzle' +import { Backend } from 'kuzzle'; -const app = new Backend('<%= appName %>') +const app = new Backend('<%= appName %>'); app.start() .then(() => { - app.log.info('Application started') + app.log.info('Application started'); }) - .catch(console.error) + .catch(console.error); diff --git a/templates/app-scaffold/ts/package.json b/templates/app-scaffold/ts/package.json index 9bbc21d0..c180eb76 100644 --- a/templates/app-scaffold/ts/package.json +++ b/templates/app-scaffold/ts/package.json @@ -6,6 +6,7 @@ "scripts": { "npm:docker": "docker-compose run kuzzle npm", "install:docker": "npm run npm:docker install", + "services": "npx kourou app:start-services", "dev:docker": "docker-compose up", "dev": "npx nodemon --ext 'js,json,ts' --inspect=0.0.0.0:9229 --exec node -r ts-node/register app.ts", "test": "npm run test:lint && npm run test:unit && npm run test:functional", @@ -13,19 +14,20 @@ "test:functional": "cucumber-js --exit --fail-fast", "test:lint": "eslint ./src --ext .ts --config .eslintrc.json", "test:lint:fix": "eslint ./src --ext .ts --config .eslintrc.json --fix", - "build": "tsc --build tsconfig.json" + "build": "tsc --build tsconfig.json", + "clean": "npm run build | grep TSFILE | cut -d' ' -f 2 | xargs rm" }, "main": "app.ts", "license": "Apache-2.0", "dependencies": { - "kuzzle": ">=2 <3" + "kuzzle": ">=2.8.0 <3" }, "devDependencies": { - "@types/node": "^14.14.7", - "@typescript-eslint/eslint-plugin": "^4.7.0", - "@typescript-eslint/parser": "^4.7.0", + "@types/node": "^14.14.11", + "@typescript-eslint/eslint-plugin": "^4.9.1", + "@typescript-eslint/parser": "^4.9.1", "cucumber": "^6.0.5", - "eslint": "^7.13.0", + "eslint": "^7.15.0", "eslint-friendly-formatter": "^4.0.1", "eslint-loader": "^4.0.2", "mocha": "8.2.1", @@ -35,8 +37,8 @@ "should": "13.2.3", "should-sinon": "0.0.6", "sinon": "^9.2.1", - "ts-node": "^9.0.0", - "typescript": "^4.0.5" + "ts-node": "^9.1.1", + "typescript": "^4.1.2" }, "files": [ "index.js", diff --git a/tsconfig.json b/tsconfig.json index e9b1e299..64709880 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "noUncheckedIndexedAccess": true, "declaration": true, "importHelpers": true, "module": "commonjs", From 9d13c220b7391606d73f98066edae3a956deba9c Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 7 Jan 2021 14:15:09 +0100 Subject: [PATCH 10/15] Move to Github Actions (#90) This PR move away from travis ci in favour of Github actions --- .github/actions/build/action.yml | 11 ++ .github/actions/es-lint/action.yml | 11 ++ .github/actions/functional-test/action.yml | 16 +++ .github/workflows/pull_request.workflow.yml | 88 +++++++++++++++ .github/workflows/push_dev.workflow.yml | 91 ++++++++++++++++ .github/workflows/push_master.workflow.yml | 106 ++++++++++++++++++ .travis.yml | 112 -------------------- tsconfig.json | 1 - 8 files changed, 323 insertions(+), 113 deletions(-) create mode 100644 .github/actions/build/action.yml create mode 100644 .github/actions/es-lint/action.yml create mode 100644 .github/actions/functional-test/action.yml create mode 100644 .github/workflows/pull_request.workflow.yml create mode 100644 .github/workflows/push_dev.workflow.yml create mode 100644 .github/workflows/push_master.workflow.yml delete mode 100644 .travis.yml diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml new file mode 100644 index 00000000..9fbd2723 --- /dev/null +++ b/.github/actions/build/action.yml @@ -0,0 +1,11 @@ +name: Build +description: Run Build +runs: + using: "composite" + steps: + - name: Install deps + run: npm ci --silent + shell: bash + - name: Build and package the application + run: npm pack + shell: bash diff --git a/.github/actions/es-lint/action.yml b/.github/actions/es-lint/action.yml new file mode 100644 index 00000000..16a581eb --- /dev/null +++ b/.github/actions/es-lint/action.yml @@ -0,0 +1,11 @@ +name: ESLint +description: Run ESLint +runs: + using: "composite" + steps: + - name: Install deps + run: npm ci --silent + shell: bash + - name: Run lint tests + run: npm run --silent test:lint + shell: bash diff --git a/.github/actions/functional-test/action.yml b/.github/actions/functional-test/action.yml new file mode 100644 index 00000000..9b4044bb --- /dev/null +++ b/.github/actions/functional-test/action.yml @@ -0,0 +1,16 @@ +name: Functional Tests +description: Run Functional Tests +inputs: + test-set: + description: Test set to run + required: true +runs: + using: "composite" + steps: + - name: Run functional test + run: | + cd package + npm install --silent + features/run-kuzzle-stack.sh + npm run test:functional:${{ inputs.test-set }} + shell: bash diff --git a/.github/workflows/pull_request.workflow.yml b/.github/workflows/pull_request.workflow.yml new file mode 100644 index 00000000..aa86f055 --- /dev/null +++ b/.github/workflows/pull_request.workflow.yml @@ -0,0 +1,88 @@ +name: Run tests + +on: [pull_request] + +jobs: + lint: + name: Lint + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: 12 + - uses: ./.github/actions/es-lint + + build: + name: Build + runs-on: ubuntu-18.04 + needs: [lint] + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: 12 + - uses: ./.github/actions/build + - name: Store build archive as artifact + uses: actions/upload-artifact@v2 + with: + name: kourou-build + path: ./kourou-*.tgz + + functional-test: + name: Functional Test - ${{ matrix.test-set }} + runs-on: ubuntu-18.04 + needs: [build] + strategy: + matrix: + test-set: [stdout, cucumber] + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + NODE_ENV: test + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: 12 + - name: Recover previously built Kourou + uses: actions/download-artifact@v2 + with: + name: kourou-build + - name: Unpack and prepare artifact for tests + run: | + tar xfz ./kourou-*.tgz + cp -fr .mocharc.json .nycrc tsconfig.json features test package/ + - uses: ./.github/actions/functional-test + with: + test-set: ${{ matrix.test-set }} diff --git a/.github/workflows/push_dev.workflow.yml b/.github/workflows/push_dev.workflow.yml new file mode 100644 index 00000000..897d84d6 --- /dev/null +++ b/.github/workflows/push_dev.workflow.yml @@ -0,0 +1,91 @@ +name: Run tests + +on: + push: + branches: + - develop + +jobs: + lint: + name: Lint + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: 12 + - uses: ./.github/actions/es-lint + + build: + name: Build + runs-on: ubuntu-18.04 + needs: [lint] + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: 12 + - uses: ./.github/actions/build + - name: Store build archive as artifact + uses: actions/upload-artifact@v2 + with: + name: kourou-build + path: ./kourou-*.tgz + + functional-test: + name: Functional Test - ${{ matrix.test-set }} + runs-on: ubuntu-18.04 + needs: [build] + strategy: + matrix: + test-set: [stdout, cucumber] + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + NODE_ENV: test + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: 12 + - name: Recover previously built Kourou + uses: actions/download-artifact@v2 + with: + name: kourou-build + - name: Unpack and prepare artifact for tests + run: | + tar xfz ./kourou-*.tgz + cp -fr .mocharc.json .nycrc tsconfig.json features test package/ + - uses: ./.github/actions/functional-test + with: + test-set: ${{ matrix.test-set }} diff --git a/.github/workflows/push_master.workflow.yml b/.github/workflows/push_master.workflow.yml new file mode 100644 index 00000000..87a90277 --- /dev/null +++ b/.github/workflows/push_master.workflow.yml @@ -0,0 +1,106 @@ +name: Run tests + +on: + push: + branches: + - master + +jobs: + lint: + name: Lint + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: 12 + - uses: ./.github/actions/es-lint + + build: + name: Build + runs-on: ubuntu-18.04 + needs: [lint] + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: 12 + - uses: ./.github/actions/build + - name: Store build archive as artifact + uses: actions/upload-artifact@v2 + with: + name: kourou-build + path: ./kourou-*.tgz + + functional-test: + name: Functional Test - ${{ matrix.test-set }} + runs-on: ubuntu-18.04 + needs: [build] + strategy: + matrix: + test-set: [stdout, cucumber] + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + NODE_ENV: test + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: 12 + - name: Recover previously built Kourou + uses: actions/download-artifact@v2 + with: + name: kourou-build + - name: Unpack and prepare artifact for tests + run: | + tar xfz ./kourou-*.tgz + cp -fr .mocharc.json .nycrc tsconfig.json features test package/ + - uses: ./.github/actions/functional-test + with: + test-set: ${{ matrix.test-set }} + + deploy: + name: Deploy to NPM.js + runs-on: ubuntu-18.04 + needs: [functional-test] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 12 + registry-url: "https://registry.npmjs.org" + - run: npm install + - run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ade434b0..00000000 --- a/.travis.yml +++ /dev/null @@ -1,112 +0,0 @@ -jobs: - include: - - stage: Tests - name: "Kourou TS build" - language: node_js - node_js: 12 - cache: - directories: - - ~/.npm - - ~/.cache - - node_modules # NPM packages - - install: - - npm install --silent --unsafe-perm - - npm install --silent --unsafe-perm --only=dev - - script: - - npm run prepack - - - stage: Tests - name: "Kourou Lint" - language: node_js - node_js: 12 - cache: - directories: - - ~/.npm - - ~/.cache - - node_modules # NPM packages - - install: - - npm install --silent --unsafe-perm - - npm install --silent --unsafe-perm --only=dev - - script: - - npm run test:lint - - - stage: Tests - name: "Kourou Functional Stdout Tests" - language: node_js - node_js: 12 - cache: - directories: - - ~/.npm - - ~/.cache - - node_modules # NPM packages - env: - - KOUROU_RUNTIME="./package/bin/run" - - install: - # install kourou dependencies for packing and testing - - npm install --silent --unsafe-perm - # create npm package - - npm pack - # extract to ./package/ - - tar xf kourou-$(node -e 'console.log(require("./package.json").version)').tgz - # install kourou production dependencies for package - - npm install --prefix package --silent --unsafe-perm --only production - # ensure tests always use the generated package - - rm -rf ./bin - - script: - - npm run test:functional:stdout - - - stage: Tests - name: "Kourou Functional Cucumber Tests" - language: node_js - node_js: 12 - cache: - directories: - - ~/.npm - - ~/.cache - - node_modules # NPM packages - env: - - KOUROU_RUNTIME="./package/bin/run" - - NODE_ENV=test - - install: - # install kourou dependencies for packing and testing - - npm install --silent --unsafe-perm - # create npm package - - npm pack - # extract to ./package/ - - tar xf kourou-$(node -e 'console.log(require("./package.json").version)').tgz - # install kourou production dependencies for package - - npm install --prefix package --silent --unsafe-perm --only production - # ensure tests always use the generated package - - rm -rf ./bin - - script: - - bash features/run-kuzzle-stack.sh - - npm run test:functional:cucumber - after_failure: - - docker-compose -f features/docker/docker-compose.yml logs - - - stage: Deploy - name: Deploy Kourou on NPM - if: type = push AND branch = master - sudo: false - language: node_js - node_js: 12 - install: - - npm install - deploy: - edge: true - provider: npm - skip_cleanup: true - email: support@kuzzle.io - api_key: - secure: "J1JpQS8HNaA3HSnSD5HQWiWTGaAk5PapaZNCBMSDhoRo+bgROS64B/eVFLY+qIlilSleRbJ8My2Wh1Iv6vqg4/M0NGTzfkRPbqFW5LD/gXj9Am+r7K5M3S0q48aplPSYS+cBVhxlQAL/pLBUryxwGIvcGYI0GMY6S91nNKVdh/meGOJOpQgr+6TVBua8gvAHONckEu/d8iPv3YDsR3McIDAvLVgHxmRNC4iF5UcUnqsTEWiF23DJEAH7g5cjEcLBLJ+76WLIF2whmjFuc+JIvFBNws2IGPLA5ycjIbJYzDdA6v9Db8FdmfSgQLU660tmd/hVLaUI7P+Db2wthVm3Tk0BWjlnVQnzYPEC4klu+RhhGuK3icrTJ6ljWnbkMXGhP0L21NMiFpY9QfjW03T//d4h+kqLtrFo97xfZ1iPPzy7Lpzycb3dwDIPya/Bqi8xLuMS7jyJuAxaissFRNE3sTmXKS+IkjHgkSlh8t1Cc1DacDO7flGH6o/7TPtiKaTu8E92uzHuOvx4lqwxrkWpy3PBH6j1TsgFLk8oJbUcStuG3Bt4JU/j/y/8vIgjxlcueqyVRv7wwpxUlIRz66Qxz6Ygoo4/OSue+UGNFVEvBYZW9+F2nr/62v2BLbVWCfXjnGyDTZsmTtleBOjFk2JIAKupgIsz8LnBdXvVOot2USI=" - on: - repo: kuzzleio/kourou - all_branches: true diff --git a/tsconfig.json b/tsconfig.json index 64709880..e9b1e299 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "noUncheckedIndexedAccess": true, "declaration": true, "importHelpers": true, "module": "commonjs", From f4032df76fef4585b1ec94ad5c1a94014d8b6c35 Mon Sep 17 00:00:00 2001 From: Alexandre Bouthinon Date: Thu, 7 Jan 2021 17:44:21 +0100 Subject: [PATCH 11/15] Add missing README entry for es:migrate command (#89) Add missing README entry for es:migrate command --- README.md | 25 +++++++++++++++++++++++++ src/commands/index/export.ts | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1dc7db4d..04ee8a57 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,7 @@ Then any argument will be passed as-is to the `sdk:query` method. * [`kourou es:indices:cat`](#kourou-esindicescat) * [`kourou es:indices:get INDEX ID`](#kourou-esindicesget-index-id) * [`kourou es:indices:insert INDEX`](#kourou-esindicesinsert-index) +* [`kourou es:migrate`](#kourou-esmigrate) * [`kourou es:snapshot:create REPOSITORY NAME`](#kourou-essnapshotcreate-repository-name) * [`kourou es:snapshot:create-repository REPOSITORY LOCATION`](#kourou-essnapshotcreate-repository-repository-location) * [`kourou es:snapshot:list REPOSITORY`](#kourou-essnapshotlist-repository) @@ -529,6 +530,30 @@ OPTIONS _See code: [src/commands/es/indices/insert.ts](src/commands/es/indices/insert.ts)_ +## `kourou es:migrate` + +Migrate all the index from one Elasticsearch server to another + +``` +USAGE + $ kourou es:migrate + +OPTIONS + --batch-size=batch-size [default: 1000] How many documents to move in batch per operation + --dest=dest (required) Destination Elasticsearch server URL + --help show CLI help + --no-interactive Skip confirmation interactive prompts (perfect for scripting) + --reset Reset destination Elasticsearch server + --src=src (required) Source Elasticsearch server URL + +EXAMPLES + kourou es:migrate --src http://elasticsearch:9200 --dest http://otherElasticsearch:9200 --reset --batch-size 2000 + kourou es:migrate --src http://elasticsearch:9200 --dest http://otherElasticsearch:9200 --reset --batch-size 2000 + --no-interactive +``` + +_See code: [src/commands/es/migrate/index.ts](src/commands/es/migrate/index.ts)_ + ## `kourou es:snapshot:create REPOSITORY NAME` Create a snapshot repository inside an ES instance diff --git a/src/commands/index/export.ts b/src/commands/index/export.ts index 1a2ddde3..a34fe9ab 100644 --- a/src/commands/index/export.ts +++ b/src/commands/index/export.ts @@ -20,7 +20,7 @@ export default class IndexExport extends Kommand { default: '2000' }), format: flags.string({ - description: '"jsonl or kuzzle - kuzzle will export in Kuzzle format usable for internal fixtures and jsonl will be usable to import that data back with kourou', + description: '"jsonl or kuzzle - kuzzle will export in Kuzzle format usable for internal fixtures and jsonl allows to import that data back with kourou', default: 'jsonl' }), ...kuzzleFlags, From 85cb671daf964a674c4c9806bff2aec8fe19bd1d Mon Sep 17 00:00:00 2001 From: Adrien Maret Date: Tue, 19 Jan 2021 16:36:38 +0100 Subject: [PATCH 12/15] Add yaml support for vault (#92) Add YAML file support for vault:* commands. --- .github/actions/build/action.yml | 2 +- .github/actions/es-lint/action.yml | 4 ++-- .github/actions/functional-test/action.yml | 5 +++-- package-lock.json | 14 ++++++++++--- package.json | 2 +- src/commands/vault/add.ts | 12 ++++++----- src/commands/vault/decrypt.ts | 13 +++++++----- src/commands/vault/encrypt.ts | 13 +++++++----- src/commands/vault/show.ts | 12 ++++++----- templates/app-scaffold/ts/README.md | 24 +++++++++------------- templates/app-scaffold/ts/app.ts | 8 ++++---- templates/app-scaffold/ts/package.json | 21 +++++++++---------- 12 files changed, 72 insertions(+), 58 deletions(-) diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index 9fbd2723..982f4388 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -4,7 +4,7 @@ runs: using: "composite" steps: - name: Install deps - run: npm ci --silent + run: npm ci shell: bash - name: Build and package the application run: npm pack diff --git a/.github/actions/es-lint/action.yml b/.github/actions/es-lint/action.yml index 16a581eb..d5835184 100644 --- a/.github/actions/es-lint/action.yml +++ b/.github/actions/es-lint/action.yml @@ -4,8 +4,8 @@ runs: using: "composite" steps: - name: Install deps - run: npm ci --silent + run: npm ci shell: bash - name: Run lint tests - run: npm run --silent test:lint + run: npm run test:lint shell: bash diff --git a/.github/actions/functional-test/action.yml b/.github/actions/functional-test/action.yml index 9b4044bb..32f566d7 100644 --- a/.github/actions/functional-test/action.yml +++ b/.github/actions/functional-test/action.yml @@ -8,9 +8,10 @@ runs: using: "composite" steps: - name: Run functional test + shell: bash run: | + set -x cd package - npm install --silent + npm install features/run-kuzzle-stack.sh npm run test:functional:${{ inputs.test-set }} - shell: bash diff --git a/package-lock.json b/package-lock.json index 24d485f5..715db92f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3820,9 +3820,12 @@ } }, "kuzzle-vault": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuzzle-vault/-/kuzzle-vault-2.0.0.tgz", - "integrity": "sha512-qljQ8YxtlKKXoa5bqhgdWUXPeUPThlmv93sYqJlxp8e1haDiuV/4LwwoG3bzjFftSs42zeMOa5QhLHy3DQIChQ==" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/kuzzle-vault/-/kuzzle-vault-2.0.4.tgz", + "integrity": "sha512-UxaGoIk4YNkHGWB0wnTDh8Mi5AM+ddUskreZ6VQU8VabPVOWYYc/Pmdg0KxjIj67fChzBi9ejMTYz5XTXeJyXw==", + "requires": { + "yaml": "^1.10.0" + } }, "levn": { "version": "0.4.1", @@ -6367,6 +6370,11 @@ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, + "yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==" + }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", diff --git a/package.json b/package.json index 2f1a0590..0a21416f 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "cli-ux": "^5.5.1", "inquirer": "^7.3.3", "kuzzle-sdk": "^7.4.1", - "kuzzle-vault": "^2.0.0", + "kuzzle-vault": "^2.0.4", "listr": "^0.14.3", "lodash": "^4.17.20", "ndjson": "^2.0.0", diff --git a/src/commands/vault/add.ts b/src/commands/vault/add.ts index c63b7561..e1e8b5e2 100644 --- a/src/commands/vault/add.ts +++ b/src/commands/vault/add.ts @@ -1,7 +1,8 @@ +import fs from 'fs' + import { flags } from '@oclif/command' -import _ from 'lodash' -import fs from 'fs' -import { Cryptonomicon } from 'kuzzle-vault' +import _ from 'lodash' +import { Cryptonomicon, Vault } from 'kuzzle-vault' import { Kommand } from '../../common' @@ -45,10 +46,11 @@ See https://github.com/kuzzleio/kuzzle-vault/ for more information. } const cryptonomicon = new Cryptonomicon(this.flags['vault-key']) + const PARSER = Vault.getParser(this.args['secrets-file']); let encryptedSecrets = {} if (fs.existsSync(this.args['secrets-file'])) { - encryptedSecrets = JSON.parse(fs.readFileSync(this.args['secrets-file'], 'utf8')) + encryptedSecrets = PARSER.parse(fs.readFileSync(this.args['secrets-file'], 'utf8')) try { cryptonomicon.decryptObject(encryptedSecrets) @@ -60,7 +62,7 @@ See https://github.com/kuzzleio/kuzzle-vault/ for more information. _.set(encryptedSecrets, this.args.key, cryptonomicon.encryptString(this.args.value)) - fs.writeFileSync(this.args['secrets-file'], JSON.stringify(encryptedSecrets, null, 2)) + fs.writeFileSync(this.args['secrets-file'], PARSER.stringify(encryptedSecrets, null, 2)) this.logOk(`Key "${this.args.key}" has been securely added "${this.args['secrets-file']}"`) } diff --git a/src/commands/vault/decrypt.ts b/src/commands/vault/decrypt.ts index 2ad6365a..023148fa 100644 --- a/src/commands/vault/decrypt.ts +++ b/src/commands/vault/decrypt.ts @@ -1,7 +1,8 @@ -import fs from 'fs' -import _ from 'lodash' +import fs from 'fs' + +import _ from 'lodash' import { flags } from '@oclif/command' -import { Cryptonomicon } from 'kuzzle-vault' +import { Cryptonomicon, Vault } from 'kuzzle-vault' import { Kommand } from '../../common' @@ -64,9 +65,11 @@ See https://github.com/kuzzleio/kuzzle-vault/ for more information. throw new Error(`File "${this.args.file}" does not exists`) } + const PARSER = Vault.getParser(this.args.file); + let encryptedSecrets = {} try { - encryptedSecrets = JSON.parse(fs.readFileSync(this.args.file, 'utf8')) + encryptedSecrets = PARSER.parse(fs.readFileSync(this.args.file, 'utf8')) } catch (error) { throw new Error(`Cannot read secrets from file "${this.args.file}": ${error.message}`) @@ -74,7 +77,7 @@ See https://github.com/kuzzleio/kuzzle-vault/ for more information. const secrets = cryptonomicon.decryptObject(encryptedSecrets) - fs.writeFileSync(outputFile, JSON.stringify(secrets, null, 2)) + fs.writeFileSync(outputFile, PARSER.stringify(secrets, null, 2)) this.logOk(`Secrets were successfully decrypted into the file ${outputFile}`) } diff --git a/src/commands/vault/encrypt.ts b/src/commands/vault/encrypt.ts index 4c25928e..1cfcb20b 100644 --- a/src/commands/vault/encrypt.ts +++ b/src/commands/vault/encrypt.ts @@ -1,7 +1,8 @@ -import fs from 'fs' -import _ from 'lodash' +import fs from 'fs' + +import _ from 'lodash' import { flags } from '@oclif/command' -import { Cryptonomicon } from 'kuzzle-vault' +import { Cryptonomicon, Vault } from 'kuzzle-vault' import { Kommand } from '../../common' @@ -76,9 +77,11 @@ See https://github.com/kuzzleio/kuzzle-vault/ for more information. throw new Error(`File "${this.args.file}" does not exists`) } + const PARSER = Vault.getParser(this.args.file); + let secrets = {} try { - secrets = JSON.parse(fs.readFileSync(this.args.file, 'utf8')) + secrets = PARSER.parse(fs.readFileSync(this.args.file, 'utf8')) } catch (error) { throw new Error(`Cannot read secrets from file "${this.args.file}": ${error.message}`) @@ -86,7 +89,7 @@ See https://github.com/kuzzleio/kuzzle-vault/ for more information. const encryptedSecrets = cryptonomicon.encryptObject(secrets) - fs.writeFileSync(outputFile, JSON.stringify(encryptedSecrets, null, 2)) + fs.writeFileSync(outputFile, PARSER.stringify(encryptedSecrets, null, 2)) this.logOk(`Secrets were successfully encrypted into the file ${outputFile}`) } diff --git a/src/commands/vault/show.ts b/src/commands/vault/show.ts index bf6f7905..bafd214f 100644 --- a/src/commands/vault/show.ts +++ b/src/commands/vault/show.ts @@ -1,8 +1,8 @@ import { flags } from '@oclif/command' -import _ from 'lodash' -import fs from 'fs' +import _ from 'lodash' +import fs from 'fs' import chalk from 'chalk' -import { Cryptonomicon } from 'kuzzle-vault' +import { Cryptonomicon, Vault } from 'kuzzle-vault' import { Kommand } from '../../common' @@ -51,9 +51,11 @@ See https://github.com/kuzzleio/kuzzle-vault/ for more information. throw new Error(`File "${this.args['secrets-file']}" does not exists`) } + const PARSER = Vault.getParser(this.args['secrets-file']); + let encryptedSecrets = {} try { - encryptedSecrets = JSON.parse(fs.readFileSync(this.args['secrets-file'], 'utf8')) + encryptedSecrets = PARSER.parse(fs.readFileSync(this.args['secrets-file'], 'utf8')) } catch (error) { throw new Error(`Cannot read secrets from file "${this.args['secrets-file']}": ${error.message}`) @@ -75,7 +77,7 @@ See https://github.com/kuzzleio/kuzzle-vault/ for more information. const decryptedSecrets = cryptonomicon.decryptObject(encryptedSecrets) this.logOk('Secrets file content:') - this.log(chalk.green(JSON.stringify(decryptedSecrets, null, 2))) + this.log(chalk.green(PARSER.stringify(decryptedSecrets, null, 2))) } } } diff --git a/templates/app-scaffold/ts/README.md b/templates/app-scaffold/ts/README.md index c57b77bf..2b89ac91 100644 --- a/templates/app-scaffold/ts/README.md +++ b/templates/app-scaffold/ts/README.md @@ -2,22 +2,18 @@ _An application running with [Kuzzle](https://github.com/kuzzleio/kuzzle)_ -## Development +## Installation and run -### Installation +Requirement: + - Node.js >= 12 + - NPM >= 6 + - Docker + - Docker-Compose -The command `kourou app:scaffold` takes care of installing dependencies for you, so you shouldn't need to worry about it. +First, install [Kourou](https://github.com/kuzzleio/kourou), the Kuzzle CLI: `npm install -g kourou` -However, if for some reason you need to reinstall dependencies (or add new ones), then you can easily do so by running the following command: +Then you need to start the services used by Kuzzle, Elasticsearch and Redis. You can run those services in the background with the following command: `kourou app:start-services` -`npm run install:docker` +Finally you can start your application with `kourou app:run`. -This command installs the dependencies listed in the `package.json` file of this project, inside the docker image. Since some dependencies are written in C or in C++ and compiled, installing in the target docker image ensures that there won't be incompatibility problems if your current system is different from the one used by docker to execute Kuzzle. - -### Run - -You can use the following command to run your application in development mode: - -`npm run dev:docker` - -The application will be reloaded whenever the code changes. +Under the hood this command simply run Node.js with Typescript as following: `node -r ts-node/register app.ts` diff --git a/templates/app-scaffold/ts/app.ts b/templates/app-scaffold/ts/app.ts index 0865e99a..84dd637d 100644 --- a/templates/app-scaffold/ts/app.ts +++ b/templates/app-scaffold/ts/app.ts @@ -1,9 +1,9 @@ -import { Backend } from 'kuzzle'; +import { Backend } from 'kuzzle' -const app = new Backend('<%= appName %>'); +const app = new Backend('<%= appName %>') app.start() .then(() => { - app.log.info('Application started'); + app.log.info('Application started') }) - .catch(console.error); + .catch(console.error) diff --git a/templates/app-scaffold/ts/package.json b/templates/app-scaffold/ts/package.json index c180eb76..2d310c9b 100644 --- a/templates/app-scaffold/ts/package.json +++ b/templates/app-scaffold/ts/package.json @@ -6,8 +6,8 @@ "scripts": { "npm:docker": "docker-compose run kuzzle npm", "install:docker": "npm run npm:docker install", - "services": "npx kourou app:start-services", "dev:docker": "docker-compose up", + "services": "npx kourou app:start-services", "dev": "npx nodemon --ext 'js,json,ts' --inspect=0.0.0.0:9229 --exec node -r ts-node/register app.ts", "test": "npm run test:lint && npm run test:unit && npm run test:functional", "test:unit": "mocha", @@ -20,25 +20,24 @@ "main": "app.ts", "license": "Apache-2.0", "dependencies": { - "kuzzle": ">=2.8.0 <3" + "kuzzle": "^2" }, "devDependencies": { - "@types/node": "^14.14.11", - "@typescript-eslint/eslint-plugin": "^4.9.1", - "@typescript-eslint/parser": "^4.9.1", + "@types/node": "^14.6.0", + "@typescript-eslint/eslint-plugin": "^3.9.1", + "@typescript-eslint/parser": "^3.9.1", "cucumber": "^6.0.5", - "eslint": "^7.15.0", + "eslint": "^7.7.0", "eslint-friendly-formatter": "^4.0.1", "eslint-loader": "^4.0.2", - "mocha": "8.2.1", + "mocha": "8.0.1", "mock-require": "^3.0.3", - "nodemon": "^2.0.6", "rewire": "^5.0.0", "should": "13.2.3", "should-sinon": "0.0.6", - "sinon": "^9.2.1", - "ts-node": "^9.1.1", - "typescript": "^4.1.2" + "sinon": "^9.0.3", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" }, "files": [ "index.js", From 070d574a6a6589f1e558abc1cef89affc127a96a Mon Sep 17 00:00:00 2001 From: Adrien Maret Date: Mon, 8 Feb 2021 13:20:42 +0100 Subject: [PATCH 13/15] Update app template (#93) Update the application template: bump dependencies add github actions add functional test framework --- package-lock.json | 945 ++++++++---------- package.json | 28 +- src/commands/instance/kill.ts | 2 +- src/support/dump-collection.ts | 2 +- src/support/execute.ts | 2 +- src/support/restore-collection.ts | 4 +- .../backend-functional-test/action.yml | 14 + .../.github/actions/backend-lint/action.yml | 11 + .../workflows/pull_request.workflow.yml | 47 + .../workflows/push_master.workflow.yml | 50 + .../workflows/push_production.workflow.yml | 50 + templates/app-scaffold/ts/docker-compose.yml | 4 +- .../ts/features/fixtures/fixtures.js | 2 + .../ts/features/fixtures/mappings.js | 1 + .../ts/features/fixtures/rights.js | 2 + .../step_definitions/common/auth-steps.js | 8 + .../common/collection-steps.js | 72 ++ .../common/controllers-steps.js | 121 +++ .../common/documents-steps.js | 182 ++++ .../step_definitions/common/realtime-steps.js | 115 +++ .../step_definitions/common/security-steps.js | 190 ++++ .../ts/features/support/assertions.js | 53 + .../app-scaffold/ts/features/support/hooks.js | 108 ++ .../app-scaffold/ts/features/support/world.js | 92 ++ .../app-scaffold/ts/features/wait-kuzzle.sh | 23 + .../app-scaffold/ts/lib/controllers/index.ts | 0 templates/app-scaffold/ts/lib/models/index.ts | 0 .../app-scaffold/ts/lib/plugins/index.ts | 0 .../app-scaffold/ts/lib/services/index.ts | 0 templates/app-scaffold/ts/lib/types/index.ts | 0 templates/app-scaffold/ts/package.json | 4 +- test/commands/app/scaffold.test.ts | 2 +- 32 files changed, 1597 insertions(+), 537 deletions(-) create mode 100644 templates/app-scaffold/ts/.github/actions/backend-functional-test/action.yml create mode 100644 templates/app-scaffold/ts/.github/actions/backend-lint/action.yml create mode 100644 templates/app-scaffold/ts/.github/workflows/pull_request.workflow.yml create mode 100644 templates/app-scaffold/ts/.github/workflows/push_master.workflow.yml create mode 100644 templates/app-scaffold/ts/.github/workflows/push_production.workflow.yml create mode 100644 templates/app-scaffold/ts/features/fixtures/fixtures.js create mode 100644 templates/app-scaffold/ts/features/fixtures/mappings.js create mode 100644 templates/app-scaffold/ts/features/fixtures/rights.js create mode 100644 templates/app-scaffold/ts/features/step_definitions/common/auth-steps.js create mode 100644 templates/app-scaffold/ts/features/step_definitions/common/collection-steps.js create mode 100644 templates/app-scaffold/ts/features/step_definitions/common/controllers-steps.js create mode 100644 templates/app-scaffold/ts/features/step_definitions/common/documents-steps.js create mode 100644 templates/app-scaffold/ts/features/step_definitions/common/realtime-steps.js create mode 100644 templates/app-scaffold/ts/features/step_definitions/common/security-steps.js create mode 100644 templates/app-scaffold/ts/features/support/assertions.js create mode 100644 templates/app-scaffold/ts/features/support/hooks.js create mode 100644 templates/app-scaffold/ts/features/support/world.js create mode 100644 templates/app-scaffold/ts/features/wait-kuzzle.sh create mode 100644 templates/app-scaffold/ts/lib/controllers/index.ts create mode 100644 templates/app-scaffold/ts/lib/models/index.ts create mode 100644 templates/app-scaffold/ts/lib/plugins/index.ts create mode 100644 templates/app-scaffold/ts/lib/services/index.ts create mode 100644 templates/app-scaffold/ts/lib/types/index.ts diff --git a/package-lock.json b/package-lock.json index 715db92f..f3b09ad7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -259,9 +259,9 @@ } }, "@eslint/eslintrc": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz", - "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", + "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -271,7 +271,7 @@ "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, @@ -759,74 +759,93 @@ } }, "@oclif/dev-cli": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@oclif/dev-cli/-/dev-cli-1.23.0.tgz", - "integrity": "sha512-oao7e2gmCQsF5X0NRdrCmtQkcbCErtvAIYM3tpnfT9dvyUdyGU+plPx9q0iCV5C5qRNB5qh6JbVFqfjJP+djxg==", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@oclif/dev-cli/-/dev-cli-1.26.0.tgz", + "integrity": "sha512-272udZP+bG4qahoAcpWcMTJKiA+V42kRMqQM7n4tgW35brYb2UP5kK+p08PpF8sgSfRTV8MoJVJG9ax5kY82PA==", "dev": true, "requires": { - "@oclif/command": "^1.5.13", - "@oclif/config": "^1.12.12", - "@oclif/errors": "^1.2.2", - "@oclif/plugin-help": "^2.1.6", + "@oclif/command": "^1.8.0", + "@oclif/config": "^1.17.0", + "@oclif/errors": "^1.3.3", + "@oclif/plugin-help": "^3.2.0", "cli-ux": "^5.2.1", "debug": "^4.1.1", - "fs-extra": "^9.0.1", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^8.1", "github-slugger": "^1.2.1", "lodash": "^4.17.11", - "normalize-package-data": "^2.5.0", + "normalize-package-data": "^3.0.0", "qqjs": "^0.3.10", - "tslib": "^1.9.3" + "tslib": "^2.0.3" }, "dependencies": { - "@oclif/plugin-help": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-2.2.3.tgz", - "integrity": "sha512-bGHUdo5e7DjPJ0vTeRBMIrfqTRDBfyR5w0MP41u0n3r7YG5p14lvMmiCXxi6WDaP2Hw5nqx3PnkAIntCKZZN7g==", + "@oclif/errors": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@oclif/errors/-/errors-1.3.4.tgz", + "integrity": "sha512-pJKXyEqwdfRTUdM8n5FIHiQQHg5ETM0Wlso8bF9GodczO40mF5Z3HufnYWJE7z8sGKxOeJCdbAVZbS8Y+d5GCw==", "dev": true, "requires": { - "@oclif/command": "^1.5.13", - "chalk": "^2.4.1", + "clean-stack": "^3.0.0", + "fs-extra": "^8.1", "indent-string": "^4.0.0", - "lodash.template": "^4.4.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0", - "widest-line": "^2.0.1", - "wrap-ansi": "^4.0.0" + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "color-convert": "^2.0.1" } }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "clean-stack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", + "dev": true, + "requires": { + "escape-string-regexp": "4.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "requires": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } }, "indent-string": { @@ -835,75 +854,41 @@ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - }, - "dependencies": { - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - } + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "ansi-regex": "^5.0.0" } }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", - "dev": true - }, - "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "requires": { - "string-width": "^2.1.1" - }, - "dependencies": { - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } } } @@ -954,12 +939,13 @@ } }, "@oclif/plugin-help": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-3.2.0.tgz", - "integrity": "sha512-7jxtpwVWAVbp1r46ZnTK/uF+FeZc6y4p1XcGaIUuPAp7wx6NJhIRN/iMT9UfNFX/Cz7mq+OyJz+E+i0zrik86g==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-3.2.1.tgz", + "integrity": "sha512-vq7rn16TrQmjX3Al/k1Z5iBZWZ3HE8fDXs52OmDJmmTqryPSNvURH9WCAsqr0PODYCSR17Hy1VTzS0x7vVVLEQ==", "requires": { "@oclif/command": "^1.5.20", "@oclif/config": "^1.15.1", + "@oclif/errors": "^1.2.2", "chalk": "^2.4.1", "indent-string": "^4.0.0", "lodash.template": "^4.4.0", @@ -1020,9 +1006,9 @@ "integrity": "sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw==" }, "@oclif/test": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@oclif/test/-/test-1.2.7.tgz", - "integrity": "sha512-1I8KQkOgScw18mr0zcly/OJjDW+kZnnmGXELaVBF69EZVzVgjE+Mz3EU1W29wf2rB7e8BqqtNrHzzoFu4oDuTw==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@oclif/test/-/test-1.2.8.tgz", + "integrity": "sha512-HCh0qPge1JCqTEw4s2ScnicEZd4Ro4/0VvdjpsfCiX6fuDV53fRZ2uqLTgxKGHrVoqOZnVrRZHyhFyEsFGs+zQ==", "dev": true, "requires": { "fancy-test": "^1.4.3" @@ -1095,9 +1081,9 @@ } }, "@types/lodash": { - "version": "4.14.162", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.162.tgz", - "integrity": "sha512-alvcho1kRUnnD1Gcl4J+hK0eencvzq9rmzvFPRmP5rPHx9VVsJj6bKLTATPVf9ktgv4ujzh7T+XWKp+jhuODig==", + "version": "4.14.168", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz", + "integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==", "dev": true }, "@types/minimatch": { @@ -1107,9 +1093,9 @@ "dev": true }, "@types/mocha": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.4.tgz", - "integrity": "sha512-M4BwiTJjHmLq6kjON7ZoI2JMlBvpY3BYSdiP6s/qCT3jb1s9/DeJF0JELpAxiVSIxXDzfNKe+r7yedMIoLbknQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.0.tgz", + "integrity": "sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ==", "dev": true }, "@types/ndjson": { @@ -1122,9 +1108,9 @@ } }, "@types/node": { - "version": "14.14.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.7.tgz", - "integrity": "sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg==" + "version": "14.14.22", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz", + "integrity": "sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw==" }, "@types/node-emoji": { "version": "1.8.1", @@ -1133,9 +1119,9 @@ "dev": true }, "@types/sinon": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.8.tgz", - "integrity": "sha512-IVnI820FZFMGI+u1R+2VdRaD/82YIQTdqLYC9DLPszZuynAJDtCvCtCs3bmyL66s7FqRM3+LPX7DhHnVTaagDw==", + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.10.tgz", + "integrity": "sha512-/faDC0erR06wMdybwI/uR8wEKV/E83T0k4sepIpB7gXuy2gzx2xiOjmztq6a2Y6rIGJ04D+6UU0VBmWy+4HEMA==", "dev": true, "requires": { "@types/sinonjs__fake-timers": "*" @@ -1161,48 +1147,43 @@ "integrity": "sha512-flgpHJjntpBAdJD43ShRosQvNC0ME97DCfGvZEDlAThQmnerRXrLbX6YgzRBQCZTthET9eAWFAMaYP0m0Y4HzQ==" }, "@typescript-eslint/eslint-plugin": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.7.0.tgz", - "integrity": "sha512-li9aiSVBBd7kU5VlQlT1AqP0uWGDK6JYKUQ9cVDnOg34VNnd9t4jr0Yqc/bKxJr/tDCPDaB4KzoSFN9fgVxe/Q==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.1.tgz", + "integrity": "sha512-5JriGbYhtqMS1kRcZTQxndz1lKMwwEXKbwZbkUZNnp6MJX0+OVXnG0kOlBZP4LUAxEyzu3cs+EXd/97MJXsGfw==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.7.0", - "@typescript-eslint/scope-manager": "4.7.0", + "@typescript-eslint/experimental-utils": "4.14.1", + "@typescript-eslint/scope-manager": "4.14.1", "debug": "^4.1.1", "functional-red-black-tree": "^1.0.1", + "lodash": "^4.17.15", "regexpp": "^3.0.0", "semver": "^7.3.2", "tsutils": "^3.17.1" }, "dependencies": { "@typescript-eslint/experimental-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.7.0.tgz", - "integrity": "sha512-cymzovXAiD4EF+YoHAB5Oh02MpnXjvyaOb+v+BdpY7lsJXZQN34oIETeUwVT2XfV9rSNpXaIcknDLfupO/tUoA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.1.tgz", + "integrity": "sha512-2CuHWOJwvpw0LofbyG5gvYjEyoJeSvVH2PnfUQSn0KQr4v8Dql2pr43ohmx4fdPQ/eVoTSFjTi/bsGEXl/zUUQ==", "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.7.0", - "@typescript-eslint/types": "4.7.0", - "@typescript-eslint/typescript-estree": "4.7.0", + "@typescript-eslint/scope-manager": "4.14.1", + "@typescript-eslint/types": "4.14.1", + "@typescript-eslint/typescript-estree": "4.14.1", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" } }, - "@typescript-eslint/types": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.7.0.tgz", - "integrity": "sha512-uLszFe0wExJc+I7q0Z/+BnP7wao/kzX0hB5vJn4LIgrfrMLgnB2UXoReV19lkJQS1a1mHWGGODSxnBx6JQC3Sg==", - "dev": true - }, "@typescript-eslint/typescript-estree": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.7.0.tgz", - "integrity": "sha512-5XZRQznD1MfUmxu1t8/j2Af4OxbA7EFU2rbo0No7meb46eHgGkSieFdfV6omiC/DGIBhH9H9gXn7okBbVOm8jw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.1.tgz", + "integrity": "sha512-M8+7MbzKC1PvJIA8kR2sSBnex8bsR5auatLCnVlNTJczmJgqRn8M+sAlQfkEq7M4IY3WmaNJ+LJjPVRrREVSHQ==", "dev": true, "requires": { - "@typescript-eslint/types": "4.7.0", - "@typescript-eslint/visitor-keys": "4.7.0", + "@typescript-eslint/types": "4.14.1", + "@typescript-eslint/visitor-keys": "4.14.1", "debug": "^4.1.1", "globby": "^11.0.1", "is-glob": "^4.0.1", @@ -1211,16 +1192,6 @@ "tsutils": "^3.17.1" } }, - "@typescript-eslint/visitor-keys": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.7.0.tgz", - "integrity": "sha512-aDJDWuCRsf1lXOtignlfiPODkzSxxop7D0rZ91L6ZuMlcMCSh0YyK+gAfo5zN/ih6WxMwhoXgJWC3cWQdaKC+A==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.7.0", - "eslint-visitor-keys": "^2.0.0" - } - }, "eslint-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", @@ -1228,22 +1199,8 @@ "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } } }, - "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true - }, "regexpp": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", @@ -1251,10 +1208,13 @@ "dev": true }, "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, @@ -1282,31 +1242,25 @@ } }, "@typescript-eslint/parser": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.7.0.tgz", - "integrity": "sha512-+meGV8bMP1sJHBI2AFq1GeTwofcGiur8LoIr6v+rEmD9knyCqDlrQcFHR0KDDfldHIFDU/enZ53fla6ReF4wRw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.14.1.tgz", + "integrity": "sha512-mL3+gU18g9JPsHZuKMZ8Z0Ss9YP1S5xYZ7n68Z98GnPq02pYNQuRXL85b9GYhl6jpdvUc45Km7hAl71vybjUmw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.7.0", - "@typescript-eslint/types": "4.7.0", - "@typescript-eslint/typescript-estree": "4.7.0", + "@typescript-eslint/scope-manager": "4.14.1", + "@typescript-eslint/types": "4.14.1", + "@typescript-eslint/typescript-estree": "4.14.1", "debug": "^4.1.1" }, "dependencies": { - "@typescript-eslint/types": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.7.0.tgz", - "integrity": "sha512-uLszFe0wExJc+I7q0Z/+BnP7wao/kzX0hB5vJn4LIgrfrMLgnB2UXoReV19lkJQS1a1mHWGGODSxnBx6JQC3Sg==", - "dev": true - }, "@typescript-eslint/typescript-estree": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.7.0.tgz", - "integrity": "sha512-5XZRQznD1MfUmxu1t8/j2Af4OxbA7EFU2rbo0No7meb46eHgGkSieFdfV6omiC/DGIBhH9H9gXn7okBbVOm8jw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.1.tgz", + "integrity": "sha512-M8+7MbzKC1PvJIA8kR2sSBnex8bsR5auatLCnVlNTJczmJgqRn8M+sAlQfkEq7M4IY3WmaNJ+LJjPVRrREVSHQ==", "dev": true, "requires": { - "@typescript-eslint/types": "4.7.0", - "@typescript-eslint/visitor-keys": "4.7.0", + "@typescript-eslint/types": "4.14.1", + "@typescript-eslint/visitor-keys": "4.14.1", "debug": "^4.1.1", "globby": "^11.0.1", "is-glob": "^4.0.1", @@ -1315,68 +1269,31 @@ "tsutils": "^3.17.1" } }, - "@typescript-eslint/visitor-keys": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.7.0.tgz", - "integrity": "sha512-aDJDWuCRsf1lXOtignlfiPODkzSxxop7D0rZ91L6ZuMlcMCSh0YyK+gAfo5zN/ih6WxMwhoXgJWC3cWQdaKC+A==", + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, "requires": { - "@typescript-eslint/types": "4.7.0", - "eslint-visitor-keys": "^2.0.0" + "lru-cache": "^6.0.0" } - }, - "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true } } }, "@typescript-eslint/scope-manager": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.7.0.tgz", - "integrity": "sha512-ILITvqwDJYbcDCROj6+Ob0oCKNg3SH46iWcNcTIT9B5aiVssoTYkhKjxOMNzR1F7WSJkik4zmuqve5MdnA0DyA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.14.1.tgz", + "integrity": "sha512-F4bjJcSqXqHnC9JGUlnqSa3fC2YH5zTtmACS1Hk+WX/nFB0guuynVK5ev35D4XZbdKjulXBAQMyRr216kmxghw==", "dev": true, "requires": { - "@typescript-eslint/types": "4.7.0", - "@typescript-eslint/visitor-keys": "4.7.0" - }, - "dependencies": { - "@typescript-eslint/types": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.7.0.tgz", - "integrity": "sha512-uLszFe0wExJc+I7q0Z/+BnP7wao/kzX0hB5vJn4LIgrfrMLgnB2UXoReV19lkJQS1a1mHWGGODSxnBx6JQC3Sg==", - "dev": true - }, - "@typescript-eslint/visitor-keys": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.7.0.tgz", - "integrity": "sha512-aDJDWuCRsf1lXOtignlfiPODkzSxxop7D0rZ91L6ZuMlcMCSh0YyK+gAfo5zN/ih6WxMwhoXgJWC3cWQdaKC+A==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.7.0", - "eslint-visitor-keys": "^2.0.0" - } - }, - "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true - } + "@typescript-eslint/types": "4.14.1", + "@typescript-eslint/visitor-keys": "4.14.1" } }, "@typescript-eslint/types": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz", - "integrity": "sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.14.1.tgz", + "integrity": "sha512-SkhzHdI/AllAgQSxXM89XwS1Tkic7csPdndUuTKabEwRcEfR8uQ/iPA3Dgio1rqsV3jtqZhY0QQni8rLswJM2w==", "dev": true }, "@typescript-eslint/typescript-estree": { @@ -1403,12 +1320,21 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz", - "integrity": "sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.1.tgz", + "integrity": "sha512-TAblbDXOI7bd0C/9PE1G+AFo7R5uc+ty1ArDoxmrC1ah61Hn6shURKy7gLdRb1qKJmjHkqu5Oq+e4Kt0jwf1IA==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" + "@typescript-eslint/types": "4.14.1", + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true + } } }, "@ungap/promise-all-settled": { @@ -1576,9 +1502,9 @@ } }, "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, "at-least-node": { @@ -2155,6 +2081,12 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -2419,13 +2351,13 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.13.0.tgz", - "integrity": "sha512-uCORMuOO8tUzJmsdRtrvcGq5qposf7Rw0LwkTJkoDbOycVQtQjmnhZSuLQnozLE4TmAzlMVV45eCHmQ1OpDKUQ==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", + "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.2.1", + "@eslint/eslintrc": "^0.3.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -2435,10 +2367,10 @@ "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.0", + "espree": "^7.3.1", "esquery": "^1.2.0", "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", + "file-entry-cache": "^6.0.0", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", "globals": "^12.1.0", @@ -2449,7 +2381,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -2458,7 +2390,7 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^5.2.3", + "table": "^6.0.4", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -2522,10 +2454,13 @@ "dev": true }, "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "shebang-command": { "version": "2.0.0", @@ -2728,13 +2663,13 @@ "dev": true }, "espree": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", - "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", "dev": true, "requires": { "acorn": "^7.4.0", - "acorn-jsx": "^5.2.0", + "acorn-jsx": "^5.3.1", "eslint-visitor-keys": "^1.3.0" } }, @@ -2867,9 +2802,9 @@ "dev": true }, "fancy-test": { - "version": "1.4.9", - "resolved": "https://registry.npmjs.org/fancy-test/-/fancy-test-1.4.9.tgz", - "integrity": "sha512-Tro3lkXPX438G3t2N9BDgD3ac5iUhNnxIE8tg/KL6z7eZ5GOCexs7fDEMacduqvJWPvsRlmyQ69V1jiTVcqkXQ==", + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/fancy-test/-/fancy-test-1.4.10.tgz", + "integrity": "sha512-AaUX6wKS7D5OP2YK2q5G7c8PGx2lgoyLUD7Bbg8z323sb9aebBqzb9UN6phzI73UgO/ViihmNfOxF3kdfZLhew==", "dev": true, "requires": { "@types/chai": "*", @@ -2878,6 +2813,7 @@ "@types/sinon": "*", "lodash": "^4.17.13", "mock-stdin": "^1.0.0", + "nock": "^13.0.0", "stdout-stderr": "^0.1.9" } }, @@ -2929,12 +2865,12 @@ } }, "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz", + "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==", "dev": true, "requires": { - "flat-cache": "^2.0.1" + "flat-cache": "^3.0.4" } }, "fill-range": { @@ -2966,6 +2902,15 @@ "path-exists": "^4.0.0" } }, + "find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "dev": true, + "requires": { + "micromatch": "^4.0.2" + } + }, "flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", @@ -2973,31 +2918,19 @@ "dev": true }, "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - }, - "dependencies": { - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } + "flatted": "^3.1.0", + "rimraf": "^3.0.2" } }, "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", "dev": true }, "foreground-child": { @@ -3188,9 +3121,9 @@ } }, "globby": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", - "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz", + "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==", "dev": true, "requires": { "array-union": "^2.1.0", @@ -3257,10 +3190,13 @@ "dev": true }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.8.tgz", + "integrity": "sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } }, "hpagent": { "version": "0.1.1", @@ -3312,9 +3248,9 @@ "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" }, "import-fresh": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", - "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -3811,12 +3747,12 @@ } }, "kuzzle-sdk": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/kuzzle-sdk/-/kuzzle-sdk-7.4.1.tgz", - "integrity": "sha512-JROmbucILLuOZJualIUMgk+Wdix/cOVH5GiLPnGRtYTHKr0ymtteEu+eL8aKWpRArYGVcJfN85QksWDCcSRXeg==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/kuzzle-sdk/-/kuzzle-sdk-7.5.3.tgz", + "integrity": "sha512-Bt59vQInjvDOYeaEYvRGqNnIjQ8IBkMEgu/rkYObGtSfbppPYSwawKlKt8hOe8aKlUyzXyL7uj8LRpX66EMFKw==", "requires": { "min-req-promise": "^1.0.1", - "ws": "^7.3.1" + "ws": "^7.4.2" } }, "kuzzle-vault": { @@ -4006,9 +3942,9 @@ }, "dependencies": { "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -4074,6 +4010,12 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", + "dev": true + }, "lodash.snakecase": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", @@ -4229,9 +4171,9 @@ } }, "loglevel": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.0.tgz", - "integrity": "sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", + "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==", "dev": true }, "loglevel-colored-level-prefix": { @@ -4292,6 +4234,15 @@ "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", "dev": true }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -4352,15 +4303,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, "mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -4563,6 +4505,18 @@ "lower-case": "^1.1.1" } }, + "nock": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.6.tgz", + "integrity": "sha512-W81UZ1Tv21SWDZcA8Lu9LXYVl2gO9ADY5GadC6gFV9690h4TXz0oCkEoMckN/sPMHkDA79Ka9dXga9Mt1+j+Sg==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash.set": "^4.3.2", + "propagate": "^2.0.0" + } + }, "node-emoji": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", @@ -4581,15 +4535,26 @@ } }, "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz", + "integrity": "sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw==", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", + "hosted-git-info": "^3.0.6", + "resolve": "^1.17.0", + "semver": "^7.3.2", "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "normalize-path": { @@ -4961,21 +4926,21 @@ "dev": true }, "prettier": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.2.tgz", - "integrity": "sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", "dev": true }, "prettier-eslint": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-11.0.0.tgz", - "integrity": "sha512-ACjL7T8m10HCO7DwYdXwhNWuZzQv86JkZAhVpzFV9brTMWi3i6LhqoELFaXf6RetDngujz89tnbDmGyvDl+rzA==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-12.0.0.tgz", + "integrity": "sha512-N8SGGQwAosISXTNl1E57sBbtnqUGlyRWjcfIUxyD3HF4ynehA9GZ8IfJgiep/OfYvCof/JEpy9ZqSl250Wia7A==", "dev": true, "requires": { "@typescript-eslint/parser": "^3.0.0", "common-tags": "^1.4.0", "dlv": "^1.1.0", - "eslint": "^6.8.0", + "eslint": "^7.9.0", "indent-string": "^4.0.0", "lodash.merge": "^4.6.0", "loglevel-colored-level-prefix": "^1.0.0", @@ -5012,6 +4977,12 @@ "eslint-visitor-keys": "^1.1.0" } }, + "@typescript-eslint/types": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz", + "integrity": "sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==", + "dev": true + }, "@typescript-eslint/typescript-estree": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz", @@ -5028,77 +4999,13 @@ "tsutils": "^3.17.1" } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "eslint": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", - "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "@typescript-eslint/visitor-keys": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz", + "integrity": "sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.3", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.2", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^7.0.0", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.3", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "eslint-visitor-keys": "^1.1.0" } }, "eslint-utils": { @@ -5110,72 +5017,19 @@ "eslint-visitor-keys": "^1.1.0" } }, - "espree": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", - "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, "indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "lru-cache": "^6.0.0" } }, "typescript": { @@ -5219,6 +5073,12 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -5388,6 +5248,12 @@ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -5618,9 +5484,9 @@ "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=" }, "sort-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-4.1.0.tgz", - "integrity": "sha512-/sRdxzkkPFUYiCrTr/2t+104nDc9AgDmEpeVYuvOWYQe3Djk1GWO6lVw3Vx2jfh1SsR0eehhd1nvFYlzt5e99w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz", + "integrity": "sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==", "dev": true, "requires": { "is-plain-obj": "^2.0.0" @@ -5702,9 +5568,9 @@ } }, "spdx-license-ids": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz", - "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", "dev": true }, "split2": { @@ -5890,43 +5756,100 @@ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" }, "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", + "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", "dev": true, "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" + "ajv": "^7.0.2", + "lodash": "^4.17.20", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0" }, "dependencies": { - "emoji-regex": { + "ajv": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.3.tgz", + "integrity": "sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" } }, "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" } } } @@ -5944,9 +5867,9 @@ } }, "tar-stream": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz", - "integrity": "sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "dev": true, "requires": { "bl": "^4.0.3", @@ -6037,12 +5960,13 @@ } }, "ts-node": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", - "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", "dev": true, "requires": { "arg": "^4.1.0", + "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", "source-map-support": "^0.5.17", @@ -6050,9 +5974,9 @@ } }, "tslib": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", - "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" }, "tsutils": { "version": "3.17.1", @@ -6116,9 +6040,9 @@ } }, "typescript": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", - "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", + "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", "dev": true }, "universalify": { @@ -6133,9 +6057,9 @@ "dev": true }, "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -6315,15 +6239,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -6351,9 +6266,9 @@ } }, "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz", + "integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA==" }, "xregexp": { "version": "4.3.0", @@ -6370,6 +6285,12 @@ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "yaml": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", diff --git a/package.json b/package.json index 0a21416f..e248c56d 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "@elastic/elasticsearch": "^7.10.0", "@oclif/command": "^1.8.0", "@oclif/config": "^1.17.0", - "@oclif/plugin-help": "^3.2.0", + "@oclif/plugin-help": "^3.2.1", "@types/bluebird": "^3.5.33", "@types/inquirer": "^7.3.1", "@types/ndjson": "^2.0.0", @@ -20,7 +20,7 @@ "chalk": "^4.1.0", "cli-ux": "^5.5.1", "inquirer": "^7.3.3", - "kuzzle-sdk": "^7.4.1", + "kuzzle-sdk": "^7.5.3", "kuzzle-vault": "^2.0.4", "listr": "^0.14.3", "lodash": "^4.17.20", @@ -28,33 +28,33 @@ "node-emoji": "^1.10.0", "strip-json-comments": "^3.1.1", "tmp": "^0.2.1", - "tslib": "^2.0.3" + "tslib": "^2.1.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@oclif/dev-cli": "^1.23.0", - "@oclif/test": "^1.2.7", + "@oclif/dev-cli": "^1.26.0", + "@oclif/test": "^1.2.8", "@types/chai": "^4.2.14", "@types/compare-version": "^0.1.31", "@types/listr": "^0.14.2", - "@types/mocha": "^8.0.4", - "@types/node": "^14.14.7", + "@types/mocha": "^8.2.0", + "@types/node": "^14.14.22", "@types/node-emoji": "^1.8.1", - "@typescript-eslint/eslint-plugin": "^4.7.0", - "@typescript-eslint/parser": "^4.7.0", + "@typescript-eslint/eslint-plugin": "^4.14.1", + "@typescript-eslint/parser": "^4.14.1", "chai": "^4.2.0", "cucumber": "^6.0.5", - "eslint": "^7.13.0", + "eslint": "^7.18.0", "eslint-config-oclif": "^3.1.0", "eslint-config-oclif-typescript": "^0.2.0", - "globby": "^11.0.1", + "globby": "^11.0.2", "mocha": "^8.2.1", "nyc": "^15.1.0", - "prettier-eslint": "^11.0.0", + "prettier-eslint": "^12.0.0", "should": "^13.2.3", "source-map-support": "^0.5.19", - "ts-node": "^9.0.0", - "typescript": "^4.0.5" + "ts-node": "^9.1.1", + "typescript": "^4.1.3" }, "engines": { "node": ">=10.0.0" diff --git a/src/commands/instance/kill.ts b/src/commands/instance/kill.ts index f607e17a..d37806dd 100644 --- a/src/commands/instance/kill.ts +++ b/src/commands/instance/kill.ts @@ -97,7 +97,7 @@ export class InstanceLogs extends Kommand { ) ) } - resolve() + resolve(undefined) })) } diff --git a/src/support/dump-collection.ts b/src/support/dump-collection.ts index 9493e331..00f2672b 100644 --- a/src/support/dump-collection.ts +++ b/src/support/dump-collection.ts @@ -18,7 +18,7 @@ export async function dumpCollectionData(sdk: any, index: string, collection: st const writeLine = (content: any) => { return new Promise(resolve => { if (ndjsonStream.write(content)) { - resolve() + resolve(undefined) } else { ndjsonStream.once('drain', resolve) diff --git a/src/support/execute.ts b/src/support/execute.ts index 465aa1a2..b177981a 100644 --- a/src/support/execute.ts +++ b/src/support/execute.ts @@ -54,7 +54,7 @@ export function execute(...args: any[]): ProcessExecutor { process.stderr.on('data', data => stderr += data.toString()) const executor: any = new Promise((resolve, reject) => { - process.on('close', code => { + process.on('close', (code: any) => { if (code === 0) { resolve({ stdout, stderr, exitCode: code }) } diff --git a/src/support/restore-collection.ts b/src/support/restore-collection.ts index 234cd119..b3f7e150 100644 --- a/src/support/restore-collection.ts +++ b/src/support/restore-collection.ts @@ -97,7 +97,7 @@ export async function restoreCollectionData(sdk: any, log: any, batchSize: numbe process.stdout.write(` ${total} documents imported`) process.stdout.write('\r') - resolve() + resolve(undefined) }) .catch((error: any) => { try { @@ -110,7 +110,7 @@ export async function restoreCollectionData(sdk: any, log: any, batchSize: numbe }) } else { - resolve() + resolve(undefined) } }) }) diff --git a/templates/app-scaffold/ts/.github/actions/backend-functional-test/action.yml b/templates/app-scaffold/ts/.github/actions/backend-functional-test/action.yml new file mode 100644 index 00000000..8a60b551 --- /dev/null +++ b/templates/app-scaffold/ts/.github/actions/backend-functional-test/action.yml @@ -0,0 +1,14 @@ +name: Backend - Functional Tests +description: Run Functional Tests for backend +runs: + using: "composite" + steps: + - name: Run backend functional test + shell: bash + run: | + cd backend + npm install + npm run build + DOCKER_COMMAND="npm run prod" docker-compose up -d + bash features/wait-kuzzle.sh + npm run test:functional diff --git a/templates/app-scaffold/ts/.github/actions/backend-lint/action.yml b/templates/app-scaffold/ts/.github/actions/backend-lint/action.yml new file mode 100644 index 00000000..5390f7ca --- /dev/null +++ b/templates/app-scaffold/ts/.github/actions/backend-lint/action.yml @@ -0,0 +1,11 @@ +name: Backend - ESLint +description: Run ESLint for backend +runs: + using: "composite" + steps: + - name: run backend lint + shell: bash + run: | + cd backend + npm ci + npm run test:lint diff --git a/templates/app-scaffold/ts/.github/workflows/pull_request.workflow.yml b/templates/app-scaffold/ts/.github/workflows/pull_request.workflow.yml new file mode 100644 index 00000000..c24962ee --- /dev/null +++ b/templates/app-scaffold/ts/.github/workflows/pull_request.workflow.yml @@ -0,0 +1,47 @@ +name: Pull request checks + +on: [pull_request] + +jobs: + lint: + name: Lint + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: "12" + - uses: ./.github/actions/backend-lint + + functional-tests: + name: Functional Tests + runs-on: ubuntu-18.04 + needs: [lint] + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: "12" + - uses: ./.github/actions/backend-functional-test diff --git a/templates/app-scaffold/ts/.github/workflows/push_master.workflow.yml b/templates/app-scaffold/ts/.github/workflows/push_master.workflow.yml new file mode 100644 index 00000000..2ccdd91e --- /dev/null +++ b/templates/app-scaffold/ts/.github/workflows/push_master.workflow.yml @@ -0,0 +1,50 @@ +name: Push checks + +on: + push: + branches: + - master + +jobs: + lint: + name: Lint + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: "12" + - uses: ./.github/actions/backend-lint + + functional-tests: + name: Functional Tests + runs-on: ubuntu-18.04 + needs: [lint] + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: "12" + - uses: ./.github/actions/backend-functional-test diff --git a/templates/app-scaffold/ts/.github/workflows/push_production.workflow.yml b/templates/app-scaffold/ts/.github/workflows/push_production.workflow.yml new file mode 100644 index 00000000..a1f6d329 --- /dev/null +++ b/templates/app-scaffold/ts/.github/workflows/push_production.workflow.yml @@ -0,0 +1,50 @@ +name: Push checks + +on: + push: + branches: + - production + +jobs: + lint: + name: Lint + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: "12" + - uses: ./.github/actions/backend-lint + + functional-tests: + name: Functional Tests + runs-on: ubuntu-18.04 + needs: [lint] + steps: + - uses: actions/checkout@v2 + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - uses: actions/setup-node@v1.4.4 + with: + node-version: "12" + - uses: ./.github/actions/backend-functional-test diff --git a/templates/app-scaffold/ts/docker-compose.yml b/templates/app-scaffold/ts/docker-compose.yml index a836a97e..d928ed50 100644 --- a/templates/app-scaffold/ts/docker-compose.yml +++ b/templates/app-scaffold/ts/docker-compose.yml @@ -3,7 +3,7 @@ version: "3" services: kuzzle: image: kuzzleio/kuzzle-runner - command: npm run dev + command: ${DOCKER_COMMAND:-npm run dev} cap_add: - SYS_PTRACE ulimits: @@ -32,7 +32,5 @@ services: elasticsearch: image: kuzzleio/elasticsearch:7 - ports: - - "9200:9200" ulimits: nofile: 65536 diff --git a/templates/app-scaffold/ts/features/fixtures/fixtures.js b/templates/app-scaffold/ts/features/fixtures/fixtures.js new file mode 100644 index 00000000..631f3756 --- /dev/null +++ b/templates/app-scaffold/ts/features/fixtures/fixtures.js @@ -0,0 +1,2 @@ +module.exports = { +} diff --git a/templates/app-scaffold/ts/features/fixtures/mappings.js b/templates/app-scaffold/ts/features/fixtures/mappings.js new file mode 100644 index 00000000..f053ebf7 --- /dev/null +++ b/templates/app-scaffold/ts/features/fixtures/mappings.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/templates/app-scaffold/ts/features/fixtures/rights.js b/templates/app-scaffold/ts/features/fixtures/rights.js new file mode 100644 index 00000000..7be35b6b --- /dev/null +++ b/templates/app-scaffold/ts/features/fixtures/rights.js @@ -0,0 +1,2 @@ +module.exports = { +}; diff --git a/templates/app-scaffold/ts/features/step_definitions/common/auth-steps.js b/templates/app-scaffold/ts/features/step_definitions/common/auth-steps.js new file mode 100644 index 00000000..4b396b44 --- /dev/null +++ b/templates/app-scaffold/ts/features/step_definitions/common/auth-steps.js @@ -0,0 +1,8 @@ +const + { + Given + } = require('cucumber'); + +Given('I\'m logged in Kuzzle as user {string} with password {string}', function (username, password) { + return this.sdk.auth.login('local', { username, password }); +}); diff --git a/templates/app-scaffold/ts/features/step_definitions/common/collection-steps.js b/templates/app-scaffold/ts/features/step_definitions/common/collection-steps.js new file mode 100644 index 00000000..48d3d673 --- /dev/null +++ b/templates/app-scaffold/ts/features/step_definitions/common/collection-steps.js @@ -0,0 +1,72 @@ +const + { + Then, + Given + } = require('cucumber'), + should = require('should'); + +Given('an existing index {string}', async function (index) { + if (!await this.sdk.index.exists(index)) { + throw new Error(`Index ${index} does not exist`); + } + + this.props.index = index; +}); + +Given('an index {string}', async function (index) { + await this.sdk.index.create(index); + + this.props.index = index; +}); + +Given('a collection {string}:{string}', async function (index, collection) { + this.props.result = await this.sdk.collection.create(index, collection); + + this.props.index = index; + this.props.collection = collection; +}); + +Given('an existing collection {string}:{string}', async function (index, collection) { + if (!await this.sdk.index.exists(index)) { + throw new Error(`Index ${index} does not exist`); + } + + if (!await this.sdk.collection.exists(index, collection)) { + throw new Error(`Collection ${index}:${collection} does not exist`); + } + + this.props.index = index; + this.props.collection = collection; +}); + +Then('I list collections in index {string}', async function (index) { + this.props.result = await this.sdk.collection.list(index); +}); + +Then(/I should( not)? see the collection "(.*?)":"(.*?)"/, async function (not, index, collection) { + const { collections } = await this.sdk.collection.list(index); + + const collectionNames = collections.map(({ name }) => name); + + if (not) { + should(collectionNames).not.containEql(collection); + } else { + should(collectionNames).containEql(collection); + } +}); + +Then('I get mappings of collection {string}:{string}', async function (index, collection) { + this.props.result = await this.sdk.collection.getMapping(index, collection); +}); + +Then('I refresh the collection', function () { + return this.sdk.collection.refresh(this.props.index, this.props.collection); +}); + +Then('I refresh the collection {string}:{string}', function (index, collection) { + return this.sdk.collection.refresh(index, collection); +}); + +Then('I wait for a refresh', async function() { + await new Promise(resolve => setTimeout(resolve, 2000)); +}); \ No newline at end of file diff --git a/templates/app-scaffold/ts/features/step_definitions/common/controllers-steps.js b/templates/app-scaffold/ts/features/step_definitions/common/controllers-steps.js new file mode 100644 index 00000000..4a9baafa --- /dev/null +++ b/templates/app-scaffold/ts/features/step_definitions/common/controllers-steps.js @@ -0,0 +1,121 @@ +const + _ = require('lodash'), + should = require('should'), + { + When, + Then + } = require('cucumber'); + +When(/I (successfully )?execute the action "(.*?)":"(.*?)" with args:/, async function (expectSuccess, controller, action, dataTable) { + const args = this.parseObject(dataTable); + + try { + const response = await this.sdk.query({ controller, action, ...args }); + + this.props.result = response.result; + } + catch (error) { + if (expectSuccess) { + throw error; + } + + this.props.error = error; + } +}); + +When(/I (successfully )?execute the action "(.*?)":"(.*?)"$/, async function (expectSuccess, controller, action) { + try { + const response = await this.sdk.query({ controller, action }); + + this.props.result = response.result; + } + catch (error) { + if (expectSuccess) { + throw error; + } + + this.props.error = error; + } +}); + +Then('I should receive a result matching:', function (dataTable) { + const expectedResult = this.parseObject(dataTable); + + should(this.props.result).not.be.undefined(); + + should(this.props.result).matchObject(expectedResult); +}); + +Then('The property {string} of the result should match:', function (path, dataTable) { + const expectedProperty = this.parseObject(dataTable); + + const property = _.get(this.props.result, path); + + should(property).not.be.undefined(); + + if (_.isPlainObject(property)) { + should(property).matchObject(expectedProperty); + } + else { + should(property).match(expectedProperty); + } +}); + +Then('The property string {string} of the result should match {string}', function (path, expectedMatch) { + const property = _.get(this.props.result, path); + + should(property).not.be.undefined(); + + if (_.isPlainObject(property)) { + should(property).matchObject(expectedProperty); + } + else { + should(property).match(expectedProperty); + } +}); + +Then('The result should contain a property {string} of type {string}', function (path, type) { + const property = _.get(this.props.result, path); + + should(property).not.be.undefined(); + + should(typeof property).be.eql(type); +}); + +Then('I should receive a {string} result equals to {string}', function (type, rawResult) { + let expectedResult; + + if (type === 'string') { + expectedResult = rawResult; + } + else if (type === 'int') { + expectedResult = parseInt(rawResult); + } + else { + throw new Error(`Unknown result type '${type}'`); + } + + should(this.props.result).not.be.undefined(); + + should(this.props.result).eql(expectedResult); +}); + +Then('I should receive an empty result', function () { + should(this.props.result).be.undefined(); +}); + +Then('I should receive an error matching:', function (dataTable) { + const expectedError = this.parseObject(dataTable); + + should(this.props.error).not.be.undefined(); + + should(this.props.error).match(expectedError); +}); + +Then('I debug {string}', function (path) { + console.log(JSON.stringify(_.get(this.props, path), null, 2)); +}); + +Then('I wait {int} ms', async function (ms) { + await new Promise(resolve => setTimeout(resolve, ms)); +}); diff --git a/templates/app-scaffold/ts/features/step_definitions/common/documents-steps.js b/templates/app-scaffold/ts/features/step_definitions/common/documents-steps.js new file mode 100644 index 00000000..8e0706b7 --- /dev/null +++ b/templates/app-scaffold/ts/features/step_definitions/common/documents-steps.js @@ -0,0 +1,182 @@ +const + _ = require('lodash'), + should = require('should'), + { + Then + } = require('cucumber'); + +Then('The document {string} content match:', async function (documentId, dataTable) { + const expectedContent = this.parseObject(dataTable); + + const document = await this.sdk.document.get( + this.props.index, + this.props.collection, + documentId); + + should(document._source).matchObject(expectedContent); +}); + +Then('The document {string}:{string}:{string} content match:', async function (index, collection, documentId, dataTable) { + const expectedContent = this.parseObject(dataTable); + + const document = await this.sdk.document.get(index, collection, documentId); + + should(document._source).matchObject(expectedContent); +}); + +Then('I {string} the following documents:', async function (action, dataTable) { + action = `m${action[0].toUpperCase() + action.slice(1)}`; + + const documents = this.parseObjectArray(dataTable); + + this.props.result = await this.sdk.document[action]( + this.props.index, + this.props.collection, + documents); +}); + +Then('I {string} the document {string} with content:', async function (action, _id, dataTable) { + const body = this.parseObject(dataTable); + + if (action === 'create') { + this.props.result = await this.sdk.document[action]( + this.props.index, + this.props.collection, + body, + _id); + } + else if (action === 'write') { + const { _source: document } = await this.sdk.document.get( + this.props.index, + this.props.collection, + _id); + + const content = { + ...document, + ...body + }; + + this.props.result = await this.sdk.bulk.write( + this.props.index, + this.props.collection, + content, + _id); + } + else { + this.props.result = await this.sdk.document[action]( + this.props.index, + this.props.collection, + _id, + body); + } +}); + +Then('I should receive a {string} array of objects matching:', function (name, dataTable) { + const expected = this.parseObjectArray(dataTable); + + should(this.props.result[name].length).be.eql( + expected.length, + `Array are not the same size: expected ${expected.length} got ${this.props.result[name].length}`); + + for (let i = 0; i < expected.length; i++) { + should(this.props.result[name][i]).matchObject(expected[i]); + } +}); + +Then('I should receive a {string} array matching:', function (name, dataTable) { + const expected = _.flatten(dataTable.rawTable).map(JSON.parse); + + should(this.props.result[name].length).be.eql( + expected.length, + `Array are not the same size: expected ${expected.length} got ${this.props.result[name].length}`); + + should(this.props.result[name].sort()).match(expected.sort()); +}); + +Then('I should receive a empty {string} array', function (name) { + should(this.props.result[name]).be.Array().be.empty(); +}); + +Then('I count {int} documents', async function (expectedCount) { + const count = await this.sdk.document.count( + this.props.index, + this.props.collection); + + should(count).be.eql(expectedCount); +}); + +Then('I count {int} documents in {string}:{string}', async function (expectedCount, index, collection) { + const count = await this.sdk.document.count( + index, + collection); + + should(count).be.eql(expectedCount); +}); + +Then('I count {int} documents matching:', async function (expectedCount, dataTable) { + const properties = this.parseObject(dataTable); + + const query = { + match: { + ...properties + } + }; + + const count = await this.sdk.document.count( + this.props.index, + this.props.collection, + { query }); + + should(count).be.eql(expectedCount); +}); + +Then(/The document "(.*?)":"(.*?)":"(.*?)"( does not)? exist/, async function (index, collection, id, not) { + const exists = await this.sdk.document.exists(index, collection, id); + + if (not && exists) { + throw new Error(`Document ${id} exists, but it shouldn't`); + } + + if (!not && !exists) { + throw new Error(`Expected document ${id} to exist`); + } +}); + +Then(/I "(.*?)" the following document ids( with verb "(.*?)")?:/, async function (action, verb, dataTable) { + action = `m${action[0].toUpperCase() + action.slice(1)}`; + const options = verb ? { verb, refresh: 'wait_for' } : { refresh: 'wait_for' }; + + const ids = _.flatten(dataTable.rawTable).map(JSON.parse); + + this.props.result = await this.sdk.document[action]( + this.props.index, + this.props.collection, + ids, + options); +}); + +Then('I search documents with the following query:', function (queryRaw) { + const query = JSON.parse(queryRaw); + + this.props.searchBody = { query }; +}); + +Then('with the following highlights:', function (highlightsRaw) { + const highlights = JSON.parse(highlightsRaw); + + this.props.searchBody.highlight = highlights; +}); + +Then('I execute the search query', async function () { + this.props.result = await this.sdk.document.search( + this.props.index, + this.props.collection, + this.props.searchBody); +}); + +Then('I delete the document {string}', async function (id) { + this.props.result = await this.sdk.document.delete( + this.props.index, + this.props.collection, + id); +}); diff --git a/templates/app-scaffold/ts/features/step_definitions/common/realtime-steps.js b/templates/app-scaffold/ts/features/step_definitions/common/realtime-steps.js new file mode 100644 index 00000000..83d3832e --- /dev/null +++ b/templates/app-scaffold/ts/features/step_definitions/common/realtime-steps.js @@ -0,0 +1,115 @@ +'use strict'; + +const util = require('util') + +const _ = require('lodash'); +const should = require('should'); +const { Then } = require('cucumber'); + +Then('I subscribe to {string}:{string} notifications', async function (index, collection) { + if (! this.props.subscriptions) { + this.props.subscriptions = {}; + } + + const roomId = await this.sdk.realtime.subscribe( + index, + collection, + {}, + notification => { + this.props.subscriptions[`${index}:${collection}`].notifications.push(notification); + }); + + this.props.subscriptions[`${index}:${collection}`] = { + unsubscribe: () => this.sdk.realtime.unsubscribe(roomId), + notifications: [] + }; +}); + +Then('I should have receive {string} notifications for {string}:{string}', function (rawNumber, index, collection) { + const expectedCount = parseInt(rawNumber, 10); + + should(this.props.subscriptions[`${index}:${collection}`].notifications) + .have.length(expectedCount); +}); + +Then('I should receive realtime notifications for {string}:{string} matching:', function (index, collection, datatable, done) { + setTimeout(() => { + let expectedNotifications; + let subscription; + + try { + expectedNotifications = this.parseObjectArray(datatable); + + should(this.props.subscriptions[`${index}:${collection}`]).not.be.undefined(); + + subscription = this.props.subscriptions[`${index}:${collection}`]; + should(subscription.notifications).be.length(expectedNotifications.length); + + for (let i = 0; i < expectedNotifications.length; i++) { + should(subscription.notifications[i]).matchObject(expectedNotifications[i]); + } + + done(); + } + catch (error) { + console.log('expected', util.inspect(expectedNotifications, false, null, true)); + + if (subscription) { + console.log('received', util.inspect(subscription.notifications, false, null, true)); + } + + done(error); + } + }, 100); +}); + +Then('I should have {string} subscriber on {string}:{string} with filter:', async function (rawCount, index, collection, rawFilter) { + const filter = eval(`var o = ${rawFilter}; o`); + const expectedCount = parseInt(rawCount, 10); + + const roomId = await this.sdk.realtime.subscribe(index, collection, filter, () => {}); + + await this.sdk.realtime.unsubscribe(roomId); + + const { result: subscriptions } = await this.sdk.query({ + controller: 'realtime', + action: 'list' + }); + + // If the count is not set because no subscriptions are left, set it to 0 + const count = _.get(subscriptions, `${index}.${collection}.${roomId}`) || 0; + + if (expectedCount === 0) { + should(count).be.eql(0); + } + else { + should(subscriptions).have.property(index); + should(subscriptions[index]).have.property(collection); + should(count).be.eql(expectedCount); + } +}); + +Then('I should have {string} subscriber on {string}:{string} without filter', async function (rawCount, index, collection) { + const expectedCount = parseInt(rawCount, 10); + + const roomId = await this.sdk.realtime.subscribe(index, collection, {}, () => {}); + + await this.sdk.realtime.unsubscribe(roomId); + + const { result: subscriptions } = await this.sdk.query({ + controller: 'realtime', + action: 'list' + }); + + // If the count is not set because no subscriptions are left, set it to 0 + const count = _.get(subscriptions, `${index}.${collection}.${roomId}`); + + if (expectedCount === 0) { + should(count).be.eql(0); + } + else { + should(subscriptions).have.property(index); + should(subscriptions[index]).have.property(collection); + should(count).be.eql(expectedCount); + } +}); diff --git a/templates/app-scaffold/ts/features/step_definitions/common/security-steps.js b/templates/app-scaffold/ts/features/step_definitions/common/security-steps.js new file mode 100644 index 00000000..46574415 --- /dev/null +++ b/templates/app-scaffold/ts/features/step_definitions/common/security-steps.js @@ -0,0 +1,190 @@ +'use strict'; + +const _ = require('lodash'); +const should = require('should'); +const { Then } = require('cucumber'); + +Then(/I (try to )?create a (strict )?profile "(.*?)" with the following policies:/, async function (trying, strict, profileId, dataTable) { + const data = this.parseObject(dataTable); + const policies = []; + + this.props.error = null; + + for (const [roleId, restrictedTo] of Object.entries(data)) { + policies.push({ roleId, restrictedTo }); + } + + try { + if (!strict) { + this.props.result = await this.sdk.security.createProfile(profileId, { + policies, + }); + } + else { + // @todo replace sdk.query once createProfile handles the new "strict" option + this.props.result = await this.sdk.query({ + controller: 'security', + action: 'createProfile', + _id: profileId, + body: { policies }, + strict: true, + }); + } + } + catch (e) { + if (trying) { + this.props.error = e; + return; + } + throw e; + } +}); + +Then(/I (can not )?"(.*?)" a role "(.*?)" with the following API rights:/, async function (not, method, roleId, dataTable) { + const controllers = this.parseObject(dataTable); + const options = { + force: not ? false : true + }; + + await this.tryAction( + this.sdk.security[method + 'Role'](roleId, { controllers }, options), + not); +}); + +Then(/I am (not )?able to get a role with id "(.*?)"/, function (not, roleId) { + return this.tryAction(this.sdk.security.getRole(roleId), not); +}); + +Then(/I am (not )?able to get a profile with id "(.*?)"/, async function (not, profileId) { + return this.tryAction(this.sdk.security.getProfile(profileId), not, `Profile ${profileId} exists`); +}); + +Then('I am able to find {int} roles by searching controller:', async function (count, dataTable) { + const controller = this.parseObject(dataTable); + + const result = await this.sdk.security.searchRoles(controller); + + this.props.result = result; +}); + +Then('I am able to mGet roles and get {int} roles with the following ids:', async function (count, dataTable) { + const data = this.parseObject(dataTable); + const roleIds = []; + for (const role of Object.values(data.ids)) { + roleIds.push(role); + } + + this.props.result = await this.sdk.security.mGetRoles(roleIds); +}); + +Then(/I (can not )?delete the role "(.*?)"/, function (not, roleId) { + return this.tryAction( + this.sdk.security.deleteRole(roleId, { refresh: 'wait_for' }), + not); +}); + +Then(/I (can not )?delete the profile "(.*?)"/, function (not, profileId) { + return this.tryAction( + this.sdk.security.deleteProfile(profileId, { refresh: 'wait_for' }), + not); +}); + +Then('I delete the user {string}', async function (userId) { + this.props.result = await this.sdk.security.deleteUser(userId, { refresh: 'wait_for' }); +}); + +Then('I create a user {string} with content:', async function (userId, dataTable) { + const content = this.parseObject(dataTable); + + const body = { + content, + credentials: { + local: { + username: userId, + password: 'password' + } + } + }; + + this.props.result = await this.sdk.security.createUser( + userId, + body, + { refresh: 'wait_for' }); +}); + +Then('I update the role {string} with:', async function (roleId, dataTable) { + const controllers = this.parseObject(dataTable); + + const rights = {}; + + for (const [controller, actions] of Object.entries(controllers)) { + rights[controller] = { actions }; + } + + this.props.result = await this.sdk.security.updateRole(roleId, rights, { refresh: 'wait_for' }); +}); + +Then('The role {string} should match the default one', async function (roleId) { + const + defaultRoles = this.kuzzleConfig.security.standard.roles, + role = await this.sdk.security.getRole(roleId); + + for (const [controller, actions] of Object.entries(role.controllers)) { + should(actions).match(defaultRoles[roleId].controllers[controller]); + } +}); + +Then('The role {string} should match:', async function (roleId, dataTable) { + const + controllers = this.parseObject(dataTable), + role = await this.sdk.security.getRole(roleId); + + for (const [controller, actions] of Object.entries(controllers)) { + should(actions).match(role.controllers[controller].actions); + } +}); + +Then('The profile {string} policies should match:', async function (profileId, dataTable) { + const expectedPolicies = this.parseObjectArray(dataTable); + + const profile = await this.sdk.security.getProfile(profileId); + + should(profile.policies).have.length(expectedPolicies.length); + + for (let i = 0; i < profile.policies; i++) { + should(profile.policies[i]).match(expectedPolicies[i]); + } +}); + +Then('The user {string} should have the following profiles:', async function (userId, dataTable) { + const expectedProfiles = _.flatten(dataTable.rawTable); + + const user = await this.sdk.security.getUser(userId); + + should(user.profileIds).be.eql(expectedProfiles); +}); + +Then(/The user "(.*?)"( should not)? exists/, async function (userId, shouldNot) { + try { + await this.sdk.security.getUser(userId); + + if (shouldNot) { + throw new Error(`User "${userId}" should not exists.`); + } + } + catch (error) { + if (error.status === 404) { + if (! shouldNot) { + throw new Error(`User "${userId}" should exists.`); + } + } + else { + throw error; + } + } +}); + +Then('I am able to mGet users with the following ids:', async function (dataTable) { + const userIds = _.flatten(dataTable.rawTable).map(JSON.parse); + this.props.result = await this.sdk.security.mGetUsers(userIds); +}); diff --git a/templates/app-scaffold/ts/features/support/assertions.js b/templates/app-scaffold/ts/features/support/assertions.js new file mode 100644 index 00000000..3495e051 --- /dev/null +++ b/templates/app-scaffold/ts/features/support/assertions.js @@ -0,0 +1,53 @@ +const _ = require('lodash'); +const should = require('should'); + +should.Assertion.add( + 'matchObject', + function (expected) { + this.params = { operator: 'match object' }; + + for (const [keyPath, expectedValue] of Object.entries(expected)) { + const objectValue = _.get(this.obj, keyPath); + + if (expectedValue === '_ANY_') { + should(objectValue).not.be.undefined(); + } + else if (expectedValue === '_STRING_') { + should(objectValue).be.String(); + } + else if (expectedValue === '_NUMBER_') { + should(objectValue).be.Number(); + } + else if (expectedValue === '_OBJECT_') { + should(objectValue).be.Object(); + } + else if (expectedValue === '_UNDEFINED_') { + should(objectValue).be.undefined(); + } + else if (expectedValue === '_DATE_NOW_') { + should(objectValue).be.approximately(Date.now(), 1000); + } + else if (expectedValue === '_DATE_NOW_SEC_') { + should(objectValue).be.approximately(Date.now() / 1000, 1000); + } + else if (_.isPlainObject(objectValue)) { + should(objectValue).matchObject( + expectedValue, + `"${keyPath}" does not match. Expected "${JSON.stringify(expectedValue)}" have "${JSON.stringify(objectValue)}"`); + } + else if (_.isArray(objectValue)) { + for (let i = 0; i < objectValue.length; i++) { + should(objectValue[i]).matchObject( + expectedValue[i], + `"${keyPath}[${i}]" does not match. Expected "${JSON.stringify(expectedValue[i])}" have "${JSON.stringify(objectValue[i])}"`); + } + } + else { + should(objectValue).match( + expectedValue, + `"${keyPath}" does not match. Expected "${JSON.stringify(expectedValue)}" have "${JSON.stringify(objectValue)}"`); + } + } + }, + false +); diff --git a/templates/app-scaffold/ts/features/support/hooks.js b/templates/app-scaffold/ts/features/support/hooks.js new file mode 100644 index 00000000..85bf717b --- /dev/null +++ b/templates/app-scaffold/ts/features/support/hooks.js @@ -0,0 +1,108 @@ +'use strict'; + +const _ = require('lodash'); +const { After, Before, BeforeAll } = require('cucumber'); +const { Kuzzle, WebSocket } = require('kuzzle-sdk'); + +const defaultMappings = require('../fixtures/mappings'); +const defaultFixtures = require('../fixtures/fixtures'); +const defaultRights = require('../fixtures/rights'); + +const World = require('./world'); + +BeforeAll({ timeout: 30 * 1000 }, async function () { + const world = new World({}); + + if (world.port.toString() === '443') { + throw new Error(`Detecting tests on port 443. Are you targeting a production server? (${world.host})`) + } + + world.sdk = new Kuzzle( + new WebSocket(world.host, { port: world.port }) + ); + + console.log(`Connecting to Kuzzle at ${world.host}:${world.port}..`); + + await world.sdk.connect(); + + await world.sdk.query({ + controller: 'admin', + action: 'loadSecurities', + body: defaultRights, + refresh: 'wait_for', + onExistingUsers: 'overwrite', + }); + + await world.sdk.query({ + controller: 'admin', + action: 'loadMappings', + body: defaultMappings, + refresh: 'wait_for' + }); + + world.sdk.disconnect(); +}); + +Before({ timeout: 30 * 1000 }, async function () { + this.props.now = Date.now(); + this.props.i = 1; + + this.sdk = new Kuzzle( + new WebSocket(this.host, { port: this.port }) + ); + + await this.sdk.connect(); + + await this.sdk.query({ + controller: 'admin', + action: 'loadFixtures', + body: defaultFixtures, + refresh: 'wait_for' + }); +}); + +After(async function () { + // Clean values stored by the scenario + this.props = {}; + + if (this.sdk && typeof this.sdk.disconnect === 'function') { + this.sdk.disconnect(); + } +}); + +// security hooks ============================================================== + +After({ tags: '@security', timeout: 60 * 1000 }, async function () { + await resetSecurityDefault(this.sdk); +}); + +async function resetSecurityDefault(sdk) { + await sdk.query({ + controller: 'admin', + action: 'resetSecurity', + refresh: 'wait_for' + }); + + sdk.jwt = null; + + await sdk.query({ + controller: 'admin', + action: 'loadSecurities', + body: defaultRights, + refresh: 'wait_for', + onExistingUsers: 'overwrite', + }); +} + +// realtime hooks ============================================================== + +After({ tags: '@realtime' }, function () { + if (_.isEmpty(this.props.subscriptions)) { + throw new Error('@realtime time has been set but no subscriptions have been made.'); + } + + const promises = Object.values(this.props.subscriptions) + .map(({ unsubscribe }) => unsubscribe()); + + return Promise.all(promises); +}); diff --git a/templates/app-scaffold/ts/features/support/world.js b/templates/app-scaffold/ts/features/support/world.js new file mode 100644 index 00000000..2fa0c334 --- /dev/null +++ b/templates/app-scaffold/ts/features/support/world.js @@ -0,0 +1,92 @@ +const ms = require('ms'); +const _ = require('lodash'); +const { setWorldConstructor } = require('cucumber'); + +require('./assertions'); + +class KuzzleWorld { + constructor(attach, parameters) { + this.attach = attach.attach; + this.parameters = parameters; + + this.host = process.env.KUZZLE_HOST || 'localhost'; + this.port = process.env.KUZZLE_PORT || '7512'; + + // Intermediate steps should store values inside this object + this.props = {}; + } + + parseObject(dataTable) { + const + rawContent = dataTable.rowsHash(), + content = {}; + + for (const [path, value] of Object.entries(rawContent)) { + if (value.includes('_AGO_')) { + // format: "_5m_AGO_" + const timeAgo = ms(value.split('_')[1]); + + _.set(content, path, this.props.now - timeAgo); + } + else { + _.set(content, path, eval(`var o = ${value}; o`)); + } + } + + return content; + } + + parseObjectArray(dataTable) { + const + objectArray = [], + keys = dataTable.rawTable[0]; + + for (let i = 1; i < dataTable.rawTable.length; i++) { + const + object = {}, + rawObject = dataTable.rawTable[i]; + + for (let j = 0; j < keys.length; j++) { + if (rawObject[j] !== '-') { + _.set(object, keys[j], eval(`var o = ${rawObject[j]}; o`)); + } + } + + objectArray.push(object); + } + + return objectArray; + } + + /** + * Await the promise provided in the argument, and throw an error depending + * on whether we expect the action to succeed or not + * + * @param {Promise} promise + * @param {boolean} failureExpected + * @param {string} [message] optional custom error message + * @throws If expectations are not met + */ + async tryAction(promise, failureExpected, message) { + this.props.error = null; + + try { + this.props.result = await promise; + } + catch (e) { + this.props.error = e; + } + + if (failureExpected && !this.props.error) { + throw new Error(message || 'Expected action to fail'); + } + + if (!failureExpected && this.props.error) { + throw this.props.error; + } + } +} + +setWorldConstructor(KuzzleWorld); + +module.exports = KuzzleWorld; diff --git a/templates/app-scaffold/ts/features/wait-kuzzle.sh b/templates/app-scaffold/ts/features/wait-kuzzle.sh new file mode 100644 index 00000000..9e89241d --- /dev/null +++ b/templates/app-scaffold/ts/features/wait-kuzzle.sh @@ -0,0 +1,23 @@ +# #!/bin/sh + +set -e + +tries=0 +max_tries=60 +echo "[$(date)] - Waiting Kuzzle..." +echo "" + +while ! curl -f -s -o /dev/null http://localhost:7512 +do + echo -ne "\r[$(date)] - Still trying to connect to Kuzzle ($tries)" + + ((tries=tries+1)) + + if [ $tries -eq $max_tries ]; then + docker-compose logs + + exit 1 + fi + + sleep 1 +done diff --git a/templates/app-scaffold/ts/lib/controllers/index.ts b/templates/app-scaffold/ts/lib/controllers/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/templates/app-scaffold/ts/lib/models/index.ts b/templates/app-scaffold/ts/lib/models/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/templates/app-scaffold/ts/lib/plugins/index.ts b/templates/app-scaffold/ts/lib/plugins/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/templates/app-scaffold/ts/lib/services/index.ts b/templates/app-scaffold/ts/lib/services/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/templates/app-scaffold/ts/lib/types/index.ts b/templates/app-scaffold/ts/lib/types/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/templates/app-scaffold/ts/package.json b/templates/app-scaffold/ts/package.json index 2d310c9b..8e835500 100644 --- a/templates/app-scaffold/ts/package.json +++ b/templates/app-scaffold/ts/package.json @@ -8,7 +8,7 @@ "install:docker": "npm run npm:docker install", "dev:docker": "docker-compose up", "services": "npx kourou app:start-services", - "dev": "npx nodemon --ext 'js,json,ts' --inspect=0.0.0.0:9229 --exec node -r ts-node/register app.ts", + "dev": "nodemon --ext 'js,json,ts' --inspect=0.0.0.0:9229 --exec node -r ts-node/register app.ts", "test": "npm run test:lint && npm run test:unit && npm run test:functional", "test:unit": "mocha", "test:functional": "cucumber-js --exit --fail-fast", @@ -46,4 +46,4 @@ "lib/**/*.d.ts", "lib/**/*.json" ] -} +} \ No newline at end of file diff --git a/test/commands/app/scaffold.test.ts b/test/commands/app/scaffold.test.ts index fdd5ae7a..ca6176ee 100644 --- a/test/commands/app/scaffold.test.ts +++ b/test/commands/app/scaffold.test.ts @@ -24,6 +24,6 @@ describe('app:scaffold', () => { const packageJson = JSON.parse(fs.readFileSync('blackmesa/package.json', 'utf8')) should(packageJson.name).be.eql('blackmesa') - should(packageJson.dependencies.kuzzle).not.be.undefined() + should(packageJson.dependencies).have.property('kuzzle') }).timeout(500 * 1000) // long timeout because of npm install which is slow af (and specially in Travis) }) From 8a6e0039ed1ce1b2fc123fb33632631e91c9615b Mon Sep 17 00:00:00 2001 From: Adrien Maret Date: Thu, 25 Feb 2021 10:07:56 +0100 Subject: [PATCH 14/15] Add Nodemon (#97) ## What does this PR do? Add missing nodemon package --- templates/app-scaffold/ts/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/app-scaffold/ts/package.json b/templates/app-scaffold/ts/package.json index 8e835500..386389ba 100644 --- a/templates/app-scaffold/ts/package.json +++ b/templates/app-scaffold/ts/package.json @@ -32,6 +32,7 @@ "eslint-loader": "^4.0.2", "mocha": "8.0.1", "mock-require": "^3.0.3", + "nodemon": "^2.0.7", "rewire": "^5.0.0", "should": "13.2.3", "should-sinon": "0.0.6", From e2184ca1763354c4cb56b82cc82a707ae744778e Mon Sep 17 00:00:00 2001 From: Aschen Date: Thu, 25 Feb 2021 13:34:56 +0100 Subject: [PATCH 15/15] Release 0.19.0 --- README.md | 2 +- package-lock.json | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6d63936a..ecff8e6d 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ $ npm install -g kourou $ kourou COMMAND running command... $ kourou (-v|--version|version) -kourou/0.18.0 linux-x64 node-v12.16.3 +kourou/0.19.0 linux-x64 node-v12.16.3 $ kourou --help [COMMAND] USAGE $ kourou COMMAND diff --git a/package-lock.json b/package-lock.json index 7185a533..d88547c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "kourou", - "version": "0.18.0", + "version": "0.19.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 65a3bc39..94f090d5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "kourou", "description": "The CLI that helps you manage your Kuzzle instances", - "version": "0.18.0", + "version": "0.19.0", "author": "The Kuzzle Team ", "bin": { "kourou": "./bin/run"