From b62614fa25245590d2c8efa5895b9c4e8b20d43e Mon Sep 17 00:00:00 2001 From: Tingluo Huang Date: Thu, 19 Sep 2019 22:02:45 -0400 Subject: [PATCH 001/192] add core method to saveState and getState. --- packages/core/__tests__/lib.test.ts | 20 +++++++++++++++---- packages/core/src/core.ts | 30 ++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/packages/core/__tests__/lib.test.ts b/packages/core/__tests__/lib.test.ts index 0bbe43d81b..8124f59634 100644 --- a/packages/core/__tests__/lib.test.ts +++ b/packages/core/__tests__/lib.test.ts @@ -17,7 +17,10 @@ const testEnvVars = { INPUT_MY_INPUT: 'val', INPUT_MISSING: '', 'INPUT_SPECIAL_CHARS_\'\t"\\': '\'\t"\\ response ', - INPUT_MULTIPLE_SPACES_VARIABLE: 'I have multiple spaces' + INPUT_MULTIPLE_SPACES_VARIABLE: 'I have multiple spaces', + + // Save inputs + STATE_state_1: 'state_val' } describe('@actions/core', () => { @@ -93,17 +96,17 @@ describe('@actions/core', () => { }) it('getInput gets required input', () => { - expect(core.getInput('my input', {required: true})).toBe('val') + expect(core.getInput('my input', { required: true })).toBe('val') }) it('getInput throws on missing required input', () => { - expect(() => core.getInput('missing', {required: true})).toThrow( + expect(() => core.getInput('missing', { required: true })).toThrow( 'Input required and not supplied: missing' ) }) it('getInput does not throw on missing non-required input', () => { - expect(core.getInput('missing', {required: false})).toBe('') + expect(core.getInput('missing', { required: false })).toBe('') }) it('getInput is case insensitive', () => { @@ -194,6 +197,15 @@ describe('@actions/core', () => { core.debug('\r\ndebug\n') assertWriteCalls([`::debug::%0D%0Adebug%0A${os.EOL}`]) }) + + it('saveState produces the correct command', () => { + core.saveState('state_1', 'some value') + assertWriteCalls([`::save-state name=state_1,::some value${os.EOL}`]) + }) + + it('getState gets wrapper action state', () => { + expect(core.getState('state_1')).toBe('state_val') + }) }) // Assert that process.stdout.write calls called only with the given arguments. diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index 072f2323e3..4bf44356f5 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -1,4 +1,4 @@ -import {issue, issueCommand} from './command' +import { issue, issueCommand } from './command' import * as os from 'os' import * as path from 'path' @@ -37,7 +37,7 @@ export enum ExitCode { */ export function exportVariable(name: string, val: string): void { process.env[name] = val - issueCommand('set-env', {name}, val) + issueCommand('set-env', { name }, val) } /** @@ -86,7 +86,7 @@ export function getInput(name: string, options?: InputOptions): string { * @param value value to store */ export function setOutput(name: string, value: string): void { - issueCommand('set-output', {name}, value) + issueCommand('set-output', { name }, value) } //----------------------------------------------------------------------- @@ -178,3 +178,27 @@ export async function group(name: string, fn: () => Promise): Promise { return result } + +//----------------------------------------------------------------------- +// Wrapper action state +//----------------------------------------------------------------------- + +/** + * Saves state for current action, the state can only be retrieved by this action's post job execution. + * + * @param name name of the state to store + * @param value value to store + */ +export function saveState(name: string, value: string): void { + issueCommand('save-state', { name }, value) +} + +/** + * Gets the value of an state set by this action's main execution. + * + * @param name name of the state to get + * @returns string + */ +export function getState(name: string): string { + return process.env[`STATE_${name}`] || '' +} From 4d152182528e3e7e0c7448cfc8230dbe25907b3f Mon Sep 17 00:00:00 2001 From: Tingluo Huang Date: Thu, 19 Sep 2019 22:14:12 -0400 Subject: [PATCH 002/192] fix lint. --- packages/core/__tests__/lib.test.ts | 10 +++++----- packages/core/src/core.ts | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/core/__tests__/lib.test.ts b/packages/core/__tests__/lib.test.ts index 8124f59634..316d653d59 100644 --- a/packages/core/__tests__/lib.test.ts +++ b/packages/core/__tests__/lib.test.ts @@ -20,7 +20,7 @@ const testEnvVars = { INPUT_MULTIPLE_SPACES_VARIABLE: 'I have multiple spaces', // Save inputs - STATE_state_1: 'state_val' + STATE_TEST_1: 'state_val' } describe('@actions/core', () => { @@ -96,17 +96,17 @@ describe('@actions/core', () => { }) it('getInput gets required input', () => { - expect(core.getInput('my input', { required: true })).toBe('val') + expect(core.getInput('my input', { required: true})).toBe('val') }) it('getInput throws on missing required input', () => { - expect(() => core.getInput('missing', { required: true })).toThrow( + expect(() => core.getInput('missing', { required: true})).toThrow( 'Input required and not supplied: missing' ) }) it('getInput does not throw on missing non-required input', () => { - expect(core.getInput('missing', { required: false })).toBe('') + expect(core.getInput('missing', { required: false})).toBe('') }) it('getInput is case insensitive', () => { @@ -204,7 +204,7 @@ describe('@actions/core', () => { }) it('getState gets wrapper action state', () => { - expect(core.getState('state_1')).toBe('state_val') + expect(core.getState('TEST_1')).toBe('state_val') }) }) diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index 4bf44356f5..b1a7054ae3 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -1,4 +1,4 @@ -import { issue, issueCommand } from './command' +import { issue, issueCommand} from './command' import * as os from 'os' import * as path from 'path' @@ -37,7 +37,7 @@ export enum ExitCode { */ export function exportVariable(name: string, val: string): void { process.env[name] = val - issueCommand('set-env', { name }, val) + issueCommand('set-env', {name}, val) } /** From 81b71dc6e643b3625eeaa6ca013b8fc5dc3fccbf Mon Sep 17 00:00:00 2001 From: Tingluo Huang Date: Thu, 19 Sep 2019 22:18:51 -0400 Subject: [PATCH 003/192] fix lint. --- packages/core/__tests__/lib.test.ts | 6 +++--- packages/core/src/core.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/__tests__/lib.test.ts b/packages/core/__tests__/lib.test.ts index 316d653d59..364cfd192f 100644 --- a/packages/core/__tests__/lib.test.ts +++ b/packages/core/__tests__/lib.test.ts @@ -96,17 +96,17 @@ describe('@actions/core', () => { }) it('getInput gets required input', () => { - expect(core.getInput('my input', { required: true})).toBe('val') + expect(core.getInput('my input', {required: true})).toBe('val') }) it('getInput throws on missing required input', () => { - expect(() => core.getInput('missing', { required: true})).toThrow( + expect(() => core.getInput('missing', {required: true})).toThrow( 'Input required and not supplied: missing' ) }) it('getInput does not throw on missing non-required input', () => { - expect(core.getInput('missing', { required: false})).toBe('') + expect(core.getInput('missing', {required: false})).toBe('') }) it('getInput is case insensitive', () => { diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index b1a7054ae3..3fee6bb55f 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -1,4 +1,4 @@ -import { issue, issueCommand} from './command' +import {issue, issueCommand} from './command' import * as os from 'os' import * as path from 'path' @@ -86,7 +86,7 @@ export function getInput(name: string, options?: InputOptions): string { * @param value value to store */ export function setOutput(name: string, value: string): void { - issueCommand('set-output', { name }, value) + issueCommand('set-output', {name}, value) } //----------------------------------------------------------------------- @@ -190,7 +190,7 @@ export async function group(name: string, fn: () => Promise): Promise { * @param value value to store */ export function saveState(name: string, value: string): void { - issueCommand('save-state', { name }, value) + issueCommand('save-state', {name}, value) } /** From 05b1b08f77edc466ca9b6e97a15952a07f149bfa Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Tue, 1 Oct 2019 12:56:09 -0400 Subject: [PATCH 004/192] Update command docs to specify a new line is needed (#171) --- docs/commands.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index d928abd6fa..6fa657f763 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -4,7 +4,7 @@ The [core toolkit package](https://github.com/actions/toolkit/tree/master/packag setting results, logging, registering secrets and exporting variables across actions. Sometimes, however, its useful to be able to do these things in a script or other tool. -To allow this, we provide a special `::` syntax which, if logged to `stdout`, will allow the runner to perform special behavior on +To allow this, we provide a special `::` syntax which, if logged to `stdout` on a new line, will allow the runner to perform special behavior on your commands. The following commands are all supported: ### Set an environment variable @@ -107,4 +107,4 @@ Finally, there are several commands to emit different levels of log output: |---|---| | [debug](https://github.com/actions/toolkit/blob/master/docs/action-debugging.md) | `echo ::debug::My debug message` | | warning | `echo ::warning::My warning message` | -| error | `echo ::error::My error message` | \ No newline at end of file +| error | `echo ::error::My error message` | From 713902387ec57f76f4fdbef8f1f179a5ef3c2049 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Tue, 1 Oct 2019 13:53:09 -0400 Subject: [PATCH 005/192] updating core docs and bumping version (#172) updating core docs and bumping version --- packages/core/README.md | 36 ++++++++++++++++++----------- packages/core/RELEASES.md | 5 +++- packages/core/__tests__/lib.test.ts | 5 ---- packages/core/package.json | 2 +- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index 58a8287fa4..860dce3d92 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -4,45 +4,51 @@ ## Usage -#### Inputs/Outputs - -You can use this library to get inputs or set outputs: +### Import the package ```js +// javascript const core = require('@actions/core'); -const myInput = core.getInput('inputName', { required: true }); +// typescript +import * as core from '@actions/core'; +``` -// Do stuff +#### Inputs/Outputs + +Action inputs can be read with `getInput`. Outputs can be set with `setOutput` which makes them available to be mapped into inputs of other actions to ensure they are decoupled. + +```js +const myInput = core.getInput('inputName', { required: true }); core.setOutput('outputKey', 'outputVal'); ``` #### Exporting variables -You can also export variables for future steps. Variables get set in the environment. +Since each step runs in a separate process, you can use `exportVariable` to add it to this step and future steps environment blocks. ```js -const core = require('@actions/core'); +core.exportVariable('envVar', 'Val'); +``` -// Do stuff +Exporting a secret exports the variable but also registers the secret with the runner to ensure it is masked in logs. -core.exportVariable('envVar', 'Val'); +```js +core.exportSecret('myPassword', mypass); ``` #### PATH Manipulation -You can explicitly add items to the path for all remaining steps in a workflow: +To make a tool's path available in the path for the remainder of the job (without altering the machine or containers state), use `addPath`. The runner will prepend the path given to the jobs PATH. ```js -const core = require('@actions/core'); - -core.addPath('pathToTool'); +core.addPath('/path/to/mytool'); ``` #### Exit codes -You should use this library to set the failing exit code for your action: +You should use this library to set the failing exit code for your action. If status is not set and the script runs to completion, that will lead to a success. ```js const core = require('@actions/core'); @@ -55,6 +61,8 @@ catch (err) { core.setFailed(`Action failed with error ${err}`); } +Note that `setNeutral` is not yet implemented in actions V2 but equivalent functionality is being planned. + ``` #### Logging diff --git a/packages/core/RELEASES.md b/packages/core/RELEASES.md index 8a0bf4260a..d677f69a53 100644 --- a/packages/core/RELEASES.md +++ b/packages/core/RELEASES.md @@ -1,8 +1,11 @@ # @actions/core Releases -### 1.1.1 +### 1.1.2 - set-secret is now available for use [#141](https://github.com/actions/toolkit/issues/141) + +### 1.1.1 + - Add support for action input variables with multiple spaces [#127](https://github.com/actions/toolkit/issues/127) - Switched ## commands to :: commands (should have no noticeable impact) [#110)(https://github.com/actions/toolkit/pull/110) diff --git a/packages/core/__tests__/lib.test.ts b/packages/core/__tests__/lib.test.ts index 0bbe43d81b..75bb04c7b3 100644 --- a/packages/core/__tests__/lib.test.ts +++ b/packages/core/__tests__/lib.test.ts @@ -125,11 +125,6 @@ describe('@actions/core', () => { assertWriteCalls([`::set-output name=some output,::some value${os.EOL}`]) }) - it('setNeutral sets the correct exit code', () => { - core.setFailed('Failure message') - expect(process.exitCode).toBe(core.ExitCode.Failure) - }) - it('setFailure sets the correct exit code and failure message', () => { core.setFailed('Failure message') expect(process.exitCode).toBe(core.ExitCode.Failure) diff --git a/packages/core/package.json b/packages/core/package.json index 151fbee45c..bad955bf2b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@actions/core", - "version": "1.1.1", + "version": "1.1.2", "description": "Actions core lib", "keywords": [ "github", From 9d54cd22eadbea3ad08d42a7cd0d9f998ac41034 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Tue, 1 Oct 2019 17:13:05 -0400 Subject: [PATCH 006/192] setSecret (#174) * setSecret --- docs/commands.md | 9 ++++---- packages/core/README.md | 6 ++++-- packages/core/RELEASES.md | 5 +++-- packages/core/__tests__/lib.test.ts | 32 ++++------------------------- packages/core/package.json | 2 +- packages/core/src/core.ts | 15 +++++--------- 6 files changed, 22 insertions(+), 47 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index 6fa657f763..5af0787ec2 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -67,15 +67,16 @@ export function setOutput(name: string, value: string): void {} If a script or action does work to create a secret at runtime, it can be registered with the runner to be masked in logs. -To mask a value in the logs, use `::set-secret`: +To mask a value in the logs, use `::add-mask`: ```sh -echo ::set-secret::BAR +echo ::add-mask::mysecretvalue ``` -This is wrapped by the core method which both sets the value as a variable for future steps and registers the secret to mask +This is wrapped by the core setSecret method + ```javascript -function exportSecret(name: string, val: string): void {} +function setSecret(secret: string): void {} ``` Now, future logs containing BAR will be masked. E.g. running `echo "Hello FOO BAR World"` will now print `Hello FOO **** World`. diff --git a/packages/core/README.md b/packages/core/README.md index 860dce3d92..026bb52027 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -32,10 +32,12 @@ Since each step runs in a separate process, you can use `exportVariable` to add core.exportVariable('envVar', 'Val'); ``` -Exporting a secret exports the variable but also registers the secret with the runner to ensure it is masked in logs. +#### Setting a secret + +Setting a secret registers the secret with the runner to ensure it is masked in logs. ```js -core.exportSecret('myPassword', mypass); +core.setSecret('myPassword'); ``` #### PATH Manipulation diff --git a/packages/core/RELEASES.md b/packages/core/RELEASES.md index d677f69a53..48f58a2b7b 100644 --- a/packages/core/RELEASES.md +++ b/packages/core/RELEASES.md @@ -1,8 +1,9 @@ # @actions/core Releases -### 1.1.2 +### 1.1.3 -- set-secret is now available for use [#141](https://github.com/actions/toolkit/issues/141) +- setSecret added to register a secret with the runner to be masked from the logs +- exportSecret which was not implemented and never worked was removed after clarification from product. ### 1.1.1 diff --git a/packages/core/__tests__/lib.test.ts b/packages/core/__tests__/lib.test.ts index 75bb04c7b3..838f23b834 100644 --- a/packages/core/__tests__/lib.test.ts +++ b/packages/core/__tests__/lib.test.ts @@ -51,34 +51,10 @@ describe('@actions/core', () => { assertWriteCalls([`::set-env name=my var2,::var val%0D%0A${os.EOL}`]) }) - // it('exportSecret produces the correct commands and sets the env', () => { - // core.exportSecret('my secret', 'secret val') - // expect(process.env['my secret']).toBe('secret val') - // assertWriteCalls([ - // `::set-env name=my secret,::secret val${os.EOL}`, - // `::set-secret]secret val${os.EOL}` - // ]) - // }) - - // it('exportSecret escapes secret names', () => { - // core.exportSecret('special char secret \r\n];', 'special secret val') - // expect(process.env['special char secret \r\n];']).toBe('special secret val') - // assertWriteCalls([ - // `::set-env name=special char secret %0D%0A%5D%3B,::special secret val${ - // os.EOL - // }`, - // `::set-secret]special secret val${os.EOL}` - // ]) - // }) - - // it('exportSecret escapes secret values', () => { - // core.exportSecret('my secret2', 'secret val\r\n') - // expect(process.env['my secret2']).toBe('secret val\r\n') - // assertWriteCalls([ - // `::set-env name=my secret2,::secret val%0D%0A${os.EOL}`, - // `::set-secret]secret val%0D%0A${os.EOL}` - // ]) - // }) + it('setSecret produces the correct command', () => { + core.setSecret('secret val') + assertWriteCalls([`::add-mask::secret val${os.EOL}`]) + }) it('prependPath produces the correct commands and sets the env', () => { core.addPath('myPath') diff --git a/packages/core/package.json b/packages/core/package.json index bad955bf2b..a08bded869 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@actions/core", - "version": "1.1.2", + "version": "1.1.3", "description": "Actions core lib", "keywords": [ "github", diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index 072f2323e3..000cdd7a60 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -31,7 +31,7 @@ export enum ExitCode { //----------------------------------------------------------------------- /** - * sets env variable for this action and future actions in the job + * Sets env variable for this action and future actions in the job * @param name the name of the variable to set * @param val the value of the variable */ @@ -41,16 +41,11 @@ export function exportVariable(name: string, val: string): void { } /** - * exports the variable and registers a secret which will get masked from logs - * @param name the name of the variable to set - * @param val value of the secret + * Registers a secret which will get masked from logs + * @param secret value of the secret */ -export function exportSecret(name: string, val: string): void { - exportVariable(name, val) - - // the runner will error with not implemented - // leaving the function but raising the error earlier - issueCommand('set-secret', {}, val) +export function setSecret(secret: string): void { + issueCommand('add-mask', {}, secret) } /** From 531da1858f3d41324a008be090532a34368d2628 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Wed, 2 Oct 2019 08:18:38 -0400 Subject: [PATCH 007/192] fix test timeout (#176) * fix test timeout --- .github/workflows/workflow.yml | 2 +- README.md | 2 +- packages/exec/__tests__/exec.test.ts | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 4d05ed6410..e13e5676b2 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -1,4 +1,4 @@ -name: Main workflow +name: toolkit-unit-tests on: [push] jobs: Ubuntu: diff --git a/README.md b/README.md index 3fd0d5a965..ddd07b3642 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

- GitHub Actions status + GitHub Actions status

## GitHub Actions Toolkit diff --git a/packages/exec/__tests__/exec.test.ts b/packages/exec/__tests__/exec.test.ts index 680d182165..e0b696413f 100644 --- a/packages/exec/__tests__/exec.test.ts +++ b/packages/exec/__tests__/exec.test.ts @@ -255,6 +255,8 @@ describe('@actions/exec', () => { }) it('Handles child process holding streams open', async function() { + // this was timing out on some slower hosted macOS runs at default 5s + jest.setTimeout(10000) const semaphorePath = path.join( getTestTemp(), 'child-process-semaphore.txt' @@ -301,6 +303,8 @@ describe('@actions/exec', () => { }) it('Handles child process holding streams open and non-zero exit code', async function() { + // this was timing out on some slower hosted macOS runs at default 5s + jest.setTimeout(10000) const semaphorePath = path.join( getTestTemp(), 'child-process-semaphore.txt' From f210cdb256e8cda34d956e67ca20288f17b3349b Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Wed, 2 Oct 2019 17:59:33 -0400 Subject: [PATCH 008/192] Update readme (#178) Updating readme --- README.md | 147 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 130 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ddd07b3642..628224c17a 100644 --- a/README.md +++ b/README.md @@ -9,37 +9,150 @@ ## GitHub Actions Toolkit -The GitHub Actions ToolKit provides a set of packages to make creating actions easier and drive consistency. +The GitHub Actions ToolKit provides a set of packages to make creating actions easier. ## Packages -The toolkit provides five separate packages. See the docs for each action. +:heavy_check_mark: [@actions/core](packages/core) -| Package | Description | -| ------- | ----------- | -| [@actions/core](packages/core) | Core functions for getting inputs, setting outputs, setting results, logging, secrets and environment variables | -| [@actions/exec](packages/exec) | Functions necessary for running tools on the command line | -| [@actions/io](packages/io) | Core functions for CLI filesystem scenarios | -| [@actions/tool-cache](packages/tool-cache) | Functions necessary for downloading and caching tools | -| [@actions/github](packages/github) | An Octokit client hydrated with the context that the current action is being run in | +Provides functions for inputs, outputs, results, logging, secrets and variables. Read more [here](packages/core) + +```bash +$ npm install @actions/core --save +``` +
+ +:runner: [@actions/exec](packages/exec) + +Provides functions to exec cli tools and process output. Read more [here](packages/exec) + +```bash +$ npm install @actions/exec --save +``` +
+ +:pencil2: [@actions/io](packages/io) + +Provides disk i/o functions like cp, mv, rmRF, find etc. Read more [here](packages/io) + +```bash +$ npm install @actions/io --save +``` +
+ +:hammer: [@actions/tool-cache](packages/tool-cache) + +Provides functions for downloading and caching tools. e.g. setup-* actions. Read more [here](packages/tool-cache) + +```bash +$ npm install @actions/tool-cache --save +``` +
+ +:octocat: [@actions/github](packages/github) + +Provides an Octokit client hydrated with the context that the current action is being run in. Read more [here](packages/github) + +```bash +$ npm install @actions/github --save +``` +
## Creating an Action with the Toolkit -Actions run in a container or on the host machine. +:question: [Choosing an action type](docs/action-types.md) + +Outlines the differences and why you would want to create a JavaScript or a container based action. +
+
+ +[Hello World JavaScript Action](https://github.com/actions/hello-world-javascript-action) + +Illustrates how to create a simple hello world javascript action. + +```javascript +... + const nameToGreet = core.getInput('who-to-greet'); + console.log(`Hello ${nameToGreet}!`); +... +``` +
+
+ +[JavaScript Action Walkthrough](https://github.com/actions/javascript-action) + + Walkthrough and template for creating a JavaScript Action with tests, linting, workflow, publishing, and versioning. + + ```javascript +PASS ./index.test.js + ✓ throws invalid number + ✓ wait 500 ms + ✓ test runs + +Test Suites: 1 passed, 1 total +Tests: 3 passed, 3 total + ``` +
+
+ +[TypeScript Action Walkthrough](https://github.com/actions/typescript-action) + +Walkthrough creating a TypeScript Action with compilation, tests, linting, workflow, publishing, and versioning. + +```javascript +import * as core from '@actions/core'; + +async function run() { + try { + const ms = core.getInput('milliseconds'); + console.log(`Waiting ${ms} milliseconds ...`) + ... + + } catch (error) { + core.setFailed(error.message); + } +} + +run(); +``` +
+
+ +[Docker Action Walkthrough](docs/container-action.md) + +Create an action that is delivered as a container and run with docker. + +```docker +FROM alpine:3.10 + +COPY LICENSE README.md / + +COPY entrypoint.sh /entrypoint.sh -[Choosing an action type](docs/action-types.md): Outlines the differences and why you would want to create a JavaScript or a container based action. +ENTRYPOINT ["/entrypoint.sh"] +``` +
+
-[Hello World JavaScript Action](https://github.com/actions/hello-world-javascript-action): Illustrates how to create a simple hello world javascript action. +[Docker Action Walkthrough with Octokit](https://github.com/actions/container-toolkit-action) -[JavaScript Action Walkthrough](https://github.com/actions/javascript-action): Walkthrough creating a JavaScript Action with tests, linting, workflow, publishing, and versioning. +Create an action that is delivered as a container which uses the toolkit. This example uses the GitHub context to construct an Octokit client. -[TypeScript Action Walkthrough](https://github.com/actions/typescript-action): Walkthrough creating a TypeScript Action with compilation, tests, linting, workflow, publishing, and versioning. +```javascript + const myInput = core.getInput('myInput'); + core.debug(`Hello ${myInput} from inside a container`); -[Docker Action Walkthrough](docs/container-action.md): Create an action that is delivered as a container and run with docker. + const context = github.context; + console.log(`We can even get context data, like the repo: ${context.repo.repo}`) +``` +
+
-[Docker Action Walkthrough with Octokit](docs/container-action-toolkit.md): Create an action that is delivered as a container which uses the toolkit. This example uses the GitHub context to construct an Octokit client. +:curly_loop: [Versioning](docs/action-versioning.md) -[Versioning](docs/action-versioning.md): Recommendations on versioning, releases and tagging your action. +Recommendations on versioning, releases and tagging your action. +
+
## Contributing From a1c30dfc53ddf86e4b82fdbec0812257d9ad96e6 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 3 Oct 2019 00:17:33 +0200 Subject: [PATCH 009/192] Add a bug report issue template (#160) Provide an issue template that will help people locate the GitHub Community forum for GitHub Actions. --- .github/ISSUE_TEMPLATE/bug_report.md | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..d205077e3d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,36 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + + + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Code Snippet** +If applicable, add a code snippet to help explain your problem. + +**Additional information** +Add any other context about the problem here. From 5ce4932391d819ac86818818d7865130480c40f1 Mon Sep 17 00:00:00 2001 From: Tingluo Huang Date: Thu, 3 Oct 2019 00:41:30 -0400 Subject: [PATCH 010/192] update doc. --- packages/core/README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/core/README.md b/packages/core/README.md index 58a8287fa4..46e405f288 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -94,4 +94,37 @@ const result = await core.group('Do something async', async () => { const response = await doSomeHTTPRequest() return response }) +``` + +#### Action state + +You can use this library to save state and get state for sharing information between a given wrapper action: + +**action.yml** +```yaml +name: 'Wrapper action sample' +inputs: + name: + default: 'GitHub' +runs: + using: 'node12' + main: 'main.js' + post: 'cleanup.js' +``` + +In action's `main.js`: + +```js +const core = require('@actions/core'); + +core.saveState("pidToKill", 12345); +``` + +In action's `cleanup.js`: +```js +const core = require('@actions/core'); + +var pid = core.getState("pidToKill"); + +kill(pid); ``` \ No newline at end of file From 1643ea2734516db5d2fe78423234cd638d5bdbb8 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Thu, 3 Oct 2019 12:45:11 -0400 Subject: [PATCH 011/192] update readme --- .../{workflow.yml => unit-tests.yml} | 6 +- README.md | 55 +++++++++++++------ 2 files changed, 43 insertions(+), 18 deletions(-) rename .github/workflows/{workflow.yml => unit-tests.yml} (96%) diff --git a/.github/workflows/workflow.yml b/.github/workflows/unit-tests.yml similarity index 96% rename from .github/workflows/workflow.yml rename to .github/workflows/unit-tests.yml index e13e5676b2..9890a5d3fb 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/unit-tests.yml @@ -1,5 +1,9 @@ name: toolkit-unit-tests -on: [push] +on: + push: + paths: + - 'packages/**' + - '*.json' jobs: Ubuntu: name: Run Ubuntu diff --git a/README.md b/README.md index 628224c17a..7fe339d2f1 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,15 @@ GitHub Actions status

+ ## GitHub Actions Toolkit The GitHub Actions ToolKit provides a set of packages to make creating actions easier. +
+

Get started with the javascript-action template!

+
+ ## Packages :heavy_check_mark: [@actions/core](packages/core) @@ -66,6 +71,12 @@ Outlines the differences and why you would want to create a JavaScript or a cont

+:curly_loop: [Versioning](docs/action-versioning.md) + +Actions are downloaded and run from the GitHub graph of repos. This contains guidance for versioning actions and safe releases. +
+
+ [Hello World JavaScript Action](https://github.com/actions/hello-world-javascript-action) Illustrates how to create a simple hello world javascript action. @@ -83,7 +94,14 @@ Illustrates how to create a simple hello world javascript action. Walkthrough and template for creating a JavaScript Action with tests, linting, workflow, publishing, and versioning. - ```javascript +```javascript +async function run() { + try { + const ms = core.getInput('milliseconds'); + console.log(`Waiting ${ms} milliseconds ...`) + ... +``` +```javascript PASS ./index.test.js ✓ throws invalid number ✓ wait 500 ms @@ -91,7 +109,7 @@ PASS ./index.test.js Test Suites: 1 passed, 1 total Tests: 3 passed, 3 total - ``` +```

@@ -107,13 +125,15 @@ async function run() { const ms = core.getInput('milliseconds'); console.log(`Waiting ${ms} milliseconds ...`) ... +``` +```javascript +PASS ./index.test.js + ✓ throws invalid number + ✓ wait 500 ms + ✓ test runs - } catch (error) { - core.setFailed(error.message); - } -} - -run(); +Test Suites: 1 passed, 1 total +Tests: 3 passed, 3 total ```

@@ -124,11 +144,8 @@ Create an action that is delivered as a container and run with docker. ```docker FROM alpine:3.10 - COPY LICENSE README.md / - COPY entrypoint.sh /entrypoint.sh - ENTRYPOINT ["/entrypoint.sh"] ```
@@ -138,18 +155,22 @@ ENTRYPOINT ["/entrypoint.sh"] Create an action that is delivered as a container which uses the toolkit. This example uses the GitHub context to construct an Octokit client. +```docker +FROM node:slim +COPY . . +RUN npm install --production +ENTRYPOINT ["node", "/lib/main.js"] +``` ```javascript - const myInput = core.getInput('myInput'); - core.debug(`Hello ${myInput} from inside a container`); +const myInput = core.getInput('myInput'); +core.debug(`Hello ${myInput} from inside a container`); - const context = github.context; - console.log(`We can even get context data, like the repo: ${context.repo.repo}`) +const context = github.context; +console.log(`We can even get context data, like the repo: ${context.repo.repo}`) ```

-:curly_loop: [Versioning](docs/action-versioning.md) - Recommendations on versioning, releases and tagging your action.

From b2151226b632760193deead38cfb1e34f4a81a3e Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Thu, 3 Oct 2019 13:07:22 -0400 Subject: [PATCH 012/192] update workflow paths --- .github/workflows/unit-tests.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 9890a5d3fb..6b4fd36f23 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -1,9 +1,11 @@ name: toolkit-unit-tests on: push: - paths: - - 'packages/**' - - '*.json' + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' jobs: Ubuntu: name: Run Ubuntu From 7b46e3ab3407697931a878fe0baf57a15de8a977 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Thu, 3 Oct 2019 13:51:11 -0400 Subject: [PATCH 013/192] update readme --- README.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7fe339d2f1..8e29b58b9b 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ Actions are downloaded and run from the GitHub graph of repos. This contains gu

-[Hello World JavaScript Action](https://github.com/actions/hello-world-javascript-action) +

Hello World JavaScript Action

Illustrates how to create a simple hello world javascript action. @@ -88,11 +88,10 @@ Illustrates how to create a simple hello world javascript action. ... ```
-
-[JavaScript Action Walkthrough](https://github.com/actions/javascript-action) +

JavaScript Action Walkthrough

- Walkthrough and template for creating a JavaScript Action with tests, linting, workflow, publishing, and versioning. +Walkthrough and template for creating a JavaScript Action with tests, linting, workflow, publishing, and versioning. ```javascript async function run() { @@ -111,9 +110,8 @@ Test Suites: 1 passed, 1 total Tests: 3 passed, 3 total ```
-
-[TypeScript Action Walkthrough](https://github.com/actions/typescript-action) +

TypeScript Action Walkthrough

Walkthrough creating a TypeScript Action with compilation, tests, linting, workflow, publishing, and versioning. @@ -138,7 +136,7 @@ Tests: 3 passed, 3 total

-[Docker Action Walkthrough](docs/container-action.md) +

Docker Action Walkthrough

Create an action that is delivered as a container and run with docker. @@ -149,9 +147,8 @@ COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] ```
-
-[Docker Action Walkthrough with Octokit](https://github.com/actions/container-toolkit-action) +

Docker Action Walkthrough with Octokit

Create an action that is delivered as a container which uses the toolkit. This example uses the GitHub context to construct an Octokit client. @@ -169,7 +166,6 @@ const context = github.context; console.log(`We can even get context data, like the repo: ${context.repo.repo}`) ```
-
Recommendations on versioning, releases and tagging your action.
From ae706665a165e8345412b4da5e456eda090fb39e Mon Sep 17 00:00:00 2001 From: Tingluo Huang Date: Thu, 3 Oct 2019 14:48:21 -0400 Subject: [PATCH 014/192] PR feedback. --- packages/core/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/README.md b/packages/core/README.md index 46e405f288..d34b950822 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -126,5 +126,5 @@ const core = require('@actions/core'); var pid = core.getState("pidToKill"); -kill(pid); +process.kill(pid); ``` \ No newline at end of file From 2e4712de6f9e7ac27580f7b8eefa57bd5c6e2038 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Wed, 9 Oct 2019 08:47:27 -0400 Subject: [PATCH 015/192] updating readmes --- .github/ISSUE_TEMPLATE/bug_report.md | 24 ++---- .github/ISSUE_TEMPLATE/enhancement_request.md | 16 ++++ CODE_OF_CONDUCT.md | 76 +++++++++++++++++++ README.md | 4 + SECURITY.md | 3 + 5 files changed, 104 insertions(+), 19 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/enhancement_request.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 SECURITY.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d205077e3d..e160702cb6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,24 +1,10 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' +Thank you 🙇‍♀ for wanting to create an issue in this repository. Before you do, please ensure you are filing the issue in the right place. Issues should only be opened on if the issue **relates to code in this repository**. ---- +* If you have found a security issue [please submit it here](https://hackerone.com/github) +* If you have questions about writing workflows or action files, then please [visit the GitHub Community Forum's Actions Board](https://github.community/t5/GitHub-Actions/bd-p/actions) +* If you are having an issue or question about GitHub Actions then please [contact customer support](https://help.github.com/en/articles/about-github-actions#contacting-support) - +If your issue is relevant to this repository, please include the information below: **Describe the bug** A clear and concise description of what the bug is. diff --git a/.github/ISSUE_TEMPLATE/enhancement_request.md b/.github/ISSUE_TEMPLATE/enhancement_request.md new file mode 100644 index 0000000000..99ad513425 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement_request.md @@ -0,0 +1,16 @@ +Thank you 🙇‍♀ for wanting to create an issue in this repository. Before you do, please ensure you are filing the issue in the right place. Issues should only be opened on if the issue **relates to code in this repository**. + +* If you have found a security issue [please submit it here](https://hackerone.com/github) +* If you have questions about writing workflows or action files, then please [visit the GitHub Community Forum's Actions Board](https://github.community/t5/GitHub-Actions/bd-p/actions) +* If you are having an issue or question about GitHub Actions then please [contact customer support](https://help.github.com/en/articles/about-github-actions#contacting-support) + +If your issue is relevant to this repository, please include the information below: + +**Describe the enhancement** +A clear and concise description of what the features or enhancment you need. + +**Code Snippet** +If applicable, add a code snippet to show the api enhancement. + +**Additional information** +Add any other context about the feature here. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..517657b8fd --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to make participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all project spaces, and it also applies when +an individual is representing the project or its community in public spaces. +Examples of representing a project or community include using an official +project e-mail address, posting via an official social media account, or acting +as an appointed representative at an online or offline event. Representation of +a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at opensource@github.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq \ No newline at end of file diff --git a/README.md b/README.md index 8e29b58b9b..0aa94f9e2d 100644 --- a/README.md +++ b/README.md @@ -174,3 +174,7 @@ Recommendations on versioning, releases and tagging your action. ## Contributing We welcome contributions. See [how to contribute](docs/contribute.md). + +## Code of Conduct + +See [our code of conduct](CODE_OF_CONDUCT.md). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..fc3f6d19ad --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,3 @@ +If you discover a security issue in this repo, please submit it through the [GitHub Security Bug Bounty](https://hackerone.com/github) + +Thanks for helping make GitHub Actions safe for everyone. \ No newline at end of file From c2bb007435fb3737fa78d05fb1d8fc2e45b393c7 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Wed, 9 Oct 2019 09:09:24 -0400 Subject: [PATCH 016/192] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 36 ++++++++++++++++------- .github/ISSUE_TEMPLATE/feature_request.md | 20 +++++++++++++ 2 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index e160702cb6..dd84ea7824 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,22 +1,38 @@ -Thank you 🙇‍♀ for wanting to create an issue in this repository. Before you do, please ensure you are filing the issue in the right place. Issues should only be opened on if the issue **relates to code in this repository**. +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' -* If you have found a security issue [please submit it here](https://hackerone.com/github) -* If you have questions about writing workflows or action files, then please [visit the GitHub Community Forum's Actions Board](https://github.community/t5/GitHub-Actions/bd-p/actions) -* If you are having an issue or question about GitHub Actions then please [contact customer support](https://help.github.com/en/articles/about-github-actions#contacting-support) - -If your issue is relevant to this repository, please include the information below: +--- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** -Steps to reproduce the behavior. +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error **Expected behavior** A clear and concise description of what you expected to happen. -**Code Snippet** -If applicable, add a code snippet to help explain your problem. +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] -**Additional information** +**Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..bbcbbe7d61 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From e984b2b6bbb0b9a9b0ba41733a50e4636274fa93 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Wed, 9 Oct 2019 09:16:07 -0400 Subject: [PATCH 017/192] updating readmes --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/ISSUE_TEMPLATE/enhancement_request.md | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7824..81c7b58d88 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,7 @@ name: Bug report about: Create a report to help us improve title: '' -labels: '' +labels: 'bug' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/enhancement_request.md b/.github/ISSUE_TEMPLATE/enhancement_request.md index 99ad513425..070045c60f 100644 --- a/.github/ISSUE_TEMPLATE/enhancement_request.md +++ b/.github/ISSUE_TEMPLATE/enhancement_request.md @@ -1,3 +1,12 @@ +--- +name: Feature Request +about: Create a request to help us improve +title: '' +labels: 'enhancement' +assignees: '' + +--- + Thank you 🙇‍♀ for wanting to create an issue in this repository. Before you do, please ensure you are filing the issue in the right place. Issues should only be opened on if the issue **relates to code in this repository**. * If you have found a security issue [please submit it here](https://hackerone.com/github) From 9c0a43bda4b90fe1f00619708bb6ea52c0cf0a5d Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Wed, 9 Oct 2019 09:17:19 -0400 Subject: [PATCH 018/192] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/ISSUE_TEMPLATE/enhancement_request.md | 2 +- .github/ISSUE_TEMPLATE/feature_request.md | 20 ------------------- 3 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 81c7b58d88..f3d5c415e0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,7 @@ name: Bug report about: Create a report to help us improve title: '' -labels: 'bug' +labels: bug assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/enhancement_request.md b/.github/ISSUE_TEMPLATE/enhancement_request.md index 070045c60f..f663965e18 100644 --- a/.github/ISSUE_TEMPLATE/enhancement_request.md +++ b/.github/ISSUE_TEMPLATE/enhancement_request.md @@ -2,7 +2,7 @@ name: Feature Request about: Create a request to help us improve title: '' -labels: 'enhancement' +labels: enhancement assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index bbcbbe7d61..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. From 747fa4805ab9e0ba4ad4e0db995286b4ad639c8b Mon Sep 17 00:00:00 2001 From: Danny Guo Date: Wed, 2 Oct 2019 19:26:15 -0400 Subject: [PATCH 019/192] Fix a setup-node warning setup-node currently outputs: ##[warning]Input 'version' has been deprecated with message: The version property will not be supported after October 1, 2019. Use node-version instead --- .github/workflows/unit-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 6b4fd36f23..ac66f9ac56 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -17,7 +17,7 @@ jobs: - name: Set Node.js 10.x uses: actions/setup-node@master with: - version: 10.x + node-version: 10.x - name: npm install run: npm install @@ -46,7 +46,7 @@ jobs: - name: Set Node.js 10.x uses: actions/setup-node@master with: - version: 10.x + node-version: 10.x - name: npm install run: npm install @@ -69,7 +69,7 @@ jobs: - name: Set Node.js 10.x uses: actions/setup-node@master with: - version: 10.x + node-version: 10.x - name: npm install run: npm install From 565d0bbe1857d083f727b9b0ef2a583260c23fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Mu=C3=B1oz=20Solera?= Date: Mon, 14 Oct 2019 07:59:46 -0700 Subject: [PATCH 020/192] Adding missing curly Brace in Usage example (#150) --- packages/github/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/github/README.md b/packages/github/README.md index fc108a234d..8f2cbe39b8 100644 --- a/packages/github/README.md +++ b/packages/github/README.md @@ -13,7 +13,7 @@ const core = require('@actions/core'); async function run() { // This should be a token with access to your repository scoped in as a secret. // The YML workflow will need to set myToken with the GitHub Secret Token - // myToken: ${{ secrets.GITHUB_TOKEN } + // myToken: ${{ secrets.GITHUB_TOKEN }} // https://help.github.com/en/articles/virtual-environments-for-github-actions#github_token-secret const myToken = core.getInput('myToken'); From a65441cf46f2c230107dcdd6137795e0a4c45a7d Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Fri, 18 Oct 2019 15:35:13 -0400 Subject: [PATCH 021/192] bump core for release and docs (#189) --- docs/commands.md | 8 ++++++++ packages/core/RELEASES.md | 4 ++++ packages/core/package.json | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/commands.md b/docs/commands.md index 5af0787ec2..c2afff9a69 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -100,6 +100,14 @@ function startGroup(name: string): void {} function endGroup(): void {} ``` +### Save State + +Save state to be used in the corresponding wrapper (finally) post job entry point. + +```bash +echo ::save-state name=FOO::foovalue +``` + ### Log Level Finally, there are several commands to emit different levels of log output: diff --git a/packages/core/RELEASES.md b/packages/core/RELEASES.md index 48f58a2b7b..30c8e2059a 100644 --- a/packages/core/RELEASES.md +++ b/packages/core/RELEASES.md @@ -1,5 +1,9 @@ # @actions/core Releases +### 1.2.0 + +- saveState and getState functions for wrapper tasks (on finally entry points that run post job) + ### 1.1.3 - setSecret added to register a secret with the runner to be masked from the logs diff --git a/packages/core/package.json b/packages/core/package.json index a08bded869..b66c5baec3 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@actions/core", - "version": "1.1.3", + "version": "1.2.0", "description": "Actions core lib", "keywords": [ "github", From 3d556ddb81195b27f922ad98d572b00ee7e3f948 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Mon, 21 Oct 2019 16:14:05 -0400 Subject: [PATCH 022/192] Overwrite tag rather then delete and create (#190) --- docs/action-versioning.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/action-versioning.md b/docs/action-versioning.md index 4e8a6c65ff..bb29e6af44 100644 --- a/docs/action-versioning.md +++ b/docs/action-versioning.md @@ -33,9 +33,8 @@ git push origin releases/v1 3. **When ready for a stable release, add a major version tag**: Move the major version tag (v1, v2, etc.) to point to the ref of the current release. This will act as the stable release for that major version. You should keep this tag updated to the most recent stable minor/patch release. ``` git checkout releases/v1 -git push origin :refs/tags/v1 git tag -fa v1 -m "Update v1 tag" -git push origin v1 +git push origin v1 --force ``` 4. **Create releases for minor and patch version updates**: From the GitHub UI create a release for each minor or patch version update titled with that release version (e.g. v1.2.3). 5. **Compatibility Breaks**: introduce a new major version branch (releases/v2) and tag (v2) if changes will break existing workflows. For example, changing inputs. From 4a3fe0bcd3ac34f58b226a326e6235a6fbf2fee0 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Wed, 23 Oct 2019 11:06:34 -0400 Subject: [PATCH 023/192] Quote the Commands in order to process on default windows (#191) --- docs/commands.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index c2afff9a69..f2c02bfc3a 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -12,7 +12,7 @@ your commands. The following commands are all supported: To set an environment variable for future out of process steps, use `::set-env`: ```sh -echo ::set-env name=FOO::BAR +echo "::set-env name=FOO::BAR" ``` Running `$FOO` in a future step will now return `BAR` @@ -28,7 +28,7 @@ export function exportVariable(name: string, val: string): void {} To prepend a string to PATH, use `::addPath`: ```sh -echo ::add-path::BAR +echo "::add-path::BAR" ``` Running `$PATH` in a future step will now return `BAR:{Previous Path}`; @@ -43,7 +43,7 @@ export function addPath(inputPath: string): void {} To set an output for the step, use `::set-output`: ```sh -echo ::set-output name=FOO::BAR +echo "::set-output name=FOO::BAR" ``` Running `steps.[step-id].outputs.FOO` in your Yaml will now give you `BAR` @@ -52,7 +52,7 @@ Running `steps.[step-id].outputs.FOO` in your Yaml will now give you `BAR` steps: - name: Set the value id: step_one - run: echo ::set-output name=FOO::BAR + run: echo "::set-output name=FOO::BAR" - name: Use it run: echo ${{ steps.step_one.outputs.FOO }} ``` @@ -70,7 +70,7 @@ If a script or action does work to create a secret at runtime, it can be registe To mask a value in the logs, use `::add-mask`: ```sh -echo ::add-mask::mysecretvalue +echo "::add-mask::mysecretvalue" ``` This is wrapped by the core setSecret method @@ -89,8 +89,8 @@ For example, if you mask the letter `l`, running `echo "Hello FOO BAR World"` wi Emitting a group with a title will instruct the logs to create a collapsable region up to the next ungroup command. ```bash -echo ::group::my title -echo ::endgroup:: +echo "::group::my title" +echo "::endgroup::" ``` This is wrapped by the core methods: @@ -105,7 +105,7 @@ function endGroup(): void {} Save state to be used in the corresponding wrapper (finally) post job entry point. ```bash -echo ::save-state name=FOO::foovalue +echo "::save-state name=FOO::foovalue" ``` ### Log Level @@ -114,6 +114,12 @@ Finally, there are several commands to emit different levels of log output: | log level | example usage | |---|---| -| [debug](https://github.com/actions/toolkit/blob/master/docs/action-debugging.md) | `echo ::debug::My debug message` | -| warning | `echo ::warning::My warning message` | -| error | `echo ::error::My error message` | +| [debug](https://github.com/actions/toolkit/blob/master/docs/action-debugging.md) | `echo "::debug::My debug message"` | +| warning | `echo "::warning::My warning message"` | +| error | `echo "::error::My error message"` | + +### Command Prompt +CMD processes the `"` character differently from other shells when echoing. In CMD, the above snippets should have the `"` characters removed in order to correctly process. For example, the set output command would be: +```cmd +echo ::set-output name=FOO::BAR +``` From a9ebfb1a784bd83f2dceeb60d8ce7f6937f0c049 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Fri, 1 Nov 2019 10:05:01 -0400 Subject: [PATCH 024/192] Add Warning about multiline secrets (#196) --- docs/commands.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/commands.md b/docs/commands.md index f2c02bfc3a..66b0162a86 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -81,7 +81,9 @@ function setSecret(secret: string): void {} Now, future logs containing BAR will be masked. E.g. running `echo "Hello FOO BAR World"` will now print `Hello FOO **** World`. -CAUTION: Do **not** mask short values if you can avoid it, it could render your output unreadable (and future steps' output as well). +**WARNING** The add-mask and setSecret commands only support single line secrets. To register a multiline secrets you must register each line individually otherwise it will not be masked. + +**WARNING** Do **not** mask short values if you can avoid it, it could render your output unreadable (and future steps' output as well). For example, if you mask the letter `l`, running `echo "Hello FOO BAR World"` will now print `He*********o FOO BAR Wor****d` ### Group and Ungroup Log Lines From 1e5fc20bfe24f535b95e04ac9a5d0f02e1b49109 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sun, 3 Nov 2019 12:24:13 -0500 Subject: [PATCH 025/192] update versioning guidance --- docs/action-versioning.md | 52 +++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/docs/action-versioning.md b/docs/action-versioning.md index bb29e6af44..281c4a94bb 100644 --- a/docs/action-versioning.md +++ b/docs/action-versioning.md @@ -6,37 +6,53 @@ Examples: ```yaml steps: - - uses: actions/setup-node@74bc508 - - uses: actions/setup-node@v1 - - uses: actions/setup-node@master # not recommended + - uses: actions/javascript-action@v1 # recommended. starter workflows use this + - user: actions/javascript-action@v1.0.0 # if an action offers specific releases + - uses: actions/javascript-action@41775a4 # binding to a specific sha ``` -Binding to the immutable sha1 of a released version is the safest for stability and security. +# Compatibility -Binding to a specific major version allows for receiving critical fixes and security patches while still maintaining compatibility and the assurance your workflow should still work. +Binding to a major version is the latest of that major version ( e.g. `v1` == "1.*" ) -Binding to master is convenient but if a new major version is released which breaks compatibility, your workflow could break. +Major versions should guarentee compatibility. A major version can add net new capabilities but should not break existing input compatibility or break existing workflows. -# Recommendations +Major version binding allows you to take advantage of bug fixes and critical functionality and security fixes. The `master` branch has latest code and is unstable to bind to since a breaking new major version may first get implemented in master. + +> Warning: do not reference `master` since that is -1. **Don't check node_modules into master**: This will discourage people from attaching to master since the action will fail. You can enforce this by including `node_modules` in your `.gitignore` file. -2. **Create a release branch for each major version**: This will act as an alpha release for that major version. Any time you are ready to publish a new version from master, you should pull those changes into this branch (following the same steps listed below). +```yaml +steps: + - uses: actions/javascript-action@master # do not do this ``` -git checkout -b releases/v1 # If this branch already exists, omit the -b flag -rm -rf node_modules -sed -i '/node_modules/d' .gitignore # Bash command that removes node_modules from .gitignore -npm install --production -git add node_modules .gitignore -git commit -m node_modules -git push origin releases/v1 + +Binding to the immutable sha1 may offer more reliability. However, note that the hosted images toolsets (e.g. ubuntu-latest) move forward and if there is a tool breaking issue, actions may react with a patch to a major version to compensate so binding to a specific sha may prevent you from getting fixes. + +> Recommendation: bind to major versions to get functionality and fixes but reserve binding to a specific release or sha as a mitigation strategy for unforseen breaks. + +# Recommendations + +1. **Create a release branch for each major version**: For example, `releases\v1`. This will allow for releases of that major major while development of a different major version proceeds. + +2. **Validate changes referencing the release branch**: + +```yaml +steps: + - uses: actions/sample-action@releases/v1 ``` -3. **When ready for a stable release, add a major version tag**: Move the major version tag (v1, v2, etc.) to point to the ref of the current release. This will act as the stable release for that major version. You should keep this tag updated to the most recent stable minor/patch release. + +3. **Create a GitHub release for each specific version**: Creating a release like [ v1.0.0 ](https://github.com/actions/javascript-action/releases/tag/v1.0.0) allows users to bind back to a specific version if an issue is encoutered with the latest major version. + +4. **Release that version by updating the major version tag**: Move the major version tag (v1, v2, etc.) to point to the ref of the current release. This will act as the stable release for that major version. You should keep this tag updated to the most recent stable minor/patch release. + ``` git checkout releases/v1 git tag -fa v1 -m "Update v1 tag" git push origin v1 --force ``` -4. **Create releases for minor and patch version updates**: From the GitHub UI create a release for each minor or patch version update titled with that release version (e.g. v1.2.3). + +This will results in a major version tag and the latest specific version pointing to the same sha. See [javascript-action tags](https://github.com/actions/javascript-action/tags) as an example. + 5. **Compatibility Breaks**: introduce a new major version branch (releases/v2) and tag (v2) if changes will break existing workflows. For example, changing inputs. See [Git-Basics-Tagging](https://git-scm.com/book/en/v2/Git-Basics-Tagging) From 626bbe7136ca0b004917bccd733762b34c2de734 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sun, 3 Nov 2019 12:34:23 -0500 Subject: [PATCH 026/192] doc tweak --- docs/action-versioning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/action-versioning.md b/docs/action-versioning.md index 281c4a94bb..0e5f98dc8b 100644 --- a/docs/action-versioning.md +++ b/docs/action-versioning.md @@ -19,7 +19,7 @@ Major versions should guarentee compatibility. A major version can add net new Major version binding allows you to take advantage of bug fixes and critical functionality and security fixes. The `master` branch has latest code and is unstable to bind to since a breaking new major version may first get implemented in master. -> Warning: do not reference `master` since that is +> Warning: do not reference `master` since that is latest code and can be carrying breaking changes of the next major version. ```yaml steps: From 46c2a7e41a1e630b581277d2b62a04d5d69cce53 Mon Sep 17 00:00:00 2001 From: Josh Gross Date: Sun, 3 Nov 2019 14:32:46 -0500 Subject: [PATCH 027/192] Fix some typos (#200) --- docs/action-versioning.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/action-versioning.md b/docs/action-versioning.md index 0e5f98dc8b..4dd93c950b 100644 --- a/docs/action-versioning.md +++ b/docs/action-versioning.md @@ -1,6 +1,6 @@ # Versioning -Actions are downloaded and run from the GitHub graph of repos. The workflow references an action use a ref. +Actions are downloaded and run from the GitHub graph of repos. The workflow references an action using a ref. Examples: @@ -15,24 +15,24 @@ steps: Binding to a major version is the latest of that major version ( e.g. `v1` == "1.*" ) -Major versions should guarentee compatibility. A major version can add net new capabilities but should not break existing input compatibility or break existing workflows. +Major versions should guarantee compatibility. A major version can add net new capabilities but should not break existing input compatibility or break existing workflows. -Major version binding allows you to take advantage of bug fixes and critical functionality and security fixes. The `master` branch has latest code and is unstable to bind to since a breaking new major version may first get implemented in master. +Major version binding allows you to take advantage of bug fixes and critical functionality and security fixes. The `master` branch has the latest code and is unstable to bind to since a breaking new major version may first get implemented in master. -> Warning: do not reference `master` since that is latest code and can be carrying breaking changes of the next major version. +> Warning: do not reference `master` since that is the latest code and can be carrying breaking changes of the next major version. ```yaml steps: - uses: actions/javascript-action@master # do not do this ``` -Binding to the immutable sha1 may offer more reliability. However, note that the hosted images toolsets (e.g. ubuntu-latest) move forward and if there is a tool breaking issue, actions may react with a patch to a major version to compensate so binding to a specific sha may prevent you from getting fixes. +Binding to the immutable sha1 may offer more reliability. However, note that the hosted images toolsets (e.g. ubuntu-latest) move forward and if there is a tool breaking issue, actions may react with a patch to a major version to compensate so binding to a specific SHA may prevent you from getting fixes. -> Recommendation: bind to major versions to get functionality and fixes but reserve binding to a specific release or sha as a mitigation strategy for unforseen breaks. +> Recommendation: bind to major versions to get functionality and fixes but reserve binding to a specific release or SHA as a mitigation strategy for unforeseen breaks. # Recommendations -1. **Create a release branch for each major version**: For example, `releases\v1`. This will allow for releases of that major major while development of a different major version proceeds. +1. **Create a release branch for each major version**: For example, `releases\v1`. This will allow for releases of that major version while the development of a different major version proceeds. 2. **Validate changes referencing the release branch**: @@ -41,7 +41,7 @@ steps: - uses: actions/sample-action@releases/v1 ``` -3. **Create a GitHub release for each specific version**: Creating a release like [ v1.0.0 ](https://github.com/actions/javascript-action/releases/tag/v1.0.0) allows users to bind back to a specific version if an issue is encoutered with the latest major version. +3. **Create a GitHub release for each specific version**: Creating a release like [ v1.0.0 ](https://github.com/actions/javascript-action/releases/tag/v1.0.0) allows users to bind back to a specific version if an issue is encountered with the latest major version. 4. **Release that version by updating the major version tag**: Move the major version tag (v1, v2, etc.) to point to the ref of the current release. This will act as the stable release for that major version. You should keep this tag updated to the most recent stable minor/patch release. @@ -51,7 +51,7 @@ git tag -fa v1 -m "Update v1 tag" git push origin v1 --force ``` -This will results in a major version tag and the latest specific version pointing to the same sha. See [javascript-action tags](https://github.com/actions/javascript-action/tags) as an example. +This will result in a major version tag and the latest specific version pointing to the same SHA. See [javascript-action tags](https://github.com/actions/javascript-action/tags) as an example. 5. **Compatibility Breaks**: introduce a new major version branch (releases/v2) and tag (v2) if changes will break existing workflows. For example, changing inputs. @@ -59,6 +59,6 @@ See [Git-Basics-Tagging](https://git-scm.com/book/en/v2/Git-Basics-Tagging) # Sample Workflow -This illustrates one possible versioning workflow which the walk through covered. +This illustrates one possible versioning workflow which the walkthrough covered. ![versioning](assets/action-releases.png) From 4f11810a003820fd131eefc436ee0e31db1db382 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sun, 3 Nov 2019 14:35:12 -0500 Subject: [PATCH 028/192] doc tweak --- docs/action-versioning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/action-versioning.md b/docs/action-versioning.md index 4dd93c950b..c6f5c14ba1 100644 --- a/docs/action-versioning.md +++ b/docs/action-versioning.md @@ -59,6 +59,6 @@ See [Git-Basics-Tagging](https://git-scm.com/book/en/v2/Git-Basics-Tagging) # Sample Workflow -This illustrates one possible versioning workflow which the walkthrough covered. +This illustrates the versioning workflow covered above. ![versioning](assets/action-releases.png) From 0fbdc19f81039a10345d47c38d3207ae89088e71 Mon Sep 17 00:00:00 2001 From: Jim Hester Date: Wed, 6 Nov 2019 10:15:14 -0500 Subject: [PATCH 029/192] Fix typo (#201) --- .github/ISSUE_TEMPLATE/enhancement_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/enhancement_request.md b/.github/ISSUE_TEMPLATE/enhancement_request.md index f663965e18..9081c8e8b6 100644 --- a/.github/ISSUE_TEMPLATE/enhancement_request.md +++ b/.github/ISSUE_TEMPLATE/enhancement_request.md @@ -16,7 +16,7 @@ Thank you 🙇‍♀ for wanting to create an issue in this repository. Before y If your issue is relevant to this repository, please include the information below: **Describe the enhancement** -A clear and concise description of what the features or enhancment you need. +A clear and concise description of what the features or enhancement you need. **Code Snippet** If applicable, add a code snippet to show the api enhancement. From a465bf5e6d88189c1bae05664487051577febf1a Mon Sep 17 00:00:00 2001 From: Josh Gross Date: Wed, 6 Nov 2019 10:16:13 -0500 Subject: [PATCH 030/192] Fix slash in example release branch --- docs/action-versioning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/action-versioning.md b/docs/action-versioning.md index c6f5c14ba1..c203cd9c3d 100644 --- a/docs/action-versioning.md +++ b/docs/action-versioning.md @@ -32,7 +32,7 @@ Binding to the immutable sha1 may offer more reliability. However, note that th # Recommendations -1. **Create a release branch for each major version**: For example, `releases\v1`. This will allow for releases of that major version while the development of a different major version proceeds. +1. **Create a release branch for each major version**: For example, `releases/v1`. This will allow for releases of that major version while the development of a different major version proceeds. 2. **Validate changes referencing the release branch**: From 47357ddfee56500ecb6c6a0293b4fef9678fa63f Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Wed, 6 Nov 2019 16:24:16 -0500 Subject: [PATCH 031/192] Document Problem Matcher Commands (#198) * Add Initial Problem Matcher docs --- docs/commands.md | 12 ++++- docs/problem-matchers.md | 107 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 docs/problem-matchers.md diff --git a/docs/commands.md b/docs/commands.md index 66b0162a86..19b172015e 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -102,6 +102,16 @@ function startGroup(name: string): void {} function endGroup(): void {} ``` +### Problem Matchers +Problems matchers can be used to scan a build's output to automatically surface lines to the user that matches the provided pattern. A file path to a .json Problem Matcher must be provided. See [Problem Matchers](problem-matchers.md) for more information on how to define a Problem Matcher. + +```bash +echo "::add-matcher::eslint-compact-problem-matcher.json" +echo "::remove-matcher::eslint-compact" +``` + +`add-matcher` takes a path to a Problem Matcher file +`remove-matcher` removes a Problem Matcher by owner ### Save State Save state to be used in the corresponding wrapper (finally) post job entry point. @@ -116,7 +126,7 @@ Finally, there are several commands to emit different levels of log output: | log level | example usage | |---|---| -| [debug](https://github.com/actions/toolkit/blob/master/docs/action-debugging.md) | `echo "::debug::My debug message"` | +| [debug](action-debugging.md) | `echo "::debug::My debug message"` | | warning | `echo "::warning::My warning message"` | | error | `echo "::error::My error message"` | diff --git a/docs/problem-matchers.md b/docs/problem-matchers.md new file mode 100644 index 0000000000..d9259efb70 --- /dev/null +++ b/docs/problem-matchers.md @@ -0,0 +1,107 @@ +# Problem Matchers +Problem Matchers are a way to scan the output of actions for a specified regex pattern and surface that information prominently in the UI. Both [GitHub Annotations](https://developer.github.com/v3/checks/runs/#annotations-object-1) and log file decorations are created when a match is detected. + +## Single Line Matchers + +Let's consider the ESLint compact output: +``` +badFile.js: line 50, col 11, Error - 'myVar' is defined but never used. (no-unused-vars) +``` +We can define a problem matcher in json that detects input in that format: +```json +{ + "problemMatcher": [ + { + "owner": "eslint-compact", + "pattern": [ + { + "regexp": "^(.+):\\sline\\s(\\d+),\\scol\\s(\\d+),\\s(Error|Warning|Info)\\s-\\s(.+)\\s\\((.+)\\)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5, + "code": 6 + } + ] + } + ] +} +``` + +The following fields are available for problem matchers: + +``` +{ + owner: An ID field that can be used to remove or replace the problem matcher. **required** + pattern: [ + { + regexp: The regex pattern that provides the groups to match against **required** + file: a group number containing the file name + line: a group number containing the line number + column: a group number containing the column information + severity: a group number containing either 'warning' or 'error' case-insensitive. Defaults to `error` + code: a group number containing the error code + message: a group number containing the error message. **required** at least one pattern must set the message + loop: loops until a match is not found, only valid on the last pattern of a multipattern matcher + } + ] +} +``` + + +## Multiline Matching +Consider the following output: +``` +test.js + 1:0 error Missing "use strict" statement strict + 5:10 error 'addOne' is defined but never used no-unused-vars +✖ 2 problems (2 errors, 0 warnings) +``` +The file name is printed once, yet multiple error lines are printed. The `loop` keyword provides a way to discover multiple errors in outputs. + +The eslint-stylish problem matcher defined below catches that output, and creates two annotations from it. + +``` +{ + "problemMatcher": [ + { + "owner": "eslint-stylish", + "pattern": [ + { + // Matches the 1st line in the output + "regexp": "^([^\\s].*)$", + "file": 1 + }, + { + // Matches the 2nd and 3rd line in the output + "regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$", + // File is carried through from above, so we definte the rest of the groups + "line": 1, + "column": 2, + "severity": 3, + "message": 4, + "code": 5, + "loop": true + } + ] + } + ] +} +``` + +The first pattern matches the `test.js` line and records the file information. This line is not decorated in the UI. +The second pattern loops through the remaining lines with `loop: true` until it fails to find a match, and surfaces these lines prominently in the UI. + +## Adding and Removing Problem Matchers +Problem Matchers are enabled and removed via the toolkit [commands](commands.md#problem-matchers). + +## Duplicate Problem Matchers +Registering two problem-matchers with the same owner will result in only the problem matcher registered last running. + +## Examples +Some of the starter actions are already using problem matchers, for example: +- [setup-node](https://github.com/actions/setup-node/tree/master/.github) +- [setup-python](https://github.com/actions/setup-python/tree/master/.github) +- [setup-go](https://github.com/actions/setup-go/tree/master/.github) +- [setup-dotnet](https://github.com/actions/setup-dotnet/tree/master/.github) From 6c824bd4480e18ab1c7e7f36036281c2454f3c0a Mon Sep 17 00:00:00 2001 From: eric sciple Date: Tue, 12 Nov 2019 12:13:32 -0500 Subject: [PATCH 032/192] Update jest and lerna to fix npm install warnings --- package-lock.json | 6433 +++++++++++++++++++++++++++++++++++---------- package.json | 4 +- 2 files changed, 5005 insertions(+), 1432 deletions(-) diff --git a/package-lock.json b/package-lock.json index cb6f89a100..c352e053a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -176,6 +176,144 @@ } } }, + "@evocateur/libnpmaccess": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz", + "integrity": "sha512-KSCAHwNWro0CF2ukxufCitT9K5LjL/KuMmNzSu8wuwN2rjyKHD8+cmOsiybK+W5hdnwc5M1SmRlVCaMHQo+3rg==", + "dev": true, + "requires": { + "@evocateur/npm-registry-fetch": "^4.0.0", + "aproba": "^2.0.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "npm-package-arg": "^6.1.0" + }, + "dependencies": { + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + } + } + }, + "@evocateur/libnpmpublish": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@evocateur/libnpmpublish/-/libnpmpublish-1.2.2.tgz", + "integrity": "sha512-MJrrk9ct1FeY9zRlyeoyMieBjGDG9ihyyD9/Ft6MMrTxql9NyoEx2hw9casTIP4CdqEVu+3nQ2nXxoJ8RCXyFg==", + "dev": true, + "requires": { + "@evocateur/npm-registry-fetch": "^4.0.0", + "aproba": "^2.0.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "lodash.clonedeep": "^4.5.0", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "semver": "^5.5.1", + "ssri": "^6.0.1" + }, + "dependencies": { + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@evocateur/npm-registry-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@evocateur/npm-registry-fetch/-/npm-registry-fetch-4.0.0.tgz", + "integrity": "sha512-k1WGfKRQyhJpIr+P17O5vLIo2ko1PFLKwoetatdduUSt/aQ4J2sJrJwwatdI5Z3SiYk/mRH9S3JpdmMFd/IK4g==", + "dev": true, + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "npm-package-arg": "^6.1.0", + "safe-buffer": "^5.1.2" + } + }, + "@evocateur/pacote": { + "version": "9.6.5", + "resolved": "https://registry.npmjs.org/@evocateur/pacote/-/pacote-9.6.5.tgz", + "integrity": "sha512-EI552lf0aG2nOV8NnZpTxNo2PcXKPmDbF9K8eCBFQdIZwHNGN/mi815fxtmUMa2wTa1yndotICIDt/V0vpEx2w==", + "dev": true, + "requires": { + "@evocateur/npm-registry-fetch": "^4.0.0", + "bluebird": "^3.5.3", + "cacache": "^12.0.3", + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.5.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.4.4", + "npm-pick-manifest": "^3.0.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.3", + "safe-buffer": "^5.2.0", + "semver": "^5.7.0", + "ssri": "^6.0.1", + "tar": "^4.4.10", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "chownr": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, "@jest/console": { "version": "24.7.1", "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", @@ -188,46 +326,361 @@ } }, "@jest/core": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.7.1.tgz", - "integrity": "sha512-ivlZ8HX/FOASfHcb5DJpSPFps8ydfUYzLZfgFFqjkLijYysnIEOieg72YRhO4ZUB32xu40hsSMmaw+IGYeKONA==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz", + "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==", "dev": true, "requires": { "@jest/console": "^24.7.1", - "@jest/reporters": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/reporters": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", "ansi-escapes": "^3.0.0", "chalk": "^2.0.1", "exit": "^0.1.2", "graceful-fs": "^4.1.15", - "jest-changed-files": "^24.7.0", - "jest-config": "^24.7.1", - "jest-haste-map": "^24.7.1", - "jest-message-util": "^24.7.1", + "jest-changed-files": "^24.9.0", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", "jest-regex-util": "^24.3.0", - "jest-resolve-dependencies": "^24.7.1", - "jest-runner": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", - "jest-watcher": "^24.7.1", + "jest-resolve": "^24.9.0", + "jest-resolve-dependencies": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "jest-watcher": "^24.9.0", "micromatch": "^3.1.10", "p-each-series": "^1.0.0", - "pirates": "^4.0.1", "realpath-native": "^1.1.0", "rimraf": "^2.5.4", + "slash": "^2.0.0", "strip-ansi": "^5.0.0" }, "dependencies": { + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + } + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + }, + "dependencies": { + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + } + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, + "diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "dev": true + }, + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + }, + "dependencies": { + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + } + } + }, + "jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + } + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -236,6 +689,15 @@ "requires": { "ansi-regex": "^4.1.0" } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } } } }, @@ -263,54 +725,272 @@ } }, "@jest/reporters": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.7.1.tgz", - "integrity": "sha512-bO+WYNwHLNhrjB9EbPL4kX/mCCG4ZhhfWmO3m4FSpbgr7N83MFejayz30kKjgqr7smLyeaRFCBQMbXpUgnhAJw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz", + "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==", "dev": true, "requires": { - "@jest/environment": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", "chalk": "^2.0.1", "exit": "^0.1.2", "glob": "^7.1.2", - "istanbul-api": "^2.1.1", "istanbul-lib-coverage": "^2.0.2", "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-report": "^2.0.4", "istanbul-lib-source-maps": "^3.0.1", - "jest-haste-map": "^24.7.1", - "jest-resolve": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-util": "^24.7.1", + "istanbul-reports": "^2.2.6", + "jest-haste-map": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", "jest-worker": "^24.6.0", - "node-notifier": "^5.2.1", + "node-notifier": "^5.4.2", "slash": "^2.0.0", "source-map": "^0.6.0", "string-length": "^2.0.0" }, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "@jest/source-map": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz", - "integrity": "sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/environment": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "dev": true, + "requires": { + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "dependencies": { + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + } + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@jest/source-map": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz", + "integrity": "sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true @@ -329,15 +1009,183 @@ } }, "@jest/test-sequencer": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.7.1.tgz", - "integrity": "sha512-84HQkCpVZI/G1zq53gHJvSmhUer4aMYp9tTaffW28Ih5OxfCg8hGr3nTSbL1OhVDRrFZwvF+/R9gY6JRkDUpUA==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz", + "integrity": "sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==", "dev": true, "requires": { - "@jest/test-result": "^24.7.1", - "jest-haste-map": "^24.7.1", - "jest-runner": "^24.7.1", - "jest-runtime": "^24.7.1" + "@jest/test-result": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@jest/transform": { @@ -382,93 +1230,97 @@ } }, "@lerna/add": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/add/-/add-3.13.3.tgz", - "integrity": "sha512-T3/Lsbo9ZFq+vL3ssaHxA8oKikZAPTJTGFe4CRuQgWCDd/M61+51jeWsngdaHpwzSSRDRjxg8fJTG10y10pnfA==", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/@lerna/add/-/add-3.18.4.tgz", + "integrity": "sha512-R+9RmYrSbcmnmaFL2aB0HJtTq95ePEa0FMS4r4NnA7Xw07l5buVBPOfxv6P8kFrVvIcNpaa7S0Eo/KkbycMhKA==", "dev": true, "requires": { - "@lerna/bootstrap": "3.13.3", - "@lerna/command": "3.13.3", - "@lerna/filter-options": "3.13.3", - "@lerna/npm-conf": "3.13.0", + "@evocateur/pacote": "^9.6.3", + "@lerna/bootstrap": "3.18.4", + "@lerna/command": "3.18.0", + "@lerna/filter-options": "3.18.4", + "@lerna/npm-conf": "3.16.0", "@lerna/validation-error": "3.13.0", "dedent": "^0.7.0", "npm-package-arg": "^6.1.0", - "p-map": "^1.2.0", - "pacote": "^9.5.0", - "semver": "^5.5.0" - } - }, - "@lerna/batch-packages": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/batch-packages/-/batch-packages-3.13.0.tgz", - "integrity": "sha512-TgLBTZ7ZlqilGnzJ3xh1KdAHcySfHytgNRTdG9YomfriTU6kVfp1HrXxKJYVGs7ClPUNt2CTFEOkw0tMBronjw==", - "dev": true, - "requires": { - "@lerna/package-graph": "3.13.0", - "@lerna/validation-error": "3.13.0", - "npmlog": "^4.1.2" + "p-map": "^2.1.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@lerna/bootstrap": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-3.13.3.tgz", - "integrity": "sha512-2XzijnLHRZOVQh8pwS7+5GR3cG4uh+EiLrWOishCq2TVzkqgjaS3GGBoef7KMCXfWHoLqAZRr/jEdLqfETLVqg==", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-3.18.4.tgz", + "integrity": "sha512-mvqMyionPSqhbeGhoUQYEBTgbJ47LkONHfQ1AKBET0fJOjIZf6x0pWC17KvfCjsiE017325ySLKDH23z1Kb9ww==", "dev": true, "requires": { - "@lerna/batch-packages": "3.13.0", - "@lerna/command": "3.13.3", - "@lerna/filter-options": "3.13.3", - "@lerna/has-npm-version": "3.13.3", - "@lerna/npm-install": "3.13.3", - "@lerna/package-graph": "3.13.0", + "@lerna/command": "3.18.0", + "@lerna/filter-options": "3.18.4", + "@lerna/has-npm-version": "3.16.5", + "@lerna/npm-install": "3.16.5", + "@lerna/package-graph": "3.18.0", "@lerna/pulse-till-done": "3.13.0", - "@lerna/rimraf-dir": "3.13.3", - "@lerna/run-lifecycle": "3.13.0", - "@lerna/run-parallel-batches": "3.13.0", - "@lerna/symlink-binary": "3.13.0", - "@lerna/symlink-dependencies": "3.13.0", + "@lerna/rimraf-dir": "3.16.5", + "@lerna/run-lifecycle": "3.16.2", + "@lerna/run-topologically": "3.18.0", + "@lerna/symlink-binary": "3.17.0", + "@lerna/symlink-dependencies": "3.17.0", "@lerna/validation-error": "3.13.0", "dedent": "^0.7.0", - "get-port": "^3.2.0", - "multimatch": "^2.1.0", + "get-port": "^4.2.0", + "multimatch": "^3.0.0", "npm-package-arg": "^6.1.0", "npmlog": "^4.1.2", "p-finally": "^1.0.0", - "p-map": "^1.2.0", + "p-map": "^2.1.0", "p-map-series": "^1.0.0", "p-waterfall": "^1.0.0", "read-package-tree": "^5.1.6", - "semver": "^5.5.0" + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@lerna/changed": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/changed/-/changed-3.13.3.tgz", - "integrity": "sha512-REMZ/1UvYrizUhN7ktlbfMUa0vhMf1ogAe97WQC4I8r3s973Orfhs3aselo1GwudUwM4tMHBH8A9vnll9or3iA==", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/@lerna/changed/-/changed-3.18.4.tgz", + "integrity": "sha512-Ui4UsneDk9gCuJRfTpR5js+Ctt9Je+j+3Q4z7H7HhBn6WeWDTp6FBGJZ7SfrBCdQ47EKK27Mr95LbJ4I77xFfQ==", "dev": true, "requires": { - "@lerna/collect-updates": "3.13.3", - "@lerna/command": "3.13.3", - "@lerna/listable": "3.13.0", - "@lerna/output": "3.13.0", - "@lerna/version": "3.13.3" + "@lerna/collect-updates": "3.18.0", + "@lerna/command": "3.18.0", + "@lerna/listable": "3.18.4", + "@lerna/output": "3.13.0" } }, "@lerna/check-working-tree": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-3.13.3.tgz", - "integrity": "sha512-LoGZvTkne+V1WpVdCTU0XNzFKsQa2AiAFKksGRT0v8NQj6VAPp0jfVYDayTqwaWt2Ne0OGKOFE79Y5LStOuhaQ==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/check-working-tree/-/check-working-tree-3.16.5.tgz", + "integrity": "sha512-xWjVBcuhvB8+UmCSb5tKVLB5OuzSpw96WEhS2uz6hkWVa/Euh1A0/HJwn2cemyK47wUrCQXtczBUiqnq9yX5VQ==", "dev": true, "requires": { - "@lerna/describe-ref": "3.13.3", + "@lerna/collect-uncommitted": "3.16.5", + "@lerna/describe-ref": "3.16.5", "@lerna/validation-error": "3.13.0" } }, "@lerna/child-process": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/child-process/-/child-process-3.13.3.tgz", - "integrity": "sha512-3/e2uCLnbU+bydDnDwyadpOmuzazS01EcnOleAnuj9235CU2U97DH6OyoG1EW/fU59x11J+HjIqovh5vBaMQjQ==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/child-process/-/child-process-3.16.5.tgz", + "integrity": "sha512-vdcI7mzei9ERRV4oO8Y1LHBZ3A5+ampRKg1wq5nutLsUA4mEBN6H7JqjWOMY9xZemv6+kATm2ofjJ3lW5TszQg==", "dev": true, "requires": { "chalk": "^2.3.1", @@ -477,157 +1329,279 @@ } }, "@lerna/clean": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/clean/-/clean-3.13.3.tgz", - "integrity": "sha512-xmNauF1PpmDaKdtA2yuRc23Tru4q7UMO6yB1a/TTwxYPYYsAWG/CBK65bV26J7x4RlZtEv06ztYGMa9zh34UXA==", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/@lerna/clean/-/clean-3.18.4.tgz", + "integrity": "sha512-puuL0sBHIv3Tvq8cdu3kCGfRpdsXuaDGIRha33GVmRPfMBi2GN8nPPysVyWmP99PfgfafO6eT5R3jqXjvASAZA==", "dev": true, "requires": { - "@lerna/command": "3.13.3", - "@lerna/filter-options": "3.13.3", + "@lerna/command": "3.18.0", + "@lerna/filter-options": "3.18.4", "@lerna/prompt": "3.13.0", "@lerna/pulse-till-done": "3.13.0", - "@lerna/rimraf-dir": "3.13.3", - "p-map": "^1.2.0", + "@lerna/rimraf-dir": "3.16.5", + "p-map": "^2.1.0", "p-map-series": "^1.0.0", "p-waterfall": "^1.0.0" } }, "@lerna/cli": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/cli/-/cli-3.13.0.tgz", - "integrity": "sha512-HgFGlyCZbYaYrjOr3w/EsY18PdvtsTmDfpUQe8HwDjXlPeCCUgliZjXLOVBxSjiOvPeOSwvopwIHKWQmYbwywg==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/cli/-/cli-3.18.0.tgz", + "integrity": "sha512-AwDyfGx7fxJgeaZllEuyJ9LZ6Tdv9yqRD9RX762yCJu+PCAFvB9bp6OYuRSGli7QQgM0CuOYnSg4xVNOmuGKDA==", "dev": true, "requires": { "@lerna/global-options": "3.13.0", "dedent": "^0.7.0", "npmlog": "^4.1.2", - "yargs": "^12.0.1" - } - }, - "@lerna/collect-updates": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-3.13.3.tgz", - "integrity": "sha512-sTpALOAxli/ZS+Mjq6fbmjU9YXqFJ2E4FrE1Ijl4wPC5stXEosg2u0Z1uPY+zVKdM+mOIhLxPVdx83rUgRS+Cg==", - "dev": true, - "requires": { - "@lerna/child-process": "3.13.3", - "@lerna/describe-ref": "3.13.3", - "minimatch": "^3.0.4", - "npmlog": "^4.1.2", - "slash": "^1.0.0" - }, - "dependencies": { - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - } - } - }, - "@lerna/command": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/command/-/command-3.13.3.tgz", - "integrity": "sha512-WHFIQCubJV0T8gSLRNr6exZUxTswrh+iAtJCb86SE0Sa+auMPklE8af7w2Yck5GJfewmxSjke3yrjNxQrstx7w==", - "dev": true, - "requires": { - "@lerna/child-process": "3.13.3", - "@lerna/package-graph": "3.13.0", - "@lerna/project": "3.13.1", - "@lerna/validation-error": "3.13.0", - "@lerna/write-log-file": "3.13.0", - "dedent": "^0.7.0", - "execa": "^1.0.0", - "is-ci": "^1.0.10", - "lodash": "^4.17.5", - "npmlog": "^4.1.2" + "yargs": "^14.2.0" }, "dependencies": { - "ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, - "is-ci": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "dev": true, "requires": { - "ci-info": "^1.5.0" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" } - } - } - }, - "@lerna/conventional-commits": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-3.13.0.tgz", - "integrity": "sha512-BeAgcNXuocmLhPxnmKU2Vy8YkPd/Uo+vu2i/p3JGsUldzrPC8iF3IDxH7fuXpEFN2Nfogu7KHachd4tchtOppA==", - "dev": true, - "requires": { - "@lerna/validation-error": "3.13.0", - "conventional-changelog-angular": "^5.0.3", - "conventional-changelog-core": "^3.1.6", - "conventional-recommended-bump": "^4.0.4", - "fs-extra": "^7.0.0", - "get-stream": "^4.0.0", - "npm-package-arg": "^6.1.0", - "npmlog": "^4.1.2", - "pify": "^3.0.0", - "semver": "^5.5.0" - }, - "dependencies": { - "pify": { + }, + "find-up": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.0.tgz", + "integrity": "sha512-/is78VKbKs70bVZH7w4YaZea6xcJWOAwkhbR0CFuZBmYtfTYF0xjGJF43AYd8g2Uii1yJwmS5GR2vBmrc32sbg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.0" + } + }, + "yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "@lerna/collect-uncommitted": { + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/collect-uncommitted/-/collect-uncommitted-3.16.5.tgz", + "integrity": "sha512-ZgqnGwpDZiWyzIQVZtQaj9tRizsL4dUOhuOStWgTAw1EMe47cvAY2kL709DzxFhjr6JpJSjXV5rZEAeU3VE0Hg==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "chalk": "^2.3.1", + "figgy-pudding": "^3.5.1", + "npmlog": "^4.1.2" + } + }, + "@lerna/collect-updates": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/collect-updates/-/collect-updates-3.18.0.tgz", + "integrity": "sha512-LJMKgWsE/var1RSvpKDIxS8eJ7POADEc0HM3FQiTpEczhP6aZfv9x3wlDjaHpZm9MxJyQilqxZcasRANmRcNgw==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "@lerna/describe-ref": "3.16.5", + "minimatch": "^3.0.4", + "npmlog": "^4.1.2", + "slash": "^2.0.0" + } + }, + "@lerna/command": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/command/-/command-3.18.0.tgz", + "integrity": "sha512-JQ0TGzuZc9Ky8xtwtSLywuvmkU8X62NTUT3rMNrUykIkOxBaO+tE0O98u2yo/9BYOeTRji9IsjKZEl5i9Qt0xQ==", + "dev": true, + "requires": { + "@lerna/child-process": "3.16.5", + "@lerna/package-graph": "3.18.0", + "@lerna/project": "3.18.0", + "@lerna/validation-error": "3.13.0", + "@lerna/write-log-file": "3.13.0", + "dedent": "^0.7.0", + "execa": "^1.0.0", + "is-ci": "^2.0.0", + "lodash": "^4.17.14", + "npmlog": "^4.1.2" + } + }, + "@lerna/conventional-commits": { + "version": "3.16.4", + "resolved": "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-3.16.4.tgz", + "integrity": "sha512-QSZJ0bC9n6FVaf+7KDIq5zMv8WnHXnwhyL5jG1Nyh3SgOg9q2uflqh7YsYB+G6FwaRfnPaKosh6obijpYg0llA==", + "dev": true, + "requires": { + "@lerna/validation-error": "3.13.0", + "conventional-changelog-angular": "^5.0.3", + "conventional-changelog-core": "^3.1.6", + "conventional-recommended-bump": "^5.0.0", + "fs-extra": "^8.1.0", + "get-stream": "^4.0.0", + "lodash.template": "^4.5.0", + "npm-package-arg": "^6.1.0", + "npmlog": "^4.1.2", + "pify": "^4.0.1", + "semver": "^6.2.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } }, "@lerna/create": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/create/-/create-3.13.3.tgz", - "integrity": "sha512-4M5xT1AyUMwt1gCDph4BfW3e6fZmt0KjTa3FoXkUotf/w/eqTsc2IQ+ULz2+gOFQmtuNbqIZEOK3J4P9ArJJ/A==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/create/-/create-3.18.0.tgz", + "integrity": "sha512-y9oS7ND5T13c+cCTJHa2Y9in02ppzyjsNynVWFuS40eIzZ3z058d9+3qSBt1nkbbQlVyfLoP6+bZPsjyzap5ig==", "dev": true, "requires": { - "@lerna/child-process": "3.13.3", - "@lerna/command": "3.13.3", - "@lerna/npm-conf": "3.13.0", + "@evocateur/pacote": "^9.6.3", + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.18.0", + "@lerna/npm-conf": "3.16.0", "@lerna/validation-error": "3.13.0", "camelcase": "^5.0.0", "dedent": "^0.7.0", - "fs-extra": "^7.0.0", - "globby": "^8.0.1", + "fs-extra": "^8.1.0", + "globby": "^9.2.0", "init-package-json": "^1.10.3", "npm-package-arg": "^6.1.0", "p-reduce": "^1.0.0", - "pacote": "^9.5.0", - "pify": "^3.0.0", - "semver": "^5.5.0", - "slash": "^1.0.0", + "pify": "^4.0.1", + "semver": "^6.2.0", + "slash": "^2.0.0", "validate-npm-package-license": "^3.0.3", "validate-npm-package-name": "^3.0.0", "whatwg-url": "^7.0.0" }, "dependencies": { "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "whatwg-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", - "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", "dev": true, "requires": { "lodash.sortby": "^4.7.0", @@ -638,71 +1612,73 @@ } }, "@lerna/create-symlink": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/create-symlink/-/create-symlink-3.13.0.tgz", - "integrity": "sha512-PTvg3jAAJSAtLFoZDsuTMv1wTOC3XYIdtg54k7uxIHsP8Ztpt+vlilY/Cni0THAqEMHvfiToel76Xdta4TU21Q==", + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/@lerna/create-symlink/-/create-symlink-3.16.2.tgz", + "integrity": "sha512-pzXIJp6av15P325sgiIRpsPXLFmkisLhMBCy4764d+7yjf2bzrJ4gkWVMhsv4AdF0NN3OyZ5jjzzTtLNqfR+Jw==", "dev": true, "requires": { - "cmd-shim": "^2.0.2", - "fs-extra": "^7.0.0", + "@zkochan/cmd-shim": "^3.1.0", + "fs-extra": "^8.1.0", "npmlog": "^4.1.2" } }, "@lerna/describe-ref": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-3.13.3.tgz", - "integrity": "sha512-5KcLTvjdS4gU5evW8ESbZ0BF44NM5HrP3dQNtWnOUSKJRgsES8Gj0lq9AlB2+YglZfjEftFT03uOYOxnKto4Uw==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/describe-ref/-/describe-ref-3.16.5.tgz", + "integrity": "sha512-c01+4gUF0saOOtDBzbLMFOTJDHTKbDFNErEY6q6i9QaXuzy9LNN62z+Hw4acAAZuJQhrVWncVathcmkkjvSVGw==", "dev": true, "requires": { - "@lerna/child-process": "3.13.3", + "@lerna/child-process": "3.16.5", "npmlog": "^4.1.2" } }, "@lerna/diff": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/diff/-/diff-3.13.3.tgz", - "integrity": "sha512-/DRS2keYbnKaAC+5AkDyZRGkP/kT7v1GlUS0JGZeiRDPQ1H6PzhX09EgE5X6nj0Ytrm0sUasDeN++CDVvgaI+A==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/diff/-/diff-3.18.0.tgz", + "integrity": "sha512-3iLNlpurc2nV9k22w8ini2Zjm2UPo3xtQgWyqdA6eJjvge0+5AlNAWfPoV6cV+Hc1xDbJD2YDSFpZPJ1ZGilRw==", "dev": true, "requires": { - "@lerna/child-process": "3.13.3", - "@lerna/command": "3.13.3", + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.18.0", "@lerna/validation-error": "3.13.0", "npmlog": "^4.1.2" } }, "@lerna/exec": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/exec/-/exec-3.13.3.tgz", - "integrity": "sha512-c0bD4XqM96CTPV8+lvkxzE7mkxiFyv/WNM4H01YvvbFAJzk+S4Y7cBtRkIYFTfkFZW3FLo8pEgtG1ONtIdM+tg==", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/@lerna/exec/-/exec-3.18.4.tgz", + "integrity": "sha512-BpBFxyCQXcfess9Nmj/OwQ9e1IhzPzNxqF5JK7dPIjko5oBn5Hm2EWVAcgUGSHKPZGLiOWPu3Wx/C92NtDBS1w==", "dev": true, "requires": { - "@lerna/batch-packages": "3.13.0", - "@lerna/child-process": "3.13.3", - "@lerna/command": "3.13.3", - "@lerna/filter-options": "3.13.3", - "@lerna/run-parallel-batches": "3.13.0", - "@lerna/validation-error": "3.13.0" + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.18.0", + "@lerna/filter-options": "3.18.4", + "@lerna/run-topologically": "3.18.0", + "@lerna/validation-error": "3.13.0", + "p-map": "^2.1.0" } }, "@lerna/filter-options": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-3.13.3.tgz", - "integrity": "sha512-DbtQX4eRgrBz1wCFWRP99JBD7ODykYme9ykEK79+RrKph40znhJQRlLg4idogj6IsUEzwo1OHjihCzSfnVo6Cg==", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/@lerna/filter-options/-/filter-options-3.18.4.tgz", + "integrity": "sha512-4giVQD6tauRwweO/322LP2gfVDOVrt/xN4khkXyfkJDfcsZziFXq+668otD9KSLL8Ps+To4Fah3XbK0MoNuEvA==", "dev": true, "requires": { - "@lerna/collect-updates": "3.13.3", - "@lerna/filter-packages": "3.13.0", - "dedent": "^0.7.0" + "@lerna/collect-updates": "3.18.0", + "@lerna/filter-packages": "3.18.0", + "dedent": "^0.7.0", + "figgy-pudding": "^3.5.1", + "npmlog": "^4.1.2" } }, "@lerna/filter-packages": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-3.13.0.tgz", - "integrity": "sha512-RWiZWyGy3Mp7GRVBn//CacSnE3Kw82PxE4+H6bQ3pDUw/9atXn7NRX+gkBVQIYeKamh7HyumJtyOKq3Pp9BADQ==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/filter-packages/-/filter-packages-3.18.0.tgz", + "integrity": "sha512-6/0pMM04bCHNATIOkouuYmPg6KH3VkPCIgTfQmdkPJTullERyEQfNUKikrefjxo1vHOoCACDpy65JYyKiAbdwQ==", "dev": true, "requires": { "@lerna/validation-error": "3.13.0", - "multimatch": "^2.1.0", + "multimatch": "^3.0.0", "npmlog": "^4.1.2" } }, @@ -716,29 +1692,59 @@ } }, "@lerna/get-packed": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/get-packed/-/get-packed-3.13.0.tgz", - "integrity": "sha512-EgSim24sjIjqQDC57bgXD9l22/HCS93uQBbGpkzEOzxAVzEgpZVm7Fm1t8BVlRcT2P2zwGnRadIvxTbpQuDPTg==", + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/get-packed/-/get-packed-3.16.0.tgz", + "integrity": "sha512-AjsFiaJzo1GCPnJUJZiTW6J1EihrPkc2y3nMu6m3uWFxoleklsSCyImumzVZJssxMi3CPpztj8LmADLedl9kXw==", "dev": true, "requires": { - "fs-extra": "^7.0.0", + "fs-extra": "^8.1.0", "ssri": "^6.0.1", "tar": "^4.4.8" } }, "@lerna/github-client": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/github-client/-/github-client-3.13.3.tgz", - "integrity": "sha512-fcJkjab4kX0zcLLSa/DCUNvU3v8wmy2c1lhdIbL7s7gABmDcV0QZq93LhnEee3VkC9UpnJ6GKG4EkD7eIifBnA==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/github-client/-/github-client-3.16.5.tgz", + "integrity": "sha512-rHQdn8Dv/CJrO3VouOP66zAcJzrHsm+wFuZ4uGAai2At2NkgKH+tpNhQy2H1PSC0Ezj9LxvdaHYrUzULqVK5Hw==", "dev": true, "requires": { - "@lerna/child-process": "3.13.3", - "@octokit/plugin-enterprise-rest": "^2.1.1", - "@octokit/rest": "^16.16.0", + "@lerna/child-process": "3.16.5", + "@octokit/plugin-enterprise-rest": "^3.6.1", + "@octokit/rest": "^16.28.4", "git-url-parse": "^11.1.2", "npmlog": "^4.1.2" } }, + "@lerna/gitlab-client": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@lerna/gitlab-client/-/gitlab-client-3.15.0.tgz", + "integrity": "sha512-OsBvRSejHXUBMgwWQqNoioB8sgzL/Pf1pOUhHKtkiMl6aAWjklaaq5HPMvTIsZPfS6DJ9L5OK2GGZuooP/5c8Q==", + "dev": true, + "requires": { + "node-fetch": "^2.5.0", + "npmlog": "^4.1.2", + "whatwg-url": "^7.0.0" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "dev": true + }, + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, "@lerna/global-options": { "version": "3.13.0", "resolved": "https://registry.npmjs.org/@lerna/global-options/-/global-options-3.13.0.tgz", @@ -746,139 +1752,140 @@ "dev": true }, "@lerna/has-npm-version": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-3.13.3.tgz", - "integrity": "sha512-mQzoghRw4dBg0R9FFfHrj0TH0glvXyzdEZmYZ8Isvx5BSuEEwpsryoywuZSdppcvLu8o7NAdU5Tac8cJ/mT52w==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/has-npm-version/-/has-npm-version-3.16.5.tgz", + "integrity": "sha512-WL7LycR9bkftyqbYop5rEGJ9sRFIV55tSGmbN1HLrF9idwOCD7CLrT64t235t3t4O5gehDnwKI5h2U3oxTrF8Q==", "dev": true, "requires": { - "@lerna/child-process": "3.13.3", - "semver": "^5.5.0" + "@lerna/child-process": "3.16.5", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@lerna/import": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/import/-/import-3.13.3.tgz", - "integrity": "sha512-gDjLAFVavG/CMvj9leBfiwd7vrXqtdFXPIz1oXmghBMnje7nCTbodbNWFe4VDDWx7reDaZIN+6PxTSvrPcF//A==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/import/-/import-3.18.0.tgz", + "integrity": "sha512-2pYIkkBTZsEdccfc+dPsKZeSw3tBzKSyl0b2lGrfmNX2Y41qqOzsJCyI1WO1uvEIP8aOaLy4hPpqRIBe4ee7hw==", "dev": true, "requires": { - "@lerna/child-process": "3.13.3", - "@lerna/command": "3.13.3", + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.18.0", "@lerna/prompt": "3.13.0", "@lerna/pulse-till-done": "3.13.0", "@lerna/validation-error": "3.13.0", "dedent": "^0.7.0", - "fs-extra": "^7.0.0", + "fs-extra": "^8.1.0", "p-map-series": "^1.0.0" } }, "@lerna/init": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/init/-/init-3.13.3.tgz", - "integrity": "sha512-bK/mp0sF6jT0N+c+xrbMCqN4xRoiZCXQzlYsyACxPK99KH/mpHv7hViZlTYUGlYcymtew6ZC770miv5A9wF9hA==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/init/-/init-3.18.0.tgz", + "integrity": "sha512-/vHpmXkMlSaJaq25v5K13mcs/2L7E32O6dSsEkHaZCDRiV2BOqsZng9jjbE/4ynfsWfLLlU9ZcydwG72C3I+mQ==", "dev": true, "requires": { - "@lerna/child-process": "3.13.3", - "@lerna/command": "3.13.3", - "fs-extra": "^7.0.0", - "p-map": "^1.2.0", - "write-json-file": "^2.3.0" + "@lerna/child-process": "3.16.5", + "@lerna/command": "3.18.0", + "fs-extra": "^8.1.0", + "p-map": "^2.1.0", + "write-json-file": "^3.2.0" } }, "@lerna/link": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/link/-/link-3.13.3.tgz", - "integrity": "sha512-IHhtdhA0KlIdevCsq6WHkI2rF3lHWHziJs2mlrEWAKniVrFczbELON1KJAgdJS1k3kAP/WeWVqmIYZ2hJDxMvg==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/link/-/link-3.18.0.tgz", + "integrity": "sha512-FbbIpH0EpsC+dpAbvxCoF3cn7F1MAyJjEa5Lh3XkDGATOlinMFuKCbmX0NLpOPQZ5zghvrui97cx+jz5F2IlHw==", "dev": true, "requires": { - "@lerna/command": "3.13.3", - "@lerna/package-graph": "3.13.0", - "@lerna/symlink-dependencies": "3.13.0", - "p-map": "^1.2.0", - "slash": "^1.0.0" - }, - "dependencies": { - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - } + "@lerna/command": "3.18.0", + "@lerna/package-graph": "3.18.0", + "@lerna/symlink-dependencies": "3.17.0", + "p-map": "^2.1.0", + "slash": "^2.0.0" } }, "@lerna/list": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/list/-/list-3.13.3.tgz", - "integrity": "sha512-rLRDsBCkydMq2FL6WY1J/elvnXIjxxRtb72lfKHdvDEqVdquT5Qgt9ci42hwjmcocFwWcFJgF6BZozj5pbc13A==", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/@lerna/list/-/list-3.18.4.tgz", + "integrity": "sha512-bgtlhAwhjHOTLq0iIuPs30abeuLbwZvVB60Ym8kPp+chh939obKU3vy2KMyX+Gpxf8pzuQG+k986YXcUBvXVsw==", "dev": true, "requires": { - "@lerna/command": "3.13.3", - "@lerna/filter-options": "3.13.3", - "@lerna/listable": "3.13.0", + "@lerna/command": "3.18.0", + "@lerna/filter-options": "3.18.4", + "@lerna/listable": "3.18.4", "@lerna/output": "3.13.0" } }, "@lerna/listable": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/listable/-/listable-3.13.0.tgz", - "integrity": "sha512-liYJ/WBUYP4N4MnSVZuLUgfa/jy3BZ02/1Om7xUY09xGVSuNVNEeB8uZUMSC+nHqFHIsMPZ8QK9HnmZb1E/eTA==", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/@lerna/listable/-/listable-3.18.4.tgz", + "integrity": "sha512-EKSsnST5k3dZfw+UTwBH1/sHQ1YfgjYjGxXCabyn55mMgc2GjoDekODMYzZ1TNF2NNy6RgIZ24X2JI8G22nZUw==", "dev": true, "requires": { - "@lerna/batch-packages": "3.13.0", + "@lerna/query-graph": "3.18.0", "chalk": "^2.3.1", "columnify": "^1.5.4" } }, "@lerna/log-packed": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/log-packed/-/log-packed-3.13.0.tgz", - "integrity": "sha512-Rmjrcz+6aM6AEcEVWmurbo8+AnHOvYtDpoeMMJh9IZ9SmZr2ClXzmD7wSvjTQc8BwOaiWjjC/ukcT0UYA2m7wg==", + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/log-packed/-/log-packed-3.16.0.tgz", + "integrity": "sha512-Fp+McSNBV/P2mnLUYTaSlG8GSmpXM7krKWcllqElGxvAqv6chk2K3c2k80MeVB4WvJ9tRjUUf+i7HUTiQ9/ckQ==", "dev": true, "requires": { - "byte-size": "^4.0.3", + "byte-size": "^5.0.1", "columnify": "^1.5.4", "has-unicode": "^2.0.1", "npmlog": "^4.1.2" } }, "@lerna/npm-conf": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-3.13.0.tgz", - "integrity": "sha512-Jg2kANsGnhg+fbPEzE0X9nX5oviEAvWj0nYyOkcE+cgWuT7W0zpnPXC4hA4C5IPQGhwhhh0IxhWNNHtjTuw53g==", + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/npm-conf/-/npm-conf-3.16.0.tgz", + "integrity": "sha512-HbO3DUrTkCAn2iQ9+FF/eisDpWY5POQAOF1m7q//CZjdC2HSW3UYbKEGsSisFxSfaF9Z4jtrV+F/wX6qWs3CuA==", "dev": true, "requires": { "config-chain": "^1.1.11", - "pify": "^3.0.0" + "pify": "^4.0.1" }, "dependencies": { "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true } } }, "@lerna/npm-dist-tag": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-3.13.0.tgz", - "integrity": "sha512-mcuhw34JhSRFrbPn0vedbvgBTvveG52bR2lVE3M3tfE8gmR/cKS/EJFO4AUhfRKGCTFn9rjaSEzlFGYV87pemQ==", + "version": "3.18.1", + "resolved": "https://registry.npmjs.org/@lerna/npm-dist-tag/-/npm-dist-tag-3.18.1.tgz", + "integrity": "sha512-vWkZh2T/O9OjPLDrba0BTWO7ug/C3sCwjw7Qyk1aEbxMBXB/eEJPqirwJTWT+EtRJQYB01ky3K8ZFOhElVyjLw==", "dev": true, "requires": { + "@evocateur/npm-registry-fetch": "^4.0.0", + "@lerna/otplease": "3.16.0", "figgy-pudding": "^3.5.1", "npm-package-arg": "^6.1.0", - "npm-registry-fetch": "^3.9.0", "npmlog": "^4.1.2" } }, "@lerna/npm-install": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-3.13.3.tgz", - "integrity": "sha512-7Jig9MLpwAfcsdQ5UeanAjndChUjiTjTp50zJ+UZz4CbIBIDhoBehvNMTCL2G6pOEC7sGEg6sAqJINAqred6Tg==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/npm-install/-/npm-install-3.16.5.tgz", + "integrity": "sha512-hfiKk8Eku6rB9uApqsalHHTHY+mOrrHeWEs+gtg7+meQZMTS3kzv4oVp5cBZigndQr3knTLjwthT/FX4KvseFg==", "dev": true, "requires": { - "@lerna/child-process": "3.13.3", + "@lerna/child-process": "3.16.5", "@lerna/get-npm-exec-opts": "3.13.0", - "fs-extra": "^7.0.0", + "fs-extra": "^8.1.0", "npm-package-arg": "^6.1.0", "npmlog": "^4.1.2", "signal-exit": "^3.0.2", @@ -886,40 +1893,51 @@ } }, "@lerna/npm-publish": { - "version": "3.13.2", - "resolved": "https://registry.npmjs.org/@lerna/npm-publish/-/npm-publish-3.13.2.tgz", - "integrity": "sha512-HMucPyEYZfom5tRJL4GsKBRi47yvSS2ynMXYxL3kO0ie+j9J7cb0Ir8NmaAMEd3uJWJVFCPuQarehyfTDZsSxg==", + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/@lerna/npm-publish/-/npm-publish-3.16.2.tgz", + "integrity": "sha512-tGMb9vfTxP57vUV5svkBQxd5Tzc+imZbu9ZYf8Mtwe0+HYfDjNiiHLIQw7G95w4YRdc5KsCE8sQ0uSj+f2soIg==", "dev": true, "requires": { - "@lerna/run-lifecycle": "3.13.0", + "@evocateur/libnpmpublish": "^1.2.2", + "@lerna/otplease": "3.16.0", + "@lerna/run-lifecycle": "3.16.2", "figgy-pudding": "^3.5.1", - "fs-extra": "^7.0.0", - "libnpmpublish": "^1.1.1", + "fs-extra": "^8.1.0", "npm-package-arg": "^6.1.0", "npmlog": "^4.1.2", - "pify": "^3.0.0", + "pify": "^4.0.1", "read-package-json": "^2.0.13" }, "dependencies": { "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true } } }, "@lerna/npm-run-script": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-3.13.3.tgz", - "integrity": "sha512-qR4o9BFt5hI8Od5/DqLalOJydnKpiQFEeN0h9xZi7MwzuX1Ukwh3X22vqsX4YRbipIelSFtrDzleNVUm5jj0ow==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/npm-run-script/-/npm-run-script-3.16.5.tgz", + "integrity": "sha512-1asRi+LjmVn3pMjEdpqKJZFT/3ZNpb+VVeJMwrJaV/3DivdNg7XlPK9LTrORuKU4PSvhdEZvJmSlxCKyDpiXsQ==", "dev": true, "requires": { - "@lerna/child-process": "3.13.3", + "@lerna/child-process": "3.16.5", "@lerna/get-npm-exec-opts": "3.13.0", "npmlog": "^4.1.2" } }, + "@lerna/otplease": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/otplease/-/otplease-3.16.0.tgz", + "integrity": "sha512-uqZ15wYOHC+/V0WnD2iTLXARjvx3vNrpiIeyIvVlDB7rWse9mL4egex/QSgZ+lDx1OID7l2kgvcUD9cFpbqB7Q==", + "dev": true, + "requires": { + "@lerna/prompt": "3.13.0", + "figgy-pudding": "^3.5.1" + } + }, "@lerna/output": { "version": "3.13.0", "resolved": "https://registry.npmjs.org/@lerna/output/-/output-3.13.0.tgz", @@ -930,42 +1948,43 @@ } }, "@lerna/pack-directory": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/@lerna/pack-directory/-/pack-directory-3.13.1.tgz", - "integrity": "sha512-kXnyqrkQbCIZOf1054N88+8h0ItC7tUN5v9ca/aWpx298gsURpxUx/1TIKqijL5TOnHMyIkj0YJmnH/PyBVLKA==", + "version": "3.16.4", + "resolved": "https://registry.npmjs.org/@lerna/pack-directory/-/pack-directory-3.16.4.tgz", + "integrity": "sha512-uxSF0HZeGyKaaVHz5FroDY9A5NDDiCibrbYR6+khmrhZtY0Bgn6hWq8Gswl9iIlymA+VzCbshWIMX4o2O8C8ng==", "dev": true, "requires": { - "@lerna/get-packed": "3.13.0", - "@lerna/package": "3.13.0", - "@lerna/run-lifecycle": "3.13.0", + "@lerna/get-packed": "3.16.0", + "@lerna/package": "3.16.0", + "@lerna/run-lifecycle": "3.16.2", "figgy-pudding": "^3.5.1", - "npm-packlist": "^1.4.1", + "npm-packlist": "^1.4.4", "npmlog": "^4.1.2", - "tar": "^4.4.8", + "tar": "^4.4.10", "temp-write": "^3.4.0" } }, "@lerna/package": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/package/-/package-3.13.0.tgz", - "integrity": "sha512-kSKO0RJQy093BufCQnkhf1jB4kZnBvL7kK5Ewolhk5gwejN+Jofjd8DGRVUDUJfQ0CkW1o6GbUeZvs8w8VIZDg==", + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/package/-/package-3.16.0.tgz", + "integrity": "sha512-2lHBWpaxcBoiNVbtyLtPUuTYEaB/Z+eEqRS9duxpZs6D+mTTZMNy6/5vpEVSCBmzvdYpyqhqaYjjSLvjjr5Riw==", "dev": true, "requires": { - "load-json-file": "^4.0.0", + "load-json-file": "^5.3.0", "npm-package-arg": "^6.1.0", "write-pkg": "^3.1.0" }, "dependencies": { "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", + "graceful-fs": "^4.1.15", "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.0" } }, "parse-json": { @@ -979,54 +1998,82 @@ } }, "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true } } }, "@lerna/package-graph": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-3.13.0.tgz", - "integrity": "sha512-3mRF1zuqFE1HEFmMMAIggXy+f+9cvHhW/jzaPEVyrPNLKsyfJQtpTNzeI04nfRvbAh+Gd2aNksvaW/w3xGJnnw==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/package-graph/-/package-graph-3.18.0.tgz", + "integrity": "sha512-BLYDHO5ihPh20i3zoXfLZ5ZWDCrPuGANgVhl7k5pCmRj90LCvT+C7V3zrw70fErGAfvkcYepMqxD+oBrAYwquQ==", "dev": true, "requires": { + "@lerna/prerelease-id-from-version": "3.16.0", "@lerna/validation-error": "3.13.0", "npm-package-arg": "^6.1.0", - "semver": "^5.5.0" + "npmlog": "^4.1.2", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@lerna/prerelease-id-from-version": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-3.16.0.tgz", + "integrity": "sha512-qZyeUyrE59uOK8rKdGn7jQz+9uOpAaF/3hbslJVFL1NqF9ELDTqjCPXivuejMX/lN4OgD6BugTO4cR7UTq/sZA==", + "dev": true, + "requires": { + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "@lerna/project": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/@lerna/project/-/project-3.13.1.tgz", - "integrity": "sha512-/GoCrpsCCTyb9sizk1+pMBrIYchtb+F1uCOn3cjn9yenyG/MfYEnlfrbV5k/UDud0Ei75YBLbmwCbigHkAKazQ==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/project/-/project-3.18.0.tgz", + "integrity": "sha512-+LDwvdAp0BurOAWmeHE3uuticsq9hNxBI0+FMHiIai8jrygpJGahaQrBYWpwbshbQyVLeQgx3+YJdW2TbEdFWA==", "dev": true, "requires": { - "@lerna/package": "3.13.0", + "@lerna/package": "3.16.0", "@lerna/validation-error": "3.13.0", "cosmiconfig": "^5.1.0", "dedent": "^0.7.0", "dot-prop": "^4.2.0", - "glob-parent": "^3.1.0", - "globby": "^8.0.1", - "load-json-file": "^4.0.0", + "glob-parent": "^5.0.0", + "globby": "^9.2.0", + "load-json-file": "^5.3.0", "npmlog": "^4.1.2", - "p-map": "^1.2.0", + "p-map": "^2.1.0", "resolve-from": "^4.0.0", - "write-json-file": "^2.3.0" + "write-json-file": "^3.2.0" }, "dependencies": { "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", + "graceful-fs": "^4.1.15", "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.0" } }, "parse-json": { @@ -1040,9 +2087,9 @@ } }, "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true } } @@ -1058,42 +2105,50 @@ } }, "@lerna/publish": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/publish/-/publish-3.13.3.tgz", - "integrity": "sha512-Ni3pZKueIfgJJoL0OXfbAuWhGlJrDNwGx3CYWp2dbNqJmKD6uBZmsDtmeARKDp92oUK60W0drXCMydkIFFHMDQ==", - "dev": true, - "requires": { - "@lerna/batch-packages": "3.13.0", - "@lerna/check-working-tree": "3.13.3", - "@lerna/child-process": "3.13.3", - "@lerna/collect-updates": "3.13.3", - "@lerna/command": "3.13.3", - "@lerna/describe-ref": "3.13.3", - "@lerna/log-packed": "3.13.0", - "@lerna/npm-conf": "3.13.0", - "@lerna/npm-dist-tag": "3.13.0", - "@lerna/npm-publish": "3.13.2", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/@lerna/publish/-/publish-3.18.4.tgz", + "integrity": "sha512-Q+MqM5DUZvk+uT6hdEyO3khXET6LwED0YEuCu8fRwtHad03HkZ9i8PtTY5h8Sn6D6RCyCOlHTuf8O0KKAUy3ow==", + "dev": true, + "requires": { + "@evocateur/libnpmaccess": "^3.1.2", + "@evocateur/npm-registry-fetch": "^4.0.0", + "@evocateur/pacote": "^9.6.3", + "@lerna/check-working-tree": "3.16.5", + "@lerna/child-process": "3.16.5", + "@lerna/collect-updates": "3.18.0", + "@lerna/command": "3.18.0", + "@lerna/describe-ref": "3.16.5", + "@lerna/log-packed": "3.16.0", + "@lerna/npm-conf": "3.16.0", + "@lerna/npm-dist-tag": "3.18.1", + "@lerna/npm-publish": "3.16.2", + "@lerna/otplease": "3.16.0", "@lerna/output": "3.13.0", - "@lerna/pack-directory": "3.13.1", + "@lerna/pack-directory": "3.16.4", + "@lerna/prerelease-id-from-version": "3.16.0", "@lerna/prompt": "3.13.0", "@lerna/pulse-till-done": "3.13.0", - "@lerna/run-lifecycle": "3.13.0", - "@lerna/run-parallel-batches": "3.13.0", + "@lerna/run-lifecycle": "3.16.2", + "@lerna/run-topologically": "3.18.0", "@lerna/validation-error": "3.13.0", - "@lerna/version": "3.13.3", + "@lerna/version": "3.18.4", "figgy-pudding": "^3.5.1", - "fs-extra": "^7.0.0", - "libnpmaccess": "^3.0.1", + "fs-extra": "^8.1.0", "npm-package-arg": "^6.1.0", - "npm-registry-fetch": "^3.9.0", "npmlog": "^4.1.2", "p-finally": "^1.0.0", - "p-map": "^1.2.0", + "p-map": "^2.1.0", "p-pipe": "^1.2.0", - "p-reduce": "^1.0.0", - "pacote": "^9.5.0", - "semver": "^5.5.0" - } + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } }, "@lerna/pulse-till-done": { "version": "3.13.0", @@ -1104,92 +2159,102 @@ "npmlog": "^4.1.2" } }, + "@lerna/query-graph": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/query-graph/-/query-graph-3.18.0.tgz", + "integrity": "sha512-fgUhLx6V0jDuKZaKj562jkuuhrfVcjl5sscdfttJ8dXNVADfDz76nzzwLY0ZU7/0m69jDedohn5Fx5p7hDEVEg==", + "dev": true, + "requires": { + "@lerna/package-graph": "3.18.0", + "figgy-pudding": "^3.5.1" + } + }, "@lerna/resolve-symlink": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/resolve-symlink/-/resolve-symlink-3.13.0.tgz", - "integrity": "sha512-Lc0USSFxwDxUs5JvIisS8JegjA6SHSAWJCMvi2osZx6wVRkEDlWG2B1JAfXUzCMNfHoZX0/XX9iYZ+4JIpjAtg==", + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/@lerna/resolve-symlink/-/resolve-symlink-3.16.0.tgz", + "integrity": "sha512-Ibj5e7njVHNJ/NOqT4HlEgPFPtPLWsO7iu59AM5bJDcAJcR96mLZ7KGVIsS2tvaO7akMEJvt2P+ErwCdloG3jQ==", "dev": true, "requires": { - "fs-extra": "^7.0.0", + "fs-extra": "^8.1.0", "npmlog": "^4.1.2", "read-cmd-shim": "^1.0.1" } }, "@lerna/rimraf-dir": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-3.13.3.tgz", - "integrity": "sha512-d0T1Hxwu3gpYVv73ytSL+/Oy8JitsmvOYUR5ouRSABsmqS7ZZCh5t6FgVDDGVXeuhbw82+vuny1Og6Q0k4ilqw==", + "version": "3.16.5", + "resolved": "https://registry.npmjs.org/@lerna/rimraf-dir/-/rimraf-dir-3.16.5.tgz", + "integrity": "sha512-bQlKmO0pXUsXoF8lOLknhyQjOZsCc0bosQDoX4lujBXSWxHVTg1VxURtWf2lUjz/ACsJVDfvHZbDm8kyBk5okA==", "dev": true, "requires": { - "@lerna/child-process": "3.13.3", + "@lerna/child-process": "3.16.5", "npmlog": "^4.1.2", "path-exists": "^3.0.0", "rimraf": "^2.6.2" } }, "@lerna/run": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/run/-/run-3.13.3.tgz", - "integrity": "sha512-ygnLIfIYS6YY1JHWOM4CsdZiY8kTYPsDFOLAwASlRnlAXF9HiMT08GFXLmMHIblZJ8yJhsM2+QgraCB0WdxzOQ==", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/@lerna/run/-/run-3.18.4.tgz", + "integrity": "sha512-u2ZNO2fVk5kVEpbpn4DLJZZxZ08LFnIFuaXJMAhxvOgvm12ZF2rabA9kZc3NXp5+DedG5nHHgyoyLVVbStKzBA==", "dev": true, "requires": { - "@lerna/batch-packages": "3.13.0", - "@lerna/command": "3.13.3", - "@lerna/filter-options": "3.13.3", - "@lerna/npm-run-script": "3.13.3", + "@lerna/command": "3.18.0", + "@lerna/filter-options": "3.18.4", + "@lerna/npm-run-script": "3.16.5", "@lerna/output": "3.13.0", - "@lerna/run-parallel-batches": "3.13.0", + "@lerna/run-topologically": "3.18.0", "@lerna/timer": "3.13.0", "@lerna/validation-error": "3.13.0", - "p-map": "^1.2.0" + "p-map": "^2.1.0" } }, "@lerna/run-lifecycle": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-3.13.0.tgz", - "integrity": "sha512-oyiaL1biZdjpmjh6X/5C4w07wNFyiwXSSHH5GQB4Ay4BPwgq9oNhCcxRoi0UVZlZ1YwzSW8sTwLgj8emkIo3Yg==", + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/@lerna/run-lifecycle/-/run-lifecycle-3.16.2.tgz", + "integrity": "sha512-RqFoznE8rDpyyF0rOJy3+KjZCeTkO8y/OB9orPauR7G2xQ7PTdCpgo7EO6ZNdz3Al+k1BydClZz/j78gNCmL2A==", "dev": true, "requires": { - "@lerna/npm-conf": "3.13.0", + "@lerna/npm-conf": "3.16.0", "figgy-pudding": "^3.5.1", - "npm-lifecycle": "^2.1.0", + "npm-lifecycle": "^3.1.2", "npmlog": "^4.1.2" } }, - "@lerna/run-parallel-batches": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/run-parallel-batches/-/run-parallel-batches-3.13.0.tgz", - "integrity": "sha512-bICFBR+cYVF1FFW+Tlm0EhWDioTUTM6dOiVziDEGE1UZha1dFkMYqzqdSf4bQzfLS31UW/KBd/2z8jy2OIjEjg==", + "@lerna/run-topologically": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/run-topologically/-/run-topologically-3.18.0.tgz", + "integrity": "sha512-lrfEewwuUMC3ioxf9Z9NdHUakN6ihekcPfdYbzR2slmdbjYKmIA5srkWdrK8NwOpQCAuekpOovH2s8X3FGEopg==", "dev": true, "requires": { - "p-map": "^1.2.0", - "p-map-series": "^1.0.0" + "@lerna/query-graph": "3.18.0", + "figgy-pudding": "^3.5.1", + "p-queue": "^4.0.0" } }, "@lerna/symlink-binary": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-3.13.0.tgz", - "integrity": "sha512-obc4Y6jxywkdaCe+DB0uTxYqP0IQ8mFWvN+k/YMbwH4G2h7M7lCBWgPy8e7xw/50+1II9tT2sxgx+jMus1sTJg==", + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/@lerna/symlink-binary/-/symlink-binary-3.17.0.tgz", + "integrity": "sha512-RLpy9UY6+3nT5J+5jkM5MZyMmjNHxZIZvXLV+Q3MXrf7Eaa1hNqyynyj4RO95fxbS+EZc4XVSk25DGFQbcRNSQ==", "dev": true, "requires": { - "@lerna/create-symlink": "3.13.0", - "@lerna/package": "3.13.0", - "fs-extra": "^7.0.0", - "p-map": "^1.2.0" + "@lerna/create-symlink": "3.16.2", + "@lerna/package": "3.16.0", + "fs-extra": "^8.1.0", + "p-map": "^2.1.0" } }, "@lerna/symlink-dependencies": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-3.13.0.tgz", - "integrity": "sha512-7CyN5WYEPkbPLbqHBIQg/YiimBzb5cIGQB0E9IkLs3+racq2vmUNQZn38LOaazQacAA83seB+zWSxlI6H+eXSg==", + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/@lerna/symlink-dependencies/-/symlink-dependencies-3.17.0.tgz", + "integrity": "sha512-KmjU5YT1bpt6coOmdFueTJ7DFJL4H1w5eF8yAQ2zsGNTtZ+i5SGFBWpb9AQaw168dydc3s4eu0W0Sirda+F59Q==", "dev": true, "requires": { - "@lerna/create-symlink": "3.13.0", - "@lerna/resolve-symlink": "3.13.0", - "@lerna/symlink-binary": "3.13.0", - "fs-extra": "^7.0.0", + "@lerna/create-symlink": "3.16.2", + "@lerna/resolve-symlink": "3.16.0", + "@lerna/symlink-binary": "3.17.0", + "fs-extra": "^8.1.0", "p-finally": "^1.0.0", - "p-map": "^1.2.0", + "p-map": "^2.1.0", "p-map-series": "^1.0.0" } }, @@ -1209,39 +2274,72 @@ } }, "@lerna/version": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@lerna/version/-/version-3.13.3.tgz", - "integrity": "sha512-o/yQGAwDHmyu17wTj4Kat1/uDhjYFMeG+H0Y0HC4zJ4a/T6rEiXx7jJrnucPTmTQTDcUBoH/It5LrPYGOPsExA==", - "dev": true, - "requires": { - "@lerna/batch-packages": "3.13.0", - "@lerna/check-working-tree": "3.13.3", - "@lerna/child-process": "3.13.3", - "@lerna/collect-updates": "3.13.3", - "@lerna/command": "3.13.3", - "@lerna/conventional-commits": "3.13.0", - "@lerna/github-client": "3.13.3", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/@lerna/version/-/version-3.18.4.tgz", + "integrity": "sha512-+gR9H89qSP8iqzNi4tRVQUbWlFMOlhbY6+5TXkP72Ibb/z87O+C46DBqizSMVaPQYdSYjS1c9Xfa1oOhEWxGaw==", + "dev": true, + "requires": { + "@lerna/check-working-tree": "3.16.5", + "@lerna/child-process": "3.16.5", + "@lerna/collect-updates": "3.18.0", + "@lerna/command": "3.18.0", + "@lerna/conventional-commits": "3.16.4", + "@lerna/github-client": "3.16.5", + "@lerna/gitlab-client": "3.15.0", "@lerna/output": "3.13.0", + "@lerna/prerelease-id-from-version": "3.16.0", "@lerna/prompt": "3.13.0", - "@lerna/run-lifecycle": "3.13.0", + "@lerna/run-lifecycle": "3.16.2", + "@lerna/run-topologically": "3.18.0", "@lerna/validation-error": "3.13.0", "chalk": "^2.3.1", "dedent": "^0.7.0", + "load-json-file": "^5.3.0", "minimatch": "^3.0.4", "npmlog": "^4.1.2", - "p-map": "^1.2.0", + "p-map": "^2.1.0", "p-pipe": "^1.2.0", "p-reduce": "^1.0.0", "p-waterfall": "^1.0.0", - "semver": "^5.5.0", - "slash": "^1.0.0", - "temp-write": "^3.4.0" + "semver": "^6.2.0", + "slash": "^2.0.0", + "temp-write": "^3.4.0", + "write-json-file": "^3.2.0" }, "dependencies": { - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "load-json-file": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "parse-json": "^4.0.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } @@ -1273,69 +2371,122 @@ "dev": true }, "@octokit/endpoint": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-4.0.0.tgz", - "integrity": "sha512-b8sptNUekjREtCTJFpOfSIL4SKh65WaakcyxWzRcSPOk5RxkZJ/S8884NGZFxZ+jCB2rDURU66pSHn14cVgWVg==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.1.tgz", + "integrity": "sha512-nBFhRUb5YzVTCX/iAK1MgQ4uWo89Gu0TH00qQHoYRCsE12dWcG1OiLd7v2EIo2+tpUKPMOQ62QFy9hy9Vg2ULg==", "dev": true, "requires": { - "deepmerge": "3.2.0", - "is-plain-object": "^2.0.4", - "universal-user-agent": "^2.0.1", - "url-template": "^2.0.8" + "@octokit/types": "^2.0.0", + "is-plain-object": "^3.0.0", + "universal-user-agent": "^4.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", + "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", + "dev": true, + "requires": { + "isobject": "^4.0.0" + } + }, + "isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "dev": true + } } }, "@octokit/plugin-enterprise-rest": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-2.2.2.tgz", - "integrity": "sha512-CTZr64jZYhGWNTDGlSJ2mvIlFsm9OEO3LqWn9I/gmoHI4jRBp4kpHoFYNemG4oA75zUAcmbuWblb7jjP877YZw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-3.6.2.tgz", + "integrity": "sha512-3wF5eueS5OHQYuAEudkpN+xVeUsg8vYEMMenEzLphUZ7PRZ8OJtDcsreL3ad9zxXmBbaFWzLmFcdob5CLyZftA==", "dev": true }, "@octokit/request": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-3.0.0.tgz", - "integrity": "sha512-DZqmbm66tq+a9FtcKrn0sjrUpi0UaZ9QPUCxxyk/4CJ2rseTMpAWRf6gCwOSUCzZcx/4XVIsDk+kz5BVdaeenA==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.1.tgz", + "integrity": "sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg==", "dev": true, "requires": { - "@octokit/endpoint": "^4.0.0", - "deprecation": "^1.0.1", - "is-plain-object": "^2.0.4", + "@octokit/endpoint": "^5.5.0", + "@octokit/request-error": "^1.0.1", + "@octokit/types": "^2.0.0", + "deprecation": "^2.0.0", + "is-plain-object": "^3.0.0", "node-fetch": "^2.3.0", "once": "^1.4.0", - "universal-user-agent": "^2.0.1" + "universal-user-agent": "^4.0.0" }, "dependencies": { + "is-plain-object": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", + "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", + "dev": true, + "requires": { + "isobject": "^4.0.0" + } + }, + "isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "dev": true + }, "node-fetch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz", - "integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", "dev": true } } }, + "@octokit/request-error": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.0.tgz", + "integrity": "sha512-DNBhROBYjjV/I9n7A8kVkmQNkqFAMem90dSxqvPq57e2hBr7mNTX98y3R2zDpqMQHVRpBDjsvsfIGgBzy+4PAg==", + "dev": true, + "requires": { + "@octokit/types": "^2.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, "@octokit/rest": { - "version": "16.25.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.25.0.tgz", - "integrity": "sha512-QKIzP0gNYjyIGmY3Gpm3beof0WFwxFR+HhRZ+Wi0fYYhkEUvkJiXqKF56Pf5glzzfhEwOrggfluEld5F/ZxsKw==", + "version": "16.35.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.35.0.tgz", + "integrity": "sha512-9ShFqYWo0CLoGYhA1FdtdykJuMzS/9H6vSbbQWDX4pWr4p9v+15MsH/wpd/3fIU+tSxylaNO48+PIHqOkBRx3w==", "dev": true, "requires": { - "@octokit/request": "3.0.0", + "@octokit/request": "^5.2.0", + "@octokit/request-error": "^1.0.2", "atob-lite": "^2.0.0", - "before-after-hook": "^1.4.0", + "before-after-hook": "^2.0.0", "btoa-lite": "^1.0.0", - "deprecation": "^1.0.1", + "deprecation": "^2.0.0", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", "lodash.uniq": "^4.5.0", "octokit-pagination-methods": "^1.1.0", "once": "^1.4.0", - "universal-user-agent": "^2.0.0", - "url-template": "^2.0.8" + "universal-user-agent": "^4.0.0" + } + }, + "@octokit/types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.0.1.tgz", + "integrity": "sha512-YDYgV6nCzdGdOm7wy43Ce8SQ3M5DMKegB8E5sTB/1xrxOdo2yS/KgUgML2N2ZGD621mkbdrAglwTyA4NDOlFFA==", + "dev": true, + "requires": { + "@types/node": ">= 8" } }, "@types/babel__core": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.1.tgz", - "integrity": "sha512-+hjBtgcFPYyCTo0A15+nxrCVJL7aC6Acg87TXd5OW3QhHswdrOLoles+ldL2Uk8q++7yIfl4tURtztccdeeyOw==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz", + "integrity": "sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -1346,9 +2497,9 @@ } }, "@types/babel__generator": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz", - "integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.0.tgz", + "integrity": "sha512-c1mZUu4up5cp9KROs/QAw0gTeHrw/x7m52LcnvMxxOZ03DmLwPV0MlGmlgzV3cnSdjhJOZsj7E7FHeioai+egw==", "dev": true, "requires": { "@babel/types": "^7.0.0" @@ -1365,20 +2516,56 @@ } }, "@types/babel__traverse": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.6.tgz", - "integrity": "sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.7.tgz", + "integrity": "sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw==", "dev": true, "requires": { "@babel/types": "^7.3.0" } }, + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "dev": true + }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.0.tgz", "integrity": "sha512-eAtOAFZefEnfJiRFQBGw1eYqa5GTLCZ1y86N0XSI/D6EB+E8z6VPV/UL7Gi5UEclFqoQk+6NRqEDsfmDLXn8sg==", "dev": true }, + "@types/istanbul-lib-report": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", + "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", + "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, "@types/jest": { "version": "24.0.11", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.11.tgz", @@ -1394,6 +2581,12 @@ "integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==", "dev": true }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, "@types/node": { "version": "11.13.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.5.tgz", @@ -1421,6 +2614,12 @@ "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==", "dev": true }, + "@types/yargs-parser": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-13.1.0.tgz", + "integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==", + "dev": true + }, "@typescript-eslint/eslint-plugin": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.9.0.tgz", @@ -1467,6 +2666,17 @@ "semver": "5.5.0" } }, + "@zkochan/cmd-shim": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@zkochan/cmd-shim/-/cmd-shim-3.1.0.tgz", + "integrity": "sha512-o8l0+x7C7sMZU3v9GuJIAU10qQLtwR1dtRQIOmlNMtyaqhmpXOzx1HWiYoWfmmf9HHZoAkXpc9TM9PQYF9d4Jg==", + "dev": true, + "requires": { + "is-windows": "^1.0.0", + "mkdirp-promise": "^5.0.1", + "mz": "^2.5.0" + } + }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -1478,9 +2688,9 @@ } }, "abab": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", - "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.2.tgz", + "integrity": "sha512-2scffjvioEmNz0OyDSLGWDfKCVwaKc6l9Pm9kOIREU13ClXZvHpg/nRL5xyjSSSLhOnXqft2HpsAzNEEA8cFFg==", "dev": true }, "abbrev": { @@ -1496,9 +2706,9 @@ "dev": true }, "acorn-globals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.1.tgz", - "integrity": "sha512-gJSiKY8dBIjV/0jagZIFBdVMtfQyA5QHCvAT48H2q8REQoW8Fs5AOjqBql1LgSXgrMWdevcE+8cdZ33NtVbIBA==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", "dev": true, "requires": { "acorn": "^6.0.1", @@ -1512,15 +2722,15 @@ "dev": true }, "acorn-walk": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", - "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", "dev": true }, "agent-base": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "dev": true, "requires": { "es6-promisify": "^5.0.0" @@ -1568,6 +2778,12 @@ "color-convert": "^1.9.0" } }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -1578,15 +2794,6 @@ "normalize-path": "^2.1.1" } }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "dev": true, - "requires": { - "default-require-extensions": "^2.0.0" - } - }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -1641,9 +2848,9 @@ "dev": true }, "array-differ": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", - "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-2.1.0.tgz", + "integrity": "sha512-KbUpJgx909ZscOc/7CLATBFam7P1Z1QRQInvgT0UztM9Q72aGKCunKASAl7WNW0tnPmPyEMeMhdsfWhfmW037w==", "dev": true }, "array-equal": { @@ -1740,19 +2947,10 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", "dev": true }, "asynckit": { @@ -1821,18 +3019,216 @@ } }, "babel-jest": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.7.1.tgz", - "integrity": "sha512-GPnLqfk8Mtt0i4OemjWkChi73A3ALs4w2/QbG64uAj8b5mmwzxc7jbJVRZt8NJkxi6FopVHog9S3xX6UJKb2qg==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", "dev": true, "requires": { - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", "@types/babel__core": "^7.1.0", "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.6.0", + "babel-preset-jest": "^24.9.0", "chalk": "^2.4.2", "slash": "^2.0.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "babel-plugin-istanbul": { @@ -1892,22 +3288,22 @@ } }, "babel-plugin-jest-hoist": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", - "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", "dev": true, "requires": { "@types/babel__traverse": "^7.0.6" } }, "babel-preset-jest": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", - "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", "dev": true, "requires": { "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.6.0" + "babel-plugin-jest-hoist": "^24.9.0" } }, "balanced-match": { @@ -1981,24 +3377,15 @@ } }, "before-after-hook": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-1.4.0.tgz", - "integrity": "sha512-l5r9ir56nda3qu14nAXIlyq1MmUSs0meCIaFAh8HwkFwP1F8eToOuS3ah2VAHHcY04jaYD7FpJC5JTXHYRbkzg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", + "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==", "dev": true }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dev": true, - "requires": { - "inherits": "~2.0.0" - } - }, "bluebird": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", - "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", "dev": true }, "brace-expansion": { @@ -2106,31 +3493,48 @@ "dev": true }, "byte-size": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-4.0.4.tgz", - "integrity": "sha512-82RPeneC6nqCdSwCX2hZUz3JPOvN5at/nTEw/CMf05Smu3Hrpo9Psb7LjN+k+XndNArG1EY8L4+BM3aTM4BCvw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-5.0.1.tgz", + "integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw==", "dev": true }, "cacache": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", - "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", "dev": true, "requires": { - "bluebird": "^3.5.3", + "bluebird": "^3.5.5", "chownr": "^1.1.1", "figgy-pudding": "^3.5.1", - "glob": "^7.1.3", + "glob": "^7.1.4", "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", "lru-cache": "^5.1.1", "mississippi": "^3.0.0", "mkdirp": "^0.5.1", "move-concurrently": "^1.0.1", "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", + "rimraf": "^2.6.3", "ssri": "^6.0.1", "unique-filename": "^1.1.1", "y18n": "^4.0.0" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } } }, "cache-base": { @@ -2312,16 +3716,6 @@ "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", "dev": true }, - "cmd-shim": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-2.0.2.tgz", - "integrity": "sha1-b8vamUg6j9FdfTChlspp1oii79s=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "mkdirp": "~0.5.0" - } - }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -2422,12 +3816,6 @@ } } }, - "compare-versions": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz", - "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==", - "dev": true - }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -2536,9 +3924,9 @@ "dev": true }, "conventional-changelog-angular": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.3.tgz", - "integrity": "sha512-YD1xzH7r9yXQte/HF9JBuEDfvjxxwDGGwZU1+ndanbY0oFgA+Po1T9JDSpPLdP0pZT6MhCAsdvFKC4TJ4MTJTA==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.6.tgz", + "integrity": "sha512-QDEmLa+7qdhVIv8sFZfVxU1VSyVvnXPsxq8Vam49mKUcO1Z8VTLEJk9uI21uiJUsnmm0I4Hrsdc9TgkOQo9WSA==", "dev": true, "requires": { "compare-func": "^1.3.1", @@ -2546,24 +3934,24 @@ } }, "conventional-changelog-core": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-3.1.6.tgz", - "integrity": "sha512-5teTAZOtJ4HLR6384h50nPAaKdDr+IaU0rnD2Gg2C3MS7hKsEPH8pZxrDNqam9eOSPQg9tET6uZY79zzgSz+ig==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-3.2.3.tgz", + "integrity": "sha512-LMMX1JlxPIq/Ez5aYAYS5CpuwbOk6QFp8O4HLAcZxe3vxoCtABkhfjetk8IYdRB9CDQGwJFLR3Dr55Za6XKgUQ==", "dev": true, "requires": { - "conventional-changelog-writer": "^4.0.3", - "conventional-commits-parser": "^3.0.1", + "conventional-changelog-writer": "^4.0.6", + "conventional-commits-parser": "^3.0.3", "dateformat": "^3.0.0", "get-pkg-repo": "^1.0.0", "git-raw-commits": "2.0.0", "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^2.0.2", + "git-semver-tags": "^2.0.3", "lodash": "^4.2.1", "normalize-package-data": "^2.3.5", "q": "^1.5.1", "read-pkg": "^3.0.0", "read-pkg-up": "^3.0.0", - "through2": "^2.0.0" + "through2": "^3.0.0" }, "dependencies": { "load-json-file": { @@ -2623,70 +4011,107 @@ "find-up": "^2.0.0", "read-pkg": "^3.0.0" } - } + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } } }, "conventional-changelog-preset-loader": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.1.1.tgz", - "integrity": "sha512-K4avzGMLm5Xw0Ek/6eE3vdOXkqnpf9ydb68XYmCc16cJ99XMMbc2oaNMuPwAsxVK6CC1yA4/I90EhmWNj0Q6HA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.0.tgz", + "integrity": "sha512-/rHb32J2EJnEXeK4NpDgMaAVTFZS3o1ExmjKMtYVgIC4MQn0vkNSbYpdGRotkfGGRWiqk3Ri3FBkiZGbAfIfOQ==", "dev": true }, "conventional-changelog-writer": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.3.tgz", - "integrity": "sha512-bIlpSiQtQZ1+nDVHEEh798Erj2jhN/wEjyw9sfxY9es6h7pREE5BNJjfv0hXGH/FTrAsEpHUq4xzK99eePpwuA==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.10.tgz", + "integrity": "sha512-vtO9vBAVh7XnSpGLTB1BOGgsGTz1MdvFjzbSXLrtapWCHWwuVOZFgwdLhlS0MaXwlF1dksWdEb6tnr42Ie2INw==", "dev": true, "requires": { "compare-func": "^1.3.1", - "conventional-commits-filter": "^2.0.1", + "conventional-commits-filter": "^2.0.2", "dateformat": "^3.0.0", - "handlebars": "^4.1.0", + "handlebars": "^4.4.0", "json-stringify-safe": "^5.0.1", - "lodash": "^4.2.1", + "lodash": "^4.17.15", "meow": "^4.0.0", - "semver": "^5.5.0", + "semver": "^6.0.0", "split": "^1.0.0", - "through2": "^2.0.0" + "through2": "^3.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } } }, "conventional-commits-filter": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.1.tgz", - "integrity": "sha512-92OU8pz/977udhBjgPEbg3sbYzIxMDFTlQT97w7KdhR9igNqdJvy8smmedAAgn4tPiqseFloKkrVfbXCVd+E7A==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.2.tgz", + "integrity": "sha512-WpGKsMeXfs21m1zIw4s9H5sys2+9JccTzpN6toXtxhpw2VNF2JUXwIakthKBy+LN4DvJm+TzWhxOMWOs1OFCFQ==", "dev": true, "requires": { - "is-subset": "^0.1.1", + "lodash.ismatch": "^4.4.0", "modify-values": "^1.0.0" } }, "conventional-commits-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.1.tgz", - "integrity": "sha512-P6U5UOvDeidUJ8ebHVDIoXzI7gMlQ1OF/id6oUvp8cnZvOXMt1n8nYl74Ey9YMn0uVQtxmCtjPQawpsssBWtGg==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.7.tgz", + "integrity": "sha512-4mx/FRC92z0yIiXGyRVYQFhn0jWDwvxnj2UuLaUi3hJSG4Thall6GXA8YOPHQK2qvotciJandJIVmuSvLgDLbQ==", "dev": true, "requires": { "JSONStream": "^1.0.4", - "is-text-path": "^1.0.0", - "lodash": "^4.2.1", + "is-text-path": "^1.0.1", + "lodash": "^4.17.15", "meow": "^4.0.0", "split2": "^2.0.0", - "through2": "^2.0.0", + "through2": "^3.0.0", "trim-off-newlines": "^1.0.0" + }, + "dependencies": { + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } } }, "conventional-recommended-bump": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-4.1.1.tgz", - "integrity": "sha512-JT2vKfSP9kR18RXXf55BRY1O3AHG8FPg5btP3l7LYfcWJsiXI6MCf30DepQ98E8Qhowvgv7a8iev0J1bEDkTFA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-5.0.1.tgz", + "integrity": "sha512-RVdt0elRcCxL90IrNP0fYCpq1uGt2MALko0eyeQ+zQuDVWtMGAy9ng6yYn3kax42lCj9+XBxQ8ZN6S9bdKxDhQ==", "dev": true, "requires": { "concat-stream": "^2.0.0", "conventional-changelog-preset-loader": "^2.1.1", "conventional-commits-filter": "^2.0.2", - "conventional-commits-parser": "^3.0.2", + "conventional-commits-parser": "^3.0.3", "git-raw-commits": "2.0.0", - "git-semver-tags": "^2.0.2", + "git-semver-tags": "^2.0.3", "meow": "^4.0.0", "q": "^1.5.1" }, @@ -2703,50 +4128,16 @@ "typedarray": "^0.0.6" } }, - "conventional-commits-filter": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.2.tgz", - "integrity": "sha512-WpGKsMeXfs21m1zIw4s9H5sys2+9JccTzpN6toXtxhpw2VNF2JUXwIakthKBy+LN4DvJm+TzWhxOMWOs1OFCFQ==", - "dev": true, - "requires": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" - } - }, - "conventional-commits-parser": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.2.tgz", - "integrity": "sha512-y5eqgaKR0F6xsBNVSQ/5cI5qIF3MojddSUi1vKIggRkqUTbkqFKH9P5YX/AT1BVZp9DtSzBTIkvjyVLotLsVog==", - "dev": true, - "requires": { - "JSONStream": "^1.0.4", - "is-text-path": "^1.0.0", - "lodash": "^4.2.1", - "meow": "^4.0.0", - "split2": "^2.0.0", - "through2": "^3.0.0", - "trim-off-newlines": "^1.0.0" - } - }, "readable-stream": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", - "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } - }, - "through2": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", - "dev": true, - "requires": { - "readable-stream": "2 || 3" - } } } }, @@ -2786,14 +4177,14 @@ "dev": true }, "cosmiconfig": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.0.tgz", - "integrity": "sha512-nxt+Nfc3JAqf4WIWd0jXLjTJZmsPLrA9DDc4nRw2KFJQJK7DNooqSXrNI7tzLG50CF8axczly5UV929tBmh/7g==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", "dev": true, "requires": { "import-fresh": "^2.0.0", "is-directory": "^0.3.1", - "js-yaml": "^3.13.0", + "js-yaml": "^3.13.1", "parse-json": "^4.0.0" }, "dependencies": { @@ -2849,15 +4240,15 @@ } }, "cssom": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", - "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", "dev": true }, "cssstyle": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", - "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", "dev": true, "requires": { "cssom": "0.3.x" @@ -2873,9 +4264,9 @@ } }, "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", "dev": true }, "damerau-levenshtein": { @@ -2914,9 +4305,9 @@ }, "dependencies": { "whatwg-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", - "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", "dev": true, "requires": { "lodash.sortby": "^4.7.0", @@ -2995,21 +4386,6 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "deepmerge": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.2.0.tgz", - "integrity": "sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow==", - "dev": true - }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", - "dev": true, - "requires": { - "strip-bom": "^3.0.0" - } - }, "defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", @@ -3082,9 +4458,9 @@ "dev": true }, "deprecation": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-1.0.1.tgz", - "integrity": "sha512-ccVHpE72+tcIKaGMql33x5MAjKQIZrk+3x2GbJ7TeraUCZWHoT+KSZpoC+JQFsUBlSTXUrBaGiF0j6zVTepPLg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", "dev": true }, "detect-indent": { @@ -3116,12 +4492,11 @@ "dev": true }, "dir-glob": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", - "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", "dev": true, "requires": { - "arrify": "^1.0.1", "path-type": "^3.0.0" }, "dependencies": { @@ -3221,6 +4596,12 @@ "once": "^1.4.0" } }, + "env-paths": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", + "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=", + "dev": true + }, "err-code": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", @@ -3262,9 +4643,9 @@ } }, "es6-promise": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", - "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", "dev": true }, "es6-promisify": { @@ -3283,9 +4664,9 @@ "dev": true }, "escodegen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", - "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", + "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", "dev": true, "requires": { "esprima": "^3.1.3", @@ -3697,6 +5078,12 @@ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", + "dev": true + }, "exec-sh": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", @@ -3910,9 +5297,9 @@ "dev": true }, "fast-glob": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.6.tgz", - "integrity": "sha512-0BvMaZc1k9F+MeWWMe8pL6YltFzZYcJsYU7D4JyDA6PAczaXvxqQQ/z+mDF7/4Mw01DeUc+i3CTKajnkANkV4w==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", "dev": true, "requires": { "@mrmlnc/readdir-enhanced": "^2.2.1", @@ -3923,13 +5310,25 @@ "micromatch": "^3.1.10" }, "dependencies": { - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-extglob": "^2.1.1" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } } } } @@ -3979,16 +5378,6 @@ "flat-cache": "^2.0.1" } }, - "fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true, - "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" - } - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -4091,14 +5480,22 @@ } }, "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "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": { - "graceful-fs": "^4.1.2", + "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + } } }, "fs-minipass": { @@ -4649,18 +6046,6 @@ } } }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - } - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -4913,9 +6298,9 @@ } }, "get-port": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", - "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", + "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==", "dev": true }, "get-stdin": { @@ -4972,13 +6357,21 @@ } }, "git-semver-tags": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.2.tgz", - "integrity": "sha512-34lMF7Yo1xEmsK2EkbArdoU79umpvm0MfzaDkSNYSJqtM5QLAVTPWgpiXSVI5o/O9EvZPSrP4Zvnec/CqhSd5w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.3.tgz", + "integrity": "sha512-tj4FD4ww2RX2ae//jSrXZzrocla9db5h0V7ikPl1P/WwoZar9epdUhwR7XHXSgc+ZkNq72BEEerqQuicoEQfzA==", "dev": true, "requires": { "meow": "^4.0.0", - "semver": "^5.5.0" + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "git-up": { @@ -5024,13 +6417,12 @@ } }, "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", + "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", "dev": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "is-glob": "^4.0.1" } }, "glob-to-regexp": { @@ -5046,36 +6438,25 @@ "dev": true }, "globby": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz", - "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", + "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", "dev": true, "requires": { - "array-union": "^1.0.1", - "dir-glob": "2.0.0", - "fast-glob": "^2.0.2", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" + "@types/glob": "^7.1.1", + "array-union": "^1.0.2", + "dir-glob": "^2.2.2", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" }, "dependencies": { - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true } } @@ -5134,9 +6515,9 @@ "dev": true }, "handlebars": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", - "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz", + "integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==", "dev": true, "requires": { "neo-async": "^2.6.0", @@ -5288,12 +6669,12 @@ } }, "https-proxy-agent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", - "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "dev": true, "requires": { - "agent-base": "^4.1.0", + "agent-base": "^4.3.0", "debug": "^3.1.0" }, "dependencies": { @@ -5339,9 +6720,9 @@ "dev": true }, "ignore-walk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", "dev": true, "requires": { "minimatch": "^3.0.4" @@ -5387,9 +6768,9 @@ } }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { "p-try": "^2.0.0" @@ -5433,6 +6814,12 @@ "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", "dev": true }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -5662,12 +7049,12 @@ "dev": true }, "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "^2.1.1" } }, "is-number": { @@ -5741,12 +7128,6 @@ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, - "is-subset": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", - "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", - "dev": true - }, "is-symbol": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", @@ -5813,42 +7194,12 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, - "istanbul-api": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.5.tgz", - "integrity": "sha512-meYk1BwDp59Pfse1TvPrkKYgVqAufbdBLEVoqvu/hLLKSaQ054ZTksbNepyc223tMnWdm6AdK2URIJJRqdP87g==", - "dev": true, - "requires": { - "async": "^2.6.1", - "compare-versions": "^3.2.1", - "fileset": "^2.0.3", - "istanbul-lib-coverage": "^2.0.4", - "istanbul-lib-hook": "^2.0.6", - "istanbul-lib-instrument": "^3.2.0", - "istanbul-lib-report": "^2.0.7", - "istanbul-lib-source-maps": "^3.0.5", - "istanbul-reports": "^2.2.3", - "js-yaml": "^3.13.0", - "make-dir": "^2.1.0", - "minimatch": "^3.0.4", - "once": "^1.4.0" - } - }, "istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", "integrity": "sha512-LXTBICkMARVgo579kWDm8SqfB6nvSDKNqIOBEjmJRnL04JvoMHCYGWaMddQnseJYtkEuEvO/sIcOxPLk9gERug==", "dev": true }, - "istanbul-lib-hook": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.6.tgz", - "integrity": "sha512-829DKONApZ7UCiPXcOYWSgkFXa4+vNYoNOt3F+4uDJLKL1OotAoVwvThoEj1i8jmOj7odbYcR3rnaHu+QroaXg==", - "dev": true, - "requires": { - "append-transform": "^1.0.0" - } - }, "istanbul-lib-instrument": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.2.0.tgz", @@ -5873,16 +7224,22 @@ } }, "istanbul-lib-report": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.7.tgz", - "integrity": "sha512-wLH6beJBFbRBLiTlMOBxmb85cnVM1Vyl36N48e4e/aTKSM3WbOx7zbVIH1SQ537fhhsPbX0/C5JB4qsmyRXXyA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", "dev": true, "requires": { - "istanbul-lib-coverage": "^2.0.4", + "istanbul-lib-coverage": "^2.0.5", "make-dir": "^2.1.0", - "supports-color": "^6.0.0" + "supports-color": "^6.1.0" }, "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", @@ -5895,18 +7252,24 @@ } }, "istanbul-lib-source-maps": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.5.tgz", - "integrity": "sha512-eDhZ7r6r1d1zQPVZehLc3D0K14vRba/eBYkz3rw16DLOrrTzve9RmnkcwrrkWVgO1FL3EK5knujVe5S8QHE9xw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", "dev": true, "requires": { "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.4", + "istanbul-lib-coverage": "^2.0.5", "make-dir": "^2.1.0", - "rimraf": "^2.6.2", + "rimraf": "^2.6.3", "source-map": "^0.6.1" }, "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -5916,12 +7279,12 @@ } }, "istanbul-reports": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.3.tgz", - "integrity": "sha512-T6EbPuc8Cb620LWAYyZ4D8SSn06dY9i1+IgUX2lTH8gbwflMc9Obd33zHTyNX653ybjpamAHS9toKS3E6cGhTw==", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", + "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", "dev": true, "requires": { - "handlebars": "^4.1.0" + "handlebars": "^4.1.2" } }, "iterall": { @@ -5931,57 +7294,319 @@ "dev": true }, "jest": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-24.7.1.tgz", - "integrity": "sha512-AbvRar5r++izmqo5gdbAjTeA6uNRGoNRuj5vHB0OnDXo2DXWZJVuaObiGgtlvhKb+cWy2oYbQSfxv7Q7GjnAtA==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", + "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", "dev": true, "requires": { "import-local": "^2.0.0", - "jest-cli": "^24.7.1" + "jest-cli": "^24.9.0" }, "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, "jest-cli": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.7.1.tgz", - "integrity": "sha512-32OBoSCVPzcTslGFl6yVCMzB2SqX3IrWwZCY5mZYkb0D2WsogmU3eV2o8z7+gRQa4o4sZPX/k7GU+II7CxM6WQ==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", + "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", "dev": true, "requires": { - "@jest/core": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/core": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", "chalk": "^2.0.1", "exit": "^0.1.2", "import-local": "^2.0.0", "is-ci": "^2.0.0", - "jest-config": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", + "jest-config": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", "prompts": "^2.0.1", "realpath-native": "^1.1.0", - "yargs": "^12.0.2" + "yargs": "^13.3.0" } - } - } - }, - "jest-changed-files": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.7.0.tgz", - "integrity": "sha512-33BgewurnwSfJrW7T5/ZAXGE44o7swLslwh8aUckzq2e17/2Os1V0QU506ZNik3hjs8MgnEMKNkcud442NCDTw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "execa": "^1.0.0", - "throat": "^4.0.0" - } - }, - "jest-circus": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-24.7.1.tgz", - "integrity": "sha512-zDNSS+7qQN0nSbR77qcOb+tOUWLcvZGzvXE1PjoV6xeHV5Vz7DPK9JkBIQF/n5Nz9yZbApDxq5NqGqmVCe0nnQ==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.7.1", + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "jest-changed-files": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", + "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + } + } + }, + "jest-circus": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-24.7.1.tgz", + "integrity": "sha512-zDNSS+7qQN0nSbR77qcOb+tOUWLcvZGzvXE1PjoV6xeHV5Vz7DPK9JkBIQF/n5Nz9yZbApDxq5NqGqmVCe0nnQ==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.7.1", "@jest/test-result": "^24.7.1", "@jest/types": "^24.7.0", "chalk": "^2.0.1", @@ -5999,28 +7624,182 @@ } }, "jest-config": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.7.1.tgz", - "integrity": "sha512-8FlJNLI+X+MU37j7j8RE4DnJkvAghXmBWdArVzypW6WxfGuxiL/CCkzBg0gHtXhD2rxla3IMOSUAHylSKYJ83g==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.7.1", - "@jest/types": "^24.7.0", - "babel-jest": "^24.7.1", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", "chalk": "^2.0.1", "glob": "^7.1.1", - "jest-environment-jsdom": "^24.7.1", - "jest-environment-node": "^24.7.1", - "jest-get-type": "^24.3.0", - "jest-jasmine2": "^24.7.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", "micromatch": "^3.1.10", - "pretty-format": "^24.7.0", + "pretty-format": "^24.9.0", "realpath-native": "^1.1.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "jest-diff": { @@ -6036,9 +7815,9 @@ } }, "jest-docblock": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.3.0.tgz", - "integrity": "sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", + "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", "dev": true, "requires": { "detect-newline": "^2.1.0" @@ -6058,89 +7837,887 @@ } }, "jest-environment-jsdom": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.7.1.tgz", - "integrity": "sha512-Gnhb+RqE2JuQGb3kJsLF8vfqjt3PHKSstq4Xc8ic+ax7QKo4Z0RWGucU3YV+DwKR3T9SYc+3YCUQEJs8r7+Jxg==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", "dev": true, "requires": { - "@jest/environment": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0", - "jest-util": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", "jsdom": "^11.5.1" - } - }, - "jest-environment-node": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.7.1.tgz", - "integrity": "sha512-GJJQt1p9/C6aj6yNZMvovZuxTUd+BEJprETdvTKSb4kHcw4mFj8777USQV0FJoJ4V3djpOwA5eWyPwfq//PFBA==", - "dev": true, - "requires": { - "@jest/environment": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0", - "jest-util": "^24.7.1" - } - }, - "jest-get-type": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", - "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", - "dev": true - }, - "jest-haste-map": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.7.1.tgz", - "integrity": "sha512-g0tWkzjpHD2qa03mTKhlydbmmYiA2KdcJe762SbfFo/7NIMgBWAA0XqQlApPwkWOF7Cxoi/gUqL0i6DIoLpMBw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.4.0", - "jest-util": "^24.7.1", - "jest-worker": "^24.6.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" - } - }, - "jest-jasmine2": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.7.1.tgz", - "integrity": "sha512-Y/9AOJDV1XS44wNwCaThq4Pw3gBPiOv/s6NcbOAkVRRUEPu+36L2xoPsqQXsDrxoBerqeyslpn2TpCI8Zr6J2w==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^24.7.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^24.7.1", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "pretty-format": "^24.7.0", - "throat": "^4.0.0" - } - }, - "jest-leak-detector": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.7.0.tgz", - "integrity": "sha512-zV0qHKZGXtmPVVzT99CVEcHE9XDf+8LwiE0Ob7jjezERiGVljmqKFWpV2IkG+rkFIEUHFEkMiICu7wnoPM/RoQ==", - "dev": true, - "requires": { - "pretty-format": "^24.7.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/environment": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "dev": true, + "requires": { + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-environment-node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/environment": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "dev": true, + "requires": { + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-get-type": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", + "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", + "dev": true + }, + "jest-haste-map": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.7.1.tgz", + "integrity": "sha512-g0tWkzjpHD2qa03mTKhlydbmmYiA2KdcJe762SbfFo/7NIMgBWAA0XqQlApPwkWOF7Cxoi/gUqL0i6DIoLpMBw==", + "dev": true, + "requires": { + "@jest/types": "^24.7.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.4.0", + "jest-util": "^24.7.1", + "jest-worker": "^24.6.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.9.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", + "throat": "^4.0.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/environment": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "dev": true, + "requires": { + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "dev": true + }, + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + } + }, + "jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "jest-leak-detector": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", + "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "dev": true, + "requires": { + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + }, + "dependencies": { + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + } + } } }, "jest-matcher-utils": { @@ -6206,72 +8783,978 @@ } }, "jest-resolve-dependencies": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.7.1.tgz", - "integrity": "sha512-2Eyh5LJB2liNzfk4eo7bD1ZyBbqEJIyyrFtZG555cSWW9xVHxII2NuOkSl1yUYTAYCAmM2f2aIT5A7HzNmubyg==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", + "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", "dev": true, "requires": { - "@jest/types": "^24.7.0", + "@jest/types": "^24.9.0", "jest-regex-util": "^24.3.0", - "jest-snapshot": "^24.7.1" + "jest-snapshot": "^24.9.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "dev": true + }, + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + }, + "dependencies": { + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + } + } + }, + "jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + } + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "jest-runner": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.7.1.tgz", - "integrity": "sha512-aNFc9liWU/xt+G9pobdKZ4qTeG/wnJrJna3VqunziDNsWT3EBpmxXZRBMKCsNMyfy+A/XHiV+tsMLufdsNdgCw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", + "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", "dev": true, "requires": { "@jest/console": "^24.7.1", - "@jest/environment": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", "chalk": "^2.4.2", "exit": "^0.1.2", "graceful-fs": "^4.1.15", - "jest-config": "^24.7.1", + "jest-config": "^24.9.0", "jest-docblock": "^24.3.0", - "jest-haste-map": "^24.7.1", - "jest-jasmine2": "^24.7.1", - "jest-leak-detector": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-resolve": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-util": "^24.7.1", + "jest-haste-map": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-leak-detector": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", "jest-worker": "^24.6.0", "source-map-support": "^0.5.6", "throat": "^4.0.0" + }, + "dependencies": { + "@jest/environment": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "dev": true, + "requires": { + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + } + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "dependencies": { + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + } + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + } + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "jest-runtime": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.7.1.tgz", - "integrity": "sha512-0VAbyBy7tll3R+82IPJpf6QZkokzXPIS71aDeqh+WzPRXRCNz6StQ45otFariPdJ4FmXpDiArdhZrzNAC3sj6A==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", "dev": true, "requires": { "@jest/console": "^24.7.1", - "@jest/environment": "^24.7.1", + "@jest/environment": "^24.9.0", "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/yargs": "^12.0.2", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", "chalk": "^2.0.1", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.1.15", - "jest-config": "^24.7.1", - "jest-haste-map": "^24.7.1", - "jest-message-util": "^24.7.1", - "jest-mock": "^24.7.0", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", "realpath-native": "^1.1.0", "slash": "^2.0.0", "strip-bom": "^3.0.0", - "yargs": "^12.0.2" + "yargs": "^13.3.0" + }, + "dependencies": { + "@jest/environment": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "dev": true, + "requires": { + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + } + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + }, + "dependencies": { + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + } + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "dev": true + }, + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + }, + "dependencies": { + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + } + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } } }, "jest-serializer": { @@ -6329,32 +9812,195 @@ } }, "jest-validate": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.7.0.tgz", - "integrity": "sha512-cgai/gts9B2chz1rqVdmLhzYxQbgQurh1PEQSvSgPZ8KGa1AqXsqC45W5wKEwzxKrWqypuQrQxnF4+G9VejJJA==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", "dev": true, "requires": { - "@jest/types": "^24.7.0", - "camelcase": "^5.0.0", + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", - "leven": "^2.1.0", - "pretty-format": "^24.7.0" + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" + }, + "dependencies": { + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + } + } } }, "jest-watcher": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.7.1.tgz", - "integrity": "sha512-Wd6TepHLRHVKLNPacEsBwlp9raeBIO+01xrN24Dek4ggTS8HHnOzYSFnvp+6MtkkJ3KfMzy220KTi95e2rRkrw==", + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", + "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", "dev": true, "requires": { - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/yargs": "^12.0.9", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", "ansi-escapes": "^3.0.0", "chalk": "^2.0.1", - "jest-util": "^24.7.1", + "jest-util": "^24.9.0", "string-length": "^2.0.0" + }, + "dependencies": { + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "jest-worker": { @@ -6559,107 +10205,44 @@ "dev": true }, "lerna": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/lerna/-/lerna-3.13.3.tgz", - "integrity": "sha512-0TkG40F02A4wjKraJBztPtj87BjUezFmaZKAha8eLdtngZkSpAdrSANa5K7jnnA8mywmpQwrKJuBmjdNpm9cBw==", - "dev": true, - "requires": { - "@lerna/add": "3.13.3", - "@lerna/bootstrap": "3.13.3", - "@lerna/changed": "3.13.3", - "@lerna/clean": "3.13.3", - "@lerna/cli": "3.13.0", - "@lerna/create": "3.13.3", - "@lerna/diff": "3.13.3", - "@lerna/exec": "3.13.3", - "@lerna/import": "3.13.3", - "@lerna/init": "3.13.3", - "@lerna/link": "3.13.3", - "@lerna/list": "3.13.3", - "@lerna/publish": "3.13.3", - "@lerna/run": "3.13.3", - "@lerna/version": "3.13.3", - "import-local": "^1.0.0", + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/lerna/-/lerna-3.18.4.tgz", + "integrity": "sha512-DiU53cvMxaU07Bj2HwBwUQ2O3c/ORNq/QwKj1vGJH4vSkZSTUxPryp2baSNlt8PmnLNXOVpw0vOTRkEF+6n/cA==", + "dev": true, + "requires": { + "@lerna/add": "3.18.4", + "@lerna/bootstrap": "3.18.4", + "@lerna/changed": "3.18.4", + "@lerna/clean": "3.18.4", + "@lerna/cli": "3.18.0", + "@lerna/create": "3.18.0", + "@lerna/diff": "3.18.0", + "@lerna/exec": "3.18.4", + "@lerna/import": "3.18.0", + "@lerna/init": "3.18.0", + "@lerna/link": "3.18.0", + "@lerna/list": "3.18.4", + "@lerna/publish": "3.18.4", + "@lerna/run": "3.18.4", + "@lerna/version": "3.18.4", + "import-local": "^2.0.0", "npmlog": "^4.1.2" - }, - "dependencies": { - "import-local": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", - "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", - "dev": true, - "requires": { - "pkg-dir": "^2.0.0", - "resolve-cwd": "^2.0.0" - } - } } }, "leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "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" - } - }, - "libnpmaccess": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-3.0.1.tgz", - "integrity": "sha512-RlZ7PNarCBt+XbnP7R6PoVgOq9t+kou5rvhaInoNibhPO7eMlRfS0B8yjatgn2yaHIwWNyoJDolC/6Lc5L/IQA==", - "dev": true, - "requires": { - "aproba": "^2.0.0", - "get-stream": "^4.0.0", - "npm-package-arg": "^6.1.0", - "npm-registry-fetch": "^3.8.0" - }, - "dependencies": { - "aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true - } - } - }, - "libnpmpublish": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-1.1.1.tgz", - "integrity": "sha512-nefbvJd/wY38zdt+b9SHL6171vqBrMtZ56Gsgfd0duEKb/pB8rDT4/ObUQLrHz1tOfht1flt2zM+UGaemzAG5g==", - "dev": true, - "requires": { - "aproba": "^2.0.0", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.0.0", - "lodash.clonedeep": "^4.5.0", - "normalize-package-data": "^2.4.0", - "npm-package-arg": "^6.1.0", - "npm-registry-fetch": "^3.8.0", - "semver": "^5.5.1", - "ssri": "^6.0.1" - }, - "dependencies": { - "aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true - }, - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - } + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" } }, "load-json-file": { @@ -6737,12 +10320,12 @@ } }, "lodash.templatesettings": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", - "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", "dev": true, "requires": { - "lodash._reinterpolate": "~3.0.0" + "lodash._reinterpolate": "^3.0.0" } }, "lodash.unescape": { @@ -6786,9 +10369,9 @@ } }, "macos-release": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.2.0.tgz", - "integrity": "sha512-iV2IDxZaX8dIcM7fG6cI46uNmHUxHE4yN+Z8tKHAW1TBPMZDIKHf/3L+YnOuj/FK9il14UaVdHmiQ1tsi90ltA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", + "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==", "dev": true }, "make-dir": { @@ -6808,9 +10391,9 @@ "dev": true }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true } } @@ -6822,40 +10405,22 @@ "dev": true }, "make-fetch-happen": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz", - "integrity": "sha512-7R5ivfy9ilRJ1EMKIOziwrns9fGeAD4bAha8EB7BIiBBLHm2KeTUGCrICFt2rbHfzheTLynv50GnNTK1zDTrcQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.1.tgz", + "integrity": "sha512-b4dfaMvUDR67zxUq1+GN7Ke9rH5WvGRmoHuMH7l+gmUCR2tCXFP6mpeJ9Dp+jB6z8mShRopSf1vLRBhRs8Cu5w==", "dev": true, "requires": { "agentkeepalive": "^3.4.1", - "cacache": "^11.0.1", + "cacache": "^12.0.0", "http-cache-semantics": "^3.8.1", "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "lru-cache": "^4.1.2", + "https-proxy-agent": "^2.2.3", + "lru-cache": "^5.1.1", "mississippi": "^3.0.0", "node-fetch-npm": "^2.0.2", "promise-retry": "^1.1.1", "socks-proxy-agent": "^4.0.0", "ssri": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } } }, "makeerror": { @@ -7009,9 +10574,9 @@ } }, "merge2": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.3.tgz", - "integrity": "sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", "dev": true }, "micromatch": { @@ -7148,6 +10713,15 @@ "minimist": "0.0.8" } }, + "mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", + "dev": true, + "requires": { + "mkdirp": "*" + } + }, "modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -7175,15 +10749,15 @@ "dev": true }, "multimatch": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", - "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-3.0.0.tgz", + "integrity": "sha512-22foS/gqQfANZ3o+W7ST2x25ueHDVNWl/b9OlGcLpy/iKxjCpvcNCM51YCenUi7Mt/jAjjqv8JwZRs8YP5sRjA==", "dev": true, "requires": { - "array-differ": "^1.0.0", - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "minimatch": "^3.0.0" + "array-differ": "^2.0.3", + "array-union": "^1.0.2", + "arrify": "^1.0.1", + "minimatch": "^3.0.4" } }, "mute-stream": { @@ -7192,6 +10766,17 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "nan": { "version": "2.13.2", "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", @@ -7225,9 +10810,9 @@ "dev": true }, "neo-async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", - "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", "dev": true }, "nice-try": { @@ -7254,25 +10839,34 @@ } }, "node-gyp": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", - "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.0.5.tgz", + "integrity": "sha512-WABl9s4/mqQdZneZHVWVG4TVr6QQJZUC6PAx47ITSk9lreZ1n+7Z9mMAIbA3vnO4J9W20P7LhCxtzfWsAD/KDw==", "dev": true, "requires": { - "fstream": "^1.0.0", + "env-paths": "^1.0.0", "glob": "^7.0.3", "graceful-fs": "^4.1.2", "mkdirp": "^0.5.0", "nopt": "2 || 3", "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", "request": "^2.87.0", "rimraf": "2", "semver": "~5.3.0", - "tar": "^2.0.0", + "tar": "^4.4.12", "which": "1" }, "dependencies": { + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", @@ -7280,28 +10874,18 @@ "dev": true }, "tar": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", - "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "dev": true, "requires": { - "block-stream": "*", - "fstream": "^1.0.12", - "inherits": "2" - }, - "dependencies": { - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - } - } + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" } } } @@ -7319,9 +10903,9 @@ "dev": true }, "node-notifier": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", - "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", + "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", "dev": true, "requires": { "growly": "^1.3.0", @@ -7374,14 +10958,14 @@ "dev": true }, "npm-lifecycle": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-2.1.0.tgz", - "integrity": "sha512-QbBfLlGBKsktwBZLj6AviHC6Q9Y3R/AY4a2PYSIRhSKSS0/CxRyD/PfxEX6tPeOCXQgMSNdwGeECacstgptc+g==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/npm-lifecycle/-/npm-lifecycle-3.1.4.tgz", + "integrity": "sha512-tgs1PaucZwkxECGKhC/stbEgFyc3TGh2TJcg2CDr6jbvQRdteHNhmMeljRzpe4wgFAXQADoy1cSqqi7mtiAa5A==", "dev": true, "requires": { "byline": "^5.0.0", - "graceful-fs": "^4.1.11", - "node-gyp": "^3.8.0", + "graceful-fs": "^4.1.15", + "node-gyp": "^5.0.2", "resolve-from": "^4.0.0", "slide": "^1.1.6", "uid-number": "0.0.6", @@ -7390,21 +10974,29 @@ } }, "npm-package-arg": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", - "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", "dev": true, "requires": { - "hosted-git-info": "^2.6.0", + "hosted-git-info": "^2.7.1", "osenv": "^0.1.5", - "semver": "^5.5.0", + "semver": "^5.6.0", "validate-npm-package-name": "^3.0.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, "npm-packlist": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", - "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.6.tgz", + "integrity": "sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg==", "dev": true, "requires": { "ignore-walk": "^3.0.1", @@ -7412,9 +11004,9 @@ } }, "npm-pick-manifest": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz", - "integrity": "sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz", + "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==", "dev": true, "requires": { "figgy-pudding": "^3.5.1", @@ -7422,38 +11014,6 @@ "semver": "^5.4.1" } }, - "npm-registry-fetch": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-3.9.0.tgz", - "integrity": "sha512-srwmt8YhNajAoSAaDWndmZgx89lJwIZ1GWxOuckH4Coek4uHv5S+o/l9FLQe/awA+JwTnj4FJHldxhlXdZEBmw==", - "dev": true, - "requires": { - "JSONStream": "^1.3.4", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.4.1", - "lru-cache": "^4.1.3", - "make-fetch-happen": "^4.0.1", - "npm-package-arg": "^6.1.0" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -7482,9 +11042,9 @@ "dev": true }, "nwsapi": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.3.tgz", - "integrity": "sha512-RowAaJGEgYXEZfQ7tvvdtAQUKPyTR6T6wNu0fwlNsGQYr/h3yQc6oI8WnVZh3Y/Sylwc+dtAlvPqfFZjhTyk3A==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", "dev": true }, "oauth-sign": { @@ -7721,9 +11281,9 @@ } }, "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", "dev": true }, "p-map-series": { @@ -7741,6 +11301,15 @@ "integrity": "sha1-SxoROZoRUgpneQ7loMHViB1r7+k=", "dev": true }, + "p-queue": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-4.0.0.tgz", + "integrity": "sha512-3cRXXn3/O0o3+eVmUroJPSj/esxoEFIm0ZOno/T+NzG/VZgPOqQ8WKmlNqubSEpZmCIngEy34unkHGg83ZIBmg==", + "dev": true, + "requires": { + "eventemitter3": "^3.1.0" + } + }, "p-reduce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", @@ -7762,56 +11331,13 @@ "p-reduce": "^1.0.0" } }, - "pacote": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.5.0.tgz", - "integrity": "sha512-aUplXozRbzhaJO48FaaeClmN+2Mwt741MC6M3bevIGZwdCaP7frXzbUOfOWa91FPHoLITzG0hYaKY363lxO3bg==", - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "cacache": "^11.3.2", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.1.0", - "glob": "^7.1.3", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^4.0.1", - "minimatch": "^3.0.4", - "minipass": "^2.3.5", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "normalize-package-data": "^2.4.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.12", - "npm-pick-manifest": "^2.2.3", - "npm-registry-fetch": "^3.8.0", - "osenv": "^0.1.5", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "protoduck": "^5.0.1", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.2", - "semver": "^5.6.0", - "ssri": "^6.0.1", - "tar": "^4.4.8", - "unique-filename": "^1.1.1", - "which": "^1.3.1" - }, - "dependencies": { - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - } - } - }, "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", "dev": true, "requires": { - "cyclist": "~0.2.2", + "cyclist": "^1.0.1", "inherits": "^2.0.3", "readable-stream": "^2.1.5" } @@ -8046,13 +11572,13 @@ } }, "prompts": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.0.4.tgz", - "integrity": "sha512-HTzM3UWp/99A0gk51gAegwo1QRYA7xjcZufMNe33rCclFszUYAuHe1fIN/3ZmiHeGPkUsNaRyQm1hHOfM0PKxA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.0.tgz", + "integrity": "sha512-NfbbPPg/74fT7wk2XYQ7hAIp9zJyZp5Fu19iRbORqqy1BhtrkZ0fPafBU+7bmn8ie69DpT0R6QpJIN2oisYjJg==", "dev": true, "requires": { - "kleur": "^3.0.2", - "sisteransi": "^1.0.0" + "kleur": "^3.0.3", + "sisteransi": "^1.0.3" } }, "promzard": { @@ -8096,12 +11622,6 @@ "genfun": "^5.0.0" } }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, "psl": { "version": "1.1.31", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", @@ -8181,18 +11701,18 @@ } }, "read-cmd-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz", - "integrity": "sha1-LV0Vd4ajfAVdIgd8MsU/gynpHHs=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz", + "integrity": "sha512-v5yCqQ/7okKoZZkBQUAfTsQ3sVJtXdNfbPnI5cceppoxEVLYA3k+VtV2omkeo8MS94JCy4fSiUwlRBAwCVRPUA==", "dev": true, "requires": { "graceful-fs": "^4.1.2" } }, "read-package-json": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.0.13.tgz", - "integrity": "sha512-/1dZ7TRZvGrYqE0UAfN6qQb5GYBsNcqS1C0tNK601CFOJmtHI7NIGXwetEPU/OtoFHZL3hDxm4rolFFVE9Bnmg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.0.tgz", + "integrity": "sha512-KLhu8M1ZZNkMcrq1+0UJbR8Dii8KZUqB0Sha4mOx/bknfKI/fyrQVrG/YIt2UOtG667sD8+ee4EXMM91W9dC+A==", "dev": true, "requires": { "glob": "^7.1.1", @@ -8211,16 +11731,14 @@ } }, "read-package-tree": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.2.2.tgz", - "integrity": "sha512-rW3XWUUkhdKmN2JKB4FL563YAgtINifso5KShykufR03nJ5loGFlkUMe1g/yxmqX073SoYYTsgXu7XdDinKZuA==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz", + "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", "dev": true, "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "once": "^1.3.0", "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0" + "readdir-scoped-modules": "^1.0.0", + "util-promisify": "^2.1.0" } }, "read-pkg": { @@ -8353,9 +11871,9 @@ } }, "readdir-scoped-modules": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz", - "integrity": "sha1-n6+jfShr5dksuuve4DDcm19AZ0c=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", "dev": true, "requires": { "debuglog": "^1.0.1", @@ -8473,21 +11991,21 @@ } }, "request-promise-core": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", - "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", + "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", "dev": true, "requires": { - "lodash": "^4.17.11" + "lodash": "^4.17.15" } }, "request-promise-native": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", - "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", + "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", "dev": true, "requires": { - "request-promise-core": "1.1.2", + "request-promise-core": "1.1.3", "stealthy-require": "^1.1.1", "tough-cookie": "^2.3.3" } @@ -8677,9 +12195,9 @@ "dev": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -8727,9 +12245,9 @@ "dev": true }, "sisteransi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz", - "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.4.tgz", + "integrity": "sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig==", "dev": true }, "slash": { @@ -8756,9 +12274,9 @@ "dev": true }, "smart-buffer": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", - "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", "dev": true }, "snapdragon": { @@ -8884,13 +12402,13 @@ } }, "socks": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.2.tgz", - "integrity": "sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz", + "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", "dev": true, "requires": { - "ip": "^1.1.5", - "smart-buffer": "4.0.2" + "ip": "1.1.5", + "smart-buffer": "^4.1.0" } }, "socks-proxy-agent": { @@ -8901,6 +12419,17 @@ "requires": { "agent-base": "~4.2.1", "socks": "~2.3.2" + }, + "dependencies": { + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + } } }, "sort-keys": { @@ -8932,9 +12461,9 @@ } }, "source-map-support": { - "version": "0.5.12", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", - "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -9198,9 +12727,9 @@ "dev": true }, "symbol-tree": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, "table": { @@ -9319,6 +12848,24 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "thenify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "dev": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "dev": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, "throat": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", @@ -9520,6 +13067,12 @@ "prelude-ls": "~1.1.2" } }, + "type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "dev": true + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -9533,16 +13086,23 @@ "dev": true }, "uglify-js": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.5.5.tgz", - "integrity": "sha512-e58FqZzPwaLODQetDQKlvErZaGkh1UmzP8YwU0aG65NLourKNtwVyDG8tkIyUU0vqWzxaikSvTaxrCSscmvqvQ==", + "version": "3.6.8", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.8.tgz", + "integrity": "sha512-XhHJ3S3ZyMwP8kY1Gkugqx3CJh2C3O0y8NPiSxtm1tyD/pktLAkFZsFGpuNfTZddKDQ/bbDBLAd2YyA1pbi8HQ==", "dev": true, "optional": true, "requires": { - "commander": "~2.20.0", + "commander": "~2.20.3", "source-map": "~0.6.1" }, "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "optional": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9565,38 +13125,15 @@ "dev": true }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "unique-filename": { @@ -9609,21 +13146,21 @@ } }, "unique-slug": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", - "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", "dev": true, "requires": { "imurmurhash": "^0.1.4" } }, "universal-user-agent": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-2.0.3.tgz", - "integrity": "sha512-eRHEHhChCBHrZsA4WEhdgiOKgdvgrMIHwnwnqD0r5C6AO8kwKcG7qSku3iXdhvHL3YvsS9ZkSGN8h/hIpoFC8g==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", + "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", "dev": true, "requires": { - "os-name": "^3.0.0" + "os-name": "^3.1.0" } }, "universalify": { @@ -9687,12 +13224,6 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, - "url-template": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", - "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=", - "dev": true - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -9705,6 +13236,15 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, + "util-promisify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz", + "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3" + } + }, "util.promisify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", @@ -9929,17 +13469,46 @@ } }, "write-json-file": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-2.3.0.tgz", - "integrity": "sha1-K2TIozAE1UuGmMdtWFp3zrYdoy8=", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-3.2.0.tgz", + "integrity": "sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ==", "dev": true, "requires": { "detect-indent": "^5.0.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "pify": "^3.0.0", + "graceful-fs": "^4.1.15", + "make-dir": "^2.1.0", + "pify": "^4.0.1", + "sort-keys": "^2.0.0", + "write-file-atomic": "^2.4.2" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + } + } + }, + "write-pkg": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/write-pkg/-/write-pkg-3.2.0.tgz", + "integrity": "sha512-tX2ifZ0YqEFOF1wjRW2Pk93NLsj02+n1UP5RvO6rCs0K6R2g1padvf006cY74PQJKMGS2r42NK7FD0dG6Y6paw==", + "dev": true, + "requires": { "sort-keys": "^2.0.0", - "write-file-atomic": "^2.0.0" + "write-json-file": "^2.2.0" }, "dependencies": { "make-dir": { @@ -9956,19 +13525,23 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true + }, + "write-json-file": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-2.3.0.tgz", + "integrity": "sha1-K2TIozAE1UuGmMdtWFp3zrYdoy8=", + "dev": true, + "requires": { + "detect-indent": "^5.0.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "pify": "^3.0.0", + "sort-keys": "^2.0.0", + "write-file-atomic": "^2.0.0" + } } } }, - "write-pkg": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/write-pkg/-/write-pkg-3.2.0.tgz", - "integrity": "sha512-tX2ifZ0YqEFOF1wjRW2Pk93NLsj02+n1UP5RvO6rCs0K6R2g1padvf006cY74PQJKMGS2r42NK7FD0dG6Y6paw==", - "dev": true, - "requires": { - "sort-keys": "^2.0.0", - "write-json-file": "^2.2.0" - } - }, "ws": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", @@ -9985,9 +13558,9 @@ "dev": true }, "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true }, "y18n": { diff --git a/package.json b/package.json index 5bdc869712..166174909b 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,9 @@ "eslint": "^5.16.0", "eslint-plugin-github": "^2.0.0", "eslint-plugin-jest": "^22.5.1", - "jest": "^24.7.1", + "jest": "^24.9.0", "jest-circus": "^24.7.1", - "lerna": "^3.13.3", + "lerna": "^3.18.4", "prettier": "^1.17.0", "ts-jest": "^24.0.2", "typescript": "^3.6.2" From 225370fc48622bbffa008d0ad36174dbc7530dee Mon Sep 17 00:00:00 2001 From: Brian Surowiec Date: Fri, 15 Nov 2019 16:02:47 -0500 Subject: [PATCH 033/192] Fix remove-matcher syntax (#211) --- docs/commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/commands.md b/docs/commands.md index 19b172015e..d1e438e253 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -107,7 +107,7 @@ Problems matchers can be used to scan a build's output to automatically surface ```bash echo "::add-matcher::eslint-compact-problem-matcher.json" -echo "::remove-matcher::eslint-compact" +echo "::remove-matcher owner=eslint-compact::" ``` `add-matcher` takes a path to a Problem Matcher file From 9a3c0051620fd66c506b94d8ee2d25ebddc3e86c Mon Sep 17 00:00:00 2001 From: Jan Jurzitza Date: Fri, 15 Nov 2019 22:12:36 +0100 Subject: [PATCH 034/192] Clarify that extractTar extracts gzipped tars (#134) The default downloadTool implementation strips the filename so when passing it to the extractTar function, it doesn't have a way of knowing the format without manually specifying it. However what the extractTar function arguments meant and how to specify them wasn't clear before reading the source code, so the documentation here got updated to reflect that. --- packages/tool-cache/src/tool-cache.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index 9ab2deb5ae..c932ab774c 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -183,11 +183,11 @@ export async function extract7z( } /** - * Extract a tar + * Extract a compressed tar archive * * @param file path to the tar * @param dest destination directory. Optional. - * @param flags flags for the tar. Optional. + * @param flags flags for the tar command to use for extraction. Defaults to 'xz' (extracting gzipped tars). Optional. * @returns path to the destination directory */ export async function extractTar( From 5c894298f2f24441718eb049215aa6decd507193 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Mon, 18 Nov 2019 16:20:01 -0500 Subject: [PATCH 035/192] toolrunner should which tool before invoking (#220) --- .github/workflows/unit-tests.yml | 70 ++----- package.json | 3 +- packages/core/package-lock.json | 2 +- packages/exec/README.md | 7 +- packages/exec/__tests__/exec.test.ts | 196 ++++++++++++++++++ .../exec/__tests__/scripts/print-args-cmd.cmd | 12 ++ .../exec/__tests__/scripts/print-args-sh.sh | 11 + packages/exec/package.json | 2 +- packages/exec/src/toolrunner.ts | 21 ++ packages/tool-cache/package-lock.json | 2 +- 10 files changed, 261 insertions(+), 65 deletions(-) create mode 100644 packages/exec/__tests__/scripts/print-args-cmd.cmd create mode 100755 packages/exec/__tests__/scripts/print-args-sh.sh diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index ac66f9ac56..feaa75fb6f 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -6,18 +6,27 @@ on: pull_request: paths-ignore: - '**.md' + jobs: - Ubuntu: - name: Run Ubuntu - runs-on: ubuntu-latest + + build: + name: Build + + strategy: + matrix: + runs-on: [ubuntu-latest, macOS-latest, windows-latest] + fail-fast: false + + runs-on: ${{ matrix.runs-on }} + steps: - name: Checkout - uses: actions/checkout@master + uses: actions/checkout@v1 - - name: Set Node.js 10.x + - name: Set Node.js 12.x uses: actions/setup-node@master with: - node-version: 10.x + node-version: 12.x - name: npm install run: npm install @@ -36,52 +45,3 @@ jobs: - name: Format run: npm run format-check - macOS: - name: Run macOS - runs-on: macos-latest - steps: - - name: Checkout - uses: actions/checkout@master - - - name: Set Node.js 10.x - uses: actions/setup-node@master - with: - node-version: 10.x - - - name: npm install - run: npm install - - - name: Bootstrap - run: npm run bootstrap - - - name: Compile - run: npm run build - - - name: npm test - run: npm test - Windows: - name: Run Windows - runs-on: windows-latest - steps: - - name: Checkout - uses: actions/checkout@master - - - name: Set Node.js 10.x - uses: actions/setup-node@master - with: - node-version: 10.x - - - name: npm install - run: npm install - - - name: Bootstrap - run: npm run bootstrap - - - name: Compile - run: npm run build - - # TODO: This currently ignores exec due to issues with Node and spawning on Windows, which I think is exacerbated by Jest. - # It doesn't seem to affect behavior in actions themselves, just when testing with Jest. - # See other similar issues here: https://github.com/nodejs/node/issues/25484 - - name: npm test - run: npm run test-ci diff --git a/package.json b/package.json index 166174909b..563eab1e13 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,7 @@ "format-check": "prettier --check packages/**/*.ts", "lint": "eslint packages/**/*.ts", "new-package": "scripts/create-package", - "test": "jest", - "test-ci": "jest --testPathIgnorePatterns=\"/packages/exec/__tests__/exec.test.ts\"" + "test": "jest" }, "devDependencies": { "@types/jest": "^24.0.11", diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json index 3449e3e313..fc47b0cbdf 100644 --- a/packages/core/package-lock.json +++ b/packages/core/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/core", - "version": "1.1.1", + "version": "1.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/exec/README.md b/packages/exec/README.md index e3eff742ae..7897ba5ad7 100644 --- a/packages/exec/README.md +++ b/packages/exec/README.md @@ -48,13 +48,10 @@ await exec.exec('node', ['index.js', 'foo=bar'], options); #### Exec tools not in the PATH -You can use it in conjunction with the `which` function from `@actions/io` to execute tools that are not in the PATH: +You can specify the full path for tools not in the PATH: ```js const exec = require('@actions/exec'); -const io = require('@actions/io'); -const pythonPath: string = await io.which('python', true) - -await exec.exec(`"${pythonPath}"`, ['main.py']); +await exec.exec('"/path/to/my-tool"', ['arg1']); ``` diff --git a/packages/exec/__tests__/exec.test.ts b/packages/exec/__tests__/exec.test.ts index e0b696413f..c0a5dda002 100644 --- a/packages/exec/__tests__/exec.test.ts +++ b/packages/exec/__tests__/exec.test.ts @@ -121,6 +121,38 @@ describe('@actions/exec', () => { } }) + it('Runs exec successfully with command from PATH', async () => { + const execOptions = getExecOptions() + const outStream = new StringStream() + execOptions.outStream = outStream + let output = '' + execOptions.listeners = { + stdout: (data: Buffer) => { + output += data.toString() + } + } + + let exitCode = 1 + let tool: string + let args: string[] + if (IS_WINDOWS) { + tool = 'cmd' + args = ['/c', 'echo', 'hello'] + } else { + tool = 'sh' + args = ['-c', 'echo hello'] + } + + exitCode = await exec.exec(tool, args, execOptions) + + expect(exitCode).toBe(0) + const rootedTool = await io.which(tool, true) + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${rootedTool} ${args.join(' ')}` + ) + expect(output.trim()).toBe(`hello`) + }) + it('Exec fails with error on bad call', async () => { const _testExecOptions = getExecOptions() @@ -418,6 +450,134 @@ describe('@actions/exec', () => { fs.unlinkSync(semaphorePath) }) + it('Exec roots relative tool path using unrooted options.cwd', async () => { + let exitCode: number + let command: string + if (IS_WINDOWS) { + command = './print-args-cmd' // let ToolRunner resolve the extension + } else { + command = './print-args-sh.sh' + } + const execOptions = getExecOptions() + execOptions.cwd = 'scripts' + const outStream = new StringStream() + execOptions.outStream = outStream + let output = '' + execOptions.listeners = { + stdout: (data: Buffer) => { + output += data.toString() + } + } + + const originalCwd = process.cwd() + try { + process.chdir(__dirname) + exitCode = await exec.exec(`${command} hello world`, [], execOptions) + } catch (err) { + process.chdir(originalCwd) + throw err + } + + expect(exitCode).toBe(0) + const toolPath = path.resolve( + __dirname, + execOptions.cwd, + `${command}${IS_WINDOWS ? '.cmd' : ''}` + ) + if (IS_WINDOWS) { + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${process.env.ComSpec} /D /S /C "${toolPath} hello world"` + ) + } else { + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${toolPath} hello world` + ) + } + expect(output.trim()).toBe(`args[0]: "hello"${os.EOL}args[1]: "world"`) + }) + + it('Exec roots relative tool path using rooted options.cwd', async () => { + let command: string + if (IS_WINDOWS) { + command = './print-args-cmd' // let ToolRunner resolve the extension + } else { + command = './print-args-sh.sh' + } + const execOptions = getExecOptions() + execOptions.cwd = path.join(__dirname, 'scripts') + const outStream = new StringStream() + execOptions.outStream = outStream + let output = '' + execOptions.listeners = { + stdout: (data: Buffer) => { + output += data.toString() + } + } + + const exitCode = await exec.exec(`${command} hello world`, [], execOptions) + + expect(exitCode).toBe(0) + const toolPath = path.resolve( + __dirname, + execOptions.cwd, + `${command}${IS_WINDOWS ? '.cmd' : ''}` + ) + if (IS_WINDOWS) { + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${process.env.ComSpec} /D /S /C "${toolPath} hello world"` + ) + } else { + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${toolPath} hello world` + ) + } + expect(output.trim()).toBe(`args[0]: "hello"${os.EOL}args[1]: "world"`) + }) + + it('Exec roots relative tool path using process.cwd', async () => { + let exitCode: number + let command: string + if (IS_WINDOWS) { + command = 'scripts/print-args-cmd' // let ToolRunner resolve the extension + } else { + command = 'scripts/print-args-sh.sh' + } + const execOptions = getExecOptions() + const outStream = new StringStream() + execOptions.outStream = outStream + let output = '' + execOptions.listeners = { + stdout: (data: Buffer) => { + output += data.toString() + } + } + + const originalCwd = process.cwd() + try { + process.chdir(__dirname) + exitCode = await exec.exec(`${command} hello world`, [], execOptions) + } catch (err) { + process.chdir(originalCwd) + throw err + } + + expect(exitCode).toBe(0) + const toolPath = path.resolve( + __dirname, + `${command}${IS_WINDOWS ? '.cmd' : ''}` + ) + if (IS_WINDOWS) { + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${process.env.ComSpec} /D /S /C "${toolPath} hello world"` + ) + } else { + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${toolPath} hello world` + ) + } + expect(output.trim()).toBe(`args[0]: "hello"${os.EOL}args[1]: "world"`) + }) + if (IS_WINDOWS) { // Win specific quoting tests it('execs .exe with verbatim args (Windows)', async () => { @@ -572,6 +732,42 @@ describe('@actions/exec', () => { ) }) + it('execs .cmd from path (Windows)', async () => { + // this test validates whether a .cmd is resolved from the PATH when the extension is not specified + const cmd = 'print-args-cmd' // note, not print-args-cmd.cmd + const cmdPath = path.join(__dirname, 'scripts', `${cmd}.cmd`) + const args: string[] = ['my arg 1', 'my arg 2'] + const outStream = new StringStream() + let output = '' + const options = { + outStream: outStream, + listeners: { + stdout: (data: Buffer) => { + output += data.toString() + } + } + } + + const originalPath = process.env['Path'] + try { + process.env['Path'] = `${originalPath};${path.dirname(cmdPath)}` + const exitCode = await exec.exec(`${cmd}`, args, options) + expect(exitCode).toBe(0) + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${ + process.env.ComSpec + } /D /S /C "${cmdPath} "my arg 1" "my arg 2""` + ) + expect(output.trim()).toBe( + 'args[0]: "my arg 1"\r\n' + + 'args[1]: "my arg 2"' + ) + } catch (err) { + process.env['Path'] = originalPath + throw err + } + }) + it('execs .cmd with arg quoting (Windows)', async () => { // this test validates .cmd quoting rules are applied, not the default libuv rules const cmdPath = path.join( diff --git a/packages/exec/__tests__/scripts/print-args-cmd.cmd b/packages/exec/__tests__/scripts/print-args-cmd.cmd new file mode 100644 index 0000000000..7f3e4e6691 --- /dev/null +++ b/packages/exec/__tests__/scripts/print-args-cmd.cmd @@ -0,0 +1,12 @@ +@echo off +setlocal +set index=0 + +:check_arg +set arg=%1 +if not defined arg goto :eof +set "arg=%arg:"=%" +echo args[%index%]: "%arg%" +set /a index=%index%+1 +shift +goto check_arg \ No newline at end of file diff --git a/packages/exec/__tests__/scripts/print-args-sh.sh b/packages/exec/__tests__/scripts/print-args-sh.sh new file mode 100755 index 0000000000..40f18cb2ca --- /dev/null +++ b/packages/exec/__tests__/scripts/print-args-sh.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +# store arguments in a special array +args=("$@") +# get number of elements +ELEMENTS=${#args[@]} + +# echo each element +for (( i=0;i<$ELEMENTS;i++)); do + echo "args[$i]: \"${args[${i}]}\"" +done \ No newline at end of file diff --git a/packages/exec/package.json b/packages/exec/package.json index a57920eac2..ee3a773df1 100644 --- a/packages/exec/package.json +++ b/packages/exec/package.json @@ -32,7 +32,7 @@ "bugs": { "url": "https://github.com/actions/toolkit/issues" }, - "devDependencies": { + "dependencies": { "@actions/io": "^1.0.1" } } diff --git a/packages/exec/src/toolrunner.ts b/packages/exec/src/toolrunner.ts index 32dc5f948e..2182fedd5d 100644 --- a/packages/exec/src/toolrunner.ts +++ b/packages/exec/src/toolrunner.ts @@ -1,8 +1,11 @@ import * as os from 'os' import * as events from 'events' import * as child from 'child_process' +import * as path from 'path' import * as stream from 'stream' import * as im from './interfaces' +import * as io from '@actions/io' +import * as ioUtil from '@actions/io/lib/io-util' /* eslint-disable @typescript-eslint/unbound-method */ @@ -392,6 +395,24 @@ export class ToolRunner extends events.EventEmitter { * @returns number */ async exec(): Promise { + // root the tool path if it is unrooted and contains relative pathing + if ( + !ioUtil.isRooted(this.toolPath) && + (this.toolPath.includes('/') || + (IS_WINDOWS && this.toolPath.includes('\\'))) + ) { + // prefer options.cwd if it is specified, however options.cwd may also need to be rooted + this.toolPath = path.resolve( + process.cwd(), + this.options.cwd || process.cwd(), + this.toolPath + ) + } + + // if the tool is only a file name, then resolve it from the PATH + // otherwise verify it exists (add extension on Windows if necessary) + this.toolPath = await io.which(this.toolPath, true) + return new Promise((resolve, reject) => { this._debug(`exec tool: ${this.toolPath}`) this._debug('arguments:') diff --git a/packages/tool-cache/package-lock.json b/packages/tool-cache/package-lock.json index ffea2aa0c0..3c3e386267 100644 --- a/packages/tool-cache/package-lock.json +++ b/packages/tool-cache/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.1.1", + "version": "1.1.2", "lockfileVersion": 1, "requires": true, "dependencies": { From d98e55434d01656aaa3dd4f0315d56e6c80fd495 Mon Sep 17 00:00:00 2001 From: Ross Brodbeck Date: Tue, 3 Dec 2019 13:55:39 -0500 Subject: [PATCH 036/192] Fix test timeouts (#235) * Fix test timeouts --- packages/exec/__tests__/exec.test.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/exec/__tests__/exec.test.ts b/packages/exec/__tests__/exec.test.ts index c0a5dda002..6f9811cfc3 100644 --- a/packages/exec/__tests__/exec.test.ts +++ b/packages/exec/__tests__/exec.test.ts @@ -287,8 +287,6 @@ describe('@actions/exec', () => { }) it('Handles child process holding streams open', async function() { - // this was timing out on some slower hosted macOS runs at default 5s - jest.setTimeout(10000) const semaphorePath = path.join( getTestTemp(), 'child-process-semaphore.txt' @@ -332,11 +330,9 @@ describe('@actions/exec', () => { ).toBe(1) fs.unlinkSync(semaphorePath) - }) + }, 10000) // this was timing out on some slower hosted macOS runs at default 5s it('Handles child process holding streams open and non-zero exit code', async function() { - // this was timing out on some slower hosted macOS runs at default 5s - jest.setTimeout(10000) const semaphorePath = path.join( getTestTemp(), 'child-process-semaphore.txt' @@ -388,7 +384,7 @@ describe('@actions/exec', () => { ).toBe(1) fs.unlinkSync(semaphorePath) - }) + }, 10000) // this was timing out on some slower hosted macOS runs at default 5s it('Handles child process holding streams open and stderr', async function() { const semaphorePath = path.join( @@ -665,7 +661,7 @@ describe('@actions/exec', () => { `[command]"${exePath}" myarg1 myarg2` ) expect(output.trim()).toBe("args[0]: 'myarg1'\r\nargs[1]: 'myarg2'") - }) + }, 20000) // slower windows runs timeout, so upping timeout to 20s (from default of 5s) it('execs .cmd with a space and with verbatim args (Windows)', async () => { // this test validates the quoting that tool runner adds around the script path. From 211b25966bfad1be2098b212925ac49ed847d7b9 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Tue, 3 Dec 2019 14:18:54 -0500 Subject: [PATCH 037/192] Unit test (#236) --- packages/exec/__tests__/exec.test.ts | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/exec/__tests__/exec.test.ts b/packages/exec/__tests__/exec.test.ts index 6f9811cfc3..a4fbc234b6 100644 --- a/packages/exec/__tests__/exec.test.ts +++ b/packages/exec/__tests__/exec.test.ts @@ -575,6 +575,36 @@ describe('@actions/exec', () => { }) if (IS_WINDOWS) { + it('Exec roots relative tool path using process.cwd (Windows path separator)', async () => { + let exitCode: number + const command = 'scripts\\print-args-cmd' // let ToolRunner resolve the extension + const execOptions = getExecOptions() + const outStream = new StringStream() + execOptions.outStream = outStream + let output = '' + execOptions.listeners = { + stdout: (data: Buffer) => { + output += data.toString() + } + } + + const originalCwd = process.cwd() + try { + process.chdir(__dirname) + exitCode = await exec.exec(`${command} hello world`, [], execOptions) + } catch (err) { + process.chdir(originalCwd) + throw err + } + + expect(exitCode).toBe(0) + const toolPath = path.resolve(__dirname, `${command}.cmd`) + expect(outStream.getContents().split(os.EOL)[0]).toBe( + `[command]${process.env.ComSpec} /D /S /C "${toolPath} hello world"` + ) + expect(output.trim()).toBe(`args[0]: "hello"${os.EOL}args[1]: "world"`) + }) + // Win specific quoting tests it('execs .exe with verbatim args (Windows)', async () => { const exePath = process.env.ComSpec From 1c12ced7ba4b5f2d21da0ddfea83b4885505503f Mon Sep 17 00:00:00 2001 From: eric sciple Date: Wed, 4 Dec 2019 11:03:36 -0500 Subject: [PATCH 038/192] bump patch version (#239) --- packages/exec/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/exec/package.json b/packages/exec/package.json index ee3a773df1..f283e8eec0 100644 --- a/packages/exec/package.json +++ b/packages/exec/package.json @@ -1,6 +1,6 @@ { "name": "@actions/exec", - "version": "1.0.1", + "version": "1.0.2", "description": "Actions exec lib", "keywords": [ "github", From 5fdab2aaf2fc3a63dc73bc3ac91fc6afa14d41de Mon Sep 17 00:00:00 2001 From: Ross Brodbeck Date: Fri, 6 Dec 2019 07:52:04 -0500 Subject: [PATCH 039/192] Update octokit graphql type dependencies (#228) * Update GraphQL support in base API --- packages/github/RELEASES.md | 4 + packages/github/package-lock.json | 84 ++++++++++++------- packages/github/package.json | 6 +- .../github/src/@types/@octokit/index.d.ts | 36 -------- packages/github/src/context.ts | 7 +- packages/github/src/github.ts | 16 ++-- 6 files changed, 72 insertions(+), 81 deletions(-) delete mode 100644 packages/github/src/@types/@octokit/index.d.ts diff --git a/packages/github/RELEASES.md b/packages/github/RELEASES.md index e8a78027cd..ef90e1c7dc 100644 --- a/packages/github/RELEASES.md +++ b/packages/github/RELEASES.md @@ -1,5 +1,9 @@ # @actions/github Releases +### 2.0.0 + +- Upgrade Octokit version to 4.x to include typescript types [#228](https://github.com/actions/toolkit/pull/228) + ### 1.1.0 - Accept Octokit.Options in the GitHub constructor [#113](https://github.com/actions/toolkit/pull/113) diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index 1fb680e469..4e43a2e4a3 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -384,64 +384,76 @@ } }, "@octokit/endpoint": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.3.1.tgz", - "integrity": "sha512-4mKqSQfeTRFpQMUGIUG1ewdQT64b2YpvjG2dE1x7nhQupdI/AjdgdcIsmPtRFEXlih/uLQLRWJL4FrivpQdC7A==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.1.tgz", + "integrity": "sha512-nBFhRUb5YzVTCX/iAK1MgQ4uWo89Gu0TH00qQHoYRCsE12dWcG1OiLd7v2EIo2+tpUKPMOQ62QFy9hy9Vg2ULg==", "requires": { - "deepmerge": "4.0.0", + "@octokit/types": "^2.0.0", "is-plain-object": "^3.0.0", - "universal-user-agent": "^3.0.0", - "url-template": "^2.0.8" + "universal-user-agent": "^4.0.0" }, "dependencies": { "universal-user-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-3.0.0.tgz", - "integrity": "sha512-T3siHThqoj5X0benA5H0qcDnrKGXzU8TKoX15x/tQHw1hQBvIEBHjxQ2klizYsqBOO/Q+WuxoQUihadeeqDnoA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", + "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", "requires": { - "os-name": "^3.0.0" + "os-name": "^3.1.0" } } } }, "@octokit/graphql": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-2.1.3.tgz", - "integrity": "sha512-XoXJqL2ondwdnMIW3wtqJWEwcBfKk37jO/rYkoxNPEVeLBDGsGO1TCWggrAlq3keGt/O+C/7VepXnukUxwt5vA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.3.1.tgz", + "integrity": "sha512-hCdTjfvrK+ilU2keAdqNBWOk+gm1kai1ZcdjRfB30oA3/T6n53UVJb7w0L5cR3/rhU91xT3HSqCd+qbvH06yxA==", "requires": { - "@octokit/request": "^5.0.0", - "universal-user-agent": "^2.0.3" + "@octokit/request": "^5.3.0", + "@octokit/types": "^2.0.0", + "universal-user-agent": "^4.0.0" + }, + "dependencies": { + "universal-user-agent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", + "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", + "requires": { + "os-name": "^3.1.0" + } + } } }, "@octokit/request": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.0.1.tgz", - "integrity": "sha512-SHOk/APYpfrzV1RNf7Ux8SZi+vZXhMIB2dBr4TQR6ExMX8R4jcy/0gHw26HLe1dWV7Wxe9WzYyDSEC0XwnoCSQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.1.tgz", + "integrity": "sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg==", "requires": { - "@octokit/endpoint": "^5.1.0", + "@octokit/endpoint": "^5.5.0", "@octokit/request-error": "^1.0.1", + "@octokit/types": "^2.0.0", "deprecation": "^2.0.0", "is-plain-object": "^3.0.0", "node-fetch": "^2.3.0", "once": "^1.4.0", - "universal-user-agent": "^3.0.0" + "universal-user-agent": "^4.0.0" }, "dependencies": { "universal-user-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-3.0.0.tgz", - "integrity": "sha512-T3siHThqoj5X0benA5H0qcDnrKGXzU8TKoX15x/tQHw1hQBvIEBHjxQ2klizYsqBOO/Q+WuxoQUihadeeqDnoA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", + "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", "requires": { - "os-name": "^3.0.0" + "os-name": "^3.1.0" } } } }, "@octokit/request-error": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.0.4.tgz", - "integrity": "sha512-L4JaJDXn8SGT+5G0uX79rZLv0MNJmfGa4vb4vy1NnpjSnWDLJRy6m90udGwvMmavwsStgbv2QNkPzzTCMmL+ig==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.0.tgz", + "integrity": "sha512-DNBhROBYjjV/I9n7A8kVkmQNkqFAMem90dSxqvPq57e2hBr7mNTX98y3R2zDpqMQHVRpBDjsvsfIGgBzy+4PAg==", "requires": { + "@octokit/types": "^2.0.0", "deprecation": "^2.0.0", "once": "^1.4.0" } @@ -504,6 +516,14 @@ } } }, + "@octokit/types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.0.2.tgz", + "integrity": "sha512-StASIL2lgT3TRjxv17z9pAqbnI7HGu9DrJlg3sEBFfCLaMEqp+O3IQPUF6EZtQ4xkAu2ml6kMBBCtGxjvmtmuQ==", + "requires": { + "@types/node": ">= 8" + } + }, "@types/babel__core": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz", @@ -570,6 +590,11 @@ "@types/istanbul-lib-report": "*" } }, + "@types/node": { + "version": "12.12.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.11.tgz", + "integrity": "sha512-O+x6uIpa6oMNTkPuHDa9MhMMehlxLAd5QcOvKRjAFsBVpeFWTOPnXbDvILvFgFFZfQ1xh1EZi1FbXxUix+zpsQ==" + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -1262,11 +1287,6 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "deepmerge": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.0.0.tgz", - "integrity": "sha512-YZ1rOP5+kHor4hMAH+HRQnBQHg+wvS1un1hAOuIcxcBy0hzcUf6Jg2a1w65kpoOUnurOfZbERwjI1TfZxNjcww==" - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", diff --git a/packages/github/package.json b/packages/github/package.json index 2ee3660b82..36aeea4afb 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "1.1.0", + "version": "2.0.0", "description": "Actions github lib", "keywords": [ "github", @@ -27,13 +27,15 @@ "scripts": { "test": "jest", "build": "tsc", + "format": "prettier --write **/*.ts", + "format-check": "prettier --check **/*.ts", "tsc": "tsc" }, "bugs": { "url": "https://github.com/actions/toolkit/issues" }, "dependencies": { - "@octokit/graphql": "^2.0.1", + "@octokit/graphql": "^4.3.1", "@octokit/rest": "^16.15.0" }, "devDependencies": { diff --git a/packages/github/src/@types/@octokit/index.d.ts b/packages/github/src/@types/@octokit/index.d.ts deleted file mode 100644 index f4e3c17533..0000000000 --- a/packages/github/src/@types/@octokit/index.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -declare module '@octokit/graphql' { - export interface GraphQlQueryResponse { - data: {[key: string]: any} | null - errors?: [ - { - message: string - path: [string] - extensions: {[key: string]: any} - locations: [ - { - line: number - column: number - } - ] - } - ] - } - - export interface GraphQLError { - message: string - locations?: {line: number; column: number}[] - path?: (string | number)[] - extensions?: { - [key: string]: any - } - } - - export interface Variables { - [key: string]: any - } - - export function defaults( - options: any - ): (query: string, variables?: Variables) => Promise -} diff --git a/packages/github/src/context.ts b/packages/github/src/context.ts index a06a8e0a48..f2aafde112 100644 --- a/packages/github/src/context.ts +++ b/packages/github/src/context.ts @@ -27,11 +27,8 @@ export class Context { readFileSync(process.env.GITHUB_EVENT_PATH, {encoding: 'utf8'}) ) } else { - process.stdout.write( - `GITHUB_EVENT_PATH ${ - process.env.GITHUB_EVENT_PATH - } does not exist${EOL}` - ) + const path = process.env.GITHUB_EVENT_PATH + process.stdout.write(`GITHUB_EVENT_PATH ${path} does not exist${EOL}`) } } this.eventName = process.env.GITHUB_EVENT_NAME as string diff --git a/packages/github/src/github.ts b/packages/github/src/github.ts index 35b5f624de..fdf19f1c67 100644 --- a/packages/github/src/github.ts +++ b/packages/github/src/github.ts @@ -1,5 +1,11 @@ // Originally pulled from https://github.com/JasonEtco/actions-toolkit/blob/master/src/github.ts -import {GraphQlQueryResponse, Variables, defaults} from '@octokit/graphql' +import {graphql} from '@octokit/graphql' + +// we need this type to set up a property on the GitHub object +// that has token authorization +// (it is not exported from octokit by default) +import {graphql as GraphQL} from '@octokit/graphql/dist-types/types' + import Octokit from '@octokit/rest' import * as Context from './context' @@ -9,14 +15,12 @@ Octokit.prototype = new Octokit() export const context = new Context.Context() export class GitHub extends Octokit { - graphql: ( - query: string, - variables?: Variables - ) => Promise + graphql: GraphQL constructor(token: string, opts: Omit = {}) { super({...opts, auth: `token ${token}`}) - this.graphql = defaults({ + + this.graphql = graphql.defaults({ headers: {authorization: `token ${token}`} }) } From 0f1fef3752c5578aa89b66256d1f0c577f65f188 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sat, 7 Dec 2019 14:26:07 -0800 Subject: [PATCH 040/192] versioning update (#246) --- docs/action-versioning.md | 22 +++++----------------- docs/assets/action-releases.drawio | 2 +- docs/assets/action-releases.png | Bin 63619 -> 67579 bytes 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/docs/action-versioning.md b/docs/action-versioning.md index c203cd9c3d..f452349462 100644 --- a/docs/action-versioning.md +++ b/docs/action-versioning.md @@ -17,7 +17,7 @@ Binding to a major version is the latest of that major version ( e.g. `v1` == "1 Major versions should guarantee compatibility. A major version can add net new capabilities but should not break existing input compatibility or break existing workflows. -Major version binding allows you to take advantage of bug fixes and critical functionality and security fixes. The `master` branch has the latest code and is unstable to bind to since a breaking new major version may first get implemented in master. +Major version binding allows you to take advantage of bug fixes and critical functionality and security fixes. The `master` branch has the latest code and is unstable to bind to since changes get committed to master and released to the market place by creating a tag. In addition, a new major version carrying breaking changes will get implemented in master after branching off the previous major version. > Warning: do not reference `master` since that is the latest code and can be carrying breaking changes of the next major version. @@ -32,30 +32,18 @@ Binding to the immutable sha1 may offer more reliability. However, note that th # Recommendations -1. **Create a release branch for each major version**: For example, `releases/v1`. This will allow for releases of that major version while the development of a different major version proceeds. +1. **Create a GitHub release for each specific version**: Creating a release like [ v1.0.0 ](https://github.com/actions/javascript-action/releases/tag/v1.0.0) allows users to bind back to a specific version if an issue is encountered with the latest major version. -2. **Validate changes referencing the release branch**: +2. **Publish the specific version to the marketplace**: When you release a specific version, choose the option to "Publish this release to the GitHub Marketplace". -```yaml -steps: - - uses: actions/sample-action@releases/v1 -``` - -3. **Create a GitHub release for each specific version**: Creating a release like [ v1.0.0 ](https://github.com/actions/javascript-action/releases/tag/v1.0.0) allows users to bind back to a specific version if an issue is encountered with the latest major version. - -4. **Release that version by updating the major version tag**: Move the major version tag (v1, v2, etc.) to point to the ref of the current release. This will act as the stable release for that major version. You should keep this tag updated to the most recent stable minor/patch release. +3. **Make the new release available to those binding to the major version tag**: Move the major version tag (v1, v2, etc.) to point to the ref of the current release. This will act as the stable release for that major version. You should keep this tag updated to the most recent stable minor/patch release. ``` -git checkout releases/v1 git tag -fa v1 -m "Update v1 tag" git push origin v1 --force ``` -This will result in a major version tag and the latest specific version pointing to the same SHA. See [javascript-action tags](https://github.com/actions/javascript-action/tags) as an example. - -5. **Compatibility Breaks**: introduce a new major version branch (releases/v2) and tag (v2) if changes will break existing workflows. For example, changing inputs. - -See [Git-Basics-Tagging](https://git-scm.com/book/en/v2/Git-Basics-Tagging) +4. **Create a new major version if breaking compat**: If the inputs or behavior are breaking, offer a new major version. Typically, this entails branching the previous major version for critical updates and moving master to the new major version. Keeping the major versions available to a minimum is desirable as adding new major versions requires end users understanding the breaks (how to adjust their yaml and expectations). Since you will likely want to innovate on just the latest major version with only critical updates to previous major versions, you will want to encourage users to move to the latest major version. For that reason, it's best to combine breaking changes with new capabilities. # Sample Workflow diff --git a/docs/assets/action-releases.drawio b/docs/assets/action-releases.drawio index b5a0066667..342c9c2852 100644 --- a/docs/assets/action-releases.drawio +++ b/docs/assets/action-releases.drawio @@ -1 +1 @@ -7Vtbc5s4GP01nj4lA+L+WCdp96E70910uu1TB4OMmWDkEdhx9tfvJ5AMSPIta4Mbx5mJ0R10zncVHll38/VnGi5mf5IYZyNkxOuRdT9CyHds+M8qXuoKxzPqioSmcV1lNhWP6b+YV4puyzTGRadjSUhWpotuZUTyHEdlpy6klDx3u01J1l11ESZYqXiMwkzU3jpN/T9pXM54vekGTcMfOE1mfHEfuXXDJIyeEkqWOV9xhKxp9amb56GYiz9qMQtj8tyqsh5G1h0lpKyv5us7nLHNFRsnxpUv4m5H1nhWzjMomHBZNX/aMtg8ZDA8HMV52V5u23xTYxJFQWAZNnanTngTKPPjGHaWF3OSw9c4WtIVjvmKrcWLMqQlJwMwwRrjPP7I0IRylIVFkUZ15ac0E2Og1B5RlJQ84TuSEVotbxnVZ9MisGSPPSX5ZjVUTVXSlx+beaHwEwoGUIEX7xm9jU3pRZTWafmjdd0aBaVmECuIMeo+C2TIkkZYv7mIS0JIE1zqu/BZ2Ka3puXYfcZkjuHOoQPFWVimq64QhFxqkk2/zdCvJIUbRQYXcFsIKpdvT8i3mKJ+DD6q4Q9ctG6jqapYdRjDTPfUFNvHpp2c0WG/E/kNy1rE+tkm3RaWvYox1n7GoH4YY0mM8WXG1Pd4FsbUC63CbMnvPlLVIEwBxoVR53mWlvhxEVZ7+gzmrcugSrVX1KoYIrS3qVM+bvVRiGR3iWSxicIsTXKm5wBeTFkHYGVrqqnD/nbxYIVpidc7MeStyO9isbHOz42lE11mbRtnbEe9g9cx4Kjm7NLEeY8qf63ZeJVAuxcj0E4gkcjqTaCRKtBoUIE2BxZo27scgbZUcKyrBseRwPHRcODYKjjOVYPjyVpsSFNov5vCI0yhvd8Uuv2YQs8azBS6qkBrWHRFAu3awwl08ovef//7r+Rhtfj+7UtBvkR3N5roY4TcDFYdT+AiqS5omEdwbx9FCyy0aYTvcM5wyicF+wKu4rDABfRZqWEMYLZgl1GYZWRZ7od8gWkKD8pgEYO+NlXjBSnSMiU5quTY9E9kEiWt69kqSCbqEyWNSznD0VOas0elJGa5TLwAJYrzKGWbzyGkAqZvuGAa4QE9KJjAppSyrm4LE1fsbaHgVYrgsC1OAaaPvGGexnG2TbC70nwK2XKdrifja2DTCRc6F2waZ1OTHroi9ee7wzmbWoTefZpdPs0uUu9ybKx+HBsnMG890/GQ7zng1XhBl1u2tnVPClhdBHUp67pn85602+0oOmSartmgolgyTW9MKZkzPEDDp3nydtW7Z3TVu2vZqno3+lTvl5nq3yiMgZP9r1Uedj/Kw5fzHgeeESkTubJNO9/RgXZLPUVBzDEsCFUgzGybJuCPv1214DuSftZEVHafWsFX8FAiqojisMQ1h3GFDtzMuJKLZGucZZi3EOKorr0y+85Jeg/KwO6eRv17clBmqurf7dN3DPYDfVToPA8LtoG/Z8yMpJjZDoaOmcXRXTv68lSLfUXRV3BxySfNQfcFeFBHhF+v94QEPc/u5xiSfZQN36F+juwwuWa/fo6pSYL51y3Pgd/1PIfOppiao9TfS577T6cIWrdDol2h03tcdCgZLzO3d2uDlDSEvDFujQMpeWSQziZp+X190bQvuxbIb+zJp5oH2zXJK+o7wWeqGb6rCuBNQ/ZQPEsxY71G8KZ6bF3nXCfLN5xf9QPZUVTzq/3CoGa2ytYxJgjGFFN24Mmy3oZ08Gzw0BsZH0DNwLAPcLnMea/47cIY2N1jkMBAA8N41oQYOiAhNicrvG+u3vNilv73LMfj7Xfx1r6s0GtezFQTY1tSW5chdJWrxm9KCunYxp/Exsnv8QWBApIOo7MJpZi4hdHuN3euByp/i1+5L5F5Pqx072jJkU3zUzS+7XFYzHSBzcFbtteBb22Io9kPUfc//XwZD09OOx3q55umfIJwsgQWFJtfRNbdm9+dWg//AQ== \ No newline at end of file +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 \ No newline at end of file diff --git a/docs/assets/action-releases.png b/docs/assets/action-releases.png index 9cf7e80bda01fe81f5168b5d2cbfe7607354b344..18f3d3ba2b9562334d043ea07cde4089081ea9ad 100644 GIT binary patch literal 67579 zcmeFYXH=7Iur>+^iXtVTAW{XfQ6vOXq(kT>^n@ZvLJNTqdO`<5iWCu11Oz)OT|fat z7bz-;g`!9epdwwQ!@2Xmd+%@Wb=La6b$*;*=eQ73p4`vO+;h)dbIl~#!rXxS(D6eo zEG*nchG;7mmVIL^EUX_m_k&kXw6bilupCMb(!&M$Qay-1?kqAe-G3j+C@K2-xe+~x z?rt(Lw2YFHk|Go(4^@&^MA#@P$-uOs%HWrxvVx)t(gC~;uJ`f_4Ep!l(9lo?w*c2r z1)|@-Z@GH9lmFvAGrusRkB_U2lD>+9BKXu9GgktU9OM`1B?Hp|&yC1I?mplzcp3c0 zSc6|U@E@w^0#$Z_p9K%KDHI=ftUKO>7{pu!hERaP!OLe%3~kIzWt4QmV;>^P9sDtH zCy@M@AJO#+@bd+)=p&E{iV83kkQ=Iq1aDZodbkD<|JyQ5Mcjj2J^xii(Z(>y*UHCL z*Urp8NGCuaZ-W6cGp`A84+tdsk^j96s-yr@0LwAo2%=Hk|9#cXJ%mUAH)Xy`0Tu}S zkBgbF!1REn-H5K90j|E_ZL%wn?jJK-fD7fJ|9*)0AEx*4P*z4FVeaxCaAhTVJOQsH zk3=cs<)Lsq%uPwf73QIc|94S@9o3Y8@`LMJC_`=Z-FzI($QBOPIuwMzi5ncN2*=|s z{B`w%weiYf`ZOa4Z4aQilAX5^HOShAsvGK{Z5bG-YmM?SRS5~Puq4@%3{lqZrtZqF zWMvvs(TGGc^z`+%vcwbIbai2#!4_0gs4f*}ZjT5EpivA=jZ{c}@BpHbhYFfTfz#Z) z91M*O0u{q_eaw+$Z`S~AD#e##6pBY#c~OJC!U9pgmd1fl2MWwfFUZK>P*2y*2k+)i zfs=t)2DUbEEXu(iZ-)j_>KJ+G`*NmC{rJ-m%B3D-wf#&X6J?@DPld0^-SOtceIH|h%XIiplFC9;8lzN)c%_%qvDp(Qbg*ManMjPp&wT*)jmZ4TiG#(dfO(2j>>`4?G zaA}aOse=*~V+7>Ddl&^$X*8SzM#sz&tz>QGP0+QmQ^x42P<;q^qCdD=$CB(|g>v@~ z@h~EgTs^Ffk-k1UI2$9V4iZXsGch%@^F}NA!pWYBSWBR>T_E^{4Mdp|kw~Pcsi&PW zS|5wl4++#&4x*uqh+#}C*#-Cpqufo%{>IuAA44b%V{BurNF^B)^@(s>Uq7@15o6

+3=BMN1;}?bvaKpQSC7~wv2o+0R6Kew{ zIF@J~0#mXu*CyD9*xCpBc<39#RIIRy4t92U9e)dD2M0ue9wo>x$Q&Ey;~h#?4nX<_ zW1;rmP^_+pn-0a^jjBUb^bIozH6+-3df6C&8`CJ>b_PsC>-w78=@C@I%v3NGG|V!L zW`y?&({r;@F$^TAxI1_gE&cHpFjFMdz|Ptp}Vu(6d%a45k`g%GM}uNxNVjki_Q(FeCsf_a7-(?ZEaxS~BRAjA@{Wa{Ok?}@Zi zfd}K!Cc!|QFk+~IhphpQh}5xyxsiR~cnn4{OvMk2@HR5CQ!-U_^&;D9d+A{*=61PsXK!qA=Bi#*ELQVCd1Y=r&PN17jfSacZ&eafw z@T7PsYX=0uToK^t5bWH8y#fP*(02N60os1VAZu515bAWuiY68w0Ty^e6c8LdH*|xU zQ^LRl@Z*6*_^BX37}i(BqY-dDZJ09NLP;ltO0?4t4J8{nAn`UHK87|3D{wIp1Ot7l z7e>c6EWn7W<3TnF)N`QWi1=VU++5kg%bbeE8j+N3FvuV!OPf$%5Hoe`%}lUn6bud? z><^<@`4X%UHu^daaJ+?&A;Hhr1nOZIXlQL|8sz6|ZRBc6U~Zo_#U>EzZRX`~jMH^> zAP~(>6uq^vp%j8;pqGg+f@W!>AFPLg`TE)uObki>{z}0nDrSlZL#z);AM2{@>1jo! z(tHhIKHhpHH>*IBFVz=CL88r}G&0tnq)ZL+G^3Gl7T_Fs`J!z|I&ipAhy|IsWdu`m zKSiSu0zpwp$;1`!YU8dSMD%p?G{PGrbm8P+J7q-+0>LoIPTA7I93K>780_t39ctm} z6KEUc0JWjoqYMbRAXl^l$v_vSN1@U1y0(E9UVc=Jjt+tl8t6fRW0YLoLXnDL_+V4K zxf0Sd3~vr@tb_%@S|^BLpo|PN1|}e3%)L-b7^DZ-yW5Vgr(df-wc|A0WMu8p>tyEg*m#yr~E2qIZg31Q+LXzs718vtH0@IttH=~|I> zFm_&{zB)D(q!}KnYpQ6EM?uNja6hA9sGd0j?_nL}i2!*~$Hbi}6e`Ty7X~$hIXE}~ zp@R z`{LZ7DhB=_*FlxYSdtIX&{P@ZU>%r+Cx{3-W(Z3a6b$cXMhUYEu~tFhLF55msgDaX zfg6&|Lx@P2hc;6Z6!0y0BnZ2;fk*pPwUN|>fjH0EnzZb^HE6iRA8cf0}q6{sp?Cgv~%nU=& zx;CNUzP8%t`WE(vI8#q591bEB*}SYEu4huB!4}ns(18LxLB{We5 z=Z&zz)66MAH^U%fA7x#btD%>TnYX7I1skkujwAU|EQ82aXrwKU#yrCQ#`by!o}Ruo z+HhYx6Rb9h5ULcS;!m@%(^kQP_l-0pfPRrC$Pr}VK{0@FK8z5KDhWDj?9Bn@h812u6owb#aBNI{BBX9Aaj zA9JL!TZpa)6hsYum^VU^8A-@E*M9*%JA}A_u&$5C5)FO8Qyo{hcbI($76dCD2W%LSTwggv8y|o$3}qr5<_|z<`u~Z2 z{sBVZ`G25Jn4TL~?pGETNfsltwhh(!_r-(xX6}1?X}2pE9M7Jp&O9Q2&W~tq{1oj# zkFQ(t=;|*sG!AcYf1CSA@60a0p3(CCq=zRR(VYd*{REHQ!Ug{xWZ-wzwc)_|q4^z+ z!rq9Sy57ym#x>N;)^=3yf)W2I6VCj=9#nLROH<>`N5;4u&K&d@cx8wlwX<1eCWyG0 zN=I;ualK)Q#jO-Xq_;e*IP{70b#Cut)z-K&$>!CLqk}_FQdkBgLZ4h?m{<9K@pcCP zoVb%NFY&S&8Fga^(-xq#1&Q3(7cstL>Kme=a!b82K4VqU`+E!6Sj#jSu_R8)j{ku$ zg!Axs;ir+M0pr!Yj{TyntU^NO%LTuKxLHN07>!r(JfFe+8Ui-aC%{czR*qsDrZy*g zf~;a3JMM551s}G*Y{c&5gzO5>3Veg_hg8I_F{)OHuH2`%gb~%A@kW|&-Z6>)~-+JkL2aU z1gp|xxap@0OEFv|#pD?72>6hIVBkSuO9j?mkF(9+Cs;UYgBf)chmLDZHQI#`%Oi~Z z%A8b5pjgCNh%6W+WvvH1v zf=r!7)_tCzmYe2hbBcCiD|l}e^QxWo5M{PAaJ)MG1XGQx=gEwQE))Se%Q@H_zC5&9 zT>*3+Z1&7jcZJ^K8{3@ZU-Y@jHZ0F$5#wl3@LyXV;V=FF{?>&Cj}OynRqt}3y18)Q zbGMwsp04)AKRI#!JSOGHSt+wz`s2@2<`EJXJP4NWxi6syExeQ>B_1{4_yW%GSo1> zx3j3lH`sGTxqVx=~aZ`E6rVp&e1*r~zzQ zxRnvFNS?)l*;;uF?@IQC_L(+$GNdpV)AKt243 zY*}B_3%^n4bWxPl0(~VtVzK2w_g*DYk-B*4?8W-(BX%xNezNkA2FkL}T0hQThK9&r zhSu%;{WkTqsNj09=AM(4l@;TP2>F_40ZqRLIe8dG8H-U{?vzfs*7)};>FC=I#Aw6= z>d|K7G+JzD}-j&UsPtl&B=J}q=HD6_=A zN*^-H*KNpG#&?PDu0ve&6-oiZuVCU=+5wa^kH+Q?>AKmnR}YP|&t1>Vyq9Ehqhrsq zBZhU~5&!fM$>tNLC*Z(1rAglkqj&9hR|@w!lOWQEQmb8>qON>vS{n)Hs=bTw8&P`} zh5y7dKd`sE)|9>L>EUq>kE{BcSj<}%bMIs6%aLYqYLq(KFtKs}CL85=VhRgJRu?`0?SylrZg! zE%xk@ww0AQE(Hs}XIGZuG+M(NP?H5t3Ho^tsm%x#b zai{vAr%tq~?0|2+FS~#lkH{j?aA)NchfXxR-f1VR#ybO#yOF5D4o7d9A-+G{Zq8K? z`HdrfxEuSq)DbHlweG4H5xx2a+ns)Ugkz_qs)s-612_d{s9O=^2}0-YFE5U_S=~n` zZ})k#4I6G%@Q^&4Ph$#vo)uR7C5V216^f;^?5hfnYBwQmI};~{3 zn>c)vSZg+$bt-1*ZlYG=v3L_2CC)wgdwuZ7V(j^LTkFC&4r*_Kx{Qj73M1)-&ybXg z_h+PDj0iEh?F#n>+jh->GwchdHcIz5NzfyeYcf{v!f@@0lp1!S`Kn$GRmR|$0uOeJ z!WOYK(cb--Lr%;gj-|tc4KC+U?RBEcv)4`|E?7)NGM5FRlhMkK{Mu~E*1#c3p7(3q zc&xPGpp@II%gj(o6B`zTz=bq_KH^VV6AyEhEEKrWi`+}|oB_L(ZRVfP(XnP%m|E7W zze4j8Lo6o|#J3=0n{BHd%bSz;W}fbh<{-UdLBuLY^;@sw;4I}04) z#O~Psw8VxG!DE68o2#=X5A@R*J8PrZl1gOZ;bN7NcH}p1R<7~=w}^DP_~$Wg4Z>2P z8_QEE9HM(2T>GUS&i2NVL|F8{b{tdYLdq?9Y$nIRDN9#^#ph?Jma){^sKCegN4zTY zCJJ>YqeEgi-lWEBH0$lP3%S@t9e{5>sc8|U{1I9=FVwi`e_m~GdnFsyJmmX_Yknt3 zgk4W{=g+631ADlG6kme-Xk%nbws-;OBjap-AMfVPGMNV(YJUJk5m}GAzLu~uqI>=@ zBQS1R*v^mr4cau*qOZ5b^1*CXUxc_rM`;sllC&<*ezrC4~cPL(^=c;sz znb^ek7p#kI$sFIyiuDP1wwr;qv~uSL2S}g8A;GbPn!VAzU4%=FZguX@(@rZI+l#tW z!-&m&CNMOjf1fNZQ?~KQjg4=0lf;8Y5pl?}|e;PNu6K zF^Q;+*!21-9tI!_cZ6owRx2&T3@J}rJ&YV!%IeLdPQv=VMPoM^S(qDX?((*!4z^1U z#xehOej*A^jjbs6|C2Z?*Ueu?iUC+zRQusaHlJhCl@b|FXwnDI!bm&F*dR1fMev|! z?uid$Y-yiM>|yQr^h@4Ild?LCwqRj9H}sD;LyXz-Cg%;G4|lb@7m^m$$-~G;JnTYS ze?DQd1ARAvg3@zsIweg9hD|m7>ZSzsuHpBw9h4Hi8Nq&u3oGFqrz7K|Bj;4B=*9Kh?})*_|m_t&+)2Qk#!y_grL;k=-7R-YZR71>xvS-f6xMlq}w(i)OPTai3ks z+cu(3a_)%u(JhxQoxzsn^V^E;XzN_0gmyeOwzIoA@hE0=e7}T(i|diqUn*{lo#chn zG>O}5iqi_-OsGQEf&pkO zoJd+jw9Xy#%z=z?#mQNHtqUx|ieR?%D35`z1Kr)Yj}mL#9Ps+{*MkzrRkbZ z{3>phdvb4+zielW?y*@Hy%(iN#f$3CUX91r_O9lz@FxAZWLjy-Dinz}Pt04Ny5&+< z+dXM;HHQ2mG7`m}dHI_f+Z&F;^U7Hm*-2a0^xe$0)1Ei!=kwS4qB;N^A6Pn5fM5g; zmrV9Wt>%o1gQ5W-q)xd93z-nvL*19!R+PUgoOFu<2ji2t|9JHXJE?+;l`2JFY~@OE zci`eXaGoQ-W=%JMS;FjST~mxaxqxB45^<{VqHc40@Nf;ZYMc##t|jP+b+y$cM%`dA zSZa(3yYG=y#(t{~_xcNp9gH0vn-DjdeR=0{AQQj848;4E<)DiifGdgr+zs8oxwb!%i z1BI{i0((<)W;0Z+wf~>jUH54bR=gKX_p_137ULa}RaI5ls&$aDxgOPp>h!x^$FCd~ zzK@0&FxJp!|I{oBnd$VGQwgyVAvwLZv1|x~|Aw9~4u(_2=5r_C*VU%0YS_V#@dNnG zBXD`P*QmK}7ip?dg97HMJw4n?1E4_0OSnuoPla?dnEgSCYkSJkWw5{9pD>#87`3H% z`u6f$%YHu1p^MD2Gm(fM`L@}rrJ?$ciG*bl7HWS732fv^2V<1Sn5MVY#~I^!El2L(#e1{0rEhS3y6efK=JfY>SOS=sj{Gvi&I5aP`-hqkZb`-AATi!@3%TH;0*E@W7!U-yXkXU#=#8(w}eKBHz}Cte?0-?b8#Q>gC_P(o2a(48J?Tef;Fg^!uy?E}`ie z;p@ynEV4B@oKa_~zvC$-3Rzed?f89u^>v9}dxW0XQxKz_5Uo_XydWsnhTC zWJA3rG$L@De|}0YG;Wp2ximT{G_K>Bu;0BWAGN_Ix;F~SLM>D5Gn=!~aEliy!x$#O;>Ku z&p&mp8t@chEm8_n{{T(WpUKThorj7MAzhvF)@ex0B6Z2i85$olWA{#98UAYcFseI1AeNA?4#6FSt# zh0HnK*gps>UWy5t`)a$pS{~h1A3S@6i<-zIb|Y|vZyaQv7uBGGLba9F-4)uuC*%@w z@5aKnm*vgfyFZHx1v|}I%~{Divn2YJORVPqQy!k;wewBe_tm(-nWp2cUt?E?nmt|8 z3h%h{!HWuuoTAO6W`VO+v045ji}SGZ8YRE!jXj0kP2@t*lL_yF>};8a@HIP&_6^@8 zk0dg#Vq8DH>bqesOX8F$?WUal{fIJ5?fhZ#Pl{(D+&8zH{5_XVEvZ~VA)ld6^NU?U)PcrwGyyN4fU;DX_pUM0PAVhf*h21+(GdimAE{{;W zS>8=?^{_J&&RlOE7nX%DG+>FmpU3 z^Sk{TjtfUf6lTcjHPb=okj zENdLEHh*t2;@Eb+pGm#GKd|A$-Go~ia`3}_B?8cf)6JjFP}v3#+IoG6hbUE@mHF=M zDT4Bw{!LLkU;i$Qjs{$e-1__eID$O93-WcfVCJKnvThvc=2PsQ;dwFcHD!`o8n4(? zMP%5}TIkx{Yx|@00Nq>;o7szc`NX+l^9JDNZoW{u0aoc!UmLE9exIwNxd*DlQZz$C zcC}ItQX$N`JmL{!-|&F*T5o#m`!`7}kPm?NP7GRB2Ey457hnFH1VX(m8Etd~$<&-6HB**A-UR&c~eeWTv9TO44?g7_fPtxK9T3!>3 z(g!m2R?UnyuF-!!&U4OH4J99}(mjSC=Lh}#$PsIDA*B;twOSzxq3{7=v@3>g%mHCu z=)T(zgyFmlxOA(w0ZxmUt+36Ap^a|6tAgL_-MX$%q#ajLQi;)s+!PhojM^^P*;<>V zukk)U=XyeFOpcAwP(`g$35Q4)&b8mF5nz?#WAdJEi3^asENeSQHpwh)TYrA8EP`?Z z65O%&d+0&_n)9oWxo$vQY?(v4lIr;dCE4aBwMJfDR&}g7z`8sn?qn|ukz~sR#kqj6 z^zCncIxe|J@486QW@i0=?Z_p-q*YO&@2xweEaroX_%6d_*Iw_GJjn^ zhMuvoVOHIj*_#ZX6g>}Fd~fNWcP?I&doXVv;Z$BkHi*^2cU2;*uQTdDYfBxQvdD_*_*UuhK1Ca3|1=RuCG@)}} zLFIn?${{5Irap{@(CIk!%5;_uAJDqcVo!=e0WZjDOM9f20-6j4r%(0NuUY{F5SAvP z3Hk;8@=moDstf9BFxZNbw0lk>Cza_1m$Oy2_RAF zMpDO!Q%-JfeE@)XXFc=%^k&6a&hYmGms;#lK-R4|EyOHeC9qWj&u2qt5+!!H6&*AVBET$pqfMD6DW%Q*dy zWkkR-uOQ~bn}zhb7vBm%Qn$m+uDx1llvNM=)2R`)jqC}>m^(c|E+ch-M?UCcSj0!1 zItUIMg$tOOe4;nR*8rT^kR*ZUu~pZzvi5L*I$!1xa^mBU0RV&%u|A#{J^$?`ufj|* zKfesmg%PGOzWM^;YK%a9uciTpeVA#UydPD*-@tKuDK_z%B&nhj-1;CxBMfkYm#yOr znj*JCYV#$WUXs7x2ZZFPyp(|e?}>DzI0UTaZzm9H$_Sk3Q8zGWAJzwJeSSrY zhxem~fl9r1)M(2R;cN5lw1qDRuN+Ji*#mC0q}h?Ith0>D^vEN~u#GUgVlSsJH&h-3 zyOtMY*6jTj#Mg*#fcm$74A$#j1l7*|TPvxnvjfq^1#3O#T24sD|1OlgXKHqs!e+Z? zcT3^og$!}^`onLS9G4M@ZakiDZuEfPgOxJ^oCkPRD#6AHkQyf=s~LgxUd0XrcKTHi zZx?#F!vzUIFT!E3IJ;2$WPTaJT~OM!%i27V)V*>z(?BBf&yPw%hd5s*hX05FkLoUS z*P_z}i1Ca-srT>>V|IE1Sjg}Ffp7v0gh}|me9kYoWU=$#gq*44x%1y&nScng0;z>&$|&p@Caxeh&cdInRVUP$;cN2WkmJ*E2pHiOD??eB11 zpll6kVEzLro|{H$NLUi2%LuKAoQ~Iyp<{BMnkU6x?>z{8FM1NPc#^sADEZ$v?l7&B zOlJFhu|DYM97b~|sAj+=T74Ec<#}0XabdU3~N-%rD z23hT8Zw|8_TFlWFb~y?_K9Jo63;m9GdlXf3g{D(lBaKO~uywL>hkkZNB z$Kwu)*XJQ~4$L}N`sj`W+GC2rFqKdZ`F+#y_67<<*%w<~UH$g@_3Ot$sax&p?fokO z*yl@NK)^X*b-E3iOlFP&WCERrfv3-u*;k!<3W}oWlQJ^C;3V!&4OSA0mzS6A(m({d zEfVROmwY+KvOw^x)k#$u85tOurEzgpQ=hB;RM6eo=@q@cIKC-${T9+N;9(d${=Mvd z6ZROblgJWxbepiLUff3FR6Oh z_4aKZGVITfuFA5URY-^C4 zucBZ>rYS47Pg(cve+W+XgQBD$0Z9IAdJgC2R@K>DO-nYbrZ+dQH7EVAnZut)L((JxL8m zr!MsX=q3N{uJ+~oLQWLz$_v523@h>Na+gB@&(7?)ZU6n|XMintjs%hr4rGX;lEIK0fL7(H$5nnc zSt8zmgeuAQ^XJcsAkEA>cfK>0-=4?R|M_Tg^-GEUr%S|@A!aA&?XB~gn{B+Bj7N_j zE2u}TFUo?XvE3SxCMVp>d97$k?bUo*8a;q_i%PytPnj8-8qC)Mx#!@Gi(b;|>J7He z&Lff#B3~ITY3>TQh%xZ~qomLhfszTZt;J085HgkU@C%3ny`Ve1yodjOp#N4a@SWIi zOsIhe@znb}rHy+kSD83*{Fuotg>2A$+$=09DR~bd~1Z%Gdo!JUlo5g0Ge%;iI!Q5Hrqoccw+&^yaL7!w*A0HD%7p^JHAyRr#A17 z7;5gWKX_N5zE$|ZAfrqzVx3Z+DQ!@;tlv8bN_t7m9ep+ESLt$oLctwz8QPTcgwOdA zkm)lX2%Cd5bo<5)WmB{_J1>`KJy0yHZaSCr5HMY3X%}EHYe5C4Gb=N_yrCZ3y>vRA z4DL`c<#2ka!VhAI!ARg98fF$bfG+%3(1qwtCji<}iPT^6dw6*GmOe0NbCsBThmUrQ zS`9~keEAK!G4;?O8w<^ytE5D^g(-so8!)t|9%I9zjrg_tsj}aQ)4|8n+3{n(fD_9G zKAbRXwP*Q2za^m{nG=m>>BE}bl-t-H0(8_gsFJMh8g6OeQ3#kMbsSKPJiCA^ix$&} zSl8A!3k`(l6Z6!9_s^h733TIX;ZP4cBO8XS%6^TGVN>IoR+pFejSH*6#rBP*BIbXB zKKlz#gF51;QyagldUXmQ*mAkqv7w$SLV=bfA7>CGv(NWywPiYXU49(*2E_KhxoRL; z+LGhmkP88L;u{LS><6D@`Te%5b!jKGoQ2zx>yl#ZK_+}JH4__f=R)wgto;0KBP*+q zW-Z@i+h6H%A<8!42j1nQ0*_p!Qo2C^yOovoDQvjbUwGQv<0)1DuH#FxA9EzZ8sMk5 zQejOl0%YD}z$aQ#Q&asxYjk#Qz`tVj9@yos!>ZukzqPW1uVWc3kO~QaQMRBj9wjj6 zF1kTfHbs|xMp)Db5t(Bq{6oWj2O*z8pIHvz`$O}a*FS&${4J4N(VspBl?%6ATTbX!$dt;`s zC}|Odwoy})TE8OrW3h$ruhtNNk{)>kK5$;_Q;NF9&_kmvrD#wQyZbe)_6Ljq;CXe4 zm38?u8&&SOAnS4pqhcTHayO5S{b>ki8o>Fyw=-|vgd+y$=l~&ptRxs1nVGGQCmjkw zsj7<1*FFam7;F;FVgMZVy^qw?;%Vk*qZ&PNY1*Ay`yT3IWO9^NyO<~uMRfy(xXIa8 zbu}YMy?()F=sbNmaJlfhl)fK#@7MszrwNTBN<^y_n%r*!i0cmK&Z zU7(WVa(nmwePlFfy3a}9@<^wmS?2wjnsG0AZSWM7gECLt831dw`UhJXU5uxD)9-lI zPnB;gn(WR>)25nGgp8dGpiaiL;%8d|u;rsi8(v5Hf`ftF_vFU+>1T+GEqB-a@b)BY zo``8y1i?4#2`KrdwmHLj((57&zy_1-?zxDyI*o8kgVB;WmzXTXH>?Y=M z#`o$ZmNN8HT;+sFhUPAHh{{>(afFB6BYY|w%QH9H)PyKM153#b-{`bn{><|?6U#VC zJF3dQ?vy5Wv!qLnnG7ofOWc@#EhO&{l%4eX!*RXp6)>%{d0{Nqk_D18H}`T>flo{w zfe7sY&}|kJp}R4CVL82p%;>BywM;0~ht36&G_ZWAL!EO`;hmA}0a1T$-{ac(A@c>P~o* zde&mQGgI37w0Ztuzt0`H8D93V)n7H!FTRmqjXf(aTQ*4a{V@e-c=zddw?#m2Dj=Qv zOI7zVNv(pEYei5=D~qE{J;UviuVES4Vs!kSHz3R*(z9hzpFVwZoSK??{{b;Vrv(!G z?kC9Z_*eCz0;f9xI_3&GAuau2hY8jj^LFsx{xXxfMg?%)1^|Ao`Wx-GEmxjcK2XAz zIiUh3UMENH^i970DMJejDC_z(xKsf2#CJT-QUQkk=f3};Y5EM%RP(CgzS41r8t=1Z zGs(GQDUGz1>Fse(Pfx!HP>J;jWlkoi7}3*JLArF!l+wGYzVZ(C6wo9Y1Fpx#2V(af za07j06sV4N20+}qbxff%^#!o6VEiq$H16-6vdib-rsr^6G(G2`7TJDbb+Ea8glSx| zmgEN$^vFlV-~$6*fDm60A{QNWL2u*w3EeA4e)NNdopY7kyFWS4%5D+3S%*!+Cesjb zkU(zNq)a^d!8prPKeGJi^G|6^sdX+;x1{RF=i}h!-8{~fJwKS) zL>y*deE686q&w)RI06K1K5*OzWEId3_?r2*VYu^jVT2TbMcoT{GI_pnZ>wpqxCv-= z$9W#H2BzQABRPeih*-qYcfEL7N|Ut zZFVnEA2$Kf_8)uy3;Xlt9T6W(uR7(*XOfs5QC2(svb2g!4MQQJ!f8|#x9h{GcA-~15Nk#Aq^ z9hE_#9q*IEJaDyYq5p3LXg&o&Ig-v;?ki8&cL6EsXl~w<4k|!r1*va7g`)6FvP{tR z(&m{BF#yn)UOPOO{{W<@ge}1>tpi_7{B}XOL=Dh#M7MxzMVZh;J=)HCZ&K+gsSw&x zb$y2IpPb5g8Z)mEJRQ40u@C~#lLV2QiJ&XjZ=SR5egWUH^Y`$0JOR0umzOuXD)0E> z&mKT`E_|74O=*cc>d7oUPilOIp4(@gGUGHk#(q=b;<-@!67>(KF)W7ncLxSsDD+JKeu?cR6T#* zSg1_=s9qiSP2cRBtT_gwd0Itfbq359KB}Ed+n~pu1~ZX^pszDP{_@QBQ>4a`(cnTg z6%BXBQ~#1q*-2a`_D!W!c`8BAS-2wwRSH4|nY$_H7HF;pz6bSO)|yYUA()SO1R?{} z@9n}-^0yaeBh<+>wzhSEsu#8ai?r9fKrs2dI~ny6%wTOMgPAHJ6Pv@S>EU0ii9>~v ze}}e!w&6DDzrykz5&a;2rx6Sbj(4QcmC}JHOtb-An=DN%Uny-eZbmi{Rjj+XJp9 zQR=#T#jz@TP)($XtAYLWVA%fO_R|EAd|>YS3bgp1b&)y9KUO@yWFt0z7J+*09F){H zpAxnC^Ym0$1*})i8nm4~LIah)o`jX>bMKOZR@w zB%)w{Z!ZY%937#2!&QJaxPHTJkHkVdzTr3#>Z~X3`uiVelmX6oB>yr!MI3fBNt|`7 zklwd;*45Shb`&iii0w}u1EuM$)y9q|UWOpKeJuPC~_AKy!xBxuj87RA? zn@YhA)N3xa#H|UZzK5(6`VB!#{ifz(^S-p%FU3Mt7v`RTT2ODfTeiG&dt*h{ZVycR zy={)&CstQ;3BZzNFe_Oqq7gwo0I~P(pxS$Cvv0S4vRHZH{pDQUuV=-}nwucvhz==A zVWgw4Za2%8_zIjhPgOex+31bl4Kz*_)a7{42~O4iw{Pk7OK(KnLHU_^03vn3sa+OZ z=JU+wp$^0LWc)f!jxXsntFsZhy0dF9sq-F=h2bVfBoZ$JI-=m?*FTPbRtB8I%?iTj zQ5_sqit-=Oe~wrLec{G)Qktr$XrH&onIWgc1(4hfd^=qvdJfDd-Bg{gm7dV*)!6d6 z4JBLwsOihiM{+ZXgZ$vDa%QhAlcN z=fr-_*I6uyZ=udQnU}&Mq5Id74tkP&>sIJoQkus4*yWG4{se=qOD`(HgrGjN!f_ck z;zBxejzmlBUl2GeD@)n{c$7~#EHjCLhCW`F3MfiS&A5NK3VfG>NuMxD&vGywJB7En zBX27?((L>4`%M?baF1iP*S!b>aFX2mOYAwE&G(qSc2EKMgf59Gds$2ahU12U>ZilJvj-xpL2uVH++cpVO=X~d8l$6!t zr3&6@Sw7C@j0@2wyi&kk#OjBV&M)Jhynax2tOi5%PQR zl^#O~GDKq0;=EQM_0a?!W*sAtICPN%B++z&vi9cyTk$xE1pp#S#5((037a)CCB>kh!mVa-K4mIh^f@H{As|W4$<#_XTt%$$Xh(e%uPr%x(9v z9{rmZm$yD76`*FS-m*xz%C)hC=ZQVU)!WAhzV{e&>uBZ$sp}`CM08UhRQycTJj?!l z7Z>;t6l&SetE$$#R;8{9IMv@VKENY-4fNutM^lT#moUCR`Y@@r|(EmIWVviU&9I? zO)%bms!6X>Uf+2m534`;0noO$6F!GwS*0^FYi?^qA3f{J)X6D4(7(0)M{KjA@u;wkoqohBCfAzMoQ_z8O_)8 zc~Sx4A4VK=CM*S{1QXfMOBDhvS&9x@QGy14f^LM0UW`p|JhoW-$j2 z;2pu7*5>lmTgZ)bpHYjKSnqT1oN#C&7?awvSLeTP<$;lO_^TgMI|H96OHW`R>l7VY zSjD4@+3_6P{Vsb&ns|ROwKe0|s;ja+arOMPV@o-pY&U+%4$FDDY=R;da*Q|y=HkPi zM06Y%K9{*BSkyAB#w{)mmtmHy5sl_|F4%sp?i2UzIYe1jR@>NaE+&3@e0HWjb1_=9 zX-P+_eEWT|p=g|t{VfG5w@^!!Q0rha5^A}4?jzqi3c{JJg=9@ve0n-U9&?iZA)!7` zNFifmDcU@6W14xX8x7pSCI2O4WVJ?CJhpl zd0v`{&JRDGjy?W`CtT-<%VXKA?>SZ3sl|Zb5q3}obRp}onC~O`kZON!?y}``axJTZ zu{{*uw3~nrKf|}M`5Al}Lq_8IC&5KPe{_L%%+1j|Ai)0J+MF$~Jou2eQrJA^u4w7# zi|3__-Ql2m+ST0Kz?y_ zd8RARuhD+&cUhJo)3673nq}2**_An+ohc}ONqhZ+%j5p>Hc>%pn#FP{cW_P&)`j?WdIkhoWqk<2M~WAm@8OvhW9c;pL@n)*u{+ve!U&&cz9 z*iCMBTsvw1mxMFU4ic+uYSh{noRoalPvNXiQ5#bMqmgSvZ$<#4d4}s77~*?W%{qYQ zyhumfB)hoFv`>EsGxgsNL-_psvgOFtJHASbfr%(*DPujBpu!IxmZIp?W|!{Nq) zR}Ig87xr6}vAMtc4gNx}tQO3jhZjaHcco0dP0TCk3_jMi1n`2<+WhyGDyc)V_j3tt zTuX-~^J@~-oj&@D8G+oN{a%dSRo5G^7-LuBCz&wG%vRBM){MI7ckC zr;fKJwmEXTc!@%E&Y4n4Vp0naa*mg;*k~RpNkk%&gTMd==lq_6<^6aPTRuot%9@Mk z)Ya8B2haAm)&KB+lI^ z^evb3mmurLdm)E1Q~C0eoFi)-_WLy?9WE#(vuHVy2GUyOovQQ`RrjTP4Q{r57E0<& zHsq1w>^q1EmLI2YutE+Bj#07fF3cNM}9v$5{4JnOCuzIgVuS^rC||0H81 zyi(K4r8WC3vkUeY^oPT<>Z4+N9W1n$HLiTvN+_!EA*c_)aS%1W`8-bQ!wl!Sha61P zqcs-~<)Jps<|z+aJb2Yo7kkFg|AZMzL*jw2cS0wuas_?@0*9h}#-G!D%Ap8W$sN&&~WBP53#`?-pV=C~bo5@(_- zMri93IbCM?k1JCe23#z!)Shby(^f#<3Zln}Kt}d3^|%WI+%JxFa2`84hRiCxlW`>{ z;fY=xQqRU;o*M#r-(-EhdAvS7yl&h>?3Y}fowLALXmV#!!WfTeuXp7(6ILEE&wULqwE2n*wS~e72Ruso4-z}c)r^rwgx(K?-tZHui}J| z>6Z!IiwegzB5DtdCgB=R^Y=G`nMZy4c{M??W?>EELD)fOQK_+b77PR;IuY}v|8&xi()vrD>!t!@>w9TZap7e; zcTfYrVtfAttlk2SN|x5wm2G<@c24VjGo5-9l{9uf-W?)HoC{MN9^mftkr{U$1~XPO9qJ!`#r*({8+v}!1+q`I zCWn2@yyd{xxB^MYZ}?uZ&pAra^N{DDsjn2pY8G1wQ7u=F*aAWOocV zRn#5~h1EZ|Ki(}u2DpE@c^@x>_XyPW>&t<}z2s#sPswn;@IMlFdvQt3_U=is{MJyd;h}2OI11*D?8muigFn>;i z_~J-k&!yQD26t6zW9}A=CCw6Si!UrE3XCO1m1V=uJzixKwTt|Ao*uM(7%7FE41=Cz z55N3KL9ok=)wxrc;=_>qVB^Zmc>n9-N@YxJaE?}UGVI!>kiOHaB913YeHW*GBb_Gx z4CH7pj}_e zsbg2BAOCaEKz*9>cQ$a~V(65_0o^e>c1jH_6T8%23`SJX6sS6+1pVn#ASaA{N$z}R zik8&pM4o7VmCPx1pY-(Myqe1J$JGOJuB?geqM^woBd+Buqn3gLam2ph2Zaq@N7))}_F z)wNs_(cAu=Ih>Zw)?WV?VecK*RMWMO?t}oLC-h!|NRcYNw@{QWAc%-42ud?3NRbkd z61ph82^K&=ihy(opkP5zks<;H=|y@GMK}}Q_xsMd&bhAh`{((?hwMGG_L?>*V11~S|C^vc_>n& z$+AA*kS4NlA(N9j4!aa{JVCgvDtGxvLba2LFE1 z!iRQUb`L*o#scTHDoUkgIZ+pRu>`-!o!3ZhCx8{R`FdoW9~-@~+OpOkkfa|*;LQuI z08a7r=c~}lmMWXAfAo{jMb})S+B1W=C5cb?PQU*i$6rqAC@Pvzfp}+olSb!Bh&c-i3 zmADqH$Wk$abS<{X|BClT{aeG9($B{zh%CiG9SSlsOv9lfpzdYHAFdG?j$!8Q(K-S{ z-K1z0B2q@ajE>g|J^1b01hA25%L4bERXV_05*Lt8e1B$q++n z437aKpl{h)MOV+WP>%MEhTYm^TL<7bc+3Af@aW?qCjXFXKsKh>j5eKKY4sRgDi}m**I+>0QjXM zf$mSAzL(srSaeS>U30o&wT<3_FM@ul?P@wxCaz7qQq@2&kKy(A zYOsC|XKirx7{Xu6Uiry(kTLerHEtqv!-a_$31bVPEo+v zsEcaps^9t&IM`^x#cQK zJPoZ-@{aCz1z7T}0K!{Y^mBfNN7aPxvmJVSd)b)CQ=#{97W;APj$WpK5n7&>pWkNW zi4hSoAick41bAHT5vB8l2MKq3@3BM?*mWpZTLOJe&e9rjS+Sc-78*CME`qvR&6B@E z%ONW@y$BvE2Y{m+=KJ1oQ@b=+BhO}z(uA6h*`bp!wu{#0jd9ilt{v`1H#JZyF__8({7JuC?a%yIkh z4G{wCi}cnM+Np=xZT;55-Cuh3aGU(#>~XLXm~Z#z_C#X_dPF|vEbhA|;G1u{xLjW@ zK|AGq)OXs?wCmI~!Z{;o47=ps8S&R>9-OmZH(h@(8GS)o7mchczRHArt=#W8J-c=n z{f;9yTEW&7i?iI4+|hDRi%`6+FLVtj#f?>W!q({G>@zv$KlsXg+Q;0oX|p(xd0ia$ z5BH=>4u#Yan~OD%Apm5?*F*hnf9g!oug~235i8oxEN<=ApLc25ZdKSZ{JprI24ZG28A*NEc(7r>xQB3C+(`I zfZarHs(LQ0e5=(?*=#?zsrCNMwwgr}eXkU`2=R!oYHJB}NUfBHQc+&FK;4~k_}l@X zZaJW&6eCK%ZeIB)WnEmI>z*Y0YF&d|>3X)N`rEn&)1{Md4Ej6gDq;SoE|AhMUUF;E z(kGsQt;%1|Tk{b_>0kq~F?P&R_`~*9`(5PxMbghF&*)@YEor2qJ393kms<|`f30;K zIo&U*p2>iWwCg`2wq^cX>*KfmOFKZ7x2F6+(3tcBFx?O@IOhNgsigwcskcCl*L~&> z=b+TGYW!K$JKjlwz~`@9CaTOP|z`CV#_^=tQ~yej&GA zI{qw`w}V8W*XJ8dkv3R?;lUo&=S{LXy8qW49w2JGZy_8GQwKIq-9(wKbL#PC2!yFD zrY)ppqJm~GJ1yRA58Svq`}Rpj-gZAtv@@vnxvIJqAjs$w{fCc0D_lifeDSrC{3_0K ztd1BuQL|b$`KGnHIcais5zH0qv6A6&1C-qa$hG$;Lq9kYjPF#`xP;ai32JKo`)wlA zmgZQ&$$NR_3@-I`4ehVw&$t()L}3-mE6GngmKAaezVSsN&)+L42$paM2)QwD)cS&W zpT1bms{BnPqP5Rjm}M@8T(liX90_8Ryid!6ZjE*dnq}*>Qd1|FYcEx8o#lbGa@I58 zS{~)5T7r&`(CY!02#7LJ(lXJCTyK^F_2y0em@zJb+43ftQ)E9@FJJYi$Pt`M-$!2G zZg&k`XIn}3l=hU16!lW@6iJT3WjPNC|!dQXR8Q8tXdK!8>&q zsibVq8(Z;K1s?9tcTZX*B%MH98N4iBoKnbdR<&}+?sBagL&f%T43hvqagk1O!MQPu z`mEY?TRJV{zdC*ETzTdmtUGKZ%Xgd97@bt?@FT816^=CkP`=Jn zFHjw_TxIQ7d)IeDDaa!v_?`9tt3gO^+1+-~2g{5*OipG3oceR#y?ZCh_AIaQ^2Km! z-#(t58_~GTaM0SO-6o@-_k2dHr*7_~5c*Q%Sp;7JDJoU>{#SC0&j&%?QU#Si zLFdm8?}1Khu%36$dlhnlzX&Eo6~2KpW)of43$7r%QU(+!J75E_M%Od;zQT7r=~aeUUJ-WWjn5UlwpN z>DDS!03fnIduuXHO|l31#^FGpVhiv9a;pxTt+0ZoSs^hx$4L=rc+{RW%lTa~-|rs) zv|vsHsMRCGM9v7|Q^rOHuFr34I-7uAFysHJ9qsuJOrLb6zRoa5`KGj#!wkysFMsxl7xj%-$#>NLWwl<5Ay)}awf_ibPFB4=wY5TX`2he#tdQ826=DHaNOjyxM$wsw zPXma<0}+gEeNQ0!0fM>Hy#r8F_S!%R)Ej{F9#cC0HN)Qdrgg@Ps07v{(VI=eCTqMS z{OHRo#h-)mpo5XM4}h1g)u2NuvV>wJyp?#w*gGHNjJI&Z3FR0x*q56~uRy;1A-F+}UZsK3D8oD}Ip7|=9UQ%%(Yi@r)s9Y;!OpOu zn{?_Ur{5b1zq<|3BmaBidTh{7PB4%Z3y^~}T4RfsTe3@G-y$ihU=Kmgq#+-E7&5+> z;OAuR1t+tJ8gf$rxtK#ByN*Y>0j@5$eG)BjKsg4Y~-u6 zpfW5d$Sc(gjJKz3~eq?Y1c-IOe7fv{roYzOaAF4e`a0X0hvNl-mP!+wAW}g zWAmM$-S6+t5I(U7NifHhpiaody*0qx%5J`qR^}sUkkiRyFhhY>l{#k_t3A>plJc_F z8}urw^6ZcL(WzWZ1G4C#3)tc3uv_Q=3SX5;8yDQTBu2E#O;wi`xqd`24j)gGz}zGt z5jzhtk%!Y7LWhhTGG6BS&*N_s)_VSzVj>Iv|*FpY$i;iR5}tm)q`lOe8X; z+Eq&kb80B$xAxC?=Wgex+&Gi)3h_~?raVI43kMlrNd$&2NOt(~Qs9(%qMG2`IyQHEpWcL>pUpTqHkH9R67&(y zXhK^tS&e5p_W>Vke8h~d_$OD7e{yy6y{%Kw%Ho?<6*4GwkZ|#O6)1Py1my8ubSN7T zPIy>Xr$AR9+_C`dat-Xflxj@?9OgS%V$o|}mw~#*ntCStCuanZZCLLHyVCe)z=V%d zWzzCn_|*3(?9YvU&_7qOQLb;Kb-%!CQC}5nSTF}$DX5iR`AMUvYL=rbE#(JUmTY$VY-;!Uhpzo zDvwR3^T#nQYpg?@u@pMuuIku^@@K|Cco(g7;1z2ZbVucyvobgjA1+oB<(VEca!Pn!2{ zwY=BH-MB|^d>pJWrM{l{{^GCEDb1ym=ldvC{$QP&dQM%|o*xZbCTAPHpWbnwTLsk5 zY)}}E*%8!My#7f?&HoEEJm96tZoMMVy1CHCy(S}SZD`n&2>_Moeio0#(xqU=4;0oR z!j@`MQoj;_MAB0$5VuIf+#@#teeVLmyEw`PZ(m0;hJyJ1a9-{wH|1rYmJ>k3syw&l-S!evwo!$bSJkNt+$?}b6ScET%w1M!%Bwry$@-MBa1n`{2 zcVjvn-Roc9MJf3T5fVx>0tvT}GKh)DYqyM0EIW_se^?NzJUk$2!fAr<$E7$1s1fC| zf+BIUNFM#N&2&SaBB&t4(}pB>J7+|zXgA+Ket?l*d(q>qE(L6#Ry|ujaVLa)mvBg>eng#jv@L!P8}G6FQ;3# zUR9?_1|E5Yw0sUY$CuN@tl*VF`!pHrpp4ZMt*$oS4@3>Z<7d_Xd;WE%{9JOGlX5Kl z#H&x!9!%N?>+@hwY9;~BLhQ~d=|RxO4Ri&0yQfhi=VudT9V9z~o1UG*H!fm;+Mu9d z<_4%JnOT1n38?5l_u^4-8x(6Cd*6r)`LF?tC=i-DWK3vq!VBB8_g6_;8r+e6J}MNe%ZZ0Z+{dZ({^O^RBm6})-vaCi#HpK(#a>= z$>SasLw|~qiHhV8ko6sE8i(jt5YLuO9C@?URVc-PL`Re;X^QXu#XbLMiknf6B_#R0 z3{l2L*l_wl((&~VE2*#Vt{gI=89u`~d{5NvWD;~ZyY-T8lThD`QJ(@cNaM<_W$X7| z39U~t(_g8yh^E0IuICmz$XQW!@&9`>bPh!tYq~5C=iN!QlRJ2zX2YhG(5Z|w6|jId zDDFEmj4*46kj!AqYMXjl-F*Mf=l(tG^o?G&TofKm#`M3B1t&qJCQ!HNMI_*`MmGVY zK&DEp+t%fwsD(7@7w;g&h9*enN`U4fZI00}W>7R@orY`7QqT7w<5;qZP$6=fe(g;OnNtjTg};C^-%Y;cA-Vr)`5H9&?aTpQ;6a zYN#UA0W#oM^xc5cj&4jG@=Gbje9IKGw&O>phFjyy^ym~p2OM1t(a32Vwx_Dk+UdZ^ z;nGO6Y<&gZTH?Xb8{~s9iQ3_@v*?WHrh_Gi-M2e*pnkk4rSzCI74Za#B*h3utsmS= zA78~feM^#E7KKXy6sA_O9>9xMwF02|51Knhz4!lf#9&pCSY1r*KR#eEwCht_EpIKvxpOE|^Zt3qTm-$xL z6tM{RTc8swl=wFTZG)(6+bjEd*^?SWU1Q)Z*Sn8P12Sg=+3Hx=%cz}gpLb#jhfmgw ztNpsVlJ5?rqGajq&IxZ;;53;+z_hY4WMHK_)Qfv)WX(5AoSF%r|8s|>K0xig?P|L|k3MO&vZ zmZ(S%zlxACsHQ!Qj))9OWS_j&_0EF#5^|f8pUh=f5QbiKZq_?;-182_5WOl&N ztJQZxBiCwA4*ktzz5llSulsgxmM+=GNB|&4{hY@VG2k<@!?tyN9p`UNT?&{OY6G=9 zA^jpyFtMp2WQtQ%Y2JLAm`MNTXWMmjvTA;go}*%nDe6ZxN-#%^1^o$C8ou}})5#ZT z9&J|v*wrk+Ut~yJZf~9cOcn$+%|1;jJ%1Mn(ro?&r^(?(wIul;jVI%L<_jL3>E=6O z!Jz*p(MnLq$pFS)%MBRMr|H?`nIry-U6;uq`rQ2oXekp80BScWE-sE+J9VEdJPLp{ znVywRhG)}nt#Qxx^_gC1#1-{D(!LDR%bmGuK%7|v>Ez%=I(A8WW(CAAdP%7tPOgQ2 zL(3>k>_8o;1@O6MJHaQ?Y`P#H*d8n|WnT>y2&e)=Vc5|_3oYHJ*FxdFXpv0VNULh- z`Cx+r2i=rw-GIZC%5w({_gjR-@(DOb55|94L8Sv!ZTvwkx5o!)^DzWLt;VUC_}o=c zNfsI^pU%paCwDkD0S20G-23)z-wMEqTPJm@g1XmGHOP|@KYaV}p8ci|e_V_xUu?>B zymclYocy2ExOX9yegddaTgGKM zftGica18(u>$+}%Mo+ZH^b5r-&~dl{v_z*t^+VvjK)zkln-I63&0SwSbe>)%SE?NV zOuip@`|~@4X3FUvU!KN!Qji6u0D_6B>uxUqEU76)#Zp&HHvU;qR z;J3RJ^_)b;)6`EpZ@x!oeKW?887PlxlkT1yK&M^}822^G1&5$XJHVuik7lR`CBkzh z=6n%iMhc5%wiX*#QwTr}s|E2n4{hl^-IMgv#c0Z~74Yjqfsg#T4vrR$b0R9bi4}HL zIdP*(@~8+w1gezA>i)yZw%xH007e`Te(Tn)whz)Rl8Y}tNSnO4!1C8-w-{LZ?aON) zUV=O{1CY)@#L7_=0Huxw?WpT)Bhvr4eloh=Rew9yhKGVYeArjd-Sf@SEH08c%Htk= z@uaCT@Fi=|?7JHqp(Srm{jdfppNn2o|-4&t&Vqrfr!`6QKNZ ztzywE{fXTY$5Zd>thzBhU}`=s)L zomP(IouL+xnBEQWxeB;IORMe7LBEP~eVPw}+G0l&Hjdfubs6l*j_b8+p&bB&A~XLn zH}%tUauql#cdq+5&tYP4!=657Go3&Tp13&S z)vfvS?Y%WF$%zyK60X~aN?pZl+FJ%Sc9T|Dm9dDuyD6-xT1&wZEuFok6U`T~f8vZY z2q6-fz?h#}zEz@|@0aCg1`a`+F;97SOqgsTR}Ak06{a#@1Y_{|Jm~dVfb3^d5iIPM zm%vO(D<0Y)*?a@|YPIAB5gk~vcxR8I2Y2i|#7-4my5!Op7Yij{A*til@1^35svk(V zZGYJUJZM@Wrv|_#sRwf1=M-gRs=NS4ePevwk#MIXJ;)%QRn-YBFJZt^p#}x806st+ z*aE4U@(KV3PXj;8Q6LEk8!Tx5tyI{k<`eXVHn+dqLFA76y->mQbtwS+XwXw9Wy#|go(3;$YP zWpL&>*wtpw4FNjcLkZim*E<74i(6jP#XQ(*x(fJ8{Mw{?7T>85)(4xVP-KrYRg~ zjY0h48s@Q#wyZJ!vJB=2!I%r(8S{-2bE}4+0dT-WN`KWes|tC!wX2Cv^Uv4yw9MKp!V+{ zxsp>}C2f9vdwn{hBpAk^8|w3kC~xCSUv$v?hfDGY&e{k!0Az&WSw5B@NPp=!7?#!A z2T;4lN9Uv#!(QTJf5uoj-B|u%QT8{iR8fjQ@Y>AZZjDs5?ECT>k34p-l_-`e(8FlW z?v4ek;xF#Y)$wcgT|!!ddJ?Z2eM0^J+R8;{a@x0p55|GU57Egm5wh_D2=}vWXo62G zL3nDGS7v4u_x^kpll}qxfb@n{=ExLFG)Hu8uomzJ-(<8;SZBGZF+{f@g4ujQ(5QkZ z&vRDP6GS7Arr-A3V&EpqhetXHSaP}gk>&CiL}2ruyr_$6UobFPCAnmle`Ulb^OanYS3j8G(v0 zRxNa3e^L?iEe`LF!p~B(l)GQ`|1oE3E4xXd@FR{nPb}$et^@sXYlN(Kx@kq;jmnKp>Cz^8D`v6op zU$elAuClI+C=|G5xooh&?-|U9+XfD>q*{P9x*;{=Is1~f0Vp!nS*55?{f*~1cmv!! zyoeyk9Bk)5tzwUNnf6X1Xh>luu!!D6|0rW#Iw7#+mHJ?PG~ zuawnOFe4hFFmuE~0<_9=QwSqs;Mz{;+gY=Gycz_~5wyN{I6TQ8CsGIJm`*yqEiE63 z+4{{6D#bs{`P9vU%9K7C_qf0)rORkWn=1r{ibg!Xa3olU%47H$7l&kx!Ka$>#p7;? z`GC$|%J=89M4)#>K*RYAX4=G-9*c+p>8x}l3T7dUN`iWI0`;RxN(Nc->DXYJj=f@C znsV1xuodK=Dn71AL82l8^#vWPv$}fXw!Cq@PesHgM~NG#h}U|84lqVPu^i?E;m^rR zj)?6ptjbT>^&oDkFMVrd+Po`Abk>v2=+UH?if=D2Rl|r_zX8Q-$T~kVT!B?Ha|AqoICQ(1~ zj2k~&;YG*OZWF<;v2i-Mx?vj3OHkmnTb@$m7AXWf0Rev#g=9A=xB0A@u>9!e+$T>y zhOpvDYX<-PuiDmA5k=lhY4gReMv5Q&lS_au^M1ti-U1IhK?|qD&N%p22U_9%TjB{T z=Z<0~g6&(ff&B#G8!>n%{7tJ7ZTtFqK?kPzx76%Zp-lu0VT^XXF$elWF;#7p|36-I zkP+6&X4%NeE99rsuFO#Rz;0P@Xf@*_aZInl({ci-OlA9*a=rOD$I(kdo;2oW`T4sQ z^A?1+XX*aAe_lX;GScbg)p5Q2cv2caR%-URH%%lY20F7^OXCs{2Xw1=VP<$w8QL6^ zXS;AU9c)2BtDV<0KcG%OtpkI3He?pjkGa*}tQB5LySZlD@Y~K{ zh6){Y4lD7=b}|gJ=yg(xKdbYc|if@bQa5)@Ya5) zYr+N_Pe4kJtm}B3z=Rq+jqwPLW#ipYE{j7(?Fug)-8OKIB2;jsDi2XjNvAL%4%!_x zUFfSsS}F9uc3(|qfKL8Jf5H?_i*fvf_Du4J4=&;hbmF#ZPX4-se}k0*>+ zkAyf>|9vPqU%%UhYav{B$GCizEC-7Tk8Dhaz}Dn4C92Y?*&6Fn-_zm7j8JhLwXmk^ z?qUG9#VoB+8z8J|*O*gRg}AQ^VqRdM77#QF!wwq=e$1de_yaA*_-J}%BMs$G%sHye z?yoidigcFVxVNl%ZxHMew*Q{khITz+u(DvFQ;EadMyh|adu4EJJqp;$Xe{%Og%8%4 z$l7>gM~jIpzE@{}Kj$M^UmbQzR9 zYOZ+DmciNfWN-9FB7Pg9gY@SyDQc(UkrkCV>t5A$`g(rk?L6-vd}bxtBMR{74H`g~ zK`?9Zkn)|CHm={hFk|13M*QwtK=ye>fwiwAX?jO&0RCQjva1$2Gqb@nUSJw46_sD(Cl;|h%L;Fy1c;!PP zO={SKI;#^7XCyv9&i@i$^fHmvK-}cp*>9O?1k^S0bmrMdX{B{2ho?4IE97@KI@(Ka z7rXTR?QhB7(_FjTKH%?v!>vCkV_++9_U`K6rkfC5U)u`n80$*iq_VtbX5GBX=!M&> zfu5Rjf&%k$Umw3T;+nIpv4dsa7kaXzAJ=oK=As8YDXx3&-Wwhih50Ge+TyfTj#@^5O0(C)?%R)c z-n+i(R2lU84%53}F^sCO7h*j5{lg`Cci+3&xjoWw@B((&>QR9}4_&EoJRV8V(O{|8 z()(hTcef{B(iHaY-%p-@jjfkmsrG)#QTP2@{*f)-H>yk(;xhJ98(P7=tSwW&4!=|N z5;J!vs_+yH^){Ao)nH^e#&TgPubRdfADd*!Mj$`e+Uuc$H| z1Xc$@bR=11S7v4KJgp`_9nXRMDl7Gc_tc9)Kc8?8AH4;Rf{=zuVt2_NF$# ze}_}S18H>r7WN;kk#rE`ha&AtqAkEfLt@Hw$qzlG#-|2ccmVU_U`C^0=yeqAP1pJ- zS9OiAYOnf)KYtM)8zZehE&Jm_gY~?U&;nkIJfQ|#rcBd~j1m3gtf)o)RW;gDl3ps- zn7O*3Br*8t%!yNsPo%xQkrV~y>Cgx__zM(F6gf^$VyL|uI_?))y~GMbIDPd$4^t`k=|yhq%EZx}PUVgPZ#{*ymnlqwg{;K$KW8Dk z2#OD$lJlICOCPE@3cIC}ff4T}P3qaK9uAe&A$%|85W? z^R<-*C~KDFaF*4a+lZGL_j8UjmhFsikxu-acUi$GN{mr((5sml-@&^Q^GZntX=f{7p(7LP zz;$eSs+y=lwiTMaM7^e%>=VnaTB}cgPtB)-NZ@Hyl6KnhkRkd`N zcUki&+TOWeN|W!T>L`P^Hx5-%*{@1Y|@j6V{C%W}QWMfJTS+WwtYGrny zie%@GW_Tk7bo=y?k3+`~gzQK}GI58q10e@1A6_hdL;dJ4!q3KCCk z(N%~M`^4&h9w-afaku8&`eB^T@==r2+WWFXLFy@2G2|}TCm=kOwG^isQ3i#{-|Z~j zi{)KP1t{uo{mU8m(7#!shTC!t8n^q@t?KGKY0An#)S|&jF_OOxE{3QHIl-#w ziO>2KiX4vZSdZZ3P160+$Pl~VWJ?a}rtZqbgZm$u#>`oP)gsH5(*DP4Vl0K}bJgG6 zp6=-y28nr?lUH=)Nn+brRkqJhhDfc8q6A_GVhR5HebiLil;uU9y0OP+iO-{XhjdHj zj^8g1^Awm#fSQ}?QkF8vi$leLc#CQN4(}=LMr-fqldnaHZ+GiWZ=McLSN-qs^}feb0Xc|Fn8SXXHvV$^wQ9;Jj3~84_z1ZJy);AAl_|I?Ljn zd^H&TFCn(|N)>@M$%?V-;gIy{%Ub))W&aJ5mJ~$8+ttsZS242d5Br=wQyJ&j_3Zr4 zCL$eu=F*QCiQnXw?&YvW5YYDxPF@wWO(hR{=8PZ%QDtj(idK%99Bfk1&ILY()y1<9 z*!!Fl5P{XNnYRKx)w{EyFylTU4;v@2^{l36532x!Up2s=&y}q84Qra`2w@`|Wt}#{ z6V|7^bz9z|!FE+~FwRzv;{2wINUhOR3b7khxMe0h>}8fug#bPox&sfShoUt<>_GfV zdq|nn(T4)idsrYX@fY+eyW_HR!9zRs^Pfzs`b=7rZogbSvnz%#LNB$xQo*x=9oVDuzyY2zW_q8oFeZP|C4MstZDJ%SKm_x?ea%R#M_}9fEmQM-J3oCxbWE&v# z@x!@)ActHTd}m&{3wb#R7SLGoUWAQ$ekS~EB!_XeOC3eMS*{4X2yqr#!{?2#GJs7s@`1(nZZx>2I#2IPv(_l zXH)F=m*56RJ4H&R7QN+p?>ViT4jucxTb|Ik(PR%R$sP8aQit;PBbBud)&svAB|NuR z@0>SqPAu!X08fO;4v!y&KTIhGk>H!E80BhmOBUMEVVkLpSeKlx1WbFh| zvjc%38#^B1BzKRR0b0khez&zgUgA{n%aDoyF&{FgvgFaZSnF~P)+cxG>4x3-tJ}3r z+Ij4__<&QthxO<9Z_@5xn>)N&&>i~2G{Dg0Hx(q1eYGIm1(I3)yE~LDvNBFQ6S{8Z z+_WDN-T8ZN);H^w()FFj0JDZSf1V%11u)-YL^}+Zyh!y92_!wp0iF%cI^6f} zBi>`DuXM(VtEGpF3+@jKxmH~0!nl7xPNA?dEgo@#Fq(B_myIp?Hz+6~7DoReJy{e# z=tm&C@d*TsO5erutl^3I4halO5_an9p~x%NN9XxcJU#WRRs2KhK$>Wj+c?GoqKz8t zt{Q){;puD1@#G%|MnhYE@&d}%@gfwXYv~c5d1&Ev4QMXH-DkfSUaHt3MLjl#g0Otd zsvr)hx;)PTcESo;!Uy|oR4sDQ!4#Sny>aC96N7SQTq|~CyZor24OEQx#pnT*H{;A%Cgg5K`1s2_qYZ_#Lqdt>s!tKGy3}=@?-0>{5;S`YUwXdvDzgPxA^x^BQSF>O&=AG1@8n=-OY=RkpW*`6MhqZ z{n_2Zy^~sc!eKl*bkH6wP(;taj2n&111>sE7$R@?$loV$eE1-!of?+`PKo44Asy+Q zwV*kFH&c2xG%`j<6mo(3V7S||qD%H@f(VdyErGTb)i>()<-`4c?o~YWMmU65_o`2X zmn<{gY3<|}@H(1)7FM-0I1-pYNAmply6jTJi+RhxTk?2oFD7`yhTr#Kx;uQ$FOE8Q zMepwr-BnUtYPM*0_sli+?zKt#9c*kZLbC&?QJ)L>&~ThYtfL}WFG8_pXm4rnbdZ@r5)7gC_{tAs8h5>U01Q^kkuO7}|1ir>3MyLw* zd%Vw+J67K^^LSfzg`dYRn?j}^8ntI~{2q@W$EloSxcaK%MyRSbdN@FQ`<)}Ra~?#N z4H_>>Vryd(RAGEDu+;RO zZC{X`CCa#v5nlycQKWd!OJBpEf0AtYjkC^l__NT$*;WN7>Y^~Cd_3Eka7KLYlgVao z)(8m9n;LmG_Ba5t(uBCS2fm_SKllIG%_v|RjLx+9mWfY7J3`V(8;a_8iF`Vl+rqw| zJ^jDj1~%7Jp5?9v@sk(T$U*!7WxyI082^+@lf6ji1kG2d`g4Wl-yq>LN*l|6`sl9) zYvT9gv>lUV`N6w;rz@ThfKkSo7YgEkLGQ%`d>&iW_lN@jy+_Q29AFES&edXZI(Wn% zXo1NYj<16TK3~D0Y=OgAy?N1d90i@>!Y~a4I4MrRwM?jw-+L9+I9%s^h$L;l=6qm? zri7oDXwio9Sfh8pMj;X8GQo9Mwc`{0V?9y`bQyXpd#}denU&&kS}l#KX4H{_Mqu`& z$6?P;t)8sr*D-@QD1LvPxMaK5YODhvPpGYwOTZ&v)4|$R9IsOeU6sZBCmCk`-#qZ{ zjTLUs1w>44fy+Qn=Yd|;8TWWR1uz&NzD(lSsq_$y;oiJ>;2Q~+`DKR3ejcr30EOq~ zKQkDl?PHhuXQ%C=PojkcAbSe@b4^<;(H1=5k|N(yBKeE+>XQ(Ilx>{&T3Cpv7TJAB za`1;{GKKEY_U`DY;`k>T+50zwpn4eY)V+9QV+`;X&tg43a`cbTph=|u1S6s#zh0Z; z?LY?INKZTmg~Ll%o<(?8TtSmwy?6x$LxIv2IessZSa9eLjEX2zJ6=w*FsjrmTI(H< zO+0R*?D#14GXkPGcUpEfA1Z#MA3M~W#nQ+?q=!r(NB*tJQVNW-upq2*77P-cdqk_5 zbwZ&Aimb)M)v{Rf$Wd1Cd?*oC%?a)8l@WBp5NF||F8r_U;YTw8kzjB8i~$?4)EAHA zaG)ppiHnE?34R^2jXPhQ{7Bz3g<{a^q7r9EKgB+=y zb&VAlPM`_0Feusdj1f|9XM0Y%g{|Y3 zzBI*IkTjrj=zy`%<0q4ya3VGM^@gm;(i5`F9hl&s+Z-xgv7xRO=O==DC$};Id!f2R zg2uQIz(+dBw5Bu!Z%)jNIsAWP$2Wr=31-Ch3ISm-gqDDbKJsQJ74^sWgM1DZF^bA! zEL6mc!nRY)I3j&^00P?Rp4J)B*VGYk%0xSwfmRD+*4MqwreB@Z#AzMhnhr2>n8({S z&&JjYiZTcoZ;HRDBBJT zIDgBe--|^D0vAlQN|9zg%d8y827^J|r>UT@q|-5h)}kCuw#~nq*lgPywTXycKKEEF zi^%;om@)BT@+~~Rw^I3%Cu4<>Ef=7clb=AG(=j^spIjvFNO(`TAAxJhRhxSv6C4+R z?+k5yf_eMXs4>ylfx+NAoYiG}W<-~f+)Bku^m4+anfjA3{@NS1-Nzr{bi_l*OKG5z zo-}aZGrm6#Jb-aNtL~@jCJV>^U;$=K&YDuCg+Fl^*q8r;W<;-#?i71|Eb@Qqu_b!^ zxB~5PzTc-U>=!kgG~!M+}~tEq+&9KS`xry4gJ3T3vcc&5&#k zT&8(*vj}-P!cf1#@lZyYV*Lu#+q;Bn!@h|RVIA`a@28z$dPV~hw$jBs0_u1MQkZhX z^e8>zJ5MvXMjF>=)nh+rgj>KC=$s|+E#se`Y1}Q!6yvZkZ@fgF!g#x?U;A(^*!jE< z5gK>e79Bo>pIJc0B2|K3oWZLr+$aNEu@2&~P9YUO$xWRNa>W*Xw9NTrVk7qT=L|2H zg=4(V^0#L!)6qUbE5U>HbI}IBzR0POf8FBuWk`y=Op*7AAT|mDT5P7Y`V2{X>wLK1NRwvih@e+_U z#rm1F?~G4X+pv^+;^ry7n<8(X8P^qDQj>F9$T;>&C;P9`d`}*Z;$VdK)vUxf=z^$VmB>P4Iw{FOC@&stPQ-B|rkRa2} z3Ftcku3CVy760_<2?eiF6d-V93wvW^?|3A<-Y$AcV2l`@d1 zA!?W`=l97`zi#((xzM;Zm-WvSv3@=Z=SfG8!iOD&7w(j5o4#(jJF0{`ud z)Btg~kj=R8Sl#0omh5O=W^?Rl=Ah1O2M5dn55L=4y*401E^LsFzgpDO!J!fMs(6 zfP|d;&H&J-?>mu9((uKbZvZjLckD(97d+)^3LtBR?$}PZ0`<*FR16sz<<=f~Lwm2@ z@QU#%pTd7$-(N|AYcEpJ!iLTOQrdI0^`sN%uHFNeXmINd+Jy#5oxWw?;KS$k@#^(# z7dq|3S+`r@j>?}kz5Lzaa>si>X|f1N+AR#0FaelaBG8V13~o$_x!=C>@Z2@vm> zYo)#ZY_1ij36y-E^wt9~y_n?1SM=bo-n8va=9WOHDkNslcd; zvOHveHnZnXAZ(iD0YDVS5a)T* zfX4D~o{_avRjrZWejXly^xvH>;F2CHJ9f{Hxt`g|HjkF`T`+5{k|Y3zRGJ%@9QpFh zL?D2nCaf|OTp7YC&{TYG+}HbZgoE;FjegBW!9C6mJt*Vs9_b=oD^U67^(Db96Yyv2G4^Caq=Y7BEY=} zpB@{~wSqflV%YL9xsgea@5A$kFNA#}X~zEzeU}c;ecT45ihIKA+$?ewXaGq}$y{^C zhAf_s`T;~uiiE~iq!$2)jnZHNp!AYL-;qgjLNnlM7F-vN=!|K$2$AB(qv0!_Ouqq- zU2jSB#XsI&eI`xUKp5_ky!I;2qOQt!@L`VxH50$WFdv=Nuc3rkVc#Sf=B~( zKVX703`7909VGQI!8k7Yr3(GLkw2$4A7Y8Hdw%W>=^D*Ch%)c8crO2K`BtnAGf%a+ z(*JMbd=Yhb=-xyuwZKz(Qq3$7r0oit|CcKnQ}Npu&%R;r=n1M#fvn~K>03}AAL=fB zN(uk-x-x~CRO7N1_<@;pB$Ofc+oIK&6>*ZbF<5N3Q91oQHvUZb8;~Omxc7pa?yeBz z9f}u~yk#<=lP0u&@w&}v^U`${m6tpC0dkV!Qo%2g251dPq0w?k=gofwLgzGqp73G+ zqU^7m+IV}o#{Jlb+OU45#&n_&tR(j@fZ5&b zyXzEvQEOBnIZnWv^KPe|@20~;h{<)=|M+|YQaClJUFlx6FUyorrr$;QxCqM>RvB!6 zzK=W5rPhxj*>SypaU5}xM~;D^aqxH;ZHt^s4=4M@Rmwg0)akLE(Ve+1hbM0UC9-*8 zys7reY%yEfuzkFCv+l!^dX6miH4v8utHauN$2?;cGyXojQD0o2b+<*_S$zCD+ib|p zHCV}Ac?0P^)Iz2;DX#$%^(ss6thd+V?$zo=_?28R-qPANqMln&_-6hvC2 zQ&3X6d$15t5J5s31XNlY0f$yWx|Htj7!c5J55MPmukZWo{r>d2WaggxoU_l~Yp=Z) z&tzG1Xs>1~X?lcM!H$PquE5-$1We&J-ANm&>H=etWaEp5E*(6pXH0it4BGZs?fO0j zU5zh(q*@PBo^ewRt{{?e!5vdKvPw60Bq2r`?lm38$yoR=8cO`ow=hD`1oC!I!CDB- zDry^8d3LA;G=AOo%iaIB$8uBkt+7x}Qut8jojoleC_*?8j7dx165=|}wJfMopIO|@ z=9H^c%_V3^=LvAlOqzm8<%N5~t?g`Mw01NeiW%8)e11Ui+#X@B=W&&r03nrEbe*-~ zZ76_-sZneY?T#bx_%0jOF_b4Fi?$80fy5_tsR%1RUUs-&0rESoizQ64^xee zr?nsN;u<`hda=+w_O7AI_F*cTIk}_BtjBq>IZOsMrxM_%O zfHdSFc@J*;oRoL<8^j@UZO>(=d9e<>(@%VsLK@I#;@>vkPV#K9E7sj9V z6P^}aOd}9gc7^je2zR9kly~G0aoHs!e~{xqL4}G(-7%Ej zFRbHBS79SZvuANn!P$*UCr3@t?55qtj25<{X~7!+$KoSj&_PO)iKe}=-j)3@QtxKo zJ&?{%kg2RLl7O^0s4kbQg6n;L;{x$5t_^4E$+FkIjO~pwKdEg!Lae7`(&DrUbY{%l z+Mm5EY%jYctRRNHBJ4@?uN7l@oQUm+H=k#xRdNv==dAX`nDHQ4ZXcDAkL;jBd%Gak z{3d^YjA&4x&`^~#aWdb(XGgQ(jjdV7TUZsDHL}fNre)Q2QaM zAhs%7{Z$k539?8Ocg=KVTFI&;KC^guO~VWVTu{$S6fVyt_eRlcKafKABIq4wuhG3^ zGe>rsq?O6{r9|oB%a#F^+nv3IG^>0&Ps;oay*NivEN4LCWK-IDK-qPH;&WIpSLgKd zN=KN$q?hq4i_Uq*Z?5MoKrvd)kXvM13st&&~(pQs5uGK%XbACtT+T0ee) ziDwQ9%WY>aSPmebrt|xP_y;)+X{C;l)G+Nzc75u12KIo6Z4g8QkjpSHQ@iimSC`Zk1}b3;3j)33Ethr7f9Mwsx{Z~abpS@q`RHABiY*dRGKT(U3f0^tZW~HL<;n(8ASw4<_^<<>)5})wy>or zIX2HA&^bYS-cPmB-OM*490g z&wf#UcI;o(*K4pQ)ExmgET{Bc*4Jj5;tp?dyDYhRfqW!=a2BTQ?ohYF{S;*iO>d8& z>Re;yCtbw{pc`5Ahx)LCqP=K|y7~B1mrPhdNlyDE*8Q?8aEujF9@haMUuoh%G|loI zzs9jqSG_j%4t-E;x$RMY4KTuko;ipVlDK`G`GjTmt`? zy&Hj9KnNl35v|`-j_lM&8|$(DRc#mfH>YoZ)}O7Ep5)(GMfQsJ;kA~t_*?}GqhIdO)q{xCIC|=uZa4?#7t7HdeLth3 z;G;D$#j;XUg7+C;n6VAoyE+JA3$io+x=;Flw|zs1gW9WL{xoR)W=+)$6`9L*|LBZD zjTWg7$>zMv;L6Rrh&||$LbIM9R;D##I%o*db1T{5Poo zy}Y7!O+ro_%m^5c7I^X^Q9h|dPi}^=SB5R#yQ)pj+(}`7Ysl!Sd}}77tma|zteFR` zpI=Xl%$q@H&NdF@`eoArUR)g?rh-mtNU+%FLzF}wzb!(T*ZGxixy*mpVs4H<$h7mc8b*7b6r_4vK=Jw#Z4O+?p^U_kn+mEUzbykE}SZ(E#9E+ zf%y4D=aG(2sdL$-O+|csQEdOOi1OS>)-#K|n8u*qH)ddY(PXdb?X{j{Rc7!v@};e7 z?n-9M@8vH%lWpqpH*YXa2y>jBw%_(BXc_R}(6iydP&vqoGM*j~0;CchLwkDm%U6`I z2u{kPix9baBBsp+qlF!zcczq~Z|)GTNNdim$%Awv%dw`LEW7HiSw?gGjBjPMNqOi| zjp>3cKQ&ULoe`Vb7yh%6Xyv7gT7*=aBAAcgOFt5OCPWhKOzgNH!|hZRpAz`sf$>?F z!6um{QnaFA*J7RY1|;@P593YaVhH2c>+_QaJ^#v?{yyxzY5jJ`UKR3w*4 zjK;UgE9Lk7A-DXQ%`@p+Xdm{NenP8uv^FLa^UTDvpxWhbZ7&C8^VRA84{7X@VFk_D zG0EDbt*svfq1Qf?CV8F19H}8@Un3{XMqg0p5tehigBQaa;k+Kpc}VOss9N&-lkPRE zU{7$Nc=;iJR6j-Pte+D}Rna}}YTik`pnlK^7FYJ@dz)Ig?m?1kk>i=zWOM9D8Z-R8aidhkN3 zN6@_k7;HQj0V|&6}9AoLgv3bBSHMRp56SKK_LY?0|H`*D|4r~9+O z0111X4wDXAIED1d(-HwN^WfI3%6-?<5NL?8J7@h`uW{eYubp4LCU;dpjMubo%@^H5n^3Pf^3K(46pfKZwBu?79;Fi5< zU#T4wuS&_$w(%Cd21;L?8_E_=Lw|?bqQU=!e!4U1pH8SSTfQopVsU7*Os;LPC^(tN zJd6*_mj3Q69zc;X{6Ij+=vkuwK$rqj9qU!&XKd%-R@o%DnT32-M1zWKD~=lV*PwID z(YZ(!#Fnm`q>#4cRh5>~Gg;8+o}duwZ*136L0C1lQQeh-H~w*YRyABF@Oj1k{)hZe426RC zz~dU$nIB+jWSCt$AuheVG46fUYi?Tj6u>QP!`2=zkYrXs%g zjMK{;SKm|k9b7)e5eM8?67BY22Gi<=OE^L6!^!=#XYrikEiB~x9tr3mwxUt#t;dC)AbCJ5=t1aF%3W7h zBp;X$bJFH~ejuY~P3fdNE$YPo@;jvyqiP#pEYZEy(@hmZe3u57?oC?anIdY5CYceu zR{dR708~?CRXGmgur7tIQ9tY7vaOtxUeBRsV^DZkLd_+_Rl^X94$fL2?EBT@r_`gJ zqn5}-VTo^4hIgs=u`-1vjnUG9m|tl3&*V6nv!q6U^A)z_k(ZF?i)z2N4U}6GyEw^A z53-rDQV;HWWfJOuiJ}XDaTk7ZSB9wX-HZbuFjAKis(HnU0GN#x04w zV2X561?yI51-Jl>LZu6BE7eqa!;D?QJRvWO>Xa&-hnYL6EO?@9%;bCol_HC)s^)mf zLtHJM>mrG@&zF8Rx*N(pll|uiCV7zvY~__p(XGAD9QbloHDpIFW5%R+{`-M9pJN^% zyw5C>O-|$ndZ?NJ`c0>PTsaSCTXo6DNoIj|Qq5C&$Zfs7f-Za!d>u$|6Pr`vo#hQL z{&{>UM^$~5$o7|*xuWGGoQmvQ?>QN~N;ye6$ydioKX&#edglo>Z#VTQNFX)9)cQ+z z?oRIBL{Xd?pv2>3J!hjHT&iT%PFVWPlL7#E5&O*%Z;)7H6!aB z?h=kSpRmBPE{{5ZLfj^~Yf`hUSzCNK0#yZw`v&n1VwZ5!vrZ10;-<}qU%;n=(T>y5 z=jZ0{`x!|RR+kM7xow#j%yyRgaxaXC*tX2?pYzY4Ng5(wdB~=jlm1{7Kc}vCCQ|BU zg)iR|KR08-%9jF7A#XE#DJMHB?wfA-K4c{ldzGEE(1-g$RY^8xfv4@WZxjY+CLbXJ z?tG=GwmNj^ov$@Z5ptmc%V#_wzdUguAJ3HGp*QIqe4ddmTE?#P;SlhV!fEBjj4@XkPxT zC$Md?@+Xy?8vqEHH>0Q=JmoKJk%!ar+M96hQ@=SZaP~{stg%X0G=DZk-WO-6kZQ-h zG;UVqGL@{u1uAjoksyXL4U?y0g^uBFIrp+!M%!CXFOl|Zs#bKsTcbqIA04(cZGJ}t zMNCgp{o%M8&Sd&47M7`ysW`guqG4u;62h@`9;Pxzg7=!VK3PaE_KoQ z=kQOH1UiZRLinx-TtJ?7`7(?D*s1>mH^LI9F-fp0AA3=cn@#5`TV20$+Oryy!7iuZ zV&ly>^6m(dpZ6Z9sn8Z=#(%Jlj=UiS_;Is^c!&fdYroz8{lz9cu-Y zvj2TWqq)$hi#PF3m;ItfxW?%Q7w0HGkI1Sm6KL@fd7c5ErWL7u0rzRPw+}`0_L5mI zo{S0p*Flr0cyrG+=)VryPFB&^(-Ut=vFKQn)`fEMkA0pK|A8wc@|G~}&n=~qWgqvL z5SN*!N&ox$WJ9G-qKN;#9;t>te@c@;UD6t$MHjr`6-bIv>{pGA@vt(fgjEw{c~>-F z{(ot}Aqj~)1b0HPD1qh%ul!GYlr;F?O6(iI$HyAH;!gAW|MBN0WEOnr4Mg&qg%nU; z&=*#&;1+FfIG~JQd$KodOQG_#{khYa>9s_ka@r1&T>2kwy4s`-A{SvxFsT?i(yJQV zgtjuQgC{RxichQfzkYQnvGln~6@(6Qbk4=tFU*bJg(uG&64SVuHwbU;z$ou_0I5@% zSHiaBSBY?}pGWn0Xi~y2nekkNQ>``jxSpfTb=I~2Yv)*-tr!7~_$7py zOR-Gd&1Xn4VPPupTPMmQu+fD6am%mNP?Il%?%x(YEUW%&{*rh%EFQlmK5hPnO7lvd zmh{u97+!=m(%@Wrs!rBN!_s+vP#F5Ke(%I74`$EbeFY3%c6AGq6dI7PYpM0!3Gx({ zi}VIR<(Hh3Zvjhqo9Hzqo>_4ZCsCuGfWV#Sq70DA(RQM&Y5>!JNKvDMBAV_Rn?E}5%*HV@43mRbS zNeUuRl6V#!V04#obshWqT7MV?hZgReaMn{VYWtPStX%IhnA?Mpx%pbRp-=h$asi^1 zSyaMr#%oHgiRcHXhd0x&d5UwNy)-t2`dB23emie4eM`(#KV3eg8pOvnLO^ddRP-Du z%HHtv`%?(*pMVN59_*|raJWCPvKKS+@S<;Esz?y7XmAO~lp_3)Fbn0M%c!#HmzVSY zeeEx>Nzt+CNYJkE*_yt1&Ksb?=PgH|xh{d49Dt8=aiT9UyM>|*!z_UvL-IYC)$arC zFmu`DDBY(*?(=G+kZpbe%R5FqgA12F=pY{nbgAPPi#Gdt7+F$^+n>*)K-2KCTIPX2 z{4Z!bsMSC;QdnPxz7Kb;FxWU%OMy4;HT2;{{T!+&9F^$SDbZp4iVFdHLmkC|eBgIM zxjW0*Ec0gA5XgJzega#^i-x-(a%xGmfjmR7N+`f7EbNXzd)~m_d+m2ifk*~HYyg$~wVfxklP-U<+^HggbX4|td;Ium zgtE5|C^uU3nN$^9lv;NPFdF$nzZMIZ-+LDzuB3_w90E1mq35mm3pj2bF-+IA0Hc$Hr!`S0%U^M`uL8Jd>y$SaE6ae0DYKYX9N zj(h6J^6MI@@ z(p{D;a9j7b0p4IcK2|#@wK$73*uoPO)#Vo8S=Ca+T`jw-8?I<1zW{M^`wg63D46lW z1V-+Zw~FWsc?EJV5YmvY*UC^Bf^Kca?m4kh^4|19*oOI_ve6Tht4u_*ixMEhnDjBr zJiY31=P_jhLApA1uNOf?k$Q<`i6kB|-K0ub%<I54VxNsV@?T+_lf%PH{VLUyM&+*Gj6%8_4&c~DUedCaR!N| zZ2tO#X>@fhm~xc)Y~Z}%qab8$oIxM0=|W6ArsC3q$Ls-b7jN84xe4F#WO@(WAJYLD zG=!e~8p`617U!8FyebkHLi8i_Ej+Jjg7*-yDiW%H@ufv@^d%4PFiMWrKrmluVH7F>kJ--St}Ra!VRMtbjf%Y3T@`S+O~qF=`FI<4V4fCa zJLs)^gAzQ@tNI=dDPkqjcGD0HoV+`?BqL!lohtr1WSIW-D^}Z_R+nZhcp%$%fY6@- zPq#?WF6q**(uHv_dZ(3knM70?y?lB9pUgy=6Hlr+9v&3cYU}NPQ(G_@?LKl zsC5a0j_zGQkVAiVCS(gtg)cN7ftb(uo9O5p5!hAv&OSl-t9s&C9z(ZDpo93=ZZ?uU z193U(%`Yo(pSg@UR<6mxm#IHBCu|rF4{JcY^^nq|KvG&<70S7sL_E1qf6-1KUH}Ei ziZ&1f<%jG82`Gds0PRPJgqlmh25g_m&J5Fbhe>`D#6wOrGp&SNJtZ7cxR4;9vs9RL zb(L1X|B@Ztv#LA<%$xIxlAiX_#!lbm(zoHC-C%wy1JSjq0(6J8d^Dr1Z3g0qe9-cB zAl?!NigBMLgN_eop@%D+eo-?)015n+MRU-!nwDAXLTlhH)^z&N^Z4nXCx1tH{s?IF zmQ{R`vDfsS4#1r^BF%rj`f&O$fXbxaJD@VWhXstZXDpd@NY_MOUW|rL?QzZNi{-l{ z@Po8*|1rbbz%A1V4gEJUG3J`;>UH7Vt-xAXtLy;@QK2a8I4II>fI2+qlRWiE4gP^} zBA*!fyLWq=fYHP(2};^ea31$P@?5(c6RbF5(;2qAF17;XQYQBKt z*&uRqG@x&}=>176qfsDy1GEkg9KGNa`^-iw?+bMQhTy}BFTfXma!&^dyizj6o6}qq zNk(C7p{h7vZqv+wQ{nD|MZ5}*0K9X~owyPM?yv`ekQMlQZH$l6VSLC6D9-7kEY&B= zVq;^&Km*D~Em>T!`o5yI=B~M<_xej9;nY<^D49QGcKDm(|9}+_6ZOVJ2Db%#<Dr zdphX1E$-a;F$p?d@#06Tr6-+@U^KVrl&TBNCp%aOKNN-!lz)kQ*5-Z$3a7K2QXP+o zcnC2vF(v0ksMLijX}Rw{_yI94!fQ!Ev#z!st_J~^NlYN{H>T=A>uvLvTF9eJ2~~Sl zR3t}tJqX3|dw~*nH~fj}0^tH^WJ#7m+*1J^*31asAb`~KI-Y1Cisimi?RsmH!!Q-y z{OzoaEXmxQ9N8nBo2U59OW^>#zq2u#r}qK4EC2O{rjaQ!U_R{Ei@qC5R#VN~jGo50 zqET8Ety0*AcYwgW4b#XlD6#B<!I(*3j_Z1F7lswnAv$OMhZ%hG1TypeZq{o<>SP-U`lfWhX{1yV~ z0R6RTcfz)j0)Q$`aO9>pxoJUusXu{I^@?_53~UBEJ3Bfapv&W#SED{flH+*2A@@Y# z+b+G9M}?pHCer3ki{7BJeYBquYZYDs+0RglsDoa0e}DhX^Fz>rx9m>85o+C$;Og`5 z?@7RY)*PlK;u=Pgd>>91e|S=*N~bYKxRLqHLF`JpZ3hCz^kWjfZ@+7O-6he)hopaC zxFNA^F-2lr|Mu)3XGhIR;i>Q6bdSu{UwUeNyqlfNRSzj;nRF7^a9%z`x~_TW&bx%S zZwE-^CCKPjEwwkW&+K}RwR0fg)@Re{M}$W08HB|W&LJQlSGOeAxT zLEM>M=)t#Wm6$8c62MXu&e^Pxy>pq7dAkEFi?VjmfBVV!hjHj*B|aCaF9j$ z;6{34XlPi8mk{4I$?IT4WgN75b1K{QsnVo9VncH8ehzFS8E~XHg2jCQ094T;SEe8) zFpNphfDWHg5`;)^cZa;2wpl%f+PK@mbJku9w;!e-V_Fb#5_^v+tngCK((;JAZO?wk z->8kT3;K5rE`tWxmV%beL`rv9-lU|+pfHj2SpA<+NTQU}ZzT#nYy~;N{6#n0=sA@G<)|n)YaE+ zZ3X_|&Sk;|Ecax!bpq9p3BWr3v7fn0pG*g+Oo43OGgmNpT1KS87(aa_GJ!^u89Oip z8fBV%=Jm&W&fuZ*3-E?{rq)0Y>TxJvWQ<_7DS)KnLyMUR6R>`D166w`9rkF}d03AP zpj1apc+y$jVKD~i^)WaP*sPA%eUU|*2e?SmUS~C$tMz zx>!gu{<6E+9d6HYCVP+jKEyeN_n*Qb*K7a6H`d;VV}5*T!GWxRqoRsAbi@tD+%mV2 zpuRpPCn{RL0xH<|mfy!{{Kz;0!>&xoDj0x3*(C;T|MX#V_T4RCj3`T5 zRum>wUXz8uj>Ag%Y+4in$AIq(q-3|&La~cIXTaNi`7>NbrpsCHaI?|>a=dK{u2XV3 zjHEWpoF6O|vXz9kjsJB~(Y*#-=>23;{n_;|GzK=o+I=J7-w77GHuj}b3e9jsKcC7( zRbGr@NaK~{=|E2*o4~J8?4m)LKBsbGZ0zn$Fv{>(Pm`_l0ZR?n-9_!DX&JbyPM(p_ z1>q_tel%XT)-wO!7I?7-E>c?`_NJV{uTaGR8t%VhuJK)=)yEJ^yjH^$Z5t<73K zKQ-aqSR+Q5bM~v*2|y$apty&Y91z=sC-AGiv?j2OBOVxx0?4ycT(QGX(W>e%HT>Q$gBtA_{llnjV}bmdnieh zcb~h=VyzN*`h>%j#r505xm}~fUL5Iz7jUVC7u57uUowh*I!F+-!X+>r;L96;bA_%q z2&T~Dh0O3KTQqUMnu*VBsd60X)3v$p;A;rO^vPa-c-&Hrx?sl#|lUKk>+WEW6 zti-GR;RN=Ma5PJ7&K(sM-x5H%vDyJP4<4k0%Da>-P*Ljuu+P~{rCJO5P`5})WM6W# z(!u@|_PmMVKvlWETdEqzbIDTuGn;n-i%bx)>%H7GcKk6n^|${dCZq^li&)P(Un_Xm z9-6)mm-NkO%n4Kn%sn4p3)RBX+l~y5;KRS;E%zhMb0I)nz8*pa%uw-qxQOXA zlf_E{paAKqa@G79ebRrba&)R)i6c(Pj;`>fzP<=wV5hp2swHl?Vn3nl|Q2k zRt*#RAgj8TpP!!vVzL*21;u)H!`pOF(-S6&Fk>jEYlfd^7r{YV;wb!D;q9g2k{J+d z#9seaPB-+~XPnsL-Ivg;@#-*nD0r?w2~qaZpkzAa?KOS3BC)Zb1&e>2mOuTOhCv;d zP-LFp>Fr34_tSCR)5*_E1Xs$g!lKhi2OMl_Lg|yWnHta5Axa9TWRZ$X8;}G+xCg7I zYE}xb;Y724T1UEE`*{y$V>_Ixm(1t`@7fDm;DL&&FTQ*duF+!Q<#oN1pDQf@vrD-l zKPFD;tRZ>{_pL7B^4U=%@jM0551ty3O7`gbf(~t+SIa z0Cc|JgAW&u?aTc!=;V(fFqww$0!B3fJk{MXC}O@!`0Xx=QE_UREQyc~zy$524@(eHXaJjS4L&P!WKW-?JI@g8wfe8LL!d4-yO*1I`pI{dJbFr zqq9@v9hA1X2(k#&!LcmQ={nIjLob;9LnzH~FAU-o`~O0`#s+8Bx-*)55ruqawFj@@ zw#hSwwW))MbtgrC)sadv938xu5Muv1 z9DRQrXB_kmunN^wAS|2PJbYM3JXr_Uci-;N4Du{Jpt$`A@9(-I{8vK1{naoI)wp!O zz2zD*j{UovQZ-ts`k+wT00RPhwcDFD-(@^tf{ft5XSN_Ra9WJQqi5_0+qdfL?M+a| z;xddeT-w(QL~5^SNFC`4w6h54L&QhjCNW4HZQKA-`Vio3?&Io3?~RGC!IbEs)+pAm z7f1hi)&t~oOhq&=dy_&J+<9*Hd4+%_^|9a-v*@>P zMSYGBwl*Y2KG{QS#DyI>7CI_^ZG9zXFzjglvbG=fZo5tsmSA*L@462!JBLbc$#N?dZoW_xL@YwEzsDSZZYSA!&(jOd-X)W zJ$jL0=7+HQ2GHp_1x>-mY`_s7exfSNX89SsvCHu3)GF`7jN=DYraKEpl&&DxD;Ttc zai5MkL22MytYz_$Sty&CYN)FEOM?AgNgS^+Ewq749F)tUz{km z^a8xYu6}Rtw5}67l@E~UPqx7oW51CJKEVE)gq!-M6eZ4@5cuc~L6vC&_ZwAR#Soqb zsGA7*FpB^EuvTKoT{X0HQ*hPHxXhik%1!PP{$46)CDT|1bsa!$O>HI5Rl!o-0|8`VIT)TbevT-6mt9+Y7UM>}49Y z^>~(YLAm(iD>i7BJOM3Duhr1Ca263lm&9K&Divs@!o_6CQ%o62E`A1s^$^1$v(`8; z4CSI>!Yt&u+~0vS;GKh9KdD)_ZNQLXlRjj3cb5YPWlTVh@FwiK$5`kku`DA_oYaH@ z=VTe8h`=JIz(2sy_=2A|)pExM@-?BV1VXBbe(WbARiKL%ff>epIGsDzpsIq)Gq@UH z9L-0dpf8v^60Um!iSxuiaNx>+O%ydi`oWZ9Qxl!J3})Gnx^X?nupf7@8JjkGE0;l} z?4l5EJ(~oFjh4?C*rkflU`!kIETrnm(YlCE!C)*oy61j>uFmP{WyABB$%EO7zMEy@ za|8m=qR0mXD45NA7ajNfJY~oS3J9b2A7*MU7Q|Gg)<0dTMfp6+iRJdMNl-UOTDZO* z6n{xla%g&j(iD7^&1 zMYw5Oq7(=#HDvgo{mL}TH1G^LOT@2KA}UGD*0p6P2D#5}o*?vwQTNRPHH7g+{>^Km z-P5+u1QEz2ZulY2C$?!V-Yx!fje3+dkx+ntt?iI$f&$~SlR)*D>4MCeaYC2swCjUs zY;A+8iqFpwhCBRZwa4?Jls7hJF3Z^v4lYDmWUViE_Rk&qd| zH|4=x`B!Pk@vL^vyY#o?SId%1qj=1RW!W~Z8dYwN+ zYQ6oOyAsrV!=6(ZD`uflt zBbtz>oUh~J_IDXVLS8lF=9k^DN=K?22`JF<1W;EUby5NS=MvkiZj$dxVQDT8y-`%mHDE#Ecw|H^S zF8$@{TUN~zdd6MQ%0sZ1C0^dh$S4I`b#DXz>~fn_@F)iRd|4f>rUK!4xzAuFq&n_1bM9#8 z`-p{zABD?n*miA=1A$x{EjgZu{go_g&x@P2w2e%I(-&Wy!Bo&2tV&S6?q7S zOUgsqVk|V$CKLB#HVe2`mrsZ{+uW-k^ojm?f-rtx{2tTMj-PVl`*wK3dY-+Kg*oA5 zuywH8moFn-r6}=WF(!F2xsyN=d41taeq2^o{X6Ez{hDIFLJ!i#)(aMb{AyZCH@&eV zo#hMo3q^)vau!x54x7bUGJGlVte~E z#C|Ip8><7;6@7U4g9vyL9 zeEDim>$32>Kk7jQO#u@gb2cJgMn4U06@D}nkLE8{Op zyzjo@(&ym@VMKuQ$`ah%mh}Xa*NBo#1?Fl0_ztwx4UW1yzBN{B=#W{W9uDk~8^*8l zKU&F&1lU^_nI-`G5TOx2{Jlgqjv6j z!^OBRG1HYNXO8~8P1C&uv-+yH&p}(0>NXfY*T9skupf4vccn7wBls%>&%=N*t7(~S zbfA>3B|qsa4OHfBxa5xSugIcsFML)L!>o*A$Jp6b^jei*I}T;OT_-(z-yvAIh38Dk zA?0LRf}Tz4-@?SW_TT*KH^zVD#~)R3zh>AP{Vc>U+QLVcQDtpPH%B=shDeX7)?KZ? zL*@3rTmV**D(~cwiTuX8RkdZt)nRd^pjZorhow%XnrbDjMDu#pKj+mkQTTD)6-AFn zx!dx;3V!{Qc*sLX?BZ>r33=>gCgggy0~oN*JU>@3oIotrTucZi_Gdd%WsdRyKiNLR z|HSVToR6EhqkP!ZA4t>`Wat~M8TAvp;b+OpJ?(?>)PH|iyA_qJvw)1 z<7X1p49UF1C4%J%GQna`8U#5m6iiY7T1YroteJ!?69~@H9T^(m`d1pLR{W>UFLb`t z1c-~SG9T$Kz1BkY_V(J-@|pPjgnRrDsn6>(1( z9~!@*h`a1Us9u~z!`{E;Y-YZlxRbO#2NLTJm%HBimlq`erd}@lYc%J9P-|(qi^3>Y z)DC+wmOZZ&!>Sv9?Mm@Z`f3xortjF(cqm0+X2s%^vo$Rdb%5#EsoL<1SleiL`6^dX zSpvUwF5JmE+`lfdD~Z^BDYH|d{i_V2?|Na6pVOS!!o=$O{$ITaQ?$-zRK!W|T{6hY zr&HviuNADRs+#Av{r&j-hv_H`)MWzDpa@oa#dyoP*!dw1vrzrBGmi&mxQqxzj6<~p?Nn@Ba<0>(oXCNp$StGanyTy!x5ygp8LuV06hQMg^Jff#_7r(d)smL6uFErN5Fmo^*wb{tB*? zVZ)20SOipHhR!@-D`smtsNQ?SEZ?w{03f?o70#5W07qo1B`y*;zcLH1 zU$BF*kyGPw)77VXmOQEw1YN8J60(`DGk9mex=&{)Kep!KvlE(a(z z9n5ch_H>``Y?=eo69T}FzZQ?o$qfrN+3Dx|ImO8; zG?FKehz6&X0wSYE}S}xTz2D*D)>xJp}r-1*^0`?~ncmfVwH!3yT2R7o2oq+$^>F`_M zJkWCj=K^Lw)%Z*pk$)bK<(aoWVhr&f(ux}zp;s;=DEBH}iei zDF{RL6p+}@j^YVAoVQ95AFsUW3~iVm8-tnCpTb5gz1ywGUIa{=P%eus-Nn0|=_0r` zAlT(TiH)UZ7x)P@GhY)6MkA8Npw8)yTMwaCTLrMms~H1*v!iBqnR=Y?1-J(LIKUg_ zM;u&6|5!o9`Y$O2+oZx&0aNpW|$2YO0HJ;!-KmQ_c z$jtv7bRgY4)@waj-Hs&hTXcg-So6}vHZ)xsLloTOp;PmOg;MVqoN$0QS1UZHZG&s? zRimont&Octb)gVMunYOC;rL%jbMKTuGBN+X2r?yCfB+C{vw4Z&?#fURE2G$a#d2^v zbT{l!dH1KjLf@dHlnRJbqL;{xpHYOT@V#u7g=)O0HIliVALW2p-RiM+nsQ|JD$1YG z=z6mpMTqD0mCTil*Yrif_IJ)SG&ZmH2XyO--RADhXnJTC!L)3a&Sdt{>ye@FAi5F( z1nl`ErfI@OkWG@dg0l!^>s4de>F zjgP@$9g72bLxTR*f#ui+Le|+TKUwGkB}jUHa$SVpM}X3+>|*HP6u2_6cl6e*d*HAg z7)d;AgRY~U-#=nZTJ`T;j#}RYczVs(%TX6_Pan9|LT3vOY7H@hT(U48JXE~b8~AN) z(P*B`lVv3@cT)u-W}HZ*8+7K>7Zp#1ys8g_t8!} zj>`P`&R0a~BhW!Pg9RGhyb+>9w6y0@SK4Mp&E=KKn3$}ZnQ&YLg1{vr%k~*3v;ttZ zSpFz3Uxj;w*hnC;HG0IB-X%i2J4^S!tuvLp^UFFAG2g}*#8i>{K8p(gecw*V4Du5! z`VC#a|P8>579;wX5}exE5!0Ya(|v?imIlUEP!ez!^qnDNQ7$ABbn;j`0Q5~p)m zR#wIi3|iIC5mUF>83ip-?$#Fuw&!9q6Yc!n;=w&V?Z(fS<`AKira|gzxInv_EM2q? zIwR363aNV!ZER+yLRn=c?>C~1FhztIv2}S9;`zktrmS8~(B^aE)BqsWkcXqQ_azlweedJlu? zPyn5b`z|nBvHFOFA6otuLoBGT_t3xoO&&r|ZXqvBaNYg$Q+HNN$&UczZ6vG<5{k?5 zxJ7LMFo+499<{b|%+A1MCiwwcn9LNXU8%pW=w!~B90C2DJ_Ew)Ayq&?MzWTxO` zi5tyz%X#t`3+gD+PJXsM4_IvTc9;4%+m6~kR`33IHsQ`!>i2h0rCz9ocFSZNqip=* za(*yu7?`)S2}43uQcyrLg^;!vA(Sn;EwEsg_vy)g51%F3*?mvT38$9B9Gc@ds(cbo%S#kW zQKWe{t-dI*tjt>F4!&QOiGx0--c#K2zrYBhk|VDsy?y&I;IQ&6HaN|1`D0`U-V+u< zp9>^oLkzxS-``yO#FO;d%(jY5TJB;Y*P@0-@ZPY^XJjKF0Fo&t7C_ zeEjLo1HN0At6W#tr=Pr}dg(pKvdcFuk@&?LmwH|vC?CqpX)V;FKEi__5aSk zQq7n_lKmToi9DA-=2~z4%d>YJ7~OY3rjBG|o7FKWwRG_i2*9Q_KyQc|0orxm^B19{ zEZ1$bdHUempk#1TvAj8R&=t7ue#e-)uzLPorHK>?q3UCXd<~V%%PjA{9~k|-?l8#n zBWElC<{V>QeN4{({7hj~Z_}Rd^^p@jp5a%ri=W8#Rg#2lA|Y458@rJa%JN6Az$Sm5 z>WbQjWy#qk+1S0n#(##;JTU(<{=)gD&&N2vFNP5h0#k-Lb;l}wmq+oWHS^Z0-=T>U zQU-;C73MlHUI2{nphd_q<`P&(`mbVwG$2@4P{Qtf<_Na}84dArMmV06jFj zAFw2a8CF*k!>*GV%I2rUwpD1|CO5G{*7M2?E9q^uls2tGrYMu?w=7w+)m~lC^Zb~v!+Eyg5Vbz zzs~Va_MK0hi|tz8eTu85P9_QEd{zR4@T2GjEgi4ipKdHM;N(q_fAyJn5oFjo!gNjg>_ZoSEk*)!`IMvbdh%Q zE!$4X&)aM(*oz4WbJ`2|_k zgqxzxPnFi^-n3IX0njF{{1@@CmUimy%d89gD&-X}eOL$N_^#?I@#zxHp??4VJuVHm zwLebDZab!38VL84pSE8AHe+|#gtUBzpChQSYC62qvPr6jsWExh0ifBX#csa9&NEoR zpi7p4*D%Z}>8IB_&F8rsqt4cbTLMcv=%atKioEElc=YPg@Q`_M;P<-^nN!Ty7kq2U z9tNiI7!;>J+PG#mt#zgrPVyX|Z*FA!jO-HihpQw~zL&X|Y^<4hIDLU}pi z3tqpvDXmaKR9{#54ON7}uB5^D5=NRDPd5R<_~xPe6#N6<9!HjE*mR}prVyq8KjMXu z%~vRX+Ko91-jt!(4F~vFNfP8^C~cn}kuQGNxpB^Bv^}DHr&CH3%FbFqpFh>#g$ERE zl=THltS1m)DwBBAHm`p2njFwwi)#zK>zMO)*BNO>%**P4cf|rv;$rvj-+#)Xo1P}) zHv7%fLGa;tE7i=aY>P3^p#eQ(N0>bLoUo}86}ki7&7b#?;wpm`EwskA5cUQuMx2a2 z^Su3dpe<+Y3vE%gYH$D#rY0T^;#?Xq+T0OUOJ^V!`4LF8Kbw+%^$f$eG{9pW>ksFr z@vmSrfJrj+$amX^)&70pV{&saZvOUUd(^$_BT%3m6hFbIe<_#Hupij2(zI`>NM12O z$18qsOTeAYPNK4kit}SGP3LTIHO0W*h|{G)DDd_tY<|A;oCb<8*Y+2sUgvkVeyQQa z{mJMJ#kcHTGlAcZnvdSQ;qA#Z;nmh32YONoAgR9bgaB({DzT!X0(+iGT%gpd;|J#U zu@|)GzlsAPOk3~@&&1AWA=#)V6ysa?q`4v!eC)+u$C(Vp1T`C@k2^#0yI4x*pgX)piQ)m}1@9megE zZN>D0g0)fHX5BTa&JYta96fY`%<4jpX4+pxl{^mKx4RzfUEi(5LU_nG13w(=ewdWq zeR27R8ruu0zB?)ZG*qr#zSu2sWz6h#*$twlB#tqzuvO2b+rQO&gYVQR_72`6_<#EP z?r^H#|9?&jsjTdMoI^zvnOWJ#F+y3{M6%1~Sjosfc2;o;kx^D0oa|BPkdeL8AuD_T zZoPk>&+qMfUEiPUb-DcGb>H{%e$K~pyqfD0vCQ!FZNzSu!DpGP4Z0kL)Wj64FI;xb z|B`?*=;ZXoT{r@m`40LMog=W^A$>8ulR|zA4yh`~nvWi^+tKLG3ayC6^^+nXI8W>V8Uk zCW*#_TJ*UV+hcb5!$WlC1>*6mj?*ao(mv+k63V)TM6#lTul|J=xESy}Qibqly5UPG zC4ar51}fCPeQnUP*V-yu)bU3Xn35%Pvv~U);)lJfqgfNlA35Ci>lpn{!TY_HXdAVc zP{I>LZEFMdk61)wfM10|#_dp1+o66bJH$F}@Ye z9^E4&NU%g4^VpS8P3BVAAmI_iY_xG$Ey=g{KFbiQg*m?2LFgwlmFH>yTD4vB@`?(! z(zR9=El{d}O(;t;SO)zJ#nqtSq`PjhxR#x4d;KB$Xy~A}N%&UKkCPj;>ZThH!wbw- zsWnO;P`D;p!83X6Cdl6Ge!$o(h||s-DjK|x{8Y*dIY1x;^OU3a#r4)On{l`F@NX=< z8(?$6rm44Q*b%&L>T-W%2&p>H_c1e

OkE27#XE)3w0$+z2|L5(fb(hs5vFrX$pR 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 literal 63619 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

%Ep9;`HQ) zFT7)?N)6dCZiT`kXXhMiK84m-G;*V_GzmyF?P6?1_4!|eIE-5R#(9+vP~7l11`NyD z(J51jm~F4cz{}9fx;ceWyT3#9nqG&~ioD&~jU2X^L}lO|@T|PragRXGx*O|zD;XfV z907XubWqr`;gz*&_0SC`S{&_ubVY1JCSG$xQKq#cvK>&?7k(B&&TKdV-G=z+YM$cA zhQT=jv_IaFCFI9Qp0lc7;F624@O{gFQ#uhUPC4mVto?bE6Cb(Kn6mJ^n6G%-endE{ z_n*xxVG9jLb^@cIooEKntC&Xq9{mblc1SEd#dT#~u>ASf+KsqZPFWj;(fQlX_eLF_ zhM#;ZC#x=150(!H6m_r8Xu`;xHiYeSZ;iX#RUAZKY^;BaDL-{ZiF-1TKU|fyeO7O* zc@0l(;)Eim?&iG}wX0X3E_E^zJ-#`Jy0`G9Jf^Fh>V6wjZ$4N)g+}oRz0!`d`nkW^ zM6?2yltLWx?~o9U@HiXJ|O`UZMFEVX1#I0NeB)SzM! zate4^uY>H^@e%=mYdL7U9p3~XoBBol#yYBN1QY@9LwKS9lyAxP`(MrQ(;3!2_A7&D zaAq$2aZ|I~6+#V1BFPT|&&w)_yfj~m`<&cTbHu+#*^;=g=_5jOLeo7;VtANE0MAgJ z?HjJ_q!=fTlcu>%QtJGZ47XwJpm}&Zd=G{oR@+{i6y6_jnKbQ~Kcm6Kg(KS^;bqJ# zFf5?+@#D1e@WBHkGn4lBKJ8nm?EBp8Ket-N!=>0)w_&e8jmIGta2UZl3NKa>v*pT8 zAyd@IKb1ZwxNk;SFG!lQ2o~z!1L(&M-_`90?`{?TzE?abxd2p4WSFkmA~KYSsmt|K zC4N;(&RY^DlW68dB;HcTjlKy{Kh_BE%KG%i|lo?pKBNH)W>sy%@i{8|7evk>pP4n6Y@K zfB~gsgu5uSwO(CrWSP{*ueGQdxL)2RPq8Uf%)-w_Xx$FLc}c#PId)| zeEwA{XDcF~$b6f%-S_v+L~!mLaoPe(-lj<)h_~96uwpjj&opF{m&OIZs#;0)9E5*H z4%j^P3@82Ti!ZeyOUUB$!_@4t!ohcwt-znJZwk4=FSi5$b;}74iZ!0>-a0Yy1jOY-vbLH1V?L{-R zEXWV`¥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 Date: Sun, 8 Dec 2019 14:34:25 -0800 Subject: [PATCH 041/192] update versioning diagram --- docs/assets/action-releases.drawio | 2 +- docs/assets/action-releases.png | Bin 67579 -> 59557 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/assets/action-releases.drawio b/docs/assets/action-releases.drawio index 342c9c2852..5681fe8faa 100644 --- a/docs/assets/action-releases.drawio +++ b/docs/assets/action-releases.drawio @@ -1 +1 @@ -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 \ No newline at end of file +7Vtbc9o4GP01PIaxLV8fA0mzD+1Mt9lpm6cdYQvjrbEYWRDor1/JlrEtCXAoGFIKD1h3Wed8F30SAzCer58IXMw+4QilA8uI1gPwMLAs03Bc9sNzNmWO4/plRkySSFSqM56Tn6hqKXKXSYTyVkWKcUqTRTszxFmGQtrKg4Tg13a1KU7boy5gjJSM5xCmVe7QqfO/JRGdiXzTDeqCv1ASz8TgviVeeQLDHzHBy0yMOLDAtPiUxXNY9SVeNZ/BCL82ssDjAIwJxrR8mq/HKOWrWy1c1Y5uqtkOwGhG5ylLmOyxKP6wo7HZpTF7OYIy2hxuV39TYxKGQQAMG7lTB94FSv8oYisrkhnO2M8oXJIVisSIjcFzCgkVZGBMACOURfccTZYOU5jnSVhmfkjSqg1LNVvklOAfaIxTTIrhgVF8tiUVlvy1pzjbjmYVXVGy+b7tlyVeWMJgVBDJB85vY5vaVKl1Qr83nhutWKpuxBNVG3WdK2TwkoRIv7iWkARIYkT1VUQvfNEb3QrsnhCeIzZzVoGgFNJk1RYCKKQm3tbbNv2MEzZRyxASbleCKuTb84x2F+VriFY1f9hDYxp1VsGqbgwz3VNT7BCb9nJGh/1e5LcsaxDrpUm6HSw7ijHgMGOsfhgDJMb4MmPKOZ6FMeVAK5guxexDVQ2yLphx4dR5nSUUPS9gsaavzL61GVSo9oJaBUMq7W3qlI9bfBQi2W0iAd4RTJM443qOwYsIr8BY2ehq6vDvPh6sEKFovRdDUWr5bSycCovX2tJVVWZNG2fsRr2F11vAUc3ZtYnzAVV+rNk4SqDdqxFoJ5BIBHoTaEsVaOuiAm1eWKBt73oEGqjggJsGx5HA8a3LgWOr4Dg3DY4na7FLmkL7jyl8gym0D5tCtx9T6IGLmUJXFWgNi25IoF37cgId/0sevn75O35cLb7+8zHHH8PxnWb3MbDclI06mrCHuHggMAvZ3O6rEjbQtpD9wjnHKZvk/IdxFcEc5azOSt3GMMwW/DGEaYqX9DDkC0QS9qIclqrR5zprtMB5QhOcWYUcm/6JTKKkdT1bBcm0+kRJ47Vo4gw3JEe+ezmvRYvQH+O4zzjuI/U+Cwn6sZBOYA490/Es33OYefSCNrdsbemBWKI6iNWmrOuezQxrl9tRdMg0WfNGeb7k+tqYEjzneKCcJlms8JlJJpVp21QWguNNoRdZimLgcp4wdX4vCuZJFKW7FFdbW51Ad3iG0wYC2Kp6NzTKwzqX8rjOmPFWYVw4anys8rD7UR6+vIHueNigdOTKNu18MWjtknqKgpgjNiDLYsLMl2nCHLvfVy34jqSfNa653adW8BU8FNc8JAhSVHIYFeiwyYwKuYh3OuyGOTSGdV+k4cq3e9/bSe/ePbO7p1H/nuzdm6r6d3vdgxkK0qGnmoQbcu+Dq9sma47krsBEv8G/P97UVvQ8uyE1JAUsa9auhlS2yK7ZryFlGCny7N+2PAd+27W59Hbd1Bz6vC957n+/XtG66XPv883/ON5dyXidwaOhzaSkJuSdMTQ6UvKNu0DeScPv64umfdm1QL5bJJ+/dLZrklfUdwTJVENIN7VDNA3ZQ/GAYsZ63SKa6gFbGdSbLH/jAJ4fyI6iGsDrF4azbtVBh636HK/Qob5637ED/ZXtt3uPfjsgrz2P63fHHqiaEOZ8Da5T6AobLyYl7QX4wp9EOcpXVYJAAUmH0dmEsuq4gdH+w+nbgcrf4ZAcOt8+H1a6awiyS1z/20IsewTzmc4j7rxkBz2/xoI4mvWo8n7RQZTx8OR4RVcH0TTl2Ob5Ih+BsyH3m/F/S/jpS4p/Pr08fPO73CY53g6a7y5kDeqQtSKdGkLuvkArHU3YQY8GUAu0GuE6IdDGzQJtudcGtHrz6DRAt2+NWSdBXbUZ7wN1F1wb6upF7HOh/utK/b2iHph+b6izZP2X3dLY1/+MBo//Aw== \ No newline at end of file diff --git a/docs/assets/action-releases.png b/docs/assets/action-releases.png index 18f3d3ba2b9562334d043ea07cde4089081ea9ad..7d07cca5bbe36cb6c58a453449edd580c34e4314 100644 GIT binary patch delta 6109 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 delta 14194 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 Date: Mon, 9 Dec 2019 11:21:43 -0500 Subject: [PATCH 042/192] commit package-lock.json --- packages/github/package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index 4e43a2e4a3..06637fd8d3 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "1.1.0", + "version": "2.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { From aa13e110b1b20585c6b153e4c50c9bfa56cc8e3c Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Mon, 9 Dec 2019 14:02:57 -0500 Subject: [PATCH 043/192] update illustration --- docs/assets/action-releases.drawio | 2 +- docs/assets/action-releases.png | Bin 59557 -> 53296 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/assets/action-releases.drawio b/docs/assets/action-releases.drawio index 5681fe8faa..20ad513df0 100644 --- a/docs/assets/action-releases.drawio +++ b/docs/assets/action-releases.drawio @@ -1 +1 @@ -7Vtbc9o4GP01PIaxLV8fA0mzD+1Mt9lpm6cdYQvjrbEYWRDor1/JlrEtCXAoGFIKD1h3Wed8F30SAzCer58IXMw+4QilA8uI1gPwMLAs03Bc9sNzNmWO4/plRkySSFSqM56Tn6hqKXKXSYTyVkWKcUqTRTszxFmGQtrKg4Tg13a1KU7boy5gjJSM5xCmVe7QqfO/JRGdiXzTDeqCv1ASz8TgviVeeQLDHzHBy0yMOLDAtPiUxXNY9SVeNZ/BCL82ssDjAIwJxrR8mq/HKOWrWy1c1Y5uqtkOwGhG5ylLmOyxKP6wo7HZpTF7OYIy2hxuV39TYxKGQQAMG7lTB94FSv8oYisrkhnO2M8oXJIVisSIjcFzCgkVZGBMACOURfccTZYOU5jnSVhmfkjSqg1LNVvklOAfaIxTTIrhgVF8tiUVlvy1pzjbjmYVXVGy+b7tlyVeWMJgVBDJB85vY5vaVKl1Qr83nhutWKpuxBNVG3WdK2TwkoRIv7iWkARIYkT1VUQvfNEb3QrsnhCeIzZzVoGgFNJk1RYCKKQm3tbbNv2MEzZRyxASbleCKuTb84x2F+VriFY1f9hDYxp1VsGqbgwz3VNT7BCb9nJGh/1e5LcsaxDrpUm6HSw7ijHgMGOsfhgDJMb4MmPKOZ6FMeVAK5guxexDVQ2yLphx4dR5nSUUPS9gsaavzL61GVSo9oJaBUMq7W3qlI9bfBQi2W0iAd4RTJM443qOwYsIr8BY2ehq6vDvPh6sEKFovRdDUWr5bSycCovX2tJVVWZNG2fsRr2F11vAUc3ZtYnzAVV+rNk4SqDdqxFoJ5BIBHoTaEsVaOuiAm1eWKBt73oEGqjggJsGx5HA8a3LgWOr4Dg3DY4na7FLmkL7jyl8gym0D5tCtx9T6IGLmUJXFWgNi25IoF37cgId/0sevn75O35cLb7+8zHHH8PxnWb3MbDclI06mrCHuHggMAvZ3O6rEjbQtpD9wjnHKZvk/IdxFcEc5azOSt3GMMwW/DGEaYqX9DDkC0QS9qIclqrR5zprtMB5QhOcWYUcm/6JTKKkdT1bBcm0+kRJ47Vo4gw3JEe+ezmvRYvQH+O4zzjuI/U+Cwn6sZBOYA490/Es33OYefSCNrdsbemBWKI6iNWmrOuezQxrl9tRdMg0WfNGeb7k+tqYEjzneKCcJlms8JlJJpVp21QWguNNoRdZimLgcp4wdX4vCuZJFKW7FFdbW51Ad3iG0wYC2Kp6NzTKwzqX8rjOmPFWYVw4anys8rD7UR6+vIHueNigdOTKNu18MWjtknqKgpgjNiDLYsLMl2nCHLvfVy34jqSfNa653adW8BU8FNc8JAhSVHIYFeiwyYwKuYh3OuyGOTSGdV+k4cq3e9/bSe/ePbO7p1H/nuzdm6r6d3vdgxkK0qGnmoQbcu+Dq9sma47krsBEv8G/P97UVvQ8uyE1JAUsa9auhlS2yK7ZryFlGCny7N+2PAd+27W59Hbd1Bz6vC957n+/XtG66XPv883/ON5dyXidwaOhzaSkJuSdMTQ6UvKNu0DeScPv64umfdm1QL5bJJ+/dLZrklfUdwTJVENIN7VDNA3ZQ/GAYsZ63SKa6gFbGdSbLH/jAJ4fyI6iGsDrF4azbtVBh636HK/Qob5637ED/ZXtt3uPfjsgrz2P63fHHqiaEOZ8Da5T6AobLyYl7QX4wp9EOcpXVYJAAUmH0dmEsuq4gdH+w+nbgcrf4ZAcOt8+H1a6awiyS1z/20IsewTzmc4j7rxkBz2/xoI4mvWo8n7RQZTx8OR4RVcH0TTl2Ob5Ih+BsyH3m/F/S/jpS4p/Pr08fPO73CY53g6a7y5kDeqQtSKdGkLuvkArHU3YQY8GUAu0GuE6IdDGzQJtudcGtHrz6DRAt2+NWSdBXbUZ7wN1F1wb6upF7HOh/utK/b2iHph+b6izZP2X3dLY1/+MBo//Aw== \ No newline at end of file +7Vtbc9o4FP41PDZjSb4+BpJmH9qZbrPTNk87ii2Mt8ZiZEFCf/1KRsa2JC6hYMhQ8oB1dPX5vnM+WSYDNJq+PjA8m3ymCckH0EleB+huACFwPF98SctyZfH8cGVIWZaoRo3hMftF6p7KOs8SUnYackpzns26xpgWBYl5x4YZoy/dZmOad2ed4ZQYhscY57X1xmvs37OET5Qd+FFT8RfJ0omaPITqlp9x/DNldF6oGQcQjavPqnqK67HUrZYTnNCXlgndD9CIUcpXV9PXEcmld2vH1f34sl7tAA0nfJqLAhCXVfXHDZ3BPp3FzTFS8PZ0m8YbO89xHEXIcYk/9vCHyBifJMKzqljQQnwN4zlbkETN2Jq85JhxRQbBBDQkRXIr0RTlOMdlmcUr48csr/uIUrtHyRn9SUY0p6yaHjnVZ11TYylve0yL9WywGoqz5Y/1uKLwJAqOoIIq3kl+O+vSsi69ZvxH67rVS5SaTrJQ9zH9XCND5ywmdudCFQmYpYTbm6hRpNNbwyrsHgidErFy0YCRHPNs0Q0CrKImXbdbd/1CM7FQ6KgId+tAVfEdBE53iNVtqF4Nf8RFaxmNqWLVfgwD/rEptotNWzljw34r8muWtYj11CbdBpYdxBi0mzGwH8YgjTGhzpjVGk/CmNVEC5zP1epjMw2KIYS4SOq8TDJOHme48umL0Lcug6rUXlGrYkidvYEt+fjVxyCS2yUSkgPhPEsLmecEvITJBoKVraHGnvzbxoMFYZy8bsVQ1cKwi4VXY/HSKF3dZNLWOGcz6h283gKOKWeXFs47UvmhsnFQQPsXE9BepJEI9RbQ0AxoeNaABmcOaDe4nIBGJjjoqsHxNHBCeD5wXBMc76rBCfQsdk4pdP9I4Ruk0N0thX4/Uhigs0mhbwa0hUVXFNC+e76ATv9ld9++/p3eL2bf/vlU0k/x6IPl6WMA/VzMOnwWF2l1wXARi7Xd1jVionWl+MZTiVPxXMovwVWCS1KKNgvzMUZgNpOXMc5zOue7IZ8RlokblbDUnb40puGMlhnPaAGrOAbhkSRRy7qBa4IEYJ8oWXYtlnOGK4qj0D/frsWK0B9x3CaO20i9TSFRPwrpReAmAF4Aw8AT8hhEXW651todZ4nmJLBLWd8/mQxb3R3uzvQxI5iTlT9lFh/IJQ8rjNKN+d8BN85NMxZrKUN39K2D9C4WAsbjbNEDXSyAKRZ+r5LumGoRXLVaRBe367Kc8L4vuTg87df0PHVSDx0t30INzX0Td6hFuA/6TdzAcqAYXnc8R+Fl7f6A5QzxfcVz/9u/mtbt/d+2feLJ84V+ELrnS2NjIF9/Njndu0S7Y0+600N77PSmdEF2jdX7hg/Zf0Dy9uQTdh8PrKcD/W74IgPwKS6lD3QPi1vkeu5pp3yVqNqpW5mM9C4dlgmn36qKaZYk+Sb56WpOla/UojQpkY4/BkhAPziPIgMkG0b6HuFoGEFzU779qOx6oNJe94fIEk+207bTYWU7FNXlvfntl3J7gsuJTd33dtlOgWs5xLP4o7b9pg7qeAT6dndfHQRAfzQ+3cY58pbsdjn6b44/f83pr4enu+/hPmfbh+sgeHcnHqg58TCi00LIza/zvS6sbtSjAFqBNh+Qjgi0c7VAQ//SgDbfgxwH6O47LHgU1E3NeB+o++jSUDd/FnIq1H8/qb9X1CMQ9oa6KDb/QLAS++b/NND9/w== \ No newline at end of file diff --git a/docs/assets/action-releases.png b/docs/assets/action-releases.png index 7d07cca5bbe36cb6c58a453449edd580c34e4314..ee7be55933a45cf62a32aa7314755d05a621f13a 100644 GIT binary patch literal 53296 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 cK520;!+1%GL(>8KW6w0!XY0bnx0V*mgE literal 59557 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 W^nhT`U+@l=gWrA|Hmii`A^Z>cn@C3h From 1d7e38e56ddd316f68442aaf98fe69945e0eda7e Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Mon, 9 Dec 2019 13:26:48 -0600 Subject: [PATCH 044/192] fix unzip error (#223) --- .../tool-cache/__tests__/tool-cache.test.ts | 117 ++++++++++++++++++ packages/tool-cache/src/tool-cache.ts | 6 +- 2 files changed, 120 insertions(+), 3 deletions(-) diff --git a/packages/tool-cache/__tests__/tool-cache.test.ts b/packages/tool-cache/__tests__/tool-cache.test.ts index ff85a2008a..d316fe43da 100644 --- a/packages/tool-cache/__tests__/tool-cache.test.ts +++ b/packages/tool-cache/__tests__/tool-cache.test.ts @@ -142,6 +142,36 @@ describe('@actions/tool-cache', function() { } }) + it('extracts a 7z to a directory that does not exist', async () => { + const tempDir = path.join(__dirname, 'test-install-7z') + const destDir = path.join(tempDir, 'not-exist') + try { + await io.mkdirP(tempDir) + + // copy the 7z file to the test dir + const _7zFile: string = path.join(tempDir, 'test.7z') + await io.cp(path.join(__dirname, 'data', 'test.7z'), _7zFile) + + // extract/cache + const extPath: string = await tc.extract7z(_7zFile, destDir) + await tc.cacheDir(extPath, 'my-7z-contents', '1.1.0') + const toolPath: string = tc.find('my-7z-contents', '1.1.0') + + expect(extPath).toContain('not-exist') + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + } finally { + await io.rmRF(tempDir) + } + }) + it('extract 7z using custom 7z tool', async function() { const tempDir = path.join( __dirname, @@ -229,6 +259,39 @@ describe('@actions/tool-cache', function() { ).toBe('folder/nested-file.txt contents') }) + it('extract .tar.gz to a directory that does not exist', async () => { + const tempDir = path.join(tempPath, 'test-install-tar.gz') + const destDir = path.join(tempDir, 'not-exist') + + await io.mkdirP(tempDir) + + // copy the .tar.gz file to the test dir + const _tgzFile: string = path.join(tempDir, 'test.tar.gz') + await io.cp(path.join(__dirname, 'data', 'test.tar.gz'), _tgzFile) + + // extract/cache + const extPath: string = await tc.extractTar(_tgzFile, destDir) + await tc.cacheDir(extPath, 'my-tgz-contents', '1.1.0') + const toolPath: string = tc.find('my-tgz-contents', '1.1.0') + + expect(extPath).toContain('not-exist') + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + expect( + fs.readFileSync( + path.join(toolPath, 'folder', 'nested-file.txt'), + 'utf8' + ) + ).toBe('folder/nested-file.txt contents') + }) + it('extract .tar.xz', async () => { const tempDir = path.join(tempPath, 'test-install-tar.xz') @@ -363,6 +426,60 @@ describe('@actions/tool-cache', function() { } }) + it('extract zip to a directory that does not exist', async function() { + const tempDir = path.join(__dirname, 'test-install-zip') + try { + await io.mkdirP(tempDir) + + // stage the layout for a zip file: + // file.txt + // folder/nested-file.txt + const stagingDir = path.join(tempDir, 'zip-staging') + await io.mkdirP(path.join(stagingDir, 'folder')) + fs.writeFileSync(path.join(stagingDir, 'file.txt'), '') + fs.writeFileSync(path.join(stagingDir, 'folder', 'nested-file.txt'), '') + + // create the zip + const zipFile = path.join(tempDir, 'test.zip') + await io.rmRF(zipFile) + if (IS_WINDOWS) { + const escapedStagingPath = stagingDir.replace(/'/g, "''") // double-up single quotes + const escapedZipFile = zipFile.replace(/'/g, "''") + const powershellPath = await io.which('powershell', true) + const args = [ + '-NoLogo', + '-Sta', + '-NoProfile', + '-NonInteractive', + '-ExecutionPolicy', + 'Unrestricted', + '-Command', + `$ErrorActionPreference = 'Stop' ; Add-Type -AssemblyName System.IO.Compression.FileSystem ; [System.IO.Compression.ZipFile]::CreateFromDirectory('${escapedStagingPath}', '${escapedZipFile}')` + ] + await exec.exec(`"${powershellPath}"`, args) + } else { + const zipPath: string = await io.which('zip') + await exec.exec(zipPath, [zipFile, '-r', '.'], {cwd: stagingDir}) + } + + const destDir = path.join(tempDir, 'not-exist') + + const extPath: string = await tc.extractZip(zipFile, destDir) + await tc.cacheDir(extPath, 'foo', '1.1.0') + const toolPath: string = tc.find('foo', '1.1.0') + + expect(extPath).toContain('not-exist') + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + } finally { + await io.rmRF(tempDir) + } + }) + it('works with a 502 temporary failure', async function() { nock('http://example.com') .get('/temp502') diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index c932ab774c..b0b695d758 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -130,7 +130,7 @@ export async function extract7z( ok(IS_WINDOWS, 'extract7z() not supported on current OS') ok(file, 'parameter "file" is required') - dest = dest || (await _createExtractFolder(dest)) + dest = await _createExtractFolder(dest) const originalCwd = process.cwd() process.chdir(dest) @@ -199,7 +199,7 @@ export async function extractTar( throw new Error("parameter 'file' is required") } - dest = dest || (await _createExtractFolder(dest)) + dest = await _createExtractFolder(dest) const tarPath: string = await io.which('tar', true) await exec(`"${tarPath}"`, [flags, '-C', dest, '-f', file]) @@ -218,7 +218,7 @@ export async function extractZip(file: string, dest?: string): Promise { throw new Error("parameter 'file' is required") } - dest = dest || (await _createExtractFolder(dest)) + dest = await _createExtractFolder(dest) if (IS_WINDOWS) { await extractZipWin(file, dest) From be9f18b69f2f179cc1fd1cdf27b0a235d05fa2df Mon Sep 17 00:00:00 2001 From: Peter Evans Date: Tue, 10 Dec 2019 23:11:03 +0900 Subject: [PATCH 045/192] Fix documentation links (#217) --- .github/ISSUE_TEMPLATE/enhancement_request.md | 2 +- docs/action-debugging.md | 8 ++++---- packages/github/README.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/enhancement_request.md b/.github/ISSUE_TEMPLATE/enhancement_request.md index 9081c8e8b6..232a56e5e1 100644 --- a/.github/ISSUE_TEMPLATE/enhancement_request.md +++ b/.github/ISSUE_TEMPLATE/enhancement_request.md @@ -11,7 +11,7 @@ Thank you 🙇‍♀ for wanting to create an issue in this repository. Before y * If you have found a security issue [please submit it here](https://hackerone.com/github) * If you have questions about writing workflows or action files, then please [visit the GitHub Community Forum's Actions Board](https://github.community/t5/GitHub-Actions/bd-p/actions) -* If you are having an issue or question about GitHub Actions then please [contact customer support](https://help.github.com/en/articles/about-github-actions#contacting-support) +* If you are having an issue or question about GitHub Actions then please [contact customer support](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-github-actions#contacting-support) If your issue is relevant to this repository, please include the information below: diff --git a/docs/action-debugging.md b/docs/action-debugging.md index 16f01bf88e..af5c88b621 100644 --- a/docs/action-debugging.md +++ b/docs/action-debugging.md @@ -11,9 +11,9 @@ Each file contains different logging information that corresponds to that proces These files contain the prefix `Runner_` or `Worker_` to indicate the log source. ### How to Access Runner Diagnostic Logs -These log files are enabled by [setting the secret](https://help.github.com/en/articles/virtual-environments-for-github-actions#creating-and-using-secrets-encrypted-variables) `ACTIONS_RUNNER_DEBUG` to `true`. +These log files are enabled by [setting the secret](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets#creating-encrypted-secrets) `ACTIONS_RUNNER_DEBUG` to `true`. -All actions ran while this secret is enabled contain additional diagnostic log files in the `runner-diagnostic-logs` folder of the [log archive](https://help.github.com/en/articles/managing-a-workflow-run#downloading-logs-and-artifacts). +All actions ran while this secret is enabled contain additional diagnostic log files in the `runner-diagnostic-logs` folder of the [log archive](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#downloading-logs-and-artifacts). ## Step Debug Logs Step debug logs increase the verbosity of a job's logs during and after a job's execution to assist with troubleshooting. @@ -21,6 +21,6 @@ Step debug logs increase the verbosity of a job's logs during and after a job's Additional log events with the prefix `::debug::` will now also appear in the job's logs. ### How to Access Step Debug Logs -This flag can be enabled by [setting the secret](https://help.github.com/en/articles/virtual-environments-for-github-actions#creating-and-using-secrets-encrypted-variables) `ACTIONS_STEP_DEBUG` to `true`. +This flag can be enabled by [setting the secret](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets#creating-encrypted-secrets) `ACTIONS_STEP_DEBUG` to `true`. -All actions ran while this secret is enabled will show debug events in the [Downloaded Logs](https://help.github.com/en/articles/managing-a-workflow-run#downloading-logs-and-artifacts) and [Web Logs](https://help.github.com/en/articles/managing-a-workflow-run#viewing-logs-to-diagnose-failures). +All actions ran while this secret is enabled will show debug events in the [Downloaded Logs](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#downloading-logs-and-artifacts) and [Web Logs](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#viewing-logs-to-diagnose-failures). diff --git a/packages/github/README.md b/packages/github/README.md index 8f2cbe39b8..12c5b0b078 100644 --- a/packages/github/README.md +++ b/packages/github/README.md @@ -14,7 +14,7 @@ async function run() { // This should be a token with access to your repository scoped in as a secret. // The YML workflow will need to set myToken with the GitHub Secret Token // myToken: ${{ secrets.GITHUB_TOKEN }} - // https://help.github.com/en/articles/virtual-environments-for-github-actions#github_token-secret + // https://help.github.com/en/actions/automating-your-workflow-with-github-actions/authenticating-with-the-github_token#about-the-github_token-secret const myToken = core.getInput('myToken'); const octokit = new github.GitHub(myToken); From bedf8245178b60ad9249687e1fd6f3525c8003f6 Mon Sep 17 00:00:00 2001 From: TriangularIT Date: Thu, 12 Dec 2019 00:02:44 +0100 Subject: [PATCH 046/192] Small typo (#251) --- docs/action-versioning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/action-versioning.md b/docs/action-versioning.md index f452349462..0bb0b34963 100644 --- a/docs/action-versioning.md +++ b/docs/action-versioning.md @@ -7,7 +7,7 @@ Examples: ```yaml steps: - uses: actions/javascript-action@v1 # recommended. starter workflows use this - - user: actions/javascript-action@v1.0.0 # if an action offers specific releases + - uses: actions/javascript-action@v1.0.0 # if an action offers specific releases - uses: actions/javascript-action@41775a4 # binding to a specific sha ``` From 80e91ee891a62ee7fabcb6ecb2854436e3c5d967 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Wed, 11 Dec 2019 18:03:17 -0500 Subject: [PATCH 047/192] Fix typo (#250) --- docs/problem-matchers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/problem-matchers.md b/docs/problem-matchers.md index d9259efb70..ae625ce306 100644 --- a/docs/problem-matchers.md +++ b/docs/problem-matchers.md @@ -76,7 +76,7 @@ The eslint-stylish problem matcher defined below catches that output, and create { // Matches the 2nd and 3rd line in the output "regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$", - // File is carried through from above, so we definte the rest of the groups + // File is carried through from above, so we define the rest of the groups "line": 1, "column": 2, "severity": 3, From 9f6c37ac5226da185f9f114441d70cf88250645b Mon Sep 17 00:00:00 2001 From: Daniel Anechitoaie <10779010+danechitoaie@users.noreply.github.com> Date: Thu, 12 Dec 2019 01:05:18 +0200 Subject: [PATCH 048/192] Updated tc.cacheFile example (#226) --- packages/tool-cache/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tool-cache/README.md b/packages/tool-cache/README.md index e00bb4b055..335f34b6a6 100644 --- a/packages/tool-cache/README.md +++ b/packages/tool-cache/README.md @@ -57,7 +57,7 @@ You can also cache files for reuse. ```js const tc = require('@actions/tool-cache'); -tc.cacheFile('path/to/exe', 'destFileName.exe', 'myExeName', '1.1.0'); +const cachedPath = await tc.cacheFile('path/to/exe', 'destFileName.exe', 'myExeName', '1.1.0'); ``` #### Find From fc45b70f305bdcaf4628bf79a5b7ebfdbbde8c2a Mon Sep 17 00:00:00 2001 From: Adam Dobrawy Date: Thu, 12 Dec 2019 00:07:02 +0100 Subject: [PATCH 049/192] Remove draft-only comments from README (#215) --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 0aa94f9e2d..3980fad2a2 100644 --- a/README.md +++ b/README.md @@ -167,10 +167,6 @@ console.log(`We can even get context data, like the repo: ${context.repo.repo}`) ```
-Recommendations on versioning, releases and tagging your action. -
-
- ## Contributing We welcome contributions. See [how to contribute](docs/contribute.md). From 40a502b14b152a70d92d8789a232036cd73e7571 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Thu, 12 Dec 2019 13:43:34 -0500 Subject: [PATCH 050/192] added matchers to readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 3980fad2a2..1d8b8b6407 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,12 @@ Actions are downloaded and run from the GitHub graph of repos. This contains gu

+:warning: [Problem Matchers](docs/problem-matchers.md) + +Problem Matchers are a way to scan the output of actions for a specified regex pattern and surface that information prominently in the UI. +
+
+

Hello World JavaScript Action

Illustrates how to create a simple hello world javascript action. From 871c495487d4b1b1a1b0177b6d2ceb4e987421bd Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Thu, 12 Dec 2019 14:21:22 -0500 Subject: [PATCH 051/192] update versioning docs for new major versions --- docs/action-versioning.md | 11 ++++++++++- docs/assets/action-releases.drawio | 2 +- docs/assets/action-releases.png | Bin 53296 -> 56658 bytes 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/action-versioning.md b/docs/action-versioning.md index 0bb0b34963..0e4b150f6f 100644 --- a/docs/action-versioning.md +++ b/docs/action-versioning.md @@ -42,8 +42,17 @@ Binding to the immutable sha1 may offer more reliability. However, note that th git tag -fa v1 -m "Update v1 tag" git push origin v1 --force ``` +# Major Versions -4. **Create a new major version if breaking compat**: If the inputs or behavior are breaking, offer a new major version. Typically, this entails branching the previous major version for critical updates and moving master to the new major version. Keeping the major versions available to a minimum is desirable as adding new major versions requires end users understanding the breaks (how to adjust their yaml and expectations). Since you will likely want to innovate on just the latest major version with only critical updates to previous major versions, you will want to encourage users to move to the latest major version. For that reason, it's best to combine breaking changes with new capabilities. +All releases for a major version should hold compat including input compatibility and behavior compatibility. + +Introduce a major version for compatibility breaks and major rewrites of the action. + +Ideally, a major version would carry other benefits to the user to entice them to upgrade their workflows. Since updating their workflows will need to be done with an understanding of the changes and what compatibility was broken, introducing a new major version shouldn't be taken lightly. + +To get feedback and to set expectations, the new major version can be initially released with `v2-beta` tag to indicate you can try it out but it's still going under some churn. Upon release the `-beta` can be dropped and there's an expectation of compatibility from that point forward. + +[An example of v2-beta with checkout](https://github.com/actions/checkout/tree/c170eefc2657d93cc91397be50a299bff978a052#checkout-v2-beta) # Sample Workflow diff --git a/docs/assets/action-releases.drawio b/docs/assets/action-releases.drawio index 20ad513df0..1bdfd71484 100644 --- a/docs/assets/action-releases.drawio +++ b/docs/assets/action-releases.drawio @@ -1 +1 @@ -7Vtbc9o4FP41PDZjSb4+BpJmH9qZbrPTNk87ii2Mt8ZiZEFCf/1KRsa2JC6hYMhQ8oB1dPX5vnM+WSYDNJq+PjA8m3ymCckH0EleB+huACFwPF98SctyZfH8cGVIWZaoRo3hMftF6p7KOs8SUnYackpzns26xpgWBYl5x4YZoy/dZmOad2ed4ZQYhscY57X1xmvs37OET5Qd+FFT8RfJ0omaPITqlp9x/DNldF6oGQcQjavPqnqK67HUrZYTnNCXlgndD9CIUcpXV9PXEcmld2vH1f34sl7tAA0nfJqLAhCXVfXHDZ3BPp3FzTFS8PZ0m8YbO89xHEXIcYk/9vCHyBifJMKzqljQQnwN4zlbkETN2Jq85JhxRQbBBDQkRXIr0RTlOMdlmcUr48csr/uIUrtHyRn9SUY0p6yaHjnVZ11TYylve0yL9WywGoqz5Y/1uKLwJAqOoIIq3kl+O+vSsi69ZvxH67rVS5SaTrJQ9zH9XCND5ywmdudCFQmYpYTbm6hRpNNbwyrsHgidErFy0YCRHPNs0Q0CrKImXbdbd/1CM7FQ6KgId+tAVfEdBE53iNVtqF4Nf8RFaxmNqWLVfgwD/rEptotNWzljw34r8muWtYj11CbdBpYdxBi0mzGwH8YgjTGhzpjVGk/CmNVEC5zP1epjMw2KIYS4SOq8TDJOHme48umL0Lcug6rUXlGrYkidvYEt+fjVxyCS2yUSkgPhPEsLmecEvITJBoKVraHGnvzbxoMFYZy8bsVQ1cKwi4VXY/HSKF3dZNLWOGcz6h283gKOKWeXFs47UvmhsnFQQPsXE9BepJEI9RbQ0AxoeNaABmcOaDe4nIBGJjjoqsHxNHBCeD5wXBMc76rBCfQsdk4pdP9I4Ruk0N0thX4/Uhigs0mhbwa0hUVXFNC+e76ATv9ld9++/p3eL2bf/vlU0k/x6IPl6WMA/VzMOnwWF2l1wXARi7Xd1jVionWl+MZTiVPxXMovwVWCS1KKNgvzMUZgNpOXMc5zOue7IZ8RlokblbDUnb40puGMlhnPaAGrOAbhkSRRy7qBa4IEYJ8oWXYtlnOGK4qj0D/frsWK0B9x3CaO20i9TSFRPwrpReAmAF4Aw8AT8hhEXW651todZ4nmJLBLWd8/mQxb3R3uzvQxI5iTlT9lFh/IJQ8rjNKN+d8BN85NMxZrKUN39K2D9C4WAsbjbNEDXSyAKRZ+r5LumGoRXLVaRBe367Kc8L4vuTg87df0PHVSDx0t30INzX0Td6hFuA/6TdzAcqAYXnc8R+Fl7f6A5QzxfcVz/9u/mtbt/d+2feLJ84V+ELrnS2NjIF9/Njndu0S7Y0+600N77PSmdEF2jdX7hg/Zf0Dy9uQTdh8PrKcD/W74IgPwKS6lD3QPi1vkeu5pp3yVqNqpW5mM9C4dlgmn36qKaZYk+Sb56WpOla/UojQpkY4/BkhAPziPIgMkG0b6HuFoGEFzU779qOx6oNJe94fIEk+207bTYWU7FNXlvfntl3J7gsuJTd33dtlOgWs5xLP4o7b9pg7qeAT6dndfHQRAfzQ+3cY58pbsdjn6b44/f83pr4enu+/hPmfbh+sgeHcnHqg58TCi00LIza/zvS6sbtSjAFqBNh+Qjgi0c7VAQ//SgDbfgxwH6O47LHgU1E3NeB+o++jSUDd/FnIq1H8/qb9X1CMQ9oa6KDb/QLAS++b/NND9/w== \ No newline at end of file +7VvbctsqFP0aP9YjkISkx9hJcx7amZ7mTNs8dYiEZVpZeBC+9esPyMi64UsdW3Ymdh4Mm5tgrbU3AqdnDyfLR46n488sIkkPWtGyZ9/3IASWi+SXsqzWFhf5a0PMaaQrlYYn+ocULbV1RiOS1SoKxhJBp3VjyNKUhKJmw5yzRb3aiCX1Uac4Ji3DU4iTwtp3S/t3GomxtgMUlAX/EBqP9eA+1FN+weHvmLNZqkfsQXuUf9bFE1z0paeajXHEFhWT/dCzh5wxsU5NlkOSqNUtFq5oJ1bF0/bswVhMEpkBMpkXf9zSGBzSWE6Ok1RUh9vW38h6CcMgsC2HoJGLPwSt/kkkV1ZnU5bKr0E443MS6RErg2cCc6HJIJlgD0ga3Sk0ZT5McJbRcG38SJOijcxVW2SCs99kyBLG8+FtK/9sSgos1bRHLN2MBvOuBF/92PQrM88yY0kq6Oy94re1ya2K3JKKH5V0pZXMlY1UpmjTXucCGTbjITEvLtRKwDwmwlxF96IWvdKtxu6RsAmRTy4rcJJgQed1EWCtmnhTb9P0C6PyQaGlFe4UQtX69jyr3sV6GrpVyR+ZqDxGacpZdRjDADo1xfaxaSdnTNjvRH7Dsgqxnquk28Kyoxhjd0MH5NfpgNwGHdaUPQsd1gPNcTIrHFqLHkki44ZixWJMBXma4ny5FjJ01cmRe+2cNTn4hWMGJr+C8k+LI06dI7bqCCc0TpULk8gRripIwlW6GrnqbxfEc8IFWe5EUJfCBhJuIcxFGcSKKuNq+LK2Y15D62+gaUeqa1PqHi99bEQ4Sqtov3eH3cjZDRoksjuTM2zJud/vX1TR4MKKdrzrUbR9Xc720tB4Th0aH14OGucGTQ2apgu7ZBx0bnHwL+Kgsz8Oom7ioGdfLA6im5yrckbO5eQc/+T3377+Gz/Mp9/++5SxT+Hwg/G1AyVy1MGLTMR5guM0lM92V5TIgTaF8htPFE7pS6a+JFMJzkgm68zbZzQSs6lKhjhJ2Ezsh3xKOJUTVbAUjb6UpsGUZVRQlsJcxcA/kc9FjUMBpw0SgF2idNuv1AACln25DYsRoFtk3BUZd3F6V3js6NTHQ6DvAdeDvufK2OgFdW45xtI9B4Tto6XGRg6hs8Vg43L7+x19yAkWZL2eyon3lKoGOUbxVvdvgT7o25u+eCUw1Hvf2UnnsULCeBJXFDjNWAHasQJ1GtGtW7DYhdDlt1xtrr+xYHG80y/IeW6X7lsNbwsbaB7qtv3GqRUC3bptELTUPMGZEk2TQ1IOokmVqkI1r6pK06aWGpW4qHShd7pgQqMo2eYt6i4ip5d+qIbyFVgn2f017uyQZdieewZFg2A7jV6laNh2ubvfg94PVm5jo24f+CrV1OvpsDrkoq28rtfLHuFsbPLGBy/ZXodXWRDXsB6F7ZV+ETSu1bymOzvULwLg7enpdI4xcFf8bjX8NcOfvybsz+Pz/XffcMFysv2s1bfe3H7WLvezLXUaCLn93rVx9uEEBsGeaz9rBNp09nEKoOvnVvAkqLddydtA3XP9K0PddA10HtTBu0U9gAdovWBGJ6i753PqoA/frVNH1rU59fa10ASnqxyoZYmt3D1bhl9NXOPu+fUgOY5bA8kFBpDsM22VjSB5LZCGcs6YpgoXMVbSk5tRkqlt4QT/kgudL0Em+XulmJ0iVDbPcQ3HSYHbIUqG874RVSKi6QamjPA5DWkay7S+zoMWG6mJczKnbJa9JwiDxqtLYFKa6UjwCAxltvwJ/Prdp/xPA/vhfw== \ No newline at end of file diff --git a/docs/assets/action-releases.png b/docs/assets/action-releases.png index ee7be55933a45cf62a32aa7314755d05a621f13a..04b17c2c01ac3a2d2207ecc7c4ad7de4be02f6bf 100644 GIT binary patch literal 56658 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⋘zw01%lr{g6s2qCF2Zq|xsm5@I=2T{ns1h4J;| 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 F{|^F>!;Sy| literal 53296 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 cK520;!+1%GL(>8KW6w0!XY0bnx0V*mgE From d4975510fed64dc22ede73dd0dc0cc91059da797 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Thu, 12 Dec 2019 14:30:31 -0500 Subject: [PATCH 052/192] update illustration --- docs/assets/action-releases.png | Bin 56658 -> 53125 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/assets/action-releases.png b/docs/assets/action-releases.png index 04b17c2c01ac3a2d2207ecc7c4ad7de4be02f6bf..98150ccff867b71da03e8596ff43b2fadc18a73f 100644 GIT binary patch literal 53125 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

qn%$LSbsrr9o*yHye(SozY?}u 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 gbqjQ{`u literal 56658 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⋘zw01%lr{g6s2qCF2Zq|xsm5@I=2T{ns1h4J;| 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 F{|^F>!;Sy| From 17acd9c66fb3dd360ef8c65fcd7c3b864064b5c7 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Sat, 14 Dec 2019 10:11:59 -0500 Subject: [PATCH 053/192] update problem matchers doc for fromPath and default severity (#256) --- docs/problem-matchers.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/problem-matchers.md b/docs/problem-matchers.md index ae625ce306..441d49b235 100644 --- a/docs/problem-matchers.md +++ b/docs/problem-matchers.md @@ -1,13 +1,17 @@ # Problem Matchers + Problem Matchers are a way to scan the output of actions for a specified regex pattern and surface that information prominently in the UI. Both [GitHub Annotations](https://developer.github.com/v3/checks/runs/#annotations-object-1) and log file decorations are created when a match is detected. ## Single Line Matchers Let's consider the ESLint compact output: + ``` badFile.js: line 50, col 11, Error - 'myVar' is defined but never used. (no-unused-vars) ``` + We can define a problem matcher in json that detects input in that format: + ```json { "problemMatcher": [ @@ -33,31 +37,34 @@ The following fields are available for problem matchers: ``` { - owner: An ID field that can be used to remove or replace the problem matcher. **required** + owner: an ID field that can be used to remove or replace the problem matcher. **required** + severity: indicates the default severity, either 'warning' or 'error' case-insensitive. Defaults to 'error' pattern: [ { - regexp: The regex pattern that provides the groups to match against **required** + regexp: the regex pattern that provides the groups to match against **required** file: a group number containing the file name + fromPath: a group number containing a filepath used to root the file (e.g. a project file) line: a group number containing the line number column: a group number containing the column information severity: a group number containing either 'warning' or 'error' case-insensitive. Defaults to `error` code: a group number containing the error code message: a group number containing the error message. **required** at least one pattern must set the message - loop: loops until a match is not found, only valid on the last pattern of a multipattern matcher + loop: whether to loop until a match is not found, only valid on the last pattern of a multipattern matcher } ] } ``` - ## Multiline Matching Consider the following output: + ``` test.js 1:0 error Missing "use strict" statement strict 5:10 error 'addOne' is defined but never used no-unused-vars ✖ 2 problems (2 errors, 0 warnings) ``` + The file name is printed once, yet multiple error lines are printed. The `loop` keyword provides a way to discover multiple errors in outputs. The eslint-stylish problem matcher defined below catches that output, and creates two annotations from it. @@ -94,12 +101,15 @@ The first pattern matches the `test.js` line and records the file information. T The second pattern loops through the remaining lines with `loop: true` until it fails to find a match, and surfaces these lines prominently in the UI. ## Adding and Removing Problem Matchers + Problem Matchers are enabled and removed via the toolkit [commands](commands.md#problem-matchers). ## Duplicate Problem Matchers + Registering two problem-matchers with the same owner will result in only the problem matcher registered last running. ## Examples + Some of the starter actions are already using problem matchers, for example: - [setup-node](https://github.com/actions/setup-node/tree/master/.github) - [setup-python](https://github.com/actions/setup-python/tree/master/.github) From 61d502068b4d1ad228deb3ded5ad129cf00d30ec Mon Sep 17 00:00:00 2001 From: eric sciple Date: Mon, 16 Dec 2019 11:59:48 -0500 Subject: [PATCH 054/192] overload downloadTool to accept destination path (#257) --- .../tool-cache/__tests__/tool-cache.test.ts | 41 +++++++++++++++++++ packages/tool-cache/src/tool-cache.ts | 21 ++++++---- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/packages/tool-cache/__tests__/tool-cache.test.ts b/packages/tool-cache/__tests__/tool-cache.test.ts index d316fe43da..be176f1bee 100644 --- a/packages/tool-cache/__tests__/tool-cache.test.ts +++ b/packages/tool-cache/__tests__/tool-cache.test.ts @@ -47,6 +47,47 @@ describe('@actions/tool-cache', function() { expect(fs.statSync(downPath).size).toBe(35) }) + it('downloads a 35 byte file (dest)', async () => { + const dest = 'test-download-file' + try { + const downPath: string = await tc.downloadTool( + 'http://example.com/bytes/35', + dest + ) + + expect(downPath).toEqual(dest) + expect(fs.existsSync(downPath)).toBeTruthy() + expect(fs.statSync(downPath).size).toBe(35) + } finally { + try { + await fs.promises.unlink(dest) + } catch { + // intentionally empty + } + } + }) + + it('downloads a 35 byte file (dest requires mkdirp)', async () => { + const dest = 'test-download-dir/test-download-file' + try { + const downPath: string = await tc.downloadTool( + 'http://example.com/bytes/35', + dest + ) + + expect(downPath).toEqual(dest) + expect(fs.existsSync(downPath)).toBeTruthy() + expect(fs.statSync(downPath).size).toBe(35) + } finally { + try { + await fs.promises.unlink(dest) + await fs.promises.rmdir(path.dirname(dest)) + } catch { + // intentionally empty + } + } + }) + it('downloads a 35 byte file after a redirect', async () => { nock('http://example.com') .get('/redirect-to') diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index b0b695d758..972eea0b0c 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -48,9 +48,13 @@ if (!tempDirectory || !cacheRoot) { * Download a tool from an url and stream it into a file * * @param url url of tool to download + * @param dest path to download tool * @returns path to downloaded tool */ -export async function downloadTool(url: string): Promise { +export async function downloadTool( + url: string, + dest?: string +): Promise { // Wrap in a promise so that we can resolve from within stream callbacks return new Promise(async (resolve, reject) => { try { @@ -58,14 +62,13 @@ export async function downloadTool(url: string): Promise { allowRetries: true, maxRetries: 3 }) - const destPath = path.join(tempDirectory, uuidV4()) - - await io.mkdirP(tempDirectory) + dest = dest || path.join(tempDirectory, uuidV4()) + await io.mkdirP(path.dirname(dest)) core.debug(`Downloading ${url}`) - core.debug(`Downloading ${destPath}`) + core.debug(`Downloading ${dest}`) - if (fs.existsSync(destPath)) { - throw new Error(`Destination file path ${destPath} already exists`) + if (fs.existsSync(dest)) { + throw new Error(`Destination file path ${dest} already exists`) } const response: httpm.HttpClientResponse = await http.get(url) @@ -80,13 +83,13 @@ export async function downloadTool(url: string): Promise { throw err } - const file: NodeJS.WritableStream = fs.createWriteStream(destPath) + const file: NodeJS.WritableStream = fs.createWriteStream(dest) file.on('open', async () => { try { const stream = response.message.pipe(file) stream.on('close', () => { core.debug('download complete') - resolve(destPath) + resolve(dest) }) } catch (err) { core.debug( From 895bdd6dd53f5d748a551ee3562fc0e48af32c4c Mon Sep 17 00:00:00 2001 From: eric sciple Date: Mon, 16 Dec 2019 12:43:21 -0500 Subject: [PATCH 055/192] remove misleading verbiage (#258) --- packages/exec/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/exec/README.md b/packages/exec/README.md index 7897ba5ad7..53a6bf5243 100644 --- a/packages/exec/README.md +++ b/packages/exec/README.md @@ -4,7 +4,7 @@ #### Basic -You can use this package to execute your tools on the command line in a cross platform way: +You can use this package to execute tools in a cross platform way: ```js const exec = require('@actions/exec'); From 606e1f27ac74e340d7bb011cee26e8fe8ef18ab8 Mon Sep 17 00:00:00 2001 From: Yusuke Sakurai Date: Wed, 18 Dec 2019 01:03:58 +0900 Subject: [PATCH 056/192] add: "types" to each package.json (#221) fix es #148 --- packages/core/package.json | 1 + packages/exec/package.json | 1 + packages/github/package.json | 1 + packages/io/package.json | 1 + packages/tool-cache/package.json | 1 + 5 files changed, 5 insertions(+) diff --git a/packages/core/package.json b/packages/core/package.json index b66c5baec3..e84993c656 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -10,6 +10,7 @@ "homepage": "https://github.com/actions/toolkit/tree/master/packages/core", "license": "MIT", "main": "lib/core.js", + "types": "lib/core.d.ts", "directories": { "lib": "lib", "test": "__tests__" diff --git a/packages/exec/package.json b/packages/exec/package.json index f283e8eec0..42d9148bb7 100644 --- a/packages/exec/package.json +++ b/packages/exec/package.json @@ -10,6 +10,7 @@ "homepage": "https://github.com/actions/toolkit/tree/master/packages/exec", "license": "MIT", "main": "lib/exec.js", + "types": "lib/exec.d.ts", "directories": { "lib": "lib", "test": "__tests__" diff --git a/packages/github/package.json b/packages/github/package.json index 36aeea4afb..e49db4a46e 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -9,6 +9,7 @@ "homepage": "https://github.com/actions/toolkit/tree/master/packages/github", "license": "MIT", "main": "lib/github.js", + "types": "lib/github.d.ts", "directories": { "lib": "lib", "test": "__tests__" diff --git a/packages/io/package.json b/packages/io/package.json index 3214676c30..9530062b66 100644 --- a/packages/io/package.json +++ b/packages/io/package.json @@ -10,6 +10,7 @@ "homepage": "https://github.com/actions/toolkit/tree/master/packages/io", "license": "MIT", "main": "lib/io.js", + "types": "lib/io.d.ts", "directories": { "lib": "lib", "test": "__tests__" diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index a9816ae0de..8405522245 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -10,6 +10,7 @@ "homepage": "https://github.com/actions/toolkit/tree/master/packages/exec", "license": "MIT", "main": "lib/tool-cache.js", + "types": "lib/tool-cache.d.ts", "directories": { "lib": "lib", "test": "__tests__" From f79897266ee00417d0fd7ce49ab61982045e8673 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Tue, 17 Dec 2019 12:48:13 -0500 Subject: [PATCH 057/192] fix errors during npm install (#262) --- package-lock.json | 116 +++++++++++++++++++++++++++++++++------------- 1 file changed, 85 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index c352e053a3..90a0da8a64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3382,6 +3382,16 @@ "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==", "dev": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bluebird": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", @@ -5378,6 +5388,13 @@ "flat-cache": "^2.0.1" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -5526,14 +5543,15 @@ "dev": true }, "fsevents": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.8.tgz", - "integrity": "sha512-tPvHgPGB7m40CZ68xqFGkKuzN+RnpGmSV+hgeKxhRpbxdqKXUFJGC3yonBOLzQBcJyGpdZFDfCsdOC2KFsXzeA==", + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz", + "integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==", "dev": true, "optional": true, "requires": { + "bindings": "^1.5.0", "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" + "node-pre-gyp": "*" }, "dependencies": { "abbrev": { @@ -5581,8 +5599,10 @@ } }, "chownr": { - "version": "1.1.1", - "bundled": true + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true }, "code-point-at": { "version": "1.1.0", @@ -5609,7 +5629,7 @@ "optional": true }, "debug": { - "version": "4.1.1", + "version": "3.2.6", "bundled": true, "dev": true, "optional": true, @@ -5636,10 +5656,12 @@ "optional": true }, "fs-minipass": { - "version": "1.2.5", + "version": "1.2.7", "bundled": true, + "dev": true, + "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "^2.6.0" } }, "fs.realpath": { @@ -5665,7 +5687,7 @@ } }, "glob": { - "version": "7.1.3", + "version": "7.1.6", "bundled": true, "dev": true, "optional": true, @@ -5694,7 +5716,7 @@ } }, "ignore-walk": { - "version": "3.0.1", + "version": "3.0.3", "bundled": true, "dev": true, "optional": true, @@ -5713,7 +5735,7 @@ } }, "inherits": { - "version": "2.0.3", + "version": "2.0.4", "bundled": true, "dev": true, "optional": true @@ -5755,18 +5777,22 @@ "optional": true }, "minipass": { - "version": "2.3.5", + "version": "2.9.0", "bundled": true, + "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" } }, "minizlib": { - "version": "1.2.1", + "version": "1.3.3", "bundled": true, + "dev": true, + "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "^2.9.0" } }, "mkdirp": { @@ -5779,24 +5805,24 @@ } }, "ms": { - "version": "2.1.1", + "version": "2.1.2", "bundled": true, "dev": true, "optional": true }, "needle": { - "version": "2.3.0", + "version": "2.4.0", "bundled": true, "dev": true, "optional": true, "requires": { - "debug": "^4.1.0", + "debug": "^3.2.6", "iconv-lite": "^0.4.4", "sax": "^1.2.4" } }, "node-pre-gyp": { - "version": "0.12.0", + "version": "0.14.0", "bundled": true, "dev": true, "optional": true, @@ -5810,7 +5836,7 @@ "rc": "^1.2.7", "rimraf": "^2.6.1", "semver": "^5.3.0", - "tar": "^4" + "tar": "^4.4.2" } }, "nopt": { @@ -5824,13 +5850,22 @@ } }, "npm-bundled": { - "version": "1.0.6", + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", "bundled": true, "dev": true, "optional": true }, "npm-packlist": { - "version": "1.4.1", + "version": "1.4.7", "bundled": true, "dev": true, "optional": true, @@ -5901,7 +5936,7 @@ "optional": true }, "process-nextick-args": { - "version": "2.0.0", + "version": "2.0.1", "bundled": true, "dev": true, "optional": true @@ -5942,7 +5977,7 @@ } }, "rimraf": { - "version": "2.6.3", + "version": "2.7.1", "bundled": true, "dev": true, "optional": true, @@ -5952,7 +5987,9 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -5967,7 +6004,7 @@ "optional": true }, "semver": { - "version": "5.7.0", + "version": "5.7.1", "bundled": true, "dev": true, "optional": true @@ -6019,6 +6056,21 @@ "dev": true, "optional": true }, + "tar": { + "version": "4.4.13", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, "util-deprecate": { "version": "1.0.2", "bundled": true, @@ -6041,8 +6093,10 @@ "optional": true }, "yallist": { - "version": "3.0.3", - "bundled": true + "version": "3.1.1", + "bundled": true, + "dev": true, + "optional": true } } }, @@ -10778,9 +10832,9 @@ } }, "nan": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", - "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", "dev": true, "optional": true }, From 568f12cee6d8a1b04cf34495b8f95f4de6441207 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Wed, 18 Dec 2019 13:23:16 -0500 Subject: [PATCH 058/192] remove trailing comma from commands (#263) --- packages/core/__tests__/command.test.ts | 79 +++++++++++++++++++ .../__tests__/{lib.test.ts => core.test.ts} | 10 +-- packages/core/src/command.ts | 9 ++- 3 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 packages/core/__tests__/command.test.ts rename packages/core/__tests__/{lib.test.ts => core.test.ts} (93%) diff --git a/packages/core/__tests__/command.test.ts b/packages/core/__tests__/command.test.ts new file mode 100644 index 0000000000..0fa99e97df --- /dev/null +++ b/packages/core/__tests__/command.test.ts @@ -0,0 +1,79 @@ +import * as command from '../src/command' +import * as os from 'os' + +/* eslint-disable @typescript-eslint/unbound-method */ + +let originalWriteFunction: (str: string) => boolean + +describe('@actions/core/src/command', () => { + beforeAll(() => { + originalWriteFunction = process.stdout.write + }) + + beforeEach(() => { + process.stdout.write = jest.fn() + }) + + afterEach(() => {}) + + afterAll(() => { + process.stdout.write = (originalWriteFunction as unknown) as ( + str: string + ) => boolean + }) + + it('command only', () => { + command.issueCommand('some-command', {}, '') + assertWriteCalls([`::some-command::${os.EOL}`]) + }) + + it('command with message', () => { + command.issueCommand('some-command', {}, 'some message') + assertWriteCalls([`::some-command::some message${os.EOL}`]) + }) + + it('command with message and properties', () => { + command.issueCommand( + 'some-command', + {prop1: 'value 1', prop2: 'value 2'}, + 'some message' + ) + assertWriteCalls([ + `::some-command prop1=value 1,prop2=value 2::some message${os.EOL}` + ]) + }) + + it('command with one property', () => { + command.issueCommand('some-command', {prop1: 'value 1'}, '') + assertWriteCalls([`::some-command prop1=value 1::${os.EOL}`]) + }) + + it('command with two properties', () => { + command.issueCommand( + 'some-command', + {prop1: 'value 1', prop2: 'value 2'}, + '' + ) + assertWriteCalls([`::some-command prop1=value 1,prop2=value 2::${os.EOL}`]) + }) + + it('command with three properties', () => { + command.issueCommand( + 'some-command', + {prop1: 'value 1', prop2: 'value 2', prop3: 'value 3'}, + '' + ) + assertWriteCalls([ + `::some-command prop1=value 1,prop2=value 2,prop3=value 3::${os.EOL}` + ]) + }) +}) + +// Assert that process.stdout.write calls called only with the given arguments. +function assertWriteCalls(calls: string[]): void { + expect(process.stdout.write).toHaveBeenCalledTimes(calls.length) + + for (let i = 0; i < calls.length; i++) { + expect(process.stdout.write).toHaveBeenNthCalledWith(i + 1, calls[i]) + } +} diff --git a/packages/core/__tests__/lib.test.ts b/packages/core/__tests__/core.test.ts similarity index 93% rename from packages/core/__tests__/lib.test.ts rename to packages/core/__tests__/core.test.ts index 8c069a31c6..2c7e0b5424 100644 --- a/packages/core/__tests__/lib.test.ts +++ b/packages/core/__tests__/core.test.ts @@ -37,21 +37,21 @@ describe('@actions/core', () => { it('exportVariable produces the correct command and sets the env', () => { core.exportVariable('my var', 'var val') - assertWriteCalls([`::set-env name=my var,::var val${os.EOL}`]) + assertWriteCalls([`::set-env name=my var::var val${os.EOL}`]) }) it('exportVariable escapes variable names', () => { core.exportVariable('special char var \r\n];', 'special val') expect(process.env['special char var \r\n];']).toBe('special val') assertWriteCalls([ - `::set-env name=special char var %0D%0A%5D%3B,::special val${os.EOL}` + `::set-env name=special char var %0D%0A%5D%3B::special val${os.EOL}` ]) }) it('exportVariable escapes variable values', () => { core.exportVariable('my var2', 'var val\r\n') expect(process.env['my var2']).toBe('var val\r\n') - assertWriteCalls([`::set-env name=my var2,::var val%0D%0A${os.EOL}`]) + assertWriteCalls([`::set-env name=my var2::var val%0D%0A${os.EOL}`]) }) it('setSecret produces the correct command', () => { @@ -101,7 +101,7 @@ describe('@actions/core', () => { it('setOutput produces the correct command', () => { core.setOutput('some output', 'some value') - assertWriteCalls([`::set-output name=some output,::some value${os.EOL}`]) + assertWriteCalls([`::set-output name=some output::some value${os.EOL}`]) }) it('setFailure sets the correct exit code and failure message', () => { @@ -171,7 +171,7 @@ describe('@actions/core', () => { it('saveState produces the correct command', () => { core.saveState('state_1', 'some value') - assertWriteCalls([`::save-state name=state_1,::some value${os.EOL}`]) + assertWriteCalls([`::save-state name=state_1::some value${os.EOL}`]) }) it('getState gets wrapper action state', () => { diff --git a/packages/core/src/command.ts b/packages/core/src/command.ts index 0773a72415..29185dfc11 100644 --- a/packages/core/src/command.ts +++ b/packages/core/src/command.ts @@ -51,13 +51,20 @@ class Command { if (this.properties && Object.keys(this.properties).length > 0) { cmdStr += ' ' + let first = true for (const key in this.properties) { if (this.properties.hasOwnProperty(key)) { const val = this.properties[key] if (val) { + if (first) { + first = false + } else { + cmdStr += ',' + } + // safely append the val - avoid blowing up when attempting to // call .replace() if message is not a string for some reason - cmdStr += `${key}=${escape(`${val || ''}`)},` + cmdStr += `${key}=${escape(`${val || ''}`)}` } } } From 81bdf00982c5c808b4273d1fda7d4bde04130af3 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Wed, 18 Dec 2019 13:24:01 -0500 Subject: [PATCH 059/192] fix Buffer deprecation warnings (#265) --- packages/exec/__tests__/exec.test.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/exec/__tests__/exec.test.ts b/packages/exec/__tests__/exec.test.ts index a4fbc234b6..4cf322f599 100644 --- a/packages/exec/__tests__/exec.test.ts +++ b/packages/exec/__tests__/exec.test.ts @@ -53,7 +53,7 @@ describe('@actions/exec', () => { expect(outstream.write).toBeCalledWith( `[command]${toolpath} /c echo hello${os.EOL}` ) - expect(outstream.write).toBeCalledWith(new Buffer(`hello${os.EOL}`)) + expect(outstream.write).toBeCalledWith(Buffer.from(`hello${os.EOL}`)) } else { expect(outstream.write).toBeCalledWith( `[command]${toolpath} -l -a${os.EOL}` @@ -83,7 +83,7 @@ describe('@actions/exec', () => { expect(outstream.write).toBeCalledWith( `[command]${toolpath} /c echo hello${os.EOL}` ) - expect(outstream.write).toBeCalledWith(new Buffer(`hello${os.EOL}`)) + expect(outstream.write).toBeCalledWith(Buffer.from(`hello${os.EOL}`)) } else { expect(outstream.write).toBeCalledWith( `[command]${toolpath} -l -a${os.EOL}` @@ -113,7 +113,7 @@ describe('@actions/exec', () => { expect(outstream.write).toBeCalledWith( `[command]${toolpath} /c echo hello${os.EOL}` ) - expect(outstream.write).toBeCalledWith(new Buffer(`hello${os.EOL}`)) + expect(outstream.write).toBeCalledWith(Buffer.from(`hello${os.EOL}`)) } else { expect(outstream.write).toBeCalledWith( `[command]${toolpath} -l -a${os.EOL}` @@ -205,7 +205,7 @@ describe('@actions/exec', () => { expect(exitCode).toBe(0) expect(outstream.write).toBeCalledWith( - new Buffer('this is output to stderr') + Buffer.from('this is output to stderr') ) }) @@ -229,7 +229,7 @@ describe('@actions/exec', () => { expect(failed).toBe(true) expect(errstream.write).toBeCalledWith( - new Buffer('this is output to stderr') + Buffer.from('this is output to stderr') ) }) @@ -264,11 +264,11 @@ describe('@actions/exec', () => { const _testExecOptions = getExecOptions() _testExecOptions.listeners = { stdout: (data: Buffer) => { - expect(data).toEqual(new Buffer('this is output to stdout')) + expect(data).toEqual(Buffer.from('this is output to stdout')) stdoutCalled = true }, stderr: (data: Buffer) => { - expect(data).toEqual(new Buffer('this is output to stderr')) + expect(data).toEqual(Buffer.from('this is output to stderr')) stderrCalled = true } } From e7cbd693eb2325191be243329da1aaf3d9341a79 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Thu, 19 Dec 2019 10:44:08 -0500 Subject: [PATCH 060/192] fix extractTar on Windows (#264) --- .../tool-cache/__tests__/data/test.tar.gz | Bin 281 -> 729 bytes .../tool-cache/__tests__/tool-cache.test.ts | 144 +++++++++--------- packages/tool-cache/src/tool-cache.ts | 36 ++++- 3 files changed, 102 insertions(+), 78 deletions(-) diff --git a/packages/tool-cache/__tests__/data/test.tar.gz b/packages/tool-cache/__tests__/data/test.tar.gz index 9ba362f6234fad6550b31467c4da52b32f3f149b..ea74cafe2211a115dd274b6f7a325a773fb458f5 100644 GIT binary patch literal 729 zcmV;~0w(<*iwFRHMEP9+1MQgKZksR^#(SNouzEQO>i@t+F1EE(Cv8&IZkl$9h?Aft zP=VWIeW5+b9%ipGlcuGKlCVmFX6pC5aj=mwKKcBe!$mwlH}Sv}Czoe$Pd}gec5aXM z45d^{Nvbx`YSB`Df4zVUMwn26=8`id31w2V;E2rky^u}Zlr}EhQcRI1yY6$h4Tn3% z?_Y&Hw*Lu&lBrFnrqA5|yIrR`^tGfy=uk&JH=67mjaNoP<%c0xOh;VzxFA>W&p&?p z?hnjupXne7IHx_%AG*^l@4G-pBBVVQKJ;djH2cwKy_epYS3-NLuW8i3`%kRupKGa) z2yJgkHLqO%mnJDpYz?_7(`-bJ-DVKaTlRV0=q|l>%`h*fu_f2>yt+syhFDWt-`(tw z?#P>ClH0K>GUj|x+{5i$Fn`+(v1^oXIxL5s2tIufCf2EgI{c~CAU$wfb zC9hon%lOwekeSk&fw#}Ru(|%@_4gmQcac0im*EE0s4c7Fpgyg9Ix?pqsQ4InB000000000000000000000000000000aESZ` L%t0o~08jt`&o_5+ literal 281 zcmV+!0p|W6iwFP!000001MSw)ZG$ir1yGOT1t5O5AD)L2M?s1}!j9UFI!K1;8nxS` z^rHzsF>Ta)gv19(Sh_s>)*DfrN*r^(lVkdRzYt_t+jNn#LJ={skX(LgLL5VECn-fZ zK8>M|Rt=D<)7z2r~g(aeE8Eo=f7;=i}SDN`8PW6|AyFM{?~9xUv}dV{Sd=%F9=qZ z=6^hF{Zz6Be)PZ8@ox9O^ZdJf{%gk?{cjCl`42uszpYA~2Fv}=&HXR`Cu#R5i~WBM fKc|n=000000000000000;8DE;vO4YL04M+eM2v~K diff --git a/packages/tool-cache/__tests__/tool-cache.test.ts b/packages/tool-cache/__tests__/tool-cache.test.ts index be176f1bee..543280a545 100644 --- a/packages/tool-cache/__tests__/tool-cache.test.ts +++ b/packages/tool-cache/__tests__/tool-cache.test.ts @@ -268,96 +268,88 @@ describe('@actions/tool-cache', function() { await io.rmRF(tempDir) } }) - } else { - it('extract .tar.gz', async () => { - const tempDir = path.join(tempPath, 'test-install-tar.gz') + } - await io.mkdirP(tempDir) + it('extract .tar.gz', async () => { + const tempDir = path.join(tempPath, 'test-install-tar.gz') - // copy the .tar.gz file to the test dir - const _tgzFile: string = path.join(tempDir, 'test.tar.gz') - await io.cp(path.join(__dirname, 'data', 'test.tar.gz'), _tgzFile) + await io.mkdirP(tempDir) - // extract/cache - const extPath: string = await tc.extractTar(_tgzFile) - await tc.cacheDir(extPath, 'my-tgz-contents', '1.1.0') - const toolPath: string = tc.find('my-tgz-contents', '1.1.0') + // copy the .tar.gz file to the test dir + const _tgzFile: string = path.join(tempDir, 'test.tar.gz') + await io.cp(path.join(__dirname, 'data', 'test.tar.gz'), _tgzFile) - expect(fs.existsSync(toolPath)).toBeTruthy() - expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() - expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() - expect( - fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) - ).toBeTruthy() - expect( - fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) - ).toBeTruthy() - expect( - fs.readFileSync( - path.join(toolPath, 'folder', 'nested-file.txt'), - 'utf8' - ) - ).toBe('folder/nested-file.txt contents') - }) + // extract/cache + const extPath: string = await tc.extractTar(_tgzFile) + await tc.cacheDir(extPath, 'my-tgz-contents', '1.1.0') + const toolPath: string = tc.find('my-tgz-contents', '1.1.0') - it('extract .tar.gz to a directory that does not exist', async () => { - const tempDir = path.join(tempPath, 'test-install-tar.gz') - const destDir = path.join(tempDir, 'not-exist') + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + expect( + fs.readFileSync(path.join(toolPath, 'folder', 'nested-file.txt'), 'utf8') + ).toBe('folder/nested-file.txt contents') + }) - await io.mkdirP(tempDir) + it('extract .tar.gz to a directory that does not exist', async () => { + const tempDir = path.join(tempPath, 'test-install-tar.gz') + const destDir = path.join(tempDir, 'not-exist') - // copy the .tar.gz file to the test dir - const _tgzFile: string = path.join(tempDir, 'test.tar.gz') - await io.cp(path.join(__dirname, 'data', 'test.tar.gz'), _tgzFile) + await io.mkdirP(tempDir) - // extract/cache - const extPath: string = await tc.extractTar(_tgzFile, destDir) - await tc.cacheDir(extPath, 'my-tgz-contents', '1.1.0') - const toolPath: string = tc.find('my-tgz-contents', '1.1.0') + // copy the .tar.gz file to the test dir + const _tgzFile: string = path.join(tempDir, 'test.tar.gz') + await io.cp(path.join(__dirname, 'data', 'test.tar.gz'), _tgzFile) - expect(extPath).toContain('not-exist') - expect(fs.existsSync(toolPath)).toBeTruthy() - expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() - expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() - expect( - fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) - ).toBeTruthy() - expect( - fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) - ).toBeTruthy() - expect( - fs.readFileSync( - path.join(toolPath, 'folder', 'nested-file.txt'), - 'utf8' - ) - ).toBe('folder/nested-file.txt contents') - }) + // extract/cache + const extPath: string = await tc.extractTar(_tgzFile, destDir) + await tc.cacheDir(extPath, 'my-tgz-contents', '1.1.0') + const toolPath: string = tc.find('my-tgz-contents', '1.1.0') - it('extract .tar.xz', async () => { - const tempDir = path.join(tempPath, 'test-install-tar.xz') + expect(extPath).toContain('not-exist') + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + expect( + fs.readFileSync(path.join(toolPath, 'folder', 'nested-file.txt'), 'utf8') + ).toBe('folder/nested-file.txt contents') + }) - await io.mkdirP(tempDir) + it('extract .tar.xz', async () => { + const tempDir = path.join(tempPath, 'test-install-tar.xz') - // copy the .tar.gz file to the test dir - const _txzFile: string = path.join(tempDir, 'test.tar.xz') - await io.cp(path.join(__dirname, 'data', 'test.tar.xz'), _txzFile) + await io.mkdirP(tempDir) - // extract/cache - const extPath: string = await tc.extractTar(_txzFile, undefined, 'x') - await tc.cacheDir(extPath, 'my-txz-contents', '1.1.0') - const toolPath: string = tc.find('my-txz-contents', '1.1.0') + // copy the .tar.gz file to the test dir + const _txzFile: string = path.join(tempDir, 'test.tar.xz') + await io.cp(path.join(__dirname, 'data', 'test.tar.xz'), _txzFile) - expect(fs.existsSync(toolPath)).toBeTruthy() - expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() - expect(fs.existsSync(path.join(toolPath, 'bar.txt'))).toBeTruthy() - expect( - fs.existsSync(path.join(toolPath, 'foo', 'hello.txt')) - ).toBeTruthy() - expect( - fs.readFileSync(path.join(toolPath, 'foo', 'hello.txt'), 'utf8') - ).toBe('foo/hello: world') - }) - } + // extract/cache + const extPath: string = await tc.extractTar(_txzFile, undefined, 'x') + await tc.cacheDir(extPath, 'my-txz-contents', '1.1.0') + const toolPath: string = tc.find('my-txz-contents', '1.1.0') + + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'bar.txt'))).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'foo', 'hello.txt'))).toBeTruthy() + expect( + fs.readFileSync(path.join(toolPath, 'foo', 'hello.txt'), 'utf8') + ).toBe('foo/hello: world') + }) it('installs a zip and finds it', async () => { const tempDir = path.join(__dirname, 'test-install-zip') diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index 972eea0b0c..87265ff9ea 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -202,9 +202,41 @@ export async function extractTar( throw new Error("parameter 'file' is required") } + // Create dest dest = await _createExtractFolder(dest) - const tarPath: string = await io.which('tar', true) - await exec(`"${tarPath}"`, [flags, '-C', dest, '-f', file]) + + // Determine whether GNU tar + let versionOutput = '' + await exec('tar --version', [], { + ignoreReturnCode: true, + listeners: { + stdout: (data: Buffer) => (versionOutput += data.toString()), + stderr: (data: Buffer) => (versionOutput += data.toString()) + } + }) + const isGnuTar = versionOutput.toUpperCase().includes('GNU TAR') + + // Initialize args + const args = [flags] + + let destArg = dest + let fileArg = file + if (IS_WINDOWS && isGnuTar) { + args.push('--force-local') + destArg = dest.replace(/\\/g, '/') + + // Technically only the dest needs to have `/` but for aesthetic consistency + // convert slashes in the file arg too. + fileArg = file.replace(/\\/g, '/') + } + + if (isGnuTar) { + // Suppress warnings when using GNU tar to extract archives created by BSD tar + args.push('--warning=no-unknown-keyword') + } + + args.push('-C', destArg, '-f', fileArg) + await exec(`tar`, args) return dest } From 5feb835dff800f53c9f6d8989f0b503262aade87 Mon Sep 17 00:00:00 2001 From: Josh Gross Date: Mon, 23 Dec 2019 15:52:20 -0500 Subject: [PATCH 061/192] Fix broken doc links (#273) --- docs/github-package.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/github-package.md b/docs/github-package.md index d91fb62bea..7164bf4708 100644 --- a/docs/github-package.md +++ b/docs/github-package.md @@ -8,7 +8,7 @@ Note that a complete version of this action can be found at https://github.com/d ## Prerequisites -This walkthrough assumes that you have gone through the basic [javascript action walkthrough](./javascript-action.md) and have a basic action set up. If not, we recommend you go through that first. +This walkthrough assumes that you have gone through the basic [javascript action walkthrough](https://github.com/actions/javascript-action) and have a basic action set up. If not, we recommend you go through that first. ## Installing dependencies @@ -159,7 +159,7 @@ run(); ## Writing unit tests for your action -Next, we're going to write a basic unit test for our action using jest. If you followed the [javascript walkthrough](./javascript-action.md), you should have a file `__tests__/main.test.ts` that runs tests when `npm test` is called. We're going to start by populating that with one test: +Next, we're going to write a basic unit test for our action using jest. If you followed the [javascript walkthrough](https://github.com/actions/javascript-action), you should have a file `__tests__/main.test.ts` that runs tests when `npm test` is called. We're going to start by populating that with one test: ```ts const nock = require('nock'); From 60d3096c7188caaa8d732e8e0152b243b21cd24a Mon Sep 17 00:00:00 2001 From: Josh Gross Date: Thu, 26 Dec 2019 17:00:18 -0500 Subject: [PATCH 062/192] Only run CI on PRs and pushes to master (#275) --- .github/workflows/unit-tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index feaa75fb6f..b9c23bff83 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -1,6 +1,8 @@ name: toolkit-unit-tests on: push: + branches: + - master paths-ignore: - '**.md' pull_request: From 8a4134761f09d0d97fb15f297705fd8644fef920 Mon Sep 17 00:00:00 2001 From: Josh Gross Date: Fri, 27 Dec 2019 19:42:30 -0500 Subject: [PATCH 063/192] Update to latest typescript version (#274) --- package-lock.json | 12 ++++++------ package.json | 4 ++-- packages/exec/__tests__/exec.test.ts | 8 ++------ packages/exec/src/toolrunner.ts | 14 +++----------- packages/io/__tests__/io.test.ts | 12 ++++++------ packages/tool-cache/src/tool-cache.ts | 8 ++------ 6 files changed, 21 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index 90a0da8a64..63913c62aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11563,9 +11563,9 @@ "dev": true }, "prettier": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.0.tgz", - "integrity": "sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", "dev": true }, "prettier-linter-helpers": { @@ -13134,9 +13134,9 @@ "dev": true }, "typescript": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz", - "integrity": "sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz", + "integrity": "sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==", "dev": true }, "uglify-js": { diff --git a/package.json b/package.json index 563eab1e13..c50ab127b8 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,8 @@ "jest": "^24.9.0", "jest-circus": "^24.7.1", "lerna": "^3.18.4", - "prettier": "^1.17.0", + "prettier": "^1.19.1", "ts-jest": "^24.0.2", - "typescript": "^3.6.2" + "typescript": "^3.7.4" } } diff --git a/packages/exec/__tests__/exec.test.ts b/packages/exec/__tests__/exec.test.ts index 4cf322f599..387c83f5ff 100644 --- a/packages/exec/__tests__/exec.test.ts +++ b/packages/exec/__tests__/exec.test.ts @@ -748,9 +748,7 @@ describe('@actions/exec', () => { const exitCode = await exec.exec(`"${cmdPath}"`, args, options) expect(exitCode).toBe(0) expect(outStream.getContents().split(os.EOL)[0]).toBe( - `[command]${ - process.env.ComSpec - } /D /S /C ""${cmdPath}" "my arg 1" "my arg 2""` + `[command]${process.env.ComSpec} /D /S /C ""${cmdPath}" "my arg 1" "my arg 2""` ) expect(output.trim()).toBe( 'args[0]: "my arg 1"\r\n' + @@ -780,9 +778,7 @@ describe('@actions/exec', () => { const exitCode = await exec.exec(`${cmd}`, args, options) expect(exitCode).toBe(0) expect(outStream.getContents().split(os.EOL)[0]).toBe( - `[command]${ - process.env.ComSpec - } /D /S /C "${cmdPath} "my arg 1" "my arg 2""` + `[command]${process.env.ComSpec} /D /S /C "${cmdPath} "my arg 1" "my arg 2""` ) expect(output.trim()).toBe( 'args[0]: "my arg 1"\r\n' + diff --git a/packages/exec/src/toolrunner.ts b/packages/exec/src/toolrunner.ts index 2182fedd5d..c578b25867 100644 --- a/packages/exec/src/toolrunner.ts +++ b/packages/exec/src/toolrunner.ts @@ -639,23 +639,15 @@ class ExecState extends events.EventEmitter { if (this.processExited) { if (this.processError) { error = new Error( - `There was an error when attempting to execute the process '${ - this.toolPath - }'. This may indicate the process failed to start. Error: ${ - this.processError - }` + `There was an error when attempting to execute the process '${this.toolPath}'. This may indicate the process failed to start. Error: ${this.processError}` ) } else if (this.processExitCode !== 0 && !this.options.ignoreReturnCode) { error = new Error( - `The process '${this.toolPath}' failed with exit code ${ - this.processExitCode - }` + `The process '${this.toolPath}' failed with exit code ${this.processExitCode}` ) } else if (this.processStderr && this.options.failOnStdErr) { error = new Error( - `The process '${ - this.toolPath - }' failed because one or more lines were written to the STDERR stream` + `The process '${this.toolPath}' failed because one or more lines were written to the STDERR stream` ) } } diff --git a/packages/io/__tests__/io.test.ts b/packages/io/__tests__/io.test.ts index 3fd12ea747..804389592e 100644 --- a/packages/io/__tests__/io.test.ts +++ b/packages/io/__tests__/io.test.ts @@ -1171,9 +1171,9 @@ describe('which', () => { const originalPath = process.env['PATH'] try { // modify PATH - process.env['PATH'] = `${process.env['PATH']}${ - path.delimiter - }${testPath}` + process.env[ + 'PATH' + ] = `${process.env['PATH']}${path.delimiter}${testPath}` // find each file for (const fileName of Object.keys(files)) { @@ -1244,9 +1244,9 @@ describe('which', () => { await fs.writeFile(notExpectedFilePath, '') const originalPath = process.env['PATH'] try { - process.env['PATH'] = `${process.env['PATH']}${ - path.delimiter - }${testPath}` + process.env[ + 'PATH' + ] = `${process.env['PATH']}${path.delimiter}${testPath}` expect(await io.which(fileName)).toBe(expectedFilePath) } finally { process.env['PATH'] = originalPath diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index 87265ff9ea..78cd554137 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -76,9 +76,7 @@ export async function downloadTool( if (response.message.statusCode !== 200) { const err = new HTTPError(response.message.statusCode) core.debug( - `Failed to download from "${url}". Code(${ - response.message.statusCode - }) Message(${response.message.statusMessage})` + `Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})` ) throw err } @@ -93,9 +91,7 @@ export async function downloadTool( }) } catch (err) { core.debug( - `Failed to download from "${url}". Code(${ - response.message.statusCode - }) Message(${response.message.statusMessage})` + `Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})` ) reject(err) } From a94e2440cbbc3f41ebf1cc31e47d47413edccbee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2019 19:48:21 -0500 Subject: [PATCH 064/192] Bump handlebars from 4.1.2 to 4.5.3 in /packages/github (#276) Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.1.2 to 4.5.3. - [Release notes](https://github.com/wycats/handlebars.js/releases) - [Changelog](https://github.com/wycats/handlebars.js/blob/master/release-notes.md) - [Commits](https://github.com/wycats/handlebars.js/compare/v4.1.2...v4.5.3) Signed-off-by: dependabot[bot] --- packages/github/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index 06637fd8d3..6dcc35ef9f 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -2365,9 +2365,9 @@ "dev": true }, "handlebars": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", - "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", + "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", "dev": true, "requires": { "neo-async": "^2.6.0", From a11539e1db495a98e3e8fd45249cc1e0ffddf1cd Mon Sep 17 00:00:00 2001 From: eric sciple Date: Tue, 31 Dec 2019 10:16:18 -0500 Subject: [PATCH 065/192] glob (#268) --- packages/glob/__tests__/glob.test.ts | 671 ++++++++++++++++++ .../__tests__/internal-path-helper.test.ts | 640 +++++++++++++++++ packages/glob/__tests__/internal-path.test.ts | 92 +++ .../__tests__/internal-pattern-helper.test.ts | 190 +++++ .../glob/__tests__/internal-pattern.test.ts | 366 ++++++++++ packages/glob/package-lock.json | 35 + packages/glob/package.json | 41 ++ packages/glob/src/glob.ts | 183 +++++ packages/glob/src/internal-glob-options.ts | 28 + packages/glob/src/internal-match-kind.ts | 16 + packages/glob/src/internal-path-helper.ts | 206 ++++++ packages/glob/src/internal-path.ts | 113 +++ packages/glob/src/internal-pattern-helper.ts | 143 ++++ packages/glob/src/internal-pattern.ts | 321 +++++++++ packages/glob/src/internal-search-state.ts | 9 + packages/glob/tsconfig.json | 11 + 16 files changed, 3065 insertions(+) create mode 100644 packages/glob/__tests__/glob.test.ts create mode 100644 packages/glob/__tests__/internal-path-helper.test.ts create mode 100644 packages/glob/__tests__/internal-path.test.ts create mode 100644 packages/glob/__tests__/internal-pattern-helper.test.ts create mode 100644 packages/glob/__tests__/internal-pattern.test.ts create mode 100644 packages/glob/package-lock.json create mode 100644 packages/glob/package.json create mode 100644 packages/glob/src/glob.ts create mode 100644 packages/glob/src/internal-glob-options.ts create mode 100644 packages/glob/src/internal-match-kind.ts create mode 100644 packages/glob/src/internal-path-helper.ts create mode 100644 packages/glob/src/internal-path.ts create mode 100644 packages/glob/src/internal-pattern-helper.ts create mode 100644 packages/glob/src/internal-pattern.ts create mode 100644 packages/glob/src/internal-search-state.ts create mode 100644 packages/glob/tsconfig.json diff --git a/packages/glob/__tests__/glob.test.ts b/packages/glob/__tests__/glob.test.ts new file mode 100644 index 0000000000..810117ae7f --- /dev/null +++ b/packages/glob/__tests__/glob.test.ts @@ -0,0 +1,671 @@ +import * as child from 'child_process' +import * as glob from '../src/glob' +import * as io from '../../io/src/io' +import * as path from 'path' +import {promises as fs} from 'fs' + +const IS_WINDOWS = process.platform === 'win32' + +/** + * These test focus on the ability of glob to find files + * and not on the pattern matching aspect + */ +describe('glob', () => { + beforeAll(async () => { + await io.rmRF(getTestTemp()) + }) + + it('detects cycle', async () => { + // Create the following layout: + // + // /file + // /symDir -> + const root = path.join(getTestTemp(), 'detects-cycle') + await fs.mkdir(root, {recursive: true}) + await fs.writeFile(path.join(root, 'file'), 'test file content') + await createSymlinkDir(root, path.join(root, 'symDir')) + + const itemPaths = await glob.glob(root) + expect(itemPaths).toEqual([root, path.join(root, 'file')]) + // todo: ? expect(itemPaths[2]).toBe(path.join(root, 'symDir')) + }) + + it('detects deep cycle starting from middle', async () => { + // Create the following layout: + // + // /file-under-root + // /folder-a + // /folder-a/file-under-a + // /folder-a/folder-b + // /folder-a/folder-b/file-under-b + // /folder-a/folder-b/folder-c + // /folder-a/folder-b/folder-c/file-under-c + // /folder-a/folder-b/folder-c/sym-folder -> + const root = path.join( + getTestTemp(), + 'detects-deep-cycle-starting-from-middle' + ) + await fs.mkdir(path.join(root, 'folder-a', 'folder-b', 'folder-c'), { + recursive: true + }) + await fs.writeFile( + path.join(root, 'file-under-root'), + 'test file under root contents' + ) + await fs.writeFile( + path.join(root, 'folder-a', 'file-under-a'), + 'test file under a contents' + ) + await fs.writeFile( + path.join(root, 'folder-a', 'folder-b', 'file-under-b'), + 'test file under b contents' + ) + await fs.writeFile( + path.join(root, 'folder-a', 'folder-b', 'folder-c', 'file-under-c'), + 'test file under c contents' + ) + await createSymlinkDir( + root, + path.join(root, 'folder-a', 'folder-b', 'folder-c', 'sym-folder') + ) + await fs.stat( + path.join( + root, + 'folder-a', + 'folder-b', + 'folder-c', + 'sym-folder', + 'file-under-root' + ) + ) + + const itemPaths = await glob.glob(path.join(root, 'folder-a', 'folder-b')) + expect(itemPaths).toEqual([ + path.join(root, 'folder-a', 'folder-b'), + path.join(root, 'folder-a', 'folder-b', 'file-under-b'), + path.join(root, 'folder-a', 'folder-b', 'folder-c'), + path.join(root, 'folder-a', 'folder-b', 'folder-c', 'file-under-c'), + path.join(root, 'folder-a', 'folder-b', 'folder-c', 'sym-folder'), + path.join( + root, + 'folder-a', + 'folder-b', + 'folder-c', + 'sym-folder', + 'file-under-root' + ), + path.join( + root, + 'folder-a', + 'folder-b', + 'folder-c', + 'sym-folder', + 'folder-a' + ), + path.join( + root, + 'folder-a', + 'folder-b', + 'folder-c', + 'sym-folder', + 'folder-a', + 'file-under-a' + ) + ]) + }) + + it('detects cycle starting from symlink', async () => { + // Create the following layout: + // + // /file + // /symDir -> + const root: string = path.join( + getTestTemp(), + 'detects-cycle-starting-from-symlink' + ) + await fs.mkdir(root, {recursive: true}) + await fs.writeFile(path.join(root, 'file'), 'test file content') + await createSymlinkDir(root, path.join(root, 'symDir')) + await fs.stat(path.join(root, 'symDir')) + + const itemPaths = await glob.glob(path.join(root, 'symDir')) + expect(itemPaths).toEqual([ + path.join(root, 'symDir'), + path.join(root, 'symDir', 'file') + ]) + // todo: ? expect(itemPaths[2]).toBe(path.join(root, 'symDir', 'symDir')); + }) + + it('does not follow symlink when followSymbolicLink=false', async () => { + // Create the following layout: + // + // /realDir + // /realDir/file + // /symDir -> /realDir + const root = path.join( + getTestTemp(), + 'does-not-follow-symlink-when-follow-false' + ) + await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'realDir'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob.glob(root, {followSymbolicLinks: false}) + expect(itemPaths).toEqual([ + root, + path.join(root, 'realDir'), + path.join(root, 'realDir', 'file'), + path.join(root, 'symDir') + ]) + }) + + it('does not follow symlink when search path is symlink and followSymbolicLink=false', async () => { + // Create the following layout: + // realDir + // realDir/file + // symDir -> realDir + const root = path.join( + getTestTemp(), + 'does-not-follow-symlink-when-search-path-is-symlink-and-follow-false' + ) + await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'realDir'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob.glob(path.join(root, 'symDir'), { + followSymbolicLinks: false + }) + expect(itemPaths).toEqual([path.join(root, 'symDir')]) + }) + + it('does not return broken symlink', async () => { + // Create the following layout: + // + // /brokenSym -> /noSuch + // /realDir + // /realDir/file + // /symDir -> /realDir + const root = path.join(getTestTemp(), 'does-not-return-broken-symlink') + await fs.mkdir(root, {recursive: true}) + await createSymlinkDir( + path.join(root, 'noSuch'), + path.join(root, 'brokenSym') + ) + await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'realDir'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob.glob(root) + expect(itemPaths).toEqual([ + root, + path.join(root, 'realDir'), + path.join(root, 'realDir', 'file'), + path.join(root, 'symDir'), + path.join(root, 'symDir', 'file') + ]) + }) + + it('does not return broken symlink when search path is broken symlink', async () => { + // Create the following layout: + // + // /brokenSym -> /noSuch + const root = path.join( + getTestTemp(), + 'does-not-return-broken-symlink-when-search-path-is-broken-symlink' + ) + await fs.mkdir(root, {recursive: true}) + const brokenSymPath = path.join(root, 'brokenSym') + await createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath) + await fs.lstat(brokenSymPath) + + const itemPaths = await glob.glob(brokenSymPath) + expect(itemPaths).toEqual([]) + }) + + it('does not search paths that are not partial matches', async () => { + // Create the following layout: + // + // /realDir + // /realDir/nested + // /realDir/nested/file + // /realDir2 + // /realDir2/nested2 + // /realDir2/nested2/symDir -> /noSuch + const root = path.join( + getTestTemp(), + 'does-not-search-paths-that-are-not-partial-matches' + ) + await fs.mkdir(path.join(root, 'realDir', 'nested'), {recursive: true}) + await fs.writeFile( + path.join(root, 'realDir', 'nested', 'file'), + 'test file content' + ) + await fs.mkdir(path.join(root, 'realDir2', 'nested2'), {recursive: true}) + await createSymlinkDir( + path.join(root, 'noSuch'), + path.join(root, 'realDir2', 'nested2', 'symDir') + ) + + const options: glob.IGlobOptions = { + followSymbolicLinks: true, + omitBrokenSymbolicLinks: false + } + + // Should throw + try { + await glob.glob(`${root}/*Dir*/*nested*/*`, options) + throw new Error('should not reach here') + } catch (err) { + expect(err.message).toMatch(/broken symbolic link/i) + } + + // Not partial match + let itemPaths = await glob.glob(`${root}/*Dir/*nested*/*`, options) + expect(itemPaths).toEqual([path.join(root, 'realDir', 'nested', 'file')]) + + // Not partial match + itemPaths = await glob.glob(`${root}/*Dir*/*nested/*`, options) + expect(itemPaths).toEqual([path.join(root, 'realDir', 'nested', 'file')]) + }) + + it('does not throw for broken symlinks that are not matches or partial matches', async () => { + // Create the following layout: + // + // /realDir + // /realDir/file + // /symDir -> /noSuch + const root = path.join( + getTestTemp(), + 'does-not-throw-for-broken-symlinks-that-are-not-matches-or-partial-matches' + ) + await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') + await createSymlinkDir(path.join(root, 'noSuch'), path.join(root, 'symDir')) + + const options: glob.IGlobOptions = { + followSymbolicLinks: true, + omitBrokenSymbolicLinks: false + } + + // Match should throw + try { + await glob.glob(`${root}/*`, options) + throw new Error('should not reach here') + } catch (err) { + expect(err.message).toMatch(/broken symbolic link/i) + } + + // Partial match should throw + try { + await glob.glob(`${root}/*/*`, options) + throw new Error('should not reach here') + } catch (err) { + expect(err.message).toMatch(/broken symbolic link/i) + } + + // Not match or partial match + const itemPaths = await glob.glob(`${root}/*eal*/*`, options) + expect(itemPaths).toEqual([path.join(root, 'realDir', 'file')]) + }) + + it('follows symlink', async () => { + // Create the following layout: + // + // /realDir + // /realDir/file + // /symDir -> /realDir + const root = path.join(getTestTemp(), 'follows-symlink') + await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'realDir'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob.glob(root) + expect(itemPaths).toEqual([ + root, + path.join(root, 'realDir'), + path.join(root, 'realDir', 'file'), + path.join(root, 'symDir'), + path.join(root, 'symDir', 'file') + ]) + }) + + it('follows symlink when search path is symlink', async () => { + // Create the following layout: + // realDir + // realDir/file + // symDir -> realDir + const root = path.join( + getTestTemp(), + 'follows-symlink-when-search-path-is-symlink' + ) + await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'realDir'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob.glob(path.join(root, 'symDir')) + expect(itemPaths).toEqual([ + path.join(root, 'symDir'), + path.join(root, 'symDir', 'file') + ]) + }) + + it('returns broken symlink when followSymbolicLinks=false', async () => { + // Create the following layout: + // + // /brokenSym -> /noSuch + // /realDir + // /realDir/file + // /symDir -> /realDir + const root = path.join( + getTestTemp(), + 'returns-broken-symlink-when-follow-false' + ) + await fs.mkdir(root, {recursive: true}) + await createSymlinkDir( + path.join(root, 'noSuch'), + path.join(root, 'brokenSym') + ) + await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) + await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'realDir'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob.glob(root, {followSymbolicLinks: false}) + expect(itemPaths).toEqual([ + root, + path.join(root, 'brokenSym'), + path.join(root, 'realDir'), + path.join(root, 'realDir', 'file'), + path.join(root, 'symDir') + ]) + }) + + it('returns broken symlink when search path is broken symlink and followSymbolicLinks=false', async () => { + // Create the following layout: + // + // /brokenSym -> /noSuch + const root = path.join( + getTestTemp(), + 'returns-broken-symlink-when-search-path-is-broken-symlink-and-follow-false' + ) + await fs.mkdir(root, {recursive: true}) + const brokenSymPath = path.join(root, 'brokenSym') + await createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath) + + const itemPaths = await glob.glob(brokenSymPath, { + followSymbolicLinks: false + }) + expect(itemPaths).toEqual([brokenSymPath]) + }) + + it('returns depth first', async () => { + // Create the following layout: + // /a-file + // /b-folder + // /b-folder/a-file + // /b-folder/b-folder + // /b-folder/b-folder/file + // /b-folder/c-file + // /c-file + const root = path.join(getTestTemp(), 'returns-depth-first') + await fs.mkdir(path.join(root, 'b-folder', 'b-folder'), {recursive: true}) + await fs.writeFile(path.join(root, 'a-file'), 'test a-file content') + await fs.writeFile( + path.join(root, 'b-folder', 'a-file'), + 'test b-folder/a-file content' + ) + await fs.writeFile( + path.join(root, 'b-folder', 'b-folder', 'file'), + 'test b-folder/b-folder/file content' + ) + await fs.writeFile( + path.join(root, 'b-folder', 'c-file'), + 'test b-folder/c-file content' + ) + await fs.writeFile(path.join(root, 'c-file'), 'test c-file content') + + const itemPaths = await glob.glob(root) + expect(itemPaths).toEqual([ + root, + path.join(root, 'a-file'), + path.join(root, 'b-folder'), + path.join(root, 'b-folder', 'a-file'), + path.join(root, 'b-folder', 'b-folder'), + path.join(root, 'b-folder', 'b-folder', 'file'), + path.join(root, 'b-folder', 'c-file'), + path.join(root, 'c-file') + ]) + }) + + it('returns descendants', async () => { + // Create the following layout: + // /file-1 + // /dir-1 + // /dir-1/file-2 + // /dir-1/dir-2 + // /dir-1/dir-2/file-3 + const root = path.join(getTestTemp(), 'returns-descendants') + await fs.mkdir(path.join(root, 'dir-1', 'dir-2'), {recursive: true}) + await fs.writeFile(path.join(root, 'file-1'), '') + await fs.writeFile(path.join(root, 'dir-1', 'file-2'), '') + await fs.writeFile(path.join(root, 'dir-1', 'dir-2', 'file-3'), '') + + // When pattern ends with `/**/` + let pattern = `${root}${path.sep}**${path.sep}` + expect( + await glob.glob(pattern, { + implicitDescendants: false + }) + ).toHaveLength(3) // sanity check + expect(await glob.glob(pattern)).toEqual([ + root, + path.join(root, 'dir-1'), + path.join(root, 'dir-1', 'dir-2'), + path.join(root, 'dir-1', 'dir-2', 'file-3'), + path.join(root, 'dir-1', 'file-2'), + path.join(root, 'file-1') + ]) + + // When pattern ends with something other than `/**/` + pattern = `${root}${path.sep}**${path.sep}dir-?` + expect( + await glob.glob(pattern, { + implicitDescendants: false + }) + ).toHaveLength(2) // sanity check + expect(await glob.glob(pattern)).toEqual([ + path.join(root, 'dir-1'), + path.join(root, 'dir-1', 'dir-2'), + path.join(root, 'dir-1', 'dir-2', 'file-3'), + path.join(root, 'dir-1', 'file-2') + ]) + }) + + it('returns directories only when trailing slash and implicit descendants false', async () => { + // Create the following layout: + // /file-1 + // /dir-1 + // /dir-1/file-2 + // /dir-1/dir-2 + // /dir-1/dir-2/file-3 + const root = path.join( + getTestTemp(), + 'returns-directories-only-when-trailing-slash-and-implicit-descendants-false' + ) + await fs.mkdir(path.join(root, 'dir-1', 'dir-2'), {recursive: true}) + await fs.writeFile(path.join(root, 'file-1'), '') + await fs.writeFile(path.join(root, 'dir-1', 'file-2'), '') + await fs.writeFile(path.join(root, 'dir-1', 'dir-2', 'file-3'), '') + + const pattern = `${root}${path.sep}**${path.sep}` + expect(await glob.glob(pattern)).toHaveLength(6) // sanity check + expect( + await glob.glob(pattern, { + implicitDescendants: false + }) + ).toEqual([ + root, + path.join(root, 'dir-1'), + path.join(root, 'dir-1', 'dir-2') + ]) + }) + + it('returns empty when search path does not exist', async () => { + const itemPaths = await glob.glob(path.join(getTestTemp(), 'nosuch')) + expect(itemPaths).toEqual([]) + }) + + it('returns hidden files', async () => { + // Create the following layout: + // + // /.emptyFolder + // /.file + // /.folder + // /.folder/file + const root = path.join(getTestTemp(), 'returns-hidden-files') + await createHiddenDirectory(path.join(root, '.emptyFolder')) + await createHiddenDirectory(path.join(root, '.folder')) + await createHiddenFile(path.join(root, '.file'), 'test .file content') + await fs.writeFile( + path.join(root, '.folder', 'file'), + 'test .folder/file content' + ) + + const itemPaths = await glob.glob(root) + expect(itemPaths).toEqual([ + root, + path.join(root, '.emptyFolder'), + path.join(root, '.file'), + path.join(root, '.folder'), + path.join(root, '.folder', 'file') + ]) + }) + + it('returns normalized paths', async () => { + // Create the following layout: + // /hello/world.txt + const root: string = path.join(getTestTemp(), 'returns-normalized-paths') + await fs.mkdir(path.join(root, 'hello'), {recursive: true}) + await fs.writeFile(path.join(root, 'hello', 'world.txt'), '') + + const itemPaths = await glob.glob( + `${root}${path.sep}${path.sep}${path.sep}hello` + ) + expect(itemPaths).toEqual([ + path.join(root, 'hello'), + path.join(root, 'hello', 'world.txt') + ]) + }) + + it('throws when match broken symlink and omitBrokenSymbolicLinks=false', async () => { + // Create the following layout: + // + // /brokenSym -> /noSuch + const root = path.join( + getTestTemp(), + 'throws-when-match-broken-symlink-and-omit-false' + ) + await fs.mkdir(root, {recursive: true}) + await createSymlinkDir( + path.join(root, 'noSuch'), + path.join(root, 'brokenSym') + ) + + try { + await glob.glob(root, {omitBrokenSymbolicLinks: false}) + throw new Error('Expected tl.find to throw') + } catch (err) { + expect(err.message).toMatch(/broken symbolic link/) + } + }) + + it('throws when search path is broken symlink and omitBrokenSymbolicLinks=false', async () => { + // Create the following layout: + // + // /brokenSym -> /noSuch + const root = path.join( + getTestTemp(), + 'throws-when-search-path-is-broken-symlink-and-omit-false' + ) + await fs.mkdir(root, {recursive: true}) + const brokenSymPath = path.join(root, 'brokenSym') + await createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath) + await fs.lstat(brokenSymPath) + + try { + await glob.glob(brokenSymPath, {omitBrokenSymbolicLinks: false}) + throw new Error('Expected tl.find to throw') + } catch (err) { + expect(err.message).toMatch(/broken symbolic link/) + } + }) +}) + +async function createHiddenDirectory(dir: string): Promise { + if (!path.basename(dir).match(/^\./)) { + throw new Error(`Expected dir '${dir}' to start with '.'.`) + } + + await fs.mkdir(dir, {recursive: true}) + if (IS_WINDOWS) { + const result = child.spawnSync('attrib.exe', ['+H', dir]) + if (result.status !== 0) { + const message: string = (result.output || []).join(' ').trim() + throw new Error( + `Failed to set hidden attribute for directory '${dir}'. ${message}` + ) + } + } +} + +async function createHiddenFile(file: string, content: string): Promise { + if (!path.basename(file).match(/^\./)) { + throw new Error(`Expected dir '${file}' to start with '.'.`) + } + + await fs.mkdir(path.dirname(file), {recursive: true}) + await fs.writeFile(file, content) + + if (IS_WINDOWS) { + const result = child.spawnSync('attrib.exe', ['+H', file]) + if (result.status !== 0) { + const message: string = (result.output || []).join(' ').trim() + throw new Error( + `Failed to set hidden attribute for file '${file}'. ${message}` + ) + } + } +} + +function getTestTemp(): string { + return path.join(__dirname, '_temp', 'glob') +} + +/** + * Creates a symlink directory on OSX/Linux, and a junction point directory on Windows. + * A symlink directory is not created on Windows since it requires an elevated context. + */ +async function createSymlinkDir(real: string, link: string): Promise { + if (IS_WINDOWS) { + await fs.symlink(real, link, 'junction') + } else { + await fs.symlink(real, link) + } +} diff --git a/packages/glob/__tests__/internal-path-helper.test.ts b/packages/glob/__tests__/internal-path-helper.test.ts new file mode 100644 index 0000000000..48a10da38c --- /dev/null +++ b/packages/glob/__tests__/internal-path-helper.test.ts @@ -0,0 +1,640 @@ +import * as pathHelper from '../src/internal-path-helper' + +const IS_WINDOWS = process.platform === 'win32' + +describe('path-helper', () => { + it('dirname interprets directory name from paths', () => { + assertDirectoryName('', '.') + assertDirectoryName('.', '.') + assertDirectoryName('..', '.') + assertDirectoryName('hello', '.') + assertDirectoryName('hello/', '.') + assertDirectoryName('hello/world', 'hello') + + if (IS_WINDOWS) { + // Removes redundant slashes + assertDirectoryName('C:\\\\hello\\\\\\world\\\\', 'C:\\hello') + assertDirectoryName('C://hello///world//', 'C:\\hello') + // Relative root: + assertDirectoryName('\\hello\\\\world\\\\again\\\\', '\\hello\\world') + assertDirectoryName('/hello///world//again//', '\\hello\\world') + // UNC: + assertDirectoryName('\\\\hello\\world\\again\\', '\\\\hello\\world') + assertDirectoryName( + '\\\\hello\\\\\\world\\\\again\\\\', + '\\\\hello\\world' + ) + assertDirectoryName( + '\\\\\\hello\\\\\\world\\\\again\\\\', + '\\\\hello\\world' + ) + assertDirectoryName( + '\\\\\\\\hello\\\\\\world\\\\again\\\\', + '\\\\hello\\world' + ) + assertDirectoryName('//hello///world//again//', '\\\\hello\\world') + assertDirectoryName('///hello///world//again//', '\\\\hello\\world') + assertDirectoryName('/////hello///world//again//', '\\\\hello\\world') + // Relative: + assertDirectoryName('hello\\world', 'hello') + + // Directory trimming + assertDirectoryName('a:/hello', 'a:\\') + assertDirectoryName('z:/hello', 'z:\\') + assertDirectoryName('A:/hello', 'A:\\') + assertDirectoryName('Z:/hello', 'Z:\\') + assertDirectoryName('C:/', 'C:\\') + assertDirectoryName('C:/hello', 'C:\\') + assertDirectoryName('C:/hello/', 'C:\\') + assertDirectoryName('C:/hello/world', 'C:\\hello') + assertDirectoryName('C:/hello/world/', 'C:\\hello') + assertDirectoryName('C:', 'C:') + assertDirectoryName('C:hello', 'C:') + assertDirectoryName('C:hello/', 'C:') + assertDirectoryName('C:hello/world', 'C:hello') + assertDirectoryName('C:hello/world/', 'C:hello') + assertDirectoryName('/', '\\') + assertDirectoryName('/hello', '\\') + assertDirectoryName('/hello/', '\\') + assertDirectoryName('/hello/world', '\\hello') + assertDirectoryName('/hello/world/', '\\hello') + assertDirectoryName('\\', '\\') + assertDirectoryName('\\hello', '\\') + assertDirectoryName('\\hello\\', '\\') + assertDirectoryName('\\hello\\world', '\\hello') + assertDirectoryName('\\hello\\world\\', '\\hello') + assertDirectoryName('//hello', '\\\\hello') + assertDirectoryName('//hello/', '\\\\hello') + assertDirectoryName('//hello/world', '\\\\hello\\world') + assertDirectoryName('//hello/world/', '\\\\hello\\world') + assertDirectoryName('\\\\hello', '\\\\hello') + assertDirectoryName('\\\\hello\\', '\\\\hello') + assertDirectoryName('\\\\hello\\world', '\\\\hello\\world') + assertDirectoryName('\\\\hello\\world\\', '\\\\hello\\world') + assertDirectoryName('//hello/world/again', '\\\\hello\\world') + assertDirectoryName('//hello/world/again/', '\\\\hello\\world') + assertDirectoryName('hello/world/', 'hello') + assertDirectoryName('hello/world/again', 'hello\\world') + assertDirectoryName('../../hello', '..\\..') + } else { + // Should not converts slashes + assertDirectoryName('/hello\\world', '/') + assertDirectoryName('/hello\\world/', '/') + assertDirectoryName('\\\\hello\\world\\again', '.') + assertDirectoryName('\\\\hello\\world/', '.') + assertDirectoryName('\\\\hello\\world/again', '\\\\hello\\world') + assertDirectoryName('hello\\world', '.') + assertDirectoryName('hello\\world/', '.') + + // Should remove redundant slashes (rooted paths; UNC format not special) + assertDirectoryName('//hello', '/') + assertDirectoryName('//hello/world', '/hello') + assertDirectoryName('//hello/world/', '/hello') + assertDirectoryName('//hello//world//', '/hello') + assertDirectoryName('///hello////world///', '/hello') + + // Should remove redundant slashes (relative paths) + assertDirectoryName('hello//world//again//', 'hello/world') + assertDirectoryName('hello///world///again///', 'hello/world') + + // Directory trimming (Windows drive root format not special) + assertDirectoryName('C:/', '.') + assertDirectoryName('C:/hello', 'C:') + assertDirectoryName('C:/hello/', 'C:') + assertDirectoryName('C:/hello/world', 'C:/hello') + assertDirectoryName('C:/hello/world/', 'C:/hello') + assertDirectoryName('C:', '.') + assertDirectoryName('C:hello', '.') + assertDirectoryName('C:hello/', '.') + assertDirectoryName('C:hello/world', 'C:hello') + assertDirectoryName('C:hello/world/', 'C:hello') + + // Directory trimming (rooted paths) + assertDirectoryName('/', '/') + assertDirectoryName('/hello', '/') + assertDirectoryName('/hello/', '/') + assertDirectoryName('/hello/world', '/hello') + assertDirectoryName('/hello/world/', '/hello') + + // Directory trimming (relative paths) + assertDirectoryName('hello/world/', 'hello') + assertDirectoryName('hello/world/again', 'hello/world') + assertDirectoryName('../../hello', '../..') + } + }) + + it('ensureAbsoluteRoot roots paths', () => { + if (IS_WINDOWS) { + const currentDrive = process.cwd().substr(0, 2) + expect(currentDrive.match(/^[A-Z]:$/i)).toBeTruthy() + const otherDrive = currentDrive.toUpperCase().startsWith('C') + ? 'D:' + : 'C:' + + // Preserves relative pathing + assertEnsureAbsoluteRoot('C:/foo', '.', `C:/foo\\.`) + assertEnsureAbsoluteRoot('C:/foo/..', 'bar', `C:/foo/..\\bar`) + assertEnsureAbsoluteRoot('C:/foo', 'bar/../baz', `C:/foo\\bar/../baz`) + + // Already rooted - drive root + assertEnsureAbsoluteRoot('D:\\', 'C:/', 'C:/') + assertEnsureAbsoluteRoot('D:\\', 'a:/hello', 'a:/hello') + assertEnsureAbsoluteRoot('D:\\', 'C:\\', 'C:\\') + assertEnsureAbsoluteRoot('D:\\', 'C:\\hello', 'C:\\hello') + + // Already rooted - relative current drive root + expect(process.cwd().length).toBeGreaterThan(3) // sanity check not drive root + assertEnsureAbsoluteRoot(`${otherDrive}\\`, currentDrive, process.cwd()) + assertEnsureAbsoluteRoot( + `${otherDrive}\\`, + `${currentDrive}hello`, + `${process.cwd()}\\hello` + ) + assertEnsureAbsoluteRoot( + `${otherDrive}\\`, + `${currentDrive}hello/world`, + `${process.cwd()}\\hello/world` + ) + assertEnsureAbsoluteRoot( + `${otherDrive}\\`, + `${currentDrive}hello\\world`, + `${process.cwd()}\\hello\\world` + ) + + // Already rooted - relative other drive root + assertEnsureAbsoluteRoot( + `${currentDrive}\\`, + otherDrive, + `${otherDrive}\\` + ) + assertEnsureAbsoluteRoot( + `${currentDrive}\\`, + `${otherDrive}hello`, + `${otherDrive}\\hello` + ) + assertEnsureAbsoluteRoot( + `${currentDrive}\\`, + `${otherDrive}hello/world`, + `${otherDrive}\\hello/world` + ) + assertEnsureAbsoluteRoot( + `${currentDrive}\\`, + `${otherDrive}hello\\world`, + `${otherDrive}\\hello\\world` + ) + + // Already rooted - current drive root + assertEnsureAbsoluteRoot(`${otherDrive}\\`, '/', `${currentDrive}\\`) + assertEnsureAbsoluteRoot( + `${otherDrive}\\`, + '/hello', + `${currentDrive}\\hello` + ) + assertEnsureAbsoluteRoot(`${otherDrive}\\`, '\\', `${currentDrive}\\`) + assertEnsureAbsoluteRoot( + `${otherDrive}\\`, + '\\hello', + `${currentDrive}\\hello` + ) + + // Already rooted - UNC + assertEnsureAbsoluteRoot('D:\\', '//machine/share', '//machine/share') + assertEnsureAbsoluteRoot( + 'D:\\', + '\\\\machine\\share', + '\\\\machine\\share' + ) + + // Relative + assertEnsureAbsoluteRoot('D:/', 'hello', 'D:/hello') + assertEnsureAbsoluteRoot('D:/', 'hello/world', 'D:/hello/world') + assertEnsureAbsoluteRoot('D:\\', 'hello', 'D:\\hello') + assertEnsureAbsoluteRoot('D:\\', 'hello\\world', 'D:\\hello\\world') + assertEnsureAbsoluteRoot('D:/root', 'hello', 'D:/root\\hello') + assertEnsureAbsoluteRoot('D:/root', 'hello/world', 'D:/root\\hello/world') + assertEnsureAbsoluteRoot('D:\\root', 'hello', 'D:\\root\\hello') + assertEnsureAbsoluteRoot( + 'D:\\root', + 'hello\\world', + 'D:\\root\\hello\\world' + ) + assertEnsureAbsoluteRoot('D:/root/', 'hello', 'D:/root/hello') + assertEnsureAbsoluteRoot('D:/root/', 'hello/world', 'D:/root/hello/world') + assertEnsureAbsoluteRoot('D:\\root\\', 'hello', 'D:\\root\\hello') + assertEnsureAbsoluteRoot( + 'D:\\root\\', + 'hello\\world', + 'D:\\root\\hello\\world' + ) + } else { + // Preserves relative pathing + assertEnsureAbsoluteRoot('/foo', '.', `/foo/.`) + assertEnsureAbsoluteRoot('/foo/..', 'bar', `/foo/../bar`) + assertEnsureAbsoluteRoot('/foo', 'bar/../baz', `/foo/bar/../baz`) + + // Already rooted + assertEnsureAbsoluteRoot('/root', '/', '/') + assertEnsureAbsoluteRoot('/root', '/hello', '/hello') + assertEnsureAbsoluteRoot('/root', '/hello/world', '/hello/world') + + // Not already rooted - Windows style drive root + assertEnsureAbsoluteRoot('/root', 'C:/', '/root/C:/') + assertEnsureAbsoluteRoot('/root', 'C:/hello', '/root/C:/hello') + assertEnsureAbsoluteRoot('/root', 'C:\\', '/root/C:\\') + + // Not already rooted - Windows style relative drive root + assertEnsureAbsoluteRoot('/root', 'C:', '/root/C:') + assertEnsureAbsoluteRoot('/root', 'C:hello/world', '/root/C:hello/world') + + // Not already rooted - Windows style current drive root + assertEnsureAbsoluteRoot('/root', '\\', '/root/\\') + assertEnsureAbsoluteRoot( + '/root', + '\\hello\\world', + '/root/\\hello\\world' + ) + + // Not already rooted - Windows style UNC + assertEnsureAbsoluteRoot( + '/root', + '\\\\machine\\share', + '/root/\\\\machine\\share' + ) + + // Not already rooted - relative + assertEnsureAbsoluteRoot('/', 'hello', '/hello') + assertEnsureAbsoluteRoot('/', 'hello/world', '/hello/world') + assertEnsureAbsoluteRoot('/', 'hello\\world', '/hello\\world') + assertEnsureAbsoluteRoot('/root', 'hello', '/root/hello') + assertEnsureAbsoluteRoot('/root', 'hello/world', '/root/hello/world') + assertEnsureAbsoluteRoot('/root', 'hello\\world', '/root/hello\\world') + assertEnsureAbsoluteRoot('/root/', 'hello', '/root/hello') + assertEnsureAbsoluteRoot('/root/', 'hello/world', '/root/hello/world') + assertEnsureAbsoluteRoot('/root/', 'hello\\world', '/root/hello\\world') + assertEnsureAbsoluteRoot('/root\\', 'hello', '/root\\/hello') + assertEnsureAbsoluteRoot('/root\\', 'hello/world', '/root\\/hello/world') + assertEnsureAbsoluteRoot( + '/root\\', + 'hello\\world', + '/root\\/hello\\world' + ) + } + }) + + it('hasAbsoluteRoot detects absolute root', () => { + if (IS_WINDOWS) { + // Drive root + assertHasAbsoluteRoot('C:/', true) + assertHasAbsoluteRoot('a:/hello', true) + assertHasAbsoluteRoot('c:/hello', true) + assertHasAbsoluteRoot('z:/hello', true) + assertHasAbsoluteRoot('A:/hello', true) + assertHasAbsoluteRoot('C:/hello', true) + assertHasAbsoluteRoot('Z:/hello', true) + assertHasAbsoluteRoot('C:\\', true) + assertHasAbsoluteRoot('C:\\hello', true) + + // Relative drive root + assertHasAbsoluteRoot('C:', false) + assertHasAbsoluteRoot('C:hello', false) + assertHasAbsoluteRoot('C:hello/world', false) + assertHasAbsoluteRoot('C:hello\\world', false) + + // Current drive root + assertHasAbsoluteRoot('/', false) + assertHasAbsoluteRoot('/hello', false) + assertHasAbsoluteRoot('/hello/world', false) + assertHasAbsoluteRoot('\\', false) + assertHasAbsoluteRoot('\\hello', false) + assertHasAbsoluteRoot('\\hello\\world', false) + + // UNC + assertHasAbsoluteRoot('//machine/share', true) + assertHasAbsoluteRoot('//machine/share/', true) + assertHasAbsoluteRoot('//machine/share/hello', true) + assertHasAbsoluteRoot('\\\\machine\\share', true) + assertHasAbsoluteRoot('\\\\machine\\share\\', true) + assertHasAbsoluteRoot('\\\\machine\\share\\hello', true) + + // Relative + assertHasAbsoluteRoot('hello', false) + assertHasAbsoluteRoot('hello/world', false) + assertHasAbsoluteRoot('hello\\world', false) + } else { + // Root + assertHasAbsoluteRoot('/', true) + assertHasAbsoluteRoot('/hello', true) + assertHasAbsoluteRoot('/hello/world', true) + + // Windows style drive root - false on OSX/Linux + assertHasAbsoluteRoot('C:/', false) + assertHasAbsoluteRoot('a:/hello', false) + assertHasAbsoluteRoot('c:/hello', false) + assertHasAbsoluteRoot('z:/hello', false) + assertHasAbsoluteRoot('A:/hello', false) + assertHasAbsoluteRoot('C:/hello', false) + assertHasAbsoluteRoot('Z:/hello', false) + assertHasAbsoluteRoot('C:\\', false) + assertHasAbsoluteRoot('C:\\hello', false) + + // Windows style relative drive root - false on OSX/Linux + assertHasAbsoluteRoot('C:', false) + assertHasAbsoluteRoot('C:hello', false) + assertHasAbsoluteRoot('C:hello/world', false) + assertHasAbsoluteRoot('C:hello\\world', false) + + // Windows style current drive root - false on OSX/Linux + assertHasAbsoluteRoot('\\', false) + assertHasAbsoluteRoot('\\hello', false) + assertHasAbsoluteRoot('\\hello\\world', false) + + // Windows style UNC - false on OSX/Linux + assertHasAbsoluteRoot('\\\\machine\\share', false) + assertHasAbsoluteRoot('\\\\machine\\share\\', false) + assertHasAbsoluteRoot('\\\\machine\\share\\hello', false) + + // Relative + assertHasAbsoluteRoot('hello', false) + assertHasAbsoluteRoot('hello/world', false) + assertHasAbsoluteRoot('hello\\world', false) + } + }) + + it('hasRoot detects root', () => { + if (IS_WINDOWS) { + // Drive root + assertHasRoot('C:/', true) + assertHasRoot('a:/hello', true) + assertHasRoot('c:/hello', true) + assertHasRoot('z:/hello', true) + assertHasRoot('A:/hello', true) + assertHasRoot('C:/hello', true) + assertHasRoot('Z:/hello', true) + assertHasRoot('C:\\', true) + assertHasRoot('C:\\hello', true) + + // Relative drive root + assertHasRoot('C:', true) + assertHasRoot('C:hello', true) + assertHasRoot('C:hello/world', true) + assertHasRoot('C:hello\\world', true) + + // Current drive root + assertHasRoot('/', true) + assertHasRoot('/hello', true) + assertHasRoot('/hello/world', true) + assertHasRoot('\\', true) + assertHasRoot('\\hello', true) + assertHasRoot('\\hello\\world', true) + + // UNC + assertHasRoot('//machine/share', true) + assertHasRoot('//machine/share/', true) + assertHasRoot('//machine/share/hello', true) + assertHasRoot('\\\\machine\\share', true) + assertHasRoot('\\\\machine\\share\\', true) + assertHasRoot('\\\\machine\\share\\hello', true) + + // Relative + assertHasRoot('hello', false) + assertHasRoot('hello/world', false) + assertHasRoot('hello\\world', false) + } else { + // Root + assertHasRoot('/', true) + assertHasRoot('/hello', true) + assertHasRoot('/hello/world', true) + + // Windows style drive root - false on OSX/Linux + assertHasRoot('C:/', false) + assertHasRoot('a:/hello', false) + assertHasRoot('c:/hello', false) + assertHasRoot('z:/hello', false) + assertHasRoot('A:/hello', false) + assertHasRoot('C:/hello', false) + assertHasRoot('Z:/hello', false) + assertHasRoot('C:\\', false) + assertHasRoot('C:\\hello', false) + + // Windows style relative drive root - false on OSX/Linux + assertHasRoot('C:', false) + assertHasRoot('C:hello', false) + assertHasRoot('C:hello/world', false) + assertHasRoot('C:hello\\world', false) + + // Windows style current drive root - false on OSX/Linux + assertHasRoot('\\', false) + assertHasRoot('\\hello', false) + assertHasRoot('\\hello\\world', false) + + // Windows style UNC - false on OSX/Linux + assertHasRoot('\\\\machine\\share', false) + assertHasRoot('\\\\machine\\share\\', false) + assertHasRoot('\\\\machine\\share\\hello', false) + + // Relative + assertHasRoot('hello', false) + assertHasRoot('hello/world', false) + assertHasRoot('hello\\world', false) + } + }) + + it('normalizeSeparators normalizes slashes', () => { + if (IS_WINDOWS) { + // Drive-rooted + assertNormalizeSeparators('C:/', 'C:\\') + assertNormalizeSeparators('C:/hello', 'C:\\hello') + assertNormalizeSeparators('C:/hello/', 'C:\\hello\\') + assertNormalizeSeparators('C:\\', 'C:\\') + assertNormalizeSeparators('C:\\hello', 'C:\\hello') + assertNormalizeSeparators('C:', 'C:') + assertNormalizeSeparators('C:hello', 'C:hello') + assertNormalizeSeparators('C:hello/world', 'C:hello\\world') + assertNormalizeSeparators('C:hello\\world', 'C:hello\\world') + assertNormalizeSeparators('/', '\\') + assertNormalizeSeparators('/hello', '\\hello') + assertNormalizeSeparators('/hello/world', '\\hello\\world') + assertNormalizeSeparators('/hello//world', '\\hello\\world') + assertNormalizeSeparators('\\', '\\') + assertNormalizeSeparators('\\hello', '\\hello') + assertNormalizeSeparators('\\hello\\', '\\hello\\') + assertNormalizeSeparators('\\hello\\world', '\\hello\\world') + assertNormalizeSeparators('\\hello\\\\world', '\\hello\\world') + + // UNC + assertNormalizeSeparators('//machine/share', '\\\\machine\\share') + assertNormalizeSeparators('//machine/share/', '\\\\machine\\share\\') + assertNormalizeSeparators( + '//machine/share/hello', + '\\\\machine\\share\\hello' + ) + assertNormalizeSeparators('///machine/share', '\\\\machine\\share') + assertNormalizeSeparators('\\\\machine\\share', '\\\\machine\\share') + assertNormalizeSeparators('\\\\machine\\share\\', '\\\\machine\\share\\') + assertNormalizeSeparators( + '\\\\machine\\share\\hello', + '\\\\machine\\share\\hello' + ) + assertNormalizeSeparators('\\\\\\machine\\share', '\\\\machine\\share') + + // Relative + assertNormalizeSeparators('hello', 'hello') + assertNormalizeSeparators('hello/world', 'hello\\world') + assertNormalizeSeparators('hello//world', 'hello\\world') + assertNormalizeSeparators('hello\\world', 'hello\\world') + assertNormalizeSeparators('hello\\\\world', 'hello\\world') + } else { + // Rooted + assertNormalizeSeparators('/', '/') + assertNormalizeSeparators('/hello', '/hello') + assertNormalizeSeparators('/hello/world', '/hello/world') + assertNormalizeSeparators('//hello/world/', '/hello/world/') + + // Backslash not converted + assertNormalizeSeparators('C:\\', 'C:\\') + assertNormalizeSeparators('C:\\\\hello\\\\', 'C:\\\\hello\\\\') + assertNormalizeSeparators('\\', '\\') + assertNormalizeSeparators('\\hello', '\\hello') + assertNormalizeSeparators('\\hello\\world', '\\hello\\world') + assertNormalizeSeparators('hello\\world', 'hello\\world') + + // UNC not converted + assertNormalizeSeparators('\\\\machine\\share', '\\\\machine\\share') + + // UNC not preserved + assertNormalizeSeparators('//machine/share', '/machine/share') + + // Relative + assertNormalizeSeparators('hello', 'hello') + assertNormalizeSeparators('hello/////world', 'hello/world') + } + }) + + it('safeTrimTrailingSeparator safely trims trailing separator', () => { + assertSafeTrimTrailingSeparator('', '') + + if (IS_WINDOWS) { + // Removes redundant slashes + assertSafeTrimTrailingSeparator( + 'C:\\\\hello\\\\\\world\\\\', + 'C:\\hello\\world' + ) + assertSafeTrimTrailingSeparator('C://hello///world//', 'C:\\hello\\world') + // Relative root: + assertSafeTrimTrailingSeparator( + '\\hello\\\\world\\\\again\\\\', + '\\hello\\world\\again' + ) + assertSafeTrimTrailingSeparator( + '/hello///world//again//', + '\\hello\\world\\again' + ) + // UNC: + assertSafeTrimTrailingSeparator('\\\\hello\\world\\', '\\\\hello\\world') + assertSafeTrimTrailingSeparator( + '\\\\hello\\world\\\\', + '\\\\hello\\world' + ) + assertSafeTrimTrailingSeparator( + '\\\\hello\\\\\\world\\\\again\\', + '\\\\hello\\world\\again' + ) + assertSafeTrimTrailingSeparator('//hello/world/', '\\\\hello\\world') + assertSafeTrimTrailingSeparator('//hello/world//', '\\\\hello\\world') + assertSafeTrimTrailingSeparator( + '//hello//world//again/', + '\\\\hello\\world\\again' + ) + // Relative: + assertSafeTrimTrailingSeparator('hello\\world\\', 'hello\\world') + + // Slash trimming + assertSafeTrimTrailingSeparator('a:/hello/', 'a:\\hello') + assertSafeTrimTrailingSeparator('z:/hello', 'z:\\hello') + assertSafeTrimTrailingSeparator('C:/', 'C:\\') + assertSafeTrimTrailingSeparator('C:\\', 'C:\\') + assertSafeTrimTrailingSeparator('C:/hello/world', 'C:\\hello\\world') + assertSafeTrimTrailingSeparator('C:/hello/world/', 'C:\\hello\\world') + assertSafeTrimTrailingSeparator('C:', 'C:') + assertSafeTrimTrailingSeparator('C:hello/', 'C:hello') + assertSafeTrimTrailingSeparator('/', '\\') + assertSafeTrimTrailingSeparator('/hello/', '\\hello') + assertSafeTrimTrailingSeparator('\\', '\\') + assertSafeTrimTrailingSeparator('\\hello\\', '\\hello') + assertSafeTrimTrailingSeparator('//hello/', '\\\\hello') + assertSafeTrimTrailingSeparator('//hello/world', '\\\\hello\\world') + assertSafeTrimTrailingSeparator('//hello/world/', '\\\\hello\\world') + assertSafeTrimTrailingSeparator('\\\\hello', '\\\\hello') + assertSafeTrimTrailingSeparator('\\\\hello\\', '\\\\hello') + assertSafeTrimTrailingSeparator('\\\\hello\\world', '\\\\hello\\world') + assertSafeTrimTrailingSeparator('\\\\hello\\world\\', '\\\\hello\\world') + assertSafeTrimTrailingSeparator('hello/world/', 'hello\\world') + assertSafeTrimTrailingSeparator('hello/', 'hello') + assertSafeTrimTrailingSeparator('../../', '..\\..') + } else { + // Should not converts slashes + assertSafeTrimTrailingSeparator('/hello\\world', '/hello\\world') + assertSafeTrimTrailingSeparator('/hello\\world/', '/hello\\world') + assertSafeTrimTrailingSeparator('\\\\hello\\world/', '\\\\hello\\world') + assertSafeTrimTrailingSeparator('hello\\world/', 'hello\\world') + + // Should remove redundant slashes (rooted paths; UNC format not special) + assertSafeTrimTrailingSeparator('//hello', '/hello') + assertSafeTrimTrailingSeparator('//hello/world', '/hello/world') + assertSafeTrimTrailingSeparator('//hello/world/', '/hello/world') + assertSafeTrimTrailingSeparator('//hello//world//', '/hello/world') + assertSafeTrimTrailingSeparator('///hello////world///', '/hello/world') + + // Should remove redundant slashes (relative paths) + assertSafeTrimTrailingSeparator('hello//world//', 'hello/world') + assertSafeTrimTrailingSeparator('hello///world///', 'hello/world') + + // Slash trimming (Windows drive root format not special) + assertSafeTrimTrailingSeparator('C:/', 'C:') + assertSafeTrimTrailingSeparator('C:/hello', 'C:/hello') + assertSafeTrimTrailingSeparator('C:/hello/', 'C:/hello') + assertSafeTrimTrailingSeparator('C:hello/', 'C:hello') + + // Slash trimming (rooted paths) + assertSafeTrimTrailingSeparator('/', '/') + assertSafeTrimTrailingSeparator('/hello', '/hello') + assertSafeTrimTrailingSeparator('/hello/', '/hello') + assertSafeTrimTrailingSeparator('/hello/world/', '/hello/world') + + // Slash trimming (relative paths) + assertSafeTrimTrailingSeparator('hello/world/', 'hello/world') + assertSafeTrimTrailingSeparator('../../', '../..') + } + }) +}) + +function assertDirectoryName(itemPath: string, expected: string): void { + expect(pathHelper.dirname(itemPath)).toBe(expected) +} + +function assertEnsureAbsoluteRoot( + root: string, + itemPath: string, + expected: string +): void { + expect(pathHelper.ensureAbsoluteRoot(root, itemPath)).toBe(expected) +} + +function assertHasAbsoluteRoot(itemPath: string, expected: boolean): void { + expect(pathHelper.hasAbsoluteRoot(itemPath)).toBe(expected) +} + +function assertHasRoot(itemPath: string, expected: boolean): void { + expect(pathHelper.hasRoot(itemPath)).toBe(expected) +} + +function assertNormalizeSeparators(itemPath: string, expected: string): void { + expect(pathHelper.normalizeSeparators(itemPath)).toBe(expected) +} + +function assertSafeTrimTrailingSeparator( + itemPath: string, + expected: string +): void { + expect(pathHelper.safeTrimTrailingSeparator(itemPath)).toBe(expected) +} diff --git a/packages/glob/__tests__/internal-path.test.ts b/packages/glob/__tests__/internal-path.test.ts new file mode 100644 index 0000000000..37090e49ff --- /dev/null +++ b/packages/glob/__tests__/internal-path.test.ts @@ -0,0 +1,92 @@ +import * as path from 'path' +import {Path} from '../src/internal-path' + +const IS_WINDOWS = process.platform === 'win32' + +describe('path', () => { + it('constructs from rooted path', () => { + assertPath(`/`, `${path.sep}`, [path.sep]) + assertPath(`/foo`, `${path.sep}foo`, [path.sep, 'foo']) + if (IS_WINDOWS) { + assertPath(`C:\\foo`, `C:\\foo`, ['C:\\', 'foo']) + assertPath(`C:foo`, `C:foo`, ['C:', 'foo']) + assertPath(`\\\\foo\\bar\\baz`, `\\\\foo\\bar\\baz`, [ + '\\\\foo\\bar', + 'baz' + ]) + } + }) + + it('constructs from rooted segments', () => { + assertPath([`/`], `${path.sep}`, [path.sep]) + assertPath([`/`, `foo`], `${path.sep}foo`, [path.sep, 'foo']) + if (IS_WINDOWS) { + assertPath([`C:\\`, `foo`], `C:\\foo`, ['C:\\', 'foo']) + assertPath([`C:`, `foo`], `C:foo`, ['C:', 'foo']) + assertPath([`\\\\foo\\bar`, `baz`], `\\\\foo\\bar\\baz`, [ + '\\\\foo\\bar', + 'baz' + ]) + } + }) + + it('constructs from relative path', () => { + assertPath(`foo`, `foo`, ['foo']) + assertPath(`foo/bar`, `foo${path.sep}bar`, ['foo', 'bar']) + }) + + it('constructs from relative segments', () => { + assertPath([`foo`], `foo`, ['foo']) + assertPath([`foo`, `bar`], `foo${path.sep}bar`, ['foo', 'bar']) + }) + + it('normalizes slashes', () => { + assertPath( + `/foo///bar${path.sep}${path.sep}${path.sep}baz`, + `${path.sep}foo${path.sep}bar${path.sep}baz`, + [path.sep, 'foo', 'bar', 'baz'] + ) + }) + + it('preserves relative pathing', () => { + assertPath( + '/foo/../bar/./baz', + `${path.sep}foo${path.sep}..${path.sep}bar${path.sep}.${path.sep}baz`, + [path.sep, 'foo', '..', 'bar', '.', 'baz'] + ) + }) + + it('trims unnecessary trailing slash', () => { + assertPath('/', path.sep, [path.sep]) + assertPath('/foo/', `${path.sep}foo`, [path.sep, 'foo']) + assertPath('foo/', 'foo', ['foo']) + assertPath('foo/bar/', `foo${path.sep}bar`, ['foo', 'bar']) + if (IS_WINDOWS) { + assertPath('\\', '\\', ['\\']) + assertPath('C:\\', 'C:\\', ['C:\\']) + assertPath('C:\\foo\\', 'C:\\foo', ['C:\\', 'foo']) + assertPath('C:foo\\', 'C:foo', ['C:', 'foo']) + assertPath('\\\\computer\\share\\', '\\\\computer\\share', [ + '\\\\computer\\share' + ]) + assertPath('\\\\computer\\share\\foo', '\\\\computer\\share\\foo', [ + '\\\\computer\\share', + 'foo' + ]) + assertPath('\\\\computer\\share\\foo\\', '\\\\computer\\share\\foo', [ + '\\\\computer\\share', + 'foo' + ]) + } + }) +}) + +function assertPath( + itemPath: string | string[], + expectedPath: string, + expectedSegments: string[] +): void { + const actual = new Path(itemPath) + expect(actual.toString()).toBe(expectedPath) + expect(actual.segments).toEqual(expectedSegments) +} diff --git a/packages/glob/__tests__/internal-pattern-helper.test.ts b/packages/glob/__tests__/internal-pattern-helper.test.ts new file mode 100644 index 0000000000..dc8b0de927 --- /dev/null +++ b/packages/glob/__tests__/internal-pattern-helper.test.ts @@ -0,0 +1,190 @@ +import * as path from 'path' +import * as patternHelper from '../src/internal-pattern-helper' +import {MatchKind} from '../src/internal-match-kind' +import {IS_WINDOWS} from '../../io/src/io-util' + +describe('pattern-helper', () => { + it('getSearchPaths omits negate search paths', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const patterns = patternHelper.parse( + [ + `${root}search1/foo/**`, + `${root}search2/bar/**`, + `!${root}search3/baz/**` + ], + patternHelper.getOptions() + ) + const searchPaths = patternHelper.getSearchPaths(patterns) + expect(searchPaths).toEqual([ + `${root}search1${path.sep}foo`, + `${root}search2${path.sep}bar` + ]) + }) + + it('getSearchPaths omits search path when ancestor is also a search path', () => { + if (IS_WINDOWS) { + const patterns = patternHelper.parse( + [ + 'C:\\Search1\\Foo\\**', + 'C:\\sEARCH1\\fOO\\bar\\**', + 'C:\\sEARCH1\\foo\\bar', + 'C:\\Search2\\**', + 'C:\\Search3\\Foo\\Bar\\**', + 'C:\\sEARCH3\\fOO\\bAR\\**' + ], + patternHelper.getOptions() + ) + const searchPaths = patternHelper.getSearchPaths(patterns) + expect(searchPaths).toEqual([ + 'C:\\Search1\\Foo', + 'C:\\Search2', + 'C:\\Search3\\Foo\\Bar' + ]) + } else { + const patterns = patternHelper.parse( + [ + '/search1/foo/**', + '/search1/foo/bar/**', + '/search2/foo/bar', + '/search2/**', + '/search3/foo/bar/**', + '/search3/foo/bar/**' + ], + patternHelper.getOptions() + ) + const searchPaths = patternHelper.getSearchPaths(patterns) + expect(searchPaths).toEqual([ + '/search1/foo', + '/search2', + '/search3/foo/bar' + ]) + } + }) + + it('match supports interleaved exclude patterns', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const itemPaths = [ + `${root}solution1/proj1/proj1.proj`, + `${root}solution1/proj1/README.txt`, + `${root}solution1/proj2/proj2.proj`, + `${root}solution1/proj2/README.txt`, + `${root}solution1/solution1.sln`, + `${root}solution2/proj1/proj1.proj`, + `${root}solution2/proj1/README.txt`, + `${root}solution2/proj2/proj2.proj`, + `${root}solution2/proj2/README.txt`, + `${root}solution2/solution2.sln` + ] + const patterns = patternHelper.parse( + [ + `${root}**/*.proj`, // include all proj files + `${root}**/README.txt`, // include all README files + `!${root}**/solution2/**`, // exclude the solution 2 folder entirely + `${root}**/*.sln`, // include all sln files + `!${root}**/proj2/README.txt` // exclude proj2 README files + ], + patternHelper.getOptions({implicitDescendants: false}) + ) + const matched = itemPaths.filter( + x => patternHelper.match(patterns, x) === MatchKind.All + ) + expect(matched).toEqual([ + `${root}solution1/proj1/proj1.proj`, + `${root}solution1/proj1/README.txt`, + `${root}solution1/proj2/proj2.proj`, + `${root}solution1/solution1.sln`, + `${root}solution2/solution2.sln` + ]) + }) + + it('match supports excluding directories', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const itemPaths = [ + root, + `${root}foo`, + `${root}foo/bar`, + `${root}foo/bar/baz` + ] + const patterns = patternHelper.parse( + [ + `${root}foo/**`, // include all files and directories + `!${root}foo/**/` // exclude directories + ], + patternHelper.getOptions({implicitDescendants: false}) + ) + const matchKinds = itemPaths.map(x => patternHelper.match(patterns, x)) + expect(matchKinds).toEqual([ + MatchKind.None, + MatchKind.File, + MatchKind.File, + MatchKind.File + ]) + }) + + it('match supports including directories only', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const itemPaths = [ + root, + `${root}foo/`, + `${root}foo/bar`, + `${root}foo/bar/baz` + ] + const patterns = patternHelper.parse( + [ + `${root}foo/**/` // include directories only + ], + patternHelper.getOptions({implicitDescendants: false}) + ) + const matchKinds = itemPaths.map(x => patternHelper.match(patterns, x)) + expect(matchKinds).toEqual([ + MatchKind.None, + MatchKind.Directory, + MatchKind.Directory, + MatchKind.Directory + ]) + }) + + it('parse skips comments', () => { + const patterns = patternHelper.parse( + ['# comment 1', ' # comment 2', '!#hello-world.txt'], + patternHelper.getOptions({implicitDescendants: false}) + ) + expect(patterns).toHaveLength(1) + expect(patterns[0].negate).toBeTruthy() + expect(patterns[0].segments.reverse()[0]).toEqual('#hello-world.txt') + }) + + it('parse skips empty patterns', () => { + const patterns = patternHelper.parse( + ['', ' ', 'hello-world.txt'], + patternHelper.getOptions({implicitDescendants: false}) + ) + expect(patterns).toHaveLength(1) + expect(patterns[0].segments.reverse()[0]).toEqual('hello-world.txt') + }) + + it('partialMatch skips negate patterns', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const patterns = patternHelper.parse( + [ + `${root}search1/foo/**`, + `${root}search2/bar/**`, + `!${root}search2/bar/**`, + `!${root}search3/baz/**` + ], + patternHelper.getOptions({implicitDescendants: false}) + ) + expect(patternHelper.partialMatch(patterns, `${root}search1`)).toBeTruthy() + expect( + patternHelper.partialMatch(patterns, `${root}search1/foo`) + ).toBeTruthy() + expect(patternHelper.partialMatch(patterns, `${root}search2`)).toBeTruthy() + expect( + patternHelper.partialMatch(patterns, `${root}search2/bar`) + ).toBeTruthy() + expect(patternHelper.partialMatch(patterns, `${root}search3`)).toBeFalsy() + expect( + patternHelper.partialMatch(patterns, `${root}search3/bar`) + ).toBeFalsy() + }) +}) diff --git a/packages/glob/__tests__/internal-pattern.test.ts b/packages/glob/__tests__/internal-pattern.test.ts new file mode 100644 index 0000000000..c6a1b2c874 --- /dev/null +++ b/packages/glob/__tests__/internal-pattern.test.ts @@ -0,0 +1,366 @@ +import * as io from '../../io/src/io' +import * as path from 'path' +import {MatchKind} from '../src/internal-match-kind' +import {promises as fs} from 'fs' + +// Mock 'os' before importing Pattern +/* eslint-disable import/first */ +/* eslint-disable @typescript-eslint/promise-function-async */ +// Note, @typescript-eslint/promise-function-async is a false positive due to the +// mock factory delegate which returns any. Fixed in a future version of jest. +jest.mock('os', () => jest.requireActual('os')) +const os = jest.requireMock('os') +import {Pattern} from '../src/internal-pattern' +jest.resetModuleRegistry() + +const IS_WINDOWS = process.platform === 'win32' + +describe('pattern', () => { + beforeAll(async () => { + await io.rmRF(getTestTemp()) + }) + + it('counts leading negate markers', () => { + const actual = [ + '/initial-includes/*.txt', + '!!/hello/two-negate-markers.txt', + '!!!!/hello/four-negate-markers.txt', + '!/initial-includes/one-negate-markers.txt', + '!!!/initial-includes/three-negate-markers.txt' + ].map(x => new Pattern(x).negate) + expect(actual).toEqual([false, false, false, true, true]) + }) + + it('escapes homedir', async () => { + const originalHomedir = os.homedir + const home = path.join(getTestTemp(), 'home-with-[and]') + await fs.mkdir(home, {recursive: true}) + try { + os.homedir = () => home + const pattern = new Pattern('~/m*') + + expect(pattern.searchPath).toBe(home) + expect(pattern.match(path.join(home, 'match'))).toBeTruthy() + expect(pattern.match(path.join(home, 'not-match'))).toBeFalsy() + } finally { + os.homedir = originalHomedir + } + }) + + it('escapes root', async () => { + const originalCwd = process.cwd() + const rootPath = path.join(getTestTemp(), 'cwd-with-[and]') + await fs.mkdir(rootPath, {recursive: true}) + try { + process.chdir(rootPath) + + // Relative + let pattern = new Pattern('m*') + expect(pattern.searchPath).toBe(rootPath) + expect(pattern.match(path.join(rootPath, 'match'))).toBeTruthy() + expect(pattern.match(path.join(rootPath, 'not-match'))).toBeFalsy() + + if (IS_WINDOWS) { + const currentDrive = process.cwd().substr(0, 2) + expect(currentDrive.match(/^[A-Z]:$/i)).toBeTruthy() + + // Relative current drive letter, e.g. C:m* + pattern = new Pattern(`${currentDrive}m*`) + expect(pattern.searchPath).toBe(rootPath) + expect(pattern.match(path.join(rootPath, 'match'))).toBeTruthy() + expect(pattern.match(path.join(rootPath, 'not-match'))).toBeFalsy() + + // Relative current drive, e.g. \path\to\cwd\m* + pattern = new Pattern( + `${Pattern.globEscape(process.cwd().substr(2))}\\m*` + ) + expect(pattern.searchPath).toBe(rootPath) + expect(pattern.match(path.join(rootPath, 'match'))).toBeTruthy() + expect(pattern.match(path.join(rootPath, 'not-match'))).toBeFalsy() + } + } finally { + process.chdir(originalCwd) + } + }) + + it('globstar matches immediately preceeding directory', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const pattern = new Pattern(`${root}foo/bar/**`) + const actual = [ + root, + `${root}foo`, + `${root}foo/bar`, + `${root}foo/bar/baz` + ].map(x => pattern.match(x)) + expect(actual).toEqual([ + MatchKind.None, + MatchKind.None, + MatchKind.All, + MatchKind.All + ]) + }) + + it('is case insensitive match on Windows', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const pattern = new Pattern(`${root}Foo/**/Baz`) + expect(pattern.match(`${root}Foo/Baz`)).toBe(MatchKind.All) + expect(pattern.match(`${root}Foo/bAZ`)).toBe( + IS_WINDOWS ? MatchKind.All : MatchKind.None + ) + expect(pattern.match(`${root}fOO/Baz`)).toBe( + IS_WINDOWS ? MatchKind.All : MatchKind.None + ) + expect(pattern.match(`${root}fOO/bar/bAZ`)).toBe( + IS_WINDOWS ? MatchKind.All : MatchKind.None + ) + }) + + it('is case insensitive partial match on Windows', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const pattern = new Pattern(`${root}Foo/Bar/**/Baz`) + expect(pattern.partialMatch(`${root}Foo`)).toBeTruthy() + expect(pattern.partialMatch(`${root}fOO`)).toBe(IS_WINDOWS ? true : false) + }) + + it('matches root', () => { + const pattern = new Pattern(IS_WINDOWS ? 'C:\\**' : '/**') + expect(pattern.match(IS_WINDOWS ? 'C:\\' : '/')).toBe(MatchKind.All) + }) + + it('partial matches root', () => { + if (IS_WINDOWS) { + let pattern = new Pattern('C:\\foo\\**') + expect(pattern.partialMatch('c:\\')).toBeTruthy() + pattern = new Pattern('c:\\foo\\**') + expect(pattern.partialMatch('C:\\')).toBeTruthy() + } else { + const pattern = new Pattern('/foo/**') + expect(pattern.partialMatch('/')).toBeTruthy() + } + }) + + it('replaces leading . segment', () => { + // Pattern is '.' + let pattern = new Pattern('.') + expect(pattern.match(process.cwd())).toBe(MatchKind.All) + expect(pattern.match(path.join(process.cwd(), 'foo'))).toBe(MatchKind.None) + + // Pattern is './foo' + pattern = new Pattern('./foo') + expect(pattern.match(path.join(process.cwd(), 'foo'))).toBe(MatchKind.All) + expect(pattern.match(path.join(process.cwd(), 'bar'))).toBe(MatchKind.None) + + // Pattern is '.foo' + pattern = new Pattern('.foo') + expect(pattern.match(path.join(process.cwd(), '.foo'))).toBe(MatchKind.All) + expect(pattern.match(path.join(process.cwd(), 'foo'))).toBe(MatchKind.None) + expect(pattern.match(`${process.cwd()}foo`)).toBe(MatchKind.None) + }) + + it('replaces leading ~ segment', async () => { + const homedir = os.homedir() + expect(homedir).toBeTruthy() + await fs.stat(homedir) + + // Pattern is '~' + let pattern = new Pattern('~') + expect(pattern.match(homedir)).toBe(MatchKind.All) + expect(pattern.match(path.join(homedir, 'foo'))).toBe(MatchKind.None) + + // Pattern is '~/foo' + pattern = new Pattern('~/foo') + expect(pattern.match(path.join(homedir, 'foo'))).toBe(MatchKind.All) + expect(pattern.match(path.join(homedir, 'bar'))).toBe(MatchKind.None) + + // Pattern is '~foo' + pattern = new Pattern('~foo') + expect(pattern.match(path.join(process.cwd(), '~foo'))).toBe(MatchKind.All) + expect(pattern.match(path.join(homedir, 'foo'))).toBe(MatchKind.None) + expect(pattern.match(`${homedir}foo`)).toBe(MatchKind.None) + }) + + it('replaces leading relative root', () => { + if (IS_WINDOWS) { + const currentDrive = process.cwd().substr(0, 2) + expect(currentDrive.match(/^[A-Z]:$/i)).toBeTruthy() + const otherDrive = currentDrive.toUpperCase().startsWith('C') + ? 'D:' + : 'C:' + expect(process.cwd().length).toBeGreaterThan(3) // sanity check not drive root + + // Pattern is 'C:' + let pattern = new Pattern(currentDrive) + expect(pattern.match(process.cwd())).toBeTruthy() + expect(pattern.match(path.join(process.cwd(), 'foo'))).toBeFalsy() + + // Pattern is 'C:foo' + pattern = new Pattern(`${currentDrive}foo`) + expect(pattern.match(path.join(process.cwd(), 'foo'))).toBeTruthy() + expect(pattern.match(path.join(process.cwd(), 'bar'))).toBeFalsy() + expect(pattern.match(`${currentDrive}\\foo`)).toBeFalsy() + + // Pattern is 'X:' + pattern = new Pattern(otherDrive) + expect(pattern.match(`${otherDrive}\\`)).toBeTruthy() + expect(pattern.match(`${otherDrive}\\foo`)).toBeFalsy() + + // Pattern is 'X:foo' + pattern = new Pattern(`${otherDrive}foo`) + expect(pattern.match(`${otherDrive}\\foo`)).toBeTruthy() + expect(pattern.match(`${otherDrive}\\bar`)).toBeFalsy() + + // Pattern is '\\path\\to\\cwd' + pattern = new Pattern(`${process.cwd().substr(2)}\\foo`) + expect(pattern.match(path.join(process.cwd(), 'foo'))).toBeTruthy() + expect(pattern.match(path.join(process.cwd(), 'bar'))).toBeFalsy() + } + }) + + it('roots exclude pattern', () => { + const patternStrings = ['!hello.txt', '!**/world.txt'] + const actual = patternStrings.map(x => new Pattern(x)) + const expected = patternStrings + .map(x => x.substr(1)) + .map(x => path.join(Pattern.globEscape(process.cwd()), x)) + .map(x => `!${x}`) + .map(x => new Pattern(x)) + expect(actual.map(x => x.negate)).toEqual([true, true]) + expect(actual.map(x => x.segments)).toEqual(expected.map(x => x.segments)) + }) + + it('roots include pattern', () => { + const patternStrings = ['hello.txt', '**/world.txt'] + const actual = patternStrings.map(x => new Pattern(x)) + const expected = patternStrings.map( + x => new Pattern(path.join(Pattern.globEscape(process.cwd()), x)) + ) + expect(actual.map(x => x.segments)).toEqual(expected.map(x => x.segments)) + }) + + it('sets trailing separator', () => { + expect(new Pattern(' foo ').trailingSeparator).toBeFalsy() + expect(new Pattern(' /foo ').trailingSeparator).toBeFalsy() + expect(new Pattern('! /foo ').trailingSeparator).toBeFalsy() + expect(new Pattern(' /foo/* ').trailingSeparator).toBeFalsy() + expect(new Pattern(' /foo/** ').trailingSeparator).toBeFalsy() + expect(new Pattern(' \\foo ').trailingSeparator).toBeFalsy() + expect(new Pattern('! \\foo ').trailingSeparator).toBeFalsy() + expect(new Pattern(' \\foo\\* ').trailingSeparator).toBeFalsy() + expect(new Pattern(' \\foo\\** ').trailingSeparator).toBeFalsy() + expect(new Pattern(' foo/ ').trailingSeparator).toBeTruthy() + expect(new Pattern(' /foo/ ').trailingSeparator).toBeTruthy() + expect(new Pattern(' C:/foo/ ').trailingSeparator).toBeTruthy() + expect(new Pattern(' C:foo/ ').trailingSeparator).toBeTruthy() + expect(new Pattern(' D:foo/ ').trailingSeparator).toBeTruthy() + expect(new Pattern('! /foo/ ').trailingSeparator).toBeTruthy() + expect(new Pattern(' /foo/*/ ').trailingSeparator).toBeTruthy() + expect(new Pattern(' /foo/**/ ').trailingSeparator).toBeTruthy() + expect(new Pattern(' foo\\ ').trailingSeparator).toEqual( + IS_WINDOWS ? true : false + ) + expect(new Pattern(' \\foo\\ ').trailingSeparator).toEqual( + IS_WINDOWS ? true : false + ) + expect(new Pattern('! \\foo\\ ').trailingSeparator).toEqual( + IS_WINDOWS ? true : false + ) + expect(new Pattern(' \\foo\\*\\ ').trailingSeparator).toEqual( + IS_WINDOWS ? true : false + ) + expect(new Pattern(' \\foo\\**\\ ').trailingSeparator).toEqual( + IS_WINDOWS ? true : false + ) + }) + + it('supports including directories only', () => { + const root = IS_WINDOWS ? 'C:\\' : '/' + const pattern = new Pattern(`${root}foo/**/`) // trailing slash + const actual = [ + root, + `${root}foo/`, + `${root}foo/bar`, + `${root}foo/bar/baz` + ].map(x => pattern.match(x)) + expect(pattern.trailingSeparator).toBeTruthy() + expect(actual).toEqual([ + MatchKind.None, + MatchKind.Directory, + MatchKind.Directory, + MatchKind.Directory + ]) + }) + + it('trims pattern', () => { + const pattern = new Pattern(' hello.txt ') + expect(pattern.segments.reverse()[0]).toBe('hello.txt') + }) + + it('trims whitespace after trimming negate markers', () => { + const pattern = new Pattern(' ! ! ! hello.txt ') + expect(pattern.negate).toBeTruthy() + expect(pattern.segments.reverse()[0]).toBe('hello.txt') + }) + + it('unescapes segments to narrow search path', () => { + // Positive + const root = IS_WINDOWS ? 'C:\\' : '/' + let pattern = new Pattern(`${root}foo/b[a]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}bar`) + expect(pattern.match(`${root}foo/bar/baz`)).toBeTruthy() + pattern = new Pattern(`${root}foo/b[*]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}b*r`) + expect(pattern.match(`${root}foo/b*r/baz`)).toBeTruthy() + expect(pattern.match(`${root}foo/bar/baz`)).toBeFalsy() + pattern = new Pattern(`${root}foo/b[?]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}b?r`) + expect(pattern.match(`${root}foo/b?r/baz`)).toBeTruthy() + expect(pattern.match(`${root}foo/bar/baz`)).toBeFalsy() + pattern = new Pattern(`${root}foo/b[!]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}b!r`) + expect(pattern.match(`${root}foo/b!r/baz`)).toBeTruthy() + pattern = new Pattern(`${root}foo/b[[]ar/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}b[ar`) + expect(pattern.match(`${root}foo/b[ar/baz`)).toBeTruthy() + pattern = new Pattern(`${root}foo/b[]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}b[]r`) + expect(pattern.match(`${root}foo/b[]r/baz`)).toBeTruthy() + pattern = new Pattern(`${root}foo/b[r/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}b[r`) + expect(pattern.match(`${root}foo/b[r/baz`)).toBeTruthy() + pattern = new Pattern(`${root}foo/b]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo${path.sep}b]r`) + expect(pattern.match(`${root}foo/b]r/baz`)).toBeTruthy() + if (!IS_WINDOWS) { + pattern = new Pattern('/foo/b\\[a]r/b*') + expect(pattern.searchPath).toBe(`${path.sep}foo${path.sep}b[a]r`) + expect(pattern.match('/foo/b[a]r/baz')).toBeTruthy() + pattern = new Pattern('/foo/b[\\!]r/b*') + expect(pattern.searchPath).toBe(`${path.sep}foo${path.sep}b!r`) + expect(pattern.match('/foo/b!r/baz')).toBeTruthy() + pattern = new Pattern('/foo/b[\\]]r/b*') + expect(pattern.searchPath).toBe(`${path.sep}foo${path.sep}b]r`) + expect(pattern.match('/foo/b]r/baz')).toBeTruthy() + pattern = new Pattern('/foo/b[\\a]r/b*') + expect(pattern.searchPath).toBe(`${path.sep}foo${path.sep}bar`) + expect(pattern.match('/foo/bar/baz')).toBeTruthy() + } + + // Negative + pattern = new Pattern(`${root}foo/b[aA]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo`) + pattern = new Pattern(`${root}foo/b[!a]r/b*`) + expect(pattern.searchPath).toBe(`${root}foo`) + if (IS_WINDOWS) { + pattern = new Pattern('C:/foo/b\\[a]r/b*') + expect(pattern.searchPath).toBe(`C:\\foo\\b\\ar`) + expect(pattern.match('C:/foo/b/ar/baz')).toBeTruthy() + pattern = new Pattern('C:/foo/b[\\!]r/b*') + expect(pattern.searchPath).toBe('C:\\foo\\b[\\!]r') + expect(pattern.match('C:/foo/b[undefined/!]r/baz')).toBeTruthy() // Note, "undefined" substr to accommodate a bug in Minimatch when nocase=true + } + }) +}) + +function getTestTemp(): string { + return path.join(__dirname, '_temp', 'internal-pattern') +} diff --git a/packages/glob/package-lock.json b/packages/glob/package-lock.json new file mode 100644 index 0000000000..3eb24c1b1a --- /dev/null +++ b/packages/glob/package-lock.json @@ -0,0 +1,35 @@ +{ + "name": "@actions/glob", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + } + } +} diff --git a/packages/glob/package.json b/packages/glob/package.json new file mode 100644 index 0000000000..067d59c997 --- /dev/null +++ b/packages/glob/package.json @@ -0,0 +1,41 @@ +{ + "name": "@actions/glob", + "version": "0.1.0", + "preview": true, + "description": "Actions glob lib", + "keywords": [ + "github", + "actions", + "glob" + ], + "homepage": "https://github.com/actions/toolkit/tree/master/packages/glob", + "license": "MIT", + "main": "lib/glob.js", + "types": "lib/glob.d.ts", + "directories": { + "lib": "lib", + "test": "__tests__" + }, + "files": [ + "lib" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/actions/toolkit.git", + "directory": "packages/glob" + }, + "scripts": { + "test": "echo \"Error: run tests from root\" && exit 1", + "tsc": "tsc" + }, + "bugs": { + "url": "https://github.com/actions/toolkit/issues" + }, + "dependencies": { + "@actions/core": "^1.2.0", + "minimatch": "^3.0.4" + } +} diff --git a/packages/glob/src/glob.ts b/packages/glob/src/glob.ts new file mode 100644 index 0000000000..861e1e8f6f --- /dev/null +++ b/packages/glob/src/glob.ts @@ -0,0 +1,183 @@ +import * as core from '@actions/core' +import * as fs from 'fs' +import * as path from 'path' +import * as patternHelper from './internal-pattern-helper' +import {IGlobOptions} from './internal-glob-options' +import {MatchKind} from './internal-match-kind' +import {Pattern} from './internal-pattern' +import {SearchState} from './internal-search-state' + +export {IGlobOptions} + +/** + * Returns files and directories matching the specified glob pattern. + * + * Order of the results is not guaranteed. + */ +export async function glob( + pattern: string, + options?: IGlobOptions +): Promise { + const result: string[] = [] + for await (const itemPath of globGenerator(pattern, options)) { + result.push(itemPath) + } + return result +} + +/** + * Returns files and directories matching the specified glob pattern. + * + * Order of the results is not guaranteed. + */ +export async function* globGenerator( + pattern: string, + options?: IGlobOptions +): AsyncGenerator { + // Set defaults options + options = patternHelper.getOptions(options) + + // Parse patterns + const patterns: Pattern[] = patternHelper.parse([pattern], options) + + // Push the search paths + const stack: SearchState[] = [] + for (const searchPath of patternHelper.getSearchPaths(patterns)) { + core.debug(`Search path '${searchPath}'`) + + // Exists? + try { + // Intentionally using lstat. Detection for broken symlink + // will be performed later (if following symlinks). + await fs.promises.lstat(searchPath) + } catch (err) { + if (err.code === 'ENOENT') { + continue + } + throw err + } + + stack.unshift(new SearchState(searchPath, 1)) + } + + // Search + const traversalChain: string[] = [] // used to detect cycles + while (stack.length) { + // Pop + const item = stack.pop() as SearchState + + // Match? + const match = patternHelper.match(patterns, item.path) + const partialMatch = + !!match || patternHelper.partialMatch(patterns, item.path) + if (!match && !partialMatch) { + continue + } + + // Stat + const stats: fs.Stats | undefined = await stat( + item, + options, + traversalChain + ) + + // Broken symlink, or symlink cycle detected, or no longer exists + if (!stats) { + continue + } + + // Directory + if (stats.isDirectory()) { + // Matched + if (match & MatchKind.Directory) { + yield item.path + } + // Descend? + else if (!partialMatch) { + continue + } + + // Push the child items in reverse + const childLevel = item.level + 1 + const childItems = (await fs.promises.readdir(item.path)).map( + x => new SearchState(path.join(item.path, x), childLevel) + ) + stack.push(...childItems.reverse()) + } + // File + else if (match & MatchKind.File) { + yield item.path + } + } +} + +/** + * Returns the search path preceeding the first segment that contains a pattern. + * + * For example, '/foo/bar*' returns '/foo'. + */ +export function getSearchPath(pattern: string): string { + const patterns: Pattern[] = patternHelper.parse( + [pattern], + patternHelper.getOptions() + ) + const searchPaths: string[] = patternHelper.getSearchPaths(patterns) + return searchPaths.length > 0 ? searchPaths[0] : '' +} + +async function stat( + item: SearchState, + options: IGlobOptions, + traversalChain: string[] +): Promise { + // Note: + // `stat` returns info about the target of a symlink (or symlink chain) + // `lstat` returns info about a symlink itself + let stats: fs.Stats + if (options.followSymbolicLinks) { + try { + // Use `stat` (following symlinks) + stats = await fs.promises.stat(item.path) + } catch (err) { + if (err.code === 'ENOENT') { + if (options.omitBrokenSymbolicLinks) { + core.debug(`Broken symlink '${item.path}'`) + return undefined + } + + throw new Error( + `No information found for the path '${item.path}'. This may indicate a broken symbolic link.` + ) + } + + throw err + } + } else { + // Use `lstat` (not following symlinks) + stats = await fs.promises.lstat(item.path) + } + + // Note, isDirectory() returns false for the lstat of a symlink + if (stats.isDirectory() && options.followSymbolicLinks) { + // Get the realpath + const realPath: string = await fs.promises.realpath(item.path) + + // Fixup the traversal chain to match the item level + while (traversalChain.length >= item.level) { + traversalChain.pop() + } + + // Test for a cycle + if (traversalChain.some((x: string) => x === realPath)) { + core.debug( + `Symlink cycle detected for path '${item.path}' and realpath '${realPath}'` + ) + return undefined + } + + // Update the traversal chain + traversalChain.push(realPath) + } + + return stats +} diff --git a/packages/glob/src/internal-glob-options.ts b/packages/glob/src/internal-glob-options.ts new file mode 100644 index 0000000000..8e427454a9 --- /dev/null +++ b/packages/glob/src/internal-glob-options.ts @@ -0,0 +1,28 @@ +export interface IGlobOptions { + /** + * Indicates whether to follow symbolic links. Generally should be true + * unless deleting files. + * + * @default true + */ + followSymbolicLinks?: boolean + + /** + * Indicates whether directories that match a glob pattern, should implicitly + * cause all descendant paths to be matched. + * + * For example, given the directory `my-dir`, the following glob patterns + * would produce the same results: `my-dir/**`, `my-dir/`, `my-dir` + * + * @default true + */ + implicitDescendants?: boolean + + /** + * Indicates whether broken symbolic should be ignored and omitted from the + * result set. Otherwise an error will be thrown. + * + * @default true + */ + omitBrokenSymbolicLinks?: boolean +} diff --git a/packages/glob/src/internal-match-kind.ts b/packages/glob/src/internal-match-kind.ts new file mode 100644 index 0000000000..75802da539 --- /dev/null +++ b/packages/glob/src/internal-match-kind.ts @@ -0,0 +1,16 @@ +/** + * Indicates whether a pattern matches a path + */ +export enum MatchKind { + /** Not matched */ + None = 0, + + /** Matched if the path is a directory */ + Directory = 1, + + /** Matched if the path is a regular file */ + File = 2, + + /** Matched */ + All = Directory | File +} diff --git a/packages/glob/src/internal-path-helper.ts b/packages/glob/src/internal-path-helper.ts new file mode 100644 index 0000000000..7d9d0ef4a5 --- /dev/null +++ b/packages/glob/src/internal-path-helper.ts @@ -0,0 +1,206 @@ +import * as assert from 'assert' +import * as path from 'path' + +const IS_WINDOWS = process.platform === 'win32' + +/** + * Similar to path.dirname except normalizes the path separators and slightly better handling for Windows UNC paths. + * + * For example, on Linux/macOS: + * - `/ => /` + * - `/hello => /` + * + * For example, on Windows: + * - `C:\ => C:\` + * - `C:\hello => C:\` + * - `C: => C:` + * - `C:hello => C:` + * - `\ => \` + * - `\hello => \` + * - `\\hello => \\hello` + * - `\\hello\world => \\hello\world` + */ +export function dirname(p: string): string { + // Normalize slashes and trim unnecessary trailing slash + p = safeTrimTrailingSeparator(p) + + // Windows UNC root, e.g. \\hello or \\hello\world + if (IS_WINDOWS && /^\\\\[^\\]+(\\[^\\]+)?$/.test(p)) { + return p + } + + // Get dirname + let result = path.dirname(p) + + // Trim trailing slash for Windows UNC root, e.g. \\hello\world\ + if (IS_WINDOWS && /^\\\\[^\\]+\\[^\\]+\\$/.test(result)) { + result = safeTrimTrailingSeparator(result) + } + + return result +} + +/** + * Roots the path if not already rooted. On Windows, relative roots like `\` + * or `C:` are expanded based on the current working directory. + */ +export function ensureAbsoluteRoot(root: string, itemPath: string): string { + assert(root, `ensureAbsoluteRoot parameter 'root' must not be empty`) + assert(itemPath, `ensureAbsoluteRoot parameter 'itemPath' must not be empty`) + + // Already rooted + if (hasAbsoluteRoot(itemPath)) { + return itemPath + } + + // Windows + if (IS_WINDOWS) { + // Check for itemPath like C: or C:foo + if (itemPath.match(/^[A-Z]:[^\\/]|^[A-Z]:$/i)) { + let cwd = process.cwd() + assert( + cwd.match(/^[A-Z]:\\/i), + `Expected current directory to start with an absolute drive root. Actual '${cwd}'` + ) + + // Drive letter matches cwd? Expand to cwd + if (itemPath[0].toUpperCase() === cwd[0].toUpperCase()) { + // Drive only, e.g. C: + if (itemPath.length === 2) { + // Preserve specified drive letter case (upper or lower) + return `${itemPath[0]}:\\${cwd.substr(3)}` + } + // Drive + path, e.g. C:foo + else { + if (!cwd.endsWith('\\')) { + cwd += '\\' + } + // Preserve specified drive letter case (upper or lower) + return `${itemPath[0]}:\\${cwd.substr(3)}${itemPath.substr(2)}` + } + } + // Different drive + else { + return `${itemPath[0]}:\\${itemPath.substr(2)}` + } + } + // Check for itemPath like \ or \foo + else if (normalizeSeparators(itemPath).match(/^\\$|^\\[^\\]/)) { + const cwd = process.cwd() + assert( + cwd.match(/^[A-Z]:\\/i), + `Expected current directory to start with an absolute drive root. Actual '${cwd}'` + ) + + return `${cwd[0]}:\\${itemPath.substr(1)}` + } + } + + assert( + hasAbsoluteRoot(root), + `ensureAbsoluteRoot parameter 'root' must have an absolute root` + ) + + // Otherwise ensure root ends with a separator + if (root.endsWith('/') || (IS_WINDOWS && root.endsWith('\\'))) { + // Intentionally empty + } else { + // Append separator + root += path.sep + } + + return root + itemPath +} + +/** + * On Linux/macOS, true if path starts with `/`. On Windows, true for paths like: + * `\\hello\share` and `C:\hello` (and using alternate separator). + */ +export function hasAbsoluteRoot(itemPath: string): boolean { + assert(itemPath, `hasAbsoluteRoot parameter 'itemPath' must not be empty`) + + // Normalize separators + itemPath = normalizeSeparators(itemPath) + + // Windows + if (IS_WINDOWS) { + // E.g. \\hello\share or C:\hello + return itemPath.startsWith('\\\\') || /^[A-Z]:\\/i.test(itemPath) + } + + // E.g. /hello + return itemPath.startsWith('/') +} + +/** + * On Linux/macOS, true if path starts with `/`. On Windows, true for paths like: + * `\`, `\hello`, `\\hello\share`, `C:`, and `C:\hello` (and using alternate separator). + */ +export function hasRoot(itemPath: string): boolean { + assert(itemPath, `isRooted parameter 'itemPath' must not be empty`) + + // Normalize separators + itemPath = normalizeSeparators(itemPath) + + // Windows + if (IS_WINDOWS) { + // E.g. \ or \hello or \\hello + // E.g. C: or C:\hello + return itemPath.startsWith('\\') || /^[A-Z]:/i.test(itemPath) + } + + // E.g. /hello + return itemPath.startsWith('/') +} + +/** + * Removes redundant slashes and converts `/` to `\` on Windows + */ +export function normalizeSeparators(p: string): string { + p = p || '' + + // Windows + if (IS_WINDOWS) { + // Convert slashes on Windows + p = p.replace(/\//g, '\\') + + // Remove redundant slashes + const isUnc = /^\\\\+[^\\]/.test(p) // e.g. \\hello + return (isUnc ? '\\' : '') + p.replace(/\\\\+/g, '\\') // preserve leading \\ for UNC + } + + // Remove redundant slashes + return p.replace(/\/\/+/g, '/') +} + +/** + * Normalizes the path separators and trims the trailing separator (when safe). + * For example, `/foo/ => /foo` but `/ => /` + */ +export function safeTrimTrailingSeparator(p: string): string { + // Short-circuit if empty + if (!p) { + return '' + } + + // Normalize separators + p = normalizeSeparators(p) + + // No trailing slash + if (!p.endsWith(path.sep)) { + return p + } + + // Check '/' on Linux/macOS and '\' on Windows + if (p === path.sep) { + return p + } + + // On Windows check if drive root. E.g. C:\ + if (IS_WINDOWS && /^[A-Z]:\\$/i.test(p)) { + return p + } + + // Otherwise trim trailing slash + return p.substr(0, p.length - 1) +} diff --git a/packages/glob/src/internal-path.ts b/packages/glob/src/internal-path.ts new file mode 100644 index 0000000000..789cb11884 --- /dev/null +++ b/packages/glob/src/internal-path.ts @@ -0,0 +1,113 @@ +import * as assert from 'assert' +import * as path from 'path' +import * as pathHelper from './internal-path-helper' + +const IS_WINDOWS = process.platform === 'win32' + +/** + * Helper class for parsing paths into segments + */ +export class Path { + segments: string[] = [] + + /** + * Constructs a Path + * @param itemPath Path or array of segments + */ + constructor(itemPath: string | string[]) { + // String + if (typeof itemPath === 'string') { + assert(itemPath, `Parameter 'itemPath' must not be empty`) + + // Normalize slashes and trim unnecessary trailing slash + itemPath = pathHelper.safeTrimTrailingSeparator(itemPath) + + // Not rooted + if (!pathHelper.hasRoot(itemPath)) { + this.segments = itemPath.split(path.sep) + } + // Rooted + else { + // Add all segments, while not at the root + let remaining = itemPath + let dir = pathHelper.dirname(remaining) + while (dir !== remaining) { + // Add the segment + const basename = path.basename(remaining) + this.segments.unshift(basename) + + // Truncate the last segment + remaining = dir + dir = pathHelper.dirname(remaining) + } + + // Remainder is the root + this.segments.unshift(remaining) + } + } + // Array + else { + // Must not be empty + assert( + itemPath.length > 0, + `Parameter 'itemPath' must not be an empty array` + ) + + // Each segment + for (let i = 0; i < itemPath.length; i++) { + let segment = itemPath[i] + + // Must not be empty + assert( + segment, + `Parameter 'itemPath' must not contain any empty segments` + ) + + // Normalize slashes + segment = pathHelper.normalizeSeparators(itemPath[i]) + + // Root segment + if (i === 0 && pathHelper.hasRoot(segment)) { + segment = pathHelper.safeTrimTrailingSeparator(segment) + assert( + segment === pathHelper.dirname(segment), + `Parameter 'itemPath' root segment contains information for multiple segments` + ) + this.segments.push(segment) + } + // All other segments + else { + // Must not contain slash + assert( + !segment.includes(path.sep), + `Parameter 'itemPath' contains unexpected path separators` + ) + this.segments.push(segment) + } + } + } + } + + /** + * Converts the path to it's string representation + */ + toString(): string { + // First segment + let result = this.segments[0] + + // All others + let skipSlash = + result.endsWith(path.sep) || (IS_WINDOWS && /^[A-Z]:$/i.test(result)) + for (let i = 1; i < this.segments.length; i++) { + if (skipSlash) { + skipSlash = false + } else { + result += path.sep + } + + result += this.segments[i] + } + + return result + } +} diff --git a/packages/glob/src/internal-pattern-helper.ts b/packages/glob/src/internal-pattern-helper.ts new file mode 100644 index 0000000000..9ca56b146c --- /dev/null +++ b/packages/glob/src/internal-pattern-helper.ts @@ -0,0 +1,143 @@ +import * as core from '@actions/core' +import * as pathHelper from './internal-path-helper' +import {IGlobOptions} from './internal-glob-options' +import {MatchKind} from './internal-match-kind' +import {Pattern} from './internal-pattern' + +const IS_WINDOWS = process.platform === 'win32' + +/** + * Returns a copy with defaults filled in + */ +export function getOptions(copy?: IGlobOptions): IGlobOptions { + const result: IGlobOptions = { + followSymbolicLinks: true, + implicitDescendants: true, + omitBrokenSymbolicLinks: true + } + + if (copy) { + if (typeof copy.followSymbolicLinks === 'boolean') { + result.followSymbolicLinks = copy.followSymbolicLinks + core.debug(`followSymbolicLinks '${result.followSymbolicLinks}'`) + } + + if (typeof copy.implicitDescendants === 'boolean') { + result.implicitDescendants = copy.implicitDescendants + core.debug(`implicitDescendants '${result.implicitDescendants}'`) + } + + if (typeof copy.omitBrokenSymbolicLinks === 'boolean') { + result.omitBrokenSymbolicLinks = copy.omitBrokenSymbolicLinks + core.debug(`omitBrokenSymbolicLinks '${result.omitBrokenSymbolicLinks}'`) + } + } + + return result +} + +/** + * Given an array of patterns, returns an array of paths to search. + * Duplicates and paths under other included paths are filtered out. + */ +export function getSearchPaths(patterns: Pattern[]): string[] { + // Ignore negate patterns + patterns = patterns.filter(x => !x.negate) + + // Create a map of all search paths + const searchPathMap: {[key: string]: string} = {} + for (const pattern of patterns) { + const key = IS_WINDOWS + ? pattern.searchPath.toUpperCase() + : pattern.searchPath + searchPathMap[key] = 'candidate' + } + + const result: string[] = [] + + for (const pattern of patterns) { + // Check if already included + const key = IS_WINDOWS + ? pattern.searchPath.toUpperCase() + : pattern.searchPath + if (searchPathMap[key] === 'included') { + continue + } + + // Check for an ancestor search path + let foundAncestor = false + let tempKey = key + let parent = pathHelper.dirname(tempKey) + while (parent !== tempKey) { + if (searchPathMap[parent]) { + foundAncestor = true + break + } + + tempKey = parent + parent = pathHelper.dirname(tempKey) + } + + // Include the search pattern in the result + if (!foundAncestor) { + result.push(pattern.searchPath) + searchPathMap[key] = 'included' + } + } + + return result +} + +/** + * Matches the patterns against the path + */ +export function match(patterns: Pattern[], itemPath: string): MatchKind { + let result: MatchKind = MatchKind.None + + for (const pattern of patterns) { + if (pattern.negate) { + result &= ~pattern.match(itemPath) + } else { + result |= pattern.match(itemPath) + } + } + + return result +} + +/** + * Parses the pattern strings into Pattern objects + */ +export function parse(patterns: string[], options: IGlobOptions): Pattern[] { + const result: Pattern[] = [] + + for (const patternString of patterns.map(x => x.trim())) { + // Skip empty or comment + if (!patternString || patternString.startsWith('#')) { + continue + } + + // Push + const pattern = new Pattern(patternString) + result.push(pattern) + + // Implicit descendants? + if ( + options.implicitDescendants && + (pattern.trailingSeparator || + pattern.segments[pattern.segments.length - 1] !== '**') + ) { + // Push + result.push(new Pattern(pattern.negate, pattern.segments.concat('**'))) + } + } + + return result +} + +/** + * Checks whether to descend further into the directory + */ +export function partialMatch(patterns: Pattern[], itemPath: string): boolean { + return patterns.some(x => !x.negate && x.partialMatch(itemPath)) +} diff --git a/packages/glob/src/internal-pattern.ts b/packages/glob/src/internal-pattern.ts new file mode 100644 index 0000000000..2735ca73cc --- /dev/null +++ b/packages/glob/src/internal-pattern.ts @@ -0,0 +1,321 @@ +import * as assert from 'assert' +import * as os from 'os' +import * as path from 'path' +import * as pathHelper from './internal-path-helper' +import {Minimatch, IMinimatch, IOptions as IMinimatchOptions} from 'minimatch' +import {MatchKind} from './internal-match-kind' +import {Path} from './internal-path' + +const IS_WINDOWS = process.platform === 'win32' + +export class Pattern { + /** + * Indicates whether matches should be excluded from the result set + */ + readonly negate: boolean = false + + /** + * The directory to search. The literal path prior to the first glob segment. + */ + readonly searchPath: string + + /** + * The path/pattern segments. Note, only the first segment (the root directory) + * may contain a directory separator charactor. Use the trailingSeparator field + * to determine whether the pattern ended with a trailing slash. + */ + readonly segments: string[] + + /** + * Indicates the pattern should only match directories, not regular files. + */ + readonly trailingSeparator: boolean + + /** + * The Minimatch object used for matching + */ + private readonly minimatch: IMinimatch + + /** + * Used to workaround a limitation with Minimatch when determining a partial + * match and the path is a root directory. For example, when the pattern is + * `/foo/**` or `C:\foo\**` and the path is `/` or `C:\`. + */ + private readonly rootRegExp: RegExp + + /* eslint-disable no-dupe-class-members */ + // Disable no-dupe-class-members due to false positive for method overload + // https://github.com/typescript-eslint/typescript-eslint/issues/291 + + constructor(pattern: string) + constructor(negate: boolean, segments: string[]) + constructor(patternOrNegate: string | boolean, segments?: string[]) { + // Pattern overload + let pattern: string + if (typeof patternOrNegate === 'string') { + pattern = patternOrNegate.trim() + } + // Segments overload + else { + // Convert to pattern + segments = segments || [] + assert(segments.length, `Parameter 'segments' must not empty`) + const root = Pattern.getLiteral(segments[0]) + assert( + root && pathHelper.hasAbsoluteRoot(root), + `Parameter 'segments' first element must be a root path` + ) + pattern = new Path(segments).toString().trim() + if (patternOrNegate) { + pattern = `!${pattern}` + } + } + + // Negate + while (pattern.startsWith('!')) { + this.negate = !this.negate + pattern = pattern.substr(1).trim() + } + + // Normalize slashes and ensures absolute root + pattern = Pattern.fixupPattern(pattern) + + // Segments + this.segments = new Path(pattern).segments + + // Trailing slash indicates the pattern should only match directories, not regular files + this.trailingSeparator = pathHelper + .normalizeSeparators(pattern) + .endsWith(path.sep) + pattern = pathHelper.safeTrimTrailingSeparator(pattern) + + // Search path (literal path prior to the first glob segment) + let foundGlob = false + const searchSegments = this.segments + .map(x => Pattern.getLiteral(x)) + .filter(x => !foundGlob && !(foundGlob = x === '')) + this.searchPath = new Path(searchSegments).toString() + + // Root RegExp (required when determining partial match) + this.rootRegExp = new RegExp( + Pattern.regExpEscape(searchSegments[0]), + IS_WINDOWS ? 'i' : '' + ) + + // Create minimatch + const minimatchOptions: IMinimatchOptions = { + dot: true, + nobrace: true, + nocase: IS_WINDOWS, + nocomment: true, + noext: true, + nonegate: true + } + pattern = IS_WINDOWS ? pattern.replace(/\\/g, '/') : pattern + this.minimatch = new Minimatch(pattern, minimatchOptions) + } + + /** + * Matches the pattern against the specified path + */ + match(itemPath: string): MatchKind { + // Last segment is globstar? + if (this.segments[this.segments.length - 1] === '**') { + // Normalize slashes + itemPath = pathHelper.normalizeSeparators(itemPath) + + // Append a trailing slash. Otherwise Minimatch will not match the directory immediately + // preceeding the globstar. For example, given the pattern `/foo/**`, Minimatch returns + // false for `/foo` but returns true for `/foo/`. Append a trailing slash to handle that quirk. + if (!itemPath.endsWith(path.sep)) { + // Note, this is safe because the constructor ensures the pattern has an absolute root. + // For example, formats like C: and C:foo on Windows are resolved to an aboslute root. + itemPath = `${itemPath}${path.sep}` + } + } else { + // Normalize slashes and trim unnecessary trailing slash + itemPath = pathHelper.safeTrimTrailingSeparator(itemPath) + } + + // Match + if (this.minimatch.match(itemPath)) { + return this.trailingSeparator ? MatchKind.Directory : MatchKind.All + } + + return MatchKind.None + } + + /** + * Indicates whether the pattern may match descendants of the specified path + */ + partialMatch(itemPath: string): boolean { + // Normalize slashes and trim unnecessary trailing slash + itemPath = pathHelper.safeTrimTrailingSeparator(itemPath) + + // matchOne does not handle root path correctly + if (pathHelper.dirname(itemPath) === itemPath) { + return this.rootRegExp.test(itemPath) + } + + return this.minimatch.matchOne( + itemPath.split(IS_WINDOWS ? /\\+/ : /\/+/), + this.minimatch.set[0], + true + ) + } + + /** + * Escapes glob patterns within a path + */ + static globEscape(s: string): string { + return (IS_WINDOWS ? s : s.replace(/\\/g, '\\\\')) // escape '\' on Linux/macOS + .replace(/(\[)(?=[^/]+\])/g, '[[]') // escape '[' when ']' follows within the path segment + .replace(/\?/g, '[?]') // escape '?' + .replace(/\*/g, '[*]') // escape '*' + } + + /** + * Normalizes slashes and ensures absolute root + */ + private static fixupPattern(pattern: string): string { + // Empty + assert(pattern, 'pattern cannot be empty') + + // Must not contain `.` segment, unless first segment + // Must not contain `..` segment + const literalSegments = new Path(pattern).segments.map(x => + Pattern.getLiteral(x) + ) + assert( + literalSegments.every((x, i) => (x !== '.' || i === 0) && x !== '..'), + `Invalid pattern '${pattern}'. Relative pathing '.' and '..' is not allowed.` + ) + + // Must not contain globs in root, e.g. Windows UNC path \\foo\b*r + assert( + !pathHelper.hasRoot(pattern) || literalSegments[0], + `Invalid pattern '${pattern}'. Root segment must not contain globs.` + ) + + // Normalize slashes + pattern = pathHelper.normalizeSeparators(pattern) + + // Replace leading `.` segment + if (pattern === '.' || pattern.startsWith(`.${path.sep}`)) { + pattern = Pattern.globEscape(process.cwd()) + pattern.substr(1) + } + // Replace leading `~` segment + else if (pattern === '~' || pattern.startsWith(`~${path.sep}`)) { + const homedir = os.homedir() + assert(homedir, 'Unable to determine HOME directory') + assert( + pathHelper.hasAbsoluteRoot(homedir), + `Expected HOME directory to be a rooted path. Actual '${homedir}'` + ) + pattern = Pattern.globEscape(homedir) + pattern.substr(1) + } + // Replace relative drive root, e.g. pattern is C: or C:foo + else if ( + IS_WINDOWS && + (pattern.match(/^[A-Z]:$/i) || pattern.match(/^[A-Z]:[^\\]/i)) + ) { + let root = pathHelper.ensureAbsoluteRoot( + 'C:\\dummy-root', + pattern.substr(0, 2) + ) + if (pattern.length > 2 && !root.endsWith('\\')) { + root += '\\' + } + pattern = Pattern.globEscape(root) + pattern.substr(2) + } + // Replace relative root, e.g. pattern is \ or \foo + else if (IS_WINDOWS && (pattern === '\\' || pattern.match(/^\\[^\\]/))) { + let root = pathHelper.ensureAbsoluteRoot('C:\\dummy-root', '\\') + if (!root.endsWith('\\')) { + root += '\\' + } + pattern = Pattern.globEscape(root) + pattern.substr(1) + } + // Otherwise ensure absolute root + else { + pattern = pathHelper.ensureAbsoluteRoot( + Pattern.globEscape(process.cwd()), + pattern + ) + } + + return pathHelper.normalizeSeparators(pattern) + } + + /** + * Attempts to unescape a pattern segment to create a literal path segment. + * Otherwise returns empty string. + */ + private static getLiteral(segment: string): string { + let literal = '' + for (let i = 0; i < segment.length; i++) { + const c = segment[i] + // Escape + if (c === '\\' && !IS_WINDOWS && i + 1 < segment.length) { + literal += segment[++i] + continue + } + // Wildcard + else if (c === '*' || c === '?') { + return '' + } + // Character set + else if (c === '[' && i + 1 < segment.length) { + let set = '' + let closed = -1 + for (let i2 = i + 1; i2 < segment.length; i2++) { + const c2 = segment[i2] + // Escape + if (c2 === '\\' && !IS_WINDOWS && i2 + 1 < segment.length) { + set += segment[++i2] + continue + } + // Closed + else if (c2 === ']') { + closed = i2 + break + } + // Otherwise + else { + set += c2 + } + } + + // Closed? + if (closed >= 0) { + // Cannot convert + if (set.length > 1) { + return '' + } + + // Convert to literal + if (set) { + literal += set + i = closed + continue + } + } + + // Otherwise fall thru + } + + // Append + literal += c + } + + return literal + } + + /** + * Escapes regexp special characters + * https://javascript.info/regexp-escaping + */ + private static regExpEscape(s: string): string { + return s.replace(/[[\\^$.|?*+()]/g, '\\$&') + } +} diff --git a/packages/glob/src/internal-search-state.ts b/packages/glob/src/internal-search-state.ts new file mode 100644 index 0000000000..590db3d869 --- /dev/null +++ b/packages/glob/src/internal-search-state.ts @@ -0,0 +1,9 @@ +export class SearchState { + readonly path: string + readonly level: number + + constructor(path: string, level: number) { + this.path = path + this.level = level + } +} diff --git a/packages/glob/tsconfig.json b/packages/glob/tsconfig.json new file mode 100644 index 0000000000..a8b812a6ff --- /dev/null +++ b/packages/glob/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./lib", + "rootDir": "./src" + }, + "include": [ + "./src" + ] +} \ No newline at end of file From 4e69ce10e91ecd239e16343fe2ae7e54d3e35fed Mon Sep 17 00:00:00 2001 From: francisfuzz <15894826+francisfuzz@users.noreply.github.com> Date: Fri, 3 Jan 2020 12:26:02 -0800 Subject: [PATCH 066/192] package-lock.json: update handlebars & uglify-js (#279) --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 63913c62aa..1eb3292436 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6569,9 +6569,9 @@ "dev": true }, "handlebars": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz", - "integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", + "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", "dev": true, "requires": { "neo-async": "^2.6.0", @@ -13140,9 +13140,9 @@ "dev": true }, "uglify-js": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.8.tgz", - "integrity": "sha512-XhHJ3S3ZyMwP8kY1Gkugqx3CJh2C3O0y8NPiSxtm1tyD/pktLAkFZsFGpuNfTZddKDQ/bbDBLAd2YyA1pbi8HQ==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.3.tgz", + "integrity": "sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg==", "dev": true, "optional": true, "requires": { From 803934eca047e379f447096fe8ee202c9c496cf5 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Fri, 3 Jan 2020 17:54:10 -0500 Subject: [PATCH 067/192] audit security vulnerabilities as part of ci (#280) --- .github/workflows/unit-tests.yml | 6 ++++++ package.json | 1 + packages/core/package.json | 1 + packages/exec/package-lock.json | 4 ++-- packages/exec/package.json | 1 + packages/github/package.json | 1 + packages/glob/package-lock.json | 14 -------------- packages/glob/package.json | 1 + packages/io/package-lock.json | 2 +- packages/io/package.json | 1 + packages/tool-cache/package.json | 5 +++-- 11 files changed, 18 insertions(+), 19 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index b9c23bff83..2f192b633f 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -47,3 +47,9 @@ jobs: - name: Format run: npm run format-check + + - name: audit tools + run: npm audit --audit-level=moderate + + - name: audit packages + run: npm run audit-all diff --git a/package.json b/package.json index c50ab127b8..5cfb35e1d7 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "root", "private": true, "scripts": { + "audit-all": "lerna run audit-moderate", "bootstrap": "lerna bootstrap", "build": "lerna run tsc", "check-all": "concurrently \"npm:format-check\" \"npm:lint\" \"npm:test\" \"npm:build -- -- --noEmit\"", diff --git a/packages/core/package.json b/packages/core/package.json index e84993c656..bdd061485f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,6 +27,7 @@ "directory": "packages/core" }, "scripts": { + "audit-moderate": "npm install && npm audit --audit-level=moderate", "test": "echo \"Error: run tests from root\" && exit 1", "tsc": "tsc" }, diff --git a/packages/exec/package-lock.json b/packages/exec/package-lock.json index ae5a0ad59d..250508c749 100644 --- a/packages/exec/package-lock.json +++ b/packages/exec/package-lock.json @@ -1,11 +1,11 @@ { "name": "@actions/exec", - "version": "1.0.0", + "version": "1.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { "@actions/io": { - "version": "1.0.0", + "version": "1.0.1", "dev": true } } diff --git a/packages/exec/package.json b/packages/exec/package.json index 42d9148bb7..0797a8095f 100644 --- a/packages/exec/package.json +++ b/packages/exec/package.json @@ -27,6 +27,7 @@ "directory": "packages/exec" }, "scripts": { + "audit-moderate": "npm install && npm audit --audit-level=moderate", "test": "echo \"Error: run tests from root\" && exit 1", "tsc": "tsc" }, diff --git a/packages/github/package.json b/packages/github/package.json index e49db4a46e..b956d42363 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -26,6 +26,7 @@ "directory": "packages/github" }, "scripts": { + "audit-moderate": "npm install && npm audit --audit-level=moderate", "test": "jest", "build": "tsc", "format": "prettier --write **/*.ts", diff --git a/packages/glob/package-lock.json b/packages/glob/package-lock.json index 3eb24c1b1a..a1e9ecc7a6 100644 --- a/packages/glob/package-lock.json +++ b/packages/glob/package-lock.json @@ -4,20 +4,6 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", diff --git a/packages/glob/package.json b/packages/glob/package.json index 067d59c997..7b4c780064 100644 --- a/packages/glob/package.json +++ b/packages/glob/package.json @@ -28,6 +28,7 @@ "directory": "packages/glob" }, "scripts": { + "audit-moderate": "npm install && npm audit --audit-level=moderate", "test": "echo \"Error: run tests from root\" && exit 1", "tsc": "tsc" }, diff --git a/packages/io/package-lock.json b/packages/io/package-lock.json index 36c16d5bff..4d44892f21 100644 --- a/packages/io/package-lock.json +++ b/packages/io/package-lock.json @@ -1,5 +1,5 @@ { "name": "@actions/io", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 1 } diff --git a/packages/io/package.json b/packages/io/package.json index 9530062b66..08dd3f84a2 100644 --- a/packages/io/package.json +++ b/packages/io/package.json @@ -27,6 +27,7 @@ "directory": "packages/io" }, "scripts": { + "audit-moderate": "npm install && npm audit --audit-level=moderate", "test": "echo \"Error: run tests from root\" && exit 1", "tsc": "tsc" }, diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index 8405522245..9f58cc484f 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -28,6 +28,7 @@ "directory": "packages/tool-cache" }, "scripts": { + "audit-moderate": "npm install && npm audit --audit-level=moderate", "test": "echo \"Error: run tests from root\" && exit 1", "tsc": "tsc" }, @@ -35,8 +36,8 @@ "url": "https://github.com/actions/toolkit/issues" }, "dependencies": { - "@actions/core": "^1.1.0", - "@actions/exec": "^1.0.1", + "@actions/core": "^1.2.0", + "@actions/exec": "^1.0.0", "@actions/io": "^1.0.1", "semver": "^6.1.0", "typed-rest-client": "^1.4.0", From bfd29dcef82f324e9fb8855b6083fc0c2902bc27 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Sat, 4 Jan 2020 14:08:05 -0500 Subject: [PATCH 068/192] only audit on ubuntu-latest (#283) --- .github/workflows/unit-tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 2f192b633f..e3f68737ec 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -50,6 +50,8 @@ jobs: - name: audit tools run: npm audit --audit-level=moderate + if: matrix.runs-on == 'ubuntu-latest' - name: audit packages run: npm run audit-all + if: matrix.runs-on == 'ubuntu-latest' From 683245ad5e28d1f0e9b897353caa4074cfff8c55 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Tue, 7 Jan 2020 01:14:32 -0500 Subject: [PATCH 069/192] bump default test timeout --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5cfb35e1d7..73f653240b 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "format-check": "prettier --check packages/**/*.ts", "lint": "eslint packages/**/*.ts", "new-package": "scripts/create-package", - "test": "jest" + "test": "jest --testTimeout 10000" }, "devDependencies": { "@types/jest": "^24.0.11", From 1a2c5929039617047c4b37b17b1c4cd6de0b8333 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Thu, 9 Jan 2020 15:05:31 -0500 Subject: [PATCH 070/192] multiple glob patterns (#287) --- ...{glob.test.ts => internal-globber.test.ts} | 270 ++++++++++++++---- .../__tests__/internal-pattern-helper.test.ts | 124 +++----- packages/glob/src/glob.ts | 188 +----------- .../glob/src/internal-glob-options-helper.ts | 32 +++ packages/glob/src/internal-glob-options.ts | 9 +- packages/glob/src/internal-globber.ts | 242 ++++++++++++++++ packages/glob/src/internal-pattern-helper.ts | 62 ---- 7 files changed, 547 insertions(+), 380 deletions(-) rename packages/glob/__tests__/{glob.test.ts => internal-globber.test.ts} (69%) create mode 100644 packages/glob/src/internal-glob-options-helper.ts create mode 100644 packages/glob/src/internal-globber.ts diff --git a/packages/glob/__tests__/glob.test.ts b/packages/glob/__tests__/internal-globber.test.ts similarity index 69% rename from packages/glob/__tests__/glob.test.ts rename to packages/glob/__tests__/internal-globber.test.ts index 810117ae7f..feba443344 100644 --- a/packages/glob/__tests__/glob.test.ts +++ b/packages/glob/__tests__/internal-globber.test.ts @@ -1,36 +1,143 @@ import * as child from 'child_process' -import * as glob from '../src/glob' import * as io from '../../io/src/io' +import * as os from 'os' import * as path from 'path' +import {Globber, DefaultGlobber} from '../src/internal-globber' +import {GlobOptions} from '../src/internal-glob-options' import {promises as fs} from 'fs' const IS_WINDOWS = process.platform === 'win32' /** - * These test focus on the ability of glob to find files + * These test focus on the ability of globber to find files * and not on the pattern matching aspect */ -describe('glob', () => { +describe('globber', () => { beforeAll(async () => { await io.rmRF(getTestTemp()) }) - it('detects cycle', async () => { + it('captures cwd', async () => { + // Create the following layout: + // first-cwd + // first-cwd/the-correct-file + // second-cwd + // second-cwd/the-wrong-file + const root = path.join(getTestTemp(), 'preserves-cwd') + await fs.mkdir(path.join(root, 'first-cwd'), {recursive: true}) + await fs.writeFile( + path.join(root, 'first-cwd', 'the-correct-file.txt'), + 'test file content' + ) + await fs.mkdir(path.join(root, 'second-cwd'), {recursive: true}) + await fs.writeFile( + path.join(root, 'second-cwd', 'the-wrong-file.txt'), + 'test file content' + ) + + const originalCwd = process.cwd() + try { + process.chdir(path.join(root, 'first-cwd')) + const globber = await DefaultGlobber.create('*') + process.chdir(path.join(root, 'second-cwd')) + expect(globber.getSearchPaths()).toEqual([path.join(root, 'first-cwd')]) + const itemPaths = await globber.glob() + expect(itemPaths).toEqual([ + path.join(root, 'first-cwd', 'the-correct-file.txt') + ]) + } finally { + process.chdir(originalCwd) + } + }) + + it('defaults to followSymbolicLinks=true', async () => { + // Create the following layout: + // + // /folder-a + // /folder-a/file + // /symDir -> /folder-a + const root = path.join( + getTestTemp(), + 'defaults-to-follow-symbolic-links-true' + ) + await fs.mkdir(path.join(root, 'folder-a'), {recursive: true}) + await fs.writeFile(path.join(root, 'folder-a', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'folder-a'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob(root, {}) + expect(itemPaths).toEqual([ + root, + path.join(root, 'folder-a'), + path.join(root, 'folder-a', 'file'), + path.join(root, 'symDir'), + path.join(root, 'symDir', 'file') + ]) + }) + + it('defaults to implicitDescendants=true', async () => { + // Create the following layout: + // + // /folder-a + // /folder-a/file + const root = path.join( + getTestTemp(), + 'defaults-to-implicit-descendants-true' + ) + await fs.mkdir(path.join(root, 'folder-a'), {recursive: true}) + await fs.writeFile(path.join(root, 'folder-a', 'file'), 'test file content') + + const itemPaths = await glob(root, {}) + expect(itemPaths).toEqual([ + root, + path.join(root, 'folder-a'), + path.join(root, 'folder-a', 'file') + ]) + }) + + it('defaults to omitBrokenSymbolicLinks=true', async () => { + // Create the following layout: + // + // /folder-a + // /folder-a/file + // /symDir -> /no-such + const root = path.join( + getTestTemp(), + 'defaults-to-omit-broken-symbolic-links-true' + ) + await fs.mkdir(path.join(root, 'folder-a'), {recursive: true}) + await fs.writeFile(path.join(root, 'folder-a', 'file'), 'test file content') + await createSymlinkDir( + path.join(root, 'no-such'), + path.join(root, 'symDir') + ) + + const itemPaths = await glob(root, {}) + expect(itemPaths).toEqual([ + root, + path.join(root, 'folder-a'), + path.join(root, 'folder-a', 'file') + ]) + }) + + it('detects cycle when followSymbolicLinks=true', async () => { // Create the following layout: // // /file // /symDir -> - const root = path.join(getTestTemp(), 'detects-cycle') + const root = path.join(getTestTemp(), 'detects-cycle-when-follow-true') await fs.mkdir(root, {recursive: true}) await fs.writeFile(path.join(root, 'file'), 'test file content') await createSymlinkDir(root, path.join(root, 'symDir')) - const itemPaths = await glob.glob(root) + const itemPaths = await glob(root, {followSymbolicLinks: true}) expect(itemPaths).toEqual([root, path.join(root, 'file')]) // todo: ? expect(itemPaths[2]).toBe(path.join(root, 'symDir')) }) - it('detects deep cycle starting from middle', async () => { + it('detects deep cycle starting from middle when followSymbolicLinks=true', async () => { // Create the following layout: // // /file-under-root @@ -43,7 +150,7 @@ describe('glob', () => { // /folder-a/folder-b/folder-c/sym-folder -> const root = path.join( getTestTemp(), - 'detects-deep-cycle-starting-from-middle' + 'detects-deep-cycle-starting-from-middle-when-follow-true' ) await fs.mkdir(path.join(root, 'folder-a', 'folder-b', 'folder-c'), { recursive: true @@ -79,7 +186,9 @@ describe('glob', () => { ) ) - const itemPaths = await glob.glob(path.join(root, 'folder-a', 'folder-b')) + const itemPaths = await glob(path.join(root, 'folder-a', 'folder-b'), { + followSymbolicLinks: true + }) expect(itemPaths).toEqual([ path.join(root, 'folder-a', 'folder-b'), path.join(root, 'folder-a', 'folder-b', 'file-under-b'), @@ -114,21 +223,23 @@ describe('glob', () => { ]) }) - it('detects cycle starting from symlink', async () => { + it('detects cycle starting from symlink when followSymbolicLinks=true', async () => { // Create the following layout: // // /file // /symDir -> const root: string = path.join( getTestTemp(), - 'detects-cycle-starting-from-symlink' + 'detects-cycle-starting-from-symlink-when-follow-true' ) await fs.mkdir(root, {recursive: true}) await fs.writeFile(path.join(root, 'file'), 'test file content') await createSymlinkDir(root, path.join(root, 'symDir')) await fs.stat(path.join(root, 'symDir')) - const itemPaths = await glob.glob(path.join(root, 'symDir')) + const itemPaths = await glob(path.join(root, 'symDir'), { + followSymbolicLinks: true + }) expect(itemPaths).toEqual([ path.join(root, 'symDir'), path.join(root, 'symDir', 'file') @@ -136,7 +247,7 @@ describe('glob', () => { // todo: ? expect(itemPaths[2]).toBe(path.join(root, 'symDir', 'symDir')); }) - it('does not follow symlink when followSymbolicLink=false', async () => { + it('does not follow symlink when followSymbolicLinks=false', async () => { // Create the following layout: // // /realDir @@ -153,7 +264,7 @@ describe('glob', () => { path.join(root, 'symDir') ) - const itemPaths = await glob.glob(root, {followSymbolicLinks: false}) + const itemPaths = await glob(root, {followSymbolicLinks: false}) expect(itemPaths).toEqual([ root, path.join(root, 'realDir'), @@ -162,7 +273,7 @@ describe('glob', () => { ]) }) - it('does not follow symlink when search path is symlink and followSymbolicLink=false', async () => { + it('does not follow symlink when search path is symlink and followSymbolicLinks=false', async () => { // Create the following layout: // realDir // realDir/file @@ -178,20 +289,23 @@ describe('glob', () => { path.join(root, 'symDir') ) - const itemPaths = await glob.glob(path.join(root, 'symDir'), { + const itemPaths = await glob(path.join(root, 'symDir'), { followSymbolicLinks: false }) expect(itemPaths).toEqual([path.join(root, 'symDir')]) }) - it('does not return broken symlink', async () => { + it('does not return broken symlink when follow-true and omit-true', async () => { // Create the following layout: // // /brokenSym -> /noSuch // /realDir // /realDir/file // /symDir -> /realDir - const root = path.join(getTestTemp(), 'does-not-return-broken-symlink') + const root = path.join( + getTestTemp(), + 'does-not-return-broken-symlink-when-follow-true-and-omit-true' + ) await fs.mkdir(root, {recursive: true}) await createSymlinkDir( path.join(root, 'noSuch'), @@ -204,7 +318,7 @@ describe('glob', () => { path.join(root, 'symDir') ) - const itemPaths = await glob.glob(root) + const itemPaths = await glob(root, {followSymbolicLinks: true}) expect(itemPaths).toEqual([ root, path.join(root, 'realDir'), @@ -214,20 +328,20 @@ describe('glob', () => { ]) }) - it('does not return broken symlink when search path is broken symlink', async () => { + it('does not return broken symlink when search path is broken symlink and followSymbolicLinks=true', async () => { // Create the following layout: // // /brokenSym -> /noSuch const root = path.join( getTestTemp(), - 'does-not-return-broken-symlink-when-search-path-is-broken-symlink' + 'does-not-return-broken-symlink-when-search-path-is-broken-symlink-and-follow-true' ) await fs.mkdir(root, {recursive: true}) const brokenSymPath = path.join(root, 'brokenSym') await createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath) await fs.lstat(brokenSymPath) - const itemPaths = await glob.glob(brokenSymPath) + const itemPaths = await glob(brokenSymPath, {followSymbolicLinks: true}) expect(itemPaths).toEqual([]) }) @@ -255,29 +369,29 @@ describe('glob', () => { path.join(root, 'realDir2', 'nested2', 'symDir') ) - const options: glob.IGlobOptions = { + const options: GlobOptions = { followSymbolicLinks: true, omitBrokenSymbolicLinks: false } // Should throw try { - await glob.glob(`${root}/*Dir*/*nested*/*`, options) + await glob(`${root}/*Dir*/*nested*/*`, options) throw new Error('should not reach here') } catch (err) { expect(err.message).toMatch(/broken symbolic link/i) } // Not partial match - let itemPaths = await glob.glob(`${root}/*Dir/*nested*/*`, options) + let itemPaths = await glob(`${root}/*Dir/*nested*/*`, options) expect(itemPaths).toEqual([path.join(root, 'realDir', 'nested', 'file')]) // Not partial match - itemPaths = await glob.glob(`${root}/*Dir*/*nested/*`, options) + itemPaths = await glob(`${root}/*Dir*/*nested/*`, options) expect(itemPaths).toEqual([path.join(root, 'realDir', 'nested', 'file')]) }) - it('does not throw for broken symlinks that are not matches or partial matches', async () => { + it('does not throw for broken symlinks that are not matches or partial matches when followSymbolicLinks=true and omitBrokenSymbolicLinks=false', async () => { // Create the following layout: // // /realDir @@ -285,20 +399,20 @@ describe('glob', () => { // /symDir -> /noSuch const root = path.join( getTestTemp(), - 'does-not-throw-for-broken-symlinks-that-are-not-matches-or-partial-matches' + 'does-not-throw-for-broken-symlinks-that-are-not-matches-or-partial-matches-when-follow-true-and-omit-false' ) await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') await createSymlinkDir(path.join(root, 'noSuch'), path.join(root, 'symDir')) - const options: glob.IGlobOptions = { + const options: GlobOptions = { followSymbolicLinks: true, omitBrokenSymbolicLinks: false } // Match should throw try { - await glob.glob(`${root}/*`, options) + await glob(`${root}/*`, options) throw new Error('should not reach here') } catch (err) { expect(err.message).toMatch(/broken symbolic link/i) @@ -306,18 +420,18 @@ describe('glob', () => { // Partial match should throw try { - await glob.glob(`${root}/*/*`, options) + await glob(`${root}/*/*`, options) throw new Error('should not reach here') } catch (err) { expect(err.message).toMatch(/broken symbolic link/i) } // Not match or partial match - const itemPaths = await glob.glob(`${root}/*eal*/*`, options) + const itemPaths = await glob(`${root}/*eal*/*`, options) expect(itemPaths).toEqual([path.join(root, 'realDir', 'file')]) }) - it('follows symlink', async () => { + it('follows symlink when follow-symbolic-links=true', async () => { // Create the following layout: // // /realDir @@ -331,7 +445,7 @@ describe('glob', () => { path.join(root, 'symDir') ) - const itemPaths = await glob.glob(root) + const itemPaths = await glob(root, {followSymbolicLinks: true}) expect(itemPaths).toEqual([ root, path.join(root, 'realDir'), @@ -341,14 +455,14 @@ describe('glob', () => { ]) }) - it('follows symlink when search path is symlink', async () => { + it('follows symlink when search path is symlink and follow-symbolic-links=true', async () => { // Create the following layout: // realDir // realDir/file // symDir -> realDir const root = path.join( getTestTemp(), - 'follows-symlink-when-search-path-is-symlink' + 'follows-symlink-when-search-path-is-symlink-and-follow-true' ) await fs.mkdir(path.join(root, 'realDir'), {recursive: true}) await fs.writeFile(path.join(root, 'realDir', 'file'), 'test file content') @@ -357,7 +471,9 @@ describe('glob', () => { path.join(root, 'symDir') ) - const itemPaths = await glob.glob(path.join(root, 'symDir')) + const itemPaths = await glob(path.join(root, 'symDir'), { + followSymbolicLinks: true + }) expect(itemPaths).toEqual([ path.join(root, 'symDir'), path.join(root, 'symDir', 'file') @@ -387,7 +503,7 @@ describe('glob', () => { path.join(root, 'symDir') ) - const itemPaths = await glob.glob(root, {followSymbolicLinks: false}) + const itemPaths = await glob(root, {followSymbolicLinks: false}) expect(itemPaths).toEqual([ root, path.join(root, 'brokenSym'), @@ -409,9 +525,7 @@ describe('glob', () => { const brokenSymPath = path.join(root, 'brokenSym') await createSymlinkDir(path.join(root, 'noSuch'), brokenSymPath) - const itemPaths = await glob.glob(brokenSymPath, { - followSymbolicLinks: false - }) + const itemPaths = await glob(brokenSymPath, {followSymbolicLinks: false}) expect(itemPaths).toEqual([brokenSymPath]) }) @@ -441,7 +555,7 @@ describe('glob', () => { ) await fs.writeFile(path.join(root, 'c-file'), 'test c-file content') - const itemPaths = await glob.glob(root) + const itemPaths = await glob(root) expect(itemPaths).toEqual([ root, path.join(root, 'a-file'), @@ -470,11 +584,11 @@ describe('glob', () => { // When pattern ends with `/**/` let pattern = `${root}${path.sep}**${path.sep}` expect( - await glob.glob(pattern, { + await glob(pattern, { implicitDescendants: false }) ).toHaveLength(3) // sanity check - expect(await glob.glob(pattern)).toEqual([ + expect(await glob(pattern)).toEqual([ root, path.join(root, 'dir-1'), path.join(root, 'dir-1', 'dir-2'), @@ -486,11 +600,11 @@ describe('glob', () => { // When pattern ends with something other than `/**/` pattern = `${root}${path.sep}**${path.sep}dir-?` expect( - await glob.glob(pattern, { + await glob(pattern, { implicitDescendants: false }) ).toHaveLength(2) // sanity check - expect(await glob.glob(pattern)).toEqual([ + expect(await glob(pattern)).toEqual([ path.join(root, 'dir-1'), path.join(root, 'dir-1', 'dir-2'), path.join(root, 'dir-1', 'dir-2', 'file-3'), @@ -515,9 +629,9 @@ describe('glob', () => { await fs.writeFile(path.join(root, 'dir-1', 'dir-2', 'file-3'), '') const pattern = `${root}${path.sep}**${path.sep}` - expect(await glob.glob(pattern)).toHaveLength(6) // sanity check + expect(await glob(pattern)).toHaveLength(6) // sanity check expect( - await glob.glob(pattern, { + await glob(pattern, { implicitDescendants: false }) ).toEqual([ @@ -528,7 +642,7 @@ describe('glob', () => { }) it('returns empty when search path does not exist', async () => { - const itemPaths = await glob.glob(path.join(getTestTemp(), 'nosuch')) + const itemPaths = await glob(path.join(getTestTemp(), 'nosuch')) expect(itemPaths).toEqual([]) }) @@ -548,7 +662,7 @@ describe('glob', () => { 'test .folder/file content' ) - const itemPaths = await glob.glob(root) + const itemPaths = await glob(root) expect(itemPaths).toEqual([ root, path.join(root, '.emptyFolder'), @@ -565,7 +679,7 @@ describe('glob', () => { await fs.mkdir(path.join(root, 'hello'), {recursive: true}) await fs.writeFile(path.join(root, 'hello', 'world.txt'), '') - const itemPaths = await glob.glob( + const itemPaths = await glob( `${root}${path.sep}${path.sep}${path.sep}hello` ) expect(itemPaths).toEqual([ @@ -574,13 +688,36 @@ describe('glob', () => { ]) }) - it('throws when match broken symlink and omitBrokenSymbolicLinks=false', async () => { + it('skips comments', async () => { + const searchPaths = await getSearchPaths( + `#aaa/*${os.EOL}/foo/*${os.EOL}#bbb/*${os.EOL}/bar/*` + ) + const drive = IS_WINDOWS ? process.cwd().substr(0, 2) : '' + expect(searchPaths).toEqual([ + IS_WINDOWS ? `${drive}\\foo` : '/foo', + IS_WINDOWS ? `${drive}\\bar` : '/bar' + ]) + }) + + it('skips empty lines', async () => { + const searchPaths = await getSearchPaths( + `${os.EOL}${os.EOL}/foo/*${os.EOL}${os.EOL}/bar/*${os.EOL}/baz/**${os.EOL}` + ) + const drive = IS_WINDOWS ? process.cwd().substr(0, 2) : '' + expect(searchPaths).toEqual([ + IS_WINDOWS ? `${drive}\\foo` : '/foo', + IS_WINDOWS ? `${drive}\\bar` : '/bar', + IS_WINDOWS ? `${drive}\\baz` : '/baz' + ]) + }) + + it('throws when match broken symlink and followSymbolicLinks=true and omitBrokenSymbolicLinks=false', async () => { // Create the following layout: // // /brokenSym -> /noSuch const root = path.join( getTestTemp(), - 'throws-when-match-broken-symlink-and-omit-false' + 'throws-when-match-broken-symlink-and-follow-true-and-omit-false' ) await fs.mkdir(root, {recursive: true}) await createSymlinkDir( @@ -589,20 +726,23 @@ describe('glob', () => { ) try { - await glob.glob(root, {omitBrokenSymbolicLinks: false}) + await glob(root, { + followSymbolicLinks: true, + omitBrokenSymbolicLinks: false + }) throw new Error('Expected tl.find to throw') } catch (err) { expect(err.message).toMatch(/broken symbolic link/) } }) - it('throws when search path is broken symlink and omitBrokenSymbolicLinks=false', async () => { + it('throws when search path is broken symlink and followSymbolicLinks=true and omitBrokenSymbolicLinks=false', async () => { // Create the following layout: // // /brokenSym -> /noSuch const root = path.join( getTestTemp(), - 'throws-when-search-path-is-broken-symlink-and-omit-false' + 'throws-when-search-path-is-broken-symlink-and-follow-true-and-omit-false' ) await fs.mkdir(root, {recursive: true}) const brokenSymPath = path.join(root, 'brokenSym') @@ -610,7 +750,10 @@ describe('glob', () => { await fs.lstat(brokenSymPath) try { - await glob.glob(brokenSymPath, {omitBrokenSymbolicLinks: false}) + await glob(brokenSymPath, { + followSymbolicLinks: true, + omitBrokenSymbolicLinks: false + }) throw new Error('Expected tl.find to throw') } catch (err) { expect(err.message).toMatch(/broken symbolic link/) @@ -669,3 +812,16 @@ async function createSymlinkDir(real: string, link: string): Promise { await fs.symlink(real, link) } } + +async function getSearchPaths(patterns: string): Promise { + const globber: Globber = await DefaultGlobber.create(patterns) + return globber.getSearchPaths() +} + +async function glob( + patterns: string, + options?: GlobOptions +): Promise { + const globber: Globber = await DefaultGlobber.create(patterns, options) + return await globber.glob() +} diff --git a/packages/glob/__tests__/internal-pattern-helper.test.ts b/packages/glob/__tests__/internal-pattern-helper.test.ts index dc8b0de927..1d60f6b3b4 100644 --- a/packages/glob/__tests__/internal-pattern-helper.test.ts +++ b/packages/glob/__tests__/internal-pattern-helper.test.ts @@ -2,18 +2,16 @@ import * as path from 'path' import * as patternHelper from '../src/internal-pattern-helper' import {MatchKind} from '../src/internal-match-kind' import {IS_WINDOWS} from '../../io/src/io-util' +import {Pattern} from '../src/internal-pattern' describe('pattern-helper', () => { it('getSearchPaths omits negate search paths', () => { const root = IS_WINDOWS ? 'C:\\' : '/' - const patterns = patternHelper.parse( - [ - `${root}search1/foo/**`, - `${root}search2/bar/**`, - `!${root}search3/baz/**` - ], - patternHelper.getOptions() - ) + const patterns = [ + `${root}search1/foo/**`, + `${root}search2/bar/**`, + `!${root}search3/baz/**` + ].map(x => new Pattern(x)) const searchPaths = patternHelper.getSearchPaths(patterns) expect(searchPaths).toEqual([ `${root}search1${path.sep}foo`, @@ -23,17 +21,14 @@ describe('pattern-helper', () => { it('getSearchPaths omits search path when ancestor is also a search path', () => { if (IS_WINDOWS) { - const patterns = patternHelper.parse( - [ - 'C:\\Search1\\Foo\\**', - 'C:\\sEARCH1\\fOO\\bar\\**', - 'C:\\sEARCH1\\foo\\bar', - 'C:\\Search2\\**', - 'C:\\Search3\\Foo\\Bar\\**', - 'C:\\sEARCH3\\fOO\\bAR\\**' - ], - patternHelper.getOptions() - ) + const patterns = [ + 'C:\\Search1\\Foo\\**', + 'C:\\sEARCH1\\fOO\\bar\\**', + 'C:\\sEARCH1\\foo\\bar', + 'C:\\Search2\\**', + 'C:\\Search3\\Foo\\Bar\\**', + 'C:\\sEARCH3\\fOO\\bAR\\**' + ].map(x => new Pattern(x)) const searchPaths = patternHelper.getSearchPaths(patterns) expect(searchPaths).toEqual([ 'C:\\Search1\\Foo', @@ -41,17 +36,15 @@ describe('pattern-helper', () => { 'C:\\Search3\\Foo\\Bar' ]) } else { - const patterns = patternHelper.parse( - [ - '/search1/foo/**', - '/search1/foo/bar/**', - '/search2/foo/bar', - '/search2/**', - '/search3/foo/bar/**', - '/search3/foo/bar/**' - ], - patternHelper.getOptions() - ) + const patterns = [ + '/search1/foo/**', + '/search1/foo/bar/**', + '/search2/foo/bar', + '/search2/**', + '/search3/foo/bar/**', + '/search3/foo/bar/**' + ].map(x => new Pattern(x)) + const searchPaths = patternHelper.getSearchPaths(patterns) expect(searchPaths).toEqual([ '/search1/foo', @@ -75,16 +68,13 @@ describe('pattern-helper', () => { `${root}solution2/proj2/README.txt`, `${root}solution2/solution2.sln` ] - const patterns = patternHelper.parse( - [ - `${root}**/*.proj`, // include all proj files - `${root}**/README.txt`, // include all README files - `!${root}**/solution2/**`, // exclude the solution 2 folder entirely - `${root}**/*.sln`, // include all sln files - `!${root}**/proj2/README.txt` // exclude proj2 README files - ], - patternHelper.getOptions({implicitDescendants: false}) - ) + const patterns = [ + `${root}**/*.proj`, // include all proj files + `${root}**/README.txt`, // include all README files + `!${root}**/solution2/**`, // exclude the solution 2 folder entirely + `${root}**/*.sln`, // include all sln files + `!${root}**/proj2/README.txt` // exclude proj2 README files + ].map(x => new Pattern(x)) const matched = itemPaths.filter( x => patternHelper.match(patterns, x) === MatchKind.All ) @@ -105,13 +95,10 @@ describe('pattern-helper', () => { `${root}foo/bar`, `${root}foo/bar/baz` ] - const patterns = patternHelper.parse( - [ - `${root}foo/**`, // include all files and directories - `!${root}foo/**/` // exclude directories - ], - patternHelper.getOptions({implicitDescendants: false}) - ) + const patterns = [ + `${root}foo/**`, // include all files and directories + `!${root}foo/**/` // exclude directories + ].map(x => new Pattern(x)) const matchKinds = itemPaths.map(x => patternHelper.match(patterns, x)) expect(matchKinds).toEqual([ MatchKind.None, @@ -129,12 +116,9 @@ describe('pattern-helper', () => { `${root}foo/bar`, `${root}foo/bar/baz` ] - const patterns = patternHelper.parse( - [ - `${root}foo/**/` // include directories only - ], - patternHelper.getOptions({implicitDescendants: false}) - ) + const patterns = [ + `${root}foo/**/` // include directories only + ].map(x => new Pattern(x)) const matchKinds = itemPaths.map(x => patternHelper.match(patterns, x)) expect(matchKinds).toEqual([ MatchKind.None, @@ -144,36 +128,14 @@ describe('pattern-helper', () => { ]) }) - it('parse skips comments', () => { - const patterns = patternHelper.parse( - ['# comment 1', ' # comment 2', '!#hello-world.txt'], - patternHelper.getOptions({implicitDescendants: false}) - ) - expect(patterns).toHaveLength(1) - expect(patterns[0].negate).toBeTruthy() - expect(patterns[0].segments.reverse()[0]).toEqual('#hello-world.txt') - }) - - it('parse skips empty patterns', () => { - const patterns = patternHelper.parse( - ['', ' ', 'hello-world.txt'], - patternHelper.getOptions({implicitDescendants: false}) - ) - expect(patterns).toHaveLength(1) - expect(patterns[0].segments.reverse()[0]).toEqual('hello-world.txt') - }) - it('partialMatch skips negate patterns', () => { const root = IS_WINDOWS ? 'C:\\' : '/' - const patterns = patternHelper.parse( - [ - `${root}search1/foo/**`, - `${root}search2/bar/**`, - `!${root}search2/bar/**`, - `!${root}search3/baz/**` - ], - patternHelper.getOptions({implicitDescendants: false}) - ) + const patterns = [ + `${root}search1/foo/**`, + `${root}search2/bar/**`, + `!${root}search2/bar/**`, + `!${root}search3/baz/**` + ].map(x => new Pattern(x)) expect(patternHelper.partialMatch(patterns, `${root}search1`)).toBeTruthy() expect( patternHelper.partialMatch(patterns, `${root}search1/foo`) diff --git a/packages/glob/src/glob.ts b/packages/glob/src/glob.ts index 861e1e8f6f..02fc9cc199 100644 --- a/packages/glob/src/glob.ts +++ b/packages/glob/src/glob.ts @@ -1,183 +1,17 @@ -import * as core from '@actions/core' -import * as fs from 'fs' -import * as path from 'path' -import * as patternHelper from './internal-pattern-helper' -import {IGlobOptions} from './internal-glob-options' -import {MatchKind} from './internal-match-kind' -import {Pattern} from './internal-pattern' -import {SearchState} from './internal-search-state' +import {Globber, DefaultGlobber} from './internal-globber' +import {GlobOptions} from './internal-glob-options' -export {IGlobOptions} +export {Globber, GlobOptions} /** - * Returns files and directories matching the specified glob pattern. + * Constructs a globber * - * Order of the results is not guaranteed. + * @param patterns Patterns separated by newlines + * @param options Glob options */ -export async function glob( - pattern: string, - options?: IGlobOptions -): Promise { - const result: string[] = [] - for await (const itemPath of globGenerator(pattern, options)) { - result.push(itemPath) - } - return result -} - -/** - * Returns files and directories matching the specified glob pattern. - * - * Order of the results is not guaranteed. - */ -export async function* globGenerator( - pattern: string, - options?: IGlobOptions -): AsyncGenerator { - // Set defaults options - options = patternHelper.getOptions(options) - - // Parse patterns - const patterns: Pattern[] = patternHelper.parse([pattern], options) - - // Push the search paths - const stack: SearchState[] = [] - for (const searchPath of patternHelper.getSearchPaths(patterns)) { - core.debug(`Search path '${searchPath}'`) - - // Exists? - try { - // Intentionally using lstat. Detection for broken symlink - // will be performed later (if following symlinks). - await fs.promises.lstat(searchPath) - } catch (err) { - if (err.code === 'ENOENT') { - continue - } - throw err - } - - stack.unshift(new SearchState(searchPath, 1)) - } - - // Search - const traversalChain: string[] = [] // used to detect cycles - while (stack.length) { - // Pop - const item = stack.pop() as SearchState - - // Match? - const match = patternHelper.match(patterns, item.path) - const partialMatch = - !!match || patternHelper.partialMatch(patterns, item.path) - if (!match && !partialMatch) { - continue - } - - // Stat - const stats: fs.Stats | undefined = await stat( - item, - options, - traversalChain - ) - - // Broken symlink, or symlink cycle detected, or no longer exists - if (!stats) { - continue - } - - // Directory - if (stats.isDirectory()) { - // Matched - if (match & MatchKind.Directory) { - yield item.path - } - // Descend? - else if (!partialMatch) { - continue - } - - // Push the child items in reverse - const childLevel = item.level + 1 - const childItems = (await fs.promises.readdir(item.path)).map( - x => new SearchState(path.join(item.path, x), childLevel) - ) - stack.push(...childItems.reverse()) - } - // File - else if (match & MatchKind.File) { - yield item.path - } - } -} - -/** - * Returns the search path preceeding the first segment that contains a pattern. - * - * For example, '/foo/bar*' returns '/foo'. - */ -export function getSearchPath(pattern: string): string { - const patterns: Pattern[] = patternHelper.parse( - [pattern], - patternHelper.getOptions() - ) - const searchPaths: string[] = patternHelper.getSearchPaths(patterns) - return searchPaths.length > 0 ? searchPaths[0] : '' -} - -async function stat( - item: SearchState, - options: IGlobOptions, - traversalChain: string[] -): Promise { - // Note: - // `stat` returns info about the target of a symlink (or symlink chain) - // `lstat` returns info about a symlink itself - let stats: fs.Stats - if (options.followSymbolicLinks) { - try { - // Use `stat` (following symlinks) - stats = await fs.promises.stat(item.path) - } catch (err) { - if (err.code === 'ENOENT') { - if (options.omitBrokenSymbolicLinks) { - core.debug(`Broken symlink '${item.path}'`) - return undefined - } - - throw new Error( - `No information found for the path '${item.path}'. This may indicate a broken symbolic link.` - ) - } - - throw err - } - } else { - // Use `lstat` (not following symlinks) - stats = await fs.promises.lstat(item.path) - } - - // Note, isDirectory() returns false for the lstat of a symlink - if (stats.isDirectory() && options.followSymbolicLinks) { - // Get the realpath - const realPath: string = await fs.promises.realpath(item.path) - - // Fixup the traversal chain to match the item level - while (traversalChain.length >= item.level) { - traversalChain.pop() - } - - // Test for a cycle - if (traversalChain.some((x: string) => x === realPath)) { - core.debug( - `Symlink cycle detected for path '${item.path}' and realpath '${realPath}'` - ) - return undefined - } - - // Update the traversal chain - traversalChain.push(realPath) - } - - return stats +export async function create( + patterns: string, + options?: GlobOptions +): Promise { + return await DefaultGlobber.create(patterns, options) } diff --git a/packages/glob/src/internal-glob-options-helper.ts b/packages/glob/src/internal-glob-options-helper.ts new file mode 100644 index 0000000000..3c81b67194 --- /dev/null +++ b/packages/glob/src/internal-glob-options-helper.ts @@ -0,0 +1,32 @@ +import * as core from '@actions/core' +import {GlobOptions} from './internal-glob-options' + +/** + * Returns a copy with defaults filled in. + */ +export function getOptions(copy?: GlobOptions): GlobOptions { + const result: GlobOptions = { + followSymbolicLinks: true, + implicitDescendants: true, + omitBrokenSymbolicLinks: true + } + + if (copy) { + if (typeof copy.followSymbolicLinks === 'boolean') { + result.followSymbolicLinks = copy.followSymbolicLinks + core.debug(`followSymbolicLinks '${result.followSymbolicLinks}'`) + } + + if (typeof copy.implicitDescendants === 'boolean') { + result.implicitDescendants = copy.implicitDescendants + core.debug(`implicitDescendants '${result.implicitDescendants}'`) + } + + if (typeof copy.omitBrokenSymbolicLinks === 'boolean') { + result.omitBrokenSymbolicLinks = copy.omitBrokenSymbolicLinks + core.debug(`omitBrokenSymbolicLinks '${result.omitBrokenSymbolicLinks}'`) + } + } + + return result +} diff --git a/packages/glob/src/internal-glob-options.ts b/packages/glob/src/internal-glob-options.ts index 8e427454a9..54d8b544c3 100644 --- a/packages/glob/src/internal-glob-options.ts +++ b/packages/glob/src/internal-glob-options.ts @@ -1,7 +1,10 @@ -export interface IGlobOptions { +/** + * Options to control globbing behavior + */ +export interface GlobOptions { /** - * Indicates whether to follow symbolic links. Generally should be true - * unless deleting files. + * Indicates whether to follow symbolic links. Generally should set to false + * when deleting files. * * @default true */ diff --git a/packages/glob/src/internal-globber.ts b/packages/glob/src/internal-globber.ts new file mode 100644 index 0000000000..a84d298d24 --- /dev/null +++ b/packages/glob/src/internal-globber.ts @@ -0,0 +1,242 @@ +import * as core from '@actions/core' +import * as fs from 'fs' +import * as globOptionsHelper from './internal-glob-options-helper' +import * as path from 'path' +import * as patternHelper from './internal-pattern-helper' +import {GlobOptions} from './internal-glob-options' +import {MatchKind} from './internal-match-kind' +import {Pattern} from './internal-pattern' +import {SearchState} from './internal-search-state' + +const IS_WINDOWS = process.platform === 'win32' + +export {GlobOptions} + +/** + * Used to match files and directories + */ +export interface Globber { + /** + * Returns the search path preceeding the first glob segment, from each pattern. + * Duplicates and descendants of other paths are filtered out. + * + * Example 1: The patterns `/foo/*` and `/bar/*` returns `/foo` and `/bar`. + * + * Example 2: The patterns `/foo/*` and `/foo/bar/*` returns `/foo`. + */ + getSearchPaths(): string[] + + /** + * Returns files and directories matching the glob patterns. + * + * Order of the results is not guaranteed. + */ + glob(): Promise + + /** + * Returns files and directories matching the glob patterns. + * + * Order of the results is not guaranteed. + */ + globGenerator(): AsyncGenerator +} + +export class DefaultGlobber implements Globber { + private readonly options: GlobOptions + private readonly patterns: Pattern[] = [] + private readonly searchPaths: string[] = [] + + private constructor(options?: GlobOptions) { + this.options = globOptionsHelper.getOptions(options) + } + + getSearchPaths(): string[] { + // Return a copy + return this.searchPaths.slice() + } + + async glob(): Promise { + const result: string[] = [] + for await (const itemPath of this.globGenerator()) { + result.push(itemPath) + } + return result + } + + async *globGenerator(): AsyncGenerator { + // Fill in defaults options + const options = globOptionsHelper.getOptions(this.options) + + // Implicit descendants? + const patterns: Pattern[] = [] + for (const pattern of this.patterns) { + patterns.push(pattern) + if ( + options.implicitDescendants && + (pattern.trailingSeparator || + pattern.segments[pattern.segments.length - 1] !== '**') + ) { + patterns.push( + new Pattern(pattern.negate, pattern.segments.concat('**')) + ) + } + } + + // Push the search paths + const stack: SearchState[] = [] + for (const searchPath of patternHelper.getSearchPaths(patterns)) { + core.debug(`Search path '${searchPath}'`) + + // Exists? + try { + // Intentionally using lstat. Detection for broken symlink + // will be performed later (if following symlinks). + await fs.promises.lstat(searchPath) + } catch (err) { + if (err.code === 'ENOENT') { + continue + } + throw err + } + + stack.unshift(new SearchState(searchPath, 1)) + } + + // Search + const traversalChain: string[] = [] // used to detect cycles + while (stack.length) { + // Pop + const item = stack.pop() as SearchState + + // Match? + const match = patternHelper.match(patterns, item.path) + const partialMatch = + !!match || patternHelper.partialMatch(patterns, item.path) + if (!match && !partialMatch) { + continue + } + + // Stat + const stats: fs.Stats | undefined = await DefaultGlobber.stat( + item, + options, + traversalChain + ) + + // Broken symlink, or symlink cycle detected, or no longer exists + if (!stats) { + continue + } + + // Directory + if (stats.isDirectory()) { + // Matched + if (match & MatchKind.Directory) { + yield item.path + } + // Descend? + else if (!partialMatch) { + continue + } + + // Push the child items in reverse + const childLevel = item.level + 1 + const childItems = (await fs.promises.readdir(item.path)).map( + x => new SearchState(path.join(item.path, x), childLevel) + ) + stack.push(...childItems.reverse()) + } + // File + else if (match & MatchKind.File) { + yield item.path + } + } + } + + /** + * Constructs a DefaultGlobber + */ + static async create( + patterns: string, + options?: GlobOptions + ): Promise { + const result = new DefaultGlobber(options) + + if (IS_WINDOWS) { + patterns = patterns.replace(/\r\n/g, '\n') + patterns = patterns.replace(/\r/g, '\n') + } + + const lines = patterns.split('\n').map(x => x.trim()) + for (const line of lines) { + // Empty or comment + if (!line || line.startsWith('#')) { + continue + } + // Pattern + else { + result.patterns.push(new Pattern(line)) + } + } + + result.searchPaths.push(...patternHelper.getSearchPaths(result.patterns)) + return result + } + + private static async stat( + item: SearchState, + options: GlobOptions, + traversalChain: string[] + ): Promise { + // Note: + // `stat` returns info about the target of a symlink (or symlink chain) + // `lstat` returns info about a symlink itself + let stats: fs.Stats + if (options.followSymbolicLinks) { + try { + // Use `stat` (following symlinks) + stats = await fs.promises.stat(item.path) + } catch (err) { + if (err.code === 'ENOENT') { + if (options.omitBrokenSymbolicLinks) { + core.debug(`Broken symlink '${item.path}'`) + return undefined + } + + throw new Error( + `No information found for the path '${item.path}'. This may indicate a broken symbolic link.` + ) + } + + throw err + } + } else { + // Use `lstat` (not following symlinks) + stats = await fs.promises.lstat(item.path) + } + + // Note, isDirectory() returns false for the lstat of a symlink + if (stats.isDirectory() && options.followSymbolicLinks) { + // Get the realpath + const realPath: string = await fs.promises.realpath(item.path) + + // Fixup the traversal chain to match the item level + while (traversalChain.length >= item.level) { + traversalChain.pop() + } + + // Test for a cycle + if (traversalChain.some((x: string) => x === realPath)) { + core.debug( + `Symlink cycle detected for path '${item.path}' and realpath '${realPath}'` + ) + return undefined + } + + // Update the traversal chain + traversalChain.push(realPath) + } + + return stats + } +} diff --git a/packages/glob/src/internal-pattern-helper.ts b/packages/glob/src/internal-pattern-helper.ts index 9ca56b146c..e0cf3a6a39 100644 --- a/packages/glob/src/internal-pattern-helper.ts +++ b/packages/glob/src/internal-pattern-helper.ts @@ -1,41 +1,9 @@ -import * as core from '@actions/core' import * as pathHelper from './internal-path-helper' -import {IGlobOptions} from './internal-glob-options' import {MatchKind} from './internal-match-kind' import {Pattern} from './internal-pattern' const IS_WINDOWS = process.platform === 'win32' -/** - * Returns a copy with defaults filled in - */ -export function getOptions(copy?: IGlobOptions): IGlobOptions { - const result: IGlobOptions = { - followSymbolicLinks: true, - implicitDescendants: true, - omitBrokenSymbolicLinks: true - } - - if (copy) { - if (typeof copy.followSymbolicLinks === 'boolean') { - result.followSymbolicLinks = copy.followSymbolicLinks - core.debug(`followSymbolicLinks '${result.followSymbolicLinks}'`) - } - - if (typeof copy.implicitDescendants === 'boolean') { - result.implicitDescendants = copy.implicitDescendants - core.debug(`implicitDescendants '${result.implicitDescendants}'`) - } - - if (typeof copy.omitBrokenSymbolicLinks === 'boolean') { - result.omitBrokenSymbolicLinks = copy.omitBrokenSymbolicLinks - core.debug(`omitBrokenSymbolicLinks '${result.omitBrokenSymbolicLinks}'`) - } - } - - return result -} - /** * Given an array of patterns, returns an array of paths to search. * Duplicates and paths under other included paths are filtered out. @@ -105,36 +73,6 @@ export function match(patterns: Pattern[], itemPath: string): MatchKind { return result } -/** - * Parses the pattern strings into Pattern objects - */ -export function parse(patterns: string[], options: IGlobOptions): Pattern[] { - const result: Pattern[] = [] - - for (const patternString of patterns.map(x => x.trim())) { - // Skip empty or comment - if (!patternString || patternString.startsWith('#')) { - continue - } - - // Push - const pattern = new Pattern(patternString) - result.push(pattern) - - // Implicit descendants? - if ( - options.implicitDescendants && - (pattern.trailingSeparator || - pattern.segments[pattern.segments.length - 1] !== '**') - ) { - // Push - result.push(new Pattern(pattern.negate, pattern.segments.concat('**'))) - } - } - - return result -} - /** * Checks whether to descend further into the directory */ From 03ebc5b8851571a1ce55409097a52b1a74019e52 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Thu, 9 Jan 2020 15:26:22 -0500 Subject: [PATCH 071/192] generated file (#294) --- packages/glob/package-lock.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/glob/package-lock.json b/packages/glob/package-lock.json index a1e9ecc7a6..3eb24c1b1a 100644 --- a/packages/glob/package-lock.json +++ b/packages/glob/package-lock.json @@ -4,6 +4,20 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", From 947ba5b559e0924b6f153513c8001124239b6fce Mon Sep 17 00:00:00 2001 From: eric sciple Date: Thu, 9 Jan 2020 17:31:27 -0500 Subject: [PATCH 072/192] bump versions (#295) --- packages/core/package.json | 2 +- packages/exec/package.json | 2 +- packages/github/package.json | 2 +- packages/io/package.json | 2 +- packages/tool-cache/package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index bdd061485f..6c4e9932f2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@actions/core", - "version": "1.2.0", + "version": "1.2.1", "description": "Actions core lib", "keywords": [ "github", diff --git a/packages/exec/package.json b/packages/exec/package.json index 0797a8095f..1b8078fdae 100644 --- a/packages/exec/package.json +++ b/packages/exec/package.json @@ -1,6 +1,6 @@ { "name": "@actions/exec", - "version": "1.0.2", + "version": "1.0.3", "description": "Actions exec lib", "keywords": [ "github", diff --git a/packages/github/package.json b/packages/github/package.json index b956d42363..d3942b731e 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "2.0.0", + "version": "2.0.1", "description": "Actions github lib", "keywords": [ "github", diff --git a/packages/io/package.json b/packages/io/package.json index 08dd3f84a2..0fd128ef5e 100644 --- a/packages/io/package.json +++ b/packages/io/package.json @@ -1,6 +1,6 @@ { "name": "@actions/io", - "version": "1.0.1", + "version": "1.0.2", "description": "Actions io lib", "keywords": [ "github", diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index 9f58cc484f..3ed7edc8cf 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.1.2", + "version": "1.2.0", "description": "Actions tool-cache lib", "keywords": [ "github", From dd64d8c7c99582054943c08e3862f6163b226e9a Mon Sep 17 00:00:00 2001 From: eric sciple Date: Fri, 10 Jan 2020 12:00:22 -0500 Subject: [PATCH 073/192] glob readme (#296) --- README.md | 9 ++++ packages/glob/README.md | 113 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 packages/glob/README.md diff --git a/README.md b/README.md index 1d8b8b6407..a4ec4b70d4 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,15 @@ $ npm install @actions/exec --save ```
+:ice_cream: [@actions/glob](packages/glob) + +Provides functions to search for files matching glob patterns. Read more [here](packages/glob) + +```bash +$ npm install @actions/glob --save +``` +
+ :pencil2: [@actions/io](packages/io) Provides disk i/o functions like cp, mv, rmRF, find etc. Read more [here](packages/io) diff --git a/packages/glob/README.md b/packages/glob/README.md new file mode 100644 index 0000000000..9575beaa1d --- /dev/null +++ b/packages/glob/README.md @@ -0,0 +1,113 @@ +# `@actions/glob` + +## Usage + +### Basic + +You can use this package to search for files matching glob patterns. + +Relative paths and absolute paths are both allowed. Relative paths are rooted against the current working directory. + +```js +const glob = require('@actions/glob'); + +const patterns = ['**/tar.gz', '**/tar.bz'] +const globber = await glob.create(patterns.join('\n')) +const files = await globber.glob() +``` + +### Opt out of following symbolic links + +```js +const glob = require('@actions/glob'); + +const globber = await glob.create('**', {followSymbolicLinks: false}) +const files = await globber.glob() +``` + +### Iterator + +When dealing with a large amount of results, consider iterating the results as they are returned: + +```js +const glob = require('@actions/glob'); + +const globber = await glob.create('**') +for await (const file of globber.globGenerator()) { + console.log(file) +} +``` + +## Recommended action inputs + +Glob follows symbolic links by default. Following is often appropriate unless deleting files. + +Users may want to opt-out from following symbolic links for other reasons. For example, +excessive amounts of symbolic links can create the appearance of very, very many files +and slow the search. + +When an action allows a user to specify input patterns, it is generally recommended to +allow users to opt-out from following symbolic links. + +Snippet from `action.yml`: + +```yaml +inputs: + files: + description: 'Files to print' + required: true + follow-symbolic-links: + description: 'Indicates whether to follow symbolic links' + default: true +``` + +And corresponding toolkit consumption: + +```js +const core = require('@actions/core') +const glob = require('@actions/glob') + +const globOptions = { + followSymbolicLinks: core.getInput('follow-symbolic-links').toUpper() !== 'FALSE' +} +const globber = glob.create(core.getInput('files'), globOptions) +for await (const file of globber.globGenerator()) { + console.log(file) +} +``` + +## Patterns + +### Glob behavior + +Patterns `*`, `?`, `[...]`, `**` (globstar) are supported. + +With the following behaviors: +- File names that begin with `.` may be included in the results +- Case insensitive on Windows +- Directory separator `/` and `\` both supported on Windows + +### Tilde expansion + +Supports basic tilde expansion, for current user HOME replacement only. + +Example: +- `~` may expand to /Users/johndoe +- `~/foo` may expand to /Users/johndoe/foo + +### Comments + +Patterns that begin with `#` are treated as comments. + +### Exclude patterns + +Leading `!` changes the meaning of an include pattern to exclude. + +Multiple leading `!` flips the meaning. + +### Escaping + +Wrapping special characters in `[]` can be used to escape literal glob characters +in a file name. For example the literal file name `hello[a-z]` can be escaped as `hello[[]a-z]`. + +On Linux/macOS `\` is also treated as an escape character. From 058ad6937dd01fed3251bf482013fe73c9329b1a Mon Sep 17 00:00:00 2001 From: eric sciple Date: Fri, 10 Jan 2020 12:09:48 -0500 Subject: [PATCH 074/192] remove todo comment (#297) --- packages/glob/__tests__/internal-globber.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/glob/__tests__/internal-globber.test.ts b/packages/glob/__tests__/internal-globber.test.ts index feba443344..cb6f343fab 100644 --- a/packages/glob/__tests__/internal-globber.test.ts +++ b/packages/glob/__tests__/internal-globber.test.ts @@ -134,7 +134,6 @@ describe('globber', () => { const itemPaths = await glob(root, {followSymbolicLinks: true}) expect(itemPaths).toEqual([root, path.join(root, 'file')]) - // todo: ? expect(itemPaths[2]).toBe(path.join(root, 'symDir')) }) it('detects deep cycle starting from middle when followSymbolicLinks=true', async () => { @@ -244,7 +243,6 @@ describe('globber', () => { path.join(root, 'symDir'), path.join(root, 'symDir', 'file') ]) - // todo: ? expect(itemPaths[2]).toBe(path.join(root, 'symDir', 'symDir')); }) it('does not follow symlink when followSymbolicLinks=false', async () => { From 2e88402d192b9006a3a789aae20956c88945adba Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Fri, 10 Jan 2020 16:37:48 -0500 Subject: [PATCH 075/192] audit fix and update http-client (#298) --- package-lock.json | 12 ++++++-- package.json | 1 + packages/core/package-lock.json | 2 +- packages/exec/package-lock.json | 5 +-- packages/github/package-lock.json | 2 +- packages/glob/package-lock.json | 5 +++ packages/io/package-lock.json | 2 +- packages/tool-cache/RELEASES.md | 4 +++ packages/tool-cache/package-lock.json | 44 +++++++++++++++------------ packages/tool-cache/package.json | 4 +-- packages/tool-cache/src/tool-cache.ts | 2 +- 11 files changed, 52 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1eb3292436..188df73360 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5444,6 +5444,12 @@ "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", "dev": true }, + "flow-bin": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.115.0.tgz", + "integrity": "sha512-xW+U2SrBaAr0EeLvKmXAmsdnrH6x0Io17P6yRJTNgrrV42G8KXhBAD00s6oGbTTqRyHD0nP47kyuU34zljZpaQ==", + "dev": true + }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", @@ -13025,9 +13031,9 @@ } }, "tree-kill": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz", - "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true }, "trim-newlines": { diff --git a/package.json b/package.json index 73f653240b..d5cb14b248 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "eslint": "^5.16.0", "eslint-plugin-github": "^2.0.0", "eslint-plugin-jest": "^22.5.1", + "flow-bin": "^0.115.0", "jest": "^24.9.0", "jest-circus": "^24.7.1", "lerna": "^3.18.4", diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json index fc47b0cbdf..a25fa4ae2e 100644 --- a/packages/core/package-lock.json +++ b/packages/core/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/core", - "version": "1.2.0", + "version": "1.2.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/exec/package-lock.json b/packages/exec/package-lock.json index 250508c749..b8b91bcd70 100644 --- a/packages/exec/package-lock.json +++ b/packages/exec/package-lock.json @@ -1,12 +1,13 @@ { "name": "@actions/exec", - "version": "1.0.2", + "version": "1.0.3", "lockfileVersion": 1, "requires": true, "dependencies": { "@actions/io": { "version": "1.0.1", - "dev": true + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.1.tgz", + "integrity": "sha512-rhq+tfZukbtaus7xyUtwKfuiCRXd1hWSfmJNEpFgBQJ4woqPEpsBw04awicjwz9tyG2/MVhAEMfVn664Cri5zA==" } } } diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index 6dcc35ef9f..c2ac4ab059 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "2.0.0", + "version": "2.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/glob/package-lock.json b/packages/glob/package-lock.json index 3eb24c1b1a..043765ec54 100644 --- a/packages/glob/package-lock.json +++ b/packages/glob/package-lock.json @@ -4,6 +4,11 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@actions/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.1.tgz", + "integrity": "sha512-xD+CQd9p4lU7ZfRqmUcbJpqR+Ss51rJRVeXMyOLrZQImN9/8Sy/BEUBnHO/UKD3z03R686PVTLfEPmkropGuLw==" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", diff --git a/packages/io/package-lock.json b/packages/io/package-lock.json index 4d44892f21..c115e2de6f 100644 --- a/packages/io/package-lock.json +++ b/packages/io/package-lock.json @@ -1,5 +1,5 @@ { "name": "@actions/io", - "version": "1.0.1", + "version": "1.0.2", "lockfileVersion": 1 } diff --git a/packages/tool-cache/RELEASES.md b/packages/tool-cache/RELEASES.md index 907dad2c65..a921d85a22 100644 --- a/packages/tool-cache/RELEASES.md +++ b/packages/tool-cache/RELEASES.md @@ -1,5 +1,9 @@ # @actions/tool-cache Releases +### 1.3.0 + +- [Uses @actions/http-client](https://github.com/actions/http-client) + ### 1.1.2 - [Use zip and unzip from PATH](https://github.com/actions/toolkit/pull/161) diff --git a/packages/tool-cache/package-lock.json b/packages/tool-cache/package-lock.json index 3c3e386267..2ec8bf9079 100644 --- a/packages/tool-cache/package-lock.json +++ b/packages/tool-cache/package-lock.json @@ -1,9 +1,32 @@ { "name": "@actions/tool-cache", - "version": "1.1.2", + "version": "1.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { + "@actions/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.1.tgz", + "integrity": "sha512-xD+CQd9p4lU7ZfRqmUcbJpqR+Ss51rJRVeXMyOLrZQImN9/8Sy/BEUBnHO/UKD3z03R686PVTLfEPmkropGuLw==" + }, + "@actions/exec": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.3.tgz", + "integrity": "sha512-TogJGnueOmM7ntCi0ASTUj4LapRRtDfj57Ja4IhPmg2fls28uVOPbAn8N+JifaOumN2UG3oEO/Ixek2A4NcYSA==", + "requires": { + "@actions/io": "^1.0.1" + } + }, + "@actions/http-client": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.1.tgz", + "integrity": "sha512-vy5DhqTJ1gtEkpRrD/6BHhUlkeyccrOX0BT9KmtO5TWxe5KSSwVHFE+J15Z0dG+tJwZJ/nHC4slUIyqpkahoMg==" + }, + "@actions/io": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", + "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==" + }, "@types/nock": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/@types/nock/-/nock-10.0.3.tgz", @@ -171,31 +194,12 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, - "tunnel": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz", - "integrity": "sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM=" - }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, - "typed-rest-client": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.5.0.tgz", - "integrity": "sha512-DVZRlmsfnTjp6ZJaatcdyvvwYwbWvR4YDNFDqb+qdTxpvaVP99YCpBkA8rxsLtAPjBVoDe4fNsnMIdZTiPuKWg==", - "requires": { - "tunnel": "0.0.4", - "underscore": "1.8.3" - } - }, - "underscore": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", - "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" - }, "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index 3ed7edc8cf..990c5d85a3 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.2.0", + "version": "1.3.0", "description": "Actions tool-cache lib", "keywords": [ "github", @@ -38,9 +38,9 @@ "dependencies": { "@actions/core": "^1.2.0", "@actions/exec": "^1.0.0", + "@actions/http-client": "^1.0.1", "@actions/io": "^1.0.1", "semver": "^6.1.0", - "typed-rest-client": "^1.4.0", "uuid": "^3.3.2" }, "devDependencies": { diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index 78cd554137..b91cf1adb7 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -3,7 +3,7 @@ import * as io from '@actions/io' import * as fs from 'fs' import * as os from 'os' import * as path from 'path' -import * as httpm from 'typed-rest-client/HttpClient' +import * as httpm from '@actions/http-client' import * as semver from 'semver' import * as uuidV4 from 'uuid/v4' import {exec} from '@actions/exec/lib/exec' From c514e7481a85cfbf6e0f556815f3eb9d68e1b9a2 Mon Sep 17 00:00:00 2001 From: sullis Date: Tue, 14 Jan 2020 07:48:34 -0800 Subject: [PATCH 076/192] GitHub Actions checkout v2 (#303) --- .github/workflows/unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index e3f68737ec..d78ebfd853 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v1 + uses: actions/checkout@v2 - name: Set Node.js 12.x uses: actions/setup-node@master From 461fc2b9c921e2496927f06f2bb218e02e585fb7 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Tue, 14 Jan 2020 11:19:06 -0500 Subject: [PATCH 077/192] bump checkout to v2, pin setup-node to v1 (#277) --- .github/workflows/unit-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index d78ebfd853..bbed17d4d1 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -1,5 +1,5 @@ name: toolkit-unit-tests -on: +on: push: branches: - master @@ -7,7 +7,7 @@ on: - '**.md' pull_request: paths-ignore: - - '**.md' + - '**.md' jobs: @@ -26,7 +26,7 @@ jobs: uses: actions/checkout@v2 - name: Set Node.js 12.x - uses: actions/setup-node@master + uses: actions/setup-node@v1 with: node-version: 12.x From e69833ed16500afaa7d137a9cf6da76fb8fb54da Mon Sep 17 00:00:00 2001 From: eric sciple Date: Tue, 14 Jan 2020 11:58:44 -0500 Subject: [PATCH 078/192] release notes (#308) --- packages/core/RELEASES.md | 5 +++++ packages/exec/RELEASES.md | 8 ++++++++ packages/github/RELEASES.md | 4 ++++ packages/glob/RELEASES.md | 5 +++++ packages/io/RELEASES.md | 4 ++++ packages/tool-cache/RELEASES.md | 6 ++++++ 6 files changed, 32 insertions(+) create mode 100644 packages/glob/RELEASES.md diff --git a/packages/core/RELEASES.md b/packages/core/RELEASES.md index 30c8e2059a..174a9dbbbe 100644 --- a/packages/core/RELEASES.md +++ b/packages/core/RELEASES.md @@ -1,5 +1,10 @@ # @actions/core Releases +### 1.2.1 + +- [Remove trailing comma from commands](https://github.com/actions/toolkit/pull/263) +- [Add \"types\" to package.json](https://github.com/actions/toolkit/pull/221) + ### 1.2.0 - saveState and getState functions for wrapper tasks (on finally entry points that run post job) diff --git a/packages/exec/RELEASES.md b/packages/exec/RELEASES.md index 27d8d78147..0e99d15653 100644 --- a/packages/exec/RELEASES.md +++ b/packages/exec/RELEASES.md @@ -1,5 +1,13 @@ # @actions/exec Releases +### 1.0.3 + +- [Add \"types\" to package.json](https://github.com/actions/toolkit/pull/221) + +### 1.0.2 + +- [Which before invoking tool](https://github.com/actions/toolkit/pull/220) + ### 1.0.0 - Initial release \ No newline at end of file diff --git a/packages/github/RELEASES.md b/packages/github/RELEASES.md index ef90e1c7dc..a827093ae3 100644 --- a/packages/github/RELEASES.md +++ b/packages/github/RELEASES.md @@ -1,5 +1,9 @@ # @actions/github Releases +### 2.0.1 + +- [Add \"types\" to package.json](https://github.com/actions/toolkit/pull/221) + ### 2.0.0 - Upgrade Octokit version to 4.x to include typescript types [#228](https://github.com/actions/toolkit/pull/228) diff --git a/packages/glob/RELEASES.md b/packages/glob/RELEASES.md new file mode 100644 index 0000000000..20b43a8a9b --- /dev/null +++ b/packages/glob/RELEASES.md @@ -0,0 +1,5 @@ +# @actions/glob Releases + +### 0.1.0 + +- Initial release diff --git a/packages/io/RELEASES.md b/packages/io/RELEASES.md index 402b7c16bb..be3f1ad154 100644 --- a/packages/io/RELEASES.md +++ b/packages/io/RELEASES.md @@ -1,5 +1,9 @@ # @actions/io Releases +### 1.0.2 + +- [Add \"types\" to package.json](https://github.com/actions/toolkit/pull/221) + ### 1.0.0 - Initial release \ No newline at end of file diff --git a/packages/tool-cache/RELEASES.md b/packages/tool-cache/RELEASES.md index a921d85a22..a4dc6b8ba5 100644 --- a/packages/tool-cache/RELEASES.md +++ b/packages/tool-cache/RELEASES.md @@ -4,6 +4,12 @@ - [Uses @actions/http-client](https://github.com/actions/http-client) +### 1.2.0 + +- [Overload downloadTool to accept destination path](https://github.com/actions/toolkit/pull/257) +- [Fix `extractTar` on Windows](https://github.com/actions/toolkit/pull/264) +- [Add \"types\" to package.json](https://github.com/actions/toolkit/pull/221) + ### 1.1.2 - [Use zip and unzip from PATH](https://github.com/actions/toolkit/pull/161) From ab5bd9d69619662111840bd7c559cd60e24e3cdc Mon Sep 17 00:00:00 2001 From: eric sciple Date: Sat, 18 Jan 2020 14:28:37 -0500 Subject: [PATCH 079/192] octokit client should follow proxy settings (#314) --- .github/workflows/unit-tests.yml | 2 + packages/github/README.md | 4 +- packages/github/__tests__/github.test.ts | 173 ++++++++++++++++++ packages/github/__tests__/proxy.d.ts | 5 + packages/github/package-lock.json | 142 +++++++++++++- packages/github/package.json | 6 +- packages/github/src/github.ts | 119 +++++++++++- packages/github/tsconfig.json | 1 - .../glob/__tests__/internal-pattern.test.ts | 24 +-- packages/glob/package-lock.json | 5 - packages/glob/src/internal-path-helper.ts | 2 +- packages/glob/src/internal-path.ts | 2 +- packages/glob/src/internal-pattern.ts | 15 +- .../tool-cache/__tests__/tool-cache.test.ts | 2 +- packages/tool-cache/package-lock.json | 32 ++-- packages/tool-cache/package.json | 4 +- packages/tool-cache/src/tool-cache.ts | 2 +- tsconfig.json | 1 + 18 files changed, 473 insertions(+), 68 deletions(-) create mode 100644 packages/github/__tests__/github.test.ts create mode 100644 packages/github/__tests__/proxy.d.ts diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index bbed17d4d1..b558b4ebd1 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -41,6 +41,8 @@ jobs: - name: npm test run: npm test + env: + GITHUB_TOKEN: ${{ github.token }} - name: Lint run: npm run lint diff --git a/packages/github/README.md b/packages/github/README.md index 12c5b0b078..bd5f708b3f 100644 --- a/packages/github/README.md +++ b/packages/github/README.md @@ -4,7 +4,7 @@ ## Usage -Returns an Octokit client. See https://octokit.github.io/rest.js for the API. +Returns an authenticated Octokit client that follows the machine [proxy settings](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners#using-a-proxy-server-with-self-hosted-runners). See https://octokit.github.io/rest.js for the API. ```js const github = require('@actions/github'); @@ -34,7 +34,7 @@ async function run() { run(); ``` -You can pass client options (except `auth`, which is handled by the token argument), as specified by [Octokit](https://octokit.github.io/rest.js/), as a second argument to the `GitHub` constructor. +You can pass client options, as specified by [Octokit](https://octokit.github.io/rest.js/), as a second argument to the `GitHub` constructor. You can also make GraphQL requests. See https://github.com/octokit/graphql.js for the API. diff --git a/packages/github/__tests__/github.test.ts b/packages/github/__tests__/github.test.ts new file mode 100644 index 0000000000..0d53095627 --- /dev/null +++ b/packages/github/__tests__/github.test.ts @@ -0,0 +1,173 @@ +import * as http from 'http' +import proxy from 'proxy' +import {GitHub} from '../src/github' + +describe('@actions/github', () => { + const proxyUrl = 'http://127.0.0.1:8080' + const originalProxyUrl = process.env['https_proxy'] + let proxyConnects: string[] + let proxyServer: http.Server + let first = true + + beforeAll(async () => { + // Start proxy server + proxyServer = proxy() as http.Server + await new Promise(resolve => { + const port = Number(proxyUrl.split(':')[2]) + proxyServer.listen(port, () => resolve()) + }) + proxyServer.on('connect', req => { + proxyConnects.push(req.url) + }) + }) + + beforeEach(() => { + delete process.env['https_proxy'] + proxyConnects = [] + }) + + afterAll(async () => { + // Stop proxy server + await new Promise(resolve => { + proxyServer.once('close', () => resolve()) + proxyServer.close() + }) + + if (originalProxyUrl) { + process.env['https_proxy'] = originalProxyUrl + } + }) + + it('basic REST client', async () => { + const token = getToken() + if (!token) { + return + } + + const octokit = new GitHub(token) + const branch = await octokit.repos.getBranch({ + owner: 'actions', + repo: 'toolkit', + branch: 'master' + }) + expect(branch.data.name).toBe('master') + expect(proxyConnects).toHaveLength(0) + }) + + it('basic REST client with custom auth', async () => { + const token = getToken() + if (!token) { + return + } + + // Valid token + let octokit = new GitHub({auth: `token ${token}`}) + const branch = await octokit.repos.getBranch({ + owner: 'actions', + repo: 'toolkit', + branch: 'master' + }) + expect(branch.data.name).toBe('master') + expect(proxyConnects).toHaveLength(0) + + // Invalid token + octokit = new GitHub({auth: `token asdf`}) + let failed = false + try { + await octokit.repos.getBranch({ + owner: 'actions', + repo: 'toolkit', + branch: 'master' + }) + } catch (err) { + failed = true + } + expect(failed).toBeTruthy() + }) + + it('basic REST client with proxy', async () => { + const token = getToken() + if (!token) { + return + } + + process.env['https_proxy'] = proxyUrl + const octokit = new GitHub(token) + const branch = await octokit.repos.getBranch({ + owner: 'actions', + repo: 'toolkit', + branch: 'master' + }) + expect(branch.data.name).toBe('master') + expect(proxyConnects).toEqual(['api.github.com:443']) + }) + + it('basic GraphQL client', async () => { + const token = getToken() + if (!token) { + return + } + + const octokit = new GitHub(token) + const repository = await octokit.graphql( + '{repository(owner:"actions", name:"toolkit"){name}}' + ) + expect(repository).toEqual({repository: {name: 'toolkit'}}) + expect(proxyConnects).toHaveLength(0) + }) + + it('basic GraphQL client with custom auth', async () => { + const token = getToken() + if (!token) { + return + } + + // Valid token + let octokit = new GitHub(token) + const repository = await octokit.graphql( + '{repository(owner:"actions", name:"toolkit"){name}}' + ) + expect(repository).toEqual({repository: {name: 'toolkit'}}) + expect(proxyConnects).toHaveLength(0) + + // Invalid token + octokit = new GitHub({auth: `token asdf`}) + let failed = false + try { + await octokit.graphql( + '{repository(owner:"actions", name:"toolkit"){name}}' + ) + } catch (err) { + failed = true + } + expect(failed).toBeTruthy() + }) + + it('basic GraphQL client with proxy', async () => { + const token = getToken() + if (!token) { + return + } + + process.env['https_proxy'] = proxyUrl + const octokit = new GitHub(token) + const repository = await octokit.graphql( + '{repository(owner:"actions", name:"toolkit"){name}}' + ) + expect(repository).toEqual({repository: {name: 'toolkit'}}) + expect(proxyConnects).toEqual(['api.github.com:443']) + }) + + function getToken(): string { + const token = process.env['GITHUB_TOKEN'] || '' + if (!token && first) { + /* eslint-disable-next-line no-console */ + console.warn( + 'Skipping GitHub tests. Set $GITHUB_TOKEN to run REST client and GraphQL client tests' + ) + first = false + } + + return token + } +}) diff --git a/packages/github/__tests__/proxy.d.ts b/packages/github/__tests__/proxy.d.ts new file mode 100644 index 0000000000..9b61127b6b --- /dev/null +++ b/packages/github/__tests__/proxy.d.ts @@ -0,0 +1,5 @@ +declare module 'proxy' { + import * as http from 'http' + function internal(): http.Server + export = internal +} diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index c2ac4ab059..983f083d06 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -1,9 +1,17 @@ { "name": "@actions/github", - "version": "2.0.1", + "version": "2.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { + "@actions/http-client": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.2.tgz", + "integrity": "sha512-ngdGx7aXM7i9BFT+7e3RWWAEt3bX4tKrdI5w5hf0wYpHz66u5Nw6AFSFXG5wzQyUQbkgeNRnJZyK2zciGqXgrQ==", + "requires": { + "tunnel": "0.0.6" + } + }, "@babel/code-frame": { "version": "7.5.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", @@ -686,6 +694,67 @@ "normalize-path": "^2.1.1" } }, + "args": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/args/-/args-3.0.2.tgz", + "integrity": "sha1-hQu46IHzE5IDpeTLF2QxCStWLC0=", + "dev": true, + "requires": { + "camelcase": "4.1.0", + "chalk": "1.1.3", + "minimist": "1.2.0", + "pkginfo": "0.4.0", + "string-similarity": "1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -886,6 +955,12 @@ } } }, + "basic-auth-parser": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2.tgz", + "integrity": "sha1-zp5xp38jwSee7NJlmypGJEwVbkE=", + "dev": true + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -2401,6 +2476,23 @@ "function-bind": "^1.1.1" } }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -4046,6 +4138,12 @@ "find-up": "^3.0.0" } }, + "pkginfo": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.0.tgz", + "integrity": "sha1-NJ27f/04CB/K3AhT32h/DHdEzWU=", + "dev": true + }, "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", @@ -4092,6 +4190,34 @@ "sisteransi": "^1.0.0" } }, + "proxy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/proxy/-/proxy-1.0.1.tgz", + "integrity": "sha512-mM9Hl6Mbw2Iiw4WLzjtPObtxX3xdsv0Fr07Kqm+GXg0eVObKBD7mc+TMQwkv2zztk5EtyLdv0+eFNXhBfPiU8A==", + "dev": true, + "requires": { + "args": "3.0.2", + "basic-auth-parser": "0.0.2", + "debug": "^4.1.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "psl": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.2.0.tgz", @@ -4728,6 +4854,15 @@ } } }, + "string-similarity": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/string-similarity/-/string-similarity-1.1.0.tgz", + "integrity": "sha1-PGZJiFikZex8QMfYFzm72ZWQSRQ=", + "dev": true, + "requires": { + "lodash": "^4.13.1" + } + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -4896,6 +5031,11 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", diff --git a/packages/github/package.json b/packages/github/package.json index d3942b731e..5733b17054 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "2.0.1", + "version": "2.0.2", "description": "Actions github lib", "keywords": [ "github", @@ -37,10 +37,12 @@ "url": "https://github.com/actions/toolkit/issues" }, "dependencies": { + "@actions/http-client": "^1.0.2", "@octokit/graphql": "^4.3.1", "@octokit/rest": "^16.15.0" }, "devDependencies": { - "jest": "^24.7.1" + "jest": "^24.7.1", + "proxy": "^1.0.1" } } diff --git a/packages/github/src/github.ts b/packages/github/src/github.ts index fdf19f1c67..e07790f181 100644 --- a/packages/github/src/github.ts +++ b/packages/github/src/github.ts @@ -4,10 +4,15 @@ import {graphql} from '@octokit/graphql' // we need this type to set up a property on the GitHub object // that has token authorization // (it is not exported from octokit by default) -import {graphql as GraphQL} from '@octokit/graphql/dist-types/types' +import { + graphql as GraphQL, + RequestParameters as GraphQLRequestParameters +} from '@octokit/graphql/dist-types/types' import Octokit from '@octokit/rest' import * as Context from './context' +import * as http from 'http' +import {HttpClient} from '@actions/http-client' // We need this in order to extend Octokit Octokit.prototype = new Octokit() @@ -17,11 +22,113 @@ export const context = new Context.Context() export class GitHub extends Octokit { graphql: GraphQL - constructor(token: string, opts: Omit = {}) { - super({...opts, auth: `token ${token}`}) + /* eslint-disable no-dupe-class-members */ + // Disable no-dupe-class-members due to false positive for method overload + // https://github.com/typescript-eslint/typescript-eslint/issues/291 - this.graphql = graphql.defaults({ - headers: {authorization: `token ${token}`} - }) + /** + * Sets up the REST client and GraphQL client with auth and proxy support. + * The parameter `token` or `opts.auth` must be supplied. The GraphQL client + * authorization is not setup when `opts.auth` is a function or object. + * + * @param token Auth token + * @param opts Octokit options + */ + constructor(token: string, opts?: Omit) + constructor(opts: Octokit.Options) + constructor(token: string | Octokit.Options, opts?: Octokit.Options) { + super(GitHub.getOctokitOptions(GitHub.disambiguate(token, opts))) + + this.graphql = GitHub.getGraphQL(GitHub.disambiguate(token, opts)) + } + + /** + * Disambiguates the constructor overload parameters + */ + private static disambiguate( + token: string | Octokit.Options, + opts?: Octokit.Options + ): [string, Octokit.Options] { + return [ + typeof token === 'string' ? token : '', + typeof token === 'object' ? token : opts || {} + ] + } + + private static getOctokitOptions( + args: [string, Octokit.Options] + ): Octokit.Options { + const token = args[0] + const options = {...args[1]} // Shallow clone - don't mutate the object provided by the caller + + // Auth + const auth = GitHub.getAuthString(token, options) + if (auth) { + options.auth = auth + } + + // Proxy + const agent = GitHub.getProxyAgent(options) + if (agent) { + // Shallow clone - don't mutate the object provided by the caller + options.request = options.request ? {...options.request} : {} + + // Set the agent + options.request.agent = agent + } + + return options + } + + private static getGraphQL(args: [string, Octokit.Options]): GraphQL { + const defaults: GraphQLRequestParameters = {} + const token = args[0] + const options = args[1] + + // Authorization + const auth = this.getAuthString(token, options) + if (auth) { + defaults.headers = { + authorization: auth + } + } + + // Proxy + const agent = GitHub.getProxyAgent(options) + if (agent) { + defaults.request = {agent} + } + + return graphql.defaults(defaults) + } + + private static getAuthString( + token: string, + options: Octokit.Options + ): string | undefined { + // Validate args + if (!token && !options.auth) { + throw new Error('Parameter token or opts.auth is required') + } else if (token && options.auth) { + throw new Error( + 'Parameters token and opts.auth may not both be specified' + ) + } + + return typeof options.auth === 'string' ? options.auth : `token ${token}` + } + + private static getProxyAgent( + options: Octokit.Options + ): http.Agent | undefined { + if (!options.request?.agent) { + const proxyUrl = process.env['https_proxy'] || process.env['HTTPS_PROXY'] + if (proxyUrl) { + const httpClient = new HttpClient() + return httpClient.getAgent('https://api.github.com') + } + } + + return undefined } } diff --git a/packages/github/tsconfig.json b/packages/github/tsconfig.json index d68a7710cb..a8b812a6ff 100644 --- a/packages/github/tsconfig.json +++ b/packages/github/tsconfig.json @@ -2,7 +2,6 @@ "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./", - "esModuleInterop": true, "outDir": "./lib", "rootDir": "./src" }, diff --git a/packages/glob/__tests__/internal-pattern.test.ts b/packages/glob/__tests__/internal-pattern.test.ts index c6a1b2c874..5a2fbcc32d 100644 --- a/packages/glob/__tests__/internal-pattern.test.ts +++ b/packages/glob/__tests__/internal-pattern.test.ts @@ -1,17 +1,9 @@ import * as io from '../../io/src/io' +import * as os from 'os' import * as path from 'path' import {MatchKind} from '../src/internal-match-kind' import {promises as fs} from 'fs' - -// Mock 'os' before importing Pattern -/* eslint-disable import/first */ -/* eslint-disable @typescript-eslint/promise-function-async */ -// Note, @typescript-eslint/promise-function-async is a false positive due to the -// mock factory delegate which returns any. Fixed in a future version of jest. -jest.mock('os', () => jest.requireActual('os')) -const os = jest.requireMock('os') import {Pattern} from '../src/internal-pattern' -jest.resetModuleRegistry() const IS_WINDOWS = process.platform === 'win32' @@ -32,19 +24,13 @@ describe('pattern', () => { }) it('escapes homedir', async () => { - const originalHomedir = os.homedir const home = path.join(getTestTemp(), 'home-with-[and]') await fs.mkdir(home, {recursive: true}) - try { - os.homedir = () => home - const pattern = new Pattern('~/m*') + const pattern = new Pattern('~/m*', undefined, home) - expect(pattern.searchPath).toBe(home) - expect(pattern.match(path.join(home, 'match'))).toBeTruthy() - expect(pattern.match(path.join(home, 'not-match'))).toBeFalsy() - } finally { - os.homedir = originalHomedir - } + expect(pattern.searchPath).toBe(home) + expect(pattern.match(path.join(home, 'match'))).toBeTruthy() + expect(pattern.match(path.join(home, 'not-match'))).toBeFalsy() }) it('escapes root', async () => { diff --git a/packages/glob/package-lock.json b/packages/glob/package-lock.json index 043765ec54..3eb24c1b1a 100644 --- a/packages/glob/package-lock.json +++ b/packages/glob/package-lock.json @@ -4,11 +4,6 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@actions/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.1.tgz", - "integrity": "sha512-xD+CQd9p4lU7ZfRqmUcbJpqR+Ss51rJRVeXMyOLrZQImN9/8Sy/BEUBnHO/UKD3z03R686PVTLfEPmkropGuLw==" - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", diff --git a/packages/glob/src/internal-path-helper.ts b/packages/glob/src/internal-path-helper.ts index 7d9d0ef4a5..0931bd2cd0 100644 --- a/packages/glob/src/internal-path-helper.ts +++ b/packages/glob/src/internal-path-helper.ts @@ -1,5 +1,5 @@ -import * as assert from 'assert' import * as path from 'path' +import assert from 'assert' const IS_WINDOWS = process.platform === 'win32' diff --git a/packages/glob/src/internal-path.ts b/packages/glob/src/internal-path.ts index 789cb11884..47001d5acc 100644 --- a/packages/glob/src/internal-path.ts +++ b/packages/glob/src/internal-path.ts @@ -1,6 +1,6 @@ -import * as assert from 'assert' import * as path from 'path' import * as pathHelper from './internal-path-helper' +import assert from 'assert' const IS_WINDOWS = process.platform === 'win32' diff --git a/packages/glob/src/internal-pattern.ts b/packages/glob/src/internal-pattern.ts index 2735ca73cc..348acca878 100644 --- a/packages/glob/src/internal-pattern.ts +++ b/packages/glob/src/internal-pattern.ts @@ -1,7 +1,7 @@ -import * as assert from 'assert' import * as os from 'os' import * as path from 'path' import * as pathHelper from './internal-path-helper' +import assert from 'assert' import {Minimatch, IMinimatch, IOptions as IMinimatchOptions} from 'minimatch' import {MatchKind} from './internal-match-kind' import {Path} from './internal-path' @@ -48,8 +48,13 @@ export class Pattern { // https://github.com/typescript-eslint/typescript-eslint/issues/291 constructor(pattern: string) + constructor(pattern: string, segments: undefined, homedir: string) constructor(negate: boolean, segments: string[]) - constructor(patternOrNegate: string | boolean, segments?: string[]) { + constructor( + patternOrNegate: string | boolean, + segments?: string[], + homedir?: string + ) { // Pattern overload let pattern: string if (typeof patternOrNegate === 'string') { @@ -78,7 +83,7 @@ export class Pattern { } // Normalize slashes and ensures absolute root - pattern = Pattern.fixupPattern(pattern) + pattern = Pattern.fixupPattern(pattern, homedir) // Segments this.segments = new Path(pattern).segments @@ -177,7 +182,7 @@ export class Pattern { /** * Normalizes slashes and ensures absolute root */ - private static fixupPattern(pattern: string): string { + private static fixupPattern(pattern: string, homedir?: string): string { // Empty assert(pattern, 'pattern cannot be empty') @@ -206,7 +211,7 @@ export class Pattern { } // Replace leading `~` segment else if (pattern === '~' || pattern.startsWith(`~${path.sep}`)) { - const homedir = os.homedir() + homedir = homedir || os.homedir() assert(homedir, 'Unable to determine HOME directory') assert( pathHelper.hasAbsoluteRoot(homedir), diff --git a/packages/tool-cache/__tests__/tool-cache.test.ts b/packages/tool-cache/__tests__/tool-cache.test.ts index 543280a545..a28900856a 100644 --- a/packages/tool-cache/__tests__/tool-cache.test.ts +++ b/packages/tool-cache/__tests__/tool-cache.test.ts @@ -1,8 +1,8 @@ import * as fs from 'fs' -import * as nock from 'nock' import * as path from 'path' import * as io from '@actions/io' import * as exec from '@actions/exec' +import nock from 'nock' const cachePath = path.join(__dirname, 'CACHE') const tempPath = path.join(__dirname, 'TEMP') diff --git a/packages/tool-cache/package-lock.json b/packages/tool-cache/package-lock.json index 2ec8bf9079..129e3dfa0f 100644 --- a/packages/tool-cache/package-lock.json +++ b/packages/tool-cache/package-lock.json @@ -1,31 +1,16 @@ { "name": "@actions/tool-cache", - "version": "1.3.0", + "version": "1.3.1", "lockfileVersion": 1, "requires": true, "dependencies": { - "@actions/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.1.tgz", - "integrity": "sha512-xD+CQd9p4lU7ZfRqmUcbJpqR+Ss51rJRVeXMyOLrZQImN9/8Sy/BEUBnHO/UKD3z03R686PVTLfEPmkropGuLw==" - }, - "@actions/exec": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.3.tgz", - "integrity": "sha512-TogJGnueOmM7ntCi0ASTUj4LapRRtDfj57Ja4IhPmg2fls28uVOPbAn8N+JifaOumN2UG3oEO/Ixek2A4NcYSA==", - "requires": { - "@actions/io": "^1.0.1" - } - }, "@actions/http-client": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.1.tgz", - "integrity": "sha512-vy5DhqTJ1gtEkpRrD/6BHhUlkeyccrOX0BT9KmtO5TWxe5KSSwVHFE+J15Z0dG+tJwZJ/nHC4slUIyqpkahoMg==" - }, - "@actions/io": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", - "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==" + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.2.tgz", + "integrity": "sha512-ngdGx7aXM7i9BFT+7e3RWWAEt3bX4tKrdI5w5hf0wYpHz66u5Nw6AFSFXG5wzQyUQbkgeNRnJZyK2zciGqXgrQ==", + "requires": { + "tunnel": "0.0.6" + } }, "@types/nock": { "version": "10.0.3", @@ -194,6 +179,11 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index 990c5d85a3..a9d10451f4 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.3.0", + "version": "1.3.1", "description": "Actions tool-cache lib", "keywords": [ "github", @@ -38,7 +38,7 @@ "dependencies": { "@actions/core": "^1.2.0", "@actions/exec": "^1.0.0", - "@actions/http-client": "^1.0.1", + "@actions/http-client": "^1.0.2", "@actions/io": "^1.0.1", "semver": "^6.1.0", "uuid": "^3.3.2" diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index b91cf1adb7..6e8c6c2a0f 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -5,7 +5,7 @@ import * as os from 'os' import * as path from 'path' import * as httpm from '@actions/http-client' import * as semver from 'semver' -import * as uuidV4 from 'uuid/v4' +import uuidV4 from 'uuid/v4' import {exec} from '@actions/exec/lib/exec' import {ExecOptions} from '@actions/exec/lib/interfaces' import {ok} from 'assert' diff --git a/tsconfig.json b/tsconfig.json index 80c954ed64..a69cbc2edf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "esModuleInterop": true, "module": "commonjs", "strict": true, "declaration": true, From 8b0300129f08728419263b016de8630f1d426d5f Mon Sep 17 00:00:00 2001 From: eric sciple Date: Sat, 18 Jan 2020 23:52:44 -0500 Subject: [PATCH 080/192] fix command escaping (#302) --- packages/core/__tests__/command.test.ts | 45 +++++++++++++++++++++++++ packages/core/__tests__/core.test.ts | 6 ++-- packages/core/src/command.ts | 32 ++++++++---------- 3 files changed, 62 insertions(+), 21 deletions(-) diff --git a/packages/core/__tests__/command.test.ts b/packages/core/__tests__/command.test.ts index 0fa99e97df..182e145cb3 100644 --- a/packages/core/__tests__/command.test.ts +++ b/packages/core/__tests__/command.test.ts @@ -27,6 +27,51 @@ describe('@actions/core/src/command', () => { assertWriteCalls([`::some-command::${os.EOL}`]) }) + it('command escapes message', () => { + // Verify replaces each instance, not just first instance + command.issueCommand( + 'some-command', + {}, + 'percent % percent % cr \r cr \r lf \n lf \n' + ) + assertWriteCalls([ + `::some-command::percent %25 percent %25 cr %0D cr %0D lf %0A lf %0A${os.EOL}` + ]) + + // Verify literal escape sequences + process.stdout.write = jest.fn() + command.issueCommand('some-command', {}, '%25 %25 %0D %0D %0A %0A') + assertWriteCalls([ + `::some-command::%2525 %2525 %250D %250D %250A %250A${os.EOL}` + ]) + }) + + it('command escapes property', () => { + // Verify replaces each instance, not just first instance + command.issueCommand( + 'some-command', + { + name: + 'percent % percent % cr \r cr \r lf \n lf \n colon : colon : comma , comma ,' + }, + '' + ) + assertWriteCalls([ + `::some-command name=percent %25 percent %25 cr %0D cr %0D lf %0A lf %0A colon %3A colon %3A comma %2C comma %2C::${os.EOL}` + ]) + + // Verify literal escape sequences + process.stdout.write = jest.fn() + command.issueCommand( + 'some-command', + {}, + '%25 %25 %0D %0D %0A %0A %3A %3A %2C %2C' + ) + assertWriteCalls([ + `::some-command::%2525 %2525 %250D %250D %250A %250A %253A %253A %252C %252C${os.EOL}` + ]) + }) + it('command with message', () => { command.issueCommand('some-command', {}, 'some message') assertWriteCalls([`::some-command::some message${os.EOL}`]) diff --git a/packages/core/__tests__/core.test.ts b/packages/core/__tests__/core.test.ts index 2c7e0b5424..90235428e8 100644 --- a/packages/core/__tests__/core.test.ts +++ b/packages/core/__tests__/core.test.ts @@ -41,10 +41,10 @@ describe('@actions/core', () => { }) it('exportVariable escapes variable names', () => { - core.exportVariable('special char var \r\n];', 'special val') - expect(process.env['special char var \r\n];']).toBe('special val') + core.exportVariable('special char var \r\n,:', 'special val') + expect(process.env['special char var \r\n,:']).toBe('special val') assertWriteCalls([ - `::set-env name=special char var %0D%0A%5D%3B::special val${os.EOL}` + `::set-env name=special char var %0D%0A%2C%3A::special val${os.EOL}` ]) }) diff --git a/packages/core/src/command.ts b/packages/core/src/command.ts index 29185dfc11..9bdfd9c41c 100644 --- a/packages/core/src/command.ts +++ b/packages/core/src/command.ts @@ -10,11 +10,11 @@ interface CommandProperties { * Commands * * Command Format: - * ##[name key=value;key=value]message + * ::name key=value,key=value::message * * Examples: - * ##[warning]This is the user warning message - * ##[set-secret name=mypassword]definitelyNotAPassword! + * ::warning::This is the message + * ::set-env name=MY_VAR::some value */ export function issueCommand( command: string, @@ -62,33 +62,29 @@ class Command { cmdStr += ',' } - // safely append the val - avoid blowing up when attempting to - // call .replace() if message is not a string for some reason - cmdStr += `${key}=${escape(`${val || ''}`)}` + cmdStr += `${key}=${escapeProperty(val)}` } } } } - cmdStr += CMD_STRING - - // safely append the message - avoid blowing up when attempting to - // call .replace() if message is not a string for some reason - const message = `${this.message || ''}` - cmdStr += escapeData(message) - + cmdStr += `${CMD_STRING}${escapeData(this.message)}` return cmdStr } } function escapeData(s: string): string { - return s.replace(/\r/g, '%0D').replace(/\n/g, '%0A') + return (s || '') + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A') } -function escape(s: string): string { - return s +function escapeProperty(s: string): string { + return (s || '') + .replace(/%/g, '%25') .replace(/\r/g, '%0D') .replace(/\n/g, '%0A') - .replace(/]/g, '%5D') - .replace(/;/g, '%3B') + .replace(/:/g, '%3A') + .replace(/,/g, '%2C') } From a9175f39863d11d5f881d151a1010b571e3b921d Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Tue, 21 Jan 2020 10:44:38 -0500 Subject: [PATCH 081/192] Correctly pull issue number for pull request review events (#311) --- packages/github/__tests__/lib.test.ts | 5 +++-- packages/github/src/context.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/github/__tests__/lib.test.ts b/packages/github/__tests__/lib.test.ts index 5c042f2a8c..dae2a8cdc2 100644 --- a/packages/github/__tests__/lib.test.ts +++ b/packages/github/__tests__/lib.test.ts @@ -52,10 +52,11 @@ describe('@actions/context', () => { }) }) - it('works with pullRequest payloads', () => { + it('works with pull_request payloads', () => { delete process.env.GITHUB_REPOSITORY context.payload = { - pullRequest: {number: 2}, + // eslint-disable-next-line @typescript-eslint/camelcase + pull_request: {number: 2}, repository: {owner: {login: 'user'}, name: 'test'} } expect(context.issue).toEqual({ diff --git a/packages/github/src/context.ts b/packages/github/src/context.ts index f2aafde112..21f14c9c09 100644 --- a/packages/github/src/context.ts +++ b/packages/github/src/context.ts @@ -44,7 +44,7 @@ export class Context { return { ...this.repo, - number: (payload.issue || payload.pullRequest || payload).number + number: (payload.issue || payload.pull_request || payload).number } } From 6072c249ee5e024fa1d48309f5b4f6ce2a18edb6 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Tue, 21 Jan 2020 13:25:05 -0500 Subject: [PATCH 082/192] release notes (#317) --- packages/core/RELEASES.md | 4 ++++ packages/core/package-lock.json | 2 +- packages/core/package.json | 2 +- packages/github/RELEASES.md | 5 +++++ packages/github/package-lock.json | 2 +- packages/github/package.json | 2 +- packages/tool-cache/RELEASES.md | 4 ++++ 7 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/core/RELEASES.md b/packages/core/RELEASES.md index 174a9dbbbe..53428af497 100644 --- a/packages/core/RELEASES.md +++ b/packages/core/RELEASES.md @@ -1,5 +1,9 @@ # @actions/core Releases +### 1.2.2 + +- [Fix escaping for runner commands](https://github.com/actions/toolkit/pull/302) + ### 1.2.1 - [Remove trailing comma from commands](https://github.com/actions/toolkit/pull/263) diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json index a25fa4ae2e..2e8b88fdd8 100644 --- a/packages/core/package-lock.json +++ b/packages/core/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/core", - "version": "1.2.1", + "version": "1.2.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index 6c4e9932f2..7be0dc09e4 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@actions/core", - "version": "1.2.1", + "version": "1.2.2", "description": "Actions core lib", "keywords": [ "github", diff --git a/packages/github/RELEASES.md b/packages/github/RELEASES.md index a827093ae3..81cb537feb 100644 --- a/packages/github/RELEASES.md +++ b/packages/github/RELEASES.md @@ -1,5 +1,10 @@ # @actions/github Releases +### 2.1.0 + +- [Octokit client follows proxy settings](https://github.com/actions/toolkit/pull/314) +- [Fix issue number for pull request comment events](https://github.com/actions/toolkit/pull/311) + ### 2.0.1 - [Add \"types\" to package.json](https://github.com/actions/toolkit/pull/221) diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index 983f083d06..e7e3eaacc7 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "2.0.2", + "version": "2.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/github/package.json b/packages/github/package.json index 5733b17054..6dc176efa0 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "2.0.2", + "version": "2.1.0", "description": "Actions github lib", "keywords": [ "github", diff --git a/packages/tool-cache/RELEASES.md b/packages/tool-cache/RELEASES.md index a4dc6b8ba5..24a6b70b5d 100644 --- a/packages/tool-cache/RELEASES.md +++ b/packages/tool-cache/RELEASES.md @@ -1,5 +1,9 @@ # @actions/tool-cache Releases +### 1.3.1 + +- [Increase http-client min version](https://github.com/actions/toolkit/pull/314) + ### 1.3.0 - [Uses @actions/http-client](https://github.com/actions/http-client) From 80e6ba7033af5caf0a42d080f61ab052a1c1db7f Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Wed, 22 Jan 2020 11:53:39 -0500 Subject: [PATCH 083/192] Update readme with type assertion information (#310) * Update readme with type assertion information * PR updates --- packages/github/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/github/README.md b/packages/github/README.md index bd5f708b3f..d6cf49b1e2 100644 --- a/packages/github/README.md +++ b/packages/github/README.md @@ -55,3 +55,20 @@ const newIssue = await octokit.issues.create({ body: 'Hello Universe!' }); ``` + +## Webhook payload typescript definitions + +The npm module `@octokit/webhooks` provides type definitions for the response payloads. You can cast the payload to these types for better type information. + +First, install the npm module `npm install @octokit/webhooks` + +Then, assert the type based on the eventName +```ts +import * as core from '@actions/core' +import * as github from '@actions/github' +import * as Webhooks from '@octokit/webhooks' +if (github.context.eventName === 'push') { + const pushPayload = github.context.payload as Webhooks.WebhookPayloadPush + core.info(`The head commit is: ${pushPayload.head}`) +} +``` From 432a78c48c941e90eedf7911ff0aa271bacad97b Mon Sep 17 00:00:00 2001 From: eric sciple Date: Thu, 23 Jan 2020 14:35:41 -0500 Subject: [PATCH 084/192] check proxy bypass before setting proxy agent (#320) --- packages/github/package-lock.json | 6 +++--- packages/github/package.json | 2 +- packages/github/src/github.ts | 10 +++++----- packages/tool-cache/package-lock.json | 6 +++--- packages/tool-cache/package.json | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index e7e3eaacc7..c3bcb49cf7 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@actions/http-client": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.2.tgz", - "integrity": "sha512-ngdGx7aXM7i9BFT+7e3RWWAEt3bX4tKrdI5w5hf0wYpHz66u5Nw6AFSFXG5wzQyUQbkgeNRnJZyK2zciGqXgrQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.3.tgz", + "integrity": "sha512-wFwh1U4adB/Zsk4cc9kVqaBOHoknhp/pJQk+aWTocbAZWpIl4Zx/At83WFRLXvxB+5HVTWOACM6qjULMZfQSfw==", "requires": { "tunnel": "0.0.6" } diff --git a/packages/github/package.json b/packages/github/package.json index 6dc176efa0..ee68a04fc5 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -37,7 +37,7 @@ "url": "https://github.com/actions/toolkit/issues" }, "dependencies": { - "@actions/http-client": "^1.0.2", + "@actions/http-client": "^1.0.3", "@octokit/graphql": "^4.3.1", "@octokit/rest": "^16.15.0" }, diff --git a/packages/github/src/github.ts b/packages/github/src/github.ts index e07790f181..a2ef233875 100644 --- a/packages/github/src/github.ts +++ b/packages/github/src/github.ts @@ -12,7 +12,7 @@ import { import Octokit from '@octokit/rest' import * as Context from './context' import * as http from 'http' -import {HttpClient} from '@actions/http-client' +import * as httpClient from '@actions/http-client' // We need this in order to extend Octokit Octokit.prototype = new Octokit() @@ -122,10 +122,10 @@ export class GitHub extends Octokit { options: Octokit.Options ): http.Agent | undefined { if (!options.request?.agent) { - const proxyUrl = process.env['https_proxy'] || process.env['HTTPS_PROXY'] - if (proxyUrl) { - const httpClient = new HttpClient() - return httpClient.getAgent('https://api.github.com') + const serverUrl = 'https://api.github.com' + if (httpClient.getProxyUrl(serverUrl)) { + const hc = new httpClient.HttpClient() + return hc.getAgent(serverUrl) } } diff --git a/packages/tool-cache/package-lock.json b/packages/tool-cache/package-lock.json index 129e3dfa0f..9eb9a83dd5 100644 --- a/packages/tool-cache/package-lock.json +++ b/packages/tool-cache/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@actions/http-client": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.2.tgz", - "integrity": "sha512-ngdGx7aXM7i9BFT+7e3RWWAEt3bX4tKrdI5w5hf0wYpHz66u5Nw6AFSFXG5wzQyUQbkgeNRnJZyK2zciGqXgrQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.3.tgz", + "integrity": "sha512-wFwh1U4adB/Zsk4cc9kVqaBOHoknhp/pJQk+aWTocbAZWpIl4Zx/At83WFRLXvxB+5HVTWOACM6qjULMZfQSfw==", "requires": { "tunnel": "0.0.6" } diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index a9d10451f4..41f73293ef 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -38,7 +38,7 @@ "dependencies": { "@actions/core": "^1.2.0", "@actions/exec": "^1.0.0", - "@actions/http-client": "^1.0.2", + "@actions/http-client": "^1.0.3", "@actions/io": "^1.0.1", "semver": "^6.1.0", "uuid": "^3.3.2" From 0ecc141d4e2c707fcdf3aeaa1801cfa1758b1683 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Mon, 10 Feb 2020 04:15:26 +0100 Subject: [PATCH 085/192] await tc.downloadTool (#337) --- packages/tool-cache/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/tool-cache/README.md b/packages/tool-cache/README.md index 335f34b6a6..a1513d3529 100644 --- a/packages/tool-cache/README.md +++ b/packages/tool-cache/README.md @@ -22,11 +22,11 @@ These can then be extracted in platform specific ways: const tc = require('@actions/tool-cache'); if (process.platform === 'win32') { - const node12Path = tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.zip'); + const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.zip'); const node12ExtractedFolder = await tc.extractZip(node12Path, 'path/to/extract/to'); // Or alternately - const node12Path = tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.7z'); + const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.7z'); const node12ExtractedFolder = await tc.extract7z(node12Path, 'path/to/extract/to'); } else { From 6cbb8e9bc81c30bf70afbb1def4ca2c1cfdc7857 Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Tue, 11 Feb 2020 09:49:46 -0500 Subject: [PATCH 086/192] @actions/artifact package (#304) * Initial commit for @actions/artifact package --- packages/artifact/README.md | 0 .../__tests__/upload-specification.test.ts | 353 ++++++++++++++ packages/artifact/__tests__/upload.test.ts | 453 ++++++++++++++++++ packages/artifact/__tests__/util.test.ts | 99 ++++ packages/artifact/package-lock.json | 26 + packages/artifact/package.json | 42 ++ .../__mocks__/internal-config-variables.ts | 30 ++ packages/artifact/src/artifact-client.ts | 9 + .../artifact/src/internal-artifact-client.ts | 124 +++++ .../artifact/src/internal-config-variables.ts | 35 ++ packages/artifact/src/internal-contracts.ts | 33 ++ .../artifact/src/internal-download-options.ts | 7 + .../src/internal-download-response.ts | 11 + .../src/internal-upload-http-client.ts | 322 +++++++++++++ .../artifact/src/internal-upload-options.ts | 18 + .../artifact/src/internal-upload-response.ts | 22 + .../src/internal-upload-specification.ts | 92 ++++ packages/artifact/src/internal-utils.ts | 115 +++++ packages/artifact/tsconfig.json | 11 + 19 files changed, 1802 insertions(+) create mode 100644 packages/artifact/README.md create mode 100644 packages/artifact/__tests__/upload-specification.test.ts create mode 100644 packages/artifact/__tests__/upload.test.ts create mode 100644 packages/artifact/__tests__/util.test.ts create mode 100644 packages/artifact/package-lock.json create mode 100644 packages/artifact/package.json create mode 100644 packages/artifact/src/__mocks__/internal-config-variables.ts create mode 100644 packages/artifact/src/artifact-client.ts create mode 100644 packages/artifact/src/internal-artifact-client.ts create mode 100644 packages/artifact/src/internal-config-variables.ts create mode 100644 packages/artifact/src/internal-contracts.ts create mode 100644 packages/artifact/src/internal-download-options.ts create mode 100644 packages/artifact/src/internal-download-response.ts create mode 100644 packages/artifact/src/internal-upload-http-client.ts create mode 100644 packages/artifact/src/internal-upload-options.ts create mode 100644 packages/artifact/src/internal-upload-response.ts create mode 100644 packages/artifact/src/internal-upload-specification.ts create mode 100644 packages/artifact/src/internal-utils.ts create mode 100644 packages/artifact/tsconfig.json diff --git a/packages/artifact/README.md b/packages/artifact/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/artifact/__tests__/upload-specification.test.ts b/packages/artifact/__tests__/upload-specification.test.ts new file mode 100644 index 0000000000..ba1cd9e435 --- /dev/null +++ b/packages/artifact/__tests__/upload-specification.test.ts @@ -0,0 +1,353 @@ +import * as io from '../../io/src/io' +import * as path from 'path' +import {promises as fs} from 'fs' +import * as core from '@actions/core' +import {getUploadSpecification} from '../src/internal-upload-specification' + +const artifactName = 'my-artifact' +const root = path.join(__dirname, '_temp', 'upload-specification') +const goodItem1Path = path.join( + root, + 'folder-a', + 'folder-b', + 'folder-c', + 'good-item1.txt' +) +const goodItem2Path = path.join(root, 'folder-d', 'good-item2.txt') +const goodItem3Path = path.join(root, 'folder-d', 'good-item3.txt') +const goodItem4Path = path.join(root, 'folder-d', 'good-item4.txt') +const goodItem5Path = path.join(root, 'good-item5.txt') +const badItem1Path = path.join( + root, + 'folder-a', + 'folder-b', + 'folder-c', + 'bad-item1.txt' +) +const badItem2Path = path.join(root, 'folder-d', 'bad-item2.txt') +const badItem3Path = path.join(root, 'folder-f', 'bad-item3.txt') +const badItem4Path = path.join(root, 'folder-h', 'folder-i', 'bad-item4.txt') +const badItem5Path = path.join(root, 'folder-h', 'folder-i', 'bad-item5.txt') +const extraFileInFolderCPath = path.join( + root, + 'folder-a', + 'folder-b', + 'folder-c', + 'extra-file-in-folder-c.txt' +) +const amazingFileInFolderHPath = path.join(root, 'folder-h', 'amazing-item.txt') + +const artifactFilesToUpload = [ + goodItem1Path, + goodItem2Path, + goodItem3Path, + goodItem4Path, + goodItem5Path, + extraFileInFolderCPath, + amazingFileInFolderHPath +] + +describe('Search', () => { + beforeAll(async () => { + // mock all output so that there is less noise when running tests + jest.spyOn(console, 'log').mockImplementation(() => {}) + jest.spyOn(core, 'debug').mockImplementation(() => {}) + jest.spyOn(core, 'info').mockImplementation(() => {}) + jest.spyOn(core, 'warning').mockImplementation(() => {}) + + // clear temp directory + await io.rmRF(root) + await fs.mkdir(path.join(root, 'folder-a', 'folder-b', 'folder-c'), { + recursive: true + }) + await fs.mkdir(path.join(root, 'folder-a', 'folder-b', 'folder-e'), { + recursive: true + }) + await fs.mkdir(path.join(root, 'folder-d'), { + recursive: true + }) + await fs.mkdir(path.join(root, 'folder-f'), { + recursive: true + }) + await fs.mkdir(path.join(root, 'folder-g'), { + recursive: true + }) + await fs.mkdir(path.join(root, 'folder-h', 'folder-i'), { + recursive: true + }) + + await fs.writeFile(goodItem1Path, 'good item1 file') + await fs.writeFile(goodItem2Path, 'good item2 file') + await fs.writeFile(goodItem3Path, 'good item3 file') + await fs.writeFile(goodItem4Path, 'good item4 file') + await fs.writeFile(goodItem5Path, 'good item5 file') + + await fs.writeFile(badItem1Path, 'bad item1 file') + await fs.writeFile(badItem2Path, 'bad item2 file') + await fs.writeFile(badItem3Path, 'bad item3 file') + await fs.writeFile(badItem4Path, 'bad item4 file') + await fs.writeFile(badItem5Path, 'bad item5 file') + + await fs.writeFile(extraFileInFolderCPath, 'extra file') + + await fs.writeFile(amazingFileInFolderHPath, 'amazing file') + /* + Directory structure of files that get created: + root/ + folder-a/ + folder-b/ + folder-c/ + good-item1.txt + bad-item1.txt + extra-file-in-folder-c.txt + folder-e/ + folder-d/ + good-item2.txt + good-item3.txt + good-item4.txt + bad-item2.txt + folder-f/ + bad-item3.txt + folder-g/ + folder-h/ + amazing-item.txt + folder-i/ + bad-item4.txt + bad-item5.txt + good-item5.txt + */ + }) + + it('Upload Specification - Fail non-existent rootDirectory', async () => { + const invalidRootDirectory = path.join( + __dirname, + '_temp', + 'upload-specification-invalid' + ) + expect(() => { + getUploadSpecification( + artifactName, + invalidRootDirectory, + artifactFilesToUpload + ) + }).toThrow(`Provided rootDirectory ${invalidRootDirectory} does not exist`) + }) + + it('Upload Specification - Fail invalid rootDirectory', async () => { + expect(() => { + getUploadSpecification(artifactName, goodItem1Path, artifactFilesToUpload) + }).toThrow( + `Provided rootDirectory ${goodItem1Path} is not a valid directory` + ) + }) + + it('Upload Specification - File does not exist', async () => { + const fakeFilePath = path.join( + artifactName, + 'folder-a', + 'folder-b', + 'non-existent-file.txt' + ) + expect(() => { + getUploadSpecification(artifactName, root, [fakeFilePath]) + }).toThrow(`File ${fakeFilePath} does not exist`) + }) + + it('Upload Specification - Non parent directory', async () => { + const folderADirectory = path.join(root, 'folder-a') + const artifactFiles = [ + goodItem1Path, + badItem1Path, + extraFileInFolderCPath, + goodItem5Path + ] + expect(() => { + getUploadSpecification(artifactName, folderADirectory, artifactFiles) + }).toThrow( + `The rootDirectory: ${folderADirectory} is not a parent directory of the file: ${goodItem5Path}` + ) + }) + + it('Upload Specification - Success', async () => { + const specifications = getUploadSpecification( + artifactName, + root, + artifactFilesToUpload + ) + expect(specifications.length).toEqual(7) + + const absolutePaths = specifications.map(item => item.absoluteFilePath) + expect(absolutePaths).toContain(goodItem1Path) + expect(absolutePaths).toContain(goodItem2Path) + expect(absolutePaths).toContain(goodItem3Path) + expect(absolutePaths).toContain(goodItem4Path) + expect(absolutePaths).toContain(goodItem5Path) + expect(absolutePaths).toContain(extraFileInFolderCPath) + expect(absolutePaths).toContain(amazingFileInFolderHPath) + + for (const specification of specifications) { + if (specification.absoluteFilePath === goodItem1Path) { + expect(specification.uploadFilePath).toEqual( + path.join( + artifactName, + 'folder-a', + 'folder-b', + 'folder-c', + 'good-item1.txt' + ) + ) + } else if (specification.absoluteFilePath === goodItem2Path) { + expect(specification.uploadFilePath).toEqual( + path.join(artifactName, 'folder-d', 'good-item2.txt') + ) + } else if (specification.absoluteFilePath === goodItem3Path) { + expect(specification.uploadFilePath).toEqual( + path.join(artifactName, 'folder-d', 'good-item3.txt') + ) + } else if (specification.absoluteFilePath === goodItem4Path) { + expect(specification.uploadFilePath).toEqual( + path.join(artifactName, 'folder-d', 'good-item4.txt') + ) + } else if (specification.absoluteFilePath === goodItem5Path) { + expect(specification.uploadFilePath).toEqual( + path.join(artifactName, 'good-item5.txt') + ) + } else if (specification.absoluteFilePath === extraFileInFolderCPath) { + expect(specification.uploadFilePath).toEqual( + path.join( + artifactName, + 'folder-a', + 'folder-b', + 'folder-c', + 'extra-file-in-folder-c.txt' + ) + ) + } else if (specification.absoluteFilePath === amazingFileInFolderHPath) { + expect(specification.uploadFilePath).toEqual( + path.join(artifactName, 'folder-h', 'amazing-item.txt') + ) + } else { + throw new Error( + 'Invalid specification found. This should never be reached' + ) + } + } + }) + + it('Upload Specification - Success with extra slash', async () => { + const rootWithSlash = `${root}/` + const specifications = getUploadSpecification( + artifactName, + rootWithSlash, + artifactFilesToUpload + ) + expect(specifications.length).toEqual(7) + + const absolutePaths = specifications.map(item => item.absoluteFilePath) + expect(absolutePaths).toContain(goodItem1Path) + expect(absolutePaths).toContain(goodItem2Path) + expect(absolutePaths).toContain(goodItem3Path) + expect(absolutePaths).toContain(goodItem4Path) + expect(absolutePaths).toContain(goodItem5Path) + expect(absolutePaths).toContain(extraFileInFolderCPath) + expect(absolutePaths).toContain(amazingFileInFolderHPath) + + for (const specification of specifications) { + if (specification.absoluteFilePath === goodItem1Path) { + expect(specification.uploadFilePath).toEqual( + path.join( + artifactName, + 'folder-a', + 'folder-b', + 'folder-c', + 'good-item1.txt' + ) + ) + } else if (specification.absoluteFilePath === goodItem2Path) { + expect(specification.uploadFilePath).toEqual( + path.join(artifactName, 'folder-d', 'good-item2.txt') + ) + } else if (specification.absoluteFilePath === goodItem3Path) { + expect(specification.uploadFilePath).toEqual( + path.join(artifactName, 'folder-d', 'good-item3.txt') + ) + } else if (specification.absoluteFilePath === goodItem4Path) { + expect(specification.uploadFilePath).toEqual( + path.join(artifactName, 'folder-d', 'good-item4.txt') + ) + } else if (specification.absoluteFilePath === goodItem5Path) { + expect(specification.uploadFilePath).toEqual( + path.join(artifactName, 'good-item5.txt') + ) + } else if (specification.absoluteFilePath === extraFileInFolderCPath) { + expect(specification.uploadFilePath).toEqual( + path.join( + artifactName, + 'folder-a', + 'folder-b', + 'folder-c', + 'extra-file-in-folder-c.txt' + ) + ) + } else if (specification.absoluteFilePath === amazingFileInFolderHPath) { + expect(specification.uploadFilePath).toEqual( + path.join(artifactName, 'folder-h', 'amazing-item.txt') + ) + } else { + throw new Error( + 'Invalid specification found. This should never be reached' + ) + } + } + }) + + it('Upload Specification - Directories should not be included', async () => { + const folderEPath = path.join(root, 'folder-a', 'folder-b', 'folder-e') + const filesWithDirectory = [ + goodItem1Path, + goodItem4Path, + folderEPath, + badItem3Path + ] + const specifications = getUploadSpecification( + artifactName, + root, + filesWithDirectory + ) + expect(specifications.length).toEqual(3) + const absolutePaths = specifications.map(item => item.absoluteFilePath) + expect(absolutePaths).toContain(goodItem1Path) + expect(absolutePaths).toContain(goodItem4Path) + expect(absolutePaths).toContain(badItem3Path) + + for (const specification of specifications) { + if (specification.absoluteFilePath === goodItem1Path) { + expect(specification.uploadFilePath).toEqual( + path.join( + artifactName, + 'folder-a', + 'folder-b', + 'folder-c', + 'good-item1.txt' + ) + ) + } else if (specification.absoluteFilePath === goodItem2Path) { + expect(specification.uploadFilePath).toEqual( + path.join(artifactName, 'folder-d', 'good-item2.txt') + ) + } else if (specification.absoluteFilePath === goodItem4Path) { + expect(specification.uploadFilePath).toEqual( + path.join(artifactName, 'folder-d', 'good-item4.txt') + ) + } else if (specification.absoluteFilePath === badItem3Path) { + expect(specification.uploadFilePath).toEqual( + path.join(artifactName, 'folder-f', 'bad-item3.txt') + ) + } else { + throw new Error( + 'Invalid specification found. This should never be reached' + ) + } + } + }) +}) diff --git a/packages/artifact/__tests__/upload.test.ts b/packages/artifact/__tests__/upload.test.ts new file mode 100644 index 0000000000..08a758ca4e --- /dev/null +++ b/packages/artifact/__tests__/upload.test.ts @@ -0,0 +1,453 @@ +import * as http from 'http' +import * as io from '../../io/src/io' +import * as net from 'net' +import * as path from 'path' +import * as uploadHttpClient from '../src/internal-upload-http-client' +import * as core from '@actions/core' +import {promises as fs} from 'fs' +import {getRuntimeUrl} from '../src/internal-config-variables' +import {HttpClient, HttpClientResponse} from '@actions/http-client' +import { + ArtifactResponse, + PatchArtifactSizeSuccessResponse +} from '../src/internal-contracts' +import {UploadSpecification} from '../src/internal-upload-specification' + +const root = path.join(__dirname, '_temp', 'artifact-upload') +const file1Path = path.join(root, 'file1.txt') +const file2Path = path.join(root, 'file2.txt') +const file3Path = path.join(root, 'folder1', 'file3.txt') +const file4Path = path.join(root, 'folder1', 'file4.txt') +const file5Path = path.join(root, 'folder1', 'folder2', 'folder3', 'file5.txt') + +let file1Size = 0 +let file2Size = 0 +let file3Size = 0 +let file4Size = 0 +let file5Size = 0 + +jest.mock('../src/internal-config-variables') +jest.mock('@actions/http-client') + +describe('Upload Tests', () => { + beforeAll(async () => { + // mock all output so that there is less noise when running tests + jest.spyOn(console, 'log').mockImplementation(() => {}) + jest.spyOn(core, 'debug').mockImplementation(() => {}) + jest.spyOn(core, 'info').mockImplementation(() => {}) + jest.spyOn(core, 'warning').mockImplementation(() => {}) + + // setup mocking for calls that got through the HttpClient + setupHttpClientMock() + + // clear temp directory and create files that will be "uploaded" + await io.rmRF(root) + await fs.mkdir(path.join(root, 'folder1', 'folder2', 'folder3'), { + recursive: true + }) + await fs.writeFile(file1Path, 'this is file 1') + await fs.writeFile(file2Path, 'this is file 2') + await fs.writeFile(file3Path, 'this is file 3') + await fs.writeFile(file4Path, 'this is file 4') + await fs.writeFile(file5Path, 'this is file 5') + /* + Directory structure for files that get created: + root/ + file1.txt + file2.txt + folder1/ + file3.txt + file4.txt + folder2/ + folder3/ + file5.txt + */ + + file1Size = (await fs.stat(file1Path)).size + file2Size = (await fs.stat(file2Path)).size + file3Size = (await fs.stat(file3Path)).size + file4Size = (await fs.stat(file4Path)).size + file5Size = (await fs.stat(file5Path)).size + }) + + /** + * Artifact Creation Tests + */ + it('Create Artifact - Success', async () => { + const artifactName = 'valid-artifact-name' + const response = await uploadHttpClient.createArtifactInFileContainer( + artifactName + ) + expect(response.containerId).toEqual('13') + expect(response.size).toEqual(-1) + expect(response.signedContent).toEqual('false') + expect(response.fileContainerResourceUrl).toEqual( + `${getRuntimeUrl()}_apis/resources/Containers/13` + ) + expect(response.type).toEqual('actions_storage') + expect(response.name).toEqual(artifactName) + expect(response.url).toEqual( + `${getRuntimeUrl()}_apis/pipelines/1/runs/1/artifacts?artifactName=${artifactName}` + ) + }) + + it('Create Artifact - Failure', async () => { + const artifactName = 'invalid-artifact-name' + expect( + uploadHttpClient.createArtifactInFileContainer(artifactName) + ).rejects.toEqual( + new Error( + 'Unable to create a container for the artifact invalid-artifact-name' + ) + ) + }) + + /** + * Artifact Upload Tests + */ + it('Upload Artifact - Success', async () => { + /** + * Normally search.findFilesToUpload() would be used for providing information about what to upload. These tests however + * focuses solely on the upload APIs so searchResult[] will be hard-coded + */ + const artifactName = 'successful-artifact' + const uploadSpecification: UploadSpecification[] = [ + { + absoluteFilePath: file1Path, + uploadFilePath: `${artifactName}/file1.txt` + }, + { + absoluteFilePath: file2Path, + uploadFilePath: `${artifactName}/file2.txt` + }, + { + absoluteFilePath: file3Path, + uploadFilePath: `${artifactName}/folder1/file3.txt` + }, + { + absoluteFilePath: file4Path, + uploadFilePath: `${artifactName}/folder1/file4.txt` + }, + { + absoluteFilePath: file5Path, + uploadFilePath: `${artifactName}/folder1/folder2/folder3/file5.txt` + } + ] + + const expectedTotalSize = + file1Size + file2Size + file3Size + file4Size + file5Size + const uploadUrl = `${getRuntimeUrl()}_apis/resources/Containers/13` + const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer( + uploadUrl, + uploadSpecification + ) + expect(uploadResult.failedItems.length).toEqual(0) + expect(uploadResult.size).toEqual(expectedTotalSize) + }) + + it('Upload Artifact - Failed Single File Upload', async () => { + const uploadSpecification: UploadSpecification[] = [ + { + absoluteFilePath: file1Path, + uploadFilePath: `this-file-upload-will-fail` + } + ] + + const uploadUrl = `${getRuntimeUrl()}_apis/resources/Containers/13` + const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer( + uploadUrl, + uploadSpecification + ) + expect(uploadResult.failedItems.length).toEqual(1) + expect(uploadResult.size).toEqual(0) + }) + + it('Upload Artifact - Partial Upload Continue On Error', async () => { + const artifactName = 'partial-artifact' + const uploadSpecification: UploadSpecification[] = [ + { + absoluteFilePath: file1Path, + uploadFilePath: `${artifactName}/file1.txt` + }, + { + absoluteFilePath: file2Path, + uploadFilePath: `${artifactName}/file2.txt` + }, + { + absoluteFilePath: file3Path, + uploadFilePath: `${artifactName}/folder1/file3.txt` + }, + { + absoluteFilePath: file4Path, + uploadFilePath: `this-file-upload-will-fail` + }, + { + absoluteFilePath: file5Path, + uploadFilePath: `${artifactName}/folder1/folder2/folder3/file5.txt` + } + ] + + const expectedPartialSize = file1Size + file2Size + file4Size + file5Size + const uploadUrl = `${getRuntimeUrl()}_apis/resources/Containers/13` + const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer( + uploadUrl, + uploadSpecification, + {continueOnError: true} + ) + expect(uploadResult.failedItems.length).toEqual(1) + expect(uploadResult.size).toEqual(expectedPartialSize) + }) + + it('Upload Artifact - Partial Upload Fail Fast', async () => { + const artifactName = 'partial-artifact' + const uploadSpecification: UploadSpecification[] = [ + { + absoluteFilePath: file1Path, + uploadFilePath: `${artifactName}/file1.txt` + }, + { + absoluteFilePath: file2Path, + uploadFilePath: `${artifactName}/file2.txt` + }, + { + absoluteFilePath: file3Path, + uploadFilePath: `${artifactName}/folder1/file3.txt` + }, + { + absoluteFilePath: file4Path, + uploadFilePath: `this-file-upload-will-fail` + }, + { + absoluteFilePath: file5Path, + uploadFilePath: `${artifactName}/folder1/folder2/folder3/file5.txt` + } + ] + + const expectedPartialSize = file1Size + file2Size + file3Size + const uploadUrl = `${getRuntimeUrl()}_apis/resources/Containers/13` + const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer( + uploadUrl, + uploadSpecification, + {continueOnError: false} + ) + expect(uploadResult.failedItems.length).toEqual(2) + expect(uploadResult.size).toEqual(expectedPartialSize) + }) + + it('Upload Artifact - Failed upload with no options', async () => { + const artifactName = 'partial-artifact' + const uploadSpecification: UploadSpecification[] = [ + { + absoluteFilePath: file1Path, + uploadFilePath: `${artifactName}/file1.txt` + }, + { + absoluteFilePath: file2Path, + uploadFilePath: `${artifactName}/file2.txt` + }, + { + absoluteFilePath: file3Path, + uploadFilePath: `${artifactName}/folder1/file3.txt` + }, + { + absoluteFilePath: file4Path, + uploadFilePath: `this-file-upload-will-fail` + }, + { + absoluteFilePath: file5Path, + uploadFilePath: `${artifactName}/folder1/folder2/folder3/file5.txt` + } + ] + + const expectedPartialSize = file1Size + file2Size + file3Size + file5Size + const uploadUrl = `${getRuntimeUrl()}_apis/resources/Containers/13` + const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer( + uploadUrl, + uploadSpecification + ) + expect(uploadResult.failedItems.length).toEqual(1) + expect(uploadResult.size).toEqual(expectedPartialSize) + }) + + it('Upload Artifact - Failed upload with empty options', async () => { + const artifactName = 'partial-artifact' + const uploadSpecification: UploadSpecification[] = [ + { + absoluteFilePath: file1Path, + uploadFilePath: `${artifactName}/file1.txt` + }, + { + absoluteFilePath: file2Path, + uploadFilePath: `${artifactName}/file2.txt` + }, + { + absoluteFilePath: file3Path, + uploadFilePath: `${artifactName}/folder1/file3.txt` + }, + { + absoluteFilePath: file4Path, + uploadFilePath: `this-file-upload-will-fail` + }, + { + absoluteFilePath: file5Path, + uploadFilePath: `${artifactName}/folder1/folder2/folder3/file5.txt` + } + ] + + const expectedPartialSize = file1Size + file2Size + file3Size + file5Size + const uploadUrl = `${getRuntimeUrl()}_apis/resources/Containers/13` + const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer( + uploadUrl, + uploadSpecification, + {} + ) + expect(uploadResult.failedItems.length).toEqual(1) + expect(uploadResult.size).toEqual(expectedPartialSize) + }) + + /** + * Artifact Association Tests + */ + it('Associate Artifact - Success', async () => { + expect(async () => { + uploadHttpClient.patchArtifactSize(130, 'my-artifact') + }).not.toThrow() + }) + + it('Associate Artifact - Not Found', async () => { + expect( + uploadHttpClient.patchArtifactSize(100, 'non-existent-artifact') + ).rejects.toThrow( + 'An Artifact with the name non-existent-artifact was not found' + ) + }) + + it('Associate Artifact - Error', async () => { + expect( + uploadHttpClient.patchArtifactSize(-2, 'my-artifact') + ).rejects.toThrow('Unable to finish uploading artifact my-artifact') + }) + + /** + * Helpers used to setup mocking for the HttpClient + */ + async function emptyMockReadBody(): Promise { + return new Promise(resolve => { + resolve() + }) + } + + function setupHttpClientMock(): void { + /** + * Mocks Post calls that are used during Artifact Creation tests + * + * Simulates success and non-success status codes depending on the artifact name along with an appropriate + * payload that represents an expected response + */ + jest + .spyOn(HttpClient.prototype, 'post') + .mockImplementation(async (requestdata, data) => { + // parse the input data and use the provided artifact name as part of the response + const inputData = JSON.parse(data) + const mockMessage = new http.IncomingMessage(new net.Socket()) + let mockReadBody = emptyMockReadBody + + if (inputData.Name === 'invalid-artifact-name') { + mockMessage.statusCode = 400 + } else { + mockMessage.statusCode = 201 + const response: ArtifactResponse = { + containerId: '13', + size: -1, + signedContent: 'false', + fileContainerResourceUrl: `${getRuntimeUrl()}_apis/resources/Containers/13`, + type: 'actions_storage', + name: inputData.Name, + url: `${getRuntimeUrl()}_apis/pipelines/1/runs/1/artifacts?artifactName=${ + inputData.Name + }` + } + const returnData: string = JSON.stringify(response, null, 2) + mockReadBody = async function(): Promise { + return new Promise(resolve => { + resolve(returnData) + }) + } + } + return new Promise(resolve => { + resolve({ + message: mockMessage, + readBody: mockReadBody + }) + }) + }) + + /** + * Mocks SendStream calls that are made during Artifact Upload tests + * + * A 500 response is used to simulate a failed upload stream. The uploadUrl can be set to + * include 'fail' to specify that the upload should fail + */ + jest + .spyOn(HttpClient.prototype, 'sendStream') + .mockImplementation(async (verb, requestUrl) => { + const mockMessage = new http.IncomingMessage(new net.Socket()) + mockMessage.statusCode = 200 + if (requestUrl.includes('fail')) { + mockMessage.statusCode = 500 + } + + return new Promise(resolve => { + resolve({ + message: mockMessage, + readBody: emptyMockReadBody + }) + }) + }) + + /** + * Mocks Patch calls that are made during Artifact Association tests + * + * Simulates success and non-success status codes depending on the input size along with an appropriate + * payload that represents an expected response + */ + jest + .spyOn(HttpClient.prototype, 'patch') + .mockImplementation(async (requestdata, data) => { + const inputData = JSON.parse(data) + const mockMessage = new http.IncomingMessage(new net.Socket()) + + // Get the name from the end of requestdata. Will be something like https://www.example.com/_apis/pipelines/workflows/15/artifacts?api-version=6.0-preview&artifactName=my-artifact + const artifactName = requestdata.split('=')[2] + let mockReadBody = emptyMockReadBody + if (inputData.Size < 1) { + mockMessage.statusCode = 400 + } else if (artifactName === 'non-existent-artifact') { + mockMessage.statusCode = 404 + } else { + mockMessage.statusCode = 200 + const response: PatchArtifactSizeSuccessResponse = { + containerId: 13, + size: inputData.Size, + signedContent: 'false', + type: 'actions_storage', + name: artifactName, + url: `${getRuntimeUrl()}_apis/pipelines/1/runs/1/artifacts?artifactName=${artifactName}`, + uploadUrl: `${getRuntimeUrl()}_apis/resources/Containers/13` + } + const returnData: string = JSON.stringify(response, null, 2) + mockReadBody = async function(): Promise { + return new Promise(resolve => { + resolve(returnData) + }) + } + } + return new Promise(resolve => { + resolve({ + message: mockMessage, + readBody: mockReadBody + }) + }) + }) + } +}) diff --git a/packages/artifact/__tests__/util.test.ts b/packages/artifact/__tests__/util.test.ts new file mode 100644 index 0000000000..e71652b3c2 --- /dev/null +++ b/packages/artifact/__tests__/util.test.ts @@ -0,0 +1,99 @@ +import * as utils from '../src/internal-utils' +import * as core from '@actions/core' +import {HttpCodes} from '@actions/http-client' +import {getRuntimeUrl, getWorkFlowRunId} from '../src/internal-config-variables' + +jest.mock('../src/internal-config-variables') + +describe('Utils', () => { + beforeAll(() => { + // mock all output so that there is less noise when running tests + jest.spyOn(console, 'log').mockImplementation(() => {}) + jest.spyOn(core, 'debug').mockImplementation(() => {}) + jest.spyOn(core, 'info').mockImplementation(() => {}) + jest.spyOn(core, 'warning').mockImplementation(() => {}) + }) + + it('Check Artifact Name for any invalid characters', () => { + const invalidNames = [ + 'my\\artifact', + 'my/artifact', + 'my"artifact', + 'my:artifact', + 'myartifact', + 'my|artifact', + 'my*artifact', + 'my?artifact', + 'my artifact', + '' + ] + for (const invalidName of invalidNames) { + expect(() => { + utils.checkArtifactName(invalidName) + }).toThrow() + } + + const validNames = [ + 'my-normal-artifact', + 'myNormalArtifact', + 'm¥ñðrmålÄr†ï£å¢†' + ] + for (const validName of validNames) { + expect(() => { + utils.checkArtifactName(validName) + }).not.toThrow() + } + }) + + it('Test constructing artifact URL', () => { + const runtimeUrl = getRuntimeUrl() + const runId = getWorkFlowRunId() + const artifactUrl = utils.getArtifactUrl() + expect(artifactUrl).toEqual( + `${runtimeUrl}_apis/pipelines/workflows/${runId}/artifacts?api-version=${utils.getApiVersion()}` + ) + }) + + it('Test constructing headers with all optional parameters', () => { + const type = 'application/json' + const size = 24 + const range = 'bytes 0-199/200' + const options = utils.getRequestOptions(type, size, range) + expect(Object.keys(options).length).toEqual(4) + expect(options['Accept']).toEqual( + `${type};api-version=${utils.getApiVersion()}` + ) + expect(options['Content-Type']).toEqual(type) + expect(options['Content-Length']).toEqual(size) + expect(options['Content-Range']).toEqual(range) + }) + + it('Test constructing headers with only required parameter', () => { + const options = utils.getRequestOptions() + expect(Object.keys(options).length).toEqual(1) + expect(options['Accept']).toEqual( + `application/json;api-version=${utils.getApiVersion()}` + ) + }) + + it('Test Success Status Code', () => { + expect(utils.isSuccessStatusCode(HttpCodes.OK)).toEqual(true) + expect(utils.isSuccessStatusCode(201)).toEqual(true) + expect(utils.isSuccessStatusCode(299)).toEqual(true) + expect(utils.isSuccessStatusCode(HttpCodes.NotFound)).toEqual(false) + expect(utils.isSuccessStatusCode(HttpCodes.BadGateway)).toEqual(false) + expect(utils.isSuccessStatusCode(HttpCodes.Forbidden)).toEqual(false) + }) + + it('Test Retry Status Code', () => { + expect(utils.isRetryableStatusCode(HttpCodes.BadGateway)).toEqual(true) + expect(utils.isRetryableStatusCode(HttpCodes.ServiceUnavailable)).toEqual( + true + ) + expect(utils.isRetryableStatusCode(HttpCodes.GatewayTimeout)).toEqual(true) + expect(utils.isRetryableStatusCode(HttpCodes.OK)).toEqual(false) + expect(utils.isRetryableStatusCode(HttpCodes.NotFound)).toEqual(false) + expect(utils.isRetryableStatusCode(HttpCodes.Forbidden)).toEqual(false) + }) +}) diff --git a/packages/artifact/package-lock.json b/packages/artifact/package-lock.json new file mode 100644 index 0000000000..d8cd2feb44 --- /dev/null +++ b/packages/artifact/package-lock.json @@ -0,0 +1,26 @@ +{ + "name": "@actions/artifact", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@actions/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.1.tgz", + "integrity": "sha512-xD+CQd9p4lU7ZfRqmUcbJpqR+Ss51rJRVeXMyOLrZQImN9/8Sy/BEUBnHO/UKD3z03R686PVTLfEPmkropGuLw==" + }, + "@actions/http-client": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.6.tgz", + "integrity": "sha512-LGmio4w98UyGX33b/W6V6Nx/sQHRXZ859YlMkn36wPsXPB82u8xTVlA/Dq2DXrm6lEq9RVmisRJa1c+HETAIJA==", + "requires": { + "tunnel": "0.0.6" + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + } + } +} diff --git a/packages/artifact/package.json b/packages/artifact/package.json new file mode 100644 index 0000000000..0f38fa9b67 --- /dev/null +++ b/packages/artifact/package.json @@ -0,0 +1,42 @@ +{ + "name": "@actions/artifact", + "version": "0.1.0", + "preview": true, + "description": "Actions artifact lib", + "keywords": [ + "github", + "actions", + "artifact" + ], + "homepage": "https://github.com/actions/toolkit/tree/master/packages/artifact", + "license": "MIT", + "main": "lib/artifact.js", + "types": "lib/artifact.d.ts", + "directories": { + "lib": "lib", + "test": "__tests__" + }, + "files": [ + "lib" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/actions/toolkit.git", + "directory": "packages/artifact" + }, + "scripts": { + "audit-moderate": "npm install && npm audit --audit-level=moderate", + "test": "echo \"Error: run tests from root\" && exit 1", + "tsc": "tsc" + }, + "bugs": { + "url": "https://github.com/actions/toolkit/issues" + }, + "dependencies": { + "@actions/core": "^1.2.1", + "@actions/http-client": "^1.0.6" + } +} diff --git a/packages/artifact/src/__mocks__/internal-config-variables.ts b/packages/artifact/src/__mocks__/internal-config-variables.ts new file mode 100644 index 0000000000..5b5536173c --- /dev/null +++ b/packages/artifact/src/__mocks__/internal-config-variables.ts @@ -0,0 +1,30 @@ +/** + * Mocks default limits for easier testing + */ +export function getUploadFileConcurrency(): number { + return 1 +} + +export function getUploadChunkConcurrency(): number { + return 1 +} + +export function getUploadChunkSize(): number { + return 4 * 1024 * 1024 // 4 MB Chunks +} +/** + * Mocks the 'ACTIONS_RUNTIME_TOKEN', 'ACTIONS_RUNTIME_URL' and 'GITHUB_RUN_ID' env variables + * that are only available from a node context on the runner. This allows for tests to run + * locally without the env variables actually being set + */ +export function getRuntimeToken(): string { + return 'totally-valid-token' +} + +export function getRuntimeUrl(): string { + return 'https://www.example.com/' +} + +export function getWorkFlowRunId(): string { + return '15' +} diff --git a/packages/artifact/src/artifact-client.ts b/packages/artifact/src/artifact-client.ts new file mode 100644 index 0000000000..a7ddc69d9c --- /dev/null +++ b/packages/artifact/src/artifact-client.ts @@ -0,0 +1,9 @@ +import {ArtifactClient, DefaultArtifactClient} from './internal-artifact-client' +export {ArtifactClient} + +/** + * Constructs an ArtifactClient + */ +export function create(): ArtifactClient { + return DefaultArtifactClient.create() +} diff --git a/packages/artifact/src/internal-artifact-client.ts b/packages/artifact/src/internal-artifact-client.ts new file mode 100644 index 0000000000..bf431861e8 --- /dev/null +++ b/packages/artifact/src/internal-artifact-client.ts @@ -0,0 +1,124 @@ +import * as core from '@actions/core' +import { + UploadSpecification, + getUploadSpecification +} from './internal-upload-specification' +import { + createArtifactInFileContainer, + uploadArtifactToFileContainer, + patchArtifactSize +} from './internal-upload-http-client' +import {UploadResponse} from './internal-upload-response' +import {UploadOptions} from './internal-upload-options' +import {checkArtifactName} from './internal-utils' + +export {UploadResponse, UploadOptions} + +export interface ArtifactClient { + /** + * Uploads an artifact + * + * @param name the name of the artifact, required + * @param files a list of absolute or relative paths that denote what files should be uploaded + * @param rootDirectory an absolute or relative file path that denotes the root parent directory of the files being uploaded + * @param options extra options for customizing the upload behavior + * @returns single UploadInfo object + */ + uploadArtifact( + name: string, + files: string[], + rootDirectory: string, + options?: UploadOptions + ): Promise +} + +export class DefaultArtifactClient implements ArtifactClient { + /** + * Constructs a DefaultArtifactClient + */ + static create(): DefaultArtifactClient { + return new DefaultArtifactClient() + } + + /** + * Uploads an artifact + */ + async uploadArtifact( + name: string, + files: string[], + rootDirectory: string, + options?: UploadOptions | undefined + ): Promise { + checkArtifactName(name) + + // Get specification for the files being uploaded + const uploadSpecification: UploadSpecification[] = getUploadSpecification( + name, + rootDirectory, + files + ) + const uploadResponse: UploadResponse = { + artifactName: name, + artifactItems: [], + size: 0, + failedItems: [] + } + + if (uploadSpecification.length === 0) { + core.warning(`No files found that can be uploaded`) + } else { + // Create an entry for the artifact in the file container + const response = await createArtifactInFileContainer(name) + if (!response.fileContainerResourceUrl) { + core.debug(response.toString()) + throw new Error( + 'No URL provided by the Artifact Service to upload an artifact to' + ) + } + core.debug(`Upload Resource URL: ${response.fileContainerResourceUrl}`) + + // Upload each of the files that were found concurrently + const uploadResult = await uploadArtifactToFileContainer( + response.fileContainerResourceUrl, + uploadSpecification, + options + ) + + //Update the size of the artifact to indicate we are done uploading + await patchArtifactSize(uploadResult.size, name) + + core.info( + `Finished uploading artifact ${name}. Reported size is ${uploadResult.size} bytes. There were ${uploadResult.failedItems.length} items that failed to upload` + ) + + uploadResponse.artifactItems = uploadSpecification.map( + item => item.absoluteFilePath + ) + uploadResponse.size = uploadResult.size + uploadResponse.failedItems = uploadResult.failedItems + } + return uploadResponse + } + + /* + Downloads a single artifact associated with a run + + export async function downloadArtifact( + name: string, + path?: string, + options?: DownloadOptions + ): Promise { + + TODO + } + + Downloads all artifacts associated with a run. Because there are multiple artifacts being downloaded, a folder will be created for each one in the specified or default directory + + export async function downloadAllArtifacts( + path?: string + ): Promise{ + + TODO + } + */ +} diff --git a/packages/artifact/src/internal-config-variables.ts b/packages/artifact/src/internal-config-variables.ts new file mode 100644 index 0000000000..aef9d34c0e --- /dev/null +++ b/packages/artifact/src/internal-config-variables.ts @@ -0,0 +1,35 @@ +export function getUploadFileConcurrency(): number { + return 2 +} + +export function getUploadChunkConcurrency(): number { + return 1 +} + +export function getUploadChunkSize(): number { + return 4 * 1024 * 1024 // 4 MB Chunks +} + +export function getRuntimeToken(): string { + const token = process.env['ACTIONS_RUNTIME_TOKEN'] + if (!token) { + throw new Error('Unable to get ACTIONS_RUNTIME_TOKEN env variable') + } + return token +} + +export function getRuntimeUrl(): string { + const runtimeUrl = process.env['ACTIONS_RUNTIME_URL'] + if (!runtimeUrl) { + throw new Error('Unable to get ACTIONS_RUNTIME_URL env variable') + } + return runtimeUrl +} + +export function getWorkFlowRunId(): string { + const workFlowRunId = process.env['GITHUB_RUN_ID'] + if (!workFlowRunId) { + throw new Error('Unable to get GITHUB_RUN_ID env variable') + } + return workFlowRunId +} diff --git a/packages/artifact/src/internal-contracts.ts b/packages/artifact/src/internal-contracts.ts new file mode 100644 index 0000000000..8124add4a7 --- /dev/null +++ b/packages/artifact/src/internal-contracts.ts @@ -0,0 +1,33 @@ +export interface ArtifactResponse { + containerId: string + size: number + signedContent: string + fileContainerResourceUrl: string + type: string + name: string + url: string +} + +export interface CreateArtifactParameters { + Type: string + Name: string +} + +export interface PatchArtifactSize { + Size: number +} + +export interface PatchArtifactSizeSuccessResponse { + containerId: number + size: number + signedContent: string + type: string + name: string + url: string + uploadUrl: string +} + +export interface UploadResults { + size: number + failedItems: string[] +} diff --git a/packages/artifact/src/internal-download-options.ts b/packages/artifact/src/internal-download-options.ts new file mode 100644 index 0000000000..e767557a27 --- /dev/null +++ b/packages/artifact/src/internal-download-options.ts @@ -0,0 +1,7 @@ +export interface DownloadOptions { + /** + * Specifies if a folder is created for the artifact that is downloaded (contents downloaded into this folder), + * defaults to false if not specified + * */ + createArtifactFolder?: boolean +} diff --git a/packages/artifact/src/internal-download-response.ts b/packages/artifact/src/internal-download-response.ts new file mode 100644 index 0000000000..e506967251 --- /dev/null +++ b/packages/artifact/src/internal-download-response.ts @@ -0,0 +1,11 @@ +export interface DownloadResponse { + /** + * The name of the artifact that was downloaded + */ + artifactName: string + + /** + * The full Path to where the artifact was downloaded + */ + downloadPath: string +} diff --git a/packages/artifact/src/internal-upload-http-client.ts b/packages/artifact/src/internal-upload-http-client.ts new file mode 100644 index 0000000000..79646c16cf --- /dev/null +++ b/packages/artifact/src/internal-upload-http-client.ts @@ -0,0 +1,322 @@ +import {debug, warning, info} from '@actions/core' +import {HttpClientResponse, HttpClient} from '@actions/http-client/index' +import {IHttpClientResponse} from '@actions/http-client/interfaces' +import { + ArtifactResponse, + CreateArtifactParameters, + PatchArtifactSize, + UploadResults +} from './internal-contracts' +import * as fs from 'fs' +import {UploadSpecification} from './internal-upload-specification' +import {UploadOptions} from './internal-upload-options' +import {URL} from 'url' +import { + createHttpClient, + getArtifactUrl, + getContentRange, + getRequestOptions, + isRetryableStatusCode, + isSuccessStatusCode +} from './internal-utils' +import { + getUploadChunkConcurrency, + getUploadChunkSize, + getUploadFileConcurrency +} from './internal-config-variables' + +/** + * Creates a file container for the new artifact in the remote blob storage/file service + * @param {string} artifactName Name of the artifact being created + * @returns The response from the Artifact Service if the file container was successfully created + */ +export async function createArtifactInFileContainer( + artifactName: string +): Promise { + const parameters: CreateArtifactParameters = { + Type: 'actions_storage', + Name: artifactName + } + const data: string = JSON.stringify(parameters, null, 2) + const artifactUrl = getArtifactUrl() + const client = createHttpClient() + const requestOptions = getRequestOptions('application/json') + + const rawResponse = await client.post(artifactUrl, data, requestOptions) + const body: string = await rawResponse.readBody() + + if (isSuccessStatusCode(rawResponse.message.statusCode) && body) { + return JSON.parse(body) + } else { + // eslint-disable-next-line no-console + console.log(rawResponse) + throw new Error( + `Unable to create a container for the artifact ${artifactName}` + ) + } +} + +/** + * Concurrently upload all of the files in chunks + * @param {string} uploadUrl Base Url for the artifact that was created + * @param {SearchResult[]} filesToUpload A list of information about the files being uploaded + * @returns The size of all the files uploaded in bytes + */ +export async function uploadArtifactToFileContainer( + uploadUrl: string, + filesToUpload: UploadSpecification[], + options?: UploadOptions +): Promise { + const client = createHttpClient() + const FILE_CONCURRENCY = getUploadFileConcurrency() + const CHUNK_CONCURRENCY = getUploadChunkConcurrency() + const MAX_CHUNK_SIZE = getUploadChunkSize() + debug( + `File Concurrency: ${FILE_CONCURRENCY}, Chunk Concurrency: ${CHUNK_CONCURRENCY} and Chunk Size: ${MAX_CHUNK_SIZE}` + ) + + const parameters: UploadFileParameters[] = [] + + // by default, file uploads will continue if there is an error unless specified differently in the options + let continueOnError = true + if (options) { + if (options.continueOnError === false) { + continueOnError = false + } + } + + // Prepare the necessary parameters to upload all the files + for (const file of filesToUpload) { + const resourceUrl = new URL(uploadUrl) + resourceUrl.searchParams.append('itemPath', file.uploadFilePath) + parameters.push({ + file: file.absoluteFilePath, + resourceUrl: resourceUrl.toString(), + restClient: client, + concurrency: CHUNK_CONCURRENCY, + maxChunkSize: MAX_CHUNK_SIZE, + continueOnError + }) + } + + const parallelUploads = [...new Array(FILE_CONCURRENCY).keys()] + const failedItemsToReport: string[] = [] + let uploadedFiles = 0 + let fileSizes = 0 + let abortPendingFileUploads = false + + // Only allow a certain amount of files to be uploaded at once, this is done to reduce potential errors + await Promise.all( + parallelUploads.map(async () => { + while (uploadedFiles < filesToUpload.length) { + const currentFileParameters = parameters[uploadedFiles] + uploadedFiles += 1 + if (abortPendingFileUploads) { + failedItemsToReport.push(currentFileParameters.file) + continue + } + + const uploadFileResult = await uploadFileAsync(currentFileParameters) + fileSizes += uploadFileResult.successfulUploadSize + if (uploadFileResult.isSuccess === false) { + failedItemsToReport.push(currentFileParameters.file) + if (!continueOnError) { + // Existing uploads will be able to finish however all pending uploads will fail fast + abortPendingFileUploads = true + } + } + } + }) + ) + + info(`Total size of all the files uploaded is ${fileSizes} bytes`) + return { + size: fileSizes, + failedItems: failedItemsToReport + } +} + +/** + * Asynchronously uploads a file. If the file is bigger than the max chunk size it will be uploaded via multiple calls + * @param {UploadFileParameters} parameters Information about the file that needs to be uploaded + * @returns The size of the file that was uploaded in bytes along with any failed uploads + */ +async function uploadFileAsync( + parameters: UploadFileParameters +): Promise { + const fileSize: number = fs.statSync(parameters.file).size + const parallelUploads = [...new Array(parameters.concurrency).keys()] + let offset = 0 + let isUploadSuccessful = true + let failedChunkSizes = 0 + let abortFileUpload = false + + await Promise.all( + parallelUploads.map(async () => { + while (offset < fileSize) { + const chunkSize = Math.min(fileSize - offset, parameters.maxChunkSize) + if (abortFileUpload) { + // if we don't want to continue on error, any pending upload chunk will be marked as failed + failedChunkSizes += chunkSize + continue + } + + const start = offset + const end = offset + chunkSize - 1 + offset += parameters.maxChunkSize + const chunk: NodeJS.ReadableStream = fs.createReadStream( + parameters.file, + { + start, + end, + autoClose: false + } + ) + + const result = await uploadChunk( + parameters.restClient, + parameters.resourceUrl, + chunk, + start, + end, + fileSize + ) + + if (!result) { + /** + * Chunk failed to upload, report as failed and do not continue uploading any more chunks for the file. It is possible that part of a chunk was + * successfully uploaded so the server may report a different size for what was uploaded + **/ + isUploadSuccessful = false + failedChunkSizes += chunkSize + warning(`Aborting upload for ${parameters.file} due to failure`) + abortFileUpload = true + } + } + }) + ) + return { + isSuccess: isUploadSuccessful, + successfulUploadSize: fileSize - failedChunkSizes + } +} + +/** + * Uploads a chunk of an individual file to the specified resourceUrl. If the upload fails and the status code + * indicates a retryable status, we try to upload the chunk as well + * @param {HttpClient} restClient RestClient that will be making the appropriate HTTP call + * @param {string} resourceUrl Url of the resource that the chunk will be uploaded to + * @param {NodeJS.ReadableStream} data Stream of the file that will be uploaded + * @param {number} start Starting byte index of file that the chunk belongs to + * @param {number} end Ending byte index of file that the chunk belongs to + * @param {number} totalSize Total size of the file in bytes that is being uploaded + * @returns if the chunk was successfully uploaded + */ +async function uploadChunk( + restClient: HttpClient, + resourceUrl: string, + data: NodeJS.ReadableStream, + start: number, + end: number, + totalSize: number +): Promise { + info( + `Uploading chunk of size ${end - + start + + 1} bytes at offset ${start} with content range: ${getContentRange( + start, + end, + totalSize + )}` + ) + + const requestOptions = getRequestOptions( + 'application/octet-stream', + totalSize, + getContentRange(start, end, totalSize) + ) + + const uploadChunkRequest = async (): Promise => { + return await restClient.sendStream('PUT', resourceUrl, data, requestOptions) + } + + const response = await uploadChunkRequest() + if (isSuccessStatusCode(response.message.statusCode)) { + debug( + `Chunk for ${start}:${end} was successfully uploaded to ${resourceUrl}` + ) + return true + } else if (isRetryableStatusCode(response.message.statusCode)) { + info( + `Received http ${response.message.statusCode} during chunk upload, will retry at offset ${start} after 10 seconds.` + ) + await new Promise(resolve => setTimeout(resolve, 10000)) + const retryResponse = await uploadChunkRequest() + if (isSuccessStatusCode(retryResponse.message.statusCode)) { + return true + } else { + info(`Unable to upload chunk even after retrying`) + // eslint-disable-next-line no-console + console.log(response) + return false + } + } + + // Upload must have failed spectacularly somehow, log full result for diagnostic purposes + // eslint-disable-next-line no-console + console.log(response) + return false +} + +/** + * Updates the size of the artifact from -1 which was initially set when the container was first created for the artifact. + * Updating the size indicates that we are done uploading all the contents of the artifact. A server side check will be run + * to check that the artifact size is correct for billing purposes + */ +export async function patchArtifactSize( + size: number, + artifactName: string +): Promise { + const client = createHttpClient() + const requestOptions = getRequestOptions('application/json') + const resourceUrl = new URL(getArtifactUrl()) + resourceUrl.searchParams.append('artifactName', artifactName) + + const parameters: PatchArtifactSize = {Size: size} + const data: string = JSON.stringify(parameters, null, 2) + debug(`URL is ${resourceUrl.toString()}`) + + const rawResponse: HttpClientResponse = await client.patch( + resourceUrl.toString(), + data, + requestOptions + ) + const body: string = await rawResponse.readBody() + + if (isSuccessStatusCode(rawResponse.message.statusCode)) { + debug( + `Artifact ${artifactName} has been successfully uploaded, total size ${size}` + ) + debug(body) + } else if (rawResponse.message.statusCode === 404) { + throw new Error(`An Artifact with the name ${artifactName} was not found`) + } else { + // eslint-disable-next-line no-console + console.log(body) + throw new Error(`Unable to finish uploading artifact ${artifactName}`) + } +} + +interface UploadFileParameters { + file: string + resourceUrl: string + restClient: HttpClient + concurrency: number + maxChunkSize: number + continueOnError: boolean +} + +interface UploadFileResult { + isSuccess: boolean + successfulUploadSize: number +} diff --git a/packages/artifact/src/internal-upload-options.ts b/packages/artifact/src/internal-upload-options.ts new file mode 100644 index 0000000000..63d4febe8f --- /dev/null +++ b/packages/artifact/src/internal-upload-options.ts @@ -0,0 +1,18 @@ +export interface UploadOptions { + /** + * Indicates if the artifact upload should continue if file or chunk fails to upload from any error. + * If there is a error during upload, a partial artifact will always be associated and available for + * download at the end. The size reported will be the amount of storage that the user or org will be + * charged for the partial artifact. Defaults to true if not specified + * + * If set to false, and an error is encountered, all other uploads will stop and any files or chunks + * that were queued will not be attempted to be uploaded. The partial artifact available will only + * include files and chunks up until the failure + * + * If set to true and an error is encountered, the failed file will be skipped and ignored and all + * other queued files will be attempted to be uploaded. The partial artifact at the end will have all + * files with the exception of the problematic files(s)/chunks(s) that failed to upload + * + */ + continueOnError?: boolean +} diff --git a/packages/artifact/src/internal-upload-response.ts b/packages/artifact/src/internal-upload-response.ts new file mode 100644 index 0000000000..f40379edad --- /dev/null +++ b/packages/artifact/src/internal-upload-response.ts @@ -0,0 +1,22 @@ +export interface UploadResponse { + /** + * The name of the artifact that was uploaded + */ + artifactName: string + + /** + * A list of all items that are meant to be uploaded as part of the artifact + */ + artifactItems: string[] + + /** + * Total size of the artifact in bytes that was uploaded + */ + size: number + + /** + * A list of items that were not uploaded as part of the artifact (includes queued items that were not uploaded if + * continueOnError is set to false). This is a subset of artifactItems. + */ + failedItems: string[] +} diff --git a/packages/artifact/src/internal-upload-specification.ts b/packages/artifact/src/internal-upload-specification.ts new file mode 100644 index 0000000000..0df866662b --- /dev/null +++ b/packages/artifact/src/internal-upload-specification.ts @@ -0,0 +1,92 @@ +import * as fs from 'fs' +import {debug} from '@actions/core' +import {join, normalize, resolve} from 'path' +import {checkArtifactName} from './internal-utils' + +export interface UploadSpecification { + absoluteFilePath: string + uploadFilePath: string +} + +/** + * Creates a specification that describes how each file that is part of the artifact will be uploaded + * @param artifactName the name of the artifact being uploaded. Used during upload to denote where the artifact is stored on the server + * @param rootDirectory an absolute file path that denotes the path that should be removed from the beginning of each artifact file + * @param artifactFiles a list of absolute file paths that denote what should be uploaded as part of the artifact + */ +export function getUploadSpecification( + artifactName: string, + rootDirectory: string, + artifactFiles: string[] +): UploadSpecification[] { + checkArtifactName(artifactName) + + const specifications: UploadSpecification[] = [] + + if (!fs.existsSync(rootDirectory)) { + throw new Error(`Provided rootDirectory ${rootDirectory} does not exist`) + } + if (!fs.lstatSync(rootDirectory).isDirectory()) { + throw new Error( + `Provided rootDirectory ${rootDirectory} is not a valid directory` + ) + } + // Normalize and resolve, this allows for either absolute or relative paths to be used + rootDirectory = normalize(rootDirectory) + rootDirectory = resolve(rootDirectory) + + /* + Example to demonstrate behavior + + Input: + artifactName: my-artifact + rootDirectory: '/home/user/files/plz-upload' + artifactFiles: [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ] + + Output: + specifications: [ + ['/home/user/files/plz-upload/file1.txt', 'my-artifact/file1.txt'], + ['/home/user/files/plz-upload/file1.txt', 'my-artifact/file2.txt'], + ['/home/user/files/plz-upload/file1.txt', 'my-artifact/dir/file3.txt'] + ] + */ + for (let file of artifactFiles) { + if (!fs.existsSync(file)) { + throw new Error(`File ${file} does not exist`) + } + + if (!fs.lstatSync(file).isDirectory()) { + // Normalize and resolve, this allows for either absolute or relative paths to be used + file = normalize(file) + file = resolve(file) + if (!file.startsWith(rootDirectory)) { + throw new Error( + `The rootDirectory: ${rootDirectory} is not a parent directory of the file: ${file}` + ) + } + + /* + uploadFilePath denotes where the file will be uploaded in the file container on the server. During a run, if multiple artifacts are uploaded, they will all + be saved in the same container. The artifact name is used as the root directory in the container to separate and distinguish uploaded artifacts + + path.join handles all the following cases and would return 'artifact-name/file-to-upload.txt + join('artifact-name/', 'file-to-upload.txt') + join('artifact-name/', '/file-to-upload.txt') + join('artifact-name', 'file-to-upload.txt') + join('artifact-name', '/file-to-upload.txt') + */ + specifications.push({ + absoluteFilePath: file, + uploadFilePath: join(artifactName, file.replace(rootDirectory, '')) + }) + } else { + // Directories are rejected by the server during upload + debug(`Removing ${file} from rawSearchResults because it is a directory`) + } + } + return specifications +} diff --git a/packages/artifact/src/internal-utils.ts b/packages/artifact/src/internal-utils.ts new file mode 100644 index 0000000000..afc2434270 --- /dev/null +++ b/packages/artifact/src/internal-utils.ts @@ -0,0 +1,115 @@ +import {debug} from '@actions/core' +import {HttpCodes, HttpClient} from '@actions/http-client' +import {BearerCredentialHandler} from '@actions/http-client/auth' +import {IHeaders} from '@actions/http-client/interfaces' +import { + getRuntimeToken, + getRuntimeUrl, + getWorkFlowRunId +} from './internal-config-variables' + +/** + * Parses a env variable that is a number + */ +export function parseEnvNumber(key: string): number | undefined { + const value = Number(process.env[key]) + if (Number.isNaN(value) || value < 0) { + return undefined + } + return value +} + +/** + * Various utility functions to help with the necessary API calls + */ +export function getApiVersion(): string { + return '6.0-preview' +} + +export function isSuccessStatusCode(statusCode?: number): boolean { + if (!statusCode) { + return false + } + return statusCode >= 200 && statusCode < 300 +} + +export function isRetryableStatusCode(statusCode?: number): boolean { + if (!statusCode) { + return false + } + + const retryableStatusCodes = [ + HttpCodes.BadGateway, + HttpCodes.ServiceUnavailable, + HttpCodes.GatewayTimeout + ] + return retryableStatusCodes.includes(statusCode) +} + +export function getContentRange( + start: number, + end: number, + total: number +): string { + // Format: `bytes start-end/fileSize + // start and end are inclusive + // For a 200 byte chunk starting at byte 0: + // Content-Range: bytes 0-199/200 + return `bytes ${start}-${end}/${total}` +} + +export function getRequestOptions( + contentType?: string, + contentLength?: number, + contentRange?: string +): IHeaders { + const requestOptions: IHeaders = { + Accept: `application/json;api-version=${getApiVersion()}` + } + if (contentType) { + requestOptions['Content-Type'] = contentType + } + if (contentLength) { + requestOptions['Content-Length'] = contentLength + } + if (contentRange) { + requestOptions['Content-Range'] = contentRange + } + return requestOptions +} + +export function createHttpClient(): HttpClient { + return new HttpClient('action/artifact', [ + new BearerCredentialHandler(getRuntimeToken()) + ]) +} + +export function getArtifactUrl(): string { + const artifactUrl = `${getRuntimeUrl()}_apis/pipelines/workflows/${getWorkFlowRunId()}/artifacts?api-version=${getApiVersion()}` + debug(`Artifact Url: ${artifactUrl}`) + return artifactUrl +} + +/** + * Invalid characters that cannot be in the artifact name or an uploaded file. Will be rejected + * from the server if attempted to be sent over. These characters are not allowed due to limitations with certain + * file systems such as NTFS. To maintain platform-agnostic behavior, all characters that are not supported by an + * individual filesystem/platform will not be supported on all fileSystems/platforms + */ +const invalidCharacters = ['\\', '/', '"', ':', '<', '>', '|', '*', '?', ' '] + +/** + * Scans the name of the item being uploaded to make sure there are no illegal characters + */ +export function checkArtifactName(name: string): void { + if (!name) { + throw new Error(`Artifact name: ${name}, is incorrectly provided`) + } + for (const invalidChar of invalidCharacters) { + if (name.includes(invalidChar)) { + throw new Error( + `Artifact name is not valid: ${name}. Contains character: "${invalidChar}". Invalid characters include: ${invalidCharacters.toString()}.` + ) + } + } +} diff --git a/packages/artifact/tsconfig.json b/packages/artifact/tsconfig.json new file mode 100644 index 0000000000..a8b812a6ff --- /dev/null +++ b/packages/artifact/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./lib", + "rootDir": "./src" + }, + "include": [ + "./src" + ] +} \ No newline at end of file From 2dcdf049a83b58db729571f2d8337b2195f339a6 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Wed, 12 Feb 2020 09:26:59 -0500 Subject: [PATCH 087/192] proxy guidance --- README.md | 6 ++++++ docs/proxy-support.md | 10 ++++++++++ 2 files changed, 16 insertions(+) create mode 100644 docs/proxy-support.md diff --git a/README.md b/README.md index a4ec4b70d4..c58f1b130e 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,12 @@ Problem Matchers are a way to scan the output of actions for a specified regex p

+:warning: [Proxy Server Support](docs/proxy-support.md) + +Self-hosted runners can be configured to run behind proxy servers. +
+
+

Hello World JavaScript Action

Illustrates how to create a simple hello world javascript action. diff --git a/docs/proxy-support.md b/docs/proxy-support.md new file mode 100644 index 0000000000..b00ef0a3cc --- /dev/null +++ b/docs/proxy-support.md @@ -0,0 +1,10 @@ +# Proxy Server Support + +Self-hosted runners [can be configured](https://help.github.com/en/actions/hosting-your-own-runners/using-a-proxy-server-with-self-hosted-runners) to run behind a proxy server in enterprises. + +For actions to **just work** behind a proxy server: + + 1. Use [tool-cache] version >= 1.3.1 + 2. Optionally use [actions/http-client](https://github.com/actions/http-client) + +If you are using other http clients, refer to the [environment variables set by the runner](https://help.github.com/en/actions/hosting-your-own-runners/using-a-proxy-server-with-self-hosted-runners). \ No newline at end of file From 9cfb1604bde22963dfef21a43341caa4188ae5a0 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Wed, 12 Feb 2020 14:59:01 -0500 Subject: [PATCH 088/192] troubleshooting problem matchers (#341) --- docs/problem-matchers.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/problem-matchers.md b/docs/problem-matchers.md index 441d49b235..df5ce9890c 100644 --- a/docs/problem-matchers.md +++ b/docs/problem-matchers.md @@ -115,3 +115,26 @@ Some of the starter actions are already using problem matchers, for example: - [setup-python](https://github.com/actions/setup-python/tree/master/.github) - [setup-go](https://github.com/actions/setup-go/tree/master/.github) - [setup-dotnet](https://github.com/actions/setup-dotnet/tree/master/.github) + +## Troubleshooting + +### Regular expression not matching + +Use ECMAScript regular expression syntax when testing patterns. + +### File property getting dropped + +[Enable debug logging](https://help.github.com/en/actions/configuring-and-managing-workflows/managing-a-workflow-run#enabling-debug-logging) to determine why the file is getting dropped. + +This usually happens when the file does not exist or is not under the workflow repo. + +### Adding from a Docker container action + +The matcher config file must first be copied to a volume that is accessible from the runner. + +For example: + +```sh +cp /eslint-compact.json "$HOME/" +echo "::add-matcher::$HOME/eslint-compact.json +``` \ No newline at end of file From f90a2dcfd4dafd277f06c7da0741216d0ef13708 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Wed, 12 Feb 2020 21:42:14 -0500 Subject: [PATCH 089/192] temporarily revert guidance for adding matcher from container action --- docs/problem-matchers.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/docs/problem-matchers.md b/docs/problem-matchers.md index df5ce9890c..f88678494d 100644 --- a/docs/problem-matchers.md +++ b/docs/problem-matchers.md @@ -127,14 +127,3 @@ Use ECMAScript regular expression syntax when testing patterns. [Enable debug logging](https://help.github.com/en/actions/configuring-and-managing-workflows/managing-a-workflow-run#enabling-debug-logging) to determine why the file is getting dropped. This usually happens when the file does not exist or is not under the workflow repo. - -### Adding from a Docker container action - -The matcher config file must first be copied to a volume that is accessible from the runner. - -For example: - -```sh -cp /eslint-compact.json "$HOME/" -echo "::add-matcher::$HOME/eslint-compact.json -``` \ No newline at end of file From 84f1e31b691b33a977191b9315ea329c3739eba2 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Thu, 13 Feb 2020 09:54:56 -0500 Subject: [PATCH 090/192] send tar --version to debug log (#342) --- packages/tool-cache/src/tool-cache.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index 6e8c6c2a0f..7cb581acd2 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -202,14 +202,17 @@ export async function extractTar( dest = await _createExtractFolder(dest) // Determine whether GNU tar + core.debug('Checking tar --version') let versionOutput = '' await exec('tar --version', [], { ignoreReturnCode: true, + silent: true, listeners: { stdout: (data: Buffer) => (versionOutput += data.toString()), stderr: (data: Buffer) => (versionOutput += data.toString()) } }) + core.debug(versionOutput.trim()) const isGnuTar = versionOutput.toUpperCase().includes('GNU TAR') // Initialize args From f383109dc391f2c2576583f9426be193eeeefbfd Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Thu, 13 Feb 2020 18:24:11 -0500 Subject: [PATCH 091/192] @actions/artifact download artifacts (#340) * Download Artifacts using @actions/artifact --- packages/artifact/README.md | 199 +++++++ packages/artifact/RELEASES.md | 5 + .../__tests__/download-specification.test.ts | 499 ++++++++++++++++++ packages/artifact/__tests__/download.test.ts | 236 +++++++++ packages/artifact/__tests__/util.test.ts | 21 + packages/artifact/additional-information.md | 45 ++ packages/artifact/package.json | 4 +- .../artifact/src/internal-artifact-client.ts | 159 +++++- .../artifact/src/internal-config-variables.ts | 17 + packages/artifact/src/internal-contracts.ts | 29 + .../src/internal-download-http-client.ts | 133 +++++ .../src/internal-download-specification.ts | 78 +++ packages/artifact/src/internal-utils.ts | 11 + 13 files changed, 1417 insertions(+), 19 deletions(-) create mode 100644 packages/artifact/RELEASES.md create mode 100644 packages/artifact/__tests__/download-specification.test.ts create mode 100644 packages/artifact/__tests__/download.test.ts create mode 100644 packages/artifact/additional-information.md create mode 100644 packages/artifact/src/internal-download-http-client.ts create mode 100644 packages/artifact/src/internal-download-specification.ts diff --git a/packages/artifact/README.md b/packages/artifact/README.md index e69de29bb2..3a53c170b0 100644 --- a/packages/artifact/README.md +++ b/packages/artifact/README.md @@ -0,0 +1,199 @@ +# `@actions/artifact` + +## Usage + +You can use this package to interact with the actions artifacts. +- [Upload an Artifact](#Upload-an-Artifact) +- [Download a Single Artifact](#Download-a-Single-Artifact) +- [Download All Artifacts](#Download-all-Artifacts) +- [Additional Documentation](#Additional-Documentation) + +Relative paths and absolute paths are both allowed. Relative paths are rooted against the current working directory. + +## Upload an Artifact + +Method Name: `uploadArtifact` + +#### Inputs + - `name` + - The name of the artifact that is being uploaded + - Required + - `files` + - A list of file paths that describe what should be uploaded as part of the artifact + - If a path is provided that does not exist, an error will be thrown + - Can be absolute or relative. Internally everything is normalized and resolved + - Required + - `rootDirectory` + - A file path that denotes the root directory of the files being uploaded. This path is used to strip the paths provided in `files` to control how they are uploaded and structured + - If a file specified in `files` is not in the `rootDirectory`, an error will be thrown + - Required + - `options` + - Extra options that allow for the customization of the upload behavior + - Optional + +#### Available Options + + - `continueOnError` + - Indicates if the artifact upload should continue in the event a file fails to upload. If there is a error during upload, a partial artifact will always be created and available for download at the end. The `size` reported will be the amount of storage that the user or org will be charged for the partial artifact. + - If set to `false`, and an error is encountered, all other uploads will stop and any files that were queued will not be attempted to be uploaded. The partial artifact available will only include files up until the failure. + - If set to `true` and an error is encountered, the failed file will be skipped and ignored and all other queued files will be attempted to be uploaded. There will be an artifact available for download at the end with everything excluding the file that failed to upload + - Optional, defaults to `true` if not specified + +#### Example using Absolute File Paths + +```js +const artifact = require('@actions/artifact'); +const artifactClient = artifact.create() +const artifactName = 'my-artifact'; +const files = [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' +] +const rootDirectory = '/home/user/files/plz-upload' +const options = { + continueOnError: true +} + +const uploadResult = await artifactClient.uploadArtifact(artifactName, files, rootDirectory, options) +``` + +#### Example using Relative File Paths +```js +// Assuming the current working directory is /home/user/files/plz-upload +const artifact = require('@actions/artifact'); +const artifactClient = artifact.create() +const artifactName = 'my-artifact'; +const files = [ + 'file1.txt', + 'file2.txt', + 'dir/file3.txt' +] + +const rootDirectory = '.' // Also possible to use __dirname +const options = { + continueOnError: false +} + +const uploadResponse = await artifactClient.uploadArtifact(artifactName, files, rootDirectory, options) +``` + +#### Upload Result + +The returned `UploadResponse` will contain the following information + +- `artifactName` + - The name of the artifact that was uploaded +- `artifactItems` + - A list of all files that describe what is uploaded if there are no errors encountered. Usually this will be equal to the inputted `files` with the exception of empty directories (will not be uploaded) +- `size` + - Total size of the artifact that was uploaded in bytes +- `failedItems` + - A list of items that were not uploaded successfully (this will include queued items that were not uploaded if `continueOnError` is set to false). This is a subset of `artifactItems` + +## Download a Single Artifact + +Method Name: `downloadArtifact` + +#### Inputs + - `name` + - The name of the artifact to download + - Required + - `path` + - Path that denotes where the artifact will be downloaded to + - Optional. Defaults to the GitHub workspace directory(`$GITHUB_WORKSPACE`) if not specified + - `options` + - Extra options that allow for the customization of the download behavior + - Optional + + +#### Available Options + + - `createArtifactFolder` + - Specifies if a folder (the artifact name) is created for the artifact that is downloaded (contents downloaded into this folder), + - Optional. Defaults to false if not specified + +#### Example + +```js +const artifact = require('@actions/artifact'); +const artifactClient = artifact.create() +const artifactName = 'my-artifact'; +const path = 'some/directory' +const options = { + createArtifactFolder: false +} + +const downloadResponse = await artifactClient.downloadArtifact(artifactName, path, options) + +// Post download, the directory structure will look like this +/some + /directory + /file1.txt + /file2.txt + /dir + /file3.txt + +// If createArtifactFolder is set to true, the directory structure will look like this +/some + /directory + /my-artifact + /file1.txt + /file2.txt + /dir + /file3.txt +``` + +#### Download Response + +The returned `DownloadResponse` will contain the following information + + - `artifactName` + - The name of the artifact that was downloaded + - `downloadPath` + - The full Path to where the artifact was downloaded + + +## Download All Artifacts + +Method Name: `downloadAllArtifacts` + +#### Inputs + - `path` + - Path that denotes where the artifact will be downloaded to + - Optional. Defaults to the GitHub workspace directory(`$GITHUB_WORKSPACE`) if not specified + +```js +const artifact = require('@actions/artifact'); +const artifactClient = artifact.create(); +const downloadResponse = await artifactClient.downloadAllArtifacts(); + +// output result +for (response in downloadResponse) { + console.log(response.artifactName); + console.log(response.downloadPath); +} +``` + +Because there are multiple artifacts, an extra directory (denoted by the name of the artifact) will be created for each artifact in the path. With 2 artifacts(`my-artifact-1` and `my-artifact-2` for example) and the default path, the directory structure will be as follows: +```js +/GITHUB_WORKSPACE + /my-artifact-1 + / .. contents of `my-artifact-1` + /my-artifact-2 + / .. contents of `my-artifact-2` +``` + +#### Download Result + +An array will be returned that describes the results for downloading all artifacts. The number of items in the array indicates the number of artifacts that were downloaded. + +Each artifact will have the same `DownloadResponse` as if it was individually downloaded + - `artifactName` + - The name of the artifact that was downloaded + - `downloadPath` + - The full Path to where the artifact was downloaded + +## Additional Documentation + +Check out [additional-information](additional-information.md) for extra documentation. diff --git a/packages/artifact/RELEASES.md b/packages/artifact/RELEASES.md new file mode 100644 index 0000000000..9fd58d506d --- /dev/null +++ b/packages/artifact/RELEASES.md @@ -0,0 +1,5 @@ +# @actions/artifact Releases + +### 0.1.0 + +- Initial release \ No newline at end of file diff --git a/packages/artifact/__tests__/download-specification.test.ts b/packages/artifact/__tests__/download-specification.test.ts new file mode 100644 index 0000000000..ac1b5f9371 --- /dev/null +++ b/packages/artifact/__tests__/download-specification.test.ts @@ -0,0 +1,499 @@ +import * as path from 'path' +import * as core from '@actions/core' +import {URL} from 'url' +import {getDownloadSpecification} from '../src/internal-download-specification' +import {ContainerEntry} from '../src/internal-contracts' + +const artifact1Name = 'my-artifact' +const artifact2Name = 'my-artifact-extra' + +// Populating with only the information that is necessary +function getPartialContainerEntry(): ContainerEntry { + return { + containerId: 10, + scopeIdentifier: '00000000-0000-0000-0000-000000000000', + path: 'ADD_INFORMATION', + itemType: 'ADD_INFORMATION', + status: 'created', + dateCreated: '2020-02-06T22:13:35.373Z', + dateLastModified: '2020-02-06T22:13:35.453Z', + createdBy: '82f0bf89-6e55-4e5a-b8b6-f75eb992578c', + lastModifiedBy: '82f0bf89-6e55-4e5a-b8b6-f75eb992578c', + itemLocation: 'ADD_INFORMATION', + contentLocation: 'ADD_INFORMATION', + contentId: '' + } +} + +function createFileEntry(entryPath: string): ContainerEntry { + const newFileEntry = getPartialContainerEntry() + newFileEntry.path = entryPath + newFileEntry.itemType = 'file' + newFileEntry.itemLocation = createItemLocation(entryPath) + newFileEntry.contentLocation = createContentLocation(entryPath) + return newFileEntry +} + +function createDirectoryEntry(directoryPath: string): ContainerEntry { + const newDirectoryEntry = getPartialContainerEntry() + newDirectoryEntry.path = directoryPath + newDirectoryEntry.itemType = 'folder' + newDirectoryEntry.itemLocation = createItemLocation(directoryPath) + newDirectoryEntry.contentLocation = createContentLocation(directoryPath) + return newDirectoryEntry +} + +function createItemLocation(relativePath: string): string { + const itemLocation = new URL( + 'https://testing/_apis/resources/Containers/10000' + ) + itemLocation.searchParams.append('itemPath', relativePath) + itemLocation.searchParams.append('metadata', 'true') + return itemLocation.toString() +} + +function createContentLocation(relativePath: string): string { + const itemLocation = new URL( + 'https://testing/_apis/resources/Containers/10000' + ) + itemLocation.searchParams.append('itemPath', relativePath) + return itemLocation.toString() +} + +/* + Represents a set of container entries for two artifacts with the following directory structure + + /my-artifact + /file1.txt + /file2.txt + /dir1 + /file3.txt + /dir2 + /dir3 + /dir4 + file4.txt + file5.txt + + /my-artifact-extra + /file1.txt +*/ + +// main artfact +const file1Path = path.join(artifact1Name, 'file1.txt') +const file2Path = path.join(artifact1Name, 'file2.txt') +const dir1Path = path.join(artifact1Name, 'dir1') +const file3Path = path.join(dir1Path, 'file3.txt') +const dir2Path = path.join(dir1Path, 'dir2') +const dir3Path = path.join(dir2Path, 'dir3') +const dir4Path = path.join(dir3Path, 'dir4') +const file4Path = path.join(dir4Path, 'file4.txt') +const file5Path = path.join(dir4Path, 'file5.txt') + +const rootDirectoryEntry = createDirectoryEntry(artifact1Name) +const directoryEntry1 = createDirectoryEntry(dir1Path) +const directoryEntry2 = createDirectoryEntry(dir2Path) +const directoryEntry3 = createDirectoryEntry(dir3Path) +const directoryEntry4 = createDirectoryEntry(dir4Path) +const fileEntry1 = createFileEntry(file1Path) +const fileEntry2 = createFileEntry(file2Path) +const fileEntry3 = createFileEntry(file3Path) +const fileEntry4 = createFileEntry(file4Path) +const fileEntry5 = createFileEntry(file5Path) + +// extra artifact +const artifact2File1Path = path.join(artifact2Name, 'file1.txt') +const rootDirectoryEntry2 = createDirectoryEntry(artifact2Name) +const extraFileEntry = createFileEntry(artifact2File1Path) + +const artifactContainerEntries: ContainerEntry[] = [ + rootDirectoryEntry, + fileEntry1, + fileEntry2, + directoryEntry1, + fileEntry3, + directoryEntry2, + directoryEntry3, + directoryEntry4, + fileEntry4, + fileEntry5, + rootDirectoryEntry2, + extraFileEntry +] + +describe('Search', () => { + beforeAll(async () => { + // mock all output so that there is less noise when running tests + jest.spyOn(console, 'log').mockImplementation(() => {}) + jest.spyOn(core, 'debug').mockImplementation(() => {}) + jest.spyOn(core, 'info').mockImplementation(() => {}) + jest.spyOn(core, 'warning').mockImplementation(() => {}) + }) + + it('Download Specification - Absolute Path with no root directory', () => { + const testDownloadPath = path.join( + __dirname, + 'some', + 'destination', + 'folder' + ) + + const specification = getDownloadSpecification( + artifact1Name, + artifactContainerEntries, + testDownloadPath, + false + ) + + expect(specification.rootDownloadLocation).toEqual(testDownloadPath) + expect(specification.filesToDownload.length).toEqual(5) + + const item1ExpectedTargetPath = path.join(testDownloadPath, 'file1.txt') + const item2ExpectedTargetPath = path.join(testDownloadPath, 'file2.txt') + const item3ExpectedTargetPath = path.join( + testDownloadPath, + 'dir1', + 'file3.txt' + ) + const item4ExpectedTargetPath = path.join( + testDownloadPath, + 'dir1', + 'dir2', + 'dir3', + 'dir4', + 'file4.txt' + ) + const item5ExpectedTargetPath = path.join( + testDownloadPath, + 'dir1', + 'dir2', + 'dir3', + 'dir4', + 'file5.txt' + ) + + const targetLocations = specification.filesToDownload.map( + item => item.targetPath + ) + expect(targetLocations).toContain(item1ExpectedTargetPath) + expect(targetLocations).toContain(item2ExpectedTargetPath) + expect(targetLocations).toContain(item3ExpectedTargetPath) + expect(targetLocations).toContain(item4ExpectedTargetPath) + expect(targetLocations).toContain(item5ExpectedTargetPath) + + for (const downloadItem of specification.filesToDownload) { + if (downloadItem.targetPath === item1ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file1Path) + ) + } else if (downloadItem.targetPath === item2ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file2Path) + ) + } else if (downloadItem.targetPath === item3ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file3Path) + ) + } else if (downloadItem.targetPath === item4ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file4Path) + ) + } else if (downloadItem.targetPath === item5ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file5Path) + ) + } else { + throw new Error('this should never be reached') + } + } + + expect(specification.directoryStructure.length).toEqual(3) + expect(specification.directoryStructure).toContain(testDownloadPath) + expect(specification.directoryStructure).toContain( + path.join(testDownloadPath, 'dir1') + ) + expect(specification.directoryStructure).toContain( + path.join(testDownloadPath, 'dir1', 'dir2', 'dir3', 'dir4') + ) + }) + + it('Download Specification - Relative Path with no root directory', () => { + const testDownloadPath = path.join('some', 'destination', 'folder') + + const specification = getDownloadSpecification( + artifact1Name, + artifactContainerEntries, + testDownloadPath, + false + ) + + expect(specification.rootDownloadLocation).toEqual(testDownloadPath) + expect(specification.filesToDownload.length).toEqual(5) + + const item1ExpectedTargetPath = path.join(testDownloadPath, 'file1.txt') + const item2ExpectedTargetPath = path.join(testDownloadPath, 'file2.txt') + const item3ExpectedTargetPath = path.join( + testDownloadPath, + 'dir1', + 'file3.txt' + ) + const item4ExpectedTargetPath = path.join( + testDownloadPath, + 'dir1', + 'dir2', + 'dir3', + 'dir4', + 'file4.txt' + ) + const item5ExpectedTargetPath = path.join( + testDownloadPath, + 'dir1', + 'dir2', + 'dir3', + 'dir4', + 'file5.txt' + ) + + const targetLocations = specification.filesToDownload.map( + item => item.targetPath + ) + expect(targetLocations).toContain(item1ExpectedTargetPath) + expect(targetLocations).toContain(item2ExpectedTargetPath) + expect(targetLocations).toContain(item3ExpectedTargetPath) + expect(targetLocations).toContain(item4ExpectedTargetPath) + expect(targetLocations).toContain(item5ExpectedTargetPath) + + for (const downloadItem of specification.filesToDownload) { + if (downloadItem.targetPath === item1ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file1Path) + ) + } else if (downloadItem.targetPath === item2ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file2Path) + ) + } else if (downloadItem.targetPath === item3ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file3Path) + ) + } else if (downloadItem.targetPath === item4ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file4Path) + ) + } else if (downloadItem.targetPath === item5ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file5Path) + ) + } else { + throw new Error('this should never be reached') + } + } + + expect(specification.directoryStructure.length).toEqual(3) + expect(specification.directoryStructure).toContain(testDownloadPath) + expect(specification.directoryStructure).toContain( + path.join(testDownloadPath, 'dir1') + ) + expect(specification.directoryStructure).toContain( + path.join(testDownloadPath, 'dir1', 'dir2', 'dir3', 'dir4') + ) + }) + + it('Download Specification - Absolute Path with root directory', () => { + const testDownloadPath = path.join( + __dirname, + 'some', + 'destination', + 'folder' + ) + + const specification = getDownloadSpecification( + artifact1Name, + artifactContainerEntries, + testDownloadPath, + true + ) + + expect(specification.rootDownloadLocation).toEqual( + path.join(testDownloadPath, artifact1Name) + ) + expect(specification.filesToDownload.length).toEqual(5) + + const item1ExpectedTargetPath = path.join( + testDownloadPath, + artifact1Name, + 'file1.txt' + ) + const item2ExpectedTargetPath = path.join( + testDownloadPath, + artifact1Name, + 'file2.txt' + ) + const item3ExpectedTargetPath = path.join( + testDownloadPath, + artifact1Name, + 'dir1', + 'file3.txt' + ) + const item4ExpectedTargetPath = path.join( + testDownloadPath, + artifact1Name, + 'dir1', + 'dir2', + 'dir3', + 'dir4', + 'file4.txt' + ) + const item5ExpectedTargetPath = path.join( + testDownloadPath, + artifact1Name, + 'dir1', + 'dir2', + 'dir3', + 'dir4', + 'file5.txt' + ) + + const targetLocations = specification.filesToDownload.map( + item => item.targetPath + ) + expect(targetLocations).toContain(item1ExpectedTargetPath) + expect(targetLocations).toContain(item2ExpectedTargetPath) + expect(targetLocations).toContain(item3ExpectedTargetPath) + expect(targetLocations).toContain(item4ExpectedTargetPath) + expect(targetLocations).toContain(item5ExpectedTargetPath) + + for (const downloadItem of specification.filesToDownload) { + if (downloadItem.targetPath === item1ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file1Path) + ) + } else if (downloadItem.targetPath === item2ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file2Path) + ) + } else if (downloadItem.targetPath === item3ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file3Path) + ) + } else if (downloadItem.targetPath === item4ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file4Path) + ) + } else if (downloadItem.targetPath === item5ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file5Path) + ) + } else { + throw new Error('this should never be reached') + } + } + + expect(specification.directoryStructure.length).toEqual(3) + expect(specification.directoryStructure).toContain( + path.join(testDownloadPath, artifact1Name) + ) + expect(specification.directoryStructure).toContain( + path.join(testDownloadPath, dir1Path) + ) + expect(specification.directoryStructure).toContain( + path.join(testDownloadPath, dir4Path) + ) + }) + + it('Download Specification - Relative Path with root directory', () => { + const testDownloadPath = path.join('some', 'destination', 'folder') + + const specification = getDownloadSpecification( + artifact1Name, + artifactContainerEntries, + testDownloadPath, + true + ) + + expect(specification.rootDownloadLocation).toEqual( + path.join(testDownloadPath, artifact1Name) + ) + expect(specification.filesToDownload.length).toEqual(5) + + const item1ExpectedTargetPath = path.join( + testDownloadPath, + artifact1Name, + 'file1.txt' + ) + const item2ExpectedTargetPath = path.join( + testDownloadPath, + artifact1Name, + 'file2.txt' + ) + const item3ExpectedTargetPath = path.join( + testDownloadPath, + artifact1Name, + 'dir1', + 'file3.txt' + ) + const item4ExpectedTargetPath = path.join( + testDownloadPath, + artifact1Name, + 'dir1', + 'dir2', + 'dir3', + 'dir4', + 'file4.txt' + ) + const item5ExpectedTargetPath = path.join( + testDownloadPath, + artifact1Name, + 'dir1', + 'dir2', + 'dir3', + 'dir4', + 'file5.txt' + ) + + const targetLocations = specification.filesToDownload.map( + item => item.targetPath + ) + expect(targetLocations).toContain(item1ExpectedTargetPath) + expect(targetLocations).toContain(item2ExpectedTargetPath) + expect(targetLocations).toContain(item3ExpectedTargetPath) + expect(targetLocations).toContain(item4ExpectedTargetPath) + expect(targetLocations).toContain(item5ExpectedTargetPath) + + for (const downloadItem of specification.filesToDownload) { + if (downloadItem.targetPath === item1ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file1Path) + ) + } else if (downloadItem.targetPath === item2ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file2Path) + ) + } else if (downloadItem.targetPath === item3ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file3Path) + ) + } else if (downloadItem.targetPath === item4ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file4Path) + ) + } else if (downloadItem.targetPath === item5ExpectedTargetPath) { + expect(downloadItem.sourceLocation).toEqual( + createContentLocation(file5Path) + ) + } else { + throw new Error('this should never be reached') + } + } + + expect(specification.directoryStructure.length).toEqual(3) + expect(specification.directoryStructure).toContain( + path.join(testDownloadPath, artifact1Name) + ) + expect(specification.directoryStructure).toContain( + path.join(testDownloadPath, dir1Path) + ) + expect(specification.directoryStructure).toContain( + path.join(testDownloadPath, dir4Path) + ) + }) +}) diff --git a/packages/artifact/__tests__/download.test.ts b/packages/artifact/__tests__/download.test.ts new file mode 100644 index 0000000000..d10c61e545 --- /dev/null +++ b/packages/artifact/__tests__/download.test.ts @@ -0,0 +1,236 @@ +import * as core from '@actions/core' +import * as http from 'http' +import * as io from '../../io/src/io' +import * as net from 'net' +import * as path from 'path' +import * as configVariables from '../src/internal-config-variables' +import {HttpClient, HttpClientResponse} from '@actions/http-client' +import * as downloadClient from '../src/internal-download-http-client' +import { + ListArtifactsResponse, + QueryArtifactResponse +} from '../src/internal-contracts' + +const root = path.join(__dirname, '_temp', 'artifact-download') + +jest.mock('../src/internal-config-variables') +jest.mock('@actions/http-client') + +describe('Download Tests', () => { + beforeAll(async () => { + await io.rmRF(root) + + // mock all output so that there is less noise when running tests + jest.spyOn(console, 'log').mockImplementation(() => {}) + jest.spyOn(core, 'debug').mockImplementation(() => {}) + jest.spyOn(core, 'info').mockImplementation(() => {}) + jest.spyOn(core, 'warning').mockImplementation(() => {}) + }) + + /** + * Test Listing Artifacts + */ + it('List Artifacts - Success', async () => { + setupSuccessfulListArtifactsResponse() + const artifacts = await downloadClient.listArtifacts() + expect(artifacts.count).toEqual(2) + + const artifactNames = artifacts.value.map(item => item.name) + expect(artifactNames).toContain('artifact1-name') + expect(artifactNames).toContain('artifact2-name') + + for (const artifact of artifacts.value) { + if (artifact.name === 'artifact1-name') { + expect(artifact.url).toEqual( + `${configVariables.getRuntimeUrl()}_apis/pipelines/1/runs/1/artifacts?artifactName=artifact1-name` + ) + } else if (artifact.name === 'artifact2-name') { + expect(artifact.url).toEqual( + `${configVariables.getRuntimeUrl()}_apis/pipelines/1/runs/1/artifacts?artifactName=artifact2-name` + ) + } else { + throw new Error( + 'Invalid artifact combination. This should never be reached' + ) + } + } + }) + + it('List Artifacts - Failure', async () => { + setupFailedResponse() + expect(downloadClient.listArtifacts()).rejects.toThrow( + 'Unable to list artifacts for the run' + ) + }) + + /** + * Test Container Items + */ + it('Container Items - Success', async () => { + setupSuccessfulContainerItemsResponse() + const response = await downloadClient.getContainerItems( + 'artifact-name', + configVariables.getRuntimeUrl() + ) + expect(response.count).toEqual(2) + + const itemPaths = response.value.map(item => item.path) + expect(itemPaths).toContain('artifact-name') + expect(itemPaths).toContain('artifact-name/file1.txt') + + for (const containerEntry of response.value) { + if (containerEntry.path === 'artifact-name') { + expect(containerEntry.itemType).toEqual('folder') + } else if (containerEntry.path === 'artifact-name/file1.txt') { + expect(containerEntry.itemType).toEqual('file') + } else { + throw new Error( + 'Invalid container combination. This should never be reached' + ) + } + } + }) + + it('Container Items - Failure', async () => { + setupFailedResponse() + expect( + downloadClient.getContainerItems( + 'artifact-name', + configVariables.getRuntimeUrl() + ) + ).rejects.toThrow( + `Unable to get ContainersItems from ${configVariables.getRuntimeUrl()}` + ) + }) + + /** + * Helper used to setup mocking for the HttpClient + */ + async function emptyMockReadBody(): Promise { + return new Promise(resolve => { + resolve() + }) + } + + /** + * Setups up HTTP GET response for a successful listArtifacts() call + */ + function setupSuccessfulListArtifactsResponse(): void { + jest.spyOn(HttpClient.prototype, 'get').mockImplementationOnce(async () => { + const mockMessage = new http.IncomingMessage(new net.Socket()) + let mockReadBody = emptyMockReadBody + + mockMessage.statusCode = 201 + const response: ListArtifactsResponse = { + count: 2, + value: [ + { + containerId: '13', + size: -1, + signedContent: 'false', + fileContainerResourceUrl: `${configVariables.getRuntimeUrl()}_apis/resources/Containers/13`, + type: 'actions_storage', + name: 'artifact1-name', + url: `${configVariables.getRuntimeUrl()}_apis/pipelines/1/runs/1/artifacts?artifactName=artifact1-name` + }, + { + containerId: '13', + size: -1, + signedContent: 'false', + fileContainerResourceUrl: `${configVariables.getRuntimeUrl()}_apis/resources/Containers/13`, + type: 'actions_storage', + name: 'artifact2-name', + url: `${configVariables.getRuntimeUrl()}_apis/pipelines/1/runs/1/artifacts?artifactName=artifact2-name` + } + ] + } + const returnData: string = JSON.stringify(response, null, 2) + mockReadBody = async function(): Promise { + return new Promise(resolve => { + resolve(returnData) + }) + } + + return new Promise(resolve => { + resolve({ + message: mockMessage, + readBody: mockReadBody + }) + }) + }) + } + + /** + * Setups up HTTP GET response when querying for container items + */ + function setupSuccessfulContainerItemsResponse(): void { + jest.spyOn(HttpClient.prototype, 'get').mockImplementationOnce(async () => { + const mockMessage = new http.IncomingMessage(new net.Socket()) + let mockReadBody = emptyMockReadBody + + mockMessage.statusCode = 201 + const response: QueryArtifactResponse = { + count: 2, + value: [ + { + containerId: 10000, + scopeIdentifier: '00000000-0000-0000-0000-000000000000', + path: 'artifact-name', + itemType: 'folder', + status: 'created', + dateCreated: '2020-02-06T22:13:35.373Z', + dateLastModified: '2020-02-06T22:13:35.453Z', + createdBy: '82f0bf89-6e55-4e5a-b8b6-f75eb992578c', + lastModifiedBy: '82f0bf89-6e55-4e5a-b8b6-f75eb992578c', + itemLocation: `${configVariables.getRuntimeUrl()}/_apis/resources/Containers/10000?itemPath=artifact-name&metadata=True`, + contentLocation: `${configVariables.getRuntimeUrl()}/_apis/resources/Containers/10000?itemPath=artifact-name`, + contentId: '' + }, + { + containerId: 10000, + scopeIdentifier: '00000000-0000-0000-0000-000000000000', + path: 'artifact-name/file1.txt', + itemType: 'file', + status: 'created', + dateCreated: '2020-02-06T22:13:35.373Z', + dateLastModified: '2020-02-06T22:13:35.453Z', + createdBy: '82f0bf89-6e55-4e5a-b8b6-f75eb992578c', + lastModifiedBy: '82f0bf89-6e55-4e5a-b8b6-f75eb992578c', + itemLocation: `${configVariables.getRuntimeUrl()}/_apis/resources/Containers/10000?itemPath=artifact-name%2Ffile1.txt&metadata=True`, + contentLocation: `${configVariables.getRuntimeUrl()}/_apis/resources/Containers/10000?itemPath=artifact-name%2Ffile1.txt`, + contentId: '' + } + ] + } + const returnData: string = JSON.stringify(response, null, 2) + mockReadBody = async function(): Promise { + return new Promise(resolve => { + resolve(returnData) + }) + } + + return new Promise(resolve => { + resolve({ + message: mockMessage, + readBody: mockReadBody + }) + }) + }) + } + + /** + * Setups up HTTP GET response for a generic failed request + */ + function setupFailedResponse(): void { + jest.spyOn(HttpClient.prototype, 'get').mockImplementationOnce(async () => { + const mockMessage = new http.IncomingMessage(new net.Socket()) + mockMessage.statusCode = 500 + return new Promise(resolve => { + resolve({ + message: mockMessage, + readBody: emptyMockReadBody + }) + }) + }) + } +}) diff --git a/packages/artifact/__tests__/util.test.ts b/packages/artifact/__tests__/util.test.ts index e71652b3c2..ffbe62bf0b 100644 --- a/packages/artifact/__tests__/util.test.ts +++ b/packages/artifact/__tests__/util.test.ts @@ -1,3 +1,6 @@ +import * as fs from 'fs' +import * as io from '../../io/src/io' +import * as path from 'path' import * as utils from '../src/internal-utils' import * as core from '@actions/core' import {HttpCodes} from '@actions/http-client' @@ -96,4 +99,22 @@ describe('Utils', () => { expect(utils.isRetryableStatusCode(HttpCodes.NotFound)).toEqual(false) expect(utils.isRetryableStatusCode(HttpCodes.Forbidden)).toEqual(false) }) + + it('Test Creating Artifact Directories', async () => { + const root = path.join(__dirname, '_temp', 'artifact-download') + // remove directory before starting + await io.rmRF(root) + + const directory1 = path.join(root, 'folder2', 'folder3') + const directory2 = path.join(directory1, 'folder1') + + // Initially should not exist + expect(fs.existsSync(directory1)).toEqual(false) + expect(fs.existsSync(directory2)).toEqual(false) + const directoryStructure = [directory1, directory2] + await utils.createDirectoriesForArtifact(directoryStructure) + // directories should now be created + expect(fs.existsSync(directory1)).toEqual(true) + expect(fs.existsSync(directory2)).toEqual(true) + }) }) diff --git a/packages/artifact/additional-information.md b/packages/artifact/additional-information.md new file mode 100644 index 0000000000..1f7356265b --- /dev/null +++ b/packages/artifact/additional-information.md @@ -0,0 +1,45 @@ +# Additional Information + +Extra information +- [Non-Supported Characters](#Non-Supported-Characters) +- [Permission loss](#Permission-Loss) +- [Considerations](#Considerations) + +## Non-Supported Characters + +When uploading an artifact, the inputted `name` parameter along with the files specified in `files` cannot contain any of the following characters. They will be rejected by the server if attempted to be sent over and the upload will fail. These characters are not allowed due to limitations and restrictions with certain file systems such as NTFS. To maintain platform-agnostic behavior, all characters that are not supported by an individual filesystem/platform will not be supported on all filesystems/platforms. + +- " +- : +- < +- \> +- | +- \* +- ? +- empty space + +In addition to the aforementioned characters, the inputted `name` also cannot include the following +- \ +- / + + +## Permission Loss + +File permissions are not maintained between uploaded and downloaded artifacts. If file permissions are something that need to be maintained (such as an executable), consider archiving all of the files using something like `tar` and then uploading the single archive. After downloading the artifact, you can `un-tar` the individual file and permissions will be preserved. + +```js +const artifact = require('@actions/artifact'); +const artifactClient = artifact.create() +const artifactName = 'my-artifact'; +const files = [ + '/home/user/files/plz-upload/my-archive.tgz', +] +const rootDirectory = '/home/user/files/plz-upload' +const uploadResult = await artifactClient.uploadArtifact(artifactName, files, rootDirectory) +``` + +## Considerations + +During upload, each file is uploaded concurrently in 4MB chunks using a separate HTTPS connection per file. Chunked uploads are used so that in the event of a failure (which is entirely possible because the internet is not perfect), the upload can be retried. If there is an error, a retry will be attempted after a certain period of time. + +Uploading will be generally be faster if there are fewer files that are larger in size vs if there are lots of smaller files. Depending on the types and quantities of files being uploaded, it might be beneficial to separately compress and archive everything into a single archive (using something like `tar` or `zip`) before starting and artifact upload to speed things up. diff --git a/packages/artifact/package.json b/packages/artifact/package.json index 0f38fa9b67..fab71fd1d8 100644 --- a/packages/artifact/package.json +++ b/packages/artifact/package.json @@ -10,8 +10,8 @@ ], "homepage": "https://github.com/actions/toolkit/tree/master/packages/artifact", "license": "MIT", - "main": "lib/artifact.js", - "types": "lib/artifact.d.ts", + "main": "lib/artifact-client.js", + "types": "lib/artifact-client.d.ts", "directories": { "lib": "lib", "test": "__tests__" diff --git a/packages/artifact/src/internal-artifact-client.ts b/packages/artifact/src/internal-artifact-client.ts index bf431861e8..9216d9764c 100644 --- a/packages/artifact/src/internal-artifact-client.ts +++ b/packages/artifact/src/internal-artifact-client.ts @@ -10,9 +10,22 @@ import { } from './internal-upload-http-client' import {UploadResponse} from './internal-upload-response' import {UploadOptions} from './internal-upload-options' -import {checkArtifactName} from './internal-utils' +import {DownloadOptions} from './internal-download-options' +import {DownloadResponse} from './internal-download-response' +import {checkArtifactName, createDirectoriesForArtifact} from './internal-utils' +import { + listArtifacts, + downloadSingleArtifact, + getContainerItems +} from './internal-download-http-client' +import {getDownloadSpecification} from './internal-download-specification' +import { + getWorkSpaceDirectory, + getDownloadArtifactConcurrency +} from './internal-config-variables' +import {normalize, resolve} from 'path' -export {UploadResponse, UploadOptions} +export {UploadResponse, UploadOptions, DownloadResponse, DownloadOptions} export interface ArtifactClient { /** @@ -30,6 +43,25 @@ export interface ArtifactClient { rootDirectory: string, options?: UploadOptions ): Promise + + /** + * Downloads a single artifact associated with a run + * + * @param name the name of the artifact being downloaded + * @param path optional path that denotes where the artifact will be downloaded to + * @param options extra options that allow for the customization of the download behavior + */ + downloadArtifact( + name: string, + path?: string, + options?: DownloadOptions + ): Promise + + /** + * Downloads all artifacts associated with a run. Because there are multiple artifacts being downloaded, a folder will be created for each one in the specified or default directory + * @param path optional path that denotes where the artifacts will be downloaded to + */ + downloadAllArtifacts(path?: string): Promise } export class DefaultArtifactClient implements ArtifactClient { @@ -100,25 +132,118 @@ export class DefaultArtifactClient implements ArtifactClient { return uploadResponse } - /* - Downloads a single artifact associated with a run + async downloadArtifact( + name: string, + path?: string | undefined, + options?: DownloadOptions | undefined + ): Promise { + const artifacts = await listArtifacts() + if (artifacts.count === 0) { + throw new Error( + `Unable to find any artifacts for the associated workflow` + ) + } - export async function downloadArtifact( - name: string, - path?: string, - options?: DownloadOptions - ): Promise { + const artifactToDownload = artifacts.value.find(artifact => { + return artifact.name === name + }) + if (!artifactToDownload) { + throw new Error(`Unable to find an artifact with the name: ${name}`) + } - TODO - } + const items = await getContainerItems( + artifactToDownload.name, + artifactToDownload.fileContainerResourceUrl + ) - Downloads all artifacts associated with a run. Because there are multiple artifacts being downloaded, a folder will be created for each one in the specified or default directory + if (!path) { + path = getWorkSpaceDirectory() + } + path = normalize(path) + path = resolve(path) - export async function downloadAllArtifacts( - path?: string - ): Promise{ + // During upload, empty directories are rejected by the remote server so there should be no artifacts that consist of only empty directories + const downloadSpecification = getDownloadSpecification( + name, + items.value, + path, + options?.createArtifactFolder || false + ) + + if (downloadSpecification.filesToDownload.length === 0) { + core.info( + `No downloadable files were found for the artifact: ${artifactToDownload.name}` + ) + } else { + // Create all necessary directories recursively before starting any download + await createDirectoriesForArtifact( + downloadSpecification.directoryStructure + ) + await downloadSingleArtifact(downloadSpecification.filesToDownload) + } - TODO + return { + artifactName: name, + downloadPath: downloadSpecification.rootDownloadLocation + } + } + + async downloadAllArtifacts( + path?: string | undefined + ): Promise { + const response: DownloadResponse[] = [] + const artifacts = await listArtifacts() + if (artifacts.count === 0) { + core.info('Unable to find any artifacts for the associated workflow') + return response + } + + if (!path) { + path = getWorkSpaceDirectory() + } + path = normalize(path) + path = resolve(path) + + const ARTIFACT_CONCURRENCY = getDownloadArtifactConcurrency() + const parallelDownloads = [...new Array(ARTIFACT_CONCURRENCY).keys()] + let downloadedArtifacts = 0 + await Promise.all( + parallelDownloads.map(async () => { + while (downloadedArtifacts < artifacts.count) { + const currentArtifactToDownload = artifacts.value[downloadedArtifacts] + downloadedArtifacts += 1 + + // Get container entries for the specific artifact + const items = await getContainerItems( + currentArtifactToDownload.name, + currentArtifactToDownload.fileContainerResourceUrl + ) + + // Promise.All is not correctly inferring that 'path' is no longer possibly undefined: https://github.com/microsoft/TypeScript/issues/34925 + const downloadSpecification = getDownloadSpecification( + currentArtifactToDownload.name, + items.value, + path!, // eslint-disable-line @typescript-eslint/no-non-null-assertion + true + ) + if (downloadSpecification.filesToDownload.length === 0) { + core.info( + `No downloadable files were found for any artifact ${currentArtifactToDownload.name}` + ) + } else { + await createDirectoriesForArtifact( + downloadSpecification.directoryStructure + ) + await downloadSingleArtifact(downloadSpecification.filesToDownload) + } + + response.push({ + artifactName: currentArtifactToDownload.name, + downloadPath: downloadSpecification.rootDownloadLocation + }) + } + }) + ) + return response } - */ } diff --git a/packages/artifact/src/internal-config-variables.ts b/packages/artifact/src/internal-config-variables.ts index aef9d34c0e..0cf8c6a127 100644 --- a/packages/artifact/src/internal-config-variables.ts +++ b/packages/artifact/src/internal-config-variables.ts @@ -10,6 +10,15 @@ export function getUploadChunkSize(): number { return 4 * 1024 * 1024 // 4 MB Chunks } +export function getDownloadFileConcurrency(): number { + return 2 +} + +export function getDownloadArtifactConcurrency(): number { + // when downloading all artifact at once, this is number of concurrent artifacts being downloaded + return 1 +} + export function getRuntimeToken(): string { const token = process.env['ACTIONS_RUNTIME_TOKEN'] if (!token) { @@ -33,3 +42,11 @@ export function getWorkFlowRunId(): string { } return workFlowRunId } + +export function getWorkSpaceDirectory(): string { + const workspaceDirectory = process.env['GITHUB_WORKSPACE'] + if (!workspaceDirectory) { + throw new Error('Unable to get GITHUB_WORKSPACE env variable') + } + return workspaceDirectory +} diff --git a/packages/artifact/src/internal-contracts.ts b/packages/artifact/src/internal-contracts.ts index 8124add4a7..7abb6130d2 100644 --- a/packages/artifact/src/internal-contracts.ts +++ b/packages/artifact/src/internal-contracts.ts @@ -31,3 +31,32 @@ export interface UploadResults { size: number failedItems: string[] } + +export interface ListArtifactsResponse { + count: number + value: ArtifactResponse[] +} + +export interface QueryArtifactResponse { + count: number + value: ContainerEntry[] +} + +export interface ContainerEntry { + containerId: number + scopeIdentifier: string + path: string + itemType: string + status: string + fileLength?: number + fileEncoding?: number + fileType?: number + dateCreated: string + dateLastModified: string + createdBy: string + lastModifiedBy: string + itemLocation: string + contentLocation: string + fileId?: number + contentId: string +} diff --git a/packages/artifact/src/internal-download-http-client.ts b/packages/artifact/src/internal-download-http-client.ts new file mode 100644 index 0000000000..bc0005a4c5 --- /dev/null +++ b/packages/artifact/src/internal-download-http-client.ts @@ -0,0 +1,133 @@ +import * as fs from 'fs' +import { + createHttpClient, + getArtifactUrl, + getRequestOptions, + isSuccessStatusCode, + isRetryableStatusCode +} from './internal-utils' +import {URL} from 'url' +import { + ListArtifactsResponse, + QueryArtifactResponse +} from './internal-contracts' +import {IHttpClientResponse} from '@actions/http-client/interfaces' +import {HttpClient} from '@actions/http-client' +import {DownloadItem} from './internal-download-specification' +import {getDownloadFileConcurrency} from './internal-config-variables' +import {warning} from '@actions/core' + +/** + * Gets a list of all artifacts that are in a specific container + */ +export async function listArtifacts(): Promise { + const artifactUrl = getArtifactUrl() + const client = createHttpClient() + const requestOptions = getRequestOptions('application/json') + + const rawResponse = await client.get(artifactUrl, requestOptions) + const body: string = await rawResponse.readBody() + if (isSuccessStatusCode(rawResponse.message.statusCode) && body) { + return JSON.parse(body) + } + // eslint-disable-next-line no-console + console.log(rawResponse) + throw new Error(`Unable to list artifacts for the run`) +} + +/** + * Fetches a set of container items that describe the contents of an artifact + * @param artifactName the name of the artifact + * @param containerUrl the artifact container URL for the run + */ +export async function getContainerItems( + artifactName: string, + containerUrl: string +): Promise { + // The itemPath search parameter controls which containers will be returned + const resourceUrl = new URL(containerUrl) + resourceUrl.searchParams.append('itemPath', artifactName) + + const client = createHttpClient() + const rawResponse = await client.get(resourceUrl.toString()) + const body: string = await rawResponse.readBody() + if (isSuccessStatusCode(rawResponse.message.statusCode) && body) { + return JSON.parse(body) + } + // eslint-disable-next-line no-console + console.log(rawResponse) + throw new Error(`Unable to get ContainersItems from ${resourceUrl}`) +} + +/** + * Concurrently downloads all the files that are part of an artifact + * @param downloadItems information about what items to download and where to save them + */ +export async function downloadSingleArtifact( + downloadItems: DownloadItem[] +): Promise { + const DOWNLOAD_CONCURRENCY = getDownloadFileConcurrency() + // Limit the number of files downloaded at a single time + const parallelDownloads = [...new Array(DOWNLOAD_CONCURRENCY).keys()] + const client = createHttpClient() + let downloadedFiles = 0 + await Promise.all( + parallelDownloads.map(async () => { + while (downloadedFiles < downloadItems.length) { + const currentFileToDownload = downloadItems[downloadedFiles] + downloadedFiles += 1 + await downloadIndividualFile( + client, + currentFileToDownload.sourceLocation, + currentFileToDownload.targetPath + ) + } + }) + ) +} + +/** + * Downloads an individual file + * @param client http client that will be used to make the necessary calls + * @param artifactLocation origin location where a file will be downloaded from + * @param downloadPath destination location for the file being downloaded + */ +export async function downloadIndividualFile( + client: HttpClient, + artifactLocation: string, + downloadPath: string +): Promise { + const stream = fs.createWriteStream(downloadPath) + const response = await client.get(artifactLocation) + if (isSuccessStatusCode(response.message.statusCode)) { + await pipeResponseToStream(response, stream) + } else if (isRetryableStatusCode(response.message.statusCode)) { + warning( + `Received http ${response.message.statusCode} during file download, will retry ${artifactLocation} after 10 seconds` + ) + await new Promise(resolve => setTimeout(resolve, 10000)) + const retryResponse = await client.get(artifactLocation) + if (isSuccessStatusCode(retryResponse.message.statusCode)) { + await pipeResponseToStream(response, stream) + } else { + // eslint-disable-next-line no-console + console.log(retryResponse) + throw new Error(`Unable to download ${artifactLocation}`) + } + } else { + // eslint-disable-next-line no-console + console.log(response) + throw new Error(`Unable to download ${artifactLocation}`) + } +} + +export async function pipeResponseToStream( + response: IHttpClientResponse, + stream: NodeJS.WritableStream +): Promise { + return new Promise(resolve => { + response.message.pipe(stream).on('close', () => { + resolve() + }) + }) +} diff --git a/packages/artifact/src/internal-download-specification.ts b/packages/artifact/src/internal-download-specification.ts new file mode 100644 index 0000000000..6e4da24602 --- /dev/null +++ b/packages/artifact/src/internal-download-specification.ts @@ -0,0 +1,78 @@ +import * as path from 'path' +import {ContainerEntry} from './internal-contracts' + +export interface DownloadSpecification { + // root download location for the artifact + rootDownloadLocation: string + + // directories that need to be created for all the items in the artifact + directoryStructure: string[] + + // individual files that need to be downloaded as part of the artifact + filesToDownload: DownloadItem[] +} + +export interface DownloadItem { + // Url that denotes where to download the item from + sourceLocation: string + + // Information about where the file should be downloaded to + targetPath: string +} + +/** + * Creates a specification for a set of files that will be downloaded + * @param artifactName the name of the artifact + * @param artifactEntries a set of container entries that describe that files that make up an artifact + * @param downloadPath the path where the artifact will be downloaded to + * @param includeRootDirectory specifies if there should be an extra directory (denoted by the artifact name) where the artifact files should be downloaded to + */ +export function getDownloadSpecification( + artifactName: string, + artifactEntries: ContainerEntry[], + downloadPath: string, + includeRootDirectory: boolean +): DownloadSpecification { + const directories = new Set() + + const specifications: DownloadSpecification = { + rootDownloadLocation: includeRootDirectory + ? path.join(downloadPath, artifactName) + : downloadPath, + directoryStructure: [], + filesToDownload: [] + } + + for (const entry of artifactEntries) { + // Ignore artifacts in the container that don't begin with the same name + if ( + entry.path.startsWith(`${artifactName}/`) || + entry.path.startsWith(`${artifactName}\\`) + ) { + // normalize all separators to the local OS + const normalizedPathEntry = path.normalize(entry.path) + // entry.path always starts with the artifact name, if includeRootDirectory is false, remove the name from the beginning of the path + const filePath = path.join( + downloadPath, + includeRootDirectory + ? normalizedPathEntry + : normalizedPathEntry.replace(artifactName, '') + ) + + // Case insensitive folder structure maintained in the backend, not every folder is created so the 'folder' + // itemType cannot be relied upon. The file must be used to determine the directory structure + if (entry.itemType === 'file') { + // Get the directories that we need to create from the filePath for each individual file + directories.add(path.dirname(filePath)) + + specifications.filesToDownload.push({ + sourceLocation: entry.contentLocation, + targetPath: filePath + }) + } + } + } + + specifications.directoryStructure = Array.from(directories) + return specifications +} diff --git a/packages/artifact/src/internal-utils.ts b/packages/artifact/src/internal-utils.ts index afc2434270..7140776b82 100644 --- a/packages/artifact/src/internal-utils.ts +++ b/packages/artifact/src/internal-utils.ts @@ -1,4 +1,5 @@ import {debug} from '@actions/core' +import {promises as fs} from 'fs' import {HttpCodes, HttpClient} from '@actions/http-client' import {BearerCredentialHandler} from '@actions/http-client/auth' import {IHeaders} from '@actions/http-client/interfaces' @@ -113,3 +114,13 @@ export function checkArtifactName(name: string): void { } } } + +export async function createDirectoriesForArtifact( + directories: string[] +): Promise { + for (const directory of directories) { + await fs.mkdir(directory, { + recursive: true + }) + } +} From fa03eb4d22d049c5c489afe5655ebdeb1f7084fa Mon Sep 17 00:00:00 2001 From: Ryo Ota Date: Wed, 19 Feb 2020 05:43:07 +0900 Subject: [PATCH 092/192] Use import {Octokit} (#332) * Use import {Octokit} * Update @octokit/rest to 16.43.1 --- packages/github/package-lock.json | 119 ++++++++++++++---------------- packages/github/package.json | 2 +- packages/github/src/github.ts | 2 +- 3 files changed, 59 insertions(+), 64 deletions(-) diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index c3bcb49cf7..88a861af87 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -391,6 +391,14 @@ "@types/yargs": "^12.0.9" } }, + "@octokit/auth-token": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.0.tgz", + "integrity": "sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg==", + "requires": { + "@octokit/types": "^2.0.0" + } + }, "@octokit/endpoint": { "version": "5.5.1", "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.1.tgz", @@ -431,6 +439,28 @@ } } }, + "@octokit/plugin-paginate-rest": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz", + "integrity": "sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q==", + "requires": { + "@octokit/types": "^2.0.1" + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz", + "integrity": "sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw==" + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz", + "integrity": "sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ==", + "requires": { + "@octokit/types": "^2.0.1", + "deprecation": "^2.3.1" + } + }, "@octokit/request": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.1.tgz", @@ -467,61 +497,26 @@ } }, "@octokit/rest": { - "version": "16.15.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.15.0.tgz", - "integrity": "sha512-Un+e7rgh38RtPOTe453pT/KPM/p2KZICimBmuZCd2wEo8PacDa4h6RqTPZs+f2DPazTTqdM7QU4LKlUjgiBwWw==", - "requires": { - "@octokit/request": "2.3.0", - "before-after-hook": "^1.2.0", + "version": "16.43.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz", + "integrity": "sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw==", + "requires": { + "@octokit/auth-token": "^2.4.0", + "@octokit/plugin-paginate-rest": "^1.1.1", + "@octokit/plugin-request-log": "^1.0.0", + "@octokit/plugin-rest-endpoint-methods": "2.4.0", + "@octokit/request": "^5.2.0", + "@octokit/request-error": "^1.0.2", + "atob-lite": "^2.0.0", + "before-after-hook": "^2.0.0", "btoa-lite": "^1.0.0", + "deprecation": "^2.0.0", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", "lodash.uniq": "^4.5.0", "octokit-pagination-methods": "^1.1.0", - "universal-user-agent": "^2.0.0", - "url-template": "^2.0.8" - }, - "dependencies": { - "@octokit/endpoint": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-3.2.3.tgz", - "integrity": "sha512-yUPCt4vMIOclox13CUxzuKiPJIFo46b/6GhUnUTw5QySczN1L0DtSxgmIZrZV4SAb9EyAqrceoyrWoYVnfF2AA==", - "requires": { - "deepmerge": "3.2.0", - "is-plain-object": "^2.0.4", - "universal-user-agent": "^2.0.1", - "url-template": "^2.0.8" - } - }, - "@octokit/request": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-2.3.0.tgz", - "integrity": "sha512-5YRqYNZOAaL7+nt7w3Scp6Sz4P2g7wKFP9npx1xdExMomk8/M/ICXVLYVam2wzxeY0cIc6wcKpjC5KI4jiNbGw==", - "requires": { - "@octokit/endpoint": "^3.1.1", - "is-plain-object": "^2.0.4", - "node-fetch": "^2.3.0", - "universal-user-agent": "^2.0.1" - } - }, - "deepmerge": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.2.0.tgz", - "integrity": "sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow==" - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "requires": { - "isobject": "^3.0.1" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - } + "once": "^1.4.0", + "universal-user-agent": "^4.0.0" } }, "@octokit/types": { @@ -830,6 +825,11 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "atob-lite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", + "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=" + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -971,9 +971,9 @@ } }, "before-after-hook": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-1.4.0.tgz", - "integrity": "sha512-l5r9ir56nda3qu14nAXIlyq1MmUSs0meCIaFAh8HwkFwP1F8eToOuS3ah2VAHHcY04jaYD7FpJC5JTXHYRbkzg==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", + "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==" }, "brace-expansion": { "version": "1.1.11", @@ -5084,11 +5084,11 @@ } }, "universal-user-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-2.1.0.tgz", - "integrity": "sha512-8itiX7G05Tu3mGDTdNY2fB4KJ8MgZLS54RdG6PkkfwMAavrXu1mV/lls/GABx9O3Rw4PnTtasxrvbMQoBYY92Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", + "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", "requires": { - "os-name": "^3.0.0" + "os-name": "^3.1.0" } }, "unset-value": { @@ -5152,11 +5152,6 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, - "url-template": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", - "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=" - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", diff --git a/packages/github/package.json b/packages/github/package.json index ee68a04fc5..e706854310 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -39,7 +39,7 @@ "dependencies": { "@actions/http-client": "^1.0.3", "@octokit/graphql": "^4.3.1", - "@octokit/rest": "^16.15.0" + "@octokit/rest": "^16.43.1" }, "devDependencies": { "jest": "^24.7.1", diff --git a/packages/github/src/github.ts b/packages/github/src/github.ts index a2ef233875..515f3700c1 100644 --- a/packages/github/src/github.ts +++ b/packages/github/src/github.ts @@ -9,7 +9,7 @@ import { RequestParameters as GraphQLRequestParameters } from '@octokit/graphql/dist-types/types' -import Octokit from '@octokit/rest' +import {Octokit} from '@octokit/rest' import * as Context from './context' import * as http from 'http' import * as httpClient from '@actions/http-client' From 41157b23c7da06e5dc5657b24f67d451cd282db7 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Thu, 20 Feb 2020 14:02:42 -0500 Subject: [PATCH 093/192] Release 2.1.1 of @actions/github (#357) --- packages/github/RELEASES.md | 5 +++++ packages/github/package-lock.json | 2 +- packages/github/package.json | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/github/RELEASES.md b/packages/github/RELEASES.md index 81cb537feb..b56ec46549 100644 --- a/packages/github/RELEASES.md +++ b/packages/github/RELEASES.md @@ -1,5 +1,10 @@ # @actions/github Releases +### 2.1.1 + +- [Use import {Octokit}](https://github.com/actions/toolkit/pull/332) +- [Check proxy bypass before setting proxy agent](https://github.com/actions/toolkit/pull/320) + ### 2.1.0 - [Octokit client follows proxy settings](https://github.com/actions/toolkit/pull/314) diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index 88a861af87..4f775654fa 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "2.1.0", + "version": "2.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/github/package.json b/packages/github/package.json index e706854310..d91663789a 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "2.1.0", + "version": "2.1.1", "description": "Actions github lib", "keywords": [ "github", From d17d4a916377cc569a5c642b9d2f56c23d1ab620 Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Thu, 20 Feb 2020 15:05:07 -0500 Subject: [PATCH 094/192] Add info about new @actions/artifact package --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index c58f1b130e..17161c8634 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,15 @@ $ npm install @actions/github --save ```
+:floppy_disk: [@actions/artifact](packages/artifact) + +Provides functions to interact with actions artifacts. Read more [here](packages/artifact) + +```bash +$ npm install @actions/artifact --save +``` +
+ ## Creating an Action with the Toolkit :question: [Choosing an action type](docs/action-types.md) From 54bcb7c4f1a53c3c6022f4f5e8be67b8fcf23462 Mon Sep 17 00:00:00 2001 From: Josh Gross Date: Wed, 26 Feb 2020 11:43:55 -0500 Subject: [PATCH 095/192] Update tool cache docs (#347) --- packages/tool-cache/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tool-cache/README.md b/packages/tool-cache/README.md index a1513d3529..f45cedd8a6 100644 --- a/packages/tool-cache/README.md +++ b/packages/tool-cache/README.md @@ -37,7 +37,7 @@ else { #### Cache -Finally, you can cache these directories in our tool-cache. This is useful if you want to switch back and forth between versions of a tool, or save a tool between runs for private runners (private runners are still in development but are on the roadmap). +Finally, you can cache these directories in our tool-cache. This is useful if you want to switch back and forth between versions of a tool, or save a tool between runs for self-hosted runners. You'll often want to add it to the path as part of this step: From a649207792d1b13ec1f5c09a62b208a9490aa73b Mon Sep 17 00:00:00 2001 From: Tingluo Huang Date: Mon, 2 Mar 2020 07:45:27 -0500 Subject: [PATCH 096/192] Add core.isDebug() to check whether actions_step_debug is on or not. (#278) --- docs/action-debugging.md | 25 +++++++++++++++---------- packages/core/README.md | 6 ++++++ packages/core/__tests__/core.test.ts | 13 +++++++++++++ packages/core/src/core.ts | 7 +++++++ 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/docs/action-debugging.md b/docs/action-debugging.md index af5c88b621..652269203a 100644 --- a/docs/action-debugging.md +++ b/docs/action-debugging.md @@ -1,9 +1,23 @@ # Debugging -If the build logs do not provide enough detail on why a build may be failing, some other options exist to assist with troubleshooting. +If the job logs do not provide enough detail on why a job may be failing, some other options exist to assist with troubleshooting. + +## Step Debug Logs +This is the primary way for customers to debug job failures caused by failed steps. + +Step debug logs increase the verbosity of a job's logs during and after a job's execution to assist with troubleshooting. + +Additional log events with the prefix `::debug::` will now also appear in the job's logs, these log events are provided by the Action's author and the runner process. + +### How to Access Step Debug Logs +This flag can be enabled by [setting the secret](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets#creating-encrypted-secrets) `ACTIONS_STEP_DEBUG` to `true`. + +All actions ran while this secret is enabled will show debug events in the [Downloaded Logs](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#downloading-logs-and-artifacts) and [Web Logs](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#viewing-logs-to-diagnose-failures). ## Runner Diagnostic Logs Runner Diagnostic Logs provide additional log files detailing how the Runner is executing an action. +You need the runner diagnostic logs only if you think there is an infrastructure problem with GitHub Actions and you want the product team to check the logs. + Each file contains different logging information that corresponds to that process: * The Runner process coordinates setting up workers to execute jobs. * The Worker process executes the job. @@ -15,12 +29,3 @@ These log files are enabled by [setting the secret](https://help.github.com/en/a All actions ran while this secret is enabled contain additional diagnostic log files in the `runner-diagnostic-logs` folder of the [log archive](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#downloading-logs-and-artifacts). -## Step Debug Logs -Step debug logs increase the verbosity of a job's logs during and after a job's execution to assist with troubleshooting. - -Additional log events with the prefix `::debug::` will now also appear in the job's logs. - -### How to Access Step Debug Logs -This flag can be enabled by [setting the secret](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets#creating-encrypted-secrets) `ACTIONS_STEP_DEBUG` to `true`. - -All actions ran while this secret is enabled will show debug events in the [Downloaded Logs](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#downloading-logs-and-artifacts) and [Web Logs](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#viewing-logs-to-diagnose-failures). diff --git a/packages/core/README.md b/packages/core/README.md index 457f73ccea..5ad27eedee 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -82,6 +82,12 @@ try { core.warning('myInput was not set'); } + if (core.isDebug()) { + // curl -v https://github.com + } else { + // curl https://github.com + } + // Do stuff } catch (err) { diff --git a/packages/core/__tests__/core.test.ts b/packages/core/__tests__/core.test.ts index 90235428e8..020f4a51b9 100644 --- a/packages/core/__tests__/core.test.ts +++ b/packages/core/__tests__/core.test.ts @@ -177,6 +177,19 @@ describe('@actions/core', () => { it('getState gets wrapper action state', () => { expect(core.getState('TEST_1')).toBe('state_val') }) + + it('isDebug check debug state', () => { + const current = process.env['RUNNER_DEBUG'] + try { + delete process.env.RUNNER_DEBUG + expect(core.isDebug()).toBe(false) + + process.env['RUNNER_DEBUG'] = '1' + expect(core.isDebug()).toBe(true) + } finally { + process.env['RUNNER_DEBUG'] = current + } + }) }) // Assert that process.stdout.write calls called only with the given arguments. diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index dcaab53e58..9072f46a79 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -102,6 +102,13 @@ export function setFailed(message: string): void { // Logging Commands //----------------------------------------------------------------------- +/** + * Gets whether Actions Step Debug is on or not + */ +export function isDebug(): boolean { + return process.env['RUNNER_DEBUG'] === '1' +} + /** * Writes debug message to user log * @param message debug message From 3261dd988308cb227481dc6580026034e5780160 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Mon, 2 Mar 2020 08:02:40 -0500 Subject: [PATCH 097/192] core 1.2.3 release (#366) --- packages/core/RELEASES.md | 4 ++++ packages/core/package-lock.json | 2 +- packages/core/package.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/core/RELEASES.md b/packages/core/RELEASES.md index 53428af497..0a518b0dfb 100644 --- a/packages/core/RELEASES.md +++ b/packages/core/RELEASES.md @@ -1,5 +1,9 @@ # @actions/core Releases +### 1.2.3 + +- [IsDebug logging](README.md#logging) + ### 1.2.2 - [Fix escaping for runner commands](https://github.com/actions/toolkit/pull/302) diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json index 2e8b88fdd8..2aa13e494b 100644 --- a/packages/core/package-lock.json +++ b/packages/core/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/core", - "version": "1.2.2", + "version": "1.2.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index 7be0dc09e4..444e14eb29 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@actions/core", - "version": "1.2.2", + "version": "1.2.3", "description": "Actions core lib", "keywords": [ "github", From 6459481e988198bf53e186abf23350b085b1250f Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Tue, 3 Mar 2020 11:16:15 -0500 Subject: [PATCH 098/192] full sha binding in versioning doc --- docs/action-versioning.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/action-versioning.md b/docs/action-versioning.md index 0e4b150f6f..7b71f93cd8 100644 --- a/docs/action-versioning.md +++ b/docs/action-versioning.md @@ -8,7 +8,7 @@ Examples: steps: - uses: actions/javascript-action@v1 # recommended. starter workflows use this - uses: actions/javascript-action@v1.0.0 # if an action offers specific releases - - uses: actions/javascript-action@41775a4 # binding to a specific sha + - uses: actions/javascript-action@41775a4da8ffae865553a738ab8ac1cd5a3c0044 # sha ``` # Compatibility @@ -26,7 +26,7 @@ steps: - uses: actions/javascript-action@master # do not do this ``` -Binding to the immutable sha1 may offer more reliability. However, note that the hosted images toolsets (e.g. ubuntu-latest) move forward and if there is a tool breaking issue, actions may react with a patch to a major version to compensate so binding to a specific SHA may prevent you from getting fixes. +Binding to the immutable full sha1 may offer more reliability. However, note that the hosted images toolsets (e.g. ubuntu-latest) move forward and if there is a tool breaking issue, actions may react with a patch to a major version to compensate so binding to a specific SHA may prevent you from getting fixes. > Recommendation: bind to major versions to get functionality and fixes but reserve binding to a specific release or SHA as a mitigation strategy for unforeseen breaks. From 259743ae1332c02ec068599dd8c6c6f02cef6671 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Thu, 5 Mar 2020 12:05:27 -0500 Subject: [PATCH 099/192] update downloadTool to handle errors from response stream and retry (#369) --- packages/artifact/package-lock.json | 5 - packages/tool-cache/RELEASES.md | 4 + .../tool-cache/__tests__/retry-helper.test.ts | 87 +++++++++++++ .../tool-cache/__tests__/tool-cache.test.ts | 102 +++++++++++++++ packages/tool-cache/package.json | 2 +- packages/tool-cache/src/retry-helper.ts | 55 +++++++++ packages/tool-cache/src/tool-cache.ts | 116 +++++++++++------- 7 files changed, 320 insertions(+), 51 deletions(-) create mode 100644 packages/tool-cache/__tests__/retry-helper.test.ts create mode 100644 packages/tool-cache/src/retry-helper.ts diff --git a/packages/artifact/package-lock.json b/packages/artifact/package-lock.json index d8cd2feb44..d4a64a1e36 100644 --- a/packages/artifact/package-lock.json +++ b/packages/artifact/package-lock.json @@ -4,11 +4,6 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@actions/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.1.tgz", - "integrity": "sha512-xD+CQd9p4lU7ZfRqmUcbJpqR+Ss51rJRVeXMyOLrZQImN9/8Sy/BEUBnHO/UKD3z03R686PVTLfEPmkropGuLw==" - }, "@actions/http-client": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.6.tgz", diff --git a/packages/tool-cache/RELEASES.md b/packages/tool-cache/RELEASES.md index 24a6b70b5d..aeafeffdb9 100644 --- a/packages/tool-cache/RELEASES.md +++ b/packages/tool-cache/RELEASES.md @@ -1,5 +1,9 @@ # @actions/tool-cache Releases +### 1.3.2 + +- [Update downloadTool with better error handling and retries](https://github.com/actions/toolkit/pull/369) + ### 1.3.1 - [Increase http-client min version](https://github.com/actions/toolkit/pull/314) diff --git a/packages/tool-cache/__tests__/retry-helper.test.ts b/packages/tool-cache/__tests__/retry-helper.test.ts new file mode 100644 index 0000000000..cccf00757e --- /dev/null +++ b/packages/tool-cache/__tests__/retry-helper.test.ts @@ -0,0 +1,87 @@ +import * as core from '@actions/core' +import {RetryHelper} from '../src/retry-helper' + +let info: string[] +let retryHelper: RetryHelper + +describe('retry-helper tests', () => { + beforeAll(() => { + // Mock @actions/core info() + jest.spyOn(core, 'info').mockImplementation((message: string) => { + info.push(message) + }) + + retryHelper = new RetryHelper(3, 0, 0) + }) + + beforeEach(() => { + // Reset info + info = [] + }) + + afterAll(() => { + // Restore + jest.restoreAllMocks() + }) + + it('first attempt succeeds', async () => { + const actual = await retryHelper.execute(async () => { + return 'some result' + }) + expect(actual).toBe('some result') + expect(info).toHaveLength(0) + }) + + it('second attempt succeeds', async () => { + let attempts = 0 + const actual = await retryHelper.execute(async () => { + if (++attempts === 1) { + throw new Error('some error') + } + + return Promise.resolve('some result') + }) + expect(attempts).toBe(2) + expect(actual).toBe('some result') + expect(info).toHaveLength(2) + expect(info[0]).toBe('some error') + expect(info[1]).toMatch(/Waiting .+ seconds before trying again/) + }) + + it('third attempt succeeds', async () => { + let attempts = 0 + const actual = await retryHelper.execute(async () => { + if (++attempts < 3) { + throw new Error(`some error ${attempts}`) + } + + return Promise.resolve('some result') + }) + expect(attempts).toBe(3) + expect(actual).toBe('some result') + expect(info).toHaveLength(4) + expect(info[0]).toBe('some error 1') + expect(info[1]).toMatch(/Waiting .+ seconds before trying again/) + expect(info[2]).toBe('some error 2') + expect(info[3]).toMatch(/Waiting .+ seconds before trying again/) + }) + + it('all attempts fail succeeds', async () => { + let attempts = 0 + let error: Error = (null as unknown) as Error + try { + await retryHelper.execute(() => { + throw new Error(`some error ${++attempts}`) + }) + } catch (err) { + error = err + } + expect(error.message).toBe('some error 3') + expect(attempts).toBe(3) + expect(info).toHaveLength(4) + expect(info[0]).toBe('some error 1') + expect(info[1]).toMatch(/Waiting .+ seconds before trying again/) + expect(info[2]).toBe('some error 2') + expect(info[3]).toMatch(/Waiting .+ seconds before trying again/) + }) +}) diff --git a/packages/tool-cache/__tests__/tool-cache.test.ts b/packages/tool-cache/__tests__/tool-cache.test.ts index a28900856a..7888fa2adc 100644 --- a/packages/tool-cache/__tests__/tool-cache.test.ts +++ b/packages/tool-cache/__tests__/tool-cache.test.ts @@ -2,6 +2,7 @@ import * as fs from 'fs' import * as path from 'path' import * as io from '@actions/io' import * as exec from '@actions/exec' +import * as stream from 'stream' import nock from 'nock' const cachePath = path.join(__dirname, 'CACHE') @@ -24,6 +25,8 @@ describe('@actions/tool-cache', function() { username: 'abc', password: 'def' }) + setGlobal('TEST_DOWNLOAD_TOOL_RETRY_MIN_SECONDS', 0) + setGlobal('TEST_DOWNLOAD_TOOL_RETRY_MAX_SECONDS', 0) }) beforeEach(async function() { @@ -33,9 +36,15 @@ describe('@actions/tool-cache', function() { await io.mkdirP(tempPath) }) + afterEach(function() { + setResponseMessageFactory(undefined) + }) + afterAll(async function() { await io.rmRF(tempPath) await io.rmRF(cachePath) + setGlobal('TEST_DOWNLOAD_TOOL_RETRY_MIN_SECONDS', undefined) + setGlobal('TEST_DOWNLOAD_TOOL_RETRY_MAX_SECONDS', undefined) }) it('downloads a 35 byte file', async () => { @@ -90,6 +99,7 @@ describe('@actions/tool-cache', function() { it('downloads a 35 byte file after a redirect', async () => { nock('http://example.com') + .persist() .get('/redirect-to') .reply(303, undefined, { location: 'http://example.com/bytes/35' @@ -103,8 +113,75 @@ describe('@actions/tool-cache', function() { expect(fs.statSync(downPath).size).toBe(35) }) + it('handles error from response message stream', async () => { + nock('http://example.com') + .persist() + .get('/error-from-response-message-stream') + .reply(200, {}) + + setResponseMessageFactory(() => { + const readStream = new stream.Readable() + /* eslint-disable @typescript-eslint/unbound-method */ + readStream._read = () => { + readStream.destroy(new Error('uh oh')) + } + /* eslint-enable @typescript-eslint/unbound-method */ + return readStream + }) + + let error = new Error('unexpected') + try { + await tc.downloadTool( + 'http://example.com/error-from-response-message-stream' + ) + } catch (err) { + error = err + } + + expect(error).not.toBeUndefined() + expect(error.message).toBe('uh oh') + }) + + it('retries error from response message stream', async () => { + nock('http://example.com') + .persist() + .get('/retries-error-from-response-message-stream') + .reply(200, {}) + + /* eslint-disable @typescript-eslint/unbound-method */ + let attempt = 1 + setResponseMessageFactory(() => { + const readStream = new stream.Readable() + let failsafe = 0 + readStream._read = () => { + // First attempt fails + if (attempt++ === 1) { + readStream.destroy(new Error('uh oh')) + return + } + + // Write some data + if (failsafe++ === 0) { + readStream.push(''.padEnd(35)) + readStream.push(null) // no more data + } + } + + return readStream + }) + /* eslint-enable @typescript-eslint/unbound-method */ + + const downPath = await tc.downloadTool( + 'http://example.com/retries-error-from-response-message-stream' + ) + + expect(fs.existsSync(downPath)).toBeTruthy() + expect(fs.statSync(downPath).size).toBe(35) + }) + it('has status code in exception dictionary for HTTP error code responses', async () => { nock('http://example.com') + .persist() .get('/bytes/bad') .reply(400, { username: 'bad', @@ -124,6 +201,7 @@ describe('@actions/tool-cache', function() { it('works with redirect code 302', async function() { nock('http://example.com') + .persist() .get('/redirect-to') .reply(302, undefined, { location: 'http://example.com/bytes/35' @@ -542,3 +620,27 @@ describe('@actions/tool-cache', function() { } }) }) + +/** + * Sets up a mock response body for downloadTool. This function works around a limitation with + * nock when the response stream emits an error. + */ +function setResponseMessageFactory( + factory: (() => stream.Readable) | undefined +): void { + setGlobal('TEST_DOWNLOAD_TOOL_RESPONSE_MESSAGE_FACTORY', factory) +} + +/** + * Sets a global variable + */ +function setGlobal(key: string, value: T | undefined): void { + /* eslint-disable @typescript-eslint/no-explicit-any */ + const g = global as any + /* eslint-enable @typescript-eslint/no-explicit-any */ + if (value === undefined) { + delete g[key] + } else { + g[key] = value + } +} diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index 41f73293ef..7d2bf1e1b3 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.3.1", + "version": "1.3.2", "description": "Actions tool-cache lib", "keywords": [ "github", diff --git a/packages/tool-cache/src/retry-helper.ts b/packages/tool-cache/src/retry-helper.ts new file mode 100644 index 0000000000..36bde0bfed --- /dev/null +++ b/packages/tool-cache/src/retry-helper.ts @@ -0,0 +1,55 @@ +import * as core from '@actions/core' + +/** + * Internal class for retries + */ +export class RetryHelper { + private maxAttempts: number + private minSeconds: number + private maxSeconds: number + + constructor(maxAttempts: number, minSeconds: number, maxSeconds: number) { + if (maxAttempts < 1) { + throw new Error('max attempts should be greater than or equal to 1') + } + + this.maxAttempts = maxAttempts + this.minSeconds = Math.floor(minSeconds) + this.maxSeconds = Math.floor(maxSeconds) + if (this.minSeconds > this.maxSeconds) { + throw new Error('min seconds should be less than or equal to max seconds') + } + } + + async execute(action: () => Promise): Promise { + let attempt = 1 + while (attempt < this.maxAttempts) { + // Try + try { + return await action() + } catch (err) { + core.info(err.message) + } + + // Sleep + const seconds = this.getSleepAmount() + core.info(`Waiting ${seconds} seconds before trying again`) + await this.sleep(seconds) + attempt++ + } + + // Last attempt + return await action() + } + + private getSleepAmount(): number { + return ( + Math.floor(Math.random() * (this.maxSeconds - this.minSeconds + 1)) + + this.minSeconds + ) + } + + private async sleep(seconds: number): Promise { + return new Promise(resolve => setTimeout(resolve, seconds * 1000)) + } +} diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index 7cb581acd2..67cee42854 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -5,10 +5,13 @@ import * as os from 'os' import * as path from 'path' import * as httpm from '@actions/http-client' import * as semver from 'semver' +import * as stream from 'stream' +import * as util from 'util' import uuidV4 from 'uuid/v4' import {exec} from '@actions/exec/lib/exec' import {ExecOptions} from '@actions/exec/lib/interfaces' import {ok} from 'assert' +import {RetryHelper} from './retry-helper' export class HTTPError extends Error { constructor(readonly httpStatusCode: number | undefined) { @@ -55,55 +58,68 @@ export async function downloadTool( url: string, dest?: string ): Promise { - // Wrap in a promise so that we can resolve from within stream callbacks - return new Promise(async (resolve, reject) => { - try { - const http = new httpm.HttpClient(userAgent, [], { - allowRetries: true, - maxRetries: 3 - }) - dest = dest || path.join(tempDirectory, uuidV4()) - await io.mkdirP(path.dirname(dest)) - core.debug(`Downloading ${url}`) - core.debug(`Downloading ${dest}`) - - if (fs.existsSync(dest)) { - throw new Error(`Destination file path ${dest} already exists`) - } + dest = dest || path.join(tempDirectory, uuidV4()) + await io.mkdirP(path.dirname(dest)) + core.debug(`Downloading ${url}`) + core.debug(`Destination ${dest}`) + + const maxAttempts = 3 + const minSeconds = getGlobal( + 'TEST_DOWNLOAD_TOOL_RETRY_MIN_SECONDS', + 10 + ) + const maxSeconds = getGlobal( + 'TEST_DOWNLOAD_TOOL_RETRY_MAX_SECONDS', + 20 + ) + const retryHelper = new RetryHelper(maxAttempts, minSeconds, maxSeconds) + return await retryHelper.execute( + async () => await downloadToolAttempt(url, dest || '') + ) +} - const response: httpm.HttpClientResponse = await http.get(url) +async function downloadToolAttempt(url: string, dest: string): Promise { + if (fs.existsSync(dest)) { + throw new Error(`Destination file path ${dest} already exists`) + } - if (response.message.statusCode !== 200) { - const err = new HTTPError(response.message.statusCode) - core.debug( - `Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})` - ) - throw err - } + // Get the response headers + const http = new httpm.HttpClient(userAgent, [], { + allowRetries: false + }) + const response: httpm.HttpClientResponse = await http.get(url) + if (response.message.statusCode !== 200) { + const err = new HTTPError(response.message.statusCode) + core.debug( + `Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})` + ) + throw err + } - const file: NodeJS.WritableStream = fs.createWriteStream(dest) - file.on('open', async () => { - try { - const stream = response.message.pipe(file) - stream.on('close', () => { - core.debug('download complete') - resolve(dest) - }) - } catch (err) { - core.debug( - `Failed to download from "${url}". Code(${response.message.statusCode}) Message(${response.message.statusMessage})` - ) - reject(err) - } - }) - file.on('error', err => { - file.end() - reject(err) - }) - } catch (err) { - reject(err) + // Download the response body + const pipeline = util.promisify(stream.pipeline) + const responseMessageFactory = getGlobal<() => stream.Readable>( + 'TEST_DOWNLOAD_TOOL_RESPONSE_MESSAGE_FACTORY', + () => response.message + ) + const readStream = responseMessageFactory() + let succeeded = false + try { + await pipeline(readStream, fs.createWriteStream(dest)) + core.debug('download complete') + succeeded = true + return dest + } finally { + // Error, delete dest before retry + if (!succeeded) { + core.debug('download failed') + try { + await io.rmRF(dest) + } catch (err) { + core.debug(`Failed to delete '${dest}'. ${err.message}`) + } } - }) + } } /** @@ -516,3 +532,13 @@ function _evaluateVersions(versions: string[], versionSpec: string): string { return version } + +/** + * Gets a global variable + */ +function getGlobal(key: string, defaultValue: T): T { + /* eslint-disable @typescript-eslint/no-explicit-any */ + const value = (global as any)[key] as T | undefined + /* eslint-enable @typescript-eslint/no-explicit-any */ + return value !== undefined ? value : defaultValue +} From df0aa9077aff6f213ff74c4c315663a4279aa662 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Thu, 5 Mar 2020 12:07:25 -0500 Subject: [PATCH 100/192] generated --- packages/tool-cache/package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tool-cache/package-lock.json b/packages/tool-cache/package-lock.json index 9eb9a83dd5..0910c9ba3c 100644 --- a/packages/tool-cache/package-lock.json +++ b/packages/tool-cache/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.3.1", + "version": "1.3.2", "lockfileVersion": 1, "requires": true, "dependencies": { From 82fbe5da0f2aacb864cb4110af22aa31cbca2f96 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Mon, 9 Mar 2020 14:17:29 -0400 Subject: [PATCH 101/192] Update jest to 25.1 (#374) * Update jest to 25.1 * Update acorn to 6.4.1 * Update dependencies, run audit on all packages, update packagelock * Remove package-lock dependencies --- package-lock.json | 11226 +++++++++++++++++++++------- package.json | 2 +- packages/github/package-lock.json | 3656 ++++----- packages/github/package.json | 2 +- 4 files changed, 10215 insertions(+), 4671 deletions(-) diff --git a/package-lock.json b/package-lock.json index 188df73360..21b3357186 100644 --- a/package-lock.json +++ b/package-lock.json @@ -68,9 +68,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", - "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", "dev": true }, "@babel/helper-split-export-declaration": { @@ -110,13 +110,22 @@ "integrity": "sha512-gxpEUhTS1sGA63EGQGuA+WESPR/6tz6ng7tSHFCmaTJK/cGK8y37cBTspX+U2xCAue2IQVvF6Z0oigmjwD8YGQ==", "dev": true }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, "@babel/plugin-syntax-object-rest-spread": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", - "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/template": { @@ -158,6 +167,12 @@ "to-fast-properties": "^2.0.0" } }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, "@cnakazawa/watch": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", @@ -314,6 +329,81 @@ } } }, + "@istanbuljs/load-nyc-config": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", + "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, "@jest/console": { "version": "24.7.1", "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", @@ -326,329 +416,657 @@ } }, "@jest/core": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz", - "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/reporters": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-25.1.0.tgz", + "integrity": "sha512-iz05+NmwCmZRzMXvMo6KFipW7nzhbpEawrKrkkdJzgytavPse0biEnCNr2wRlyCsp3SmKaEY+SGv7YWYQnIdig==", + "dev": true, + "requires": { + "@jest/console": "^25.1.0", + "@jest/reporters": "^25.1.0", + "@jest/test-result": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-changed-files": "^24.9.0", - "jest-config": "^24.9.0", - "jest-haste-map": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.9.0", - "jest-resolve-dependencies": "^24.9.0", - "jest-runner": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-snapshot": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "jest-watcher": "^24.9.0", - "micromatch": "^3.1.10", - "p-each-series": "^1.0.0", + "graceful-fs": "^4.2.3", + "jest-changed-files": "^25.1.0", + "jest-config": "^25.1.0", + "jest-haste-map": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-resolve": "^25.1.0", + "jest-resolve-dependencies": "^25.1.0", + "jest-runner": "^25.1.0", + "jest-runtime": "^25.1.0", + "jest-snapshot": "^25.1.0", + "jest-util": "^25.1.0", + "jest-validate": "^25.1.0", + "jest-watcher": "^25.1.0", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", "realpath-native": "^1.1.0", - "rimraf": "^2.5.4", - "slash": "^2.0.0", - "strip-ansi": "^5.0.0" + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" }, "dependencies": { - "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" + "@babel/highlight": "^7.8.3" } }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "@babel/generator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", "dev": true, "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" + "@babel/types": "^7.8.7", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } } }, - "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" }, "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "has-flag": "^3.0.0" } } } }, + "@babel/parser": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "dev": true + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "@babel/traverse": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@jest/console": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", + "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "dev": true, + "requires": { + "@jest/source-map": "^25.1.0", + "chalk": "^3.0.0", + "jest-util": "^25.1.0", + "slash": "^3.0.0" + } + }, + "@jest/source-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", + "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.3", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", + "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "dev": true, + "requires": { + "@jest/console": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, "@jest/transform": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", - "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", + "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^24.9.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", + "@jest/types": "^25.1.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.9.0", - "jest-regex-util": "^24.9.0", - "jest-util": "^24.9.0", - "micromatch": "^3.1.10", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-util": "^25.1.0", + "micromatch": "^4.0.2", "pirates": "^4.0.1", "realpath-native": "^1.1.0", - "slash": "^2.0.0", + "slash": "^3.0.0", "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" - }, - "dependencies": { - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", - "dev": true - } + "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" } }, "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", "dev": true, "requires": { "@types/yargs-parser": "*" } }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } + }, "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "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.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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 }, "diff-sequences": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", - "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.1.0.tgz", + "integrity": "sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw==", "dev": true }, "expect": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", - "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-25.1.0.tgz", + "integrity": "sha512-wqHzuoapQkhc3OKPlrpetsfueuEiMf3iWh0R8+duCu9PIjXoP7HgD5aeypwTnXUAjC8aMsiVDaWwlbJ1RlQ38g==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-regex-util": "^24.9.0" + "@jest/types": "^25.1.0", + "ansi-styles": "^4.0.0", + "jest-get-type": "^25.1.0", + "jest-matcher-utils": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-regex-util": "^25.1.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" }, "dependencies": { - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "@babel/core": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true } } }, "jest-diff": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", - "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.1.0.tgz", + "integrity": "sha512-nepXgajT+h017APJTreSieh4zCqnSHEJ1iT8HDlewu630lSJ4Kjjr9KNzm+kzGwwcpsDE6Snx1GJGzzsefaEHw==", "dev": true, "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "chalk": "^3.0.0", + "diff-sequences": "^25.1.0", + "jest-get-type": "^25.1.0", + "pretty-format": "^25.1.0" } }, "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", + "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", "dev": true }, "jest-haste-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", - "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", + "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "anymatch": "^2.0.0", + "@jest/types": "^25.1.0", + "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.9.0", - "micromatch": "^3.1.10", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "micromatch": "^4.0.2", "sane": "^4.0.3", "walker": "^1.0.7" } }, "jest-matcher-utils": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", - "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz", + "integrity": "sha512-KGOAFcSFbclXIFE7bS4C53iYobKI20ZWleAdAFun4W1Wz1Kkej8Ng6RRbhL8leaEvIOjGXhGf/a1JjO8bkxIWQ==", "dev": true, "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "chalk": "^3.0.0", + "jest-diff": "^25.1.0", + "jest-get-type": "^25.1.0", + "pretty-format": "^25.1.0" } }, "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", + "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", + "chalk": "^3.0.0", + "micromatch": "^4.0.2", + "slash": "^3.0.0", "stack-utils": "^1.0.1" } }, - "jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0" - } + "jest-regex-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", + "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "dev": true }, "jest-resolve": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", - "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.1.0.tgz", + "integrity": "sha512-XkBQaU1SRCHj2Evz2Lu4Czs+uIgJXWypfO57L7JYccmAXv4slXA6hzNblmcRmf7P3cQ1mE7fL3ABV6jAwk4foQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", + "@jest/types": "^25.1.0", "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", + "chalk": "^3.0.0", "jest-pnp-resolver": "^1.2.1", "realpath-native": "^1.1.0" } }, "jest-serializer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", - "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", + "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", "dev": true }, "jest-snapshot": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", - "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.1.0.tgz", + "integrity": "sha512-xZ73dFYN8b/+X2hKLXz4VpBZGIAn7muD/DAg+pXtDzDGw3iIV10jM7WiHqhCcpDZfGiKEj7/2HXAEPtHTj0P2A==", "dev": true, "requires": { "@babel/types": "^7.0.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "expect": "^24.9.0", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-resolve": "^24.9.0", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "expect": "^25.1.0", + "jest-diff": "^25.1.0", + "jest-get-type": "^25.1.0", + "jest-matcher-utils": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-resolve": "^25.1.0", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "pretty-format": "^24.9.0", - "semver": "^6.2.0" + "pretty-format": "^25.1.0", + "semver": "^7.1.1" + }, + "dependencies": { + "semver": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", + "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", + "dev": true + } } }, "jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", + "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", - "dev": true, - "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" - } - } + "mkdirp": "^0.5.1" } }, "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", + "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", "dev": true, "requires": { "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" + "supports-color": "^7.0.0" } }, "merge-stream": { @@ -657,16 +1075,47 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", + "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "@jest/types": "^25.1.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + }, + "react-is": { + "version": "16.13.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz", + "integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" } }, "semver": { @@ -675,6 +1124,12 @@ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -682,21 +1137,59 @@ "dev": true }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "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": "^4.1.0" + "ansi-regex": "^5.0.0" } }, "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } } } @@ -725,234 +1218,602 @@ } }, "@jest/reporters": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz", - "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==", - "dev": true, - "requires": { - "@jest/environment": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-25.1.0.tgz", + "integrity": "sha512-ORLT7hq2acJQa8N+NKfs68ZtHFnJPxsGqmofxW7v7urVhzJvpKZG9M7FAcgh9Ee1ZbCteMrirHA3m5JfBtAaDg==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^25.1.0", + "@jest/environment": "^25.1.0", + "@jest/test-result": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.2", - "istanbul-lib-coverage": "^2.0.2", - "istanbul-lib-instrument": "^3.0.1", - "istanbul-lib-report": "^2.0.4", - "istanbul-lib-source-maps": "^3.0.1", - "istanbul-reports": "^2.2.6", - "jest-haste-map": "^24.9.0", - "jest-resolve": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.6.0", - "node-notifier": "^5.4.2", - "slash": "^2.0.0", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.0", + "jest-haste-map": "^25.1.0", + "jest-resolve": "^25.1.0", + "jest-runtime": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "node-notifier": "^6.0.0", + "slash": "^3.0.0", "source-map": "^0.6.0", - "string-length": "^2.0.0" + "string-length": "^3.1.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^4.0.1" }, "dependencies": { + "@babel/generator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", + "dev": true, + "requires": { + "@babel/types": "^7.8.7", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "dev": true + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + } + } + }, + "@babel/traverse": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + } + } + }, + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", + "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", "dev": true, "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "@jest/source-map": "^25.1.0", + "chalk": "^3.0.0", + "jest-util": "^25.1.0", + "slash": "^3.0.0" } }, "@jest/environment": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", - "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.1.0.tgz", + "integrity": "sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg==", "dev": true, "requires": { - "@jest/fake-timers": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0" + "@jest/fake-timers": "^25.1.0", + "@jest/types": "^25.1.0", + "jest-mock": "^25.1.0" } }, "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.1.0.tgz", + "integrity": "sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" + "@jest/types": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-mock": "^25.1.0", + "jest-util": "^25.1.0", + "lolex": "^5.0.0" } }, "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", + "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", "dev": true, "requires": { "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", + "graceful-fs": "^4.2.3", "source-map": "^0.6.0" } }, "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", + "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "@jest/console": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", - "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", + "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^24.9.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", + "@jest/types": "^25.1.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.9.0", - "jest-regex-util": "^24.9.0", - "jest-util": "^24.9.0", - "micromatch": "^3.1.10", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-util": "^25.1.0", + "micromatch": "^4.0.2", "pirates": "^4.0.1", "realpath-native": "^1.1.0", - "slash": "^2.0.0", + "slash": "^3.0.0", "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" + "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" } }, "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", "dev": true, "requires": { "@types/yargs-parser": "*" } }, - "jest-haste-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", - "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.9.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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 + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" }, "dependencies": { - "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/core": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "dev": true, "requires": { - "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" + "safe-buffer": "~5.1.1" } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, + "jest-haste-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", + "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", + "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", + "chalk": "^3.0.0", + "micromatch": "^4.0.2", + "slash": "^3.0.0", "stack-utils": "^1.0.1" } }, "jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.1.0.tgz", + "integrity": "sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag==", "dev": true, "requires": { - "@jest/types": "^24.9.0" + "@jest/types": "^25.1.0" } }, "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", + "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", "dev": true }, "jest-resolve": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", - "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.1.0.tgz", + "integrity": "sha512-XkBQaU1SRCHj2Evz2Lu4Czs+uIgJXWypfO57L7JYccmAXv4slXA6hzNblmcRmf7P3cQ1mE7fL3ABV6jAwk4foQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", + "@jest/types": "^25.1.0", "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", + "chalk": "^3.0.0", "jest-pnp-resolver": "^1.2.1", "realpath-native": "^1.1.0" } }, "jest-serializer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", - "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", + "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", "dev": true }, "jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", + "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" + "mkdirp": "^0.5.1" + } + }, + "jest-worker": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", + "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" } }, "merge-stream": { @@ -961,6 +1822,34 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -968,16 +1857,64 @@ "dev": true }, "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } - } - } - }, + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + } + } + }, "@jest/source-map": { "version": "24.3.0", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz", @@ -1009,160 +1946,510 @@ } }, "@jest/test-sequencer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz", - "integrity": "sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-25.1.0.tgz", + "integrity": "sha512-WgZLRgVr2b4l/7ED1J1RJQBOharxS11EFhmwDqknpknE0Pm87HLZVS2Asuuw+HQdfQvm2aXL2FvvBLxOD1D0iw==", "dev": true, "requires": { - "@jest/test-result": "^24.9.0", - "jest-haste-map": "^24.9.0", - "jest-runner": "^24.9.0", - "jest-runtime": "^24.9.0" + "@jest/test-result": "^25.1.0", + "jest-haste-map": "^25.1.0", + "jest-runner": "^25.1.0", + "jest-runtime": "^25.1.0" }, "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "dev": true, "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "@babel/highlight": "^7.8.3" } }, - "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "@babel/generator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", + "dev": true, + "requires": { + "@babel/types": "^7.8.7", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "dev": true + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "@babel/traverse": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@jest/console": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", + "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" + "@jest/source-map": "^25.1.0", + "chalk": "^3.0.0", + "jest-util": "^25.1.0", + "slash": "^3.0.0" } }, "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", + "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", "dev": true, "requires": { "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", + "graceful-fs": "^4.2.3", "source-map": "^0.6.0" } }, "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", + "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "@jest/console": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/transform": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", + "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^25.1.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-util": "^25.1.0", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" } }, "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", "dev": true, "requires": { "@types/yargs-parser": "*" } }, - "jest-haste-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", - "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.9.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, - "jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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 + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "@babel/core": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "jest-haste-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", + "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", "dev": true, "requires": { - "@jest/types": "^24.9.0" + "@jest/types": "^25.1.0", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" } }, + "jest-regex-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", + "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "dev": true + }, "jest-serializer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", - "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", + "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", "dev": true }, "jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", + "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" + "mkdirp": "^0.5.1" } }, "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", + "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", "dev": true, "requires": { "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" + "supports-color": "^7.0.0" } }, "merge-stream": { @@ -1171,6 +2458,34 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -1178,12 +2493,44 @@ "dev": true }, "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } } } @@ -2483,10 +3830,19 @@ "@types/node": ">= 8" } }, + "@sinonjs/commons": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz", + "integrity": "sha512-Debi3Baff1Qu1Unc3mjJ96MgpbwTn43S1+9yJ0llWygPwDNu2aaWBD6yc9y/Z8XDRNhx7U+u2UDg2OGQXkclUQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, "@types/babel__core": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz", - "integrity": "sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.6.tgz", + "integrity": "sha512-tTnhWszAqvXnhW7m5jQU9PomXSiKXk2sFxpahXvI20SZKu9ylPi8WtIxueZ6ehDWikPT0jeFujMj3X4ZHuf3Tg==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -2497,9 +3853,9 @@ } }, "@types/babel__generator": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.0.tgz", - "integrity": "sha512-c1mZUu4up5cp9KROs/QAw0gTeHrw/x7m52LcnvMxxOZ03DmLwPV0MlGmlgzV3cnSdjhJOZsj7E7FHeioai+egw==", + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", + "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", "dev": true, "requires": { "@babel/types": "^7.0.0" @@ -2516,14 +3872,20 @@ } }, "@types/babel__traverse": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.7.tgz", - "integrity": "sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw==", + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.9.tgz", + "integrity": "sha512-jEFQ8L1tuvPjOI8lnpaf73oCJe+aoxL6ygqSy6c8LcW98zaC+4mzWuQIRCEvKeCOu+lbqdXcg4Uqmm1S8AP1tw==", "dev": true, "requires": { "@babel/types": "^7.3.0" } }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -2548,9 +3910,9 @@ "dev": true }, "@types/istanbul-lib-report": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", - "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "*" @@ -2615,9 +3977,9 @@ "dev": true }, "@types/yargs-parser": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-13.1.0.tgz", - "integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", "dev": true }, "@typescript-eslint/eslint-plugin": { @@ -2688,9 +4050,9 @@ } }, "abab": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.2.tgz", - "integrity": "sha512-2scffjvioEmNz0OyDSLGWDfKCVwaKc6l9Pm9kOIREU13ClXZvHpg/nRL5xyjSSSLhOnXqft2HpsAzNEEA8cFFg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", + "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", "dev": true }, "abbrev": { @@ -2700,10 +4062,9 @@ "dev": true }, "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==" }, "acorn-globals": { "version": "4.3.4", @@ -2947,12 +4308,6 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -3019,193 +4374,477 @@ } }, "babel-jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", - "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-25.1.0.tgz", + "integrity": "sha512-tz0VxUhhOE2y+g8R2oFrO/2VtVjA1lkJeavlhExuRBg3LdNJY9gwQ+Vcvqt9+cqy71MCTJhewvTB7Qtnnr9SWg==", "dev": true, "requires": { - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", "@types/babel__core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.9.0", - "chalk": "^2.4.2", - "slash": "^2.0.0" + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^25.1.0", + "chalk": "^3.0.0", + "slash": "^3.0.0" }, "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "dev": true, "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "@babel/highlight": "^7.8.3" } }, - "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "@babel/generator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", + "dev": true, + "requires": { + "@babel/types": "^7.8.7", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" } }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", "dev": true, "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" + "@babel/types": "^7.8.3" } }, - "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "dev": true + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "@babel/traverse": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" } }, "@jest/transform": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", - "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", + "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^24.9.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", + "@jest/types": "^25.1.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.9.0", - "jest-regex-util": "^24.9.0", - "jest-util": "^24.9.0", - "micromatch": "^3.1.10", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-util": "^25.1.0", + "micromatch": "^4.0.2", "pirates": "^4.0.1", "realpath-native": "^1.1.0", - "slash": "^2.0.0", + "slash": "^3.0.0", "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" + "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" } }, "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", "dev": true, "requires": { "@types/yargs-parser": "*" } }, - "jest-haste-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", - "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.9.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, - "jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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 + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "@babel/core": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "jest-haste-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", + "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", "dev": true, "requires": { - "@jest/types": "^24.9.0" + "@jest/types": "^25.1.0", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" } }, "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", + "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", "dev": true }, "jest-serializer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", - "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", + "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", "dev": true }, "jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", + "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" + "mkdirp": "^0.5.1" } }, "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", + "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", "dev": true, "requires": { "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" + "supports-color": "^7.0.0" } }, "merge-stream": { @@ -3214,6 +4853,34 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -3221,12 +4888,44 @@ "dev": true }, "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } } } @@ -3288,22 +4987,23 @@ } }, "babel-plugin-jest-hoist": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", - "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.1.0.tgz", + "integrity": "sha512-oIsopO41vW4YFZ9yNYoLQATnnN46lp+MZ6H4VvPKFkcc2/fkl3CfE/NZZSmnEIEsJRmJAgkVEK0R7Zbl50CpTw==", "dev": true, "requires": { "@types/babel__traverse": "^7.0.6" } }, "babel-preset-jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", - "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-25.1.0.tgz", + "integrity": "sha512-eCGn64olaqwUMaugXsTtGAM2I0QTahjEtnRu0ql8Ie+gDWAc1N6wqN0k2NilnyTunM69Pad7gJY7LOtwLimoFQ==", "dev": true, "requires": { + "@babel/plugin-syntax-bigint": "^7.0.0", "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.9.0" + "babel-plugin-jest-hoist": "^25.1.0" } }, "balanced-match": { @@ -3438,9 +5138,9 @@ } }, "browser-process-hrtime": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", - "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, "browser-resolve": { @@ -3738,6 +5438,12 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, + "collect-v8-coverage": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.0.tgz", + "integrity": "sha512-VKIhJgvk8E1W28m5avZ2Gv2Ruv5YiF56ug2oclvaG9md69BuZImMG2sk9g7QNKLUbtYAKQjXjYxbYZVUlMMKmQ==", + "dev": true + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -4250,18 +5956,26 @@ } }, "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", "dev": true }, "cssstyle": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", - "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.2.0.tgz", + "integrity": "sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA==", "dev": true, "requires": { - "cssom": "0.3.x" + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } } }, "currently-unhandled": { @@ -4312,19 +6026,6 @@ "abab": "^2.0.0", "whatwg-mimetype": "^2.2.0", "whatwg-url": "^7.0.0" - }, - "dependencies": { - "whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - } } }, "date-fns": { @@ -4480,9 +6181,9 @@ "dev": true }, "detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, "dezalgo": { @@ -4674,24 +6375,18 @@ "dev": true }, "escodegen": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", - "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", + "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", "dev": true, "requires": { - "esprima": "^3.1.3", + "esprima": "^4.0.1", "estraverse": "^4.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1", "source-map": "~0.6.1" }, "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -6177,6 +7872,12 @@ "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", "dev": true }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -6572,7 +8273,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true + "dev": true, + "optional": true }, "handlebars": { "version": "4.5.3", @@ -6684,6 +8386,12 @@ "whatwg-encoding": "^1.0.1" } }, + "html-escaper": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz", + "integrity": "sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==", + "dev": true + }, "http-cache-semantics": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", @@ -6749,6 +8457,12 @@ } } }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, "humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", @@ -6977,6 +8691,12 @@ "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", "dev": true }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", @@ -7225,12 +8945,13 @@ "dev": true }, "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isarray": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz", + "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==", + "dev": true, + "optional": true + }, + "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", @@ -7284,50 +9005,69 @@ } }, "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", "dev": true, "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" }, "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "make-dir": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", + "dev": true, + "requires": { + "semver": "^6.0.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 }, "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" } } } }, "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", "dev": true, "requires": { "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", + "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" }, "dependencies": { "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", "dev": true }, "source-map": { @@ -7339,12 +9079,13 @@ } }, "istanbul-reports": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", - "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-2osTcC8zcOSUkImzN2EWQta3Vdi4WjjKw99P2yWx5mLnigAM0Rd5uYFn1cf2i/Ois45GkNjaoTqc5CxgMSX80A==", "dev": true, "requires": { - "handlebars": "^4.1.2" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" } }, "iterall": { @@ -7354,1333 +9095,4328 @@ "dev": true }, "jest": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", - "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-25.1.0.tgz", + "integrity": "sha512-FV6jEruneBhokkt9MQk0WUFoNTwnF76CLXtwNMfsc0um0TlB/LG2yxUd0KqaFjEJ9laQmVWQWS0sG/t2GsuI0w==", "dev": true, "requires": { - "import-local": "^2.0.0", - "jest-cli": "^24.9.0" + "@jest/core": "^25.1.0", + "import-local": "^3.0.2", + "jest-cli": "^25.1.0" }, "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "dev": true, "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "@babel/highlight": "^7.8.3" } }, - "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "@babel/generator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" + "@babel/types": "^7.8.7", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } } }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", "dev": true, "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" } }, - "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "@babel/types": "^7.8.3" } }, - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "@babel/types": "^7.8.3" } }, - "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", "dev": true, "requires": { - "@types/yargs-parser": "*" + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" } }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "@babel/parser": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "dev": true + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" } }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "jest-cli": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", - "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", + "@babel/traverse": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", "dev": true, "requires": { - "@jest/core": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "import-local": "^2.0.0", - "is-ci": "^2.0.0", - "jest-config": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "prompts": "^2.0.1", - "realpath-native": "^1.1.0", - "yargs": "^13.3.0" + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" } }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" } }, - "jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "@jest/console": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", + "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", "dev": true, "requires": { - "@jest/types": "^24.9.0" + "@jest/source-map": "^25.1.0", + "chalk": "^3.0.0", + "jest-util": "^25.1.0", + "slash": "^3.0.0" } }, - "jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "@jest/source-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", + "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", + "graceful-fs": "^4.2.3", "source-map": "^0.6.0" } }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "@jest/test-result": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", + "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "@jest/console": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" } }, - "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "@jest/transform": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", + "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", "dev": true, "requires": { - "p-try": "^2.0.0" + "@babel/core": "^7.1.0", + "@jest/types": "^25.1.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-util": "^25.1.0", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" } }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "@jest/types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "@types/yargs": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "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 }, - "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==", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" } }, - "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" + "fill-range": "^7.0.1" } }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } - } - } - }, - "jest-changed-files": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", - "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "execa": "^1.0.0", - "throat": "^4.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" } }, - "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "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": { - "@types/yargs-parser": "*" - } - } - } - }, - "jest-circus": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-24.7.1.tgz", - "integrity": "sha512-zDNSS+7qQN0nSbR77qcOb+tOUWLcvZGzvXE1PjoV6xeHV5Vz7DPK9JkBIQF/n5Nz9yZbApDxq5NqGqmVCe0nnQ==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^24.7.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^24.7.1", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "pretty-format": "^24.7.0", - "stack-utils": "^1.0.1", - "throat": "^4.0.0" - } - }, - "jest-config": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", - "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.9.0", - "@jest/types": "^24.9.0", - "babel-jest": "^24.9.0", - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^24.9.0", - "jest-environment-node": "^24.9.0", - "jest-get-type": "^24.9.0", - "jest-jasmine2": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.9.0", - "realpath-native": "^1.1.0" - }, - "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", - "dev": true, - "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "color-name": "~1.1.4" } }, - "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "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 + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" + "to-regex-range": "^5.0.1" } }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, - "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", "dev": true, - "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" - } + "optional": true }, - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, - "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", "dev": true, "requires": { - "@types/yargs-parser": "*" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" } }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "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 }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "@babel/core": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } } }, - "jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "jest-cli": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-25.1.0.tgz", + "integrity": "sha512-p+aOfczzzKdo3AsLJlhs8J5EW6ffVidfSZZxXedJ0mHPBOln1DccqFmGCoO8JWd4xRycfmwy1eoQkMsF8oekPg==", "dev": true, "requires": { - "@jest/types": "^24.9.0" + "@jest/core": "^25.1.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^25.1.0", + "jest-util": "^25.1.0", + "jest-validate": "^25.1.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^15.0.0" } }, - "jest-resolve": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", - "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "jest-haste-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", + "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" + "@jest/types": "^25.1.0", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" } }, + "jest-regex-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", + "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "dev": true + }, + "jest-serializer": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", + "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "dev": true + }, "jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", + "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" + "mkdirp": "^0.5.1" } }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "jest-worker": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", + "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "jest-diff": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.7.0.tgz", - "integrity": "sha512-ULQZ5B1lWpH70O4xsANC4tf4Ko6RrpwhE3PtG6ERjMg1TiYTC2Wp4IntJVGro6a8HG9luYHhhmF4grF0Pltckg==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.3.0", - "jest-get-type": "^24.3.0", - "pretty-format": "^24.7.0" - } - }, - "jest-docblock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", - "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", - "dev": true, - "requires": { - "detect-newline": "^2.1.0" - } - }, - "jest-each": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.7.1.tgz", - "integrity": "sha512-4fsS8fEfLa3lfnI1Jw6NxjhyRTgfpuOVTeUZZFyVYqeTa4hPhr2YkToUhouuLTrL2eMGOfpbdMyRx0GQ/VooKA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", - "jest-util": "^24.7.1", - "pretty-format": "^24.7.0" - } - }, - "jest-environment-jsdom": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", - "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", - "dev": true, - "requires": { - "@jest/environment": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-util": "^24.9.0", - "jsdom": "^11.5.1" - }, - "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "p-locate": "^4.1.0" } }, - "@jest/environment": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", - "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", "dev": true, "requires": { - "@jest/fake-timers": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0" + "braces": "^3.0.1", + "picomatch": "^2.0.5" } }, - "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "p-limit": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" + "p-try": "^2.0.0" } }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" + "p-limit": "^2.2.0" } }, - "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "find-up": "^4.0.0" } }, - "@jest/transform": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", - "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^24.9.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.9.0", - "jest-regex-util": "^24.9.0", - "jest-util": "^24.9.0", - "micromatch": "^3.1.10", - "pirates": "^4.0.1", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" + "resolve-from": "^5.0.0" } }, - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "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": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, - "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "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": { - "@types/yargs-parser": "*" + "ansi-regex": "^5.0.0" } }, - "jest-haste-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", - "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.9.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" + "has-flag": "^4.0.0" } }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" } }, - "jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0" + "is-number": "^7.0.0" } }, - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", - "dev": true - }, - "jest-serializer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", - "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", - "dev": true - }, - "jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, - "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { - "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "yargs": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.0.tgz", + "integrity": "sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.0" + } }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "yargs-parser": { + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.0.tgz", + "integrity": "sha512-o/Jr6JBOv6Yx3pL+5naWSoIA2jJ+ZkMYQG/ie9qFbukBe4uzmBatlXFOiu/tNKRWEtyf+n5w7jc/O16ufqOTdQ==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } }, - "jest-environment-node": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", - "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "jest-changed-files": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-25.1.0.tgz", + "integrity": "sha512-bdL1aHjIVy3HaBO3eEQeemGttsq1BDlHgWcOjEOIAcga7OOEGWHD2WSu8HhL7I1F0mFFyci8VKU4tRNk+qtwDA==", "dev": true, "requires": { - "@jest/environment": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-util": "^24.9.0" + "@jest/types": "^25.1.0", + "execa": "^3.2.0", + "throat": "^5.0.0" }, "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "@jest/types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", "dev": true, "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" } }, - "@jest/environment": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", - "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "@types/yargs": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", "dev": true, "requires": { - "@jest/fake-timers": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0" + "@types/yargs-parser": "*" } }, - "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "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": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "color-name": "~1.1.4" } }, - "@jest/transform": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", - "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^24.9.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.9.0", - "jest-regex-util": "^24.9.0", - "jest-util": "^24.9.0", - "micromatch": "^3.1.10", - "pirates": "^4.0.1", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" - } + "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 }, - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "cross-spawn": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, - "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "execa": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz", + "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", "dev": true, "requires": { - "@types/yargs-parser": "*" + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" } }, - "jest-haste-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", - "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.9.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" + "pump": "^3.0.0" } }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" + "path-key": "^3.0.0" } }, - "jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", "dev": true, "requires": { - "@jest/types": "^24.9.0" + "mimic-fn": "^2.1.0" } }, - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "p-finally": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", + "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", "dev": true }, - "jest-serializer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", - "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" + "shebang-regex": "^3.0.0" } }, - "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" + "has-flag": "^4.0.0" } }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "dev": true }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "isexe": "^2.0.0" } } } }, - "jest-get-type": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", - "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", - "dev": true - }, - "jest-haste-map": { + "jest-circus": { "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.7.1.tgz", - "integrity": "sha512-g0tWkzjpHD2qa03mTKhlydbmmYiA2KdcJe762SbfFo/7NIMgBWAA0XqQlApPwkWOF7Cxoi/gUqL0i6DIoLpMBw==", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-24.7.1.tgz", + "integrity": "sha512-zDNSS+7qQN0nSbR77qcOb+tOUWLcvZGzvXE1PjoV6xeHV5Vz7DPK9JkBIQF/n5Nz9yZbApDxq5NqGqmVCe0nnQ==", "dev": true, "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.7.1", + "@jest/test-result": "^24.7.1", "@jest/types": "^24.7.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.4.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.7.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.7.1", + "jest-matcher-utils": "^24.7.0", + "jest-message-util": "^24.7.1", + "jest-snapshot": "^24.7.1", "jest-util": "^24.7.1", - "jest-worker": "^24.6.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" + "pretty-format": "^24.7.0", + "stack-utils": "^1.0.1", + "throat": "^4.0.0" } }, - "jest-jasmine2": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", - "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "jest-config": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-25.1.0.tgz", + "integrity": "sha512-tLmsg4SZ5H7tuhBC5bOja0HEblM0coS3Wy5LTCb2C8ZV6eWLewHyK+3qSq9Bi29zmWQ7ojdCd3pxpx4l4d2uGw==", "dev": true, "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^24.9.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-snapshot": "^24.9.0", - "jest-util": "^24.9.0", - "pretty-format": "^24.9.0", - "throat": "^4.0.0" + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^25.1.0", + "@jest/types": "^25.1.0", + "babel-jest": "^25.1.0", + "chalk": "^3.0.0", + "glob": "^7.1.1", + "jest-environment-jsdom": "^25.1.0", + "jest-environment-node": "^25.1.0", + "jest-get-type": "^25.1.0", + "jest-jasmine2": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-resolve": "^25.1.0", + "jest-util": "^25.1.0", + "jest-validate": "^25.1.0", + "micromatch": "^4.0.2", + "pretty-format": "^25.1.0", + "realpath-native": "^1.1.0" }, "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "@jest/types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", "dev": true, "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" } }, - "@jest/environment": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", - "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "@types/yargs": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", "dev": true, "requires": { - "@jest/fake-timers": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0" + "@types/yargs-parser": "*" } }, - "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" - } + "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 }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, - "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "fill-range": "^7.0.1" } }, - "@jest/transform": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", - "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^24.9.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.9.0", - "jest-regex-util": "^24.9.0", - "jest-util": "^24.9.0", - "micromatch": "^3.1.10", - "pirates": "^4.0.1", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "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": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "color-name": "~1.1.4" } }, - "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "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 + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "@types/yargs-parser": "*" + "to-regex-range": "^5.0.1" } }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "diff-sequences": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", - "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "expect": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", - "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "jest-get-type": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", + "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", + "dev": true + }, + "jest-regex-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", + "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "dev": true + }, + "jest-resolve": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.1.0.tgz", + "integrity": "sha512-XkBQaU1SRCHj2Evz2Lu4Czs+uIgJXWypfO57L7JYccmAXv4slXA6hzNblmcRmf7P3cQ1mE7fL3ABV6jAwk4foQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-regex-util": "^24.9.0" + "@jest/types": "^25.1.0", + "browser-resolve": "^1.11.3", + "chalk": "^3.0.0", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" } }, - "jest-diff": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", - "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "jest-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", + "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", "dev": true, "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1" } }, - "jest-each": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", - "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.9.0", - "jest-util": "^24.9.0", - "pretty-format": "^24.9.0" + "braces": "^3.0.1", + "picomatch": "^2.0.5" } }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", - "dev": true - }, - "jest-haste-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", - "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "pretty-format": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", + "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.9.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" + "@jest/types": "^25.1.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" } }, - "jest-matcher-utils": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", - "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "react-is": { + "version": "16.13.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz", + "integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "has-flag": "^4.0.0" } }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "jest-diff": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.7.0.tgz", + "integrity": "sha512-ULQZ5B1lWpH70O4xsANC4tf4Ko6RrpwhE3PtG6ERjMg1TiYTC2Wp4IntJVGro6a8HG9luYHhhmF4grF0Pltckg==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.3.0", + "jest-get-type": "^24.3.0", + "pretty-format": "^24.7.0" + } + }, + "jest-docblock": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-25.1.0.tgz", + "integrity": "sha512-370P/mh1wzoef6hUKiaMcsPtIapY25suP6JqM70V9RJvdKLrV4GaGbfUseUVk4FZJw4oTZ1qSCJNdrClKt5JQA==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.7.1.tgz", + "integrity": "sha512-4fsS8fEfLa3lfnI1Jw6NxjhyRTgfpuOVTeUZZFyVYqeTa4hPhr2YkToUhouuLTrL2eMGOfpbdMyRx0GQ/VooKA==", + "dev": true, + "requires": { + "@jest/types": "^24.7.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.3.0", + "jest-util": "^24.7.1", + "pretty-format": "^24.7.0" + } + }, + "jest-environment-jsdom": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-25.1.0.tgz", + "integrity": "sha512-ILb4wdrwPAOHX6W82GGDUiaXSSOE274ciuov0lztOIymTChKFtC02ddyicRRCdZlB5YSrv3vzr1Z5xjpEe1OHQ==", + "dev": true, + "requires": { + "@jest/environment": "^25.1.0", + "@jest/fake-timers": "^25.1.0", + "@jest/types": "^25.1.0", + "jest-mock": "^25.1.0", + "jest-util": "^25.1.0", + "jsdom": "^15.1.1" + }, + "dependencies": { + "@babel/generator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", + "dev": true, + "requires": { + "@babel/types": "^7.8.7", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "dev": true + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + } + } + }, + "@babel/traverse": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + } + } + }, + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@jest/console": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", + "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "dev": true, + "requires": { + "@jest/source-map": "^25.1.0", + "chalk": "^3.0.0", + "jest-util": "^25.1.0", + "slash": "^3.0.0" + } + }, + "@jest/environment": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.1.0.tgz", + "integrity": "sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg==", + "dev": true, + "requires": { + "@jest/fake-timers": "^25.1.0", + "@jest/types": "^25.1.0", + "jest-mock": "^25.1.0" + } + }, + "@jest/fake-timers": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.1.0.tgz", + "integrity": "sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-mock": "^25.1.0", + "jest-util": "^25.1.0", + "lolex": "^5.0.0" + } + }, + "@jest/source-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", + "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.3", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", + "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "dev": true, + "requires": { + "@jest/console": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/transform": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", + "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^25.1.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-util": "^25.1.0", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, + "@jest/types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/yargs": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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 + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/core": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "jest-haste-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", + "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-message-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", + "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^3.0.0", + "micromatch": "^4.0.2", + "slash": "^3.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.1.0.tgz", + "integrity": "sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0" + } + }, + "jest-regex-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", + "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "dev": true + }, + "jest-serializer": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", + "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "dev": true + }, + "jest-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", + "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1" + } + }, + "jest-worker": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", + "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + } + } + }, + "jest-environment-node": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-25.1.0.tgz", + "integrity": "sha512-U9kFWTtAPvhgYY5upnH9rq8qZkj6mYLup5l1caAjjx9uNnkLHN2xgZy5mo4SyLdmrh/EtB9UPpKFShvfQHD0Iw==", + "dev": true, + "requires": { + "@jest/environment": "^25.1.0", + "@jest/fake-timers": "^25.1.0", + "@jest/types": "^25.1.0", + "jest-mock": "^25.1.0", + "jest-util": "^25.1.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", + "dev": true, + "requires": { + "@babel/types": "^7.8.7", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "dev": true + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + } + } + }, + "@babel/traverse": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + } + } + }, + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@jest/console": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", + "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "dev": true, + "requires": { + "@jest/source-map": "^25.1.0", + "chalk": "^3.0.0", + "jest-util": "^25.1.0", + "slash": "^3.0.0" + } + }, + "@jest/environment": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.1.0.tgz", + "integrity": "sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg==", + "dev": true, + "requires": { + "@jest/fake-timers": "^25.1.0", + "@jest/types": "^25.1.0", + "jest-mock": "^25.1.0" + } + }, + "@jest/fake-timers": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.1.0.tgz", + "integrity": "sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-mock": "^25.1.0", + "jest-util": "^25.1.0", + "lolex": "^5.0.0" + } + }, + "@jest/source-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", + "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.3", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", + "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "dev": true, + "requires": { + "@jest/console": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/transform": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", + "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^25.1.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-util": "^25.1.0", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, + "@jest/types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/yargs": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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 + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/core": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "jest-haste-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", + "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-message-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", + "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^3.0.0", + "micromatch": "^4.0.2", + "slash": "^3.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.1.0.tgz", + "integrity": "sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0" + } + }, + "jest-regex-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", + "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "dev": true + }, + "jest-serializer": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", + "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "dev": true + }, + "jest-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", + "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1" + } + }, + "jest-worker": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", + "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + } + } + }, + "jest-get-type": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", + "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", + "dev": true + }, + "jest-haste-map": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.7.1.tgz", + "integrity": "sha512-g0tWkzjpHD2qa03mTKhlydbmmYiA2KdcJe762SbfFo/7NIMgBWAA0XqQlApPwkWOF7Cxoi/gUqL0i6DIoLpMBw==", + "dev": true, + "requires": { + "@jest/types": "^24.7.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.4.0", + "jest-util": "^24.7.1", + "jest-worker": "^24.6.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-25.1.0.tgz", + "integrity": "sha512-GdncRq7jJ7sNIQ+dnXvpKO2MyP6j3naNK41DTTjEAhLEdpImaDA9zSAZwDhijjSF/D7cf4O5fdyUApGBZleaEg==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^25.1.0", + "@jest/source-map": "^25.1.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "co": "^4.6.0", + "expect": "^25.1.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^25.1.0", + "jest-matcher-utils": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-runtime": "^25.1.0", + "jest-snapshot": "^25.1.0", + "jest-util": "^25.1.0", + "pretty-format": "^25.1.0", + "throat": "^5.0.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", + "dev": true, + "requires": { + "@babel/types": "^7.8.7", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/traverse": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + } + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "dev": true + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + } + } + }, + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@jest/console": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", + "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "dev": true, + "requires": { + "@jest/source-map": "^25.1.0", + "chalk": "^3.0.0", + "jest-util": "^25.1.0", + "slash": "^3.0.0" + } + }, + "@jest/environment": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.1.0.tgz", + "integrity": "sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg==", + "dev": true, + "requires": { + "@jest/fake-timers": "^25.1.0", + "@jest/types": "^25.1.0", + "jest-mock": "^25.1.0" + } + }, + "@jest/fake-timers": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.1.0.tgz", + "integrity": "sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-mock": "^25.1.0", + "jest-util": "^25.1.0", + "lolex": "^5.0.0" + } + }, + "@jest/source-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", + "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.3", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", + "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "dev": true, + "requires": { + "@jest/console": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/transform": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", + "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^25.1.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-util": "^25.1.0", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, + "@jest/types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/yargs": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "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.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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 + }, + "diff-sequences": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.1.0.tgz", + "integrity": "sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw==", + "dev": true + }, + "expect": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-25.1.0.tgz", + "integrity": "sha512-wqHzuoapQkhc3OKPlrpetsfueuEiMf3iWh0R8+duCu9PIjXoP7HgD5aeypwTnXUAjC8aMsiVDaWwlbJ1RlQ38g==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "ansi-styles": "^4.0.0", + "jest-get-type": "^25.1.0", + "jest-matcher-utils": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-regex-util": "^25.1.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/core": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/traverse": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "jest-diff": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.1.0.tgz", + "integrity": "sha512-nepXgajT+h017APJTreSieh4zCqnSHEJ1iT8HDlewu630lSJ4Kjjr9KNzm+kzGwwcpsDE6Snx1GJGzzsefaEHw==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "diff-sequences": "^25.1.0", + "jest-get-type": "^25.1.0", + "pretty-format": "^25.1.0" + } + }, + "jest-each": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-25.1.0.tgz", + "integrity": "sha512-R9EL8xWzoPySJ5wa0DXFTj7NrzKpRD40Jy+zQDp3Qr/2QmevJgkN9GqioCGtAJ2bW9P/MQRznQHQQhoeAyra7A==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "jest-get-type": "^25.1.0", + "jest-util": "^25.1.0", + "pretty-format": "^25.1.0" + } + }, + "jest-get-type": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", + "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", + "dev": true + }, + "jest-haste-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", + "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-matcher-utils": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz", + "integrity": "sha512-KGOAFcSFbclXIFE7bS4C53iYobKI20ZWleAdAFun4W1Wz1Kkej8Ng6RRbhL8leaEvIOjGXhGf/a1JjO8bkxIWQ==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "jest-diff": "^25.1.0", + "jest-get-type": "^25.1.0", + "pretty-format": "^25.1.0" + } + }, + "jest-message-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", + "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", + "chalk": "^3.0.0", + "micromatch": "^4.0.2", + "slash": "^3.0.0", "stack-utils": "^1.0.1" } }, - "jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "jest-mock": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.1.0.tgz", + "integrity": "sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0" + } + }, + "jest-regex-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", + "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "dev": true + }, + "jest-resolve": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.1.0.tgz", + "integrity": "sha512-XkBQaU1SRCHj2Evz2Lu4Czs+uIgJXWypfO57L7JYccmAXv4slXA6hzNblmcRmf7P3cQ1mE7fL3ABV6jAwk4foQ==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "browser-resolve": "^1.11.3", + "chalk": "^3.0.0", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-serializer": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", + "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "dev": true + }, + "jest-snapshot": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.1.0.tgz", + "integrity": "sha512-xZ73dFYN8b/+X2hKLXz4VpBZGIAn7muD/DAg+pXtDzDGw3iIV10jM7WiHqhCcpDZfGiKEj7/2HXAEPtHTj0P2A==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "expect": "^25.1.0", + "jest-diff": "^25.1.0", + "jest-get-type": "^25.1.0", + "jest-matcher-utils": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-resolve": "^25.1.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^25.1.0", + "semver": "^7.1.1" + }, + "dependencies": { + "semver": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", + "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", + "dev": true + } + } + }, + "jest-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", + "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1" + } + }, + "jest-worker": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", + "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "pretty-format": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", + "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + }, + "react-is": { + "version": "16.13.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz", + "integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + } + } + }, + "jest-leak-detector": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-25.1.0.tgz", + "integrity": "sha512-3xRI264dnhGaMHRvkFyEKpDeaRzcEBhyNrOG5oT8xPxOyUAblIAQnpiR3QXu4wDor47MDTiHbiFcbypdLcLW5w==", + "dev": true, + "requires": { + "jest-get-type": "^25.1.0", + "pretty-format": "^25.1.0" + }, + "dependencies": { + "@jest/types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/yargs": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "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.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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 + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-get-type": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", + "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", + "dev": true + }, + "pretty-format": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", + "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + }, + "react-is": { + "version": "16.13.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz", + "integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-matcher-utils": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.7.0.tgz", + "integrity": "sha512-158ieSgk3LNXeUhbVJYRXyTPSCqNgVXOp/GT7O94mYd3pk/8+odKTyR1JLtNOQSPzNi8NFYVONtvSWA/e1RDXg==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.7.0", + "jest-get-type": "^24.3.0", + "pretty-format": "^24.7.0" + } + }, + "jest-message-util": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.7.1.tgz", + "integrity": "sha512-dk0gqVtyqezCHbcbk60CdIf+8UHgD+lmRHifeH3JRcnAqh4nEyPytSc9/L1+cQyxC+ceaeP696N4ATe7L+omcg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.7.1", + "@jest/types": "^24.7.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.7.0.tgz", + "integrity": "sha512-6taW4B4WUcEiT2V9BbOmwyGuwuAFT2G8yghF7nyNW1/2gq5+6aTqSPcS9lS6ArvEkX55vbPAS/Jarx5LSm4Fng==", + "dev": true, + "requires": { + "@jest/types": "^24.7.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", + "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", + "dev": true + }, + "jest-regex-util": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", + "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", + "dev": true + }, + "jest-resolve": { + "version": "24.7.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.7.1.tgz", + "integrity": "sha512-Bgrc+/UUZpGJ4323sQyj85hV9d+ANyPNu6XfRDUcyFNX1QrZpSoM0kE4Mb2vZMAYTJZsBFzYe8X1UaOkOELSbw==", + "dev": true, + "requires": { + "@jest/types": "^24.7.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-resolve-dependencies": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-25.1.0.tgz", + "integrity": "sha512-Cu/Je38GSsccNy4I2vL12ZnBlD170x2Oh1devzuM9TLH5rrnLW1x51lN8kpZLYTvzx9j+77Y5pqBaTqfdzVzrw==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-snapshot": "^25.1.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", + "dev": true, + "requires": { + "@babel/types": "^7.8.7", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" + }, + "dependencies": { + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "dev": true + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/traverse": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@jest/console": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", + "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "dev": true, + "requires": { + "@jest/source-map": "^25.1.0", + "chalk": "^3.0.0", + "jest-util": "^25.1.0", + "slash": "^3.0.0" + } + }, + "@jest/source-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", + "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.3", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", + "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "dev": true, + "requires": { + "@jest/console": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/transform": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", + "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^25.1.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-util": "^25.1.0", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, + "@jest/types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/yargs": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "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.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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 + }, + "diff-sequences": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.1.0.tgz", + "integrity": "sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw==", + "dev": true + }, + "expect": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-25.1.0.tgz", + "integrity": "sha512-wqHzuoapQkhc3OKPlrpetsfueuEiMf3iWh0R8+duCu9PIjXoP7HgD5aeypwTnXUAjC8aMsiVDaWwlbJ1RlQ38g==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "ansi-styles": "^4.0.0", + "jest-get-type": "^25.1.0", + "jest-matcher-utils": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-regex-util": "^25.1.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/core": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "jest-diff": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.1.0.tgz", + "integrity": "sha512-nepXgajT+h017APJTreSieh4zCqnSHEJ1iT8HDlewu630lSJ4Kjjr9KNzm+kzGwwcpsDE6Snx1GJGzzsefaEHw==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "diff-sequences": "^25.1.0", + "jest-get-type": "^25.1.0", + "pretty-format": "^25.1.0" + } + }, + "jest-get-type": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", + "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", + "dev": true + }, + "jest-haste-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", + "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-matcher-utils": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz", + "integrity": "sha512-KGOAFcSFbclXIFE7bS4C53iYobKI20ZWleAdAFun4W1Wz1Kkej8Ng6RRbhL8leaEvIOjGXhGf/a1JjO8bkxIWQ==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "jest-diff": "^25.1.0", + "jest-get-type": "^25.1.0", + "pretty-format": "^25.1.0" + } + }, + "jest-message-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", + "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0" + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^3.0.0", + "micromatch": "^4.0.2", + "slash": "^3.0.0", + "stack-utils": "^1.0.1" } }, "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", + "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", "dev": true }, "jest-resolve": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", - "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.1.0.tgz", + "integrity": "sha512-XkBQaU1SRCHj2Evz2Lu4Czs+uIgJXWypfO57L7JYccmAXv4slXA6hzNblmcRmf7P3cQ1mE7fL3ABV6jAwk4foQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", + "@jest/types": "^25.1.0", "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", + "chalk": "^3.0.0", "jest-pnp-resolver": "^1.2.1", "realpath-native": "^1.1.0" } }, "jest-serializer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", - "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", + "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", "dev": true }, "jest-snapshot": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", - "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.1.0.tgz", + "integrity": "sha512-xZ73dFYN8b/+X2hKLXz4VpBZGIAn7muD/DAg+pXtDzDGw3iIV10jM7WiHqhCcpDZfGiKEj7/2HXAEPtHTj0P2A==", "dev": true, "requires": { "@babel/types": "^7.0.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "expect": "^24.9.0", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-resolve": "^24.9.0", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "expect": "^25.1.0", + "jest-diff": "^25.1.0", + "jest-get-type": "^25.1.0", + "jest-matcher-utils": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-resolve": "^25.1.0", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "pretty-format": "^24.9.0", - "semver": "^6.2.0" + "pretty-format": "^25.1.0", + "semver": "^7.1.1" } }, "jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", + "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" + "mkdirp": "^0.5.1" } }, "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", + "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", "dev": true, "requires": { "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" + "supports-color": "^7.0.0" } }, "merge-stream": { @@ -8689,22 +13425,50 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", + "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "@jest/types": "^25.1.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" } }, + "react-is": { + "version": "16.13.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz", + "integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==", + "dev": true + }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", + "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, "source-map": { @@ -8714,983 +13478,1472 @@ "dev": true }, "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } } } }, - "jest-leak-detector": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", - "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "jest-runner": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-25.1.0.tgz", + "integrity": "sha512-su3O5fy0ehwgt+e8Wy7A8CaxxAOCMzL4gUBftSs0Ip32S0epxyZPDov9Znvkl1nhVOJNf4UwAsnqfc3plfQH9w==", "dev": true, "requires": { - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "@jest/console": "^25.1.0", + "@jest/environment": "^25.1.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.3", + "jest-config": "^25.1.0", + "jest-docblock": "^25.1.0", + "jest-haste-map": "^25.1.0", + "jest-jasmine2": "^25.1.0", + "jest-leak-detector": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-resolve": "^25.1.0", + "jest-runtime": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" }, "dependencies": { + "@babel/generator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", + "dev": true, + "requires": { + "@babel/types": "^7.8.7", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "dev": true + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + } + } + }, + "@babel/traverse": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + } + } + }, + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@jest/console": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", + "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "dev": true, + "requires": { + "@jest/source-map": "^25.1.0", + "chalk": "^3.0.0", + "jest-util": "^25.1.0", + "slash": "^3.0.0" + } + }, + "@jest/environment": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.1.0.tgz", + "integrity": "sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg==", + "dev": true, + "requires": { + "@jest/fake-timers": "^25.1.0", + "@jest/types": "^25.1.0", + "jest-mock": "^25.1.0" + } + }, + "@jest/fake-timers": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.1.0.tgz", + "integrity": "sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-mock": "^25.1.0", + "jest-util": "^25.1.0", + "lolex": "^5.0.0" + } + }, + "@jest/source-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", + "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.3", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", + "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "dev": true, + "requires": { + "@jest/console": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/transform": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", + "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^25.1.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-util": "^25.1.0", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" } }, "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", "dev": true, "requires": { "@types/yargs-parser": "*" } }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", - "dev": true - }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } - } - } - }, - "jest-matcher-utils": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.7.0.tgz", - "integrity": "sha512-158ieSgk3LNXeUhbVJYRXyTPSCqNgVXOp/GT7O94mYd3pk/8+odKTyR1JLtNOQSPzNi8NFYVONtvSWA/e1RDXg==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.7.0", - "jest-get-type": "^24.3.0", - "pretty-format": "^24.7.0" - } - }, - "jest-message-util": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.7.1.tgz", - "integrity": "sha512-dk0gqVtyqezCHbcbk60CdIf+8UHgD+lmRHifeH3JRcnAqh4nEyPytSc9/L1+cQyxC+ceaeP696N4ATe7L+omcg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-mock": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.7.0.tgz", - "integrity": "sha512-6taW4B4WUcEiT2V9BbOmwyGuwuAFT2G8yghF7nyNW1/2gq5+6aTqSPcS9lS6ArvEkX55vbPAS/Jarx5LSm4Fng==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0" - } - }, - "jest-pnp-resolver": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", - "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", - "dev": true - }, - "jest-regex-util": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", - "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", - "dev": true - }, - "jest-resolve": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.7.1.tgz", - "integrity": "sha512-Bgrc+/UUZpGJ4323sQyj85hV9d+ANyPNu6XfRDUcyFNX1QrZpSoM0kE4Mb2vZMAYTJZsBFzYe8X1UaOkOELSbw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" - } - }, - "jest-resolve-dependencies": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", - "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-snapshot": "^24.9.0" - }, - "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "dev": true, "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", "dev": true, "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" } }, - "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "fill-range": "^7.0.1" } }, - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "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": { - "@types/yargs-parser": "*" + "color-name": "~1.1.4" } }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "diff-sequences": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", - "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "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 }, - "expect": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", - "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-regex-util": "^24.9.0" - }, - "dependencies": { - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", - "dev": true - } + "to-regex-range": "^5.0.1" } }, - "jest-diff": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", - "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", "dev": true }, - "jest-matcher-utils": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", - "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/core": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "jest-haste-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", + "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", "dev": true, "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "@jest/types": "^25.1.0", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" } }, "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", + "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", + "chalk": "^3.0.0", + "micromatch": "^4.0.2", + "slash": "^3.0.0", "stack-utils": "^1.0.1" } }, + "jest-mock": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.1.0.tgz", + "integrity": "sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0" + } + }, + "jest-regex-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", + "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "dev": true + }, "jest-resolve": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", - "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.1.0.tgz", + "integrity": "sha512-XkBQaU1SRCHj2Evz2Lu4Czs+uIgJXWypfO57L7JYccmAXv4slXA6hzNblmcRmf7P3cQ1mE7fL3ABV6jAwk4foQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", + "@jest/types": "^25.1.0", "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", + "chalk": "^3.0.0", "jest-pnp-resolver": "^1.2.1", "realpath-native": "^1.1.0" } }, - "jest-snapshot": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", - "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "jest-serializer": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", + "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", + "dev": true + }, + "jest-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", + "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", "dev": true, "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "expect": "^24.9.0", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-resolve": "^24.9.0", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^24.9.0", - "semver": "^6.2.0" + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1" } }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "jest-worker": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", + "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" } }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true - } - } - }, - "jest-runner": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", - "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "chalk": "^2.4.2", - "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-config": "^24.9.0", - "jest-docblock": "^24.3.0", - "jest-haste-map": "^24.9.0", - "jest-jasmine2": "^24.9.0", - "jest-leak-detector": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-resolve": "^24.9.0", - "jest-runtime": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.6.0", - "source-map-support": "^0.5.6", - "throat": "^4.0.0" - }, - "dependencies": { - "@jest/environment": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", - "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "@jest/fake-timers": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0" + "has-flag": "^4.0.0" } }, - "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" } }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" + "is-number": "^7.0.0" } }, - "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + } + } + }, + "jest-runtime": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-25.1.0.tgz", + "integrity": "sha512-mpPYYEdbExKBIBB16ryF6FLZTc1Rbk9Nx0ryIpIMiDDkOeGa0jQOKVI/QeGvVGlunKKm62ywcioeFVzIbK03bA==", + "dev": true, + "requires": { + "@jest/console": "^25.1.0", + "@jest/environment": "^25.1.0", + "@jest/source-map": "^25.1.0", + "@jest/test-result": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.3", + "jest-config": "^25.1.0", + "jest-haste-map": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-mock": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-resolve": "^25.1.0", + "jest-snapshot": "^25.1.0", + "jest-util": "^25.1.0", + "jest-validate": "^25.1.0", + "realpath-native": "^1.1.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.0.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "@babel/types": "^7.8.7", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" }, "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", - "dev": true, - "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" - } + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, - "@jest/transform": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", - "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", "dev": true, "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^24.9.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.9.0", - "jest-regex-util": "^24.9.0", - "jest-util": "^24.9.0", - "micromatch": "^3.1.10", - "pirates": "^4.0.1", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" } }, - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "@babel/types": "^7.8.3" } }, - "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", "dev": true, "requires": { - "@types/yargs-parser": "*" + "@babel/types": "^7.8.3" } }, - "jest-haste-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", - "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.9.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" }, "dependencies": { - "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" + "has-flag": "^3.0.0" } } } }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0" - } - }, - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "@babel/parser": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", "dev": true }, - "jest-resolve": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", - "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + } } }, - "jest-serializer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", - "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", - "dev": true - }, - "jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "@babel/traverse": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" }, "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "dev": true, "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "@babel/highlight": "^7.8.3" } } - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + } }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" } - } - } - }, - "jest-runtime": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", - "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.9.0", - "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/yargs": "^13.0.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "jest-config": "^24.9.0", - "jest-haste-map": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.9.0", - "jest-snapshot": "^24.9.0", - "jest-util": "^24.9.0", - "jest-validate": "^24.9.0", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "strip-bom": "^3.0.0", - "yargs": "^13.3.0" - }, - "dependencies": { + }, + "@jest/console": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", + "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "dev": true, + "requires": { + "@jest/source-map": "^25.1.0", + "chalk": "^3.0.0", + "jest-util": "^25.1.0", + "slash": "^3.0.0" + } + }, "@jest/environment": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", - "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.1.0.tgz", + "integrity": "sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg==", "dev": true, "requires": { - "@jest/fake-timers": "^24.9.0", - "@jest/transform": "^24.9.0", - "@jest/types": "^24.9.0", - "jest-mock": "^24.9.0" + "@jest/fake-timers": "^25.1.0", + "@jest/types": "^25.1.0", + "jest-mock": "^25.1.0" } }, "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.1.0.tgz", + "integrity": "sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-mock": "^25.1.0", + "jest-util": "^25.1.0", + "lolex": "^5.0.0" + } + }, + "@jest/source-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", + "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" + "callsites": "^3.0.0", + "graceful-fs": "^4.2.3", + "source-map": "^0.6.0" } }, "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", + "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" - }, - "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", - "dev": true, - "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" - } - }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" - } - } + "@jest/console": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", - "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", + "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^24.9.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", + "@jest/types": "^25.1.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.9.0", - "jest-regex-util": "^24.9.0", - "jest-util": "^24.9.0", - "micromatch": "^3.1.10", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-util": "^25.1.0", + "micromatch": "^4.0.2", "pirates": "^4.0.1", "realpath-native": "^1.1.0", - "slash": "^2.0.0", + "slash": "^3.0.0", "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" - }, - "dependencies": { - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", - "dev": true - } + "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" } }, "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", "dev": true, "requires": { "@types/yargs-parser": "*" } }, "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "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.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.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 + }, "diff-sequences": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", - "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.1.0.tgz", + "integrity": "sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, "expect": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", - "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-25.1.0.tgz", + "integrity": "sha512-wqHzuoapQkhc3OKPlrpetsfueuEiMf3iWh0R8+duCu9PIjXoP7HgD5aeypwTnXUAjC8aMsiVDaWwlbJ1RlQ38g==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-regex-util": "^24.9.0" - }, - "dependencies": { - "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", - "dev": true - } + "@jest/types": "^25.1.0", + "ansi-styles": "^4.0.0", + "jest-get-type": "^25.1.0", + "jest-matcher-utils": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-regex-util": "^25.1.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" } }, "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "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 + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", + "dev": true, + "requires": { + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/core": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, "jest-diff": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", - "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.1.0.tgz", + "integrity": "sha512-nepXgajT+h017APJTreSieh4zCqnSHEJ1iT8HDlewu630lSJ4Kjjr9KNzm+kzGwwcpsDE6Snx1GJGzzsefaEHw==", "dev": true, "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "chalk": "^3.0.0", + "diff-sequences": "^25.1.0", + "jest-get-type": "^25.1.0", + "pretty-format": "^25.1.0" } }, "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", + "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", "dev": true }, "jest-haste-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", - "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", + "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "anymatch": "^2.0.0", + "@jest/types": "^25.1.0", + "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.9.0", - "jest-util": "^24.9.0", - "jest-worker": "^24.9.0", - "micromatch": "^3.1.10", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "micromatch": "^4.0.2", "sane": "^4.0.3", "walker": "^1.0.7" } }, "jest-matcher-utils": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", - "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz", + "integrity": "sha512-KGOAFcSFbclXIFE7bS4C53iYobKI20ZWleAdAFun4W1Wz1Kkej8Ng6RRbhL8leaEvIOjGXhGf/a1JjO8bkxIWQ==", "dev": true, "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "pretty-format": "^24.9.0" + "chalk": "^3.0.0", + "jest-diff": "^25.1.0", + "jest-get-type": "^25.1.0", + "pretty-format": "^25.1.0" } }, "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", + "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", + "chalk": "^3.0.0", + "micromatch": "^4.0.2", + "slash": "^3.0.0", "stack-utils": "^1.0.1" } }, "jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.1.0.tgz", + "integrity": "sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag==", "dev": true, "requires": { - "@jest/types": "^24.9.0" + "@jest/types": "^25.1.0" } }, + "jest-regex-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", + "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", + "dev": true + }, "jest-resolve": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", - "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.1.0.tgz", + "integrity": "sha512-XkBQaU1SRCHj2Evz2Lu4Czs+uIgJXWypfO57L7JYccmAXv4slXA6hzNblmcRmf7P3cQ1mE7fL3ABV6jAwk4foQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", + "@jest/types": "^25.1.0", "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", + "chalk": "^3.0.0", "jest-pnp-resolver": "^1.2.1", "realpath-native": "^1.1.0" } }, "jest-serializer": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", - "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", + "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", "dev": true }, "jest-snapshot": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", - "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.1.0.tgz", + "integrity": "sha512-xZ73dFYN8b/+X2hKLXz4VpBZGIAn7muD/DAg+pXtDzDGw3iIV10jM7WiHqhCcpDZfGiKEj7/2HXAEPtHTj0P2A==", "dev": true, "requires": { "@babel/types": "^7.0.0", - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "expect": "^24.9.0", - "jest-diff": "^24.9.0", - "jest-get-type": "^24.9.0", - "jest-matcher-utils": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-resolve": "^24.9.0", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "expect": "^25.1.0", + "jest-diff": "^25.1.0", + "jest-get-type": "^25.1.0", + "jest-matcher-utils": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-resolve": "^25.1.0", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "pretty-format": "^24.9.0", - "semver": "^6.2.0" + "pretty-format": "^25.1.0", + "semver": "^7.1.1" + }, + "dependencies": { + "semver": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", + "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", + "dev": true + } } }, "jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", + "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", - "dev": true, - "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" - } - }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" - } - } + "mkdirp": "^0.5.1" } }, "jest-worker": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", - "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", + "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", "dev": true, "requires": { "merge-stream": "^2.0.0", - "supports-color": "^6.1.0" + "supports-color": "^7.0.0" } }, "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "^4.1.0" } }, "merge-stream": { @@ -9699,22 +14952,38 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", "dev": true, "requires": { "p-try": "^2.0.0" } }, "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^2.2.0" } }, "p-try": { @@ -9723,24 +14992,42 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", + "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" + "@jest/types": "^25.1.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" } }, + "react-is": { + "version": "16.13.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz", + "integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==", + "dev": true + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9748,67 +15035,122 @@ "dev": true }, "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": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "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": "^4.1.0" + "ansi-regex": "^5.0.0" } }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" } }, "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.0.tgz", + "integrity": "sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA==", "dev": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^3.0.0", + "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" + "yargs-parser": "^18.1.0" } }, "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.0.tgz", + "integrity": "sha512-o/Jr6JBOv6Yx3pL+5naWSoIA2jJ+ZkMYQG/ie9qFbukBe4uzmBatlXFOiu/tNKRWEtyf+n5w7jc/O16ufqOTdQ==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -9872,194 +15214,726 @@ } }, "jest-validate": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", - "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.1.0.tgz", + "integrity": "sha512-kGbZq1f02/zVO2+t1KQGSVoCTERc5XeObLwITqC6BTRH3Adv7NZdYqCpKIZLUgpLXf2yISzQ465qOZpul8abXA==", "dev": true, "requires": { - "@jest/types": "^24.9.0", + "@jest/types": "^25.1.0", "camelcase": "^5.3.1", - "chalk": "^2.0.1", - "jest-get-type": "^24.9.0", + "chalk": "^3.0.0", + "jest-get-type": "^25.1.0", "leven": "^3.1.0", - "pretty-format": "^24.9.0" + "pretty-format": "^25.1.0" + }, + "dependencies": { + "@jest/types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/yargs": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "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.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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 + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-get-type": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", + "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", + "dev": true + }, + "pretty-format": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", + "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", + "dev": true, + "requires": { + "@jest/types": "^25.1.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + }, + "react-is": { + "version": "16.13.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz", + "integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-watcher": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-25.1.0.tgz", + "integrity": "sha512-Q9eZ7pyaIr6xfU24OeTg4z1fUqBF/4MP6J801lyQfg7CsnZ/TCzAPvCfckKdL5dlBBEKBeHV0AdyjFZ5eWj4ig==", + "dev": true, + "requires": { + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "jest-util": "^25.1.0", + "string-length": "^3.1.0" }, "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/generator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", + "dev": true, + "requires": { + "@babel/types": "^7.8.7", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helpers": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "dev": true, + "requires": { + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" + } + }, + "@babel/highlight": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", + "dev": true + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "@babel/traverse": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "@babel/types": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@jest/console": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", + "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", + "dev": true, + "requires": { + "@jest/source-map": "^25.1.0", + "chalk": "^3.0.0", + "jest-util": "^25.1.0", + "slash": "^3.0.0" + } + }, + "@jest/source-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", + "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.3", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", + "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", + "dev": true, + "requires": { + "@jest/console": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/transform": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", + "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^25.1.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-util": "^25.1.0", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/yargs": { + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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 + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", + "dev": true, + "optional": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", "dev": true, "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "@babel/core": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } } }, - "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "jest-haste-map": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", + "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", "dev": true, "requires": { - "@types/yargs-parser": "*" + "@jest/types": "^25.1.0", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" } }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "jest-regex-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", + "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", "dev": true }, - "jest-get-type": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", - "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "jest-serializer": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", + "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", "dev": true }, - "pretty-format": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", - "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", - "dev": true, - "requires": { - "@jest/types": "^24.9.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - } - } - } - }, - "jest-watcher": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", - "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", - "dev": true, - "requires": { - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/yargs": "^13.0.0", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "jest-util": "^24.9.0", - "string-length": "^2.0.0" - }, - "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "jest-util": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", + "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", "dev": true, "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1" } }, - "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "jest-worker": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", + "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" } }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" - } + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, - "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "braces": "^3.0.1", + "picomatch": "^2.0.5" } }, - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, - "@types/yargs": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", - "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "@types/yargs-parser": "*" + "has-flag": "^4.0.0" } }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" } }, - "jest-mock": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", - "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { - "@jest/types": "^24.9.0" + "is-number": "^7.0.0" } }, - "jest-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", - "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { - "@jest/console": "^24.9.0", - "@jest/fake-timers": "^24.9.0", - "@jest/source-map": "^24.9.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -10107,43 +15981,43 @@ "dev": true }, "jsdom": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", - "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.2.1.tgz", + "integrity": "sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g==", "dev": true, "requires": { "abab": "^2.0.0", - "acorn": "^5.5.3", - "acorn-globals": "^4.1.0", + "acorn": "^7.1.0", + "acorn-globals": "^4.3.2", "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": "^1.0.0", - "data-urls": "^1.0.0", + "cssom": "^0.4.1", + "cssstyle": "^2.0.0", + "data-urls": "^1.1.0", "domexception": "^1.0.1", - "escodegen": "^1.9.1", + "escodegen": "^1.11.1", "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.3.0", - "nwsapi": "^2.0.7", - "parse5": "4.0.0", + "nwsapi": "^2.2.0", + "parse5": "5.1.0", "pn": "^1.1.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", + "request": "^2.88.0", + "request-promise-native": "^1.0.7", + "saxes": "^3.1.9", "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.4", + "tough-cookie": "^3.0.1", "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.1.2", "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^5.2.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^7.0.0", "xml-name-validator": "^3.0.0" }, "dependencies": { "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", "dev": true } } @@ -10258,12 +16132,6 @@ "invert-kv": "^2.0.0" } }, - "left-pad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "dev": true - }, "lerna": { "version": "3.18.4", "resolved": "https://registry.npmjs.org/lerna/-/lerna-3.18.4.tgz", @@ -10400,6 +16268,15 @@ "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", "dev": true }, + "lolex": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", + "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -10963,16 +16840,26 @@ "dev": true }, "node-notifier": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", - "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-6.0.0.tgz", + "integrity": "sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw==", "dev": true, + "optional": true, "requires": { "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", + "is-wsl": "^2.1.1", + "semver": "^6.3.0", "shellwords": "^0.1.1", - "which": "^1.3.0" + "which": "^1.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "optional": true + } } }, "nopt": { @@ -11302,13 +17189,10 @@ "dev": true }, "p-each-series": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", - "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", - "dev": true, - "requires": { - "p-reduce": "^1.0.0" - } + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", + "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", + "dev": true }, "p-finally": { "version": "1.0.0", @@ -11449,9 +17333,9 @@ } }, "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", "dev": true }, "pascalcase": { @@ -11511,6 +17395,12 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, + "picomatch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", + "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", + "dev": true + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -11632,13 +17522,13 @@ } }, "prompts": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.0.tgz", - "integrity": "sha512-NfbbPPg/74fT7wk2XYQ7hAIp9zJyZp5Fu19iRbORqqy1BhtrkZ0fPafBU+7bmn8ie69DpT0R6QpJIN2oisYjJg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.1.tgz", + "integrity": "sha512-qIP2lQyCwYbdzcqHIUi2HAxiWixhoM9OdLCWf8txXsapC/X9YdsCoeyRIXE/GP+Q0J37Q7+XN/MFqbUa7IzXNA==", "dev": true, "requires": { "kleur": "^3.0.3", - "sisteransi": "^1.0.3" + "sisteransi": "^1.0.4" } }, "promzard": { @@ -12068,6 +17958,18 @@ "request-promise-core": "1.1.3", "stealthy-require": "^1.1.1", "tough-cookie": "^2.3.3" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } } }, "require-directory": { @@ -12236,11 +18138,14 @@ } } }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "saxes": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", + "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "dev": true, + "requires": { + "xmlchars": "^2.1.1" + } }, "semver": { "version": "5.5.0", @@ -12296,7 +18201,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true + "dev": true, + "optional": true }, "signal-exit": { "version": "3.0.2", @@ -12691,13 +18597,30 @@ "dev": true }, "string-length": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", - "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-3.1.0.tgz", + "integrity": "sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA==", "dev": true, "requires": { "astral-regex": "^1.0.0", - "strip-ansi": "^4.0.0" + "strip-ansi": "^5.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, "string-width": { @@ -12740,6 +18663,12 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, "strip-indent": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", @@ -12780,6 +18709,33 @@ "has-flag": "^3.0.0" } }, + "supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "svg-element-attributes": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/svg-element-attributes/-/svg-element-attributes-1.3.0.tgz", @@ -12884,6 +18840,33 @@ } } }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } + }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, "test-exclude": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.2.tgz", @@ -13012,11 +18995,12 @@ } }, "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", "dev": true, "requires": { + "ip-regex": "^2.1.0", "psl": "^1.1.28", "punycode": "^2.1.1" } @@ -13127,6 +19111,12 @@ "prelude-ls": "~1.1.2" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, "type-fest": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", @@ -13139,6 +19129,15 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, "typescript": { "version": "3.7.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz", @@ -13321,6 +19320,31 @@ "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", "dev": true }, + "v8-to-istanbul": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.2.tgz", + "integrity": "sha512-G9R+Hpw0ITAmPSr47lSlc5A1uekSYzXxTMlFxso2xoffwo4jQnzbv1p9yXIinO8UMZKfAFewaCHwWvnH4Jb4Ug==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "@types/istanbul-lib-coverage": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", + "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } + } + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -13352,12 +19376,23 @@ } }, "w3c-hr-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", - "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", + "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", "dev": true, "requires": { - "browser-process-hrtime": "^0.1.2" + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" } }, "walker": { @@ -13406,9 +19441,9 @@ "dev": true }, "whatwg-url": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", - "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", "dev": true, "requires": { "lodash.sortby": "^4.7.0", @@ -13603,13 +19638,10 @@ } }, "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.2.tgz", + "integrity": "sha512-2qj/tYkDPDSVf7JiHanwEBwkhxi7DchFewIsSnR33MQtG3O/BPAJjqs4g6XEuayuRqIExSQMHZlmyDLbuSrXYw==", + "dev": true }, "xml-name-validator": { "version": "3.0.0", @@ -13617,6 +19649,12 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index d5cb14b248..cd525a688d 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "eslint-plugin-github": "^2.0.0", "eslint-plugin-jest": "^22.5.1", "flow-bin": "^0.115.0", - "jest": "^24.9.0", + "jest": "^25.1.0", "jest-circus": "^24.7.1", "lerna": "^3.18.4", "prettier": "^1.19.1", diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index 4f775654fa..70ff5431eb 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -13,29 +13,30 @@ } }, "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "dev": true, "requires": { - "@babel/highlight": "^7.0.0" + "@babel/highlight": "^7.8.3" } }, "@babel/core": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.5.5.tgz", - "integrity": "sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.5.5", - "@babel/helpers": "^7.5.5", - "@babel/parser": "^7.5.5", - "@babel/template": "^7.4.4", - "@babel/traverse": "^7.5.5", - "@babel/types": "^7.5.5", - "convert-source-map": "^1.1.0", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.7", + "@babel/helpers": "^7.8.4", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", + "convert-source-map": "^1.7.0", "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", "json5": "^2.1.0", "lodash": "^4.17.13", "resolve": "^1.3.2", @@ -43,21 +44,6 @@ "source-map": "^0.5.0" }, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -67,16 +53,15 @@ } }, "@babel/generator": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz", - "integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", "dev": true, "requires": { - "@babel/types": "^7.5.5", + "@babel/types": "^7.8.7", "jsesc": "^2.5.1", "lodash": "^4.17.13", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "source-map": "^0.5.0" }, "dependencies": { "source-map": { @@ -88,126 +73,170 @@ } }, "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", "dev": true, "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-plugin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", - "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", "dev": true }, "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", "dev": true, "requires": { - "@babel/types": "^7.4.4" + "@babel/types": "^7.8.3" } }, "@babel/helpers": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.5.5.tgz", - "integrity": "sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", "dev": true, "requires": { - "@babel/template": "^7.4.4", - "@babel/traverse": "^7.5.5", - "@babel/types": "^7.5.5" + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" } }, "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", "dev": true, "requires": { "chalk": "^2.0.0", "esutils": "^2.0.2", "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "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" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/parser": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", - "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", "dev": true }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, "@babel/plugin-syntax-object-rest-spread": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", - "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", - "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.4", - "@babel/types": "^7.4.4" + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" } }, "@babel/traverse": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz", - "integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", "dev": true, "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.5.5", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.5.5", - "@babel/types": "^7.5.5", + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.6", + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "@babel/types": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", - "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -215,180 +244,216 @@ "to-fast-properties": "^2.0.0" } }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, "@cnakazawa/watch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", - "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", "dev": true, "requires": { "exec-sh": "^0.3.2", "minimist": "^1.2.0" } }, + "@istanbuljs/load-nyc-config": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", + "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true + }, "@jest/console": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", - "integrity": "sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.1.0.tgz", + "integrity": "sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA==", "dev": true, "requires": { - "@jest/source-map": "^24.3.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "@jest/source-map": "^25.1.0", + "chalk": "^3.0.0", + "jest-util": "^25.1.0", + "slash": "^3.0.0" } }, "@jest/core": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.8.0.tgz", - "integrity": "sha512-R9rhAJwCBQzaRnrRgAdVfnglUuATXdwTRsYqs6NMdVcAl5euG8LtWDe+fVkN27YfKVBW61IojVsXKaOmSnqd/A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/reporters": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-25.1.0.tgz", + "integrity": "sha512-iz05+NmwCmZRzMXvMo6KFipW7nzhbpEawrKrkkdJzgytavPse0biEnCNr2wRlyCsp3SmKaEY+SGv7YWYQnIdig==", + "dev": true, + "requires": { + "@jest/console": "^25.1.0", + "@jest/reporters": "^25.1.0", + "@jest/test-result": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-changed-files": "^24.8.0", - "jest-config": "^24.8.0", - "jest-haste-map": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-resolve-dependencies": "^24.8.0", - "jest-runner": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-snapshot": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", - "jest-watcher": "^24.8.0", - "micromatch": "^3.1.10", - "p-each-series": "^1.0.0", - "pirates": "^4.0.1", + "graceful-fs": "^4.2.3", + "jest-changed-files": "^25.1.0", + "jest-config": "^25.1.0", + "jest-haste-map": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-resolve": "^25.1.0", + "jest-resolve-dependencies": "^25.1.0", + "jest-runner": "^25.1.0", + "jest-runtime": "^25.1.0", + "jest-snapshot": "^25.1.0", + "jest-util": "^25.1.0", + "jest-validate": "^25.1.0", + "jest-watcher": "^25.1.0", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", "realpath-native": "^1.1.0", - "rimraf": "^2.5.4", - "strip-ansi": "^5.0.0" + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "@jest/environment": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.8.0.tgz", - "integrity": "sha512-vlGt2HLg7qM+vtBrSkjDxk9K0YtRBi7HfRFaDxoRtyi+DyVChzhF20duvpdAnKVBV6W5tym8jm0U9EfXbDk1tw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.1.0.tgz", + "integrity": "sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg==", "dev": true, "requires": { - "@jest/fake-timers": "^24.8.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "jest-mock": "^24.8.0" + "@jest/fake-timers": "^25.1.0", + "@jest/types": "^25.1.0", + "jest-mock": "^25.1.0" } }, "@jest/fake-timers": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.8.0.tgz", - "integrity": "sha512-2M4d5MufVXwi6VzZhJ9f5S/wU4ud2ck0kxPof1Iz3zWx6Y+V2eJrES9jEktB6O3o/oEyk+il/uNu9PvASjWXQw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.1.0.tgz", + "integrity": "sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ==", "dev": true, "requires": { - "@jest/types": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-mock": "^24.8.0" + "@jest/types": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-mock": "^25.1.0", + "jest-util": "^25.1.0", + "lolex": "^5.0.0" } }, "@jest/reporters": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.8.0.tgz", - "integrity": "sha512-eZ9TyUYpyIIXfYCrw0UHUWUvE35vx5I92HGMgS93Pv7du+GHIzl+/vh8Qj9MCWFK/4TqyttVBPakWMOfZRIfxw==", - "dev": true, - "requires": { - "@jest/environment": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-25.1.0.tgz", + "integrity": "sha512-ORLT7hq2acJQa8N+NKfs68ZtHFnJPxsGqmofxW7v7urVhzJvpKZG9M7FAcgh9Ee1ZbCteMrirHA3m5JfBtAaDg==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^25.1.0", + "@jest/environment": "^25.1.0", + "@jest/test-result": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.2", - "istanbul-lib-coverage": "^2.0.2", - "istanbul-lib-instrument": "^3.0.1", - "istanbul-lib-report": "^2.0.4", - "istanbul-lib-source-maps": "^3.0.1", - "istanbul-reports": "^2.1.1", - "jest-haste-map": "^24.8.0", - "jest-resolve": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-util": "^24.8.0", - "jest-worker": "^24.6.0", - "node-notifier": "^5.2.1", - "slash": "^2.0.0", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.0", + "jest-haste-map": "^25.1.0", + "jest-resolve": "^25.1.0", + "jest-runtime": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "node-notifier": "^6.0.0", + "slash": "^3.0.0", "source-map": "^0.6.0", - "string-length": "^2.0.0" + "string-length": "^3.1.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^4.0.1" } }, "@jest/source-map": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz", - "integrity": "sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.1.0.tgz", + "integrity": "sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA==", "dev": true, "requires": { "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", + "graceful-fs": "^4.2.3", "source-map": "^0.6.0" } }, "@jest/test-result": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.8.0.tgz", - "integrity": "sha512-+YdLlxwizlfqkFDh7Mc7ONPQAhA4YylU1s529vVM1rsf67vGZH/2GGm5uO8QzPeVyaVMobCQ7FTxl38QrKRlng==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.1.0.tgz", + "integrity": "sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg==", "dev": true, "requires": { - "@jest/console": "^24.7.1", - "@jest/types": "^24.8.0", - "@types/istanbul-lib-coverage": "^2.0.0" + "@jest/console": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" } }, "@jest/test-sequencer": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.8.0.tgz", - "integrity": "sha512-OzL/2yHyPdCHXEzhoBuq37CE99nkme15eHkAzXRVqthreWZamEMA0WoetwstsQBCXABhczpK03JNbc4L01vvLg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-25.1.0.tgz", + "integrity": "sha512-WgZLRgVr2b4l/7ED1J1RJQBOharxS11EFhmwDqknpknE0Pm87HLZVS2Asuuw+HQdfQvm2aXL2FvvBLxOD1D0iw==", "dev": true, "requires": { - "@jest/test-result": "^24.8.0", - "jest-haste-map": "^24.8.0", - "jest-runner": "^24.8.0", - "jest-runtime": "^24.8.0" + "@jest/test-result": "^25.1.0", + "jest-haste-map": "^25.1.0", + "jest-runner": "^25.1.0", + "jest-runtime": "^25.1.0" } }, "@jest/transform": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.8.0.tgz", - "integrity": "sha512-xBMfFUP7TortCs0O+Xtez2W7Zu1PLH9bvJgtraN1CDST6LBM/eTOZ9SfwS/lvV8yOfcDpFmwf9bq5cYbXvqsvA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.1.0.tgz", + "integrity": "sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^24.8.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", + "@jest/types": "^25.1.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-util": "^24.8.0", - "micromatch": "^3.1.10", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-util": "^25.1.0", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", "realpath-native": "^1.1.0", - "slash": "^2.0.0", + "slash": "^3.0.0", "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" + "write-file-atomic": "^3.0.0" } }, "@jest/types": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.8.0.tgz", - "integrity": "sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.1.0.tgz", + "integrity": "sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^12.0.9" + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" } }, "@octokit/auth-token": { @@ -527,10 +592,19 @@ "@types/node": ">= 8" } }, + "@sinonjs/commons": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz", + "integrity": "sha512-Debi3Baff1Qu1Unc3mjJ96MgpbwTn43S1+9yJ0llWygPwDNu2aaWBD6yc9y/Z8XDRNhx7U+u2UDg2OGQXkclUQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, "@types/babel__core": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz", - "integrity": "sha512-cfCCrFmiGY/yq0NuKNxIQvZFy9kY/1immpSpTngOnyIbD4+eJOG5mxphhHDv3CHL9GltO4GcKr54kGBg3RNdbg==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.6.tgz", + "integrity": "sha512-tTnhWszAqvXnhW7m5jQU9PomXSiKXk2sFxpahXvI20SZKu9ylPi8WtIxueZ6ehDWikPT0jeFujMj3X4ZHuf3Tg==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -541,9 +615,9 @@ } }, "@types/babel__generator": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz", - "integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==", + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", + "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", "dev": true, "requires": { "@babel/types": "^7.0.0" @@ -560,14 +634,20 @@ } }, "@types/babel__traverse": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.7.tgz", - "integrity": "sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw==", + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.9.tgz", + "integrity": "sha512-jEFQ8L1tuvPjOI8lnpaf73oCJe+aoxL6ygqSy6c8LcW98zaC+4mzWuQIRCEvKeCOu+lbqdXcg4Uqmm1S8AP1tw==", "dev": true, "requires": { "@babel/types": "^7.3.0" } }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "@types/istanbul-lib-coverage": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", @@ -575,9 +655,9 @@ "dev": true }, "@types/istanbul-lib-report": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", - "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "*" @@ -605,27 +685,36 @@ "dev": true }, "@types/yargs": { - "version": "12.0.12", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.12.tgz", - "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==", + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", + "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", "dev": true }, "abab": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", - "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", + "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", "dev": true }, "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", "dev": true }, "acorn-globals": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.2.tgz", - "integrity": "sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", "dev": true, "requires": { "acorn": "^6.0.1", @@ -633,9 +722,9 @@ }, "dependencies": { "acorn": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.1.tgz", - "integrity": "sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", "dev": true } } @@ -647,46 +736,59 @@ "dev": true }, "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", + "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + } }, "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "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": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "dev": true, "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" } }, "args": { @@ -807,12 +909,6 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -837,55 +933,57 @@ "dev": true }, "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", "dev": true }, "babel-jest": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.8.0.tgz", - "integrity": "sha512-+5/kaZt4I9efoXzPlZASyK/lN9qdRKmmUav9smVc0ruPQD7IsfucQ87gpOE8mn2jbDuS6M/YOW6n3v9ZoIfgnw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-25.1.0.tgz", + "integrity": "sha512-tz0VxUhhOE2y+g8R2oFrO/2VtVjA1lkJeavlhExuRBg3LdNJY9gwQ+Vcvqt9+cqy71MCTJhewvTB7Qtnnr9SWg==", "dev": true, "requires": { - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", "@types/babel__core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.6.0", - "chalk": "^2.4.2", - "slash": "^2.0.0" + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^25.1.0", + "chalk": "^3.0.0", + "slash": "^3.0.0" } }, "babel-plugin-istanbul": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", - "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "find-up": "^3.0.0", - "istanbul-lib-instrument": "^3.3.0", - "test-exclude": "^5.2.3" + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" } }, "babel-plugin-jest-hoist": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", - "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.1.0.tgz", + "integrity": "sha512-oIsopO41vW4YFZ9yNYoLQATnnN46lp+MZ6H4VvPKFkcc2/fkl3CfE/NZZSmnEIEsJRmJAgkVEK0R7Zbl50CpTw==", "dev": true, "requires": { "@types/babel__traverse": "^7.0.6" } }, "babel-preset-jest": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", - "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-25.1.0.tgz", + "integrity": "sha512-eCGn64olaqwUMaugXsTtGAM2I0QTahjEtnRu0ql8Ie+gDWAc1N6wqN0k2NilnyTunM69Pad7gJY7LOtwLimoFQ==", "dev": true, "requires": { + "@babel/plugin-syntax-bigint": "^7.0.0", "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.6.0" + "babel-plugin-jest-hoist": "^25.1.0" } }, "balanced-match": { @@ -986,44 +1084,18 @@ } }, "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } + "fill-range": "^7.0.1" } }, "browser-process-hrtime": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", - "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, "browser-resolve": { @@ -1044,9 +1116,9 @@ } }, "bser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.0.tgz", - "integrity": "sha512-8zsjWrQkkBoLK6uxASk1nJ2SKv97ltiGDo6A3wA0/yRPz+CwmEyDo0hUrhIuukG2JHpAl3bvFIixw2/3Hi0DOg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, "requires": { "node-int64": "^0.4.0" @@ -1116,14 +1188,13 @@ "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==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, "ci-info": { @@ -1162,31 +1233,14 @@ } }, "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "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" - } - } + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" } }, "co": { @@ -1195,10 +1249,10 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "collect-v8-coverage": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.0.tgz", + "integrity": "sha512-VKIhJgvk8E1W28m5avZ2Gv2Ruv5YiF56ug2oclvaG9md69BuZImMG2sk9g7QNKLUbtYAKQjXjYxbYZVUlMMKmQ==", "dev": true }, "collection-visit": { @@ -1212,18 +1266,18 @@ } }, "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "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.3" + "color-name": "~1.1.4" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "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 }, "combined-stream": { @@ -1235,13 +1289,6 @@ "delayed-stream": "~1.0.0" } }, - "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", - "dev": true, - "optional": true - }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -1255,9 +1302,9 @@ "dev": true }, "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "dev": true, "requires": { "safe-buffer": "~5.1.1" @@ -1288,18 +1335,26 @@ } }, "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", "dev": true }, "cssstyle": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", - "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.2.0.tgz", + "integrity": "sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA==", "dev": true, "requires": { - "cssom": "0.3.x" + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } } }, "dashdash": { @@ -1320,28 +1375,15 @@ "abab": "^2.0.0", "whatwg-mimetype": "^2.2.0", "whatwg-url": "^7.0.0" - }, - "dependencies": { - "whatwg-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", - "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - } } }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "decamelize": { @@ -1430,15 +1472,15 @@ "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" }, "detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, "diff-sequences": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", - "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.1.0.tgz", + "integrity": "sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw==", "dev": true }, "domexception": { @@ -1460,6 +1502,12 @@ "safer-buffer": "^2.1.0" } }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -1468,33 +1516,29 @@ "once": "^1.4.0" } }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", "dev": true, "requires": { - "es-to-primitive": "^1.2.0", + "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" } }, "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, "requires": { "is-callable": "^1.1.4", @@ -1509,12 +1553,12 @@ "dev": true }, "escodegen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", - "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", + "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", "dev": true, "requires": { - "esprima": "^3.1.3", + "esprima": "^4.0.1", "estraverse": "^4.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1", @@ -1522,27 +1566,27 @@ } }, "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, "exec-sh": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", - "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", + "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", "dev": true }, "execa": { @@ -1580,6 +1624,15 @@ "to-regex": "^3.0.1" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -1597,21 +1650,27 @@ "requires": { "is-extendable": "^0.1.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, "expect": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.8.0.tgz", - "integrity": "sha512-/zYvP8iMDrzaaxHVa724eJBCKqSHmO0FA7EDkBiRHxg6OipmMn1fN+C8T9L9K8yr7UONkOifu6+LLH+z76CnaA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-25.1.0.tgz", + "integrity": "sha512-wqHzuoapQkhc3OKPlrpetsfueuEiMf3iWh0R8+duCu9PIjXoP7HgD5aeypwTnXUAjC8aMsiVDaWwlbJ1RlQ38g==", "dev": true, "requires": { - "@jest/types": "^24.8.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.8.0", - "jest-matcher-utils": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-regex-util": "^24.3.0" + "@jest/types": "^25.1.0", + "ansi-styles": "^4.0.0", + "jest-get-type": "^25.1.0", + "jest-matcher-utils": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-regex-util": "^25.1.0" } }, "extend": { @@ -1728,15 +1787,15 @@ "dev": true }, "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", "dev": true }, "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "fast-levenshtein": { @@ -1746,44 +1805,31 @@ "dev": true }, "fb-watchman": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", - "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", "dev": true, "requires": { - "bser": "^2.0.0" + "bser": "2.1.1" } }, "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "to-regex-range": "^5.0.1" } }, "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, "for-in": { @@ -1825,569 +1871,34 @@ "dev": true }, "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", + "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", "dev": true, - "optional": true, - "requires": { - "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^4.1.0", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.12.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.7.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "requires": { "pump": "^3.0.0" } @@ -2408,9 +1919,9 @@ } }, "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -2428,28 +1939,17 @@ "dev": true }, "graceful-fs": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", - "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", "dev": true }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true - }, - "handlebars": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", - "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", "dev": true, - "requires": { - "neo-async": "^2.6.0", - "optimist": "^0.6.1", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4" - } + "optional": true }, "har-schema": { "version": "2.0.0", @@ -2494,15 +1994,15 @@ } }, "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, "has-value": { @@ -2534,6 +2034,26 @@ "kind-of": "^4.0.0" }, "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", @@ -2545,12 +2065,6 @@ } } }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true - }, "html-encoding-sniffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", @@ -2560,6 +2074,12 @@ "whatwg-encoding": "^1.0.1" } }, + "html-escaper": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz", + "integrity": "sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==", + "dev": true + }, "http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", @@ -2571,6 +2091,12 @@ "sshpk": "^1.7.0" } }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -2581,13 +2107,13 @@ } }, "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", "dev": true, "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" } }, "imurmurhash": { @@ -2612,19 +2138,10 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", "dev": true }, "is-accessor-descriptor": { @@ -2647,12 +2164,6 @@ } } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -2660,9 +2171,9 @@ "dev": true }, "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", "dev": true }, "is-ci": { @@ -2695,9 +2206,9 @@ } }, "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", "dev": true }, "is-descriptor": { @@ -2726,9 +2237,9 @@ "dev": true }, "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "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 }, "is-generator-fn": { @@ -2738,24 +2249,10 @@ "dev": true }, "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, "is-plain-object": { "version": "3.0.0", @@ -2766,12 +2263,12 @@ } }, "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", "dev": true, "requires": { - "has": "^1.0.1" + "has": "^1.0.3" } }, "is-stream": { @@ -2780,12 +2277,12 @@ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "dev": true, "requires": { - "has-symbols": "^1.0.0" + "has-symbols": "^1.0.1" } }, "is-typedarray": { @@ -2801,10 +2298,11 @@ "dev": true }, "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz", + "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==", + "dev": true, + "optional": true }, "isarray": { "version": "1.0.0", @@ -2829,24 +2327,24 @@ "dev": true }, "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", "dev": true }, "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz", + "integrity": "sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg==", "dev": true, "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" }, "dependencies": { "semver": { @@ -2858,290 +2356,354 @@ } }, "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", "dev": true, "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" } }, "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", "dev": true, "requires": { "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", + "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "istanbul-reports": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", - "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-2osTcC8zcOSUkImzN2EWQta3Vdi4WjjKw99P2yWx5mLnigAM0Rd5uYFn1cf2i/Ois45GkNjaoTqc5CxgMSX80A==", "dev": true, "requires": { - "handlebars": "^4.1.2" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" } }, "jest": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-24.8.0.tgz", - "integrity": "sha512-o0HM90RKFRNWmAWvlyV8i5jGZ97pFwkeVoGvPW1EtLTgJc2+jcuqcbbqcSZLE/3f2S5pt0y2ZBETuhpWNl1Reg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-25.1.0.tgz", + "integrity": "sha512-FV6jEruneBhokkt9MQk0WUFoNTwnF76CLXtwNMfsc0um0TlB/LG2yxUd0KqaFjEJ9laQmVWQWS0sG/t2GsuI0w==", "dev": true, "requires": { - "import-local": "^2.0.0", - "jest-cli": "^24.8.0" + "@jest/core": "^25.1.0", + "import-local": "^3.0.2", + "jest-cli": "^25.1.0" }, "dependencies": { "jest-cli": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.8.0.tgz", - "integrity": "sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-25.1.0.tgz", + "integrity": "sha512-p+aOfczzzKdo3AsLJlhs8J5EW6ffVidfSZZxXedJ0mHPBOln1DccqFmGCoO8JWd4xRycfmwy1eoQkMsF8oekPg==", "dev": true, "requires": { - "@jest/core": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", + "@jest/core": "^25.1.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", "exit": "^0.1.2", - "import-local": "^2.0.0", + "import-local": "^3.0.2", "is-ci": "^2.0.0", - "jest-config": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", + "jest-config": "^25.1.0", + "jest-util": "^25.1.0", + "jest-validate": "^25.1.0", "prompts": "^2.0.1", "realpath-native": "^1.1.0", - "yargs": "^12.0.2" + "yargs": "^15.0.0" } } } }, "jest-changed-files": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.8.0.tgz", - "integrity": "sha512-qgANC1Yrivsq+UrLXsvJefBKVoCsKB0Hv+mBb6NMjjZ90wwxCDmU3hsCXBya30cH+LnPYjwgcU65i6yJ5Nfuug==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-25.1.0.tgz", + "integrity": "sha512-bdL1aHjIVy3HaBO3eEQeemGttsq1BDlHgWcOjEOIAcga7OOEGWHD2WSu8HhL7I1F0mFFyci8VKU4tRNk+qtwDA==", "dev": true, "requires": { - "@jest/types": "^24.8.0", - "execa": "^1.0.0", - "throat": "^4.0.0" + "@jest/types": "^25.1.0", + "execa": "^3.2.0", + "throat": "^5.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz", + "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "p-finally": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", + "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "jest-config": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.8.0.tgz", - "integrity": "sha512-Czl3Nn2uEzVGsOeaewGWoDPD8GStxCpAe0zOYs2x2l0fZAgPbCr3uwUkgNKV3LwE13VXythM946cd5rdGkkBZw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-25.1.0.tgz", + "integrity": "sha512-tLmsg4SZ5H7tuhBC5bOja0HEblM0coS3Wy5LTCb2C8ZV6eWLewHyK+3qSq9Bi29zmWQ7ojdCd3pxpx4l4d2uGw==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.8.0", - "@jest/types": "^24.8.0", - "babel-jest": "^24.8.0", - "chalk": "^2.0.1", + "@jest/test-sequencer": "^25.1.0", + "@jest/types": "^25.1.0", + "babel-jest": "^25.1.0", + "chalk": "^3.0.0", "glob": "^7.1.1", - "jest-environment-jsdom": "^24.8.0", - "jest-environment-node": "^24.8.0", - "jest-get-type": "^24.8.0", - "jest-jasmine2": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.8.0", + "jest-environment-jsdom": "^25.1.0", + "jest-environment-node": "^25.1.0", + "jest-get-type": "^25.1.0", + "jest-jasmine2": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-resolve": "^25.1.0", + "jest-util": "^25.1.0", + "jest-validate": "^25.1.0", + "micromatch": "^4.0.2", + "pretty-format": "^25.1.0", "realpath-native": "^1.1.0" } }, "jest-diff": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.8.0.tgz", - "integrity": "sha512-wxetCEl49zUpJ/bvUmIFjd/o52J+yWcoc5ZyPq4/W1LUKGEhRYDIbP1KcF6t+PvqNrGAFk4/JhtxDq/Nnzs66g==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.1.0.tgz", + "integrity": "sha512-nepXgajT+h017APJTreSieh4zCqnSHEJ1iT8HDlewu630lSJ4Kjjr9KNzm+kzGwwcpsDE6Snx1GJGzzsefaEHw==", "dev": true, "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.3.0", - "jest-get-type": "^24.8.0", - "pretty-format": "^24.8.0" + "chalk": "^3.0.0", + "diff-sequences": "^25.1.0", + "jest-get-type": "^25.1.0", + "pretty-format": "^25.1.0" } }, "jest-docblock": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.3.0.tgz", - "integrity": "sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-25.1.0.tgz", + "integrity": "sha512-370P/mh1wzoef6hUKiaMcsPtIapY25suP6JqM70V9RJvdKLrV4GaGbfUseUVk4FZJw4oTZ1qSCJNdrClKt5JQA==", "dev": true, "requires": { - "detect-newline": "^2.1.0" + "detect-newline": "^3.0.0" } }, "jest-each": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.8.0.tgz", - "integrity": "sha512-NrwK9gaL5+XgrgoCsd9svsoWdVkK4gnvyhcpzd6m487tXHqIdYeykgq3MKI1u4I+5Zf0tofr70at9dWJDeb+BA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-25.1.0.tgz", + "integrity": "sha512-R9EL8xWzoPySJ5wa0DXFTj7NrzKpRD40Jy+zQDp3Qr/2QmevJgkN9GqioCGtAJ2bW9P/MQRznQHQQhoeAyra7A==", "dev": true, "requires": { - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.8.0", - "jest-util": "^24.8.0", - "pretty-format": "^24.8.0" + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "jest-get-type": "^25.1.0", + "jest-util": "^25.1.0", + "pretty-format": "^25.1.0" } }, "jest-environment-jsdom": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.8.0.tgz", - "integrity": "sha512-qbvgLmR7PpwjoFjM/sbuqHJt/NCkviuq9vus9NBn/76hhSidO+Z6Bn9tU8friecegbJL8gzZQEMZBQlFWDCwAQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-25.1.0.tgz", + "integrity": "sha512-ILb4wdrwPAOHX6W82GGDUiaXSSOE274ciuov0lztOIymTChKFtC02ddyicRRCdZlB5YSrv3vzr1Z5xjpEe1OHQ==", "dev": true, "requires": { - "@jest/environment": "^24.8.0", - "@jest/fake-timers": "^24.8.0", - "@jest/types": "^24.8.0", - "jest-mock": "^24.8.0", - "jest-util": "^24.8.0", - "jsdom": "^11.5.1" + "@jest/environment": "^25.1.0", + "@jest/fake-timers": "^25.1.0", + "@jest/types": "^25.1.0", + "jest-mock": "^25.1.0", + "jest-util": "^25.1.0", + "jsdom": "^15.1.1" } }, "jest-environment-node": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.8.0.tgz", - "integrity": "sha512-vIGUEScd1cdDgR6sqn2M08sJTRLQp6Dk/eIkCeO4PFHxZMOgy+uYLPMC4ix3PEfM5Au/x3uQ/5Tl0DpXXZsJ/Q==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-25.1.0.tgz", + "integrity": "sha512-U9kFWTtAPvhgYY5upnH9rq8qZkj6mYLup5l1caAjjx9uNnkLHN2xgZy5mo4SyLdmrh/EtB9UPpKFShvfQHD0Iw==", "dev": true, "requires": { - "@jest/environment": "^24.8.0", - "@jest/fake-timers": "^24.8.0", - "@jest/types": "^24.8.0", - "jest-mock": "^24.8.0", - "jest-util": "^24.8.0" + "@jest/environment": "^25.1.0", + "@jest/fake-timers": "^25.1.0", + "@jest/types": "^25.1.0", + "jest-mock": "^25.1.0", + "jest-util": "^25.1.0" } }, "jest-get-type": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.8.0.tgz", - "integrity": "sha512-RR4fo8jEmMD9zSz2nLbs2j0zvPpk/KCEz3a62jJWbd2ayNo0cb+KFRxPHVhE4ZmgGJEQp0fosmNz84IfqM8cMQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.1.0.tgz", + "integrity": "sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==", "dev": true }, "jest-haste-map": { - "version": "24.8.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.8.1.tgz", - "integrity": "sha512-SwaxMGVdAZk3ernAx2Uv2sorA7jm3Kx+lR0grp6rMmnY06Kn/urtKx1LPN2mGTea4fCT38impYT28FfcLUhX0g==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.1.0.tgz", + "integrity": "sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw==", "dev": true, "requires": { - "@jest/types": "^24.8.0", - "anymatch": "^2.0.0", + "@jest/types": "^25.1.0", + "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.4.0", - "jest-util": "^24.8.0", - "jest-worker": "^24.6.0", - "micromatch": "^3.1.10", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", + "micromatch": "^4.0.2", "sane": "^4.0.3", "walker": "^1.0.7" } }, "jest-jasmine2": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.8.0.tgz", - "integrity": "sha512-cEky88npEE5LKd5jPpTdDCLvKkdyklnaRycBXL6GNmpxe41F0WN44+i7lpQKa/hcbXaQ+rc9RMaM4dsebrYong==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-25.1.0.tgz", + "integrity": "sha512-GdncRq7jJ7sNIQ+dnXvpKO2MyP6j3naNK41DTTjEAhLEdpImaDA9zSAZwDhijjSF/D7cf4O5fdyUApGBZleaEg==", "dev": true, "requires": { "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", + "@jest/environment": "^25.1.0", + "@jest/source-map": "^25.1.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", "co": "^4.6.0", - "expect": "^24.8.0", + "expect": "^25.1.0", "is-generator-fn": "^2.0.0", - "jest-each": "^24.8.0", - "jest-matcher-utils": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-snapshot": "^24.8.0", - "jest-util": "^24.8.0", - "pretty-format": "^24.8.0", - "throat": "^4.0.0" + "jest-each": "^25.1.0", + "jest-matcher-utils": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-runtime": "^25.1.0", + "jest-snapshot": "^25.1.0", + "jest-util": "^25.1.0", + "pretty-format": "^25.1.0", + "throat": "^5.0.0" } }, "jest-leak-detector": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.8.0.tgz", - "integrity": "sha512-cG0yRSK8A831LN8lIHxI3AblB40uhv0z+SsQdW3GoMMVcK+sJwrIIyax5tu3eHHNJ8Fu6IMDpnLda2jhn2pD/g==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-25.1.0.tgz", + "integrity": "sha512-3xRI264dnhGaMHRvkFyEKpDeaRzcEBhyNrOG5oT8xPxOyUAblIAQnpiR3QXu4wDor47MDTiHbiFcbypdLcLW5w==", "dev": true, "requires": { - "pretty-format": "^24.8.0" + "jest-get-type": "^25.1.0", + "pretty-format": "^25.1.0" } }, "jest-matcher-utils": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.8.0.tgz", - "integrity": "sha512-lex1yASY51FvUuHgm0GOVj7DCYEouWSlIYmCW7APSqB9v8mXmKSn5+sWVF0MhuASG0bnYY106/49JU1FZNl5hw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz", + "integrity": "sha512-KGOAFcSFbclXIFE7bS4C53iYobKI20ZWleAdAFun4W1Wz1Kkej8Ng6RRbhL8leaEvIOjGXhGf/a1JjO8bkxIWQ==", "dev": true, "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.8.0", - "jest-get-type": "^24.8.0", - "pretty-format": "^24.8.0" + "chalk": "^3.0.0", + "jest-diff": "^25.1.0", + "jest-get-type": "^25.1.0", + "pretty-format": "^25.1.0" } }, "jest-message-util": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.8.0.tgz", - "integrity": "sha512-p2k71rf/b6ns8btdB0uVdljWo9h0ovpnEe05ZKWceQGfXYr4KkzgKo3PBi8wdnd9OtNh46VpNIJynUn/3MKm1g==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.1.0.tgz", + "integrity": "sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", + "chalk": "^3.0.0", + "micromatch": "^4.0.2", + "slash": "^3.0.0", "stack-utils": "^1.0.1" } }, "jest-mock": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.8.0.tgz", - "integrity": "sha512-6kWugwjGjJw+ZkK4mDa0Df3sDlUTsV47MSrT0nGQ0RBWJbpODDQ8MHDVtGtUYBne3IwZUhtB7elxHspU79WH3A==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.1.0.tgz", + "integrity": "sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag==", "dev": true, "requires": { - "@jest/types": "^24.8.0" + "@jest/types": "^25.1.0" } }, "jest-pnp-resolver": { @@ -3151,187 +2713,178 @@ "dev": true }, "jest-regex-util": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", - "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.1.0.tgz", + "integrity": "sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w==", "dev": true }, "jest-resolve": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.8.0.tgz", - "integrity": "sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.1.0.tgz", + "integrity": "sha512-XkBQaU1SRCHj2Evz2Lu4Czs+uIgJXWypfO57L7JYccmAXv4slXA6hzNblmcRmf7P3cQ1mE7fL3ABV6jAwk4foQ==", "dev": true, "requires": { - "@jest/types": "^24.8.0", + "@jest/types": "^25.1.0", "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", + "chalk": "^3.0.0", "jest-pnp-resolver": "^1.2.1", "realpath-native": "^1.1.0" } }, "jest-resolve-dependencies": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.8.0.tgz", - "integrity": "sha512-hyK1qfIf/krV+fSNyhyJeq3elVMhK9Eijlwy+j5jqmZ9QsxwKBiP6qukQxaHtK8k6zql/KYWwCTQ+fDGTIJauw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-25.1.0.tgz", + "integrity": "sha512-Cu/Je38GSsccNy4I2vL12ZnBlD170x2Oh1devzuM9TLH5rrnLW1x51lN8kpZLYTvzx9j+77Y5pqBaTqfdzVzrw==", "dev": true, "requires": { - "@jest/types": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-snapshot": "^24.8.0" + "@jest/types": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-snapshot": "^25.1.0" } }, "jest-runner": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.8.0.tgz", - "integrity": "sha512-utFqC5BaA3JmznbissSs95X1ZF+d+4WuOWwpM9+Ak356YtMhHE/GXUondZdcyAAOTBEsRGAgH/0TwLzfI9h7ow==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-25.1.0.tgz", + "integrity": "sha512-su3O5fy0ehwgt+e8Wy7A8CaxxAOCMzL4gUBftSs0Ip32S0epxyZPDov9Znvkl1nhVOJNf4UwAsnqfc3plfQH9w==", "dev": true, "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.8.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "chalk": "^2.4.2", + "@jest/console": "^25.1.0", + "@jest/environment": "^25.1.0", + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-config": "^24.8.0", - "jest-docblock": "^24.3.0", - "jest-haste-map": "^24.8.0", - "jest-jasmine2": "^24.8.0", - "jest-leak-detector": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-resolve": "^24.8.0", - "jest-runtime": "^24.8.0", - "jest-util": "^24.8.0", - "jest-worker": "^24.6.0", + "graceful-fs": "^4.2.3", + "jest-config": "^25.1.0", + "jest-docblock": "^25.1.0", + "jest-haste-map": "^25.1.0", + "jest-jasmine2": "^25.1.0", + "jest-leak-detector": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-resolve": "^25.1.0", + "jest-runtime": "^25.1.0", + "jest-util": "^25.1.0", + "jest-worker": "^25.1.0", "source-map-support": "^0.5.6", - "throat": "^4.0.0" + "throat": "^5.0.0" } }, "jest-runtime": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.8.0.tgz", - "integrity": "sha512-Mq0aIXhvO/3bX44ccT+czU1/57IgOMyy80oM0XR/nyD5zgBcesF84BPabZi39pJVA6UXw+fY2Q1N+4BiVUBWOA==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.8.0", - "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.8.0", - "@jest/types": "^24.8.0", - "@types/yargs": "^12.0.2", - "chalk": "^2.0.1", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-25.1.0.tgz", + "integrity": "sha512-mpPYYEdbExKBIBB16ryF6FLZTc1Rbk9Nx0ryIpIMiDDkOeGa0jQOKVI/QeGvVGlunKKm62ywcioeFVzIbK03bA==", + "dev": true, + "requires": { + "@jest/console": "^25.1.0", + "@jest/environment": "^25.1.0", + "@jest/source-map": "^25.1.0", + "@jest/test-result": "^25.1.0", + "@jest/transform": "^25.1.0", + "@jest/types": "^25.1.0", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0", + "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "jest-config": "^24.8.0", - "jest-haste-map": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-mock": "^24.8.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.8.0", - "jest-snapshot": "^24.8.0", - "jest-util": "^24.8.0", - "jest-validate": "^24.8.0", + "graceful-fs": "^4.2.3", + "jest-config": "^25.1.0", + "jest-haste-map": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-mock": "^25.1.0", + "jest-regex-util": "^25.1.0", + "jest-resolve": "^25.1.0", + "jest-snapshot": "^25.1.0", + "jest-util": "^25.1.0", + "jest-validate": "^25.1.0", "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "strip-bom": "^3.0.0", - "yargs": "^12.0.2" + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.0.0" } }, "jest-serializer": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", - "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.1.0.tgz", + "integrity": "sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA==", "dev": true }, "jest-snapshot": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.8.0.tgz", - "integrity": "sha512-5ehtWoc8oU9/cAPe6fez6QofVJLBKyqkY2+TlKTOf0VllBB/mqUNdARdcjlZrs9F1Cv+/HKoCS/BknT0+tmfPg==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.1.0.tgz", + "integrity": "sha512-xZ73dFYN8b/+X2hKLXz4VpBZGIAn7muD/DAg+pXtDzDGw3iIV10jM7WiHqhCcpDZfGiKEj7/2HXAEPtHTj0P2A==", "dev": true, "requires": { "@babel/types": "^7.0.0", - "@jest/types": "^24.8.0", - "chalk": "^2.0.1", - "expect": "^24.8.0", - "jest-diff": "^24.8.0", - "jest-matcher-utils": "^24.8.0", - "jest-message-util": "^24.8.0", - "jest-resolve": "^24.8.0", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", + "expect": "^25.1.0", + "jest-diff": "^25.1.0", + "jest-get-type": "^25.1.0", + "jest-matcher-utils": "^25.1.0", + "jest-message-util": "^25.1.0", + "jest-resolve": "^25.1.0", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "pretty-format": "^24.8.0", - "semver": "^5.5.0" + "pretty-format": "^25.1.0", + "semver": "^7.1.1" + }, + "dependencies": { + "semver": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", + "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==", + "dev": true + } } }, "jest-util": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.8.0.tgz", - "integrity": "sha512-DYZeE+XyAnbNt0BG1OQqKy/4GVLPtzwGx5tsnDrFcax36rVE3lTA5fbvgmbVPUZf9w77AJ8otqR4VBbfFJkUZA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.1.0.tgz", + "integrity": "sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw==", "dev": true, "requires": { - "@jest/console": "^24.7.1", - "@jest/fake-timers": "^24.8.0", - "@jest/source-map": "^24.3.0", - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", + "@jest/types": "^25.1.0", + "chalk": "^3.0.0", "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" + "mkdirp": "^0.5.1" } }, "jest-validate": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.8.0.tgz", - "integrity": "sha512-+/N7VOEMW1Vzsrk3UWBDYTExTPwf68tavEPKDnJzrC6UlHtUDU/fuEdXqFoHzv9XnQ+zW6X3qMZhJ3YexfeLDA==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.1.0.tgz", + "integrity": "sha512-kGbZq1f02/zVO2+t1KQGSVoCTERc5XeObLwITqC6BTRH3Adv7NZdYqCpKIZLUgpLXf2yISzQ465qOZpul8abXA==", "dev": true, "requires": { - "@jest/types": "^24.8.0", - "camelcase": "^5.0.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.8.0", - "leven": "^2.1.0", - "pretty-format": "^24.8.0" + "@jest/types": "^25.1.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "jest-get-type": "^25.1.0", + "leven": "^3.1.0", + "pretty-format": "^25.1.0" } }, "jest-watcher": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.8.0.tgz", - "integrity": "sha512-SBjwHt5NedQoVu54M5GEx7cl7IGEFFznvd/HNT8ier7cCAx/Qgu9ZMlaTQkvK22G1YOpcWBLQPFSImmxdn3DAw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-25.1.0.tgz", + "integrity": "sha512-Q9eZ7pyaIr6xfU24OeTg4z1fUqBF/4MP6J801lyQfg7CsnZ/TCzAPvCfckKdL5dlBBEKBeHV0AdyjFZ5eWj4ig==", "dev": true, "requires": { - "@jest/test-result": "^24.8.0", - "@jest/types": "^24.8.0", - "@types/yargs": "^12.0.9", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "jest-util": "^24.8.0", - "string-length": "^2.0.0" + "@jest/test-result": "^25.1.0", + "@jest/types": "^25.1.0", + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "jest-util": "^25.1.0", + "string-length": "^3.1.0" } }, "jest-worker": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz", - "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", + "integrity": "sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg==", "dev": true, "requires": { - "merge-stream": "^1.0.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" } }, "js-tokens": { @@ -3340,6 +2893,16 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -3347,36 +2910,36 @@ "dev": true }, "jsdom": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", - "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.2.1.tgz", + "integrity": "sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g==", "dev": true, "requires": { "abab": "^2.0.0", - "acorn": "^5.5.3", - "acorn-globals": "^4.1.0", + "acorn": "^7.1.0", + "acorn-globals": "^4.3.2", "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": "^1.0.0", - "data-urls": "^1.0.0", + "cssom": "^0.4.1", + "cssstyle": "^2.0.0", + "data-urls": "^1.1.0", "domexception": "^1.0.1", - "escodegen": "^1.9.1", + "escodegen": "^1.11.1", "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.3.0", - "nwsapi": "^2.0.7", - "parse5": "4.0.0", + "nwsapi": "^2.2.0", + "parse5": "5.1.0", "pn": "^1.1.0", - "request": "^2.87.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", + "request": "^2.88.0", + "request-promise-native": "^1.0.7", + "saxes": "^3.1.9", "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.4", + "tough-cookie": "^3.0.1", "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.1.2", "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^5.2.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^7.0.0", "xml-name-validator": "^3.0.0" } }, @@ -3386,12 +2949,6 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -3411,9 +2968,9 @@ "dev": true }, "json5": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", - "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", + "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", "dev": true, "requires": { "minimist": "^1.2.0" @@ -3432,9 +2989,9 @@ } }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, "kleur": { @@ -3443,25 +3000,10 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "left-pad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "dev": true - }, "leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true }, "levn": { @@ -3474,26 +3016,13 @@ "type-check": "~0.3.2" } }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "^4.1.0" } }, "lodash": { @@ -3523,13 +3052,13 @@ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "lolex": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", + "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", "dev": true, "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" + "@sinonjs/commons": "^1.7.0" } }, "macos-release": { @@ -3538,19 +3067,18 @@ "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==" }, "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", "dev": true, "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "semver": "^6.0.0" }, "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } @@ -3564,15 +3092,6 @@ "tmpl": "1.0.x" } }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -3588,60 +3107,35 @@ "object-visit": "^1.0.0" } }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, "merge-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", - "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", - "dev": true, - "requires": { - "readable-stream": "^2.0.1" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "braces": "^3.0.1", + "picomatch": "^2.0.5" } }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", "dev": true }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", "dev": true, "requires": { - "mime-db": "1.40.0" + "mime-db": "1.43.0" } }, "mimic-fn": { @@ -3719,18 +3213,11 @@ } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true, - "optional": true - }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", @@ -3756,12 +3243,6 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, - "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", - "dev": true - }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -3785,38 +3266,33 @@ "dev": true }, "node-notifier": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", - "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-6.0.0.tgz", + "integrity": "sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw==", "dev": true, + "optional": true, "requires": { "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", + "is-wsl": "^2.1.1", + "semver": "^6.3.0", "shellwords": "^0.1.1", - "which": "^1.3.0" - } - }, - "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==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "which": "^1.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "optional": true + } } }, "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, "npm-run-path": { "version": "2.0.2", @@ -3826,16 +3302,10 @@ "path-key": "^2.0.0" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, "nwsapi": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz", - "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", + "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", "dev": true }, "oauth-sign": { @@ -3875,6 +3345,12 @@ } } }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -3898,14 +3374,26 @@ } } }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, "requires": { "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" } }, "object.pick": { @@ -3938,55 +3426,27 @@ "wrappy": "1" } }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", "dev": true, "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - }, - "dependencies": { - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - } + "mimic-fn": "^2.1.0" } }, "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "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.4", + "fast-levenshtein": "~2.0.6", "levn": "~0.3.0", "prelude-ls": "~1.1.2", "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - }, - "dependencies": { - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - } - } - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" + "word-wrap": "~1.2.3" } }, "os-name": { @@ -3998,76 +3458,45 @@ "windows-release": "^3.1.0" } }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, "p-each-series": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", - "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", - "dev": true, - "requires": { - "p-reduce": "^1.0.0" - } + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", + "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", + "dev": true }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true - }, "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", "dev": true, "requires": { "p-try": "^2.0.0" } }, "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^2.2.0" } }, - "p-reduce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", - "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", - "dev": true - }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", "dev": true }, "pascalcase": { @@ -4077,9 +3506,9 @@ "dev": true }, "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-is-absolute": { @@ -4099,25 +3528,16 @@ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "picomatch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", + "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", "dev": true }, "pirates": { @@ -4130,12 +3550,12 @@ } }, "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { - "find-up": "^3.0.0" + "find-up": "^4.0.0" } }, "pkginfo": { @@ -4163,31 +3583,25 @@ "dev": true }, "pretty-format": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.8.0.tgz", - "integrity": "sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.1.0.tgz", + "integrity": "sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==", "dev": true, - "requires": { - "@jest/types": "^24.8.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "requires": { + "@jest/types": "^25.1.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } }, "prompts": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.1.0.tgz", - "integrity": "sha512-+x5TozgqYdOwWsQFZizE/Tra3fKvAoy037kOyU6cgz84n8f6zxngLOV4O32kTwt9FcLCxAqw0P/c8rOr9y+Gfg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.1.tgz", + "integrity": "sha512-qIP2lQyCwYbdzcqHIUi2HAxiWixhoM9OdLCWf8txXsapC/X9YdsCoeyRIXE/GP+Q0J37Q7+XN/MFqbUa7IzXNA==", "dev": true, "requires": { - "kleur": "^3.0.2", - "sisteransi": "^1.0.0" + "kleur": "^3.0.3", + "sisteransi": "^1.0.4" } }, "proxy": { @@ -4219,9 +3633,9 @@ } }, "psl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.2.0.tgz", - "integrity": "sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", + "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==", "dev": true }, "pump": { @@ -4246,47 +3660,11 @@ "dev": true }, "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", + "version": "16.13.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz", + "integrity": "sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA==", "dev": true }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, "realpath-native": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", @@ -4325,9 +3703,9 @@ "dev": true }, "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "dev": true, "requires": { "aws-sign2": "~0.7.0", @@ -4337,7 +3715,7 @@ "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", - "har-validator": "~5.1.0", + "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", @@ -4347,47 +3725,53 @@ "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", + "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" }, "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" + "psl": "^1.1.28", + "punycode": "^2.1.1" } } } }, "request-promise-core": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", - "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", + "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", "dev": true, "requires": { - "lodash": "^4.17.11" + "lodash": "^4.17.15" } }, "request-promise-native": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", - "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", + "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", "dev": true, "requires": { - "request-promise-core": "1.1.2", + "request-promise-core": "1.1.3", "stealthy-require": "^1.1.1", "tough-cookie": "^2.3.3" + }, + "dependencies": { + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } } }, "require-directory": { @@ -4403,27 +3787,27 @@ "dev": true }, "resolve": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz", - "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", "dev": true, "requires": { "path-parse": "^1.0.6" } }, "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "requires": { - "resolve-from": "^3.0.0" + "resolve-from": "^5.0.0" } }, "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, "resolve-url": { @@ -4439,9 +3823,9 @@ "dev": true }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "requires": { "glob": "^7.1.3" @@ -4489,13 +3873,146 @@ "micromatch": "^3.1.4", "minimist": "^1.1.1", "walker": "~1.0.5" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } } }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "saxes": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", + "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", + "dev": true, + "requires": { + "xmlchars": "^2.1.1" + } }, "semver": { "version": "5.7.0", @@ -4563,7 +4080,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true + "dev": true, + "optional": true }, "signal-exit": { "version": "3.0.2", @@ -4571,15 +4089,15 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "sisteransi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.2.tgz", - "integrity": "sha512-ZcYcZcT69nSLAR2oLN2JwNmLkJEKGooFMCdvOkFrToUt/WfcRWqhIg4P4KwY4dmLbuyXIx4o4YmPsvMRJYJd/w==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.4.tgz", + "integrity": "sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig==", "dev": true }, "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, "snapdragon": { @@ -4598,6 +4116,15 @@ "use": "^3.1.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -4616,6 +4143,12 @@ "is-extendable": "^0.1.0" } }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -4708,12 +4241,12 @@ "dev": true }, "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", "dev": true, "requires": { - "atob": "^2.1.1", + "atob": "^2.1.2", "decode-uri-component": "^0.2.0", "resolve-url": "^0.2.1", "source-map-url": "^0.4.0", @@ -4721,9 +4254,9 @@ } }, "source-map-support": { - "version": "0.5.12", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", - "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -4736,38 +4269,6 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true - }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -4777,6 +4278,12 @@ "extend-shallow": "^3.0.0" } }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -4828,28 +4335,28 @@ "dev": true }, "string-length": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", - "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-3.1.0.tgz", + "integrity": "sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA==", "dev": true, "requires": { "astral-regex": "^1.0.0", - "strip-ansi": "^4.0.0" + "strip-ansi": "^5.2.0" }, "dependencies": { "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^4.1.0" } } } @@ -4864,54 +4371,49 @@ } }, "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": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "string.prototype.trimleft": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", + "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "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" - } - } + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "string.prototype.trimright": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", + "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" } }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "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": "^4.1.0" + "ansi-regex": "^5.0.0" } }, "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true }, "strip-eof": { @@ -4919,13 +4421,29 @@ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", + "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" } }, "symbol-tree": { @@ -4934,22 +4452,31 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" } }, "throat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", - "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "dev": true }, "tmpl": { @@ -4997,21 +4524,21 @@ } }, "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "^7.0.0" } }, "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", "dev": true, "requires": { + "ip-regex": "^2.1.0", "psl": "^1.1.28", "punycode": "^2.1.1" } @@ -5025,12 +4552,6 @@ "punycode": "^2.1.0" } }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, "tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", @@ -5060,15 +4581,25 @@ "prelude-ls": "~1.1.2" } }, - "uglify-js": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", - "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, - "optional": true, "requires": { - "commander": "~2.20.0", - "source-map": "~0.6.1" + "is-typedarray": "^1.0.0" } }, "union-value": { @@ -5158,36 +4689,41 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" } }, "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", "dev": true }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "v8-to-istanbul": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.2.tgz", + "integrity": "sha512-G9R+Hpw0ITAmPSr47lSlc5A1uekSYzXxTMlFxso2xoffwo4jQnzbv1p9yXIinO8UMZKfAFewaCHwWvnH4Jb4Ug==", "dev": true, "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } } }, "verror": { @@ -5202,12 +4738,23 @@ } }, "w3c-hr-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", - "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "dev": true, + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", + "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", "dev": true, "requires": { - "browser-process-hrtime": "^0.1.2" + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" } }, "walker": { @@ -5241,9 +4788,9 @@ "dev": true }, "whatwg-url": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", - "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", "dev": true, "requires": { "lodash.sortby": "^4.7.0", @@ -5273,57 +4820,21 @@ "execa": "^1.0.0" } }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" } }, "wrappy": { @@ -5332,24 +4843,22 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", - "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.2.tgz", + "integrity": "sha512-2qj/tYkDPDSVf7JiHanwEBwkhxi7DchFewIsSnR33MQtG3O/BPAJjqs4g6XEuayuRqIExSQMHZlmyDLbuSrXYw==", + "dev": true }, "xml-name-validator": { "version": "3.0.0", @@ -5357,6 +4866,12 @@ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "y18n": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", @@ -5364,37 +4879,28 @@ "dev": true }, "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.0.tgz", + "integrity": "sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA==", "dev": true, "requires": { - "cliui": "^4.0.0", + "cliui": "^6.0.0", "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", + "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^2.0.0", + "string-width": "^4.2.0", "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - }, - "dependencies": { - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - } + "y18n": "^4.0.0", + "yargs-parser": "^18.1.0" } }, "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.0.tgz", + "integrity": "sha512-o/Jr6JBOv6Yx3pL+5naWSoIA2jJ+ZkMYQG/ie9qFbukBe4uzmBatlXFOiu/tNKRWEtyf+n5w7jc/O16ufqOTdQ==", "dev": true, "requires": { "camelcase": "^5.0.0", diff --git a/packages/github/package.json b/packages/github/package.json index d91663789a..45582ccc41 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -42,7 +42,7 @@ "@octokit/rest": "^16.43.1" }, "devDependencies": { - "jest": "^24.7.1", + "jest": "^25.1.0", "proxy": "^1.0.1" } } From 5859d7172e5f4281198f6d74eeb9fe96fb27a23d Mon Sep 17 00:00:00 2001 From: eric sciple Date: Mon, 9 Mar 2020 14:35:53 -0400 Subject: [PATCH 102/192] only retry downloadtool on 500s and 408 and 429 (#373) --- packages/tool-cache/RELEASES.md | 4 + .../tool-cache/__tests__/retry-helper.test.ts | 40 ++++++++- .../tool-cache/__tests__/tool-cache.test.ts | 33 +++++++ packages/tool-cache/package.json | 2 +- packages/tool-cache/src/retry-helper.ts | 9 +- packages/tool-cache/src/tool-cache.ts | 86 +++++++++++-------- 6 files changed, 136 insertions(+), 38 deletions(-) diff --git a/packages/tool-cache/RELEASES.md b/packages/tool-cache/RELEASES.md index aeafeffdb9..7541b4a7de 100644 --- a/packages/tool-cache/RELEASES.md +++ b/packages/tool-cache/RELEASES.md @@ -1,5 +1,9 @@ # @actions/tool-cache Releases +### 1.3.3 + +- [Update downloadTool to only retry 500s and 408 and 429](https://github.com/actions/toolkit/pull/373) + ### 1.3.2 - [Update downloadTool with better error handling and retries](https://github.com/actions/toolkit/pull/369) diff --git a/packages/tool-cache/__tests__/retry-helper.test.ts b/packages/tool-cache/__tests__/retry-helper.test.ts index cccf00757e..90b2e7c9fc 100644 --- a/packages/tool-cache/__tests__/retry-helper.test.ts +++ b/packages/tool-cache/__tests__/retry-helper.test.ts @@ -66,7 +66,7 @@ describe('retry-helper tests', () => { expect(info[3]).toMatch(/Waiting .+ seconds before trying again/) }) - it('all attempts fail succeeds', async () => { + it('all attempts fail', async () => { let attempts = 0 let error: Error = (null as unknown) as Error try { @@ -84,4 +84,42 @@ describe('retry-helper tests', () => { expect(info[2]).toBe('some error 2') expect(info[3]).toMatch(/Waiting .+ seconds before trying again/) }) + + it('checks retryable after first attempt', async () => { + let attempts = 0 + let error: Error = (null as unknown) as Error + try { + await retryHelper.execute( + async () => { + throw new Error(`some error ${++attempts}`) + }, + () => false + ) + } catch (err) { + error = err + } + expect(error.message).toBe('some error 1') + expect(attempts).toBe(1) + expect(info).toHaveLength(0) + }) + + it('checks retryable after second attempt', async () => { + let attempts = 0 + let error: Error = (null as unknown) as Error + try { + await retryHelper.execute( + async () => { + throw new Error(`some error ${++attempts}`) + }, + (e: Error) => e.message === 'some error 1' + ) + } catch (err) { + error = err + } + expect(error.message).toBe('some error 2') + expect(attempts).toBe(2) + expect(info).toHaveLength(2) + expect(info[0]).toBe('some error 1') + expect(info[1]).toMatch(/Waiting .+ seconds before trying again/) + }) }) diff --git a/packages/tool-cache/__tests__/tool-cache.test.ts b/packages/tool-cache/__tests__/tool-cache.test.ts index 7888fa2adc..42a3e4eafc 100644 --- a/packages/tool-cache/__tests__/tool-cache.test.ts +++ b/packages/tool-cache/__tests__/tool-cache.test.ts @@ -619,6 +619,39 @@ describe('@actions/tool-cache', function() { expect(err.toString()).toContain('502') } }) + + it('retries 429s', async function() { + nock('http://example.com') + .get('/too-many-requests-429') + .times(2) + .reply(429, undefined) + nock('http://example.com') + .get('/too-many-requests-429') + .reply(500, undefined) + + try { + const statusCodeUrl = 'http://example.com/too-many-requests-429' + await tc.downloadTool(statusCodeUrl) + } catch (err) { + expect(err.toString()).toContain('500') + } + }) + + it("doesn't retry 404", async function() { + nock('http://example.com') + .get('/not-found-404') + .reply(404, undefined) + nock('http://example.com') + .get('/not-found-404') + .reply(500, undefined) + + try { + const statusCodeUrl = 'http://example.com/not-found-404' + await tc.downloadTool(statusCodeUrl) + } catch (err) { + expect(err.toString()).toContain('404') + } + }) }) /** diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index 7d2bf1e1b3..585e403818 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.3.2", + "version": "1.3.3", "description": "Actions tool-cache lib", "keywords": [ "github", diff --git a/packages/tool-cache/src/retry-helper.ts b/packages/tool-cache/src/retry-helper.ts index 36bde0bfed..16f8bb7e96 100644 --- a/packages/tool-cache/src/retry-helper.ts +++ b/packages/tool-cache/src/retry-helper.ts @@ -21,13 +21,20 @@ export class RetryHelper { } } - async execute(action: () => Promise): Promise { + async execute( + action: () => Promise, + isRetryable?: (e: Error) => boolean + ): Promise { let attempt = 1 while (attempt < this.maxAttempts) { // Try try { return await action() } catch (err) { + if (isRetryable && !isRetryable(err)) { + throw err + } + core.info(err.message) } diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index 67cee42854..ea2b90361d 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -23,30 +23,6 @@ export class HTTPError extends Error { const IS_WINDOWS = process.platform === 'win32' const userAgent = 'actions/tool-cache' -// On load grab temp directory and cache directory and remove them from env (currently don't want to expose this) -let tempDirectory: string = process.env['RUNNER_TEMP'] || '' -let cacheRoot: string = process.env['RUNNER_TOOL_CACHE'] || '' -// If directories not found, place them in common temp locations -if (!tempDirectory || !cacheRoot) { - let baseLocation: string - if (IS_WINDOWS) { - // On windows use the USERPROFILE env variable - baseLocation = process.env['USERPROFILE'] || 'C:\\' - } else { - if (process.platform === 'darwin') { - baseLocation = '/Users' - } else { - baseLocation = '/home' - } - } - if (!tempDirectory) { - tempDirectory = path.join(baseLocation, 'actions', 'temp') - } - if (!cacheRoot) { - cacheRoot = path.join(baseLocation, 'actions', 'cache') - } -} - /** * Download a tool from an url and stream it into a file * @@ -58,23 +34,40 @@ export async function downloadTool( url: string, dest?: string ): Promise { - dest = dest || path.join(tempDirectory, uuidV4()) + dest = dest || path.join(_getTempDirectory(), uuidV4()) await io.mkdirP(path.dirname(dest)) core.debug(`Downloading ${url}`) core.debug(`Destination ${dest}`) const maxAttempts = 3 - const minSeconds = getGlobal( + const minSeconds = _getGlobal( 'TEST_DOWNLOAD_TOOL_RETRY_MIN_SECONDS', 10 ) - const maxSeconds = getGlobal( + const maxSeconds = _getGlobal( 'TEST_DOWNLOAD_TOOL_RETRY_MAX_SECONDS', 20 ) const retryHelper = new RetryHelper(maxAttempts, minSeconds, maxSeconds) return await retryHelper.execute( - async () => await downloadToolAttempt(url, dest || '') + async () => { + return await downloadToolAttempt(url, dest || '') + }, + (err: Error) => { + if (err instanceof HTTPError && err.httpStatusCode) { + // Don't retry anything less than 500, except 408 Request Timeout and 429 Too Many Requests + if ( + err.httpStatusCode < 500 && + err.httpStatusCode !== 408 && + err.httpStatusCode !== 429 + ) { + return false + } + } + + // Otherwise retry + return true + } ) } @@ -98,7 +91,7 @@ async function downloadToolAttempt(url: string, dest: string): Promise { // Download the response body const pipeline = util.promisify(stream.pipeline) - const responseMessageFactory = getGlobal<() => stream.Readable>( + const responseMessageFactory = _getGlobal<() => stream.Readable>( 'TEST_DOWNLOAD_TOOL_RESPONSE_MESSAGE_FACTORY', () => response.message ) @@ -417,7 +410,12 @@ export function find( let toolPath = '' if (versionSpec) { versionSpec = semver.clean(versionSpec) || '' - const cachePath = path.join(cacheRoot, toolName, versionSpec, arch) + const cachePath = path.join( + _getCacheDirectory(), + toolName, + versionSpec, + arch + ) core.debug(`checking cache: ${cachePath}`) if (fs.existsSync(cachePath) && fs.existsSync(`${cachePath}.complete`)) { core.debug(`Found tool in cache ${toolName} ${versionSpec} ${arch}`) @@ -439,7 +437,7 @@ export function findAllVersions(toolName: string, arch?: string): string[] { const versions: string[] = [] arch = arch || os.arch() - const toolPath = path.join(cacheRoot, toolName) + const toolPath = path.join(_getCacheDirectory(), toolName) if (fs.existsSync(toolPath)) { const children: string[] = fs.readdirSync(toolPath) @@ -459,7 +457,7 @@ export function findAllVersions(toolName: string, arch?: string): string[] { async function _createExtractFolder(dest?: string): Promise { if (!dest) { // create a temp dir - dest = path.join(tempDirectory, uuidV4()) + dest = path.join(_getTempDirectory(), uuidV4()) } await io.mkdirP(dest) return dest @@ -471,7 +469,7 @@ async function _createToolPath( arch?: string ): Promise { const folderPath = path.join( - cacheRoot, + _getCacheDirectory(), tool, semver.clean(version) || version, arch || '' @@ -486,7 +484,7 @@ async function _createToolPath( function _completeToolPath(tool: string, version: string, arch?: string): void { const folderPath = path.join( - cacheRoot, + _getCacheDirectory(), tool, semver.clean(version) || version, arch || '' @@ -533,10 +531,28 @@ function _evaluateVersions(versions: string[], versionSpec: string): string { return version } +/** + * Gets RUNNER_TOOL_CACHE + */ +function _getCacheDirectory(): string { + const cacheDirectory = process.env['RUNNER_TOOL_CACHE'] || '' + ok(cacheDirectory, 'Expected RUNNER_TOOL_CACHE to be defined') + return cacheDirectory +} + +/** + * Gets RUNNER_TEMP + */ +function _getTempDirectory(): string { + const tempDirectory = process.env['RUNNER_TEMP'] || '' + ok(tempDirectory, 'Expected RUNNER_TEMP to be defined') + return tempDirectory +} + /** * Gets a global variable */ -function getGlobal(key: string, defaultValue: T): T { +function _getGlobal(key: string, defaultValue: T): T { /* eslint-disable @typescript-eslint/no-explicit-any */ const value = (global as any)[key] as T | undefined /* eslint-enable @typescript-eslint/no-explicit-any */ From dffb5572a9fe908cd0fd537646bb0f5f23d21167 Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Thu, 12 Mar 2020 14:50:27 +0100 Subject: [PATCH 103/192] Updates to @actions/artifact package (#367) * GZip implementation * Optimizations and cleanup * Update tests * More test updates * Update packages/artifact/src/internal-utils.ts Co-Authored-By: Josh Gross * Clarification around Upload Paths * Refactor to make http clients classes * GZip fixes * Documentation around compression * More detailed status information during large uploads * Pretty format * Percentage updates without rounding * Fix edge cases with formatting numbers * Update packages/artifact/src/internal-utils.ts Co-Authored-By: Josh Gross * Cleanup * Small reorg with status reporter * PR Feedback * Cleanup + Simplification * Test Cleanup * Mock updates * More cleanup * Format fixes * Overhaul to the http-manager * Fix tests * Promisify stats * Documentation around implementation * Improvements to documentation * PR Feedback * Remove Downloading multiple artifacts concurrently Co-authored-by: Josh Gross --- packages/artifact/README.md | 4 +- packages/artifact/RELEASES.md | 9 +- .../__tests__/download-specification.test.ts | 4 +- packages/artifact/__tests__/download.test.ts | 20 +- .../__tests__/upload-specification.test.ts | 2 +- packages/artifact/__tests__/upload.test.ts | 33 +- packages/artifact/__tests__/util.test.ts | 52 +- .../{ => docs}/additional-information.md | 8 + .../artifact/docs/implementation-details.md | 43 ++ packages/artifact/package-lock.json | 106 ++++ packages/artifact/package.json | 5 +- packages/artifact/src/artifact-client.ts | 15 +- .../src/internal-download-http-client.ts | 133 ----- .../src/internal-upload-http-client.ts | 322 ------------ .../__mocks__/config-variables.ts} | 13 + .../artifact-client.ts} | 135 +++-- .../config-variables.ts} | 17 +- .../contracts.ts} | 3 +- .../src/internal/download-http-client.ts | 189 +++++++ .../download-options.ts} | 0 .../download-response.ts} | 0 .../download-specification.ts} | 2 +- .../artifact/src/internal/http-manager.ts | 33 ++ packages/artifact/src/internal/upload-gzip.ts | 53 ++ .../src/internal/upload-http-client.ts | 465 ++++++++++++++++++ .../upload-options.ts} | 0 .../upload-response.ts} | 0 .../upload-specification.ts} | 9 +- .../src/internal/upload-status-reporter.ts | 90 ++++ .../{internal-utils.ts => internal/utils.ts} | 67 ++- 30 files changed, 1255 insertions(+), 577 deletions(-) rename packages/artifact/{ => docs}/additional-information.md (70%) create mode 100644 packages/artifact/docs/implementation-details.md delete mode 100644 packages/artifact/src/internal-download-http-client.ts delete mode 100644 packages/artifact/src/internal-upload-http-client.ts rename packages/artifact/src/{__mocks__/internal-config-variables.ts => internal/__mocks__/config-variables.ts} (78%) rename packages/artifact/src/{internal-artifact-client.ts => internal/artifact-client.ts} (63%) rename packages/artifact/src/{internal-config-variables.ts => internal/config-variables.ts} (82%) rename packages/artifact/src/{internal-contracts.ts => internal/contracts.ts} (96%) create mode 100644 packages/artifact/src/internal/download-http-client.ts rename packages/artifact/src/{internal-download-options.ts => internal/download-options.ts} (100%) rename packages/artifact/src/{internal-download-response.ts => internal/download-response.ts} (100%) rename packages/artifact/src/{internal-download-specification.ts => internal/download-specification.ts} (98%) create mode 100644 packages/artifact/src/internal/http-manager.ts create mode 100644 packages/artifact/src/internal/upload-gzip.ts create mode 100644 packages/artifact/src/internal/upload-http-client.ts rename packages/artifact/src/{internal-upload-options.ts => internal/upload-options.ts} (100%) rename packages/artifact/src/{internal-upload-response.ts => internal/upload-response.ts} (100%) rename packages/artifact/src/{internal-upload-specification.ts => internal/upload-specification.ts} (91%) create mode 100644 packages/artifact/src/internal/upload-status-reporter.ts rename packages/artifact/src/{internal-utils.ts => internal/utils.ts} (59%) diff --git a/packages/artifact/README.md b/packages/artifact/README.md index 3a53c170b0..f5ef597ec7 100644 --- a/packages/artifact/README.md +++ b/packages/artifact/README.md @@ -196,4 +196,6 @@ Each artifact will have the same `DownloadResponse` as if it was individually do ## Additional Documentation -Check out [additional-information](additional-information.md) for extra documentation. +Check out [additional-information](docs/additional-information.md) for extra documentation around usage, restrictions and behavior. + +Check out [implementation-details](docs/implementation-details.md) for extra information about the implementation of this package. diff --git a/packages/artifact/RELEASES.md b/packages/artifact/RELEASES.md index 9fd58d506d..2b7e17b6c0 100644 --- a/packages/artifact/RELEASES.md +++ b/packages/artifact/RELEASES.md @@ -2,4 +2,11 @@ ### 0.1.0 -- Initial release \ No newline at end of file +- Initial release + +### 0.1.1 + +- Fixes to TCP connections not closing +- GZip file compression to speed up downloads +- Improved logging and output +- Extra documentation \ No newline at end of file diff --git a/packages/artifact/__tests__/download-specification.test.ts b/packages/artifact/__tests__/download-specification.test.ts index ac1b5f9371..50ca4648ae 100644 --- a/packages/artifact/__tests__/download-specification.test.ts +++ b/packages/artifact/__tests__/download-specification.test.ts @@ -1,8 +1,8 @@ import * as path from 'path' import * as core from '@actions/core' import {URL} from 'url' -import {getDownloadSpecification} from '../src/internal-download-specification' -import {ContainerEntry} from '../src/internal-contracts' +import {getDownloadSpecification} from '../src/internal/download-specification' +import {ContainerEntry} from '../src/internal/contracts' const artifact1Name = 'my-artifact' const artifact2Name = 'my-artifact-extra' diff --git a/packages/artifact/__tests__/download.test.ts b/packages/artifact/__tests__/download.test.ts index d10c61e545..3da3cad4ec 100644 --- a/packages/artifact/__tests__/download.test.ts +++ b/packages/artifact/__tests__/download.test.ts @@ -3,17 +3,17 @@ import * as http from 'http' import * as io from '../../io/src/io' import * as net from 'net' import * as path from 'path' -import * as configVariables from '../src/internal-config-variables' +import * as configVariables from '../src/internal/config-variables' import {HttpClient, HttpClientResponse} from '@actions/http-client' -import * as downloadClient from '../src/internal-download-http-client' +import {DownloadHttpClient} from '../src/internal/download-http-client' import { ListArtifactsResponse, QueryArtifactResponse -} from '../src/internal-contracts' +} from '../src/internal/contracts' const root = path.join(__dirname, '_temp', 'artifact-download') -jest.mock('../src/internal-config-variables') +jest.mock('../src/internal/config-variables') jest.mock('@actions/http-client') describe('Download Tests', () => { @@ -32,7 +32,8 @@ describe('Download Tests', () => { */ it('List Artifacts - Success', async () => { setupSuccessfulListArtifactsResponse() - const artifacts = await downloadClient.listArtifacts() + const downloadHttpClient = new DownloadHttpClient() + const artifacts = await downloadHttpClient.listArtifacts() expect(artifacts.count).toEqual(2) const artifactNames = artifacts.value.map(item => item.name) @@ -58,7 +59,8 @@ describe('Download Tests', () => { it('List Artifacts - Failure', async () => { setupFailedResponse() - expect(downloadClient.listArtifacts()).rejects.toThrow( + const downloadHttpClient = new DownloadHttpClient() + expect(downloadHttpClient.listArtifacts()).rejects.toThrow( 'Unable to list artifacts for the run' ) }) @@ -68,7 +70,8 @@ describe('Download Tests', () => { */ it('Container Items - Success', async () => { setupSuccessfulContainerItemsResponse() - const response = await downloadClient.getContainerItems( + const downloadHttpClient = new DownloadHttpClient() + const response = await downloadHttpClient.getContainerItems( 'artifact-name', configVariables.getRuntimeUrl() ) @@ -93,8 +96,9 @@ describe('Download Tests', () => { it('Container Items - Failure', async () => { setupFailedResponse() + const downloadHttpClient = new DownloadHttpClient() expect( - downloadClient.getContainerItems( + downloadHttpClient.getContainerItems( 'artifact-name', configVariables.getRuntimeUrl() ) diff --git a/packages/artifact/__tests__/upload-specification.test.ts b/packages/artifact/__tests__/upload-specification.test.ts index ba1cd9e435..a270c7c67a 100644 --- a/packages/artifact/__tests__/upload-specification.test.ts +++ b/packages/artifact/__tests__/upload-specification.test.ts @@ -2,7 +2,7 @@ import * as io from '../../io/src/io' import * as path from 'path' import {promises as fs} from 'fs' import * as core from '@actions/core' -import {getUploadSpecification} from '../src/internal-upload-specification' +import {getUploadSpecification} from '../src/internal/upload-specification' const artifactName = 'my-artifact' const root = path.join(__dirname, '_temp', 'upload-specification') diff --git a/packages/artifact/__tests__/upload.test.ts b/packages/artifact/__tests__/upload.test.ts index 08a758ca4e..502c20ead8 100644 --- a/packages/artifact/__tests__/upload.test.ts +++ b/packages/artifact/__tests__/upload.test.ts @@ -2,16 +2,16 @@ import * as http from 'http' import * as io from '../../io/src/io' import * as net from 'net' import * as path from 'path' -import * as uploadHttpClient from '../src/internal-upload-http-client' +import {UploadHttpClient} from '../src/internal/upload-http-client' import * as core from '@actions/core' import {promises as fs} from 'fs' -import {getRuntimeUrl} from '../src/internal-config-variables' +import {getRuntimeUrl} from '../src/internal/config-variables' import {HttpClient, HttpClientResponse} from '@actions/http-client' import { ArtifactResponse, PatchArtifactSizeSuccessResponse -} from '../src/internal-contracts' -import {UploadSpecification} from '../src/internal-upload-specification' +} from '../src/internal/contracts' +import {UploadSpecification} from '../src/internal/upload-specification' const root = path.join(__dirname, '_temp', 'artifact-upload') const file1Path = path.join(root, 'file1.txt') @@ -26,7 +26,7 @@ let file3Size = 0 let file4Size = 0 let file5Size = 0 -jest.mock('../src/internal-config-variables') +jest.mock('../src/internal/config-variables') jest.mock('@actions/http-client') describe('Upload Tests', () => { @@ -75,6 +75,7 @@ describe('Upload Tests', () => { */ it('Create Artifact - Success', async () => { const artifactName = 'valid-artifact-name' + const uploadHttpClient = new UploadHttpClient() const response = await uploadHttpClient.createArtifactInFileContainer( artifactName ) @@ -93,6 +94,7 @@ describe('Upload Tests', () => { it('Create Artifact - Failure', async () => { const artifactName = 'invalid-artifact-name' + const uploadHttpClient = new UploadHttpClient() expect( uploadHttpClient.createArtifactInFileContainer(artifactName) ).rejects.toEqual( @@ -137,12 +139,13 @@ describe('Upload Tests', () => { const expectedTotalSize = file1Size + file2Size + file3Size + file4Size + file5Size const uploadUrl = `${getRuntimeUrl()}_apis/resources/Containers/13` + const uploadHttpClient = new UploadHttpClient() const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer( uploadUrl, uploadSpecification ) expect(uploadResult.failedItems.length).toEqual(0) - expect(uploadResult.size).toEqual(expectedTotalSize) + expect(uploadResult.uploadSize).toEqual(expectedTotalSize) }) it('Upload Artifact - Failed Single File Upload', async () => { @@ -154,12 +157,13 @@ describe('Upload Tests', () => { ] const uploadUrl = `${getRuntimeUrl()}_apis/resources/Containers/13` + const uploadHttpClient = new UploadHttpClient() const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer( uploadUrl, uploadSpecification ) expect(uploadResult.failedItems.length).toEqual(1) - expect(uploadResult.size).toEqual(0) + expect(uploadResult.uploadSize).toEqual(0) }) it('Upload Artifact - Partial Upload Continue On Error', async () => { @@ -189,13 +193,14 @@ describe('Upload Tests', () => { const expectedPartialSize = file1Size + file2Size + file4Size + file5Size const uploadUrl = `${getRuntimeUrl()}_apis/resources/Containers/13` + const uploadHttpClient = new UploadHttpClient() const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer( uploadUrl, uploadSpecification, {continueOnError: true} ) expect(uploadResult.failedItems.length).toEqual(1) - expect(uploadResult.size).toEqual(expectedPartialSize) + expect(uploadResult.uploadSize).toEqual(expectedPartialSize) }) it('Upload Artifact - Partial Upload Fail Fast', async () => { @@ -225,13 +230,14 @@ describe('Upload Tests', () => { const expectedPartialSize = file1Size + file2Size + file3Size const uploadUrl = `${getRuntimeUrl()}_apis/resources/Containers/13` + const uploadHttpClient = new UploadHttpClient() const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer( uploadUrl, uploadSpecification, {continueOnError: false} ) expect(uploadResult.failedItems.length).toEqual(2) - expect(uploadResult.size).toEqual(expectedPartialSize) + expect(uploadResult.uploadSize).toEqual(expectedPartialSize) }) it('Upload Artifact - Failed upload with no options', async () => { @@ -261,12 +267,13 @@ describe('Upload Tests', () => { const expectedPartialSize = file1Size + file2Size + file3Size + file5Size const uploadUrl = `${getRuntimeUrl()}_apis/resources/Containers/13` + const uploadHttpClient = new UploadHttpClient() const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer( uploadUrl, uploadSpecification ) expect(uploadResult.failedItems.length).toEqual(1) - expect(uploadResult.size).toEqual(expectedPartialSize) + expect(uploadResult.uploadSize).toEqual(expectedPartialSize) }) it('Upload Artifact - Failed upload with empty options', async () => { @@ -296,25 +303,28 @@ describe('Upload Tests', () => { const expectedPartialSize = file1Size + file2Size + file3Size + file5Size const uploadUrl = `${getRuntimeUrl()}_apis/resources/Containers/13` + const uploadHttpClient = new UploadHttpClient() const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer( uploadUrl, uploadSpecification, {} ) expect(uploadResult.failedItems.length).toEqual(1) - expect(uploadResult.size).toEqual(expectedPartialSize) + expect(uploadResult.uploadSize).toEqual(expectedPartialSize) }) /** * Artifact Association Tests */ it('Associate Artifact - Success', async () => { + const uploadHttpClient = new UploadHttpClient() expect(async () => { uploadHttpClient.patchArtifactSize(130, 'my-artifact') }).not.toThrow() }) it('Associate Artifact - Not Found', async () => { + const uploadHttpClient = new UploadHttpClient() expect( uploadHttpClient.patchArtifactSize(100, 'non-existent-artifact') ).rejects.toThrow( @@ -323,6 +333,7 @@ describe('Upload Tests', () => { }) it('Associate Artifact - Error', async () => { + const uploadHttpClient = new UploadHttpClient() expect( uploadHttpClient.patchArtifactSize(-2, 'my-artifact') ).rejects.toThrow('Unable to finish uploading artifact my-artifact') diff --git a/packages/artifact/__tests__/util.test.ts b/packages/artifact/__tests__/util.test.ts index ffbe62bf0b..c1fcca4043 100644 --- a/packages/artifact/__tests__/util.test.ts +++ b/packages/artifact/__tests__/util.test.ts @@ -1,12 +1,12 @@ import * as fs from 'fs' import * as io from '../../io/src/io' import * as path from 'path' -import * as utils from '../src/internal-utils' +import * as utils from '../src/internal/utils' import * as core from '@actions/core' import {HttpCodes} from '@actions/http-client' -import {getRuntimeUrl, getWorkFlowRunId} from '../src/internal-config-variables' +import {getRuntimeUrl, getWorkFlowRunId} from '../src/internal/config-variables' -jest.mock('../src/internal-config-variables') +jest.mock('../src/internal/config-variables') describe('Utils', () => { beforeAll(() => { @@ -49,6 +49,36 @@ describe('Utils', () => { } }) + it('Check Artifact File Path for any invalid characters', () => { + const invalidNames = [ + 'some/invalid"artifact/path', + 'some/invalid:artifact/path', + 'some/invalidartifact/path', + 'some/invalid|artifact/path', + 'some/invalid*artifact/path', + 'some/invalid?artifact/path', + 'some/invalid artifact/path', + '' + ] + for (const invalidName of invalidNames) { + expect(() => { + utils.checkArtifactFilePath(invalidName) + }).toThrow() + } + + const validNames = [ + 'my/perfectly-normal/artifact-path', + 'my/perfectly\\Normal/Artifact-path', + 'm¥/ñðrmål/Är†ï£å¢†' + ] + for (const validName of validNames) { + expect(() => { + utils.checkArtifactFilePath(validName) + }).not.toThrow() + } + }) + it('Test constructing artifact URL', () => { const runtimeUrl = getRuntimeUrl() const runId = getWorkFlowRunId() @@ -61,13 +91,25 @@ describe('Utils', () => { it('Test constructing headers with all optional parameters', () => { const type = 'application/json' const size = 24 + const uncompressedLength = 100 const range = 'bytes 0-199/200' - const options = utils.getRequestOptions(type, size, range) - expect(Object.keys(options).length).toEqual(4) + const options = utils.getRequestOptions( + type, + true, + true, + uncompressedLength, + size, + range + ) + expect(Object.keys(options).length).toEqual(8) expect(options['Accept']).toEqual( `${type};api-version=${utils.getApiVersion()}` ) expect(options['Content-Type']).toEqual(type) + expect(options['Connection']).toEqual('Keep-Alive') + expect(options['Keep-Alive']).toEqual('10') + expect(options['Content-Encoding']).toEqual('gzip') + expect(options['x-tfs-filelength']).toEqual(uncompressedLength) expect(options['Content-Length']).toEqual(size) expect(options['Content-Range']).toEqual(range) }) diff --git a/packages/artifact/additional-information.md b/packages/artifact/docs/additional-information.md similarity index 70% rename from packages/artifact/additional-information.md rename to packages/artifact/docs/additional-information.md index 1f7356265b..0118a0dcb4 100644 --- a/packages/artifact/additional-information.md +++ b/packages/artifact/docs/additional-information.md @@ -43,3 +43,11 @@ const uploadResult = await artifactClient.uploadArtifact(artifactName, files, ro During upload, each file is uploaded concurrently in 4MB chunks using a separate HTTPS connection per file. Chunked uploads are used so that in the event of a failure (which is entirely possible because the internet is not perfect), the upload can be retried. If there is an error, a retry will be attempted after a certain period of time. Uploading will be generally be faster if there are fewer files that are larger in size vs if there are lots of smaller files. Depending on the types and quantities of files being uploaded, it might be beneficial to separately compress and archive everything into a single archive (using something like `tar` or `zip`) before starting and artifact upload to speed things up. + +## Is my artifact compressed? + +GZip is used internally to compress individual files before starting an upload. Compression helps reduce the total amount of data that must be uploaded and stored while helping to speed up uploads (this performance benefit is significant especially on self hosted runners). If GZip does not reduce the size of the file that is being uploaded, the original file is uploaded as-is. + +Compression using GZip also helps speed up artifact download as part of a workflow. Header information is used to determine if an individual file was uploaded using GZip and if necessary, decompression is used. + +When downloading an artifact from the GitHub UI (this differs from downloading an artifact during a workflow), a single Zip file is dynamically created that contains all of the files uploaded as part of an artifact. Any files that were uploaded using GZip will be decompressed on the server before being added to the Zip file with the remaining files. \ No newline at end of file diff --git a/packages/artifact/docs/implementation-details.md b/packages/artifact/docs/implementation-details.md new file mode 100644 index 0000000000..b30eaff421 --- /dev/null +++ b/packages/artifact/docs/implementation-details.md @@ -0,0 +1,43 @@ +# Implementation Details + +## Proxy support + +This package uses the `@actions/http-client` NPM package internally which supports proxied requests out of the box. + +## HttpManager + +### `keep-alive` header + +When an HTTP call is made to upload or download an individual file, the server will close the HTTP connection after the upload/download is complete and respond with a header indicating `Connection: close`. + +[HTTP closed connection header information](https://tools.ietf.org/html/rfc2616#section-14.10) + +TCP connections are sometimes not immediately closed by the node client (Windows might hold on to the port for an extra period of time before actually releasing it for example) and a large amount of closed connections can cause port exhaustion before ports get released and are available again. + +VMs hosted by GitHub Actions have 1024 available ports so uploading 1000+ files very quickly can cause port exhaustion if connections get closed immediately. This can start to cause strange undefined behavior and timeouts. + +In order for connections to not close immediately, the `keep-alive` header is used to indicate to the server that the connection should stay open. If a `keep-alive` header is used, the connection needs to be disposed of by calling `dispose()` in the `HttpClient`. + +[`keep-alive` header information](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive) +[@actions/http-client client disposal](https://github.com/actions/http-client/blob/04e5ad73cd3fd1f5610a32116b0759eddf6570d2/index.ts#L292) + + +### Multiple HTTP clients + +During an artifact upload or download, files are concurrently uploaded or downloaded using `async/await`. When an error or retry is encountered, the `HttpClient` that made a call is disposed of and a new one is created. If a single `HttpClient` was used for all HTTP calls and it had to be disposed, it could inadvertently effect any other calls that could be concurrently happening. + +Any other concurrent uploads or downloads should be left untouched. Because of this, each concurrent upload or download gets its own `HttpClient`. The `http-manager` is used to manage all available clients and each concurrent upload or download maintains a `httpClientIndex` that keep track of which client should be used (and potentially disposed and recycled if necessary) + +### Potential resource leaks + +When an HTTP response is received, it consists of two parts +- `message` +- `body` + +The `message` contains information such as the response code and header information and it is available immediately. The body however is not available immediately and it can be read by calling `await response.readBody()`. + +TCP connections consist of an input and output buffer to manage what is sent and received across a connection. If the body is not read (even if its contents are not needed) the buffers can stay in use even after `dispose()` gets called on the `HttpClient`. The buffers get released automatically after a certain period of time, but in order for them to be explicitly cleared, `readBody()` is always called. + +### Non Concurrent calls + +Both `upload-http-client` and `download-http-client` do not instantiate or create any HTTP clients (the `HttpManager` has that responsibility). If an HTTP call has to be made that does not require the `keep-alive` header (such as when calling `listArtifacts` or `patchArtifactSize`), the first `HttpClient` in the `HttpManager` is used. The number of available clients is equal to the upload or download concurrency and there will always be at least one available. \ No newline at end of file diff --git a/packages/artifact/package-lock.json b/packages/artifact/package-lock.json index d4a64a1e36..87c0b84426 100644 --- a/packages/artifact/package-lock.json +++ b/packages/artifact/package-lock.json @@ -12,10 +12,116 @@ "tunnel": "0.0.6" } }, + "@types/tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-6IwZ9HzWbCq6XoQWhxLpDjuADodH/MKXRUIDFudvgjcVdjFknvmR+DNsoUeer4XPrEnrZs04Jj+kfV9pFsrhmA==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "tmp": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz", + "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==", + "requires": { + "rimraf": "^2.6.3" + } + }, + "tmp-promise": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-2.0.2.tgz", + "integrity": "sha512-zl71nFWjPKW2KXs+73gEk8RmqvtAeXPxhWDkTUoa3MSMkjq3I+9OeknjF178MQoMYsdqL730hfzvNfEkePxq9Q==", + "requires": { + "tmp": "0.1.0" + } + }, "tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" } } } diff --git a/packages/artifact/package.json b/packages/artifact/package.json index fab71fd1d8..e5b16450ab 100644 --- a/packages/artifact/package.json +++ b/packages/artifact/package.json @@ -37,6 +37,9 @@ }, "dependencies": { "@actions/core": "^1.2.1", - "@actions/http-client": "^1.0.6" + "@actions/http-client": "^1.0.6", + "@types/tmp": "^0.1.0", + "tmp": "^0.1.0", + "tmp-promise": "^2.0.2" } } diff --git a/packages/artifact/src/artifact-client.ts b/packages/artifact/src/artifact-client.ts index a7ddc69d9c..f6aa793c36 100644 --- a/packages/artifact/src/artifact-client.ts +++ b/packages/artifact/src/artifact-client.ts @@ -1,5 +1,16 @@ -import {ArtifactClient, DefaultArtifactClient} from './internal-artifact-client' -export {ArtifactClient} +import {UploadOptions} from './internal/upload-options' +import {UploadResponse} from './internal/upload-response' +import {DownloadOptions} from './internal/download-options' +import {DownloadResponse} from './internal/download-response' +import {ArtifactClient, DefaultArtifactClient} from './internal/artifact-client' + +export { + ArtifactClient, + UploadResponse, + UploadOptions, + DownloadResponse, + DownloadOptions +} /** * Constructs an ArtifactClient diff --git a/packages/artifact/src/internal-download-http-client.ts b/packages/artifact/src/internal-download-http-client.ts deleted file mode 100644 index bc0005a4c5..0000000000 --- a/packages/artifact/src/internal-download-http-client.ts +++ /dev/null @@ -1,133 +0,0 @@ -import * as fs from 'fs' -import { - createHttpClient, - getArtifactUrl, - getRequestOptions, - isSuccessStatusCode, - isRetryableStatusCode -} from './internal-utils' -import {URL} from 'url' -import { - ListArtifactsResponse, - QueryArtifactResponse -} from './internal-contracts' -import {IHttpClientResponse} from '@actions/http-client/interfaces' -import {HttpClient} from '@actions/http-client' -import {DownloadItem} from './internal-download-specification' -import {getDownloadFileConcurrency} from './internal-config-variables' -import {warning} from '@actions/core' - -/** - * Gets a list of all artifacts that are in a specific container - */ -export async function listArtifacts(): Promise { - const artifactUrl = getArtifactUrl() - const client = createHttpClient() - const requestOptions = getRequestOptions('application/json') - - const rawResponse = await client.get(artifactUrl, requestOptions) - const body: string = await rawResponse.readBody() - if (isSuccessStatusCode(rawResponse.message.statusCode) && body) { - return JSON.parse(body) - } - // eslint-disable-next-line no-console - console.log(rawResponse) - throw new Error(`Unable to list artifacts for the run`) -} - -/** - * Fetches a set of container items that describe the contents of an artifact - * @param artifactName the name of the artifact - * @param containerUrl the artifact container URL for the run - */ -export async function getContainerItems( - artifactName: string, - containerUrl: string -): Promise { - // The itemPath search parameter controls which containers will be returned - const resourceUrl = new URL(containerUrl) - resourceUrl.searchParams.append('itemPath', artifactName) - - const client = createHttpClient() - const rawResponse = await client.get(resourceUrl.toString()) - const body: string = await rawResponse.readBody() - if (isSuccessStatusCode(rawResponse.message.statusCode) && body) { - return JSON.parse(body) - } - // eslint-disable-next-line no-console - console.log(rawResponse) - throw new Error(`Unable to get ContainersItems from ${resourceUrl}`) -} - -/** - * Concurrently downloads all the files that are part of an artifact - * @param downloadItems information about what items to download and where to save them - */ -export async function downloadSingleArtifact( - downloadItems: DownloadItem[] -): Promise { - const DOWNLOAD_CONCURRENCY = getDownloadFileConcurrency() - // Limit the number of files downloaded at a single time - const parallelDownloads = [...new Array(DOWNLOAD_CONCURRENCY).keys()] - const client = createHttpClient() - let downloadedFiles = 0 - await Promise.all( - parallelDownloads.map(async () => { - while (downloadedFiles < downloadItems.length) { - const currentFileToDownload = downloadItems[downloadedFiles] - downloadedFiles += 1 - await downloadIndividualFile( - client, - currentFileToDownload.sourceLocation, - currentFileToDownload.targetPath - ) - } - }) - ) -} - -/** - * Downloads an individual file - * @param client http client that will be used to make the necessary calls - * @param artifactLocation origin location where a file will be downloaded from - * @param downloadPath destination location for the file being downloaded - */ -export async function downloadIndividualFile( - client: HttpClient, - artifactLocation: string, - downloadPath: string -): Promise { - const stream = fs.createWriteStream(downloadPath) - const response = await client.get(artifactLocation) - if (isSuccessStatusCode(response.message.statusCode)) { - await pipeResponseToStream(response, stream) - } else if (isRetryableStatusCode(response.message.statusCode)) { - warning( - `Received http ${response.message.statusCode} during file download, will retry ${artifactLocation} after 10 seconds` - ) - await new Promise(resolve => setTimeout(resolve, 10000)) - const retryResponse = await client.get(artifactLocation) - if (isSuccessStatusCode(retryResponse.message.statusCode)) { - await pipeResponseToStream(response, stream) - } else { - // eslint-disable-next-line no-console - console.log(retryResponse) - throw new Error(`Unable to download ${artifactLocation}`) - } - } else { - // eslint-disable-next-line no-console - console.log(response) - throw new Error(`Unable to download ${artifactLocation}`) - } -} - -export async function pipeResponseToStream( - response: IHttpClientResponse, - stream: NodeJS.WritableStream -): Promise { - return new Promise(resolve => { - response.message.pipe(stream).on('close', () => { - resolve() - }) - }) -} diff --git a/packages/artifact/src/internal-upload-http-client.ts b/packages/artifact/src/internal-upload-http-client.ts deleted file mode 100644 index 79646c16cf..0000000000 --- a/packages/artifact/src/internal-upload-http-client.ts +++ /dev/null @@ -1,322 +0,0 @@ -import {debug, warning, info} from '@actions/core' -import {HttpClientResponse, HttpClient} from '@actions/http-client/index' -import {IHttpClientResponse} from '@actions/http-client/interfaces' -import { - ArtifactResponse, - CreateArtifactParameters, - PatchArtifactSize, - UploadResults -} from './internal-contracts' -import * as fs from 'fs' -import {UploadSpecification} from './internal-upload-specification' -import {UploadOptions} from './internal-upload-options' -import {URL} from 'url' -import { - createHttpClient, - getArtifactUrl, - getContentRange, - getRequestOptions, - isRetryableStatusCode, - isSuccessStatusCode -} from './internal-utils' -import { - getUploadChunkConcurrency, - getUploadChunkSize, - getUploadFileConcurrency -} from './internal-config-variables' - -/** - * Creates a file container for the new artifact in the remote blob storage/file service - * @param {string} artifactName Name of the artifact being created - * @returns The response from the Artifact Service if the file container was successfully created - */ -export async function createArtifactInFileContainer( - artifactName: string -): Promise { - const parameters: CreateArtifactParameters = { - Type: 'actions_storage', - Name: artifactName - } - const data: string = JSON.stringify(parameters, null, 2) - const artifactUrl = getArtifactUrl() - const client = createHttpClient() - const requestOptions = getRequestOptions('application/json') - - const rawResponse = await client.post(artifactUrl, data, requestOptions) - const body: string = await rawResponse.readBody() - - if (isSuccessStatusCode(rawResponse.message.statusCode) && body) { - return JSON.parse(body) - } else { - // eslint-disable-next-line no-console - console.log(rawResponse) - throw new Error( - `Unable to create a container for the artifact ${artifactName}` - ) - } -} - -/** - * Concurrently upload all of the files in chunks - * @param {string} uploadUrl Base Url for the artifact that was created - * @param {SearchResult[]} filesToUpload A list of information about the files being uploaded - * @returns The size of all the files uploaded in bytes - */ -export async function uploadArtifactToFileContainer( - uploadUrl: string, - filesToUpload: UploadSpecification[], - options?: UploadOptions -): Promise { - const client = createHttpClient() - const FILE_CONCURRENCY = getUploadFileConcurrency() - const CHUNK_CONCURRENCY = getUploadChunkConcurrency() - const MAX_CHUNK_SIZE = getUploadChunkSize() - debug( - `File Concurrency: ${FILE_CONCURRENCY}, Chunk Concurrency: ${CHUNK_CONCURRENCY} and Chunk Size: ${MAX_CHUNK_SIZE}` - ) - - const parameters: UploadFileParameters[] = [] - - // by default, file uploads will continue if there is an error unless specified differently in the options - let continueOnError = true - if (options) { - if (options.continueOnError === false) { - continueOnError = false - } - } - - // Prepare the necessary parameters to upload all the files - for (const file of filesToUpload) { - const resourceUrl = new URL(uploadUrl) - resourceUrl.searchParams.append('itemPath', file.uploadFilePath) - parameters.push({ - file: file.absoluteFilePath, - resourceUrl: resourceUrl.toString(), - restClient: client, - concurrency: CHUNK_CONCURRENCY, - maxChunkSize: MAX_CHUNK_SIZE, - continueOnError - }) - } - - const parallelUploads = [...new Array(FILE_CONCURRENCY).keys()] - const failedItemsToReport: string[] = [] - let uploadedFiles = 0 - let fileSizes = 0 - let abortPendingFileUploads = false - - // Only allow a certain amount of files to be uploaded at once, this is done to reduce potential errors - await Promise.all( - parallelUploads.map(async () => { - while (uploadedFiles < filesToUpload.length) { - const currentFileParameters = parameters[uploadedFiles] - uploadedFiles += 1 - if (abortPendingFileUploads) { - failedItemsToReport.push(currentFileParameters.file) - continue - } - - const uploadFileResult = await uploadFileAsync(currentFileParameters) - fileSizes += uploadFileResult.successfulUploadSize - if (uploadFileResult.isSuccess === false) { - failedItemsToReport.push(currentFileParameters.file) - if (!continueOnError) { - // Existing uploads will be able to finish however all pending uploads will fail fast - abortPendingFileUploads = true - } - } - } - }) - ) - - info(`Total size of all the files uploaded is ${fileSizes} bytes`) - return { - size: fileSizes, - failedItems: failedItemsToReport - } -} - -/** - * Asynchronously uploads a file. If the file is bigger than the max chunk size it will be uploaded via multiple calls - * @param {UploadFileParameters} parameters Information about the file that needs to be uploaded - * @returns The size of the file that was uploaded in bytes along with any failed uploads - */ -async function uploadFileAsync( - parameters: UploadFileParameters -): Promise { - const fileSize: number = fs.statSync(parameters.file).size - const parallelUploads = [...new Array(parameters.concurrency).keys()] - let offset = 0 - let isUploadSuccessful = true - let failedChunkSizes = 0 - let abortFileUpload = false - - await Promise.all( - parallelUploads.map(async () => { - while (offset < fileSize) { - const chunkSize = Math.min(fileSize - offset, parameters.maxChunkSize) - if (abortFileUpload) { - // if we don't want to continue on error, any pending upload chunk will be marked as failed - failedChunkSizes += chunkSize - continue - } - - const start = offset - const end = offset + chunkSize - 1 - offset += parameters.maxChunkSize - const chunk: NodeJS.ReadableStream = fs.createReadStream( - parameters.file, - { - start, - end, - autoClose: false - } - ) - - const result = await uploadChunk( - parameters.restClient, - parameters.resourceUrl, - chunk, - start, - end, - fileSize - ) - - if (!result) { - /** - * Chunk failed to upload, report as failed and do not continue uploading any more chunks for the file. It is possible that part of a chunk was - * successfully uploaded so the server may report a different size for what was uploaded - **/ - isUploadSuccessful = false - failedChunkSizes += chunkSize - warning(`Aborting upload for ${parameters.file} due to failure`) - abortFileUpload = true - } - } - }) - ) - return { - isSuccess: isUploadSuccessful, - successfulUploadSize: fileSize - failedChunkSizes - } -} - -/** - * Uploads a chunk of an individual file to the specified resourceUrl. If the upload fails and the status code - * indicates a retryable status, we try to upload the chunk as well - * @param {HttpClient} restClient RestClient that will be making the appropriate HTTP call - * @param {string} resourceUrl Url of the resource that the chunk will be uploaded to - * @param {NodeJS.ReadableStream} data Stream of the file that will be uploaded - * @param {number} start Starting byte index of file that the chunk belongs to - * @param {number} end Ending byte index of file that the chunk belongs to - * @param {number} totalSize Total size of the file in bytes that is being uploaded - * @returns if the chunk was successfully uploaded - */ -async function uploadChunk( - restClient: HttpClient, - resourceUrl: string, - data: NodeJS.ReadableStream, - start: number, - end: number, - totalSize: number -): Promise { - info( - `Uploading chunk of size ${end - - start + - 1} bytes at offset ${start} with content range: ${getContentRange( - start, - end, - totalSize - )}` - ) - - const requestOptions = getRequestOptions( - 'application/octet-stream', - totalSize, - getContentRange(start, end, totalSize) - ) - - const uploadChunkRequest = async (): Promise => { - return await restClient.sendStream('PUT', resourceUrl, data, requestOptions) - } - - const response = await uploadChunkRequest() - if (isSuccessStatusCode(response.message.statusCode)) { - debug( - `Chunk for ${start}:${end} was successfully uploaded to ${resourceUrl}` - ) - return true - } else if (isRetryableStatusCode(response.message.statusCode)) { - info( - `Received http ${response.message.statusCode} during chunk upload, will retry at offset ${start} after 10 seconds.` - ) - await new Promise(resolve => setTimeout(resolve, 10000)) - const retryResponse = await uploadChunkRequest() - if (isSuccessStatusCode(retryResponse.message.statusCode)) { - return true - } else { - info(`Unable to upload chunk even after retrying`) - // eslint-disable-next-line no-console - console.log(response) - return false - } - } - - // Upload must have failed spectacularly somehow, log full result for diagnostic purposes - // eslint-disable-next-line no-console - console.log(response) - return false -} - -/** - * Updates the size of the artifact from -1 which was initially set when the container was first created for the artifact. - * Updating the size indicates that we are done uploading all the contents of the artifact. A server side check will be run - * to check that the artifact size is correct for billing purposes - */ -export async function patchArtifactSize( - size: number, - artifactName: string -): Promise { - const client = createHttpClient() - const requestOptions = getRequestOptions('application/json') - const resourceUrl = new URL(getArtifactUrl()) - resourceUrl.searchParams.append('artifactName', artifactName) - - const parameters: PatchArtifactSize = {Size: size} - const data: string = JSON.stringify(parameters, null, 2) - debug(`URL is ${resourceUrl.toString()}`) - - const rawResponse: HttpClientResponse = await client.patch( - resourceUrl.toString(), - data, - requestOptions - ) - const body: string = await rawResponse.readBody() - - if (isSuccessStatusCode(rawResponse.message.statusCode)) { - debug( - `Artifact ${artifactName} has been successfully uploaded, total size ${size}` - ) - debug(body) - } else if (rawResponse.message.statusCode === 404) { - throw new Error(`An Artifact with the name ${artifactName} was not found`) - } else { - // eslint-disable-next-line no-console - console.log(body) - throw new Error(`Unable to finish uploading artifact ${artifactName}`) - } -} - -interface UploadFileParameters { - file: string - resourceUrl: string - restClient: HttpClient - concurrency: number - maxChunkSize: number - continueOnError: boolean -} - -interface UploadFileResult { - isSuccess: boolean - successfulUploadSize: number -} diff --git a/packages/artifact/src/__mocks__/internal-config-variables.ts b/packages/artifact/src/internal/__mocks__/config-variables.ts similarity index 78% rename from packages/artifact/src/__mocks__/internal-config-variables.ts rename to packages/artifact/src/internal/__mocks__/config-variables.ts index 5b5536173c..46efafdf00 100644 --- a/packages/artifact/src/__mocks__/internal-config-variables.ts +++ b/packages/artifact/src/internal/__mocks__/config-variables.ts @@ -12,6 +12,19 @@ export function getUploadChunkConcurrency(): number { export function getUploadChunkSize(): number { return 4 * 1024 * 1024 // 4 MB Chunks } + +export function getUploadRetryCount(): number { + return 1 +} + +export function getRetryWaitTimeInMilliseconds(): number { + return 1 +} + +export function getDownloadFileConcurrency(): number { + return 1 +} + /** * Mocks the 'ACTIONS_RUNTIME_TOKEN', 'ACTIONS_RUNTIME_URL' and 'GITHUB_RUN_ID' env variables * that are only available from a node context on the runner. This allows for tests to run diff --git a/packages/artifact/src/internal-artifact-client.ts b/packages/artifact/src/internal/artifact-client.ts similarity index 63% rename from packages/artifact/src/internal-artifact-client.ts rename to packages/artifact/src/internal/artifact-client.ts index 9216d9764c..0f90383de8 100644 --- a/packages/artifact/src/internal-artifact-client.ts +++ b/packages/artifact/src/internal/artifact-client.ts @@ -2,31 +2,18 @@ import * as core from '@actions/core' import { UploadSpecification, getUploadSpecification -} from './internal-upload-specification' -import { - createArtifactInFileContainer, - uploadArtifactToFileContainer, - patchArtifactSize -} from './internal-upload-http-client' -import {UploadResponse} from './internal-upload-response' -import {UploadOptions} from './internal-upload-options' -import {DownloadOptions} from './internal-download-options' -import {DownloadResponse} from './internal-download-response' -import {checkArtifactName, createDirectoriesForArtifact} from './internal-utils' -import { - listArtifacts, - downloadSingleArtifact, - getContainerItems -} from './internal-download-http-client' -import {getDownloadSpecification} from './internal-download-specification' -import { - getWorkSpaceDirectory, - getDownloadArtifactConcurrency -} from './internal-config-variables' +} from './upload-specification' +import {UploadHttpClient} from './upload-http-client' +import {UploadResponse} from './upload-response' +import {UploadOptions} from './upload-options' +import {DownloadOptions} from './download-options' +import {DownloadResponse} from './download-response' +import {checkArtifactName, createDirectoriesForArtifact} from './utils' +import {DownloadHttpClient} from './download-http-client' +import {getDownloadSpecification} from './download-specification' +import {getWorkSpaceDirectory} from './config-variables' import {normalize, resolve} from 'path' -export {UploadResponse, UploadOptions, DownloadResponse, DownloadOptions} - export interface ArtifactClient { /** * Uploads an artifact @@ -96,11 +83,15 @@ export class DefaultArtifactClient implements ArtifactClient { failedItems: [] } + const uploadHttpClient = new UploadHttpClient() + if (uploadSpecification.length === 0) { core.warning(`No files found that can be uploaded`) } else { // Create an entry for the artifact in the file container - const response = await createArtifactInFileContainer(name) + const response = await uploadHttpClient.createArtifactInFileContainer( + name + ) if (!response.fileContainerResourceUrl) { core.debug(response.toString()) throw new Error( @@ -110,23 +101,24 @@ export class DefaultArtifactClient implements ArtifactClient { core.debug(`Upload Resource URL: ${response.fileContainerResourceUrl}`) // Upload each of the files that were found concurrently - const uploadResult = await uploadArtifactToFileContainer( + const uploadResult = await uploadHttpClient.uploadArtifactToFileContainer( response.fileContainerResourceUrl, uploadSpecification, options ) - //Update the size of the artifact to indicate we are done uploading - await patchArtifactSize(uploadResult.size, name) + // Update the size of the artifact to indicate we are done uploading + // The uncompressed size is used for display when downloading a zip of the artifact from the UI + await uploadHttpClient.patchArtifactSize(uploadResult.totalSize, name) core.info( - `Finished uploading artifact ${name}. Reported size is ${uploadResult.size} bytes. There were ${uploadResult.failedItems.length} items that failed to upload` + `Finished uploading artifact ${name}. Reported size is ${uploadResult.uploadSize} bytes. There were ${uploadResult.failedItems.length} items that failed to upload` ) uploadResponse.artifactItems = uploadSpecification.map( item => item.absoluteFilePath ) - uploadResponse.size = uploadResult.size + uploadResponse.size = uploadResult.uploadSize uploadResponse.failedItems = uploadResult.failedItems } return uploadResponse @@ -137,7 +129,9 @@ export class DefaultArtifactClient implements ArtifactClient { path?: string | undefined, options?: DownloadOptions | undefined ): Promise { - const artifacts = await listArtifacts() + const downloadHttpClient = new DownloadHttpClient() + + const artifacts = await downloadHttpClient.listArtifacts() if (artifacts.count === 0) { throw new Error( `Unable to find any artifacts for the associated workflow` @@ -151,7 +145,7 @@ export class DefaultArtifactClient implements ArtifactClient { throw new Error(`Unable to find an artifact with the name: ${name}`) } - const items = await getContainerItems( + const items = await downloadHttpClient.getContainerItems( artifactToDownload.name, artifactToDownload.fileContainerResourceUrl ) @@ -179,7 +173,9 @@ export class DefaultArtifactClient implements ArtifactClient { await createDirectoriesForArtifact( downloadSpecification.directoryStructure ) - await downloadSingleArtifact(downloadSpecification.filesToDownload) + await downloadHttpClient.downloadSingleArtifact( + downloadSpecification.filesToDownload + ) } return { @@ -191,8 +187,10 @@ export class DefaultArtifactClient implements ArtifactClient { async downloadAllArtifacts( path?: string | undefined ): Promise { + const downloadHttpClient = new DownloadHttpClient() + const response: DownloadResponse[] = [] - const artifacts = await listArtifacts() + const artifacts = await downloadHttpClient.listArtifacts() if (artifacts.count === 0) { core.info('Unable to find any artifacts for the associated workflow') return response @@ -204,46 +202,41 @@ export class DefaultArtifactClient implements ArtifactClient { path = normalize(path) path = resolve(path) - const ARTIFACT_CONCURRENCY = getDownloadArtifactConcurrency() - const parallelDownloads = [...new Array(ARTIFACT_CONCURRENCY).keys()] let downloadedArtifacts = 0 - await Promise.all( - parallelDownloads.map(async () => { - while (downloadedArtifacts < artifacts.count) { - const currentArtifactToDownload = artifacts.value[downloadedArtifacts] - downloadedArtifacts += 1 - - // Get container entries for the specific artifact - const items = await getContainerItems( - currentArtifactToDownload.name, - currentArtifactToDownload.fileContainerResourceUrl - ) - - // Promise.All is not correctly inferring that 'path' is no longer possibly undefined: https://github.com/microsoft/TypeScript/issues/34925 - const downloadSpecification = getDownloadSpecification( - currentArtifactToDownload.name, - items.value, - path!, // eslint-disable-line @typescript-eslint/no-non-null-assertion - true - ) - if (downloadSpecification.filesToDownload.length === 0) { - core.info( - `No downloadable files were found for any artifact ${currentArtifactToDownload.name}` - ) - } else { - await createDirectoriesForArtifact( - downloadSpecification.directoryStructure - ) - await downloadSingleArtifact(downloadSpecification.filesToDownload) - } - - response.push({ - artifactName: currentArtifactToDownload.name, - downloadPath: downloadSpecification.rootDownloadLocation - }) - } + while (downloadedArtifacts < artifacts.count) { + const currentArtifactToDownload = artifacts.value[downloadedArtifacts] + downloadedArtifacts += 1 + + // Get container entries for the specific artifact + const items = await downloadHttpClient.getContainerItems( + currentArtifactToDownload.name, + currentArtifactToDownload.fileContainerResourceUrl + ) + + const downloadSpecification = getDownloadSpecification( + currentArtifactToDownload.name, + items.value, + path, + true + ) + if (downloadSpecification.filesToDownload.length === 0) { + core.info( + `No downloadable files were found for any artifact ${currentArtifactToDownload.name}` + ) + } else { + await createDirectoriesForArtifact( + downloadSpecification.directoryStructure + ) + await downloadHttpClient.downloadSingleArtifact( + downloadSpecification.filesToDownload + ) + } + + response.push({ + artifactName: currentArtifactToDownload.name, + downloadPath: downloadSpecification.rootDownloadLocation }) - ) + } return response } } diff --git a/packages/artifact/src/internal-config-variables.ts b/packages/artifact/src/internal/config-variables.ts similarity index 82% rename from packages/artifact/src/internal-config-variables.ts rename to packages/artifact/src/internal/config-variables.ts index 0cf8c6a127..baffffcd77 100644 --- a/packages/artifact/src/internal-config-variables.ts +++ b/packages/artifact/src/internal/config-variables.ts @@ -2,21 +2,20 @@ export function getUploadFileConcurrency(): number { return 2 } -export function getUploadChunkConcurrency(): number { - return 1 -} - export function getUploadChunkSize(): number { return 4 * 1024 * 1024 // 4 MB Chunks } -export function getDownloadFileConcurrency(): number { - return 2 +export function getUploadRetryCount(): number { + return 3 } -export function getDownloadArtifactConcurrency(): number { - // when downloading all artifact at once, this is number of concurrent artifacts being downloaded - return 1 +export function getRetryWaitTimeInMilliseconds(): number { + return 10000 +} + +export function getDownloadFileConcurrency(): number { + return 2 } export function getRuntimeToken(): string { diff --git a/packages/artifact/src/internal-contracts.ts b/packages/artifact/src/internal/contracts.ts similarity index 96% rename from packages/artifact/src/internal-contracts.ts rename to packages/artifact/src/internal/contracts.ts index 7abb6130d2..c0518dffde 100644 --- a/packages/artifact/src/internal-contracts.ts +++ b/packages/artifact/src/internal/contracts.ts @@ -28,7 +28,8 @@ export interface PatchArtifactSizeSuccessResponse { } export interface UploadResults { - size: number + uploadSize: number + totalSize: number failedItems: string[] } diff --git a/packages/artifact/src/internal/download-http-client.ts b/packages/artifact/src/internal/download-http-client.ts new file mode 100644 index 0000000000..dadfe5aff8 --- /dev/null +++ b/packages/artifact/src/internal/download-http-client.ts @@ -0,0 +1,189 @@ +import * as fs from 'fs' +import * as zlib from 'zlib' +import { + getArtifactUrl, + getRequestOptions, + isSuccessStatusCode, + isRetryableStatusCode, + createHttpClient +} from './utils' +import {URL} from 'url' +import {ListArtifactsResponse, QueryArtifactResponse} from './contracts' +import {IHttpClientResponse} from '@actions/http-client/interfaces' +import {HttpManager} from './http-manager' +import {DownloadItem} from './download-specification' +import { + getDownloadFileConcurrency, + getRetryWaitTimeInMilliseconds +} from './config-variables' +import {warning} from '@actions/core' +import {IncomingHttpHeaders} from 'http' + +export class DownloadHttpClient { + // http manager is used for concurrent connection when downloading mulitple files at once + private downloadHttpManager: HttpManager + + constructor() { + this.downloadHttpManager = new HttpManager(getDownloadFileConcurrency()) + } + + /** + * Gets a list of all artifacts that are in a specific container + */ + async listArtifacts(): Promise { + const artifactUrl = getArtifactUrl() + // use the first client from the httpManager, `keep-alive` is not used so the connection will close immediatly + const client = this.downloadHttpManager.getClient(0) + const requestOptions = getRequestOptions('application/json') + + const rawResponse = await client.get(artifactUrl, requestOptions) + const body: string = await rawResponse.readBody() + if (isSuccessStatusCode(rawResponse.message.statusCode) && body) { + return JSON.parse(body) + } + // eslint-disable-next-line no-console + console.log(rawResponse) + throw new Error(`Unable to list artifacts for the run`) + } + + /** + * Fetches a set of container items that describe the contents of an artifact + * @param artifactName the name of the artifact + * @param containerUrl the artifact container URL for the run + */ + async getContainerItems( + artifactName: string, + containerUrl: string + ): Promise { + // the itemPath search parameter controls which containers will be returned + const resourceUrl = new URL(containerUrl) + resourceUrl.searchParams.append('itemPath', artifactName) + + // no concurrent calls so a single httpClient without the http-manager is sufficient + const client = createHttpClient() + + // no keep-alive header, client disposal is not necessary + const requestOptions = getRequestOptions('application/json') + const rawResponse = await client.get(resourceUrl.toString(), requestOptions) + const body: string = await rawResponse.readBody() + if (isSuccessStatusCode(rawResponse.message.statusCode) && body) { + return JSON.parse(body) + } + // eslint-disable-next-line no-console + console.log(rawResponse) + throw new Error(`Unable to get ContainersItems from ${resourceUrl}`) + } + + /** + * Concurrently downloads all the files that are part of an artifact + * @param downloadItems information about what items to download and where to save them + */ + async downloadSingleArtifact(downloadItems: DownloadItem[]): Promise { + const DOWNLOAD_CONCURRENCY = getDownloadFileConcurrency() + // limit the number of files downloaded at a single time + const parallelDownloads = [...new Array(DOWNLOAD_CONCURRENCY).keys()] + let downloadedFiles = 0 + await Promise.all( + parallelDownloads.map(async index => { + while (downloadedFiles < downloadItems.length) { + const currentFileToDownload = downloadItems[downloadedFiles] + downloadedFiles += 1 + await this.downloadIndividualFile( + index, + currentFileToDownload.sourceLocation, + currentFileToDownload.targetPath + ) + } + }) + ) + + // done downloading, safety dispose all connections + this.downloadHttpManager.disposeAndReplaceAllClients() + } + + /** + * Downloads an individual file + * @param httpClientIndex the index of the http client that is used to make all of the calls + * @param artifactLocation origin location where a file will be downloaded from + * @param downloadPath destination location for the file being downloaded + */ + private async downloadIndividualFile( + httpClientIndex: number, + artifactLocation: string, + downloadPath: string + ): Promise { + const stream = fs.createWriteStream(downloadPath) + const client = this.downloadHttpManager.getClient(httpClientIndex) + const requestOptions = getRequestOptions('application/octet-stream', true) + const response = await client.get(artifactLocation, requestOptions) + + // check the response headers to determine if the file was compressed using gzip + const isGzip = (headers: IncomingHttpHeaders): boolean => { + return ( + 'content-encoding' in headers && headers['content-encoding'] === 'gzip' + ) + } + + if (isSuccessStatusCode(response.message.statusCode)) { + await this.pipeResponseToStream( + response, + stream, + isGzip(response.message.headers) + ) + } else if (isRetryableStatusCode(response.message.statusCode)) { + warning( + `Received http ${response.message.statusCode} during file download, will retry ${artifactLocation} after 10 seconds` + ) + // if an error is encountered, dispose of the http connection, and create a new one + this.downloadHttpManager.disposeAndReplaceClient(httpClientIndex) + await new Promise(resolve => + setTimeout(resolve, getRetryWaitTimeInMilliseconds()) + ) + const retryResponse = await client.get(artifactLocation) + if (isSuccessStatusCode(retryResponse.message.statusCode)) { + await this.pipeResponseToStream( + response, + stream, + isGzip(response.message.headers) + ) + } else { + // eslint-disable-next-line no-console + console.log(retryResponse) + throw new Error(`Unable to download ${artifactLocation}`) + } + } else { + // eslint-disable-next-line no-console + console.log(response) + throw new Error(`Unable to download ${artifactLocation}`) + } + } + + /** + * Pipes the response from downloading an individual file to the appropriate stream + * @param response the http response recieved when downloading a file + * @param stream the stream where the file should be written to + * @param isGzip does the response need to be be uncompressed + */ + private async pipeResponseToStream( + response: IHttpClientResponse, + stream: NodeJS.WritableStream, + isGzip: boolean + ): Promise { + return new Promise(resolve => { + if (isGzip) { + // pipe the response into gunzip to decompress + const gunzip = zlib.createGunzip() + response.message + .pipe(gunzip) + .pipe(stream) + .on('close', () => { + resolve() + }) + } else { + response.message.pipe(stream).on('close', () => { + resolve() + }) + } + }) + } +} diff --git a/packages/artifact/src/internal-download-options.ts b/packages/artifact/src/internal/download-options.ts similarity index 100% rename from packages/artifact/src/internal-download-options.ts rename to packages/artifact/src/internal/download-options.ts diff --git a/packages/artifact/src/internal-download-response.ts b/packages/artifact/src/internal/download-response.ts similarity index 100% rename from packages/artifact/src/internal-download-response.ts rename to packages/artifact/src/internal/download-response.ts diff --git a/packages/artifact/src/internal-download-specification.ts b/packages/artifact/src/internal/download-specification.ts similarity index 98% rename from packages/artifact/src/internal-download-specification.ts rename to packages/artifact/src/internal/download-specification.ts index 6e4da24602..b7ab0c427c 100644 --- a/packages/artifact/src/internal-download-specification.ts +++ b/packages/artifact/src/internal/download-specification.ts @@ -1,5 +1,5 @@ import * as path from 'path' -import {ContainerEntry} from './internal-contracts' +import {ContainerEntry} from './contracts' export interface DownloadSpecification { // root download location for the artifact diff --git a/packages/artifact/src/internal/http-manager.ts b/packages/artifact/src/internal/http-manager.ts new file mode 100644 index 0000000000..59bf26c184 --- /dev/null +++ b/packages/artifact/src/internal/http-manager.ts @@ -0,0 +1,33 @@ +import {HttpClient} from '@actions/http-client/index' +import {createHttpClient} from './utils' + +/** + * Used for managing http clients during either upload or download + */ +export class HttpManager { + private clients: HttpClient[] + + constructor(clientCount: number) { + if (clientCount < 1) { + throw new Error('There must be at least one client') + } + this.clients = new Array(clientCount).fill(createHttpClient()) + } + + getClient(index: number): HttpClient { + return this.clients[index] + } + + // client disposal is necessary if a keep-alive connection is used to properly close the connection + // for more information see: https://github.com/actions/http-client/blob/04e5ad73cd3fd1f5610a32116b0759eddf6570d2/index.ts#L292 + disposeAndReplaceClient(index: number): void { + this.clients[index].dispose() + this.clients[index] = createHttpClient() + } + + disposeAndReplaceAllClients(): void { + for (const [index] of this.clients.entries()) { + this.disposeAndReplaceClient(index) + } + } +} diff --git a/packages/artifact/src/internal/upload-gzip.ts b/packages/artifact/src/internal/upload-gzip.ts new file mode 100644 index 0000000000..58525765a7 --- /dev/null +++ b/packages/artifact/src/internal/upload-gzip.ts @@ -0,0 +1,53 @@ +import * as fs from 'fs' +import * as zlib from 'zlib' +import {promisify} from 'util' +const stat = promisify(fs.stat) + +/** + * Creates a Gzip compressed file of an original file at the provided temporary filepath location + * @param {string} originalFilePath filepath of whatever will be compressed. The original file will be unmodified + * @param {string} tempFilePath the location of where the Gzip file will be created + * @returns the size of gzip file that gets created + */ +export async function createGZipFileOnDisk( + originalFilePath: string, + tempFilePath: string +): Promise { + return new Promise((resolve, reject) => { + const inputStream = fs.createReadStream(originalFilePath) + const gzip = zlib.createGzip() + const outputStream = fs.createWriteStream(tempFilePath) + inputStream.pipe(gzip).pipe(outputStream) + outputStream.on('finish', async () => { + // wait for stream to finish before calculating the size which is needed as part of the Content-Length header when starting an upload + const size = (await stat(tempFilePath)).size + resolve(size) + }) + outputStream.on('error', error => { + // eslint-disable-next-line no-console + console.log(error) + reject + }) + }) +} + +/** + * Creates a GZip file in memory using a buffer. Should be used for smaller files to reduce disk I/O + * @param originalFilePath the path to the original file that is being GZipped + * @returns a buffer with the GZip file + */ +export async function createGZipFileInBuffer( + originalFilePath: string +): Promise { + return new Promise(async resolve => { + const inputStream = fs.createReadStream(originalFilePath) + const gzip = zlib.createGzip() + inputStream.pipe(gzip) + // read stream into buffer, using experimental async itterators see https://github.com/nodejs/readable-stream/issues/403#issuecomment-479069043 + const chunks = [] + for await (const chunk of gzip) { + chunks.push(chunk) + } + resolve(Buffer.concat(chunks)) + }) +} diff --git a/packages/artifact/src/internal/upload-http-client.ts b/packages/artifact/src/internal/upload-http-client.ts new file mode 100644 index 0000000000..e34327010b --- /dev/null +++ b/packages/artifact/src/internal/upload-http-client.ts @@ -0,0 +1,465 @@ +import * as fs from 'fs' +import * as tmp from 'tmp-promise' +import * as stream from 'stream' +import { + ArtifactResponse, + CreateArtifactParameters, + PatchArtifactSize, + UploadResults +} from './contracts' +import { + getArtifactUrl, + getContentRange, + getRequestOptions, + isRetryableStatusCode, + isSuccessStatusCode +} from './utils' +import { + getUploadChunkSize, + getUploadFileConcurrency, + getUploadRetryCount, + getRetryWaitTimeInMilliseconds +} from './config-variables' +import {promisify} from 'util' +import {URL} from 'url' +import {performance} from 'perf_hooks' +import {UploadStatusReporter} from './upload-status-reporter' +import {debug, warning, info} from '@actions/core' +import {HttpClientResponse} from '@actions/http-client/index' +import {IHttpClientResponse} from '@actions/http-client/interfaces' +import {HttpManager} from './http-manager' +import {UploadSpecification} from './upload-specification' +import {UploadOptions} from './upload-options' +import {createGZipFileOnDisk, createGZipFileInBuffer} from './upload-gzip' +const stat = promisify(fs.stat) + +export class UploadHttpClient { + private uploadHttpManager: HttpManager + private statusReporter: UploadStatusReporter + + constructor() { + this.uploadHttpManager = new HttpManager(getUploadFileConcurrency()) + this.statusReporter = new UploadStatusReporter() + } + + /** + * Creates a file container for the new artifact in the remote blob storage/file service + * @param {string} artifactName Name of the artifact being created + * @returns The response from the Artifact Service if the file container was successfully created + */ + async createArtifactInFileContainer( + artifactName: string + ): Promise { + const parameters: CreateArtifactParameters = { + Type: 'actions_storage', + Name: artifactName + } + const data: string = JSON.stringify(parameters, null, 2) + const artifactUrl = getArtifactUrl() + + // use the first client from the httpManager, `keep-alive` is not used so the connection will close immediatly + const client = this.uploadHttpManager.getClient(0) + const requestOptions = getRequestOptions('application/json', false, false) + const rawResponse = await client.post(artifactUrl, data, requestOptions) + const body: string = await rawResponse.readBody() + + if (isSuccessStatusCode(rawResponse.message.statusCode) && body) { + return JSON.parse(body) + } else { + // eslint-disable-next-line no-console + console.log(rawResponse) + throw new Error( + `Unable to create a container for the artifact ${artifactName}` + ) + } + } + + /** + * Concurrently upload all of the files in chunks + * @param {string} uploadUrl Base Url for the artifact that was created + * @param {SearchResult[]} filesToUpload A list of information about the files being uploaded + * @returns The size of all the files uploaded in bytes + */ + async uploadArtifactToFileContainer( + uploadUrl: string, + filesToUpload: UploadSpecification[], + options?: UploadOptions + ): Promise { + const FILE_CONCURRENCY = getUploadFileConcurrency() + const MAX_CHUNK_SIZE = getUploadChunkSize() + debug( + `File Concurrency: ${FILE_CONCURRENCY}, and Chunk Size: ${MAX_CHUNK_SIZE}` + ) + + const parameters: UploadFileParameters[] = [] + // by default, file uploads will continue if there is an error unless specified differently in the options + let continueOnError = true + if (options) { + if (options.continueOnError === false) { + continueOnError = false + } + } + + // prepare the necessary parameters to upload all the files + for (const file of filesToUpload) { + const resourceUrl = new URL(uploadUrl) + resourceUrl.searchParams.append('itemPath', file.uploadFilePath) + parameters.push({ + file: file.absoluteFilePath, + resourceUrl: resourceUrl.toString(), + maxChunkSize: MAX_CHUNK_SIZE, + continueOnError + }) + } + + const parallelUploads = [...new Array(FILE_CONCURRENCY).keys()] + const failedItemsToReport: string[] = [] + let currentFile = 0 + let completedFiles = 0 + let uploadFileSize = 0 + let totalFileSize = 0 + let abortPendingFileUploads = false + + this.statusReporter.setTotalNumberOfFilesToUpload(filesToUpload.length) + this.statusReporter.start() + + // only allow a certain amount of files to be uploaded at once, this is done to reduce potential errors + await Promise.all( + parallelUploads.map(async index => { + while (currentFile < filesToUpload.length) { + const currentFileParameters = parameters[currentFile] + currentFile += 1 + if (abortPendingFileUploads) { + failedItemsToReport.push(currentFileParameters.file) + continue + } + + const startTime = performance.now() + const uploadFileResult = await this.uploadFileAsync( + index, + currentFileParameters + ) + + debug( + `File: ${++completedFiles}/${filesToUpload.length}. ${ + currentFileParameters.file + } took ${(performance.now() - startTime).toFixed( + 3 + )} milliseconds to finish upload` + ) + uploadFileSize += uploadFileResult.successfullUploadSize + totalFileSize += uploadFileResult.totalSize + if (uploadFileResult.isSuccess === false) { + failedItemsToReport.push(currentFileParameters.file) + if (!continueOnError) { + // existing uploads will be able to finish however all pending uploads will fail fast + abortPendingFileUploads = true + } + } + this.statusReporter.incrementProcessedCount() + } + }) + ) + + this.statusReporter.stop() + // done uploading, safety dispose all connections + this.uploadHttpManager.disposeAndReplaceAllClients() + + info(`Total size of all the files uploaded is ${uploadFileSize} bytes`) + return { + uploadSize: uploadFileSize, + totalSize: totalFileSize, + failedItems: failedItemsToReport + } + } + + /** + * Asynchronously uploads a file. The file is compressed and uploaded using GZip if it is determined to save space. + * If the upload file is bigger than the max chunk size it will be uploaded via multiple calls + * @param {number} httpClientIndex The index of the httpClient that is being used to make all of the calls + * @param {UploadFileParameters} parameters Information about the file that needs to be uploaded + * @returns The size of the file that was uploaded in bytes along with any failed uploads + */ + private async uploadFileAsync( + httpClientIndex: number, + parameters: UploadFileParameters + ): Promise { + const totalFileSize: number = (await stat(parameters.file)).size + let offset = 0 + let isUploadSuccessful = true + let failedChunkSizes = 0 + let uploadFileSize = 0 + let isGzip = true + + // the file that is being uploaded is less than 64k in size, to increase thoroughput and to minimize disk I/O + // for creating a new GZip file, an in-memory buffer is used for compression + if (totalFileSize < 65536) { + const buffer = await createGZipFileInBuffer(parameters.file) + let uploadStream: NodeJS.ReadableStream + + if (totalFileSize < buffer.byteLength) { + // compression did not help with reducing the size, use a readable stream from the original file for upload + uploadStream = fs.createReadStream(parameters.file) + isGzip = false + uploadFileSize = totalFileSize + } else { + // create a readable stream using a PassThrough stream that is both readable and writable + const passThrough = new stream.PassThrough() + passThrough.end(buffer) + uploadStream = passThrough + uploadFileSize = buffer.byteLength + } + + const result = await this.uploadChunk( + httpClientIndex, + parameters.resourceUrl, + uploadStream, + 0, + uploadFileSize - 1, + uploadFileSize, + isGzip, + totalFileSize + ) + + if (!result) { + // chunk failed to upload + isUploadSuccessful = false + failedChunkSizes += uploadFileSize + warning(`Aborting upload for ${parameters.file} due to failure`) + } + + return { + isSuccess: isUploadSuccessful, + successfullUploadSize: uploadFileSize - failedChunkSizes, + totalSize: totalFileSize + } + } else { + // the file that is being uploaded is greater than 64k in size, a temprorary file gets created on disk using the + // npm tmp-promise package and this file gets used during compression for the GZip file that gets created + return tmp + .file() + .then(async tmpFile => { + // create a GZip file of the original file being uploaded, the original file should not be modified in any way + uploadFileSize = await createGZipFileOnDisk( + parameters.file, + tmpFile.path + ) + let uploadFilePath = tmpFile.path + + // compression did not help with size reduction, use the original file for upload and delete the temp GZip file + if (totalFileSize < uploadFileSize) { + uploadFileSize = totalFileSize + uploadFilePath = parameters.file + isGzip = false + tmpFile.cleanup() + } + + let abortFileUpload = false + // upload only a single chunk at a time + while (offset < uploadFileSize) { + const chunkSize = Math.min( + uploadFileSize - offset, + parameters.maxChunkSize + ) + if (abortFileUpload) { + // if we don't want to continue in the event of an error, any pending upload chunks will be marked as failed + failedChunkSizes += chunkSize + continue + } + + // if an individual file is greater than 100MB (1024*1024*100) in size, display extra information about the upload status + if (uploadFileSize > 104857600) { + this.statusReporter.updateLargeFileStatus( + parameters.file, + offset, + uploadFileSize + ) + } + + const start = offset + const end = offset + chunkSize - 1 + offset += parameters.maxChunkSize + + const result = await this.uploadChunk( + httpClientIndex, + parameters.resourceUrl, + fs.createReadStream(uploadFilePath, { + start, + end, + autoClose: false + }), + start, + end, + uploadFileSize, + isGzip, + totalFileSize + ) + + if (!result) { + // Chunk failed to upload, report as failed and do not continue uploading any more chunks for the file. It is possible that part of a chunk was + // successfully uploaded so the server may report a different size for what was uploaded + isUploadSuccessful = false + failedChunkSizes += chunkSize + warning(`Aborting upload for ${parameters.file} due to failure`) + abortFileUpload = true + } + } + }) + .then( + async (): Promise => { + // only after the file upload is complete and the temporary file is deleted, return the UploadResult + return new Promise(resolve => { + resolve({ + isSuccess: isUploadSuccessful, + successfullUploadSize: uploadFileSize - failedChunkSizes, + totalSize: totalFileSize + }) + }) + } + ) + } + } + + /** + * Uploads a chunk of an individual file to the specified resourceUrl. If the upload fails and the status code + * indicates a retryable status, we try to upload the chunk as well + * @param {number} httpClientIndex The index of the httpClient being used to make all the necessary calls + * @param {string} resourceUrl Url of the resource that the chunk will be uploaded to + * @param {NodeJS.ReadableStream} data Stream of the file that will be uploaded + * @param {number} start Starting byte index of file that the chunk belongs to + * @param {number} end Ending byte index of file that the chunk belongs to + * @param {number} uploadFileSize Total size of the file in bytes that is being uploaded + * @param {boolean} isGzip Denotes if we are uploading a Gzip compressed stream + * @param {number} totalFileSize Original total size of the file that is being uploaded + * @returns if the chunk was successfully uploaded + */ + private async uploadChunk( + httpClientIndex: number, + resourceUrl: string, + data: NodeJS.ReadableStream, + start: number, + end: number, + uploadFileSize: number, + isGzip: boolean, + totalFileSize: number + ): Promise { + // prepare all the necessary headers before making any http call + const requestOptions = getRequestOptions( + 'application/octet-stream', + true, + isGzip, + totalFileSize, + end - start + 1, + getContentRange(start, end, uploadFileSize) + ) + + const uploadChunkRequest = async (): Promise => { + const client = this.uploadHttpManager.getClient(httpClientIndex) + return await client.sendStream('PUT', resourceUrl, data, requestOptions) + } + + let retryCount = 0 + const retryLimit = getUploadRetryCount() + + // allow for failed chunks to be retried multiple times + while (retryCount <= retryLimit) { + try { + const response = await uploadChunkRequest() + + // Always read the body of the response. There is potential for a resource leak if the body is not read which will + // result in the connection remaining open along with unintended consequences when trying to dispose of the client + await response.readBody() + + if (isSuccessStatusCode(response.message.statusCode)) { + return true + } else if (isRetryableStatusCode(response.message.statusCode)) { + retryCount++ + if (retryCount > retryLimit) { + info( + `Retry limit has been reached for chunk at offset ${start} to ${resourceUrl}` + ) + return false + } else { + info( + `HTTP ${response.message.statusCode} during chunk upload, will retry at offset ${start} after ${getRetryWaitTimeInMilliseconds} milliseconds. Retry count #${retryCount}. URL ${resourceUrl}` + ) + this.uploadHttpManager.disposeAndReplaceClient(httpClientIndex) + await new Promise(resolve => + setTimeout(resolve, getRetryWaitTimeInMilliseconds()) + ) + } + } else { + info(`#ERROR# Unable to upload chunk to ${resourceUrl}`) + // eslint-disable-next-line no-console + console.log(response) + return false + } + } catch (error) { + // eslint-disable-next-line no-console + console.log(error) + + retryCount++ + if (retryCount > retryLimit) { + info( + `Retry limit has been reached for chunk at offset ${start} to ${resourceUrl}` + ) + return false + } else { + info(`Retrying chunk upload after encountering an error`) + this.uploadHttpManager.disposeAndReplaceClient(httpClientIndex) + await new Promise(resolve => + setTimeout(resolve, getRetryWaitTimeInMilliseconds()) + ) + } + } + } + return false + } + + /** + * Updates the size of the artifact from -1 which was initially set when the container was first created for the artifact. + * Updating the size indicates that we are done uploading all the contents of the artifact + */ + async patchArtifactSize(size: number, artifactName: string): Promise { + const requestOptions = getRequestOptions('application/json', false, false) + const resourceUrl = new URL(getArtifactUrl()) + resourceUrl.searchParams.append('artifactName', artifactName) + + const parameters: PatchArtifactSize = {Size: size} + const data: string = JSON.stringify(parameters, null, 2) + debug(`URL is ${resourceUrl.toString()}`) + + // use the first client from the httpManager, `keep-alive` is not used so the connection will close immediatly + const client = this.uploadHttpManager.getClient(0) + const rawResponse: HttpClientResponse = await client.patch( + resourceUrl.toString(), + data, + requestOptions + ) + const body: string = await rawResponse.readBody() + if (isSuccessStatusCode(rawResponse.message.statusCode)) { + debug( + `Artifact ${artifactName} has been successfully uploaded, total size ${size}` + ) + } else if (rawResponse.message.statusCode === 404) { + throw new Error(`An Artifact with the name ${artifactName} was not found`) + } else { + // eslint-disable-next-line no-console + console.log(body) + throw new Error(`Unable to finish uploading artifact ${artifactName}`) + } + } +} + +interface UploadFileParameters { + file: string + resourceUrl: string + maxChunkSize: number + continueOnError: boolean +} + +interface UploadFileResult { + isSuccess: boolean + successfullUploadSize: number + totalSize: number +} diff --git a/packages/artifact/src/internal-upload-options.ts b/packages/artifact/src/internal/upload-options.ts similarity index 100% rename from packages/artifact/src/internal-upload-options.ts rename to packages/artifact/src/internal/upload-options.ts diff --git a/packages/artifact/src/internal-upload-response.ts b/packages/artifact/src/internal/upload-response.ts similarity index 100% rename from packages/artifact/src/internal-upload-response.ts rename to packages/artifact/src/internal/upload-response.ts diff --git a/packages/artifact/src/internal-upload-specification.ts b/packages/artifact/src/internal/upload-specification.ts similarity index 91% rename from packages/artifact/src/internal-upload-specification.ts rename to packages/artifact/src/internal/upload-specification.ts index 0df866662b..e0815357ab 100644 --- a/packages/artifact/src/internal-upload-specification.ts +++ b/packages/artifact/src/internal/upload-specification.ts @@ -1,7 +1,7 @@ import * as fs from 'fs' import {debug} from '@actions/core' import {join, normalize, resolve} from 'path' -import {checkArtifactName} from './internal-utils' +import {checkArtifactName, checkArtifactFilePath} from './utils' export interface UploadSpecification { absoluteFilePath: string @@ -58,7 +58,6 @@ export function getUploadSpecification( if (!fs.existsSync(file)) { throw new Error(`File ${file} does not exist`) } - if (!fs.lstatSync(file).isDirectory()) { // Normalize and resolve, this allows for either absolute or relative paths to be used file = normalize(file) @@ -69,6 +68,10 @@ export function getUploadSpecification( ) } + // Check for forbidden characters in file paths that will be rejected during upload + const uploadPath = file.replace(rootDirectory, '') + checkArtifactFilePath(uploadPath) + /* uploadFilePath denotes where the file will be uploaded in the file container on the server. During a run, if multiple artifacts are uploaded, they will all be saved in the same container. The artifact name is used as the root directory in the container to separate and distinguish uploaded artifacts @@ -81,7 +84,7 @@ export function getUploadSpecification( */ specifications.push({ absoluteFilePath: file, - uploadFilePath: join(artifactName, file.replace(rootDirectory, '')) + uploadFilePath: join(artifactName, uploadPath) }) } else { // Directories are rejected by the server during upload diff --git a/packages/artifact/src/internal/upload-status-reporter.ts b/packages/artifact/src/internal/upload-status-reporter.ts new file mode 100644 index 0000000000..8f4242c1a4 --- /dev/null +++ b/packages/artifact/src/internal/upload-status-reporter.ts @@ -0,0 +1,90 @@ +import {info} from '@actions/core' + +/** + * Upload Status Reporter that displays information about the progress/status of an artifact that is being uploaded + * + * Every 10 seconds, the total status of the upload gets displayed. If there is a large file that is being uploaded, + * extra information about the individual status of an upload can also be displayed + */ + +export class UploadStatusReporter { + private totalNumberOfFilesToUpload = 0 + private processedCount = 0 + private largeUploads = new Map() + private totalUploadStatus: NodeJS.Timeout | undefined + private largeFileUploadStatus: NodeJS.Timeout | undefined + + constructor() { + this.totalUploadStatus = undefined + this.largeFileUploadStatus = undefined + } + + setTotalNumberOfFilesToUpload(fileTotal: number): void { + this.totalNumberOfFilesToUpload = fileTotal + } + + start(): void { + const _this = this + + // displays information about the total upload status every 10 seconds + this.totalUploadStatus = setInterval(function() { + // display 1 decimal place without any rounding + const percentage = _this.formatPercentage( + _this.processedCount, + _this.totalNumberOfFilesToUpload + ) + info( + `Total file(s): ${ + _this.totalNumberOfFilesToUpload + } ---- Processed file #${_this.processedCount} (${percentage.slice( + 0, + percentage.indexOf('.') + 2 + )}%)` + ) + }, 10000) + + // displays extra information about any large files that take a significant amount of time to upload every 1 second + this.largeFileUploadStatus = setInterval(function() { + for (const value of Array.from(_this.largeUploads.values())) { + info(value) + } + // delete all entires in the map after displaying the information so it will not be displayed again unless explicitly added + _this.largeUploads = new Map() + }, 1000) + } + + updateLargeFileStatus( + fileName: string, + numerator: number, + denomiator: number + ): void { + // display 1 decimal place without any rounding + const percentage = this.formatPercentage(numerator, denomiator) + const displayInformation = `Uploading ${fileName} (${percentage.slice( + 0, + percentage.indexOf('.') + 2 + )}%)` + + // any previously added display information should be overwritten for the specific large file because a map is being used + this.largeUploads.set(fileName, displayInformation) + } + + stop(): void { + if (this.totalUploadStatus) { + clearInterval(this.totalUploadStatus) + } + + if (this.largeFileUploadStatus) { + clearInterval(this.largeFileUploadStatus) + } + } + + incrementProcessedCount(): void { + this.processedCount++ + } + + private formatPercentage(numerator: number, denominator: number): string { + // toFixed() rounds, so use extra precision to display accurate information even though 4 decimal places are not displayed + return ((numerator / denominator) * 100).toFixed(4).toString() + } +} diff --git a/packages/artifact/src/internal-utils.ts b/packages/artifact/src/internal/utils.ts similarity index 59% rename from packages/artifact/src/internal-utils.ts rename to packages/artifact/src/internal/utils.ts index 7140776b82..9919dd1055 100644 --- a/packages/artifact/src/internal-utils.ts +++ b/packages/artifact/src/internal/utils.ts @@ -7,7 +7,7 @@ import { getRuntimeToken, getRuntimeUrl, getWorkFlowRunId -} from './internal-config-variables' +} from './config-variables' /** * Parses a env variable that is a number @@ -59,17 +59,40 @@ export function getContentRange( return `bytes ${start}-${end}/${total}` } +/** + * Sets all the necessary headers when making HTTP calls + * @param {string} contentType the type of content being uploaded + * @param {boolean} isKeepAlive is the same connection being used to make multiple calls + * @param {boolean} isGzip is the connection being used to upload GZip compressed content + * @param {number} uncompressedLength the original size of the content if something is being uploaded that has been compressed + * @param {number} contentLength the length of the content that is being uploaded + * @param {string} contentRange the range of the content that is being uploaded + * @returns appropriate request options to make a specific http call + */ export function getRequestOptions( contentType?: string, + isKeepAlive?: boolean, + isGzip?: boolean, + uncompressedLength?: number, contentLength?: number, contentRange?: string ): IHeaders { const requestOptions: IHeaders = { + // same Accept type for each http call that gets made Accept: `application/json;api-version=${getApiVersion()}` } if (contentType) { requestOptions['Content-Type'] = contentType } + if (isKeepAlive) { + requestOptions['Connection'] = 'Keep-Alive' + // keep alive for at least 10 seconds before closing the connection + requestOptions['Keep-Alive'] = '10' + } + if (isGzip) { + requestOptions['Content-Encoding'] = 'gzip' + requestOptions['x-tfs-filelength'] = uncompressedLength + } if (contentLength) { requestOptions['Content-Length'] = contentLength } @@ -96,20 +119,54 @@ export function getArtifactUrl(): string { * from the server if attempted to be sent over. These characters are not allowed due to limitations with certain * file systems such as NTFS. To maintain platform-agnostic behavior, all characters that are not supported by an * individual filesystem/platform will not be supported on all fileSystems/platforms + * + * FilePaths can include characters such as \ and / which are not permitted in the artifact name alone */ -const invalidCharacters = ['\\', '/', '"', ':', '<', '>', '|', '*', '?', ' '] +const invalidArtifactFilePathCharacters = [ + '"', + ':', + '<', + '>', + '|', + '*', + '?', + ' ' +] +const invalidArtifactNameCharacters = [ + ...invalidArtifactFilePathCharacters, + '\\', + '/' +] /** - * Scans the name of the item being uploaded to make sure there are no illegal characters + * Scans the name of the artifact to make sure there are no illegal characters */ export function checkArtifactName(name: string): void { if (!name) { throw new Error(`Artifact name: ${name}, is incorrectly provided`) } - for (const invalidChar of invalidCharacters) { + + for (const invalidChar of invalidArtifactNameCharacters) { if (name.includes(invalidChar)) { throw new Error( - `Artifact name is not valid: ${name}. Contains character: "${invalidChar}". Invalid characters include: ${invalidCharacters.toString()}.` + `Artifact name is not valid: ${name}. Contains character: "${invalidChar}". Invalid artifact name characters include: ${invalidArtifactNameCharacters.toString()}.` + ) + } + } +} + +/** + * Scans the name of the filePath used to make sure there are no illegal characters + */ +export function checkArtifactFilePath(path: string): void { + if (!path) { + throw new Error(`Artifact path: ${path}, is incorrectly provided`) + } + + for (const invalidChar of invalidArtifactFilePathCharacters) { + if (path.includes(invalidChar)) { + throw new Error( + `Artifact path is not valid: ${path}. Contains character: "${invalidChar}". Invalid characters include: ${invalidArtifactFilePathCharacters.toString()}.` ) } } From 37590cb3ee0bdc9621bfe9a0f7d400271b063073 Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Thu, 12 Mar 2020 20:36:09 +0100 Subject: [PATCH 104/192] Update RELEASES.md --- packages/artifact/RELEASES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/artifact/RELEASES.md b/packages/artifact/RELEASES.md index 2b7e17b6c0..35097b3502 100644 --- a/packages/artifact/RELEASES.md +++ b/packages/artifact/RELEASES.md @@ -4,9 +4,9 @@ - Initial release -### 0.1.1 +### 0.2.0 - Fixes to TCP connections not closing - GZip file compression to speed up downloads - Improved logging and output -- Extra documentation \ No newline at end of file +- Extra documentation From 3748609c7333df4f1ebd7f1d08cee2cbe718fed7 Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Thu, 12 Mar 2020 20:36:46 +0100 Subject: [PATCH 105/192] Update package.json --- packages/artifact/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/artifact/package.json b/packages/artifact/package.json index e5b16450ab..1eed412c60 100644 --- a/packages/artifact/package.json +++ b/packages/artifact/package.json @@ -1,6 +1,6 @@ { "name": "@actions/artifact", - "version": "0.1.0", + "version": "0.2.0", "preview": true, "description": "Actions artifact lib", "keywords": [ From b0e01b71c0e630eb4b420f763029a7476c6cf075 Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Thu, 12 Mar 2020 20:39:10 +0100 Subject: [PATCH 106/192] Update package-lock.json --- packages/artifact/package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/artifact/package-lock.json b/packages/artifact/package-lock.json index 87c0b84426..51e30cd1a5 100644 --- a/packages/artifact/package-lock.json +++ b/packages/artifact/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/artifact", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { From 17b97eceec0f9b62cfdb47f2c1c78d7074588e75 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Mon, 16 Mar 2020 16:08:48 -0400 Subject: [PATCH 107/192] adr: glob (#381) --- docs/adrs/0381-glob-module.md | 216 ++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 docs/adrs/0381-glob-module.md diff --git a/docs/adrs/0381-glob-module.md b/docs/adrs/0381-glob-module.md new file mode 100644 index 0000000000..af2c324542 --- /dev/null +++ b/docs/adrs/0381-glob-module.md @@ -0,0 +1,216 @@ +# ADR 381: `glob` module + +**Date**: 2019-12-05 + +**Status**: Accepted + +## Context + +This ADR proposes adding a `glob` function to the toolkit. + +First party actions should have a consistent glob experience. + +Related to artifact upload/download v2. + +## Decision + +### New module + +Create a new module `@actions/glob` that can be versioned at it's own pace - not tied to `@actions/io`. + +### Signature + +```js +/** + * Constructs a globber from patterns + * + * @param patterns Patterns separated by newlines + * @param options Glob options + */ +export function create( + patterns: string, + options?: GlobOptions +): Promise {} + +/** + * Used to match files and directories + */ +export interface Globber { + /** + * Returns the search path preceeding the first glob segment, from each pattern. + * Duplicates and descendants of other paths are filtered out. + * + * Example 1: The patterns `/foo/*` and `/bar/*` returns `/foo` and `/bar`. + * + * Example 2: The patterns `/foo/*` and `/foo/bar/*` returns `/foo`. + */ + getSearchPaths(): string[] + + /** + * Returns files and directories matching the glob patterns. + * + * Order of the results is not guaranteed. + */ + glob(): Promise + + /** + * Returns files and directories matching the glob patterns. + * + * Order of the results is not guaranteed. + */ + globGenerator(): AsyncGenerator +} + +/** + * Options to control globbing behavior + */ +export interface GlobOptions { + /** + * Indicates whether to follow symbolic links. Generally should set to false + * when deleting files. + * + * @default true + */ + followSymbolicLinks?: boolean + + /** + * Indicates whether directories that match a glob pattern, should implicitly + * cause all descendant paths to be matched. + * + * For example, given the directory `my-dir`, the following glob patterns + * would produce the same results: `my-dir/**`, `my-dir/`, `my-dir` + * + * @default true + */ + implicitDescendants?: boolean + + /** + * Indicates whether broken symbolic should be ignored and omitted from the + * result set. Otherwise an error will be thrown. + * + * @default true + */ + omitBrokenSymbolicLinks?: boolean +} +``` + +### Toolkit usage + +Example, do not follow symbolic links: + +```js +const patterns = core.getInput('path') +const globber = glob.create(patterns, {followSymbolicLinks: false}) +const files = globber.glob() +``` + +Example, iterator: + +```js +const patterns = core.getInput('path') +const globber = glob.create(patterns) +for await (const file of this.globGenerator()) { + console.log(file) +} +``` + +### Action usage + +Actions should follow symbolic links by default. + +Users can opt-out. + +Example: + +```yaml +jobs: + build: + steps: + - uses: actions/upload-artifact@v1 + with: + path: | + **/*.tar.gz + **/*.pkg + follow-symbolic-links: false # opt out, should default to true +``` + +### HashFiles function + +Hash files should not follow symbolic links by default. + +User can opt-in by specifying flag `--follow-symbolic-links`. + +Example: + +```yaml +jobs: + build: + steps: + - uses: actions/cache@v1 + with: + hash: ${{ hashFiles('--follow-symbolic-links', '**/package-lock.json') }} +``` + +### Glob behavior + +Patterns `*`, `?`, `[...]`, `**` (globstar) are supported. + +With the following behaviors: + +- File names that begin with `.` may be included in the results +- Case insensitive on Windows +- Directory separator `/` and `\` both supported on Windows + +Note: +- Refer [here](https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html#Pattern-Matching) for more information about Bash glob patterns. +- Refer [here](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html) for more information about Bash glob options. + +### Tilde expansion + +Support basic tilde expansion, for current user HOME replacement only. + +For example, on macOS: +- `~` may expand to `/Users/johndoe` +- `~/foo` may expand to `/Users/johndoe/foo` + +Note: +- Refer [here](https://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html) for more information about Bash tilde expansion. +- All other forms of tilde expansion are not supported. +- Use `os.homedir()` to resolve the HOME path + +### Root and normalize paths + +An unrooted pattern will be rooted using the current working directory, prior to searching. Additionally the search path will be normalized prior to searching (relative pathing removed, slashes normalized on Windows, extra slashes removed). + +The two side effects are: +1. Rooted and normalized paths are always returned +2. The pattern `**` will include the working directory in the results + +These side effects diverge from Bash behavior. Whereas Bash is designed to be a shell, we are designing an API. This decision is intended to improve predictability of the API results. + +Note: +- In Bash, the results are not rooted when the pattern is relative. +- In Bash, the results are not normalized. For example, the results from `./*` may look like: `./foo ./bar` +- In Bash, the results from the pattern `**` does not include the working directory. However the results from `/foo/**` would include the directory `/foo`. Also the results from `foo/**` would include the directory `foo`. + +## Comments + +Patterns that begin with `#` are treated as comments. + +## Exclude patterns + +Leading `!` changes the meaning of an include pattern to exclude. + +Note: +- Multiple leading `!` flips the meaning. + +## Escaping + +Wrapping special characters in `[]` can be used to escape literal glob characters in a file name. For example the literal file name `hello[a-z]` can be escaped as `hello[[]a-z]`. + +On Linux/macOS `\` is also treated as an escape character. + +## Consequences + +- Publish new module `@actions/glob` +- Publish docs for the module (add link from `./README.md` to new doc `./packages/glob/README.md`) \ No newline at end of file From 12f30111a040dc2d1238a14516f5297d12e3175b Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Tue, 17 Mar 2020 11:57:32 -0400 Subject: [PATCH 108/192] Update Contributing.md and add information about ADR's (#383) * Updating Contributing.md + add adr details --- docs/contribute.md => .github/CONTRIBUTING.md | 23 +++++++++++++++++-- README.md | 2 +- docs/adrs/README.md | 19 +++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) rename docs/contribute.md => .github/CONTRIBUTING.md (59%) create mode 100644 docs/adrs/README.md diff --git a/docs/contribute.md b/.github/CONTRIBUTING.md similarity index 59% rename from docs/contribute.md rename to .github/CONTRIBUTING.md index a27741c1d4..d89e2032b8 100644 --- a/docs/contribute.md +++ b/.github/CONTRIBUTING.md @@ -1,4 +1,23 @@ -## Development +# Contributions + +We welcome contributions in the form of issues and pull requests. We view the contributions and process as the same for internal and external contributors. + +## Issues + +Log issues for both bugs and enhancement requests. Logging issues are important for the open community. + +Issues in this repository should be for the toolkit packages. Runner specific issues can be filed [in the runner repository](https://github.com/actions/runner). + +## Enhancements and Feature Requests + +We ask that before significant effort is put into code changes, that we have agreement on taking the change before time is invested in code changes. + +1. Create a feature request. +2. When we agree to take the enhancement, create an ADR to agree on the details of the change. + +An ADR is an Architectural Decision Record. This allows consensus on the direction forward and also serves as a record of the change and motivation. [Read more here](../docs/adrs/README.md). + +## Development Life Cycle This repository uses [Lerna](https://github.com/lerna/lerna#readme) to manage multiple packages. Read the documentation there to begin contributing. @@ -37,4 +56,4 @@ This will ask you some questions about the new package. Start with `0.0.0` as th } ``` -3. Start developing 😄 and open a pull request. +3. Start developing 😄. diff --git a/README.md b/README.md index 17161c8634..948dd98564 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ console.log(`We can even get context data, like the repo: ${context.repo.repo}`) ## Contributing -We welcome contributions. See [how to contribute](docs/contribute.md). +We welcome contributions. See [how to contribute](.github/CONTRIBUTING.md). ## Code of Conduct diff --git a/docs/adrs/README.md b/docs/adrs/README.md new file mode 100644 index 0000000000..6851a5ec69 --- /dev/null +++ b/docs/adrs/README.md @@ -0,0 +1,19 @@ +# ADRs + +ADR, short for "Architecture Decision Record" is a way of capturing important architectural decisions, along with their context and consequences. + +This folder includes ADRs for the actions toolkit. ADRs are proposed in the form of a pull request, and they commonly follow this format: + +* **Title**: short present tense imperative phrase, less than 50 characters, like a git commit message. + +* **Status**: proposed, accepted, rejected, deprecated, superseded, etc. + +* **Context**: what is the issue that we're seeing that is motivating this decision or change. + +* **Decision**: what is the change that we're actually proposing or doing. + +* **Consequences**: what becomes easier or more difficult to do because of this change. + +--- + +- More information about ADRs can be found [here](https://github.com/joelparkerhenderson/architecture_decision_record). \ No newline at end of file From b94f6a1340bd03bd467ff9f167c89b8a6a970a02 Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Thu, 19 Mar 2020 15:56:27 +0100 Subject: [PATCH 109/192] Update additional-information.md --- packages/artifact/docs/additional-information.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/artifact/docs/additional-information.md b/packages/artifact/docs/additional-information.md index 0118a0dcb4..42b9c8b225 100644 --- a/packages/artifact/docs/additional-information.md +++ b/packages/artifact/docs/additional-information.md @@ -4,6 +4,7 @@ Extra information - [Non-Supported Characters](#Non-Supported-Characters) - [Permission loss](#Permission-Loss) - [Considerations](#Considerations) +- [Compression](#Is-my-artifact-compressed) ## Non-Supported Characters @@ -50,4 +51,4 @@ GZip is used internally to compress individual files before starting an upload. Compression using GZip also helps speed up artifact download as part of a workflow. Header information is used to determine if an individual file was uploaded using GZip and if necessary, decompression is used. -When downloading an artifact from the GitHub UI (this differs from downloading an artifact during a workflow), a single Zip file is dynamically created that contains all of the files uploaded as part of an artifact. Any files that were uploaded using GZip will be decompressed on the server before being added to the Zip file with the remaining files. \ No newline at end of file +When downloading an artifact from the GitHub UI (this differs from downloading an artifact during a workflow), a single Zip file is dynamically created that contains all of the files uploaded as part of an artifact. Any files that were uploaded using GZip will be decompressed on the server before being added to the Zip file with the remaining files. From cb7022ea2c70e757af01e31daeb8cd39f55e4582 Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Fri, 20 Mar 2020 18:42:18 +0100 Subject: [PATCH 110/192] Update implementation-details.md --- packages/artifact/docs/implementation-details.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/artifact/docs/implementation-details.md b/packages/artifact/docs/implementation-details.md index b30eaff421..d2f8464653 100644 --- a/packages/artifact/docs/implementation-details.md +++ b/packages/artifact/docs/implementation-details.md @@ -1,5 +1,11 @@ # Implementation Details +Warning: Implementation details may change at any time without notice. This is meant to serve as a reference to help users understand the package. + +## Upload/Compression flow + +![image](https://user-images.githubusercontent.com/16109154/77190819-38685d80-6ada-11ea-8281-4703ff8cc025.png) + ## Proxy support This package uses the `@actions/http-client` NPM package internally which supports proxied requests out of the box. @@ -40,4 +46,4 @@ TCP connections consist of an input and output buffer to manage what is sent and ### Non Concurrent calls -Both `upload-http-client` and `download-http-client` do not instantiate or create any HTTP clients (the `HttpManager` has that responsibility). If an HTTP call has to be made that does not require the `keep-alive` header (such as when calling `listArtifacts` or `patchArtifactSize`), the first `HttpClient` in the `HttpManager` is used. The number of available clients is equal to the upload or download concurrency and there will always be at least one available. \ No newline at end of file +Both `upload-http-client` and `download-http-client` do not instantiate or create any HTTP clients (the `HttpManager` has that responsibility). If an HTTP call has to be made that does not require the `keep-alive` header (such as when calling `listArtifacts` or `patchArtifactSize`), the first `HttpClient` in the `HttpManager` is used. The number of available clients is equal to the upload or download concurrency and there will always be at least one available. From 905c2aa21671e57683c7113418f7bc4eaed964be Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Tue, 24 Mar 2020 10:13:06 +0100 Subject: [PATCH 111/192] Contribution guidelines for artifact package (#388) * Contribution guidelines * PR Feedback --- packages/artifact/CONTRIBUTIONS.md | 30 ++++++++++++++++++++++++++++++ packages/artifact/README.md | 6 ++++++ 2 files changed, 36 insertions(+) create mode 100644 packages/artifact/CONTRIBUTIONS.md diff --git a/packages/artifact/CONTRIBUTIONS.md b/packages/artifact/CONTRIBUTIONS.md new file mode 100644 index 0000000000..d537f5ca87 --- /dev/null +++ b/packages/artifact/CONTRIBUTIONS.md @@ -0,0 +1,30 @@ +# Contributions + +This package is used internally by the v2+ versions of [upload-artifact](https://github.com/actions/upload-artifact) and [download-artifact](https://github.com/actions/download-artifact). This package can also be used by other actions to interact with artifacts. Any changes or updates to this package will propagate updates to these actions so it is important that major changes or updates get properly tested. + +Any issues or feature requests that are related to the artifact actions should be filled in the appropriate repo. + +A limited range of unit tests run as part of each PR when making changes to the artifact packages. For small contributions and fixes, they should be sufficient. + +If making large changes, there are a few scenarios that should be tested. + +- Uploading very large artifacts (large artifacts get compressed using gzip so compression/decompression must be tested) +- Uploading artifacts with lots of small files (each file is uploaded with its own HTTP call, timeouts and non-success HTTP responses can be expected so they must be properly handled) +- Uploading artifacts using a self-hosted runner (uploads and downloads behave differently due to extra latency) +- Downloading a single artifact (large and small, if lots of small files are part of an artifact, timeouts and non-success HTTP responses can be expected) +- Downloading all artifacts at once + +Large architectural changes can impact upload/download performance so it is important to separately run extra tests. We request that any large contributions/changes have extra detailed testing so we can verify performance and possible regressions. + +It is not possible to run end-to-end tests for artifacts as part of a PR in this repo because certain env variables such as `ACTIONS_RUNTIME_URL` are only available from the context of an action as opposed to a shell script. These env variables are needed in order to make the necessary API calls. + +# Testing + +Any easy way to test changes is to fork the artifact actions and to use `npm link` to test your changes. + +1. Fork the [upload-artifact](https://github.com/actions/upload-artifact) and [download-artifact](https://github.com/actions/download-artifact) repos +2. Clone the forks locally +3. With your local changes to the toolkit repo, type `npm link` after ensuring there are no errors when running `tsc` +4. In the locally cloned fork, type `npm link @actions/artifact` +4. Create a new release for your local fork using `tsc` and `npm run release` (this will create a new `dist/index.js` file using `@zeit/ncc`) +5. Commit and push your local changes, you will then be able to test your changes with your forked action \ No newline at end of file diff --git a/packages/artifact/README.md b/packages/artifact/README.md index f5ef597ec7..0fb2c3ff03 100644 --- a/packages/artifact/README.md +++ b/packages/artifact/README.md @@ -199,3 +199,9 @@ Each artifact will have the same `DownloadResponse` as if it was individually do Check out [additional-information](docs/additional-information.md) for extra documentation around usage, restrictions and behavior. Check out [implementation-details](docs/implementation-details.md) for extra information about the implementation of this package. + +## Contributions + +See [contributor guidelines](https://github.com/actions/toolkit/blob/master/.github/CONTRIBUTING.md) for general guidelines and information about toolkit contributions. + +For contributions related to this package, see [artifact contributions](CONTRIBUTIONS.md) for more information. From 36a4b7df618f9172b8072b2706b4a4d7793d1baf Mon Sep 17 00:00:00 2001 From: Brian Cristante <33549821+brcrista@users.noreply.github.com> Date: Tue, 7 Apr 2020 09:42:56 -0400 Subject: [PATCH 112/192] Add screenshot to release instructions (#406) --- docs/action-versioning.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/action-versioning.md b/docs/action-versioning.md index 7b71f93cd8..4a8e816897 100644 --- a/docs/action-versioning.md +++ b/docs/action-versioning.md @@ -34,7 +34,9 @@ Binding to the immutable full sha1 may offer more reliability. However, note th 1. **Create a GitHub release for each specific version**: Creating a release like [ v1.0.0 ](https://github.com/actions/javascript-action/releases/tag/v1.0.0) allows users to bind back to a specific version if an issue is encountered with the latest major version. -2. **Publish the specific version to the marketplace**: When you release a specific version, choose the option to "Publish this release to the GitHub Marketplace". +2. **Publish the specific version to the marketplace**: When you release a specific version, choose the option to "Publish this Action to the GitHub Marketplace". + +screenshot 3. **Make the new release available to those binding to the major version tag**: Move the major version tag (v1, v2, etc.) to point to the ref of the current release. This will act as the stable release for that major version. You should keep this tag updated to the most recent stable minor/patch release. From 1b521c477880e09c8ac4856c9556e8e06e8c00d2 Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Wed, 8 Apr 2020 16:55:18 +0200 Subject: [PATCH 113/192] Updates to @actions/artifact (#396) * Add support for 429s and Exponential backoff * Refactor status-reporter so it can be used with download and upload * Extra logs * Fixes around download & gzip * Cleanup headers and add extra tests * Improved Docs * Spelling bloopers * Improved error messages * User http client version 1.0.7 --- packages/artifact/__tests__/download.test.ts | 112 +++++++- packages/artifact/__tests__/upload.test.ts | 4 +- packages/artifact/__tests__/util.test.ts | 85 +++++- .../artifact/docs/implementation-details.md | 6 +- packages/artifact/package-lock.json | 6 +- packages/artifact/package.json | 2 +- .../internal/__mocks__/config-variables.ts | 12 +- .../artifact/src/internal/artifact-client.ts | 1 + .../artifact/src/internal/config-variables.ts | 20 +- .../src/internal/download-http-client.ts | 250 +++++++++++++----- .../artifact/src/internal/status-reporter.ts | 92 +++++++ packages/artifact/src/internal/upload-gzip.ts | 2 +- .../src/internal/upload-http-client.ts | 228 +++++++++------- .../src/internal/upload-status-reporter.ts | 90 ------- packages/artifact/src/internal/utils.ts | 139 +++++++++- 15 files changed, 756 insertions(+), 293 deletions(-) create mode 100644 packages/artifact/src/internal/status-reporter.ts delete mode 100644 packages/artifact/src/internal/upload-status-reporter.ts diff --git a/packages/artifact/__tests__/download.test.ts b/packages/artifact/__tests__/download.test.ts index 3da3cad4ec..40ab58cb75 100644 --- a/packages/artifact/__tests__/download.test.ts +++ b/packages/artifact/__tests__/download.test.ts @@ -4,6 +4,8 @@ import * as io from '../../io/src/io' import * as net from 'net' import * as path from 'path' import * as configVariables from '../src/internal/config-variables' +import {promises as fs} from 'fs' +import {DownloadItem} from '../src/internal/download-specification' import {HttpClient, HttpClientResponse} from '@actions/http-client' import {DownloadHttpClient} from '../src/internal/download-http-client' import { @@ -11,7 +13,7 @@ import { QueryArtifactResponse } from '../src/internal/contracts' -const root = path.join(__dirname, '_temp', 'artifact-download') +const root = path.join(__dirname, '_temp', 'artifact-download-tests') jest.mock('../src/internal/config-variables') jest.mock('@actions/http-client') @@ -19,12 +21,16 @@ jest.mock('@actions/http-client') describe('Download Tests', () => { beforeAll(async () => { await io.rmRF(root) + await fs.mkdir(path.join(root), { + recursive: true + }) // mock all output so that there is less noise when running tests jest.spyOn(console, 'log').mockImplementation(() => {}) jest.spyOn(core, 'debug').mockImplementation(() => {}) jest.spyOn(core, 'info').mockImplementation(() => {}) jest.spyOn(core, 'warning').mockImplementation(() => {}) + jest.spyOn(core, 'error').mockImplementation(() => {}) }) /** @@ -107,6 +113,56 @@ describe('Download Tests', () => { ) }) + it('Test downloading an individual artifact with gzip', async () => { + setupDownloadItemResponse(true, 200) + const downloadHttpClient = new DownloadHttpClient() + + const items: DownloadItem[] = [] + items.push({ + sourceLocation: `${configVariables.getRuntimeUrl()}_apis/resources/Containers/13?itemPath=my-artifact%2FFileA.txt`, + targetPath: path.join(root, 'FileA.txt') + }) + + await expect( + downloadHttpClient.downloadSingleArtifact(items) + ).resolves.not.toThrow() + }) + + it('Test downloading an individual artifact without gzip', async () => { + setupDownloadItemResponse(false, 200) + const downloadHttpClient = new DownloadHttpClient() + + const items: DownloadItem[] = [] + items.push({ + sourceLocation: `${configVariables.getRuntimeUrl()}_apis/resources/Containers/13?itemPath=my-artifact%2FFileB.txt`, + targetPath: path.join(root, 'FileB.txt') + }) + + await expect( + downloadHttpClient.downloadSingleArtifact(items) + ).resolves.not.toThrow() + }) + + it('Test retryable status codes during artifact download', async () => { + // The first http response should return a retryable status call while the subsequent call should return a 200 so + // the download should successfully finish + const retryableStatusCodes = [429, 502, 503, 504] + for (const statusCode of retryableStatusCodes) { + setupDownloadItemResponse(false, statusCode) + const downloadHttpClient = new DownloadHttpClient() + + const items: DownloadItem[] = [] + items.push({ + sourceLocation: `${configVariables.getRuntimeUrl()}_apis/resources/Containers/13?itemPath=my-artifact%2FFileC.txt`, + targetPath: path.join(root, 'FileC.txt') + }) + + await expect( + downloadHttpClient.downloadSingleArtifact(items) + ).resolves.not.toThrow() + } + }) + /** * Helper used to setup mocking for the HttpClient */ @@ -164,6 +220,60 @@ describe('Download Tests', () => { }) } + /** + * Setups up HTTP GET response for downloading items + * @param isGzip is the downloaded item gzip encoded + * @param firstHttpResponseCode the http response code that should be returned + */ + function setupDownloadItemResponse( + isGzip: boolean, + firstHttpResponseCode: number + ): void { + jest + .spyOn(DownloadHttpClient.prototype, 'pipeResponseToFile') + .mockImplementationOnce(async () => { + return new Promise(resolve => { + resolve() + }) + }) + + jest + .spyOn(HttpClient.prototype, 'get') + .mockImplementationOnce(async () => { + const mockMessage = new http.IncomingMessage(new net.Socket()) + mockMessage.statusCode = firstHttpResponseCode + if (isGzip) { + mockMessage.headers = { + 'content-type': 'gzip' + } + } + + return new Promise(resolve => { + resolve({ + message: mockMessage, + readBody: emptyMockReadBody + }) + }) + }) + .mockImplementationOnce(async () => { + // chained response, if the HTTP GET function gets called again, return a successful response + const mockMessage = new http.IncomingMessage(new net.Socket()) + mockMessage.statusCode = 200 + if (isGzip) { + mockMessage.headers = { + 'content-type': 'gzip' + } + } + + return new Promise(resolve => { + resolve({ + message: mockMessage, + readBody: emptyMockReadBody + }) + }) + }) + } + /** * Setups up HTTP GET response when querying for container items */ diff --git a/packages/artifact/__tests__/upload.test.ts b/packages/artifact/__tests__/upload.test.ts index 502c20ead8..be1b3bf2ff 100644 --- a/packages/artifact/__tests__/upload.test.ts +++ b/packages/artifact/__tests__/upload.test.ts @@ -12,6 +12,7 @@ import { PatchArtifactSizeSuccessResponse } from '../src/internal/contracts' import {UploadSpecification} from '../src/internal/upload-specification' +import {getArtifactUrl} from '../src/internal/utils' const root = path.join(__dirname, '_temp', 'artifact-upload') const file1Path = path.join(root, 'file1.txt') @@ -36,6 +37,7 @@ describe('Upload Tests', () => { jest.spyOn(core, 'debug').mockImplementation(() => {}) jest.spyOn(core, 'info').mockImplementation(() => {}) jest.spyOn(core, 'warning').mockImplementation(() => {}) + jest.spyOn(core, 'error').mockImplementation(() => {}) // setup mocking for calls that got through the HttpClient setupHttpClientMock() @@ -99,7 +101,7 @@ describe('Upload Tests', () => { uploadHttpClient.createArtifactInFileContainer(artifactName) ).rejects.toEqual( new Error( - 'Unable to create a container for the artifact invalid-artifact-name' + `Unable to create a container for the artifact invalid-artifact-name at ${getArtifactUrl()}` ) ) }) diff --git a/packages/artifact/__tests__/util.test.ts b/packages/artifact/__tests__/util.test.ts index c1fcca4043..1ca963eb91 100644 --- a/packages/artifact/__tests__/util.test.ts +++ b/packages/artifact/__tests__/util.test.ts @@ -4,7 +4,12 @@ import * as path from 'path' import * as utils from '../src/internal/utils' import * as core from '@actions/core' import {HttpCodes} from '@actions/http-client' -import {getRuntimeUrl, getWorkFlowRunId} from '../src/internal/config-variables' +import { + getRuntimeUrl, + getWorkFlowRunId, + getInitialRetryIntervalInMilliseconds, + getRetryMultiplier +} from '../src/internal/config-variables' jest.mock('../src/internal/config-variables') @@ -17,6 +22,30 @@ describe('Utils', () => { jest.spyOn(core, 'warning').mockImplementation(() => {}) }) + it('Check exponential retry range', () => { + // No retries should return the initial retry interval + const retryWaitTime0 = utils.getExponentialRetryTimeInMilliseconds(0) + expect(retryWaitTime0).toEqual(getInitialRetryIntervalInMilliseconds()) + + const testMinMaxRange = (retryCount: number): void => { + const retryWaitTime = utils.getExponentialRetryTimeInMilliseconds( + retryCount + ) + const minRange = + getInitialRetryIntervalInMilliseconds() * + getRetryMultiplier() * + retryCount + const maxRange = minRange * getRetryMultiplier() + + expect(retryWaitTime).toBeGreaterThanOrEqual(minRange) + expect(retryWaitTime).toBeLessThan(maxRange) + } + + for (let i = 1; i < 10; i++) { + testMinMaxRange(i) + } + }) + it('Check Artifact Name for any invalid characters', () => { const invalidNames = [ 'my\\artifact', @@ -88,13 +117,13 @@ describe('Utils', () => { ) }) - it('Test constructing headers with all optional parameters', () => { - const type = 'application/json' + it('Test constructing upload headers with all optional parameters', () => { + const contentType = 'application/octet-stream' const size = 24 const uncompressedLength = 100 const range = 'bytes 0-199/200' - const options = utils.getRequestOptions( - type, + const options = utils.getUploadRequestOptions( + contentType, true, true, uncompressedLength, @@ -103,9 +132,9 @@ describe('Utils', () => { ) expect(Object.keys(options).length).toEqual(8) expect(options['Accept']).toEqual( - `${type};api-version=${utils.getApiVersion()}` + `application/json;api-version=${utils.getApiVersion()}` ) - expect(options['Content-Type']).toEqual(type) + expect(options['Content-Type']).toEqual(contentType) expect(options['Connection']).toEqual('Keep-Alive') expect(options['Keep-Alive']).toEqual('10') expect(options['Content-Encoding']).toEqual('gzip') @@ -114,9 +143,33 @@ describe('Utils', () => { expect(options['Content-Range']).toEqual(range) }) - it('Test constructing headers with only required parameter', () => { - const options = utils.getRequestOptions() - expect(Object.keys(options).length).toEqual(1) + it('Test constructing upload headers with only required parameter', () => { + const options = utils.getUploadRequestOptions('application/octet-stream') + expect(Object.keys(options).length).toEqual(2) + expect(options['Accept']).toEqual( + `application/json;api-version=${utils.getApiVersion()}` + ) + expect(options['Content-Type']).toEqual('application/octet-stream') + }) + + it('Test constructing download headers with all optional parameters', () => { + const contentType = 'application/json' + const options = utils.getDownloadRequestOptions(contentType, true, true) + expect(Object.keys(options).length).toEqual(5) + expect(options['Content-Type']).toEqual(contentType) + expect(options['Connection']).toEqual('Keep-Alive') + expect(options['Keep-Alive']).toEqual('10') + expect(options['Accept-Encoding']).toEqual('gzip') + expect(options['Accept']).toEqual( + `application/octet-stream;api-version=${utils.getApiVersion()}` + ) + }) + + it('Test constructing download headers with only required parameter', () => { + const options = utils.getDownloadRequestOptions('application/octet-stream') + expect(Object.keys(options).length).toEqual(2) + expect(options['Content-Type']).toEqual('application/octet-stream') + // check for default accept type expect(options['Accept']).toEqual( `application/json;api-version=${utils.getApiVersion()}` ) @@ -137,11 +190,23 @@ describe('Utils', () => { true ) expect(utils.isRetryableStatusCode(HttpCodes.GatewayTimeout)).toEqual(true) + expect(utils.isRetryableStatusCode(429)).toEqual(true) expect(utils.isRetryableStatusCode(HttpCodes.OK)).toEqual(false) expect(utils.isRetryableStatusCode(HttpCodes.NotFound)).toEqual(false) expect(utils.isRetryableStatusCode(HttpCodes.Forbidden)).toEqual(false) }) + it('Test Throttled Status Code', () => { + expect(utils.isThrottledStatusCode(429)).toEqual(true) + expect(utils.isThrottledStatusCode(HttpCodes.InternalServerError)).toEqual( + false + ) + expect(utils.isThrottledStatusCode(HttpCodes.BadGateway)).toEqual(false) + expect(utils.isThrottledStatusCode(HttpCodes.ServiceUnavailable)).toEqual( + false + ) + }) + it('Test Creating Artifact Directories', async () => { const root = path.join(__dirname, '_temp', 'artifact-download') // remove directory before starting diff --git a/packages/artifact/docs/implementation-details.md b/packages/artifact/docs/implementation-details.md index d2f8464653..3bf09e5c15 100644 --- a/packages/artifact/docs/implementation-details.md +++ b/packages/artifact/docs/implementation-details.md @@ -6,9 +6,13 @@ Warning: Implementation details may change at any time without notice. This is m ![image](https://user-images.githubusercontent.com/16109154/77190819-38685d80-6ada-11ea-8281-4703ff8cc025.png) +## Retry Logic when downloading an individual file + +![image](https://user-images.githubusercontent.com/16109154/78555461-5be71400-780d-11ea-9abd-b05b77a95a3f.png) + ## Proxy support -This package uses the `@actions/http-client` NPM package internally which supports proxied requests out of the box. +This package uses the `@actions/http-client` NPM package internally which supports proxied requests out of the box. ## HttpManager diff --git a/packages/artifact/package-lock.json b/packages/artifact/package-lock.json index 51e30cd1a5..9953a51854 100644 --- a/packages/artifact/package-lock.json +++ b/packages/artifact/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@actions/http-client": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.6.tgz", - "integrity": "sha512-LGmio4w98UyGX33b/W6V6Nx/sQHRXZ859YlMkn36wPsXPB82u8xTVlA/Dq2DXrm6lEq9RVmisRJa1c+HETAIJA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.7.tgz", + "integrity": "sha512-PY3ys/XH5WMekkHyZhYSa/scYvlE5T/TV/T++vABHuY5ZRgtiBZkn2L2tV5Pv/xDCl59lSZb9WwRuWExDyAsSg==", "requires": { "tunnel": "0.0.6" } diff --git a/packages/artifact/package.json b/packages/artifact/package.json index 1eed412c60..a940a88c88 100644 --- a/packages/artifact/package.json +++ b/packages/artifact/package.json @@ -37,7 +37,7 @@ }, "dependencies": { "@actions/core": "^1.2.1", - "@actions/http-client": "^1.0.6", + "@actions/http-client": "^1.0.7", "@types/tmp": "^0.1.0", "tmp": "^0.1.0", "tmp-promise": "^2.0.2" diff --git a/packages/artifact/src/internal/__mocks__/config-variables.ts b/packages/artifact/src/internal/__mocks__/config-variables.ts index 46efafdf00..af7f7636fa 100644 --- a/packages/artifact/src/internal/__mocks__/config-variables.ts +++ b/packages/artifact/src/internal/__mocks__/config-variables.ts @@ -13,12 +13,16 @@ export function getUploadChunkSize(): number { return 4 * 1024 * 1024 // 4 MB Chunks } -export function getUploadRetryCount(): number { - return 1 +export function getRetryLimit(): number { + return 2 } -export function getRetryWaitTimeInMilliseconds(): number { - return 1 +export function getRetryMultiplier(): number { + return 1.5 +} + +export function getInitialRetryIntervalInMilliseconds(): number { + return 10 } export function getDownloadFileConcurrency(): number { diff --git a/packages/artifact/src/internal/artifact-client.ts b/packages/artifact/src/internal/artifact-client.ts index 0f90383de8..afbf9e8a71 100644 --- a/packages/artifact/src/internal/artifact-client.ts +++ b/packages/artifact/src/internal/artifact-client.ts @@ -173,6 +173,7 @@ export class DefaultArtifactClient implements ArtifactClient { await createDirectoriesForArtifact( downloadSpecification.directoryStructure ) + core.info('Directory structure has been setup for the artifact') await downloadHttpClient.downloadSingleArtifact( downloadSpecification.filesToDownload ) diff --git a/packages/artifact/src/internal/config-variables.ts b/packages/artifact/src/internal/config-variables.ts index baffffcd77..5cdfd2d952 100644 --- a/packages/artifact/src/internal/config-variables.ts +++ b/packages/artifact/src/internal/config-variables.ts @@ -1,19 +1,31 @@ +// The number of concurrent uploads that happens at the same time export function getUploadFileConcurrency(): number { return 2 } +// When uploading large files that can't be uploaded with a single http call, this controls +// the chunk size that is used during upload export function getUploadChunkSize(): number { return 4 * 1024 * 1024 // 4 MB Chunks } -export function getUploadRetryCount(): number { - return 3 +// The maximum number of retries that can be attempted before an upload or download fails +export function getRetryLimit(): number { + return 5 } -export function getRetryWaitTimeInMilliseconds(): number { - return 10000 +// With exponential backoff, the larger the retry count, the larger the wait time before another attempt +// The retry multiplier controls by how much the backOff time increases depending on the number of retries +export function getRetryMultiplier(): number { + return 1.5 } +// The initial wait time if an upload or download fails and a retry is being attempted for the first time +export function getInitialRetryIntervalInMilliseconds(): number { + return 3000 +} + +// The number of concurrent downloads that happens at the same time export function getDownloadFileConcurrency(): number { return 2 } diff --git a/packages/artifact/src/internal/download-http-client.ts b/packages/artifact/src/internal/download-http-client.ts index dadfe5aff8..8d139806a8 100644 --- a/packages/artifact/src/internal/download-http-client.ts +++ b/packages/artifact/src/internal/download-http-client.ts @@ -1,30 +1,35 @@ import * as fs from 'fs' +import * as core from '@actions/core' import * as zlib from 'zlib' import { getArtifactUrl, - getRequestOptions, + getDownloadRequestOptions, isSuccessStatusCode, isRetryableStatusCode, - createHttpClient + isThrottledStatusCode, + getExponentialRetryTimeInMilliseconds, + tryGetRetryAfterValueTimeInMilliseconds, + displayHttpDiagnostics } from './utils' import {URL} from 'url' +import {StatusReporter} from './status-reporter' +import {performance} from 'perf_hooks' import {ListArtifactsResponse, QueryArtifactResponse} from './contracts' import {IHttpClientResponse} from '@actions/http-client/interfaces' import {HttpManager} from './http-manager' import {DownloadItem} from './download-specification' -import { - getDownloadFileConcurrency, - getRetryWaitTimeInMilliseconds -} from './config-variables' -import {warning} from '@actions/core' +import {getDownloadFileConcurrency, getRetryLimit} from './config-variables' import {IncomingHttpHeaders} from 'http' export class DownloadHttpClient { - // http manager is used for concurrent connection when downloading mulitple files at once + // http manager is used for concurrent connections when downloading multiple files at once private downloadHttpManager: HttpManager + private statusReporter: StatusReporter constructor() { this.downloadHttpManager = new HttpManager(getDownloadFileConcurrency()) + // downloads are usually significantly faster than uploads so display status information every second + this.statusReporter = new StatusReporter(1000) } /** @@ -32,18 +37,20 @@ export class DownloadHttpClient { */ async listArtifacts(): Promise { const artifactUrl = getArtifactUrl() - // use the first client from the httpManager, `keep-alive` is not used so the connection will close immediatly + + // use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately const client = this.downloadHttpManager.getClient(0) - const requestOptions = getRequestOptions('application/json') + const requestOptions = getDownloadRequestOptions('application/json') + const response = await client.get(artifactUrl, requestOptions) + const body: string = await response.readBody() - const rawResponse = await client.get(artifactUrl, requestOptions) - const body: string = await rawResponse.readBody() - if (isSuccessStatusCode(rawResponse.message.statusCode) && body) { + if (isSuccessStatusCode(response.message.statusCode) && body) { return JSON.parse(body) } - // eslint-disable-next-line no-console - console.log(rawResponse) - throw new Error(`Unable to list artifacts for the run`) + displayHttpDiagnostics(response) + throw new Error( + `Unable to list artifacts for the run. Resource Url ${artifactUrl}` + ) } /** @@ -59,18 +66,16 @@ export class DownloadHttpClient { const resourceUrl = new URL(containerUrl) resourceUrl.searchParams.append('itemPath', artifactName) - // no concurrent calls so a single httpClient without the http-manager is sufficient - const client = createHttpClient() + // use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately + const client = this.downloadHttpManager.getClient(0) + const requestOptions = getDownloadRequestOptions('application/json') + const response = await client.get(resourceUrl.toString(), requestOptions) + const body: string = await response.readBody() - // no keep-alive header, client disposal is not necessary - const requestOptions = getRequestOptions('application/json') - const rawResponse = await client.get(resourceUrl.toString(), requestOptions) - const body: string = await rawResponse.readBody() - if (isSuccessStatusCode(rawResponse.message.statusCode) && body) { + if (isSuccessStatusCode(response.message.statusCode) && body) { return JSON.parse(body) } - // eslint-disable-next-line no-console - console.log(rawResponse) + displayHttpDiagnostics(response) throw new Error(`Unable to get ContainersItems from ${resourceUrl}`) } @@ -81,24 +86,53 @@ export class DownloadHttpClient { async downloadSingleArtifact(downloadItems: DownloadItem[]): Promise { const DOWNLOAD_CONCURRENCY = getDownloadFileConcurrency() // limit the number of files downloaded at a single time + core.debug(`Download file concurrency is set to ${DOWNLOAD_CONCURRENCY}`) const parallelDownloads = [...new Array(DOWNLOAD_CONCURRENCY).keys()] + let currentFile = 0 let downloadedFiles = 0 + + core.info( + `Total number of files that will be downloaded: ${downloadItems.length}` + ) + + this.statusReporter.setTotalNumberOfFilesToProcess(downloadItems.length) + this.statusReporter.start() + await Promise.all( parallelDownloads.map(async index => { - while (downloadedFiles < downloadItems.length) { - const currentFileToDownload = downloadItems[downloadedFiles] - downloadedFiles += 1 + while (currentFile < downloadItems.length) { + const currentFileToDownload = downloadItems[currentFile] + currentFile += 1 + + const startTime = performance.now() await this.downloadIndividualFile( index, currentFileToDownload.sourceLocation, currentFileToDownload.targetPath ) + + if (core.isDebug()) { + core.debug( + `File: ${++downloadedFiles}/${downloadItems.length}. ${ + currentFileToDownload.targetPath + } took ${(performance.now() - startTime).toFixed( + 3 + )} milliseconds to finish downloading` + ) + } + + this.statusReporter.incrementProcessedCount() } }) ) - - // done downloading, safety dispose all connections - this.downloadHttpManager.disposeAndReplaceAllClients() + .catch(error => { + throw new Error(`Unable to download the artifact: ${error}`) + }) + .finally(() => { + this.statusReporter.stop() + // safety dispose all connections + this.downloadHttpManager.disposeAndReplaceAllClients() + }) } /** @@ -112,10 +146,20 @@ export class DownloadHttpClient { artifactLocation: string, downloadPath: string ): Promise { - const stream = fs.createWriteStream(downloadPath) - const client = this.downloadHttpManager.getClient(httpClientIndex) - const requestOptions = getRequestOptions('application/octet-stream', true) - const response = await client.get(artifactLocation, requestOptions) + let retryCount = 0 + const retryLimit = getRetryLimit() + const destinationStream = fs.createWriteStream(downloadPath) + const requestOptions = getDownloadRequestOptions( + 'application/json', + true, + true + ) + + // a single GET request is used to download a file + const makeDownloadRequest = async (): Promise => { + const client = this.downloadHttpManager.getClient(httpClientIndex) + return await client.get(artifactLocation, requestOptions) + } // check the response headers to determine if the file was compressed using gzip const isGzip = (headers: IncomingHttpHeaders): boolean => { @@ -124,66 +168,126 @@ export class DownloadHttpClient { ) } - if (isSuccessStatusCode(response.message.statusCode)) { - await this.pipeResponseToStream( - response, - stream, - isGzip(response.message.headers) - ) - } else if (isRetryableStatusCode(response.message.statusCode)) { - warning( - `Received http ${response.message.statusCode} during file download, will retry ${artifactLocation} after 10 seconds` - ) - // if an error is encountered, dispose of the http connection, and create a new one - this.downloadHttpManager.disposeAndReplaceClient(httpClientIndex) - await new Promise(resolve => - setTimeout(resolve, getRetryWaitTimeInMilliseconds()) - ) - const retryResponse = await client.get(artifactLocation) - if (isSuccessStatusCode(retryResponse.message.statusCode)) { - await this.pipeResponseToStream( + // Increments the current retry count and then checks if the retry limit has been reached + // If there have been too many retries, fail so the download stops. If there is a retryAfterValue value provided, + // it will be used + const backOff = async (retryAfterValue?: number): Promise => { + retryCount++ + if (retryCount > retryLimit) { + return Promise.reject( + new Error( + `Retry limit has been reached. Unable to download ${artifactLocation}` + ) + ) + } else { + this.downloadHttpManager.disposeAndReplaceClient(httpClientIndex) + if (retryAfterValue) { + // Back off by waiting the specified time denoted by the retry-after header + core.info( + `Backoff due to too many requests, retry #${retryCount}. Waiting for ${retryAfterValue} milliseconds before continuing the download` + ) + await new Promise(resolve => setTimeout(resolve, retryAfterValue)) + } else { + // Back off using an exponential value that depends on the retry count + const backoffTime = getExponentialRetryTimeInMilliseconds(retryCount) + core.info( + `Exponential backoff for retry #${retryCount}. Waiting for ${backoffTime} milliseconds before continuing the download` + ) + await new Promise(resolve => setTimeout(resolve, backoffTime)) + } + core.info( + `Finished backoff for retry #${retryCount}, continuing with download` + ) + } + } + + // keep trying to download a file until a retry limit has been reached + while (retryCount <= retryLimit) { + let response: IHttpClientResponse + try { + response = await makeDownloadRequest() + } catch (error) { + // if an error is caught, it is usually indicative of a timeout so retry the download + core.info('An error occurred while attempting to download a file') + // eslint-disable-next-line no-console + console.log(error) + + // increment the retryCount and use exponential backoff to wait before making the next request + await backOff() + continue + } + + if (isSuccessStatusCode(response.message.statusCode)) { + // The body contains the contents of the file however calling response.readBody() causes all the content to be converted to a string + // which can cause some gzip encoded data to be lost + // Instead of using response.readBody(), response.message is a readableStream that can be directly used to get the raw body contents + return this.pipeResponseToFile( response, - stream, + destinationStream, isGzip(response.message.headers) ) + } else if (isRetryableStatusCode(response.message.statusCode)) { + core.info( + `A ${response.message.statusCode} response code has been received while attempting to download an artifact` + ) + // if a throttled status code is received, try to get the retryAfter header value, else differ to standard exponential backoff + isThrottledStatusCode(response.message.statusCode) + ? await backOff( + tryGetRetryAfterValueTimeInMilliseconds(response.message.headers) + ) + : await backOff() } else { - // eslint-disable-next-line no-console - console.log(retryResponse) - throw new Error(`Unable to download ${artifactLocation}`) + // Some unexpected response code, fail immediately and stop the download + displayHttpDiagnostics(response) + return Promise.reject( + new Error( + `Unexpected http ${response.message.statusCode} during download for ${artifactLocation}` + ) + ) } - } else { - // eslint-disable-next-line no-console - console.log(response) - throw new Error(`Unable to download ${artifactLocation}`) } } /** - * Pipes the response from downloading an individual file to the appropriate stream - * @param response the http response recieved when downloading a file - * @param stream the stream where the file should be written to - * @param isGzip does the response need to be be uncompressed + * Pipes the response from downloading an individual file to the appropriate destination stream while decoding gzip content if necessary + * @param response the http response received when downloading a file + * @param destinationStream the stream where the file should be written to + * @param isGzip a boolean denoting if the content is compressed using gzip and if we need to decode it */ - private async pipeResponseToStream( + async pipeResponseToFile( response: IHttpClientResponse, - stream: NodeJS.WritableStream, + destinationStream: fs.WriteStream, isGzip: boolean ): Promise { - return new Promise(resolve => { + await new Promise((resolve, reject) => { if (isGzip) { - // pipe the response into gunzip to decompress const gunzip = zlib.createGunzip() response.message .pipe(gunzip) - .pipe(stream) + .pipe(destinationStream) .on('close', () => { resolve() }) + .on('error', error => { + core.error( + `An error has been encountered while decompressing and writing a downloaded file to ${destinationStream.path}` + ) + reject(error) + }) } else { - response.message.pipe(stream).on('close', () => { - resolve() - }) + response.message + .pipe(destinationStream) + .on('close', () => { + resolve() + }) + .on('error', error => { + core.error( + `An error has been encountered while writing a downloaded file to ${destinationStream.path}` + ) + reject(error) + }) } }) + return } } diff --git a/packages/artifact/src/internal/status-reporter.ts b/packages/artifact/src/internal/status-reporter.ts new file mode 100644 index 0000000000..681dcfc92f --- /dev/null +++ b/packages/artifact/src/internal/status-reporter.ts @@ -0,0 +1,92 @@ +import {info} from '@actions/core' + +/** + * Status Reporter that displays information about the progress/status of an artifact that is being uploaded or downloaded + * + * Variable display time that can be adjusted using the displayFrequencyInMilliseconds variable + * The total status of the upload/download gets displayed according to this value + * If there is a large file that is being uploaded, extra information about the individual status can also be displayed using the updateLargeFileStatus function + */ + +export class StatusReporter { + private totalNumberOfFilesToProcess = 0 + private processedCount = 0 + private displayFrequencyInMilliseconds: number + private largeFiles = new Map() + private totalFileStatus: NodeJS.Timeout | undefined + private largeFileStatus: NodeJS.Timeout | undefined + + constructor(displayFrequencyInMilliseconds: number) { + this.totalFileStatus = undefined + this.largeFileStatus = undefined + this.displayFrequencyInMilliseconds = displayFrequencyInMilliseconds + } + + setTotalNumberOfFilesToProcess(fileTotal: number): void { + this.totalNumberOfFilesToProcess = fileTotal + } + + start(): void { + // displays information about the total upload/download status + this.totalFileStatus = setInterval(() => { + // display 1 decimal place without any rounding + const percentage = this.formatPercentage( + this.processedCount, + this.totalNumberOfFilesToProcess + ) + info( + `Total file count: ${ + this.totalNumberOfFilesToProcess + } ---- Processed file #${this.processedCount} (${percentage.slice( + 0, + percentage.indexOf('.') + 2 + )}%)` + ) + }, this.displayFrequencyInMilliseconds) + + // displays extra information about any large files that take a significant amount of time to upload or download every 1 second + this.largeFileStatus = setInterval(() => { + for (const value of Array.from(this.largeFiles.values())) { + info(value) + } + // delete all entires in the map after displaying the information so it will not be displayed again unless explicitly added + this.largeFiles.clear() + }, 1000) + } + + // if there is a large file that is being uploaded in chunks, this is used to display extra information about the status of the upload + updateLargeFileStatus( + fileName: string, + numerator: number, + denominator: number + ): void { + // display 1 decimal place without any rounding + const percentage = this.formatPercentage(numerator, denominator) + const displayInformation = `Uploading ${fileName} (${percentage.slice( + 0, + percentage.indexOf('.') + 2 + )}%)` + + // any previously added display information should be overwritten for the specific large file because a map is being used + this.largeFiles.set(fileName, displayInformation) + } + + stop(): void { + if (this.totalFileStatus) { + clearInterval(this.totalFileStatus) + } + + if (this.largeFileStatus) { + clearInterval(this.largeFileStatus) + } + } + + incrementProcessedCount(): void { + this.processedCount++ + } + + private formatPercentage(numerator: number, denominator: number): string { + // toFixed() rounds, so use extra precision to display accurate information even though 4 decimal places are not displayed + return ((numerator / denominator) * 100).toFixed(4).toString() + } +} diff --git a/packages/artifact/src/internal/upload-gzip.ts b/packages/artifact/src/internal/upload-gzip.ts index 58525765a7..6d4529bec4 100644 --- a/packages/artifact/src/internal/upload-gzip.ts +++ b/packages/artifact/src/internal/upload-gzip.ts @@ -43,7 +43,7 @@ export async function createGZipFileInBuffer( const inputStream = fs.createReadStream(originalFilePath) const gzip = zlib.createGzip() inputStream.pipe(gzip) - // read stream into buffer, using experimental async itterators see https://github.com/nodejs/readable-stream/issues/403#issuecomment-479069043 + // read stream into buffer, using experimental async iterators see https://github.com/nodejs/readable-stream/issues/403#issuecomment-479069043 const chunks = [] for await (const chunk of gzip) { chunks.push(chunk) diff --git a/packages/artifact/src/internal/upload-http-client.ts b/packages/artifact/src/internal/upload-http-client.ts index e34327010b..4f7676c543 100644 --- a/packages/artifact/src/internal/upload-http-client.ts +++ b/packages/artifact/src/internal/upload-http-client.ts @@ -1,4 +1,5 @@ import * as fs from 'fs' +import * as core from '@actions/core' import * as tmp from 'tmp-promise' import * as stream from 'stream' import { @@ -10,21 +11,23 @@ import { import { getArtifactUrl, getContentRange, - getRequestOptions, + getUploadRequestOptions, isRetryableStatusCode, - isSuccessStatusCode + isSuccessStatusCode, + isThrottledStatusCode, + displayHttpDiagnostics, + getExponentialRetryTimeInMilliseconds, + tryGetRetryAfterValueTimeInMilliseconds } from './utils' import { getUploadChunkSize, getUploadFileConcurrency, - getUploadRetryCount, - getRetryWaitTimeInMilliseconds + getRetryLimit } from './config-variables' import {promisify} from 'util' import {URL} from 'url' import {performance} from 'perf_hooks' -import {UploadStatusReporter} from './upload-status-reporter' -import {debug, warning, info} from '@actions/core' +import {StatusReporter} from './status-reporter' import {HttpClientResponse} from '@actions/http-client/index' import {IHttpClientResponse} from '@actions/http-client/interfaces' import {HttpManager} from './http-manager' @@ -35,11 +38,11 @@ const stat = promisify(fs.stat) export class UploadHttpClient { private uploadHttpManager: HttpManager - private statusReporter: UploadStatusReporter + private statusReporter: StatusReporter constructor() { this.uploadHttpManager = new HttpManager(getUploadFileConcurrency()) - this.statusReporter = new UploadStatusReporter() + this.statusReporter = new StatusReporter(10000) } /** @@ -57,19 +60,18 @@ export class UploadHttpClient { const data: string = JSON.stringify(parameters, null, 2) const artifactUrl = getArtifactUrl() - // use the first client from the httpManager, `keep-alive` is not used so the connection will close immediatly + // use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately const client = this.uploadHttpManager.getClient(0) - const requestOptions = getRequestOptions('application/json', false, false) + const requestOptions = getUploadRequestOptions('application/json', false) const rawResponse = await client.post(artifactUrl, data, requestOptions) const body: string = await rawResponse.readBody() if (isSuccessStatusCode(rawResponse.message.statusCode) && body) { return JSON.parse(body) } else { - // eslint-disable-next-line no-console - console.log(rawResponse) + displayHttpDiagnostics(rawResponse) throw new Error( - `Unable to create a container for the artifact ${artifactName}` + `Unable to create a container for the artifact ${artifactName} at ${artifactUrl}` ) } } @@ -87,7 +89,7 @@ export class UploadHttpClient { ): Promise { const FILE_CONCURRENCY = getUploadFileConcurrency() const MAX_CHUNK_SIZE = getUploadChunkSize() - debug( + core.debug( `File Concurrency: ${FILE_CONCURRENCY}, and Chunk Size: ${MAX_CHUNK_SIZE}` ) @@ -120,7 +122,7 @@ export class UploadHttpClient { let totalFileSize = 0 let abortPendingFileUploads = false - this.statusReporter.setTotalNumberOfFilesToUpload(filesToUpload.length) + this.statusReporter.setTotalNumberOfFilesToProcess(filesToUpload.length) this.statusReporter.start() // only allow a certain amount of files to be uploaded at once, this is done to reduce potential errors @@ -140,19 +142,23 @@ export class UploadHttpClient { currentFileParameters ) - debug( - `File: ${++completedFiles}/${filesToUpload.length}. ${ - currentFileParameters.file - } took ${(performance.now() - startTime).toFixed( - 3 - )} milliseconds to finish upload` - ) - uploadFileSize += uploadFileResult.successfullUploadSize + if (core.isDebug()) { + core.debug( + `File: ${++completedFiles}/${filesToUpload.length}. ${ + currentFileParameters.file + } took ${(performance.now() - startTime).toFixed( + 3 + )} milliseconds to finish upload` + ) + } + + uploadFileSize += uploadFileResult.successfulUploadSize totalFileSize += uploadFileResult.totalSize if (uploadFileResult.isSuccess === false) { failedItemsToReport.push(currentFileParameters.file) if (!continueOnError) { - // existing uploads will be able to finish however all pending uploads will fail fast + // fail fast + core.error(`aborting artifact upload`) abortPendingFileUploads = true } } @@ -165,7 +171,7 @@ export class UploadHttpClient { // done uploading, safety dispose all connections this.uploadHttpManager.disposeAndReplaceAllClients() - info(`Total size of all the files uploaded is ${uploadFileSize} bytes`) + core.info(`Total size of all the files uploaded is ${uploadFileSize} bytes`) return { uploadSize: uploadFileSize, totalSize: totalFileSize, @@ -191,7 +197,7 @@ export class UploadHttpClient { let uploadFileSize = 0 let isGzip = true - // the file that is being uploaded is less than 64k in size, to increase thoroughput and to minimize disk I/O + // the file that is being uploaded is less than 64k in size, to increase throughput and to minimize disk I/O // for creating a new GZip file, an in-memory buffer is used for compression if (totalFileSize < 65536) { const buffer = await createGZipFileInBuffer(parameters.file) @@ -225,16 +231,16 @@ export class UploadHttpClient { // chunk failed to upload isUploadSuccessful = false failedChunkSizes += uploadFileSize - warning(`Aborting upload for ${parameters.file} due to failure`) + core.warning(`Aborting upload for ${parameters.file} due to failure`) } return { isSuccess: isUploadSuccessful, - successfullUploadSize: uploadFileSize - failedChunkSizes, + successfulUploadSize: uploadFileSize - failedChunkSizes, totalSize: totalFileSize } } else { - // the file that is being uploaded is greater than 64k in size, a temprorary file gets created on disk using the + // the file that is being uploaded is greater than 64k in size, a temporary file gets created on disk using the // npm tmp-promise package and this file gets used during compression for the GZip file that gets created return tmp .file() @@ -261,11 +267,6 @@ export class UploadHttpClient { uploadFileSize - offset, parameters.maxChunkSize ) - if (abortFileUpload) { - // if we don't want to continue in the event of an error, any pending upload chunks will be marked as failed - failedChunkSizes += chunkSize - continue - } // if an individual file is greater than 100MB (1024*1024*100) in size, display extra information about the upload status if (uploadFileSize > 104857600) { @@ -280,6 +281,12 @@ export class UploadHttpClient { const end = offset + chunkSize - 1 offset += parameters.maxChunkSize + if (abortFileUpload) { + // if we don't want to continue in the event of an error, any pending upload chunks will be marked as failed + failedChunkSizes += chunkSize + continue + } + const result = await this.uploadChunk( httpClientIndex, parameters.resourceUrl, @@ -300,7 +307,9 @@ export class UploadHttpClient { // successfully uploaded so the server may report a different size for what was uploaded isUploadSuccessful = false failedChunkSizes += chunkSize - warning(`Aborting upload for ${parameters.file} due to failure`) + core.warning( + `Aborting upload for ${parameters.file} due to failure` + ) abortFileUpload = true } } @@ -311,7 +320,7 @@ export class UploadHttpClient { return new Promise(resolve => { resolve({ isSuccess: isUploadSuccessful, - successfullUploadSize: uploadFileSize - failedChunkSizes, + successfulUploadSize: uploadFileSize - failedChunkSizes, totalSize: totalFileSize }) }) @@ -344,7 +353,7 @@ export class UploadHttpClient { totalFileSize: number ): Promise { // prepare all the necessary headers before making any http call - const requestOptions = getRequestOptions( + const requestOptions = getUploadRequestOptions( 'application/octet-stream', true, isGzip, @@ -359,58 +368,91 @@ export class UploadHttpClient { } let retryCount = 0 - const retryLimit = getUploadRetryCount() + const retryLimit = getRetryLimit() + + // Increments the current retry count and then checks if the retry limit has been reached + // If there have been too many retries, fail so the download stops + const incrementAndCheckRetryLimit = ( + response?: IHttpClientResponse + ): boolean => { + retryCount++ + if (retryCount > retryLimit) { + if (response) { + displayHttpDiagnostics(response) + } + core.info( + `Retry limit has been reached for chunk at offset ${start} to ${resourceUrl}` + ) + return true + } + return false + } + + const backOff = async (retryAfterValue?: number): Promise => { + this.uploadHttpManager.disposeAndReplaceClient(httpClientIndex) + if (retryAfterValue) { + core.info( + `Backoff due to too many requests, retry #${retryCount}. Waiting for ${retryAfterValue} milliseconds before continuing the upload` + ) + await new Promise(resolve => setTimeout(resolve, retryAfterValue)) + } else { + const backoffTime = getExponentialRetryTimeInMilliseconds(retryCount) + core.info( + `Exponential backoff for retry #${retryCount}. Waiting for ${backoffTime} milliseconds before continuing the upload at offset ${start}` + ) + await new Promise(resolve => setTimeout(resolve, backoffTime)) + } + core.info( + `Finished backoff for retry #${retryCount}, continuing with upload` + ) + return + } // allow for failed chunks to be retried multiple times while (retryCount <= retryLimit) { + let response: IHttpClientResponse + try { - const response = await uploadChunkRequest() - - // Always read the body of the response. There is potential for a resource leak if the body is not read which will - // result in the connection remaining open along with unintended consequences when trying to dispose of the client - await response.readBody() - - if (isSuccessStatusCode(response.message.statusCode)) { - return true - } else if (isRetryableStatusCode(response.message.statusCode)) { - retryCount++ - if (retryCount > retryLimit) { - info( - `Retry limit has been reached for chunk at offset ${start} to ${resourceUrl}` - ) - return false - } else { - info( - `HTTP ${response.message.statusCode} during chunk upload, will retry at offset ${start} after ${getRetryWaitTimeInMilliseconds} milliseconds. Retry count #${retryCount}. URL ${resourceUrl}` - ) - this.uploadHttpManager.disposeAndReplaceClient(httpClientIndex) - await new Promise(resolve => - setTimeout(resolve, getRetryWaitTimeInMilliseconds()) - ) - } - } else { - info(`#ERROR# Unable to upload chunk to ${resourceUrl}`) - // eslint-disable-next-line no-console - console.log(response) - return false - } + response = await uploadChunkRequest() } catch (error) { + // if an error is caught, it is usually indicative of a timeout so retry the upload + core.info( + `An error has been caught http-client index ${httpClientIndex}, retrying the upload` + ) // eslint-disable-next-line no-console console.log(error) - retryCount++ - if (retryCount > retryLimit) { - info( - `Retry limit has been reached for chunk at offset ${start} to ${resourceUrl}` - ) + if (incrementAndCheckRetryLimit()) { return false - } else { - info(`Retrying chunk upload after encountering an error`) - this.uploadHttpManager.disposeAndReplaceClient(httpClientIndex) - await new Promise(resolve => - setTimeout(resolve, getRetryWaitTimeInMilliseconds()) - ) } + await backOff() + continue + } + + // Always read the body of the response. There is potential for a resource leak if the body is not read which will + // result in the connection remaining open along with unintended consequences when trying to dispose of the client + await response.readBody() + + if (isSuccessStatusCode(response.message.statusCode)) { + return true + } else if (isRetryableStatusCode(response.message.statusCode)) { + core.info( + `A ${response.message.statusCode} status code has been received, will attempt to retry the upload` + ) + if (incrementAndCheckRetryLimit(response)) { + return false + } + isThrottledStatusCode(response.message.statusCode) + ? await backOff( + tryGetRetryAfterValueTimeInMilliseconds(response.message.headers) + ) + : await backOff() + } else { + core.error( + `Unexpected response. Unable to upload chunk to ${resourceUrl}` + ) + displayHttpDiagnostics(response) + return false } } return false @@ -421,32 +463,34 @@ export class UploadHttpClient { * Updating the size indicates that we are done uploading all the contents of the artifact */ async patchArtifactSize(size: number, artifactName: string): Promise { - const requestOptions = getRequestOptions('application/json', false, false) + const requestOptions = getUploadRequestOptions('application/json', false) const resourceUrl = new URL(getArtifactUrl()) resourceUrl.searchParams.append('artifactName', artifactName) const parameters: PatchArtifactSize = {Size: size} const data: string = JSON.stringify(parameters, null, 2) - debug(`URL is ${resourceUrl.toString()}`) + core.debug(`URL is ${resourceUrl.toString()}`) - // use the first client from the httpManager, `keep-alive` is not used so the connection will close immediatly + // use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately const client = this.uploadHttpManager.getClient(0) - const rawResponse: HttpClientResponse = await client.patch( + const response: HttpClientResponse = await client.patch( resourceUrl.toString(), data, requestOptions ) - const body: string = await rawResponse.readBody() - if (isSuccessStatusCode(rawResponse.message.statusCode)) { - debug( - `Artifact ${artifactName} has been successfully uploaded, total size ${size}` + const body: string = await response.readBody() + if (isSuccessStatusCode(response.message.statusCode)) { + core.debug( + `Artifact ${artifactName} has been successfully uploaded, total size in bytes: ${size}` ) - } else if (rawResponse.message.statusCode === 404) { + } else if (response.message.statusCode === 404) { throw new Error(`An Artifact with the name ${artifactName} was not found`) } else { - // eslint-disable-next-line no-console - console.log(body) - throw new Error(`Unable to finish uploading artifact ${artifactName}`) + displayHttpDiagnostics(response) + core.info(body) + throw new Error( + `Unable to finish uploading artifact ${artifactName} to ${resourceUrl}` + ) } } } @@ -460,6 +504,6 @@ interface UploadFileParameters { interface UploadFileResult { isSuccess: boolean - successfullUploadSize: number + successfulUploadSize: number totalSize: number } diff --git a/packages/artifact/src/internal/upload-status-reporter.ts b/packages/artifact/src/internal/upload-status-reporter.ts deleted file mode 100644 index 8f4242c1a4..0000000000 --- a/packages/artifact/src/internal/upload-status-reporter.ts +++ /dev/null @@ -1,90 +0,0 @@ -import {info} from '@actions/core' - -/** - * Upload Status Reporter that displays information about the progress/status of an artifact that is being uploaded - * - * Every 10 seconds, the total status of the upload gets displayed. If there is a large file that is being uploaded, - * extra information about the individual status of an upload can also be displayed - */ - -export class UploadStatusReporter { - private totalNumberOfFilesToUpload = 0 - private processedCount = 0 - private largeUploads = new Map() - private totalUploadStatus: NodeJS.Timeout | undefined - private largeFileUploadStatus: NodeJS.Timeout | undefined - - constructor() { - this.totalUploadStatus = undefined - this.largeFileUploadStatus = undefined - } - - setTotalNumberOfFilesToUpload(fileTotal: number): void { - this.totalNumberOfFilesToUpload = fileTotal - } - - start(): void { - const _this = this - - // displays information about the total upload status every 10 seconds - this.totalUploadStatus = setInterval(function() { - // display 1 decimal place without any rounding - const percentage = _this.formatPercentage( - _this.processedCount, - _this.totalNumberOfFilesToUpload - ) - info( - `Total file(s): ${ - _this.totalNumberOfFilesToUpload - } ---- Processed file #${_this.processedCount} (${percentage.slice( - 0, - percentage.indexOf('.') + 2 - )}%)` - ) - }, 10000) - - // displays extra information about any large files that take a significant amount of time to upload every 1 second - this.largeFileUploadStatus = setInterval(function() { - for (const value of Array.from(_this.largeUploads.values())) { - info(value) - } - // delete all entires in the map after displaying the information so it will not be displayed again unless explicitly added - _this.largeUploads = new Map() - }, 1000) - } - - updateLargeFileStatus( - fileName: string, - numerator: number, - denomiator: number - ): void { - // display 1 decimal place without any rounding - const percentage = this.formatPercentage(numerator, denomiator) - const displayInformation = `Uploading ${fileName} (${percentage.slice( - 0, - percentage.indexOf('.') + 2 - )}%)` - - // any previously added display information should be overwritten for the specific large file because a map is being used - this.largeUploads.set(fileName, displayInformation) - } - - stop(): void { - if (this.totalUploadStatus) { - clearInterval(this.totalUploadStatus) - } - - if (this.largeFileUploadStatus) { - clearInterval(this.largeFileUploadStatus) - } - } - - incrementProcessedCount(): void { - this.processedCount++ - } - - private formatPercentage(numerator: number, denominator: number): string { - // toFixed() rounds, so use extra precision to display accurate information even though 4 decimal places are not displayed - return ((numerator / denominator) * 100).toFixed(4).toString() - } -} diff --git a/packages/artifact/src/internal/utils.ts b/packages/artifact/src/internal/utils.ts index 9919dd1055..95e14c0024 100644 --- a/packages/artifact/src/internal/utils.ts +++ b/packages/artifact/src/internal/utils.ts @@ -1,14 +1,38 @@ -import {debug} from '@actions/core' +import {debug, info} from '@actions/core' import {promises as fs} from 'fs' import {HttpCodes, HttpClient} from '@actions/http-client' import {BearerCredentialHandler} from '@actions/http-client/auth' -import {IHeaders} from '@actions/http-client/interfaces' +import {IHeaders, IHttpClientResponse} from '@actions/http-client/interfaces' +import {IncomingHttpHeaders} from 'http' import { getRuntimeToken, getRuntimeUrl, - getWorkFlowRunId + getWorkFlowRunId, + getRetryMultiplier, + getInitialRetryIntervalInMilliseconds } from './config-variables' +/** + * Returns a retry time in milliseconds that exponentially gets larger + * depending on the amount of retries that have been attempted + */ +export function getExponentialRetryTimeInMilliseconds( + retryCount: number +): number { + if (retryCount < 0) { + throw new Error('RetryCount should not be negative') + } else if (retryCount === 0) { + return getInitialRetryIntervalInMilliseconds() + } + + const minTime = + getInitialRetryIntervalInMilliseconds() * getRetryMultiplier() * retryCount + const maxTime = minTime * getRetryMultiplier() + + // returns a random number between the minTime (inclusive) and the maxTime (exclusive) + return Math.random() * (maxTime - minTime) + minTime +} + /** * Parses a env variable that is a number */ @@ -42,11 +66,47 @@ export function isRetryableStatusCode(statusCode?: number): boolean { const retryableStatusCodes = [ HttpCodes.BadGateway, HttpCodes.ServiceUnavailable, - HttpCodes.GatewayTimeout + HttpCodes.GatewayTimeout, + HttpCodes.TooManyRequests ] return retryableStatusCodes.includes(statusCode) } +export function isThrottledStatusCode(statusCode?: number): boolean { + if (!statusCode) { + return false + } + + return statusCode === HttpCodes.TooManyRequests +} + +/** + * Attempts to get the retry-after value from a set of http headers. The retry time + * is originally denoted in seconds, so if present, it is converted to milliseconds + * @param headers all the headers received when making an http call + */ +export function tryGetRetryAfterValueTimeInMilliseconds( + headers: IncomingHttpHeaders +): number | undefined { + if (headers['retry-after']) { + const retryTime = Number(headers['retry-after']) + if (!isNaN(retryTime)) { + info(`Retry-After header is present with a value of ${retryTime}`) + return retryTime * 1000 + } + info( + `Returned retry-after header value: ${retryTime} is non-numeric and cannot be used` + ) + return undefined + } + info( + `No retry-after header was found. Dumping all headers for diagnostic purposes` + ) + // eslint-disable-next-line no-console + console.log(headers) + return undefined +} + export function getContentRange( start: number, end: number, @@ -60,27 +120,62 @@ export function getContentRange( } /** - * Sets all the necessary headers when making HTTP calls + * Sets all the necessary headers when downloading an artifact + * @param {string} contentType the type of content being uploaded + * @param {boolean} isKeepAlive is the same connection being used to make multiple calls + * @param {boolean} acceptGzip can we accept a gzip encoded response + * @param {string} acceptType the type of content that we can accept + * @returns appropriate request options to make a specific http call during artifact download + */ +export function getDownloadRequestOptions( + contentType: string, + isKeepAlive?: boolean, + acceptGzip?: boolean +): IHeaders { + const requestOptions: IHeaders = {} + + if (contentType) { + requestOptions['Content-Type'] = contentType + } + if (isKeepAlive) { + requestOptions['Connection'] = 'Keep-Alive' + // keep alive for at least 10 seconds before closing the connection + requestOptions['Keep-Alive'] = '10' + } + if (acceptGzip) { + // if we are expecting a response with gzip encoding, it should be using an octet-stream in the accept header + requestOptions['Accept-Encoding'] = 'gzip' + requestOptions[ + 'Accept' + ] = `application/octet-stream;api-version=${getApiVersion()}` + } else { + // default to application/json if we are not working with gzip content + requestOptions['Accept'] = `application/json;api-version=${getApiVersion()}` + } + + return requestOptions +} + +/** + * Sets all the necessary headers when uploading an artifact * @param {string} contentType the type of content being uploaded * @param {boolean} isKeepAlive is the same connection being used to make multiple calls * @param {boolean} isGzip is the connection being used to upload GZip compressed content * @param {number} uncompressedLength the original size of the content if something is being uploaded that has been compressed * @param {number} contentLength the length of the content that is being uploaded * @param {string} contentRange the range of the content that is being uploaded - * @returns appropriate request options to make a specific http call + * @returns appropriate request options to make a specific http call during artifact upload */ -export function getRequestOptions( - contentType?: string, +export function getUploadRequestOptions( + contentType: string, isKeepAlive?: boolean, isGzip?: boolean, uncompressedLength?: number, contentLength?: number, contentRange?: string ): IHeaders { - const requestOptions: IHeaders = { - // same Accept type for each http call that gets made - Accept: `application/json;api-version=${getApiVersion()}` - } + const requestOptions: IHeaders = {} + requestOptions['Accept'] = `application/json;api-version=${getApiVersion()}` if (contentType) { requestOptions['Content-Type'] = contentType } @@ -99,6 +194,7 @@ export function getRequestOptions( if (contentRange) { requestOptions['Content-Range'] = contentRange } + return requestOptions } @@ -114,6 +210,25 @@ export function getArtifactUrl(): string { return artifactUrl } +/** + * Uh oh! Something might have gone wrong during either upload or download. The IHtttpClientResponse object contains information + * about the http call that was made by the actions http client. This information might be useful to display for diagnostic purposes, but + * this entire object is really big and most of the information is not really useful. This function takes the response object and displays only + * the information that we want. + * + * Certain information such as the TLSSocket and the Readable state are not really useful for diagnostic purposes so they can be avoided. + * Other information such as the headers, the response code and message might be useful, so this is displayed. + */ +export function displayHttpDiagnostics(response: IHttpClientResponse): void { + info( + `##### Begin Diagnostic HTTP information ##### +Status Code: ${response.message.statusCode} +Status Message: ${response.message.statusMessage} +Header Information: ${JSON.stringify(response.message.headers, undefined, 2)} +###### End Diagnostic HTTP information ######` + ) +} + /** * Invalid characters that cannot be in the artifact name or an uploaded file. Will be rejected * from the server if attempted to be sent over. These characters are not allowed due to limitations with certain From c010a271d94e467c33b73e73d9b337b6d6f8a416 Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Thu, 9 Apr 2020 17:14:12 +0200 Subject: [PATCH 114/192] @actions/artifact package updates (#408) * Clear error message when storage quota has been hit * Improved download of empty files * Extra info to RELEASES.md * PR Feedback --- packages/artifact/RELEASES.md | 9 +++ .../__tests__/download-specification.test.ts | 65 +++++++++++++++++-- packages/artifact/__tests__/upload.test.ts | 14 ++++ packages/artifact/__tests__/util.test.ts | 54 +++++++++++++-- packages/artifact/package-lock.json | 2 +- packages/artifact/package.json | 2 +- .../artifact/src/internal/artifact-client.ts | 12 +++- .../src/internal/download-specification.ts | 19 ++++-- .../src/internal/upload-http-client.ts | 7 ++ packages/artifact/src/internal/utils.ts | 16 +++++ 10 files changed, 180 insertions(+), 20 deletions(-) diff --git a/packages/artifact/RELEASES.md b/packages/artifact/RELEASES.md index 35097b3502..577cd43a08 100644 --- a/packages/artifact/RELEASES.md +++ b/packages/artifact/RELEASES.md @@ -10,3 +10,12 @@ - GZip file compression to speed up downloads - Improved logging and output - Extra documentation + +### 0.3.0 + +- Fixes to gzip decompression when downloading artifacts +- Support handling 429 response codes +- Improved download experience when dealing with empty files +- Exponential backoff when retryable status codes are encountered +- Clearer error message if storage quota has been reached +- Improved logging and output during artifact download diff --git a/packages/artifact/__tests__/download-specification.test.ts b/packages/artifact/__tests__/download-specification.test.ts index 50ca4648ae..bc40879c0a 100644 --- a/packages/artifact/__tests__/download-specification.test.ts +++ b/packages/artifact/__tests__/download-specification.test.ts @@ -21,7 +21,8 @@ function getPartialContainerEntry(): ContainerEntry { lastModifiedBy: '82f0bf89-6e55-4e5a-b8b6-f75eb992578c', itemLocation: 'ADD_INFORMATION', contentLocation: 'ADD_INFORMATION', - contentId: '' + contentId: '', + fileLength: 100 } } @@ -72,13 +73,13 @@ function createContentLocation(relativePath: string): string { /dir3 /dir4 file4.txt - file5.txt - + file5.txt (no length property) + file6.txt (empty file) /my-artifact-extra /file1.txt */ -// main artfact +// main artifact const file1Path = path.join(artifact1Name, 'file1.txt') const file2Path = path.join(artifact1Name, 'file2.txt') const dir1Path = path.join(artifact1Name, 'dir1') @@ -88,6 +89,7 @@ const dir3Path = path.join(dir2Path, 'dir3') const dir4Path = path.join(dir3Path, 'dir4') const file4Path = path.join(dir4Path, 'file4.txt') const file5Path = path.join(dir4Path, 'file5.txt') +const file6Path = path.join(dir4Path, 'file6.txt') const rootDirectoryEntry = createDirectoryEntry(artifact1Name) const directoryEntry1 = createDirectoryEntry(dir1Path) @@ -98,7 +100,11 @@ const fileEntry1 = createFileEntry(file1Path) const fileEntry2 = createFileEntry(file2Path) const fileEntry3 = createFileEntry(file3Path) const fileEntry4 = createFileEntry(file4Path) -const fileEntry5 = createFileEntry(file5Path) + +const missingLengthFileEntry = createFileEntry(file5Path) +missingLengthFileEntry.fileLength = undefined // one file does not have a fileLength +const emptyLengthFileEntry = createFileEntry(file6Path) +emptyLengthFileEntry.fileLength = 0 // empty file path // extra artifact const artifact2File1Path = path.join(artifact2Name, 'file1.txt') @@ -115,7 +121,8 @@ const artifactContainerEntries: ContainerEntry[] = [ directoryEntry3, directoryEntry4, fileEntry4, - fileEntry5, + missingLengthFileEntry, + emptyLengthFileEntry, rootDirectoryEntry2, extraFileEntry ] @@ -170,6 +177,14 @@ describe('Search', () => { 'dir4', 'file5.txt' ) + const item6ExpectedTargetPath = path.join( + testDownloadPath, + 'dir1', + 'dir2', + 'dir3', + 'dir4', + 'file6.txt' + ) const targetLocations = specification.filesToDownload.map( item => item.targetPath @@ -214,6 +229,9 @@ describe('Search', () => { expect(specification.directoryStructure).toContain( path.join(testDownloadPath, 'dir1', 'dir2', 'dir3', 'dir4') ) + + expect(specification.emptyFilesToCreate.length).toEqual(1) + expect(specification.emptyFilesToCreate).toContain(item6ExpectedTargetPath) }) it('Download Specification - Relative Path with no root directory', () => { @@ -252,6 +270,14 @@ describe('Search', () => { 'dir4', 'file5.txt' ) + const item6ExpectedTargetPath = path.join( + testDownloadPath, + 'dir1', + 'dir2', + 'dir3', + 'dir4', + 'file6.txt' + ) const targetLocations = specification.filesToDownload.map( item => item.targetPath @@ -296,6 +322,9 @@ describe('Search', () => { expect(specification.directoryStructure).toContain( path.join(testDownloadPath, 'dir1', 'dir2', 'dir3', 'dir4') ) + + expect(specification.emptyFilesToCreate.length).toEqual(1) + expect(specification.emptyFilesToCreate).toContain(item6ExpectedTargetPath) }) it('Download Specification - Absolute Path with root directory', () => { @@ -352,6 +381,15 @@ describe('Search', () => { 'dir4', 'file5.txt' ) + const item6ExpectedTargetPath = path.join( + testDownloadPath, + artifact1Name, + 'dir1', + 'dir2', + 'dir3', + 'dir4', + 'file6.txt' + ) const targetLocations = specification.filesToDownload.map( item => item.targetPath @@ -398,6 +436,9 @@ describe('Search', () => { expect(specification.directoryStructure).toContain( path.join(testDownloadPath, dir4Path) ) + + expect(specification.emptyFilesToCreate.length).toEqual(1) + expect(specification.emptyFilesToCreate).toContain(item6ExpectedTargetPath) }) it('Download Specification - Relative Path with root directory', () => { @@ -449,6 +490,15 @@ describe('Search', () => { 'dir4', 'file5.txt' ) + const item6ExpectedTargetPath = path.join( + testDownloadPath, + artifact1Name, + 'dir1', + 'dir2', + 'dir3', + 'dir4', + 'file6.txt' + ) const targetLocations = specification.filesToDownload.map( item => item.targetPath @@ -495,5 +545,8 @@ describe('Search', () => { expect(specification.directoryStructure).toContain( path.join(testDownloadPath, dir4Path) ) + + expect(specification.emptyFilesToCreate.length).toEqual(1) + expect(specification.emptyFilesToCreate).toContain(item6ExpectedTargetPath) }) }) diff --git a/packages/artifact/__tests__/upload.test.ts b/packages/artifact/__tests__/upload.test.ts index be1b3bf2ff..fd5ca362fa 100644 --- a/packages/artifact/__tests__/upload.test.ts +++ b/packages/artifact/__tests__/upload.test.ts @@ -106,6 +106,18 @@ describe('Upload Tests', () => { ) }) + it('Create Artifact - Storage Quota Error', async () => { + const artifactName = 'storage-quota-hit' + const uploadHttpClient = new UploadHttpClient() + expect( + uploadHttpClient.createArtifactInFileContainer(artifactName) + ).rejects.toEqual( + new Error( + 'Artifact storage quota has been hit. Unable to upload any new artifacts' + ) + ) + }) + /** * Artifact Upload Tests */ @@ -367,6 +379,8 @@ describe('Upload Tests', () => { if (inputData.Name === 'invalid-artifact-name') { mockMessage.statusCode = 400 + } else if (inputData.Name === 'storage-quota-hit') { + mockMessage.statusCode = 403 } else { mockMessage.statusCode = 201 const response: ArtifactResponse = { diff --git a/packages/artifact/__tests__/util.test.ts b/packages/artifact/__tests__/util.test.ts index 1ca963eb91..889777eea5 100644 --- a/packages/artifact/__tests__/util.test.ts +++ b/packages/artifact/__tests__/util.test.ts @@ -190,14 +190,14 @@ describe('Utils', () => { true ) expect(utils.isRetryableStatusCode(HttpCodes.GatewayTimeout)).toEqual(true) - expect(utils.isRetryableStatusCode(429)).toEqual(true) + expect(utils.isRetryableStatusCode(HttpCodes.TooManyRequests)).toEqual(true) expect(utils.isRetryableStatusCode(HttpCodes.OK)).toEqual(false) expect(utils.isRetryableStatusCode(HttpCodes.NotFound)).toEqual(false) expect(utils.isRetryableStatusCode(HttpCodes.Forbidden)).toEqual(false) }) it('Test Throttled Status Code', () => { - expect(utils.isThrottledStatusCode(429)).toEqual(true) + expect(utils.isThrottledStatusCode(HttpCodes.TooManyRequests)).toEqual(true) expect(utils.isThrottledStatusCode(HttpCodes.InternalServerError)).toEqual( false ) @@ -207,6 +207,17 @@ describe('Utils', () => { ) }) + it('Test Forbidden Status Code', () => { + expect(utils.isForbiddenStatusCode(HttpCodes.Forbidden)).toEqual(true) + expect(utils.isForbiddenStatusCode(HttpCodes.InternalServerError)).toEqual( + false + ) + expect(utils.isForbiddenStatusCode(HttpCodes.TooManyRequests)).toEqual( + false + ) + expect(utils.isForbiddenStatusCode(HttpCodes.OK)).toEqual(false) + }) + it('Test Creating Artifact Directories', async () => { const root = path.join(__dirname, '_temp', 'artifact-download') // remove directory before starting @@ -216,12 +227,43 @@ describe('Utils', () => { const directory2 = path.join(directory1, 'folder1') // Initially should not exist - expect(fs.existsSync(directory1)).toEqual(false) - expect(fs.existsSync(directory2)).toEqual(false) + await expect(fs.promises.access(directory1)).rejects.not.toBeUndefined() + await expect(fs.promises.access(directory2)).rejects.not.toBeUndefined() const directoryStructure = [directory1, directory2] await utils.createDirectoriesForArtifact(directoryStructure) // directories should now be created - expect(fs.existsSync(directory1)).toEqual(true) - expect(fs.existsSync(directory2)).toEqual(true) + await expect(fs.promises.access(directory1)).resolves.toEqual(undefined) + await expect(fs.promises.access(directory2)).resolves.toEqual(undefined) + }) + + it('Test Creating Empty Files', async () => { + const root = path.join(__dirname, '_temp', 'empty-files') + await io.rmRF(root) + + const emptyFile1 = path.join(root, 'emptyFile1') + const directoryToCreate = path.join(root, 'folder1') + const emptyFile2 = path.join(directoryToCreate, 'emptyFile2') + + // empty files should only be created after the directory structure is fully setup + // ensure they are first created by using the createDirectoriesForArtifact method + const directoryStructure = [root, directoryToCreate] + await utils.createDirectoriesForArtifact(directoryStructure) + await expect(fs.promises.access(root)).resolves.toEqual(undefined) + await expect(fs.promises.access(directoryToCreate)).resolves.toEqual( + undefined + ) + + await expect(fs.promises.access(emptyFile1)).rejects.not.toBeUndefined() + await expect(fs.promises.access(emptyFile2)).rejects.not.toBeUndefined() + + const emptyFilesToCreate = [emptyFile1, emptyFile2] + await utils.createEmptyFilesForArtifact(emptyFilesToCreate) + + await expect(fs.promises.access(emptyFile1)).resolves.toEqual(undefined) + const size1 = (await fs.promises.stat(emptyFile1)).size + expect(size1).toEqual(0) + await expect(fs.promises.access(emptyFile2)).resolves.toEqual(undefined) + const size2 = (await fs.promises.stat(emptyFile2)).size + expect(size2).toEqual(0) }) }) diff --git a/packages/artifact/package-lock.json b/packages/artifact/package-lock.json index 9953a51854..43d68b4378 100644 --- a/packages/artifact/package-lock.json +++ b/packages/artifact/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/artifact", - "version": "0.2.0", + "version": "0.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/artifact/package.json b/packages/artifact/package.json index a940a88c88..b070e220ea 100644 --- a/packages/artifact/package.json +++ b/packages/artifact/package.json @@ -1,6 +1,6 @@ { "name": "@actions/artifact", - "version": "0.2.0", + "version": "0.3.0", "preview": true, "description": "Actions artifact lib", "keywords": [ diff --git a/packages/artifact/src/internal/artifact-client.ts b/packages/artifact/src/internal/artifact-client.ts index afbf9e8a71..746a2e8a05 100644 --- a/packages/artifact/src/internal/artifact-client.ts +++ b/packages/artifact/src/internal/artifact-client.ts @@ -8,7 +8,11 @@ import {UploadResponse} from './upload-response' import {UploadOptions} from './upload-options' import {DownloadOptions} from './download-options' import {DownloadResponse} from './download-response' -import {checkArtifactName, createDirectoriesForArtifact} from './utils' +import { + checkArtifactName, + createDirectoriesForArtifact, + createEmptyFilesForArtifact +} from './utils' import {DownloadHttpClient} from './download-http-client' import {getDownloadSpecification} from './download-specification' import {getWorkSpaceDirectory} from './config-variables' @@ -174,6 +178,9 @@ export class DefaultArtifactClient implements ArtifactClient { downloadSpecification.directoryStructure ) core.info('Directory structure has been setup for the artifact') + await createEmptyFilesForArtifact( + downloadSpecification.emptyFilesToCreate + ) await downloadHttpClient.downloadSingleArtifact( downloadSpecification.filesToDownload ) @@ -228,6 +235,9 @@ export class DefaultArtifactClient implements ArtifactClient { await createDirectoriesForArtifact( downloadSpecification.directoryStructure ) + await createEmptyFilesForArtifact( + downloadSpecification.emptyFilesToCreate + ) await downloadHttpClient.downloadSingleArtifact( downloadSpecification.filesToDownload ) diff --git a/packages/artifact/src/internal/download-specification.ts b/packages/artifact/src/internal/download-specification.ts index b7ab0c427c..0e285ec7f8 100644 --- a/packages/artifact/src/internal/download-specification.ts +++ b/packages/artifact/src/internal/download-specification.ts @@ -8,6 +8,9 @@ export interface DownloadSpecification { // directories that need to be created for all the items in the artifact directoryStructure: string[] + // empty files that are part of the artifact that don't require any downloading + emptyFilesToCreate: string[] + // individual files that need to be downloaded as part of the artifact filesToDownload: DownloadItem[] } @@ -33,6 +36,7 @@ export function getDownloadSpecification( downloadPath: string, includeRootDirectory: boolean ): DownloadSpecification { + // use a set for the directory paths so that there are no duplicates const directories = new Set() const specifications: DownloadSpecification = { @@ -40,6 +44,7 @@ export function getDownloadSpecification( ? path.join(downloadPath, artifactName) : downloadPath, directoryStructure: [], + emptyFilesToCreate: [], filesToDownload: [] } @@ -64,11 +69,15 @@ export function getDownloadSpecification( if (entry.itemType === 'file') { // Get the directories that we need to create from the filePath for each individual file directories.add(path.dirname(filePath)) - - specifications.filesToDownload.push({ - sourceLocation: entry.contentLocation, - targetPath: filePath - }) + if (entry.fileLength === 0) { + // An empty file was uploaded, create the empty files locally so that no extra http calls are made + specifications.emptyFilesToCreate.push(filePath) + } else { + specifications.filesToDownload.push({ + sourceLocation: entry.contentLocation, + targetPath: filePath + }) + } } } } diff --git a/packages/artifact/src/internal/upload-http-client.ts b/packages/artifact/src/internal/upload-http-client.ts index 4f7676c543..cae5b526bb 100644 --- a/packages/artifact/src/internal/upload-http-client.ts +++ b/packages/artifact/src/internal/upload-http-client.ts @@ -15,6 +15,7 @@ import { isRetryableStatusCode, isSuccessStatusCode, isThrottledStatusCode, + isForbiddenStatusCode, displayHttpDiagnostics, getExponentialRetryTimeInMilliseconds, tryGetRetryAfterValueTimeInMilliseconds @@ -68,6 +69,12 @@ export class UploadHttpClient { if (isSuccessStatusCode(rawResponse.message.statusCode) && body) { return JSON.parse(body) + } else if (isForbiddenStatusCode(rawResponse.message.statusCode)) { + // if a 403 is returned when trying to create a file container, the customer has exceeded + // their storage quota so no new artifact containers can be created + throw new Error( + `Artifact storage quota has been hit. Unable to upload any new artifacts` + ) } else { displayHttpDiagnostics(rawResponse) throw new Error( diff --git a/packages/artifact/src/internal/utils.ts b/packages/artifact/src/internal/utils.ts index 95e14c0024..41f4179dff 100644 --- a/packages/artifact/src/internal/utils.ts +++ b/packages/artifact/src/internal/utils.ts @@ -58,6 +58,14 @@ export function isSuccessStatusCode(statusCode?: number): boolean { return statusCode >= 200 && statusCode < 300 } +export function isForbiddenStatusCode(statusCode?: number): boolean { + if (!statusCode) { + return false + } + + return statusCode === HttpCodes.Forbidden +} + export function isRetryableStatusCode(statusCode?: number): boolean { if (!statusCode) { return false @@ -296,3 +304,11 @@ export async function createDirectoriesForArtifact( }) } } + +export async function createEmptyFilesForArtifact( + emptyFilesToCreate: string[] +): Promise { + for (const filePath of emptyFilesToCreate) { + await (await fs.open(filePath, 'w')).close() + } +} From a6a5d98be8466ae2f33ca8778aa991961262255c Mon Sep 17 00:00:00 2001 From: Linda_pp Date: Fri, 10 Apr 2020 01:52:24 +0900 Subject: [PATCH 115/192] Expose ExecOptions interface (#322) --- packages/exec/src/exec.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/exec/src/exec.ts b/packages/exec/src/exec.ts index fe88986b50..80a0363c49 100644 --- a/packages/exec/src/exec.ts +++ b/packages/exec/src/exec.ts @@ -1,6 +1,8 @@ -import * as im from './interfaces' +import {ExecOptions} from './interfaces' import * as tr from './toolrunner' +export {ExecOptions} + /** * Exec a command. * Output will be streamed to the live console. @@ -14,7 +16,7 @@ import * as tr from './toolrunner' export async function exec( commandLine: string, args?: string[], - options?: im.ExecOptions + options?: ExecOptions ): Promise { const commandArgs = tr.argStringToArray(commandLine) if (commandArgs.length === 0) { From 5b940ebda7e7b86545fe9741903c930bc1191eb0 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Thu, 9 Apr 2020 17:00:53 -0400 Subject: [PATCH 116/192] Be more lenient in accepting command inputs (#405) * Be more lenient in accepting command inputs --- packages/core/__tests__/command.test.ts | 15 +++++++ packages/core/__tests__/core.test.ts | 53 ++++++++++++++++++++++++- packages/core/src/command.ts | 28 ++++++++++--- packages/core/src/core.ts | 37 +++++++++-------- 4 files changed, 109 insertions(+), 24 deletions(-) diff --git a/packages/core/__tests__/command.test.ts b/packages/core/__tests__/command.test.ts index 182e145cb3..da3df23a4e 100644 --- a/packages/core/__tests__/command.test.ts +++ b/packages/core/__tests__/command.test.ts @@ -112,6 +112,21 @@ describe('@actions/core/src/command', () => { `::some-command prop1=value 1,prop2=value 2,prop3=value 3::${os.EOL}` ]) }) + + it('should handle issueing commands for non-string objects', () => { + command.issueCommand( + 'some-command', + { + prop1: ({test: 'object'} as unknown) as string, + prop2: (123 as unknown) as string, + prop3: (true as unknown) as string + }, + ({test: 'object'} as unknown) as string + ) + assertWriteCalls([ + `::some-command prop1={"test"%3A"object"},prop2=123,prop3=true::{"test":"object"}${os.EOL}` + ]) + }) }) // Assert that process.stdout.write calls called only with the given arguments. diff --git a/packages/core/__tests__/core.test.ts b/packages/core/__tests__/core.test.ts index 020f4a51b9..d10e0590d8 100644 --- a/packages/core/__tests__/core.test.ts +++ b/packages/core/__tests__/core.test.ts @@ -54,6 +54,16 @@ describe('@actions/core', () => { assertWriteCalls([`::set-env name=my var2::var val%0D%0A${os.EOL}`]) }) + it('exportVariable handles boolean inputs', () => { + core.exportVariable('my var', true) + assertWriteCalls([`::set-env name=my var::true${os.EOL}`]) + }) + + it('exportVariable handles number inputs', () => { + core.exportVariable('my var', 5) + assertWriteCalls([`::set-env name=my var::5${os.EOL}`]) + }) + it('setSecret produces the correct command', () => { core.setSecret('secret val') assertWriteCalls([`::add-mask::secret val${os.EOL}`]) @@ -104,18 +114,35 @@ describe('@actions/core', () => { assertWriteCalls([`::set-output name=some output::some value${os.EOL}`]) }) - it('setFailure sets the correct exit code and failure message', () => { + it('setOutput handles bools', () => { + core.setOutput('some output', false) + assertWriteCalls([`::set-output name=some output::false${os.EOL}`]) + }) + + it('setOutput handles numbers', () => { + core.setOutput('some output', 1.01) + assertWriteCalls([`::set-output name=some output::1.01${os.EOL}`]) + }) + + it('setFailed sets the correct exit code and failure message', () => { core.setFailed('Failure message') expect(process.exitCode).toBe(core.ExitCode.Failure) assertWriteCalls([`::error::Failure message${os.EOL}`]) }) - it('setFailure escapes the failure message', () => { + it('setFailed escapes the failure message', () => { core.setFailed('Failure \r\n\nmessage\r') expect(process.exitCode).toBe(core.ExitCode.Failure) assertWriteCalls([`::error::Failure %0D%0A%0Amessage%0D${os.EOL}`]) }) + it('setFailed handles Error', () => { + const message = 'this is my error message' + core.setFailed(new Error(message)) + expect(process.exitCode).toBe(core.ExitCode.Failure) + assertWriteCalls([`::error::Error: ${message}${os.EOL}`]) + }) + it('error sets the correct error message', () => { core.error('Error message') assertWriteCalls([`::error::Error message${os.EOL}`]) @@ -126,6 +153,12 @@ describe('@actions/core', () => { assertWriteCalls([`::error::Error message%0D%0A%0A${os.EOL}`]) }) + it('error handles an error object', () => { + const message = 'this is my error message' + core.error(new Error(message)) + assertWriteCalls([`::error::Error: ${message}${os.EOL}`]) + }) + it('warning sets the correct message', () => { core.warning('Warning') assertWriteCalls([`::warning::Warning${os.EOL}`]) @@ -136,6 +169,12 @@ describe('@actions/core', () => { assertWriteCalls([`::warning::%0D%0Awarning%0A${os.EOL}`]) }) + it('warning handles an error object', () => { + const message = 'this is my error message' + core.warning(new Error(message)) + assertWriteCalls([`::warning::Error: ${message}${os.EOL}`]) + }) + it('startGroup starts a new group', () => { core.startGroup('my-group') assertWriteCalls([`::group::my-group${os.EOL}`]) @@ -174,6 +213,16 @@ describe('@actions/core', () => { assertWriteCalls([`::save-state name=state_1::some value${os.EOL}`]) }) + it('saveState handles numbers', () => { + core.saveState('state_1', 1) + assertWriteCalls([`::save-state name=state_1::1${os.EOL}`]) + }) + + it('saveState handles bools', () => { + core.saveState('state_1', true) + assertWriteCalls([`::save-state name=state_1::true${os.EOL}`]) + }) + it('getState gets wrapper action state', () => { expect(core.getState('TEST_1')).toBe('state_val') }) diff --git a/packages/core/src/command.ts b/packages/core/src/command.ts index 9bdfd9c41c..57067a1a52 100644 --- a/packages/core/src/command.ts +++ b/packages/core/src/command.ts @@ -2,8 +2,11 @@ import * as os from 'os' // For internal use, subject to change. +// We use any as a valid input type +/* eslint-disable @typescript-eslint/no-explicit-any */ + interface CommandProperties { - [key: string]: string + [key: string]: any } /** @@ -19,7 +22,7 @@ interface CommandProperties { export function issueCommand( command: string, properties: CommandProperties, - message: string + message: any ): void { const cmd = new Command(command, properties, message) process.stdout.write(cmd.toString() + os.EOL) @@ -73,15 +76,28 @@ class Command { } } -function escapeData(s: string): string { - return (s || '') +/** + * Sanatizes an input into a string so it can be passed into issueCommand safely + * @param input input to sanitize into a string + */ +export function toCommandValue(input: any): string { + if (input === null || input === undefined) { + return '' + } else if (typeof input === 'string' || input instanceof String) { + return input as string + } + return JSON.stringify(input) +} + +function escapeData(s: any): string { + return toCommandValue(s) .replace(/%/g, '%25') .replace(/\r/g, '%0D') .replace(/\n/g, '%0A') } -function escapeProperty(s: string): string { - return (s || '') +function escapeProperty(s: any): string { + return toCommandValue(s) .replace(/%/g, '%25') .replace(/\r/g, '%0D') .replace(/\n/g, '%0A') diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index 9072f46a79..85fc547a96 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -1,4 +1,4 @@ -import {issue, issueCommand} from './command' +import {issue, issueCommand, toCommandValue} from './command' import * as os from 'os' import * as path from 'path' @@ -33,11 +33,13 @@ export enum ExitCode { /** * Sets env variable for this action and future actions in the job * @param name the name of the variable to set - * @param val the value of the variable + * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify */ -export function exportVariable(name: string, val: string): void { - process.env[name] = val - issueCommand('set-env', {name}, val) +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function exportVariable(name: string, val: any): void { + const convertedVal = toCommandValue(val) + process.env[name] = convertedVal + issueCommand('set-env', {name}, convertedVal) } /** @@ -78,9 +80,10 @@ export function getInput(name: string, options?: InputOptions): string { * Sets the value of an output. * * @param name name of the output to set - * @param value value to store + * @param value value to store. Non-string values will be converted to a string via JSON.stringify */ -export function setOutput(name: string, value: string): void { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function setOutput(name: string, value: any): void { issueCommand('set-output', {name}, value) } @@ -93,8 +96,9 @@ export function setOutput(name: string, value: string): void { * When the action exits it will be with an exit code of 1 * @param message add error issue message */ -export function setFailed(message: string): void { +export function setFailed(message: string | Error): void { process.exitCode = ExitCode.Failure + error(message) } @@ -119,18 +123,18 @@ export function debug(message: string): void { /** * Adds an error issue - * @param message error issue message + * @param message error issue message. Errors will be converted to string via toString() */ -export function error(message: string): void { - issue('error', message) +export function error(message: string | Error): void { + issue('error', message instanceof Error ? message.toString() : message) } /** * Adds an warning issue - * @param message warning issue message + * @param message warning issue message. Errors will be converted to string via toString() */ -export function warning(message: string): void { - issue('warning', message) +export function warning(message: string | Error): void { + issue('warning', message instanceof Error ? message.toString() : message) } /** @@ -189,9 +193,10 @@ export async function group(name: string, fn: () => Promise): Promise { * Saves state for current action, the state can only be retrieved by this action's post job execution. * * @param name name of the state to store - * @param value value to store + * @param value value to store. Non-string values will be converted to a string via JSON.stringify */ -export function saveState(name: string, value: string): void { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function saveState(name: string, value: any): void { issueCommand('save-state', {name}, value) } From 3c125ce4e0d4893c10c4093562d27eafbca595c5 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Mon, 13 Apr 2020 10:19:49 -0400 Subject: [PATCH 117/192] Update eslint to 2.2.7 (#410) --- .eslintrc.json | 2 +- package-lock.json | 161 +++++++++++++++++++++-- package.json | 2 +- packages/github/__tests__/github.test.ts | 2 +- tsconfig.eslint.json | 6 + 5 files changed, 162 insertions(+), 11 deletions(-) create mode 100644 tsconfig.eslint.json diff --git a/.eslintrc.json b/.eslintrc.json index 02c79de1a0..2f73a3cf2a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,7 +5,7 @@ "parserOptions": { "ecmaVersion": 9, "sourceType": "module", - "project": "./tsconfig.json" + "project": "./tsconfig.eslint.json" }, "rules": { "eslint-comments/no-use": "off", diff --git a/package-lock.json b/package-lock.json index 21b3357186..7c24ec0efe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3886,6 +3886,12 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -3943,6 +3949,12 @@ "integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==", "dev": true }, + "@types/json-schema": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "dev": true + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -3995,6 +4007,20 @@ "regexpp": "^2.0.1", "requireindex": "^1.2.0", "tsutils": "^3.7.0" + }, + "dependencies": { + "@typescript-eslint/parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.9.0.tgz", + "integrity": "sha512-CWgC1XrQ34H/+LwAU7vY5xteZDkNqeAkeidEpJnJgkKu0yqQ3ZhQ7S+dI6MX4vmmM1TKRbOrKuXc6W0fIHhdbA==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "1.9.0", + "@typescript-eslint/typescript-estree": "1.9.0", + "eslint-scope": "^4.0.0", + "eslint-visitor-keys": "^1.0.0" + } + } } }, "@typescript-eslint/experimental-utils": { @@ -4007,15 +4033,98 @@ } }, "@typescript-eslint/parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.9.0.tgz", - "integrity": "sha512-CWgC1XrQ34H/+LwAU7vY5xteZDkNqeAkeidEpJnJgkKu0yqQ3ZhQ7S+dI6MX4vmmM1TKRbOrKuXc6W0fIHhdbA==", + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.27.0.tgz", + "integrity": "sha512-HFUXZY+EdwrJXZo31DW4IS1ujQW3krzlRjBrFRrJcMDh0zCu107/nRfhk/uBasO8m0NVDbBF5WZKcIUMRO7vPg==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "1.9.0", - "@typescript-eslint/typescript-estree": "1.9.0", - "eslint-scope": "^4.0.0", - "eslint-visitor-keys": "^1.0.0" + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "2.27.0", + "@typescript-eslint/typescript-estree": "2.27.0", + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "@typescript-eslint/experimental-utils": { + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.27.0.tgz", + "integrity": "sha512-vOsYzjwJlY6E0NJRXPTeCGqjv5OHgRU1kzxHKWJVPjDYGbPgLudBXjIlc+OD1hDBZ4l1DLbOc5VjofKahsu9Jw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.27.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.27.0.tgz", + "integrity": "sha512-t2miCCJIb/FU8yArjAvxllxbTiyNqaXJag7UOpB5DVoM3+xnjeOngtqlJkLRnMtzaRcJhe3CIR9RmL40omubhg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^6.3.0", + "tsutils": "^3.17.1" + } + }, + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", + "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.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 + }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } } }, "@typescript-eslint/typescript-estree": { @@ -4064,7 +4173,8 @@ "acorn": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", - "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==" + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", + "dev": true }, "acorn-globals": { "version": "4.3.4", @@ -6562,6 +6672,41 @@ "prettier": ">=1.12.0", "read-pkg-up": "^4.0.0", "svg-element-attributes": "^1.2.1" + }, + "dependencies": { + "@typescript-eslint/experimental-utils": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz", + "integrity": "sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "1.13.0", + "eslint-scope": "^4.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.13.0.tgz", + "integrity": "sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "1.13.0", + "@typescript-eslint/typescript-estree": "1.13.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz", + "integrity": "sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==", + "dev": true, + "requires": { + "lodash.unescape": "4.0.1", + "semver": "5.5.0" + } + } } }, "eslint-plugin-graphql": { diff --git a/package.json b/package.json index cd525a688d..5b52d7fbff 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@types/jest": "^24.0.11", "@types/node": "^11.13.5", "@types/signale": "^1.2.1", - "@typescript-eslint/parser": "^1.9.0", + "@typescript-eslint/parser": "^2.2.7", "concurrently": "^4.1.0", "eslint": "^5.16.0", "eslint-plugin-github": "^2.0.0", diff --git a/packages/github/__tests__/github.test.ts b/packages/github/__tests__/github.test.ts index 0d53095627..bf81b07ace 100644 --- a/packages/github/__tests__/github.test.ts +++ b/packages/github/__tests__/github.test.ts @@ -11,7 +11,7 @@ describe('@actions/github', () => { beforeAll(async () => { // Start proxy server - proxyServer = proxy() as http.Server + proxyServer = proxy() await new Promise(resolve => { const port = Number(proxyUrl.split(':')[2]) proxyServer.listen(port, () => resolve()) diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 0000000000..d18952e867 --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "node_modules" + ] +} \ No newline at end of file From 05e39f551d33e1688f61b209ab5cdd335198f1b8 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Mon, 13 Apr 2020 13:25:54 -0400 Subject: [PATCH 118/192] Add docs and wrapper for "echo" command (#411) * Add docs and wrapper for "echo" command * Update parameter to enabled --- docs/commands.md | 25 ++++++++++++++++++++++++- packages/core/__tests__/core.test.ts | 10 ++++++++++ packages/core/src/core.ts | 9 +++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/docs/commands.md b/docs/commands.md index d1e438e253..186f4b24a9 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -122,7 +122,7 @@ echo "::save-state name=FOO::foovalue" ### Log Level -Finally, there are several commands to emit different levels of log output: +There are several commands to emit different levels of log output: | log level | example usage | |---|---| @@ -130,6 +130,29 @@ Finally, there are several commands to emit different levels of log output: | warning | `echo "::warning::My warning message"` | | error | `echo "::error::My error message"` | +### Command Echoing +By default, the echoing of commands to stdout only occurs if [Step Debugging is enabled](./actions-debugging.md#How-to-Access-Step-Debug-Logs) + +You can enable or disable this for the current step by using the `echo` command. + +```bash +echo "::echo::on" +``` + +You can also disable echoing. + +```bash +echo "::echo::off" +``` + +This is wrapped by the core method: + +```javascript +function setCommandEcho(enabled: boolean): void {} +``` + +The `add-mask`, `debug`, `warning` and `error` commands do not support echoing. + ### Command Prompt CMD processes the `"` character differently from other shells when echoing. In CMD, the above snippets should have the `"` characters removed in order to correctly process. For example, the set output command would be: ```cmd diff --git a/packages/core/__tests__/core.test.ts b/packages/core/__tests__/core.test.ts index d10e0590d8..2901065262 100644 --- a/packages/core/__tests__/core.test.ts +++ b/packages/core/__tests__/core.test.ts @@ -239,6 +239,16 @@ describe('@actions/core', () => { process.env['RUNNER_DEBUG'] = current } }) + + it('setCommandEcho can enable echoing', () => { + core.setCommandEcho(true) + assertWriteCalls([`::echo::on${os.EOL}`]) + }) + + it('setCommandEcho can disable echoing', () => { + core.setCommandEcho(false) + assertWriteCalls([`::echo::off${os.EOL}`]) + }) }) // Assert that process.stdout.write calls called only with the given arguments. diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index 85fc547a96..0cf9f04ca7 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -87,6 +87,15 @@ export function setOutput(name: string, value: any): void { issueCommand('set-output', {name}, value) } +/** + * Enables or disables the echoing of commands into stdout for the rest of the step. + * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. + * + */ +export function setCommandEcho(enabled: boolean): void { + issue('echo', enabled ? 'on' : 'off') +} + //----------------------------------------------------------------------- // Results //----------------------------------------------------------------------- From c4b6011310026099af025aeccc4c0f46ddf001f1 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Mon, 13 Apr 2020 13:39:42 -0400 Subject: [PATCH 119/192] Allow specifying stdin (#360) * Allow specifying stdin --- packages/exec/__tests__/exec.test.ts | 46 +++++++++++++++++++ .../exec/__tests__/scripts/wait-for-input.cmd | 3 ++ .../exec/__tests__/scripts/wait-for-input.js | 3 ++ .../exec/__tests__/scripts/wait-for-input.sh | 4 ++ packages/exec/src/interfaces.ts | 3 ++ packages/exec/src/toolrunner.ts | 8 ++++ 6 files changed, 67 insertions(+) create mode 100755 packages/exec/__tests__/scripts/wait-for-input.cmd create mode 100755 packages/exec/__tests__/scripts/wait-for-input.js create mode 100755 packages/exec/__tests__/scripts/wait-for-input.sh diff --git a/packages/exec/__tests__/exec.test.ts b/packages/exec/__tests__/exec.test.ts index 387c83f5ff..f4dfab0ff9 100644 --- a/packages/exec/__tests__/exec.test.ts +++ b/packages/exec/__tests__/exec.test.ts @@ -286,6 +286,52 @@ describe('@actions/exec', () => { expect(stderrCalled).toBeTruthy() }) + it('Handles stdin shell', async () => { + let command: string + if (IS_WINDOWS) { + command = 'wait-for-input.cmd' + } else { + command = 'wait-for-input.sh' + } + + const waitForInput: string = path.join(__dirname, 'scripts', command) + + const _testExecOptions = getExecOptions() + + _testExecOptions.listeners = { + stdout: (data: Buffer) => { + expect(data).toEqual(Buffer.from(`this is my input${os.EOL}`)) + } + } + + _testExecOptions.input = Buffer.from('this is my input') + + const exitCode = await exec.exec(`"${waitForInput}"`, [], _testExecOptions) + expect(exitCode).toBe(0) + }) + + it('Handles stdin js', async () => { + const waitForInput: string = path.join( + __dirname, + 'scripts', + 'wait-for-input.js' + ) + + const _testExecOptions = getExecOptions() + + _testExecOptions.listeners = { + stdout: (data: Buffer) => { + expect(data).toEqual(Buffer.from(`this is my input`)) + } + } + + _testExecOptions.input = Buffer.from('this is my input') + + const nodePath = await io.which('node', true) + const exitCode = await exec.exec(nodePath, [waitForInput], _testExecOptions) + expect(exitCode).toBe(0) + }) + it('Handles child process holding streams open', async function() { const semaphorePath = path.join( getTestTemp(), diff --git a/packages/exec/__tests__/scripts/wait-for-input.cmd b/packages/exec/__tests__/scripts/wait-for-input.cmd new file mode 100755 index 0000000000..1ba3ec4bcf --- /dev/null +++ b/packages/exec/__tests__/scripts/wait-for-input.cmd @@ -0,0 +1,3 @@ +@echo off +set /p var= +echo %var% diff --git a/packages/exec/__tests__/scripts/wait-for-input.js b/packages/exec/__tests__/scripts/wait-for-input.js new file mode 100755 index 0000000000..82b1e3077e --- /dev/null +++ b/packages/exec/__tests__/scripts/wait-for-input.js @@ -0,0 +1,3 @@ +var fs = require('fs') +var data = fs.readFileSync(0, 'utf-8') +process.stdout.write(data) diff --git a/packages/exec/__tests__/scripts/wait-for-input.sh b/packages/exec/__tests__/scripts/wait-for-input.sh new file mode 100755 index 0000000000..1e8cd850b0 --- /dev/null +++ b/packages/exec/__tests__/scripts/wait-for-input.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +read var +echo $var diff --git a/packages/exec/src/interfaces.ts b/packages/exec/src/interfaces.ts index e9219b0b40..436fc0ac7c 100644 --- a/packages/exec/src/interfaces.ts +++ b/packages/exec/src/interfaces.ts @@ -30,6 +30,9 @@ export interface ExecOptions { /** optional. How long in ms to wait for STDIO streams to close after the exit event of the process before terminating. defaults to 10000 */ delay?: number + /** optional. input to write to the process on STDIN. */ + input?: Buffer + /** optional. Listeners for output. Callback functions that will be called on these events */ listeners?: { stdout?: (data: Buffer) => void diff --git a/packages/exec/src/toolrunner.ts b/packages/exec/src/toolrunner.ts index c578b25867..01e7cd1f5a 100644 --- a/packages/exec/src/toolrunner.ts +++ b/packages/exec/src/toolrunner.ts @@ -524,6 +524,14 @@ export class ToolRunner extends events.EventEmitter { resolve(exitCode) } }) + + if (this.options.input) { + if (!cp.stdin) { + throw new Error('child process missing stdin') + } + + cp.stdin.end(this.options.input) + } }) } } From a28977e977dcbf2b05667c188e8554905f0e0837 Mon Sep 17 00:00:00 2001 From: Pierre Grimaud Date: Mon, 20 Apr 2020 12:56:03 +0200 Subject: [PATCH 120/192] Fix typos (#417) --- docs/adrs/0381-glob-module.md | 2 +- packages/artifact/src/internal/status-reporter.ts | 2 +- packages/core/__tests__/command.test.ts | 2 +- packages/glob/__tests__/internal-pattern.test.ts | 2 +- packages/glob/src/internal-globber.ts | 2 +- packages/glob/src/internal-pattern.ts | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/adrs/0381-glob-module.md b/docs/adrs/0381-glob-module.md index af2c324542..a61fd8bb15 100644 --- a/docs/adrs/0381-glob-module.md +++ b/docs/adrs/0381-glob-module.md @@ -37,7 +37,7 @@ export function create( */ export interface Globber { /** - * Returns the search path preceeding the first glob segment, from each pattern. + * Returns the search path preceding the first glob segment, from each pattern. * Duplicates and descendants of other paths are filtered out. * * Example 1: The patterns `/foo/*` and `/bar/*` returns `/foo` and `/bar`. diff --git a/packages/artifact/src/internal/status-reporter.ts b/packages/artifact/src/internal/status-reporter.ts index 681dcfc92f..b890ad8a39 100644 --- a/packages/artifact/src/internal/status-reporter.ts +++ b/packages/artifact/src/internal/status-reporter.ts @@ -49,7 +49,7 @@ export class StatusReporter { for (const value of Array.from(this.largeFiles.values())) { info(value) } - // delete all entires in the map after displaying the information so it will not be displayed again unless explicitly added + // delete all entries in the map after displaying the information so it will not be displayed again unless explicitly added this.largeFiles.clear() }, 1000) } diff --git a/packages/core/__tests__/command.test.ts b/packages/core/__tests__/command.test.ts index da3df23a4e..c5bb1dd550 100644 --- a/packages/core/__tests__/command.test.ts +++ b/packages/core/__tests__/command.test.ts @@ -113,7 +113,7 @@ describe('@actions/core/src/command', () => { ]) }) - it('should handle issueing commands for non-string objects', () => { + it('should handle issuing commands for non-string objects', () => { command.issueCommand( 'some-command', { diff --git a/packages/glob/__tests__/internal-pattern.test.ts b/packages/glob/__tests__/internal-pattern.test.ts index 5a2fbcc32d..11b0507d79 100644 --- a/packages/glob/__tests__/internal-pattern.test.ts +++ b/packages/glob/__tests__/internal-pattern.test.ts @@ -69,7 +69,7 @@ describe('pattern', () => { } }) - it('globstar matches immediately preceeding directory', () => { + it('globstar matches immediately preceding directory', () => { const root = IS_WINDOWS ? 'C:\\' : '/' const pattern = new Pattern(`${root}foo/bar/**`) const actual = [ diff --git a/packages/glob/src/internal-globber.ts b/packages/glob/src/internal-globber.ts index a84d298d24..aac3032d1b 100644 --- a/packages/glob/src/internal-globber.ts +++ b/packages/glob/src/internal-globber.ts @@ -17,7 +17,7 @@ export {GlobOptions} */ export interface Globber { /** - * Returns the search path preceeding the first glob segment, from each pattern. + * Returns the search path preceding the first glob segment, from each pattern. * Duplicates and descendants of other paths are filtered out. * * Example 1: The patterns `/foo/*` and `/bar/*` returns `/foo` and `/bar`. diff --git a/packages/glob/src/internal-pattern.ts b/packages/glob/src/internal-pattern.ts index 348acca878..1e2a992f8f 100644 --- a/packages/glob/src/internal-pattern.ts +++ b/packages/glob/src/internal-pattern.ts @@ -21,7 +21,7 @@ export class Pattern { /** * The path/pattern segments. Note, only the first segment (the root directory) - * may contain a directory separator charactor. Use the trailingSeparator field + * may contain a directory separator character. Use the trailingSeparator field * to determine whether the pattern ended with a trailing slash. */ readonly segments: string[] @@ -130,7 +130,7 @@ export class Pattern { itemPath = pathHelper.normalizeSeparators(itemPath) // Append a trailing slash. Otherwise Minimatch will not match the directory immediately - // preceeding the globstar. For example, given the pattern `/foo/**`, Minimatch returns + // preceding the globstar. For example, given the pattern `/foo/**`, Minimatch returns // false for `/foo` but returns true for `/foo/`. Append a trailing slash to handle that quirk. if (!itemPath.endsWith(path.sep)) { // Note, this is safe because the constructor ensures the pattern has an absolute root. From bb1053a8a7b45d0972470591c2355e6e80df4088 Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Mon, 20 Apr 2020 22:58:53 +0200 Subject: [PATCH 121/192] @actions/artifact 0.3.1 update (#420) * Updates to 0.3.1 package update --- packages/artifact/README.md | 1 + packages/artifact/RELEASES.md | 5 + packages/artifact/__tests__/util.test.ts | 2 - .../artifact/docs/additional-information.md | 1 - .../artifact/docs/implementation-details.md | 2 +- packages/artifact/package-lock.json | 2 +- packages/artifact/package.json | 2 +- .../src/internal/upload-http-client.ts | 148 +++++++++--------- packages/artifact/src/internal/utils.ts | 11 +- 9 files changed, 81 insertions(+), 93 deletions(-) diff --git a/packages/artifact/README.md b/packages/artifact/README.md index 0fb2c3ff03..197f80a591 100644 --- a/packages/artifact/README.md +++ b/packages/artifact/README.md @@ -7,6 +7,7 @@ You can use this package to interact with the actions artifacts. - [Download a Single Artifact](#Download-a-Single-Artifact) - [Download All Artifacts](#Download-all-Artifacts) - [Additional Documentation](#Additional-Documentation) +- [Contributions](#Contributions) Relative paths and absolute paths are both allowed. Relative paths are rooted against the current working directory. diff --git a/packages/artifact/RELEASES.md b/packages/artifact/RELEASES.md index 577cd43a08..cd83e65a26 100644 --- a/packages/artifact/RELEASES.md +++ b/packages/artifact/RELEASES.md @@ -19,3 +19,8 @@ - Exponential backoff when retryable status codes are encountered - Clearer error message if storage quota has been reached - Improved logging and output during artifact download + +### 0.3.1 + +- Fix to ensure temporary gzip files get correctly deleted during artifact upload +- Remove spaces as a forbidden character during upload diff --git a/packages/artifact/__tests__/util.test.ts b/packages/artifact/__tests__/util.test.ts index 889777eea5..276cfa9d77 100644 --- a/packages/artifact/__tests__/util.test.ts +++ b/packages/artifact/__tests__/util.test.ts @@ -57,7 +57,6 @@ describe('Utils', () => { 'my|artifact', 'my*artifact', 'my?artifact', - 'my artifact', '' ] for (const invalidName of invalidNames) { @@ -87,7 +86,6 @@ describe('Utils', () => { 'some/invalid|artifact/path', 'some/invalid*artifact/path', 'some/invalid?artifact/path', - 'some/invalid artifact/path', '' ] for (const invalidName of invalidNames) { diff --git a/packages/artifact/docs/additional-information.md b/packages/artifact/docs/additional-information.md index 42b9c8b225..709e4380db 100644 --- a/packages/artifact/docs/additional-information.md +++ b/packages/artifact/docs/additional-information.md @@ -17,7 +17,6 @@ When uploading an artifact, the inputted `name` parameter along with the files s - | - \* - ? -- empty space In addition to the aforementioned characters, the inputted `name` also cannot include the following - \ diff --git a/packages/artifact/docs/implementation-details.md b/packages/artifact/docs/implementation-details.md index 3bf09e5c15..bed96f0bf4 100644 --- a/packages/artifact/docs/implementation-details.md +++ b/packages/artifact/docs/implementation-details.md @@ -4,7 +4,7 @@ Warning: Implementation details may change at any time without notice. This is m ## Upload/Compression flow -![image](https://user-images.githubusercontent.com/16109154/77190819-38685d80-6ada-11ea-8281-4703ff8cc025.png) +![image](https://user-images.githubusercontent.com/16109154/79765587-19522b00-8327-11ea-9679-410bb10e1b13.png) ## Retry Logic when downloading an individual file diff --git a/packages/artifact/package-lock.json b/packages/artifact/package-lock.json index 43d68b4378..5e6ea9fced 100644 --- a/packages/artifact/package-lock.json +++ b/packages/artifact/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/artifact", - "version": "0.3.0", + "version": "0.3.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/artifact/package.json b/packages/artifact/package.json index b070e220ea..16f136c48f 100644 --- a/packages/artifact/package.json +++ b/packages/artifact/package.json @@ -1,6 +1,6 @@ { "name": "@actions/artifact", - "version": "0.3.0", + "version": "0.3.1", "preview": true, "description": "Actions artifact lib", "keywords": [ diff --git a/packages/artifact/src/internal/upload-http-client.ts b/packages/artifact/src/internal/upload-http-client.ts index cae5b526bb..deb2b540c9 100644 --- a/packages/artifact/src/internal/upload-http-client.ts +++ b/packages/artifact/src/internal/upload-http-client.ts @@ -248,91 +248,85 @@ export class UploadHttpClient { } } else { // the file that is being uploaded is greater than 64k in size, a temporary file gets created on disk using the - // npm tmp-promise package and this file gets used during compression for the GZip file that gets created - return tmp - .file() - .then(async tmpFile => { - // create a GZip file of the original file being uploaded, the original file should not be modified in any way - uploadFileSize = await createGZipFileOnDisk( + // npm tmp-promise package and this file gets used to create a GZipped file + const tempFile = await tmp.file() + + // create a GZip file of the original file being uploaded, the original file should not be modified in any way + uploadFileSize = await createGZipFileOnDisk( + parameters.file, + tempFile.path + ) + + let uploadFilePath = tempFile.path + + // compression did not help with size reduction, use the original file for upload and delete the temp GZip file + if (totalFileSize < uploadFileSize) { + uploadFileSize = totalFileSize + uploadFilePath = parameters.file + isGzip = false + } + + let abortFileUpload = false + // upload only a single chunk at a time + while (offset < uploadFileSize) { + const chunkSize = Math.min( + uploadFileSize - offset, + parameters.maxChunkSize + ) + + // if an individual file is greater than 100MB (1024*1024*100) in size, display extra information about the upload status + if (uploadFileSize > 104857600) { + this.statusReporter.updateLargeFileStatus( parameters.file, - tmpFile.path + offset, + uploadFileSize ) - let uploadFilePath = tmpFile.path - - // compression did not help with size reduction, use the original file for upload and delete the temp GZip file - if (totalFileSize < uploadFileSize) { - uploadFileSize = totalFileSize - uploadFilePath = parameters.file - isGzip = false - tmpFile.cleanup() - } + } - let abortFileUpload = false - // upload only a single chunk at a time - while (offset < uploadFileSize) { - const chunkSize = Math.min( - uploadFileSize - offset, - parameters.maxChunkSize - ) + const start = offset + const end = offset + chunkSize - 1 + offset += parameters.maxChunkSize - // if an individual file is greater than 100MB (1024*1024*100) in size, display extra information about the upload status - if (uploadFileSize > 104857600) { - this.statusReporter.updateLargeFileStatus( - parameters.file, - offset, - uploadFileSize - ) - } + if (abortFileUpload) { + // if we don't want to continue in the event of an error, any pending upload chunks will be marked as failed + failedChunkSizes += chunkSize + continue + } - const start = offset - const end = offset + chunkSize - 1 - offset += parameters.maxChunkSize + const result = await this.uploadChunk( + httpClientIndex, + parameters.resourceUrl, + fs.createReadStream(uploadFilePath, { + start, + end, + autoClose: false + }), + start, + end, + uploadFileSize, + isGzip, + totalFileSize + ) - if (abortFileUpload) { - // if we don't want to continue in the event of an error, any pending upload chunks will be marked as failed - failedChunkSizes += chunkSize - continue - } + if (!result) { + // Chunk failed to upload, report as failed and do not continue uploading any more chunks for the file. It is possible that part of a chunk was + // successfully uploaded so the server may report a different size for what was uploaded + isUploadSuccessful = false + failedChunkSizes += chunkSize + core.warning(`Aborting upload for ${parameters.file} due to failure`) + abortFileUpload = true + } + } - const result = await this.uploadChunk( - httpClientIndex, - parameters.resourceUrl, - fs.createReadStream(uploadFilePath, { - start, - end, - autoClose: false - }), - start, - end, - uploadFileSize, - isGzip, - totalFileSize - ) + // Delete the temporary file that was created as part of the upload. If the temp file does not get manually deleted by + // calling cleanup, it gets removed when the node process exits. For more info see: https://www.npmjs.com/package/tmp-promise#about + await tempFile.cleanup() - if (!result) { - // Chunk failed to upload, report as failed and do not continue uploading any more chunks for the file. It is possible that part of a chunk was - // successfully uploaded so the server may report a different size for what was uploaded - isUploadSuccessful = false - failedChunkSizes += chunkSize - core.warning( - `Aborting upload for ${parameters.file} due to failure` - ) - abortFileUpload = true - } - } - }) - .then( - async (): Promise => { - // only after the file upload is complete and the temporary file is deleted, return the UploadResult - return new Promise(resolve => { - resolve({ - isSuccess: isUploadSuccessful, - successfulUploadSize: uploadFileSize - failedChunkSizes, - totalSize: totalFileSize - }) - }) - } - ) + return { + isSuccess: isUploadSuccessful, + successfulUploadSize: uploadFileSize - failedChunkSizes, + totalSize: totalFileSize + } } } diff --git a/packages/artifact/src/internal/utils.ts b/packages/artifact/src/internal/utils.ts index 41f4179dff..406867e559 100644 --- a/packages/artifact/src/internal/utils.ts +++ b/packages/artifact/src/internal/utils.ts @@ -245,16 +245,7 @@ Header Information: ${JSON.stringify(response.message.headers, undefined, 2)} * * FilePaths can include characters such as \ and / which are not permitted in the artifact name alone */ -const invalidArtifactFilePathCharacters = [ - '"', - ':', - '<', - '>', - '|', - '*', - '?', - ' ' -] +const invalidArtifactFilePathCharacters = ['"', ':', '<', '>', '|', '*', '?'] const invalidArtifactNameCharacters = [ ...invalidArtifactFilePathCharacters, '\\', From 1688b117e1fb7715c3879a29496a7562cf57dfca Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Thu, 23 Apr 2020 20:52:53 +0200 Subject: [PATCH 122/192] E2E tests for the @actions/artifact package (#421) * End-to-end artifact tests * E2E tests for artifact package --- .github/workflows/artifact-tests.yml | 79 +++++++++++++++++++ .../__tests__/ci-test-action/action.yml | 5 ++ .../__tests__/ci-test-action/index.js | 5 ++ packages/artifact/package-lock.json | 11 +++ packages/artifact/package.json | 3 + scripts/test-artifact-file.sh | 25 ++++++ 6 files changed, 128 insertions(+) create mode 100644 .github/workflows/artifact-tests.yml create mode 100644 packages/artifact/__tests__/ci-test-action/action.yml create mode 100644 packages/artifact/__tests__/ci-test-action/index.js create mode 100755 scripts/test-artifact-file.sh diff --git a/.github/workflows/artifact-tests.yml b/.github/workflows/artifact-tests.yml new file mode 100644 index 0000000000..8b45404a59 --- /dev/null +++ b/.github/workflows/artifact-tests.yml @@ -0,0 +1,79 @@ +name: artifact-unit-tests +on: push + +jobs: + build: + name: Build + + strategy: + matrix: + runs-on: [ubuntu-latest, windows-latest, macOS-latest] + fail-fast: false + + runs-on: ${{ matrix.runs-on }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Set Node.js 12.x + uses: actions/setup-node@v1 + with: + node-version: 12.x + + # In order to upload & download artifacts from a shell script, certain env variables need to be set that are only available in the + # node context. This runs a local action that gets and sets the necessary env variables that are needed + - name: Set env variables + uses: ./packages/artifact/__tests__/ci-test-action/ + + # Need root node_modules because certain npm packages like jest are configured for the entire repository and it won't be possible + # without these to just compile the artifacts package + - name: Install root npm packages + run: npm ci + + - name: Compile artifact package + run: | + npm ci + npm run tsc + working-directory: packages/artifact + + - name: Set artifact file contents + run: | + echo "::set-env name=non-gzip-artifact-content::hello" + echo "::set-env name=gzip-artifact-content::Some large amount of text that has a compression ratio that is greater than 100%. If greater than 100%, gzip is used to upload the file" + + - name: Create files that will be uploaded + run: | + mkdir artifact-path + echo ${{ env.non-gzip-artifact-content }} > artifact-path/world.txt + echo ${{ env.gzip-artifact-content }} > artifact-path/gzip.txt + + # We're using node -e to call the functions directly available in the @actions/artifact package + - name: Upload artifacts using uploadArtifact() + run: | + node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().uploadArtifact('my-artifact-1',['artifact-path/world.txt'], '${{ github.workspace }}'))" + node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().uploadArtifact('my-artifact-2',['artifact-path/gzip.txt'], '${{ github.workspace }}'))" + + - name: Download artifacts using downloadArtifact() + run: | + mkdir artifact-1-directory + node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().downloadArtifact('my-artifact-1','artifact-1-directory'))" + mkdir artifact-2-directory + node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().downloadArtifact('my-artifact-2','artifact-2-directory'))" + + - name: Verify downloadArtifact() + shell: bash + run: | + scripts/test-artifact-file.sh "artifact-1-directory/artifact-path/world.txt" "${{ env.non-gzip-artifact-content }}" + scripts/test-artifact-file.sh "artifact-2-directory/artifact-path/gzip.txt" "${{ env.gzip-artifact-content }}" + + - name: Download artifacts using downloadAllArtifacts() + run: | + mkdir multi-artifact-directory + node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().downloadAllArtifacts('multi-artifact-directory'))" + + - name: Verify downloadAllArtifacts() + shell: bash + run: | + scripts/test-artifact-file.sh "multi-artifact-directory/my-artifact-1/artifact-path/world.txt" "${{ env.non-gzip-artifact-content }}" + scripts/test-artifact-file.sh "multi-artifact-directory/my-artifact-2/artifact-path/gzip.txt" "${{ env.gzip-artifact-content }}" \ No newline at end of file diff --git a/packages/artifact/__tests__/ci-test-action/action.yml b/packages/artifact/__tests__/ci-test-action/action.yml new file mode 100644 index 0000000000..9f80bce9b0 --- /dev/null +++ b/packages/artifact/__tests__/ci-test-action/action.yml @@ -0,0 +1,5 @@ +name: 'Set env variables' +description: 'Sets certain env variables so that e2e artifact upload and download can be tested in a shell' +runs: + using: 'node12' + main: 'index.js' \ No newline at end of file diff --git a/packages/artifact/__tests__/ci-test-action/index.js b/packages/artifact/__tests__/ci-test-action/index.js new file mode 100644 index 0000000000..e2ff6aea20 --- /dev/null +++ b/packages/artifact/__tests__/ci-test-action/index.js @@ -0,0 +1,5 @@ +// Certain env variables are not set by default in a shell context and are only available in a node context from a running action +// In order to be able to upload and download artifacts e2e in a shell when running CI tests, we need these env variables set +console.log(`::set-env name=ACTIONS_RUNTIME_URL::${process.env.ACTIONS_RUNTIME_URL}`) +console.log(`::set-env name=ACTIONS_RUNTIME_TOKEN::${process.env.ACTIONS_RUNTIME_TOKEN}`) +console.log(`::set-env name=GITHUB_RUN_ID::${process.env.GITHUB_RUN_ID}`) \ No newline at end of file diff --git a/packages/artifact/package-lock.json b/packages/artifact/package-lock.json index 5e6ea9fced..4cccbe33cf 100644 --- a/packages/artifact/package-lock.json +++ b/packages/artifact/package-lock.json @@ -4,6 +4,11 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@actions/core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.3.tgz", + "integrity": "sha512-Wp4xnyokakM45Uuj4WLUxdsa8fJjKVl1fDTsPbTEcTcuu0Nb26IPQbOtjmnfaCPGcaoPOOqId8H9NapZ8gii4w==" + }, "@actions/http-client": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.7.tgz", @@ -118,6 +123,12 @@ "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "dev": true + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/packages/artifact/package.json b/packages/artifact/package.json index 16f136c48f..1068489d23 100644 --- a/packages/artifact/package.json +++ b/packages/artifact/package.json @@ -41,5 +41,8 @@ "@types/tmp": "^0.1.0", "tmp": "^0.1.0", "tmp-promise": "^2.0.2" + }, + "devDependencies": { + "typescript": "^3.8.3" } } diff --git a/scripts/test-artifact-file.sh b/scripts/test-artifact-file.sh new file mode 100755 index 0000000000..8b90c1ad1e --- /dev/null +++ b/scripts/test-artifact-file.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +path="$1" +expectedContent="$2" + +if [ "$path" == "" ]; then + echo "File path not provided" + exit 1 +fi + +if [ "$expectedContent" == "" ]; then + echo "Expected file contents not provided" + exit 1 +fi + +if [ ! -f "$path" ]; then + echo "Expected file $path does not exist" + exit 1 +fi + +actualContent=$(cat $path) +if [ "$actualContent" != "$expectedContent" ];then + echo "File contents are not correct, expected $expectedContent, recieved $actualContent" + exit 1 +fi \ No newline at end of file From eec6689a6199552572cb47edfed2edd1634b84e7 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Thu, 23 Apr 2020 21:12:00 -0400 Subject: [PATCH 123/192] bump tool-cache's http-client to 1.0.8 (#429) --- packages/tool-cache/RELEASES.md | 6 ++++++ packages/tool-cache/package-lock.json | 26 ++++++++++++++++++++++---- packages/tool-cache/package.json | 4 ++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/packages/tool-cache/RELEASES.md b/packages/tool-cache/RELEASES.md index 7541b4a7de..3bbe764c87 100644 --- a/packages/tool-cache/RELEASES.md +++ b/packages/tool-cache/RELEASES.md @@ -1,5 +1,11 @@ # @actions/tool-cache Releases +### 1.3.4 + +- [Update the http-client to 1.0.8 which had a security fix](https://github.com/actions/toolkit/pull/429) + +Here is [the security issue](https://github.com/actions/http-client/pull/27) that was fixed in the http-client 1.0.8 release + ### 1.3.3 - [Update downloadTool to only retry 500s and 408 and 429](https://github.com/actions/toolkit/pull/373) diff --git a/packages/tool-cache/package-lock.json b/packages/tool-cache/package-lock.json index 0910c9ba3c..ac351347c7 100644 --- a/packages/tool-cache/package-lock.json +++ b/packages/tool-cache/package-lock.json @@ -1,17 +1,35 @@ { "name": "@actions/tool-cache", - "version": "1.3.2", + "version": "1.3.4", "lockfileVersion": 1, "requires": true, "dependencies": { - "@actions/http-client": { + "@actions/core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.3.tgz", + "integrity": "sha512-Wp4xnyokakM45Uuj4WLUxdsa8fJjKVl1fDTsPbTEcTcuu0Nb26IPQbOtjmnfaCPGcaoPOOqId8H9NapZ8gii4w==" + }, + "@actions/exec": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.3.tgz", - "integrity": "sha512-wFwh1U4adB/Zsk4cc9kVqaBOHoknhp/pJQk+aWTocbAZWpIl4Zx/At83WFRLXvxB+5HVTWOACM6qjULMZfQSfw==", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.3.tgz", + "integrity": "sha512-TogJGnueOmM7ntCi0ASTUj4LapRRtDfj57Ja4IhPmg2fls28uVOPbAn8N+JifaOumN2UG3oEO/Ixek2A4NcYSA==", + "requires": { + "@actions/io": "^1.0.1" + } + }, + "@actions/http-client": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz", + "integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==", "requires": { "tunnel": "0.0.6" } }, + "@actions/io": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", + "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==" + }, "@types/nock": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/@types/nock/-/nock-10.0.3.tgz", diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index 585e403818..4df39c9963 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.3.3", + "version": "1.3.4", "description": "Actions tool-cache lib", "keywords": [ "github", @@ -38,7 +38,7 @@ "dependencies": { "@actions/core": "^1.2.0", "@actions/exec": "^1.0.0", - "@actions/http-client": "^1.0.3", + "@actions/http-client": "^1.0.8", "@actions/io": "^1.0.1", "semver": "^6.1.0", "uuid": "^3.3.2" From df7e2c13c89ea93069cad62f1adfe78a87111cc0 Mon Sep 17 00:00:00 2001 From: per1234 Date: Fri, 24 Apr 2020 02:55:31 -0700 Subject: [PATCH 124/192] Fix broken links in documentation (#425) --- docs/action-debugging.md | 4 ++-- docs/commands.md | 2 +- packages/github/README.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/action-debugging.md b/docs/action-debugging.md index 652269203a..d58346936d 100644 --- a/docs/action-debugging.md +++ b/docs/action-debugging.md @@ -11,7 +11,7 @@ Additional log events with the prefix `::debug::` will now also appear in the jo ### How to Access Step Debug Logs This flag can be enabled by [setting the secret](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets#creating-encrypted-secrets) `ACTIONS_STEP_DEBUG` to `true`. -All actions ran while this secret is enabled will show debug events in the [Downloaded Logs](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#downloading-logs-and-artifacts) and [Web Logs](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#viewing-logs-to-diagnose-failures). +All actions ran while this secret is enabled will show debug events in the [Downloaded Logs](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#downloading-logs) and [Web Logs](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#viewing-logs-to-diagnose-failures). ## Runner Diagnostic Logs Runner Diagnostic Logs provide additional log files detailing how the Runner is executing an action. @@ -27,5 +27,5 @@ These files contain the prefix `Runner_` or `Worker_` to indicate the log source ### How to Access Runner Diagnostic Logs These log files are enabled by [setting the secret](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets#creating-encrypted-secrets) `ACTIONS_RUNNER_DEBUG` to `true`. -All actions ran while this secret is enabled contain additional diagnostic log files in the `runner-diagnostic-logs` folder of the [log archive](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#downloading-logs-and-artifacts). +All actions ran while this secret is enabled contain additional diagnostic log files in the `runner-diagnostic-logs` folder of the [log archive](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#downloading-logs). diff --git a/docs/commands.md b/docs/commands.md index 186f4b24a9..bc91edc7e9 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -131,7 +131,7 @@ There are several commands to emit different levels of log output: | error | `echo "::error::My error message"` | ### Command Echoing -By default, the echoing of commands to stdout only occurs if [Step Debugging is enabled](./actions-debugging.md#How-to-Access-Step-Debug-Logs) +By default, the echoing of commands to stdout only occurs if [Step Debugging is enabled](./action-debugging.md#How-to-Access-Step-Debug-Logs) You can enable or disable this for the current step by using the `echo` command. diff --git a/packages/github/README.md b/packages/github/README.md index d6cf49b1e2..ce606c1945 100644 --- a/packages/github/README.md +++ b/packages/github/README.md @@ -4,7 +4,7 @@ ## Usage -Returns an authenticated Octokit client that follows the machine [proxy settings](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners#using-a-proxy-server-with-self-hosted-runners). See https://octokit.github.io/rest.js for the API. +Returns an authenticated Octokit client that follows the machine [proxy settings](https://help.github.com/en/actions/hosting-your-own-runners/using-a-proxy-server-with-self-hosted-runners). See https://octokit.github.io/rest.js for the API. ```js const github = require('@actions/github'); From 7257597d731b34d14090db516d9ea53439300e98 Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Mon, 27 Apr 2020 09:13:56 -0400 Subject: [PATCH 125/192] Spelling (#431) * spelling: absolute * spelling: content * spelling: received * spelling: sanitizes --- packages/core/src/command.ts | 2 +- packages/glob/src/internal-pattern.ts | 2 +- packages/io/__tests__/io.test.ts | 4 ++-- scripts/test-artifact-file.sh | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/command.ts b/packages/core/src/command.ts index 57067a1a52..acdf9def37 100644 --- a/packages/core/src/command.ts +++ b/packages/core/src/command.ts @@ -77,7 +77,7 @@ class Command { } /** - * Sanatizes an input into a string so it can be passed into issueCommand safely + * Sanitizes an input into a string so it can be passed into issueCommand safely * @param input input to sanitize into a string */ export function toCommandValue(input: any): string { diff --git a/packages/glob/src/internal-pattern.ts b/packages/glob/src/internal-pattern.ts index 1e2a992f8f..72e3dc7137 100644 --- a/packages/glob/src/internal-pattern.ts +++ b/packages/glob/src/internal-pattern.ts @@ -134,7 +134,7 @@ export class Pattern { // false for `/foo` but returns true for `/foo/`. Append a trailing slash to handle that quirk. if (!itemPath.endsWith(path.sep)) { // Note, this is safe because the constructor ensures the pattern has an absolute root. - // For example, formats like C: and C:foo on Windows are resolved to an aboslute root. + // For example, formats like C: and C:foo on Windows are resolved to an absolute root. itemPath = `${itemPath}${path.sep}` } } else { diff --git a/packages/io/__tests__/io.test.ts b/packages/io/__tests__/io.test.ts index 804389592e..76d07f5b17 100644 --- a/packages/io/__tests__/io.test.ts +++ b/packages/io/__tests__/io.test.ts @@ -778,7 +778,7 @@ describe('mkdirP', () => { await io.mkdirP(getTestTemp()) await fs.mkdir(rootPath) await fs.mkdir(realDirPath) - await fs.writeFile(realFilePath, 'test real_dir/file.txt contet') + await fs.writeFile(realFilePath, 'test real_dir/file.txt content') await createSymlinkDir(realDirPath, symlinkDirPath) await io.mkdirP(symlinkDirPath) @@ -802,7 +802,7 @@ describe('mkdirP', () => { await io.mkdirP(getTestTemp()) await fs.mkdir(rootPath) await fs.mkdir(realDirPath) - await fs.writeFile(realFilePath, 'test real_dir/file.txt contet') + await fs.writeFile(realFilePath, 'test real_dir/file.txt content') await createSymlinkDir(realDirPath, symlinkDirPath) const subDirPath = path.join(symlinkDirPath, 'sub_dir') diff --git a/scripts/test-artifact-file.sh b/scripts/test-artifact-file.sh index 8b90c1ad1e..708dc8070a 100755 --- a/scripts/test-artifact-file.sh +++ b/scripts/test-artifact-file.sh @@ -20,6 +20,6 @@ fi actualContent=$(cat $path) if [ "$actualContent" != "$expectedContent" ];then - echo "File contents are not correct, expected $expectedContent, recieved $actualContent" + echo "File contents are not correct, expected $expectedContent, received $actualContent" exit 1 fi \ No newline at end of file From 1e88dec883669909f1b4ad1000646e0d5c380166 Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Mon, 27 Apr 2020 17:17:31 +0200 Subject: [PATCH 126/192] Update ts-jest to latest versions (#419) --- package-lock.json | 92 ++++++++++++++++++++++++++++++++++++++--------- package.json | 2 +- tsconfig.json | 3 +- 3 files changed, 78 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7c24ec0efe..f6a43d03e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16370,6 +16370,12 @@ "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", "dev": true }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, "lodash.set": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", @@ -16481,9 +16487,9 @@ } }, "make-error": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", - "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, "make-fetch-happen": { @@ -19184,35 +19190,87 @@ "dev": true }, "ts-jest": { - "version": "24.0.2", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.0.2.tgz", - "integrity": "sha512-h6ZCZiA1EQgjczxq+uGLXQlNgeg02WWJBbeT8j6nyIBRQdglqbvzDoHahTEIiS6Eor6x8mK6PfZ7brQ9Q6tzHw==", + "version": "25.4.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-25.4.0.tgz", + "integrity": "sha512-+0ZrksdaquxGUBwSdTIcdX7VXdwLIlSRsyjivVA9gcO+Cvr6ByqDhu/mi5+HCcb6cMkiQp5xZ8qRO7/eCqLeyw==", "dev": true, "requires": { "bs-logger": "0.x", "buffer-from": "1.x", "fast-json-stable-stringify": "2.x", "json5": "2.x", + "lodash.memoize": "4.x", "make-error": "1.x", - "mkdirp": "0.x", + "micromatch": "4.x", + "mkdirp": "1.x", "resolve": "1.x", - "semver": "^5.5", - "yargs-parser": "10.x" + "semver": "6.x", + "yargs-parser": "18.x" }, "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } diff --git a/package.json b/package.json index 5b52d7fbff..f8e3b17c0e 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "jest-circus": "^24.7.1", "lerna": "^3.18.4", "prettier": "^1.19.1", - "ts-jest": "^24.0.2", + "ts-jest": "^25.4.0", "typescript": "^3.7.4" } } diff --git a/tsconfig.json b/tsconfig.json index a69cbc2edf..0f6316a090 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ }, "exclude": [ "node_modules", - "**/*.test.ts" + "**/*.test.ts", + "**/__mocks__/*" ] } \ No newline at end of file From 34f71e80ce5cc78c5b28f301914b9a9e16af43c5 Mon Sep 17 00:00:00 2001 From: Reinier Timmer Date: Tue, 28 Apr 2020 16:36:49 +0200 Subject: [PATCH 127/192] Check if tool path exists before executing (#385) --- packages/tool-cache/__tests__/tool-cache.test.ts | 6 +++--- packages/tool-cache/src/tool-cache.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/tool-cache/__tests__/tool-cache.test.ts b/packages/tool-cache/__tests__/tool-cache.test.ts index 42a3e4eafc..f3718327e6 100644 --- a/packages/tool-cache/__tests__/tool-cache.test.ts +++ b/packages/tool-cache/__tests__/tool-cache.test.ts @@ -461,7 +461,7 @@ describe('@actions/tool-cache', function() { ] await exec.exec(`"${powershellPath}"`, args) } else { - const zipPath: string = await io.which('zip') + const zipPath: string = await io.which('zip', true) await exec.exec(`"${zipPath}`, [zipFile, '-r', '.'], {cwd: stagingDir}) } @@ -512,7 +512,7 @@ describe('@actions/tool-cache', function() { ] await exec.exec(`"${powershellPath}"`, args) } else { - const zipPath: string = await io.which('zip') + const zipPath: string = await io.which('zip', true) await exec.exec(zipPath, [zipFile, '-r', '.'], {cwd: stagingDir}) } @@ -569,7 +569,7 @@ describe('@actions/tool-cache', function() { ] await exec.exec(`"${powershellPath}"`, args) } else { - const zipPath: string = await io.which('zip') + const zipPath: string = await io.which('zip', true) await exec.exec(zipPath, [zipFile, '-r', '.'], {cwd: stagingDir}) } diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index ea2b90361d..f43f8b51dd 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -279,7 +279,7 @@ async function extractZipWin(file: string, dest: string): Promise { const command = `$ErrorActionPreference = 'Stop' ; try { Add-Type -AssemblyName System.IO.Compression.FileSystem } catch { } ; [System.IO.Compression.ZipFile]::ExtractToDirectory('${escapedFile}', '${escapedDest}')` // run powershell - const powershellPath = await io.which('powershell') + const powershellPath = await io.which('powershell', true) const args = [ '-NoLogo', '-Sta', @@ -294,7 +294,7 @@ async function extractZipWin(file: string, dest: string): Promise { } async function extractZipNix(file: string, dest: string): Promise { - const unzipPath = await io.which('unzip') + const unzipPath = await io.which('unzip', true) await exec(`"${unzipPath}"`, [file], {cwd: dest}) } From 4e734dc4c195ad099784450e87e2a9b32b3a7b18 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Tue, 28 Apr 2020 11:59:04 -0400 Subject: [PATCH 128/192] bump exec version --- packages/exec/package-lock.json | 2 +- packages/exec/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/exec/package-lock.json b/packages/exec/package-lock.json index b8b91bcd70..eb869efb2b 100644 --- a/packages/exec/package-lock.json +++ b/packages/exec/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/exec", - "version": "1.0.3", + "version": "1.0.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/exec/package.json b/packages/exec/package.json index 1b8078fdae..09e438e427 100644 --- a/packages/exec/package.json +++ b/packages/exec/package.json @@ -1,6 +1,6 @@ { "name": "@actions/exec", - "version": "1.0.3", + "version": "1.0.4", "description": "Actions exec lib", "keywords": [ "github", From 57d20b4db494c25af8d2f3d9323650044610e531 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Wed, 29 Apr 2020 17:33:01 +0200 Subject: [PATCH 129/192] tool-cache: make extract functions quiet by default and more verbose if `core.isDebug` is set (#206) * tool-cache: make unzip and 7z extract quiet by default This avoids spamming the log when unzipping large archives. * tool-cache: make tar, unzip and 7z verbose when `core.isDebug` Make the extract function print the list of extracted file if the action is run in debug mode. --- packages/tool-cache/__tests__/tool-cache.test.ts | 2 +- packages/tool-cache/src/tool-cache.ts | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/tool-cache/__tests__/tool-cache.test.ts b/packages/tool-cache/__tests__/tool-cache.test.ts index f3718327e6..7dc17c67f6 100644 --- a/packages/tool-cache/__tests__/tool-cache.test.ts +++ b/packages/tool-cache/__tests__/tool-cache.test.ts @@ -334,7 +334,7 @@ describe('@actions/tool-cache', function() { .readFileSync(path.join(tempDir, 'mock7zr-args.txt')) .toString() .trim() - ).toBe(`x -bb1 -bd -sccUTF-8 ${_7zFile}`) + ).toBe(`x -bb0 -bd -sccUTF-8 ${_7zFile}`) expect(fs.existsSync(path.join(extPath, 'file.txt'))).toBeTruthy() expect( fs.existsSync(path.join(extPath, 'file-with-ç-character.txt')) diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index f43f8b51dd..6fa2d510eb 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -144,9 +144,10 @@ export async function extract7z( process.chdir(dest) if (_7zPath) { try { + const logLevel = core.isDebug() ? '-bb1' : '-bb0' const args: string[] = [ 'x', // eXtract files with full paths - '-bb1', // -bb[0-3] : set output log level + logLevel, // -bb[0-3] : set output log level '-bd', // disable progress indicator '-sccUTF-8', // set charset for for console input/output file @@ -227,6 +228,10 @@ export async function extractTar( // Initialize args const args = [flags] + if (core.isDebug() && !flags.includes('v')) { + args.push('-v') + } + let destArg = dest let fileArg = file if (IS_WINDOWS && isGnuTar) { @@ -295,7 +300,11 @@ async function extractZipWin(file: string, dest: string): Promise { async function extractZipNix(file: string, dest: string): Promise { const unzipPath = await io.which('unzip', true) - await exec(`"${unzipPath}"`, [file], {cwd: dest}) + const args = [file] + if (!core.isDebug()) { + args.unshift('-q') + } + await exec(`"${unzipPath}"`, args, {cwd: dest}) } /** From 187d4aa6250e38be6346f705d565569850bf0c3f Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Thu, 30 Apr 2020 09:48:16 -0400 Subject: [PATCH 130/192] @actions/core 1.2.4 + release notes (#439) --- packages/core/RELEASES.md | 4 ++++ packages/core/package-lock.json | 2 +- packages/core/package.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/core/RELEASES.md b/packages/core/RELEASES.md index 0a518b0dfb..34361bc2c0 100644 --- a/packages/core/RELEASES.md +++ b/packages/core/RELEASES.md @@ -1,5 +1,9 @@ # @actions/core Releases +### 1.2.4 +- [Be more lenient in accepting non-string command inputs](https://github.com/actions/toolkit/pull/405) +- [Add Echo commands](https://github.com/actions/toolkit/pull/411) + ### 1.2.3 - [IsDebug logging](README.md#logging) diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json index 2aa13e494b..44faa4ad54 100644 --- a/packages/core/package-lock.json +++ b/packages/core/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/core", - "version": "1.2.3", + "version": "1.2.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index 444e14eb29..22f3a7ac3c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@actions/core", - "version": "1.2.3", + "version": "1.2.4", "description": "Actions core lib", "keywords": [ "github", From 97cabf0eb94386823f5e0bd26300a46f9a7ce50f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 May 2020 11:32:41 +0200 Subject: [PATCH 131/192] Bump @actions/http-client from 1.0.7 to 1.0.8 in /packages/artifact (#437) Bumps [@actions/http-client](https://github.com/actions/http-client) from 1.0.7 to 1.0.8. - [Release notes](https://github.com/actions/http-client/releases) - [Changelog](https://github.com/actions/http-client/blob/master/RELEASES.md) - [Commits](https://github.com/actions/http-client/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/artifact/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/artifact/package-lock.json b/packages/artifact/package-lock.json index 4cccbe33cf..f48f1b823e 100644 --- a/packages/artifact/package-lock.json +++ b/packages/artifact/package-lock.json @@ -10,9 +10,9 @@ "integrity": "sha512-Wp4xnyokakM45Uuj4WLUxdsa8fJjKVl1fDTsPbTEcTcuu0Nb26IPQbOtjmnfaCPGcaoPOOqId8H9NapZ8gii4w==" }, "@actions/http-client": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.7.tgz", - "integrity": "sha512-PY3ys/XH5WMekkHyZhYSa/scYvlE5T/TV/T++vABHuY5ZRgtiBZkn2L2tV5Pv/xDCl59lSZb9WwRuWExDyAsSg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz", + "integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==", "requires": { "tunnel": "0.0.6" } From 94cd8efa47754171c6384bb198c96011126678ab Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Fri, 1 May 2020 09:42:38 -0400 Subject: [PATCH 132/192] Ignore DS_Store files. (#440) * Ignore DSStore Files * Simplify Ignore for DS_Store --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index db6371d359..255bac5731 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules/ packages/*/node_modules/ packages/*/lib/ -packages/*/__tests__/_temp/ \ No newline at end of file +packages/*/__tests__/_temp/ +.DS_Store From 6a744be7ed9bab4b95ca7600529b7ce5ced1e54a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 May 2020 09:46:19 -0400 Subject: [PATCH 133/192] Bump @actions/http-client from 1.0.3 to 1.0.8 in /packages/github (#438) Bumps [@actions/http-client](https://github.com/actions/http-client) from 1.0.3 to 1.0.8. - [Release notes](https://github.com/actions/http-client/releases) - [Changelog](https://github.com/actions/http-client/blob/master/RELEASES.md) - [Commits](https://github.com/actions/http-client/commits) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/github/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index 70ff5431eb..34a0a94afb 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@actions/http-client": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.3.tgz", - "integrity": "sha512-wFwh1U4adB/Zsk4cc9kVqaBOHoknhp/pJQk+aWTocbAZWpIl4Zx/At83WFRLXvxB+5HVTWOACM6qjULMZfQSfw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz", + "integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==", "requires": { "tunnel": "0.0.6" } From 11dcc8b313ba2e10b1ade9111d5efd65632d21b7 Mon Sep 17 00:00:00 2001 From: Justin Hutchings Date: Wed, 6 May 2020 09:58:36 -0700 Subject: [PATCH 134/192] Add CodeQL Analysis workflow (#434) * Add CodeQL Analysis workflow * Rename .github/workflows/workflows/codeql.yml to .github/workflows/codeql.yml * Remove autobuilder * Add back autobuilder * Disable c# analysis --- .github/workflows/codeql.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..e68bfdcdf8 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,34 @@ +name: "Code Scanning - Action" + +on: + push: + schedule: + - cron: '0 0 * * 0' + +jobs: + CodeQL-Build: + + strategy: + fail-fast: false + + + # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: javascript + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 264e52add636fd679299bc3d61c6e0fa0a35c7d7 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Thu, 7 May 2020 09:41:18 -0400 Subject: [PATCH 135/192] set base URL for GHES (#449) --- packages/github/RELEASES.md | 4 ++++ packages/github/package-lock.json | 2 +- packages/github/package.json | 2 +- packages/github/src/github.ts | 34 ++++++++++++++++++++++++++----- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/packages/github/RELEASES.md b/packages/github/RELEASES.md index b56ec46549..00db5e66f1 100644 --- a/packages/github/RELEASES.md +++ b/packages/github/RELEASES.md @@ -1,5 +1,9 @@ # @actions/github Releases +### 2.2.0 + +- [Support GHES: Use GITHUB_API_URL and GITHUB_GRAPHQL_URL to determine baseUrl](https://github.com/actions/toolkit/pull/449) + ### 2.1.1 - [Use import {Octokit}](https://github.com/actions/toolkit/pull/332) diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index 34a0a94afb..a075eac212 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "2.1.1", + "version": "2.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/github/package.json b/packages/github/package.json index 45582ccc41..ef58523ba6 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "2.1.1", + "version": "2.2.0", "description": "Actions github lib", "keywords": [ "github", diff --git a/packages/github/src/github.ts b/packages/github/src/github.ts index 515f3700c1..699998dc69 100644 --- a/packages/github/src/github.ts +++ b/packages/github/src/github.ts @@ -61,6 +61,9 @@ export class GitHub extends Octokit { const token = args[0] const options = {...args[1]} // Shallow clone - don't mutate the object provided by the caller + // Base URL - GHES or Dotcom + options.baseUrl = options.baseUrl || this.getApiBaseUrl() + // Auth const auth = GitHub.getAuthString(token, options) if (auth) { @@ -68,7 +71,7 @@ export class GitHub extends Octokit { } // Proxy - const agent = GitHub.getProxyAgent(options) + const agent = GitHub.getProxyAgent(options.baseUrl, options) if (agent) { // Shallow clone - don't mutate the object provided by the caller options.request = options.request ? {...options.request} : {} @@ -82,6 +85,7 @@ export class GitHub extends Octokit { private static getGraphQL(args: [string, Octokit.Options]): GraphQL { const defaults: GraphQLRequestParameters = {} + defaults.baseUrl = this.getGraphQLBaseUrl() const token = args[0] const options = args[1] @@ -94,7 +98,7 @@ export class GitHub extends Octokit { } // Proxy - const agent = GitHub.getProxyAgent(options) + const agent = GitHub.getProxyAgent(defaults.baseUrl, options) if (agent) { defaults.request = {agent} } @@ -119,16 +123,36 @@ export class GitHub extends Octokit { } private static getProxyAgent( + destinationUrl: string, options: Octokit.Options ): http.Agent | undefined { if (!options.request?.agent) { - const serverUrl = 'https://api.github.com' - if (httpClient.getProxyUrl(serverUrl)) { + if (httpClient.getProxyUrl(destinationUrl)) { const hc = new httpClient.HttpClient() - return hc.getAgent(serverUrl) + return hc.getAgent(destinationUrl) } } return undefined } + + private static getApiBaseUrl(): string { + return process.env['GITHUB_API_URL'] || 'https://api.github.com' + } + + private static getGraphQLBaseUrl(): string { + let url = + process.env['GITHUB_GRAPHQL_URL'] || 'https://api.github.com/graphql' + + // Shouldn't be a trailing slash, but remove if so + if (url.endsWith('/')) { + url = url.substr(0, url.length - 1) + } + + // Remove trailing "/graphql" + if (url.toUpperCase().endsWith('/GRAPHQL')) { + url = url.substr(0, url.length - '/graphql'.length) + } + return url + } } From a5ff692285a26ab31d4e076fa9e923415d49803c Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Thu, 7 May 2020 10:24:11 -0400 Subject: [PATCH 136/192] Redirect general feedback to the Community Forums (#447) --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d89e2032b8..b907ac3c2f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -6,7 +6,7 @@ We welcome contributions in the form of issues and pull requests. We view the c Log issues for both bugs and enhancement requests. Logging issues are important for the open community. -Issues in this repository should be for the toolkit packages. Runner specific issues can be filed [in the runner repository](https://github.com/actions/runner). +Issues in this repository should be for the toolkit packages. General feedback for GitHub Actions should be filed in the [community forums.](https://github.community/t5/GitHub-Actions/bd-p/actions) Runner specific issues can be filed [in the runner repository](https://github.com/actions/runner). ## Enhancements and Feature Requests From 83dd3ef0f1e5bc93c5ab7072e1edf1715a01ba9d Mon Sep 17 00:00:00 2001 From: eric sciple Date: Thu, 7 May 2020 11:39:38 -0400 Subject: [PATCH 137/192] separate audit workflow (#450) --- .github/workflows/audit.yml | 38 ++++++++++++++++++++++++++++++++ .github/workflows/unit-tests.yml | 8 ------- README.md | 3 ++- 3 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/audit.yml diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 0000000000..669b4b4965 --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,38 @@ +name: toolkit-audit +on: + push: + branches: + - master + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' + +jobs: + + build: + name: Audit + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Set Node.js 12.x + uses: actions/setup-node@v1 + with: + node-version: 12.x + + - name: npm install + run: npm install + + - name: Bootstrap + run: npm run bootstrap + + - name: audit tools + run: npm audit --audit-level=moderate + + - name: audit packages + run: npm run audit-all diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index b558b4ebd1..bbcb866c31 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -49,11 +49,3 @@ jobs: - name: Format run: npm run format-check - - - name: audit tools - run: npm audit --audit-level=moderate - if: matrix.runs-on == 'ubuntu-latest' - - - name: audit packages - run: npm run audit-all - if: matrix.runs-on == 'ubuntu-latest' diff --git a/README.md b/README.md index 948dd98564..3dab6d39e9 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@

- GitHub Actions status + Toolkit unit tests status + Toolkit audit status

From d1b52e7168ff8e06e252235f1812b32f799a45c0 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 12 May 2020 07:22:22 -0700 Subject: [PATCH 138/192] Update homepage to be tool-cache instead of exec (#452) --- packages/tool-cache/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index 4df39c9963..a5a77c24b9 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -7,7 +7,7 @@ "actions", "exec" ], - "homepage": "https://github.com/actions/toolkit/tree/master/packages/exec", + "homepage": "https://github.com/actions/toolkit/tree/master/packages/tool-cache", "license": "MIT", "main": "lib/tool-cache.js", "types": "lib/tool-cache.d.ts", From 0471ed4ad777277385d971c0c86c87917e9373df Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Tue, 12 May 2020 17:48:36 +0200 Subject: [PATCH 139/192] artifact header cleanup (#441) * Update NPM packages for @actions/artifact * Clarifications around headers * Revert NPM updates * Apply suggestions from code review Co-authored-by: Josh Gross Co-authored-by: Josh Gross --- packages/artifact/__tests__/util.test.ts | 50 +++++++++---------- .../src/internal/download-http-client.ts | 23 ++++----- .../src/internal/upload-http-client.ts | 14 +++--- packages/artifact/src/internal/utils.ts | 12 ++--- 4 files changed, 47 insertions(+), 52 deletions(-) diff --git a/packages/artifact/__tests__/util.test.ts b/packages/artifact/__tests__/util.test.ts index 276cfa9d77..5265437d18 100644 --- a/packages/artifact/__tests__/util.test.ts +++ b/packages/artifact/__tests__/util.test.ts @@ -120,7 +120,7 @@ describe('Utils', () => { const size = 24 const uncompressedLength = 100 const range = 'bytes 0-199/200' - const options = utils.getUploadRequestOptions( + const headers = utils.getUploadHeaders( contentType, true, true, @@ -128,47 +128,47 @@ describe('Utils', () => { size, range ) - expect(Object.keys(options).length).toEqual(8) - expect(options['Accept']).toEqual( + expect(Object.keys(headers).length).toEqual(8) + expect(headers['Accept']).toEqual( `application/json;api-version=${utils.getApiVersion()}` ) - expect(options['Content-Type']).toEqual(contentType) - expect(options['Connection']).toEqual('Keep-Alive') - expect(options['Keep-Alive']).toEqual('10') - expect(options['Content-Encoding']).toEqual('gzip') - expect(options['x-tfs-filelength']).toEqual(uncompressedLength) - expect(options['Content-Length']).toEqual(size) - expect(options['Content-Range']).toEqual(range) + expect(headers['Content-Type']).toEqual(contentType) + expect(headers['Connection']).toEqual('Keep-Alive') + expect(headers['Keep-Alive']).toEqual('10') + expect(headers['Content-Encoding']).toEqual('gzip') + expect(headers['x-tfs-filelength']).toEqual(uncompressedLength) + expect(headers['Content-Length']).toEqual(size) + expect(headers['Content-Range']).toEqual(range) }) it('Test constructing upload headers with only required parameter', () => { - const options = utils.getUploadRequestOptions('application/octet-stream') - expect(Object.keys(options).length).toEqual(2) - expect(options['Accept']).toEqual( + const headers = utils.getUploadHeaders('application/octet-stream') + expect(Object.keys(headers).length).toEqual(2) + expect(headers['Accept']).toEqual( `application/json;api-version=${utils.getApiVersion()}` ) - expect(options['Content-Type']).toEqual('application/octet-stream') + expect(headers['Content-Type']).toEqual('application/octet-stream') }) it('Test constructing download headers with all optional parameters', () => { const contentType = 'application/json' - const options = utils.getDownloadRequestOptions(contentType, true, true) - expect(Object.keys(options).length).toEqual(5) - expect(options['Content-Type']).toEqual(contentType) - expect(options['Connection']).toEqual('Keep-Alive') - expect(options['Keep-Alive']).toEqual('10') - expect(options['Accept-Encoding']).toEqual('gzip') - expect(options['Accept']).toEqual( + const headers = utils.getDownloadHeaders(contentType, true, true) + expect(Object.keys(headers).length).toEqual(5) + expect(headers['Content-Type']).toEqual(contentType) + expect(headers['Connection']).toEqual('Keep-Alive') + expect(headers['Keep-Alive']).toEqual('10') + expect(headers['Accept-Encoding']).toEqual('gzip') + expect(headers['Accept']).toEqual( `application/octet-stream;api-version=${utils.getApiVersion()}` ) }) it('Test constructing download headers with only required parameter', () => { - const options = utils.getDownloadRequestOptions('application/octet-stream') - expect(Object.keys(options).length).toEqual(2) - expect(options['Content-Type']).toEqual('application/octet-stream') + const headers = utils.getDownloadHeaders('application/octet-stream') + expect(Object.keys(headers).length).toEqual(2) + expect(headers['Content-Type']).toEqual('application/octet-stream') // check for default accept type - expect(options['Accept']).toEqual( + expect(headers['Accept']).toEqual( `application/json;api-version=${utils.getApiVersion()}` ) }) diff --git a/packages/artifact/src/internal/download-http-client.ts b/packages/artifact/src/internal/download-http-client.ts index 8d139806a8..4ddd76d5a1 100644 --- a/packages/artifact/src/internal/download-http-client.ts +++ b/packages/artifact/src/internal/download-http-client.ts @@ -3,7 +3,7 @@ import * as core from '@actions/core' import * as zlib from 'zlib' import { getArtifactUrl, - getDownloadRequestOptions, + getDownloadHeaders, isSuccessStatusCode, isRetryableStatusCode, isThrottledStatusCode, @@ -40,8 +40,8 @@ export class DownloadHttpClient { // use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately const client = this.downloadHttpManager.getClient(0) - const requestOptions = getDownloadRequestOptions('application/json') - const response = await client.get(artifactUrl, requestOptions) + const headers = getDownloadHeaders('application/json') + const response = await client.get(artifactUrl, headers) const body: string = await response.readBody() if (isSuccessStatusCode(response.message.statusCode) && body) { @@ -68,8 +68,8 @@ export class DownloadHttpClient { // use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately const client = this.downloadHttpManager.getClient(0) - const requestOptions = getDownloadRequestOptions('application/json') - const response = await client.get(resourceUrl.toString(), requestOptions) + const headers = getDownloadHeaders('application/json') + const response = await client.get(resourceUrl.toString(), headers) const body: string = await response.readBody() if (isSuccessStatusCode(response.message.statusCode) && body) { @@ -149,22 +149,19 @@ export class DownloadHttpClient { let retryCount = 0 const retryLimit = getRetryLimit() const destinationStream = fs.createWriteStream(downloadPath) - const requestOptions = getDownloadRequestOptions( - 'application/json', - true, - true - ) + const headers = getDownloadHeaders('application/json', true, true) // a single GET request is used to download a file const makeDownloadRequest = async (): Promise => { const client = this.downloadHttpManager.getClient(httpClientIndex) - return await client.get(artifactLocation, requestOptions) + return await client.get(artifactLocation, headers) } // check the response headers to determine if the file was compressed using gzip - const isGzip = (headers: IncomingHttpHeaders): boolean => { + const isGzip = (incomingHeaders: IncomingHttpHeaders): boolean => { return ( - 'content-encoding' in headers && headers['content-encoding'] === 'gzip' + 'content-encoding' in incomingHeaders && + incomingHeaders['content-encoding'] === 'gzip' ) } diff --git a/packages/artifact/src/internal/upload-http-client.ts b/packages/artifact/src/internal/upload-http-client.ts index deb2b540c9..dc2d331899 100644 --- a/packages/artifact/src/internal/upload-http-client.ts +++ b/packages/artifact/src/internal/upload-http-client.ts @@ -11,7 +11,7 @@ import { import { getArtifactUrl, getContentRange, - getUploadRequestOptions, + getUploadHeaders, isRetryableStatusCode, isSuccessStatusCode, isThrottledStatusCode, @@ -63,8 +63,8 @@ export class UploadHttpClient { // use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately const client = this.uploadHttpManager.getClient(0) - const requestOptions = getUploadRequestOptions('application/json', false) - const rawResponse = await client.post(artifactUrl, data, requestOptions) + const headers = getUploadHeaders('application/json', false) + const rawResponse = await client.post(artifactUrl, data, headers) const body: string = await rawResponse.readBody() if (isSuccessStatusCode(rawResponse.message.statusCode) && body) { @@ -354,7 +354,7 @@ export class UploadHttpClient { totalFileSize: number ): Promise { // prepare all the necessary headers before making any http call - const requestOptions = getUploadRequestOptions( + const headers = getUploadHeaders( 'application/octet-stream', true, isGzip, @@ -365,7 +365,7 @@ export class UploadHttpClient { const uploadChunkRequest = async (): Promise => { const client = this.uploadHttpManager.getClient(httpClientIndex) - return await client.sendStream('PUT', resourceUrl, data, requestOptions) + return await client.sendStream('PUT', resourceUrl, data, headers) } let retryCount = 0 @@ -464,7 +464,7 @@ export class UploadHttpClient { * Updating the size indicates that we are done uploading all the contents of the artifact */ async patchArtifactSize(size: number, artifactName: string): Promise { - const requestOptions = getUploadRequestOptions('application/json', false) + const headers = getUploadHeaders('application/json', false) const resourceUrl = new URL(getArtifactUrl()) resourceUrl.searchParams.append('artifactName', artifactName) @@ -477,7 +477,7 @@ export class UploadHttpClient { const response: HttpClientResponse = await client.patch( resourceUrl.toString(), data, - requestOptions + headers ) const body: string = await response.readBody() if (isSuccessStatusCode(response.message.statusCode)) { diff --git a/packages/artifact/src/internal/utils.ts b/packages/artifact/src/internal/utils.ts index 406867e559..9f9109ebce 100644 --- a/packages/artifact/src/internal/utils.ts +++ b/packages/artifact/src/internal/utils.ts @@ -62,7 +62,6 @@ export function isForbiddenStatusCode(statusCode?: number): boolean { if (!statusCode) { return false } - return statusCode === HttpCodes.Forbidden } @@ -84,7 +83,6 @@ export function isThrottledStatusCode(statusCode?: number): boolean { if (!statusCode) { return false } - return statusCode === HttpCodes.TooManyRequests } @@ -133,9 +131,9 @@ export function getContentRange( * @param {boolean} isKeepAlive is the same connection being used to make multiple calls * @param {boolean} acceptGzip can we accept a gzip encoded response * @param {string} acceptType the type of content that we can accept - * @returns appropriate request options to make a specific http call during artifact download + * @returns appropriate headers to make a specific http call during artifact download */ -export function getDownloadRequestOptions( +export function getDownloadHeaders( contentType: string, isKeepAlive?: boolean, acceptGzip?: boolean @@ -172,9 +170,9 @@ export function getDownloadRequestOptions( * @param {number} uncompressedLength the original size of the content if something is being uploaded that has been compressed * @param {number} contentLength the length of the content that is being uploaded * @param {string} contentRange the range of the content that is being uploaded - * @returns appropriate request options to make a specific http call during artifact upload + * @returns appropriate headers to make a specific http call during artifact upload */ -export function getUploadRequestOptions( +export function getUploadHeaders( contentType: string, isKeepAlive?: boolean, isGzip?: boolean, @@ -207,7 +205,7 @@ export function getUploadRequestOptions( } export function createHttpClient(): HttpClient { - return new HttpClient('action/artifact', [ + return new HttpClient('actions/artifact', [ new BearerCredentialHandler(getRuntimeToken()) ]) } From 932779cf587ca0cf7eab689300724926f63e63d1 Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Wed, 6 May 2020 11:10:18 -0400 Subject: [PATCH 140/192] Initial commit to create @actions/cache package --- README.md | 9 + packages/cache/CONTRIBUTIONS.md | 0 packages/cache/README.md | 1 + packages/cache/RELEASES.md | 5 + .../__tests__/__fixtures__/helloWorld.txt | 1 + .../cache/__tests__/cacheHttpClient.test.ts | 28 ++ packages/cache/__tests__/cacheUtils.test.ts | 177 +++++++++ packages/cache/__tests__/restoreCache.test.ts | 303 ++++++++++++++++ packages/cache/__tests__/saveCache.test.ts | 219 +++++++++++ packages/cache/__tests__/tar.test.ts | 191 ++++++++++ packages/cache/package-lock.json | 32 ++ packages/cache/package.json | 49 +++ packages/cache/src/cache.ts | 169 +++++++++ .../cache/src/internal/cacheHttpClient.ts | 339 ++++++++++++++++++ packages/cache/src/internal/cacheUtils.ts | 104 ++++++ packages/cache/src/internal/constants.ts | 14 + packages/cache/src/internal/contracts.d.ts | 25 ++ packages/cache/src/internal/tar.ts | 86 +++++ packages/cache/tsconfig.json | 11 + 19 files changed, 1763 insertions(+) create mode 100644 packages/cache/CONTRIBUTIONS.md create mode 100644 packages/cache/README.md create mode 100644 packages/cache/RELEASES.md create mode 100644 packages/cache/__tests__/__fixtures__/helloWorld.txt create mode 100644 packages/cache/__tests__/cacheHttpClient.test.ts create mode 100644 packages/cache/__tests__/cacheUtils.test.ts create mode 100644 packages/cache/__tests__/restoreCache.test.ts create mode 100644 packages/cache/__tests__/saveCache.test.ts create mode 100644 packages/cache/__tests__/tar.test.ts create mode 100644 packages/cache/package-lock.json create mode 100644 packages/cache/package.json create mode 100644 packages/cache/src/cache.ts create mode 100644 packages/cache/src/internal/cacheHttpClient.ts create mode 100644 packages/cache/src/internal/cacheUtils.ts create mode 100644 packages/cache/src/internal/constants.ts create mode 100644 packages/cache/src/internal/contracts.d.ts create mode 100644 packages/cache/src/internal/tar.ts create mode 100644 packages/cache/tsconfig.json diff --git a/README.md b/README.md index 3dab6d39e9..8701ab5c6f 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,15 @@ $ npm install @actions/artifact --save ```
+:dart: [@actions/cache](packages/cache) + +Provides functions to interact with actions cache. Read more [here](packages/cache) + +```bash +$ npm install @actions/artifact --save +``` +
+ ## Creating an Action with the Toolkit :question: [Choosing an action type](docs/action-types.md) diff --git a/packages/cache/CONTRIBUTIONS.md b/packages/cache/CONTRIBUTIONS.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/cache/README.md b/packages/cache/README.md new file mode 100644 index 0000000000..b65c7f3440 --- /dev/null +++ b/packages/cache/README.md @@ -0,0 +1 @@ +# `@actions/cache` diff --git a/packages/cache/RELEASES.md b/packages/cache/RELEASES.md new file mode 100644 index 0000000000..b47fc55002 --- /dev/null +++ b/packages/cache/RELEASES.md @@ -0,0 +1,5 @@ +# @actions/cache Releases + +### 0.0.0 + +- Initial release \ No newline at end of file diff --git a/packages/cache/__tests__/__fixtures__/helloWorld.txt b/packages/cache/__tests__/__fixtures__/helloWorld.txt new file mode 100644 index 0000000000..95d09f2b10 --- /dev/null +++ b/packages/cache/__tests__/__fixtures__/helloWorld.txt @@ -0,0 +1 @@ +hello world \ No newline at end of file diff --git a/packages/cache/__tests__/cacheHttpClient.test.ts b/packages/cache/__tests__/cacheHttpClient.test.ts new file mode 100644 index 0000000000..d21652801b --- /dev/null +++ b/packages/cache/__tests__/cacheHttpClient.test.ts @@ -0,0 +1,28 @@ +import {getCacheVersion} from '../src/internal/cacheHttpClient' +import {CompressionMethod} from '../src/internal/constants' + +test('getCacheVersion with path input and compression method undefined returns version', async () => { + const inputPath = 'node_modules' + const result = getCacheVersion(inputPath) + expect(result).toEqual( + 'b3e0c6cb5ecf32614eeb2997d905b9c297046d7cbf69062698f25b14b4cb0985' + ) +}) + +test('getCacheVersion with zstd compression returns version', async () => { + const inputPath = 'node_modules' + const result = getCacheVersion(inputPath, CompressionMethod.Zstd) + + expect(result).toEqual( + '273877e14fd65d270b87a198edbfa2db5a43de567c9a548d2a2505b408befe24' + ) +}) + +test('getCacheVersion with gzip compression does not change vesion', async () => { + const inputPath = 'node_modules' + const result = getCacheVersion(inputPath, CompressionMethod.Gzip) + + expect(result).toEqual( + 'b3e0c6cb5ecf32614eeb2997d905b9c297046d7cbf69062698f25b14b4cb0985' + ) +}) diff --git a/packages/cache/__tests__/cacheUtils.test.ts b/packages/cache/__tests__/cacheUtils.test.ts new file mode 100644 index 0000000000..b09eed1388 --- /dev/null +++ b/packages/cache/__tests__/cacheUtils.test.ts @@ -0,0 +1,177 @@ +import * as core from '@actions/core' +import * as io from '@actions/io' +import {promises as fs} from 'fs' +import * as os from 'os' +import * as path from 'path' +import {v4 as uuidV4} from 'uuid' +import * as cacheUtils from '../src/internal/cacheUtils' + +jest.mock('@actions/core') +jest.mock('os') + +function getTempDir(): string { + return path.join(__dirname, '_temp', 'cacheUtils') +} + +afterAll(async () => { + delete process.env['GITHUB_WORKSPACE'] + await io.rmRF(getTempDir()) +}) + +test('getArchiveFileSize returns file size', () => { + const filePath = path.join(__dirname, '__fixtures__', 'helloWorld.txt') + + const size = cacheUtils.getArchiveFileSize(filePath) + + expect(size).toBe(11) +}) + +test('logWarning logs a message with a warning prefix', () => { + const message = 'A warning occurred.' + + const infoMock = jest.spyOn(core, 'info') + + cacheUtils.logWarning(message) + + expect(infoMock).toHaveBeenCalledWith(`[warning]${message}`) +}) + +test('resolvePaths with no ~ in path', async () => { + const filePath = '.cache' + + // Create the following layout: + // cwd + // cwd/.cache + // cwd/.cache/file.txt + + const root = path.join(getTempDir(), 'no-tilde') + // tarball entries will be relative to workspace + process.env['GITHUB_WORKSPACE'] = root + + await fs.mkdir(root, {recursive: true}) + const cache = path.join(root, '.cache') + await fs.mkdir(cache, {recursive: true}) + await fs.writeFile(path.join(cache, 'file.txt'), 'cached') + + const originalCwd = process.cwd() + + try { + process.chdir(root) + + const resolvedPath = await cacheUtils.resolvePaths([filePath]) + + const expectedPath = [filePath] + expect(resolvedPath).toStrictEqual(expectedPath) + } finally { + process.chdir(originalCwd) + } +}) + +test('resolvePaths with ~ in path', async () => { + const cacheDir = uuidV4() + const filePath = `~/${cacheDir}` + // Create the following layout: + // ~/uuid + // ~/uuid/file.txt + + const homedir = jest.requireActual('os').homedir() + const homedirMock = jest.spyOn(os, 'homedir') + homedirMock.mockReturnValue(homedir) + + const target = path.join(homedir, cacheDir) + await fs.mkdir(target, {recursive: true}) + await fs.writeFile(path.join(target, 'file.txt'), 'cached') + + const root = getTempDir() + process.env['GITHUB_WORKSPACE'] = root + + try { + const resolvedPath = await cacheUtils.resolvePaths([filePath]) + + const expectedPath = [path.relative(root, target)] + expect(resolvedPath).toStrictEqual(expectedPath) + } finally { + await io.rmRF(target) + } +}) + +test('resolvePaths with home not found', async () => { + const filePath = '~/.cache/yarn' + const homedirMock = jest.spyOn(os, 'homedir') + homedirMock.mockReturnValue('') + + await expect(cacheUtils.resolvePaths([filePath])).rejects.toThrow( + 'Unable to determine HOME directory' + ) +}) + +test('resolvePaths inclusion pattern returns found', async () => { + const pattern = '*.ts' + // Create the following layout: + // inclusion-patterns + // inclusion-patterns/miss.txt + // inclusion-patterns/test.ts + + const root = path.join(getTempDir(), 'inclusion-patterns') + // tarball entries will be relative to workspace + process.env['GITHUB_WORKSPACE'] = root + + await fs.mkdir(root, {recursive: true}) + await fs.writeFile(path.join(root, 'miss.txt'), 'no match') + await fs.writeFile(path.join(root, 'test.ts'), 'match') + + const originalCwd = process.cwd() + + try { + process.chdir(root) + + const resolvedPath = await cacheUtils.resolvePaths([pattern]) + + const expectedPath = ['test.ts'] + expect(resolvedPath).toStrictEqual(expectedPath) + } finally { + process.chdir(originalCwd) + } +}) + +test('resolvePaths exclusion pattern returns not found', async () => { + const patterns = ['*.ts', '!test.ts'] + // Create the following layout: + // exclusion-patterns + // exclusion-patterns/miss.txt + // exclusion-patterns/test.ts + + const root = path.join(getTempDir(), 'exclusion-patterns') + // tarball entries will be relative to workspace + process.env['GITHUB_WORKSPACE'] = root + + await fs.mkdir(root, {recursive: true}) + await fs.writeFile(path.join(root, 'miss.txt'), 'no match') + await fs.writeFile(path.join(root, 'test.ts'), 'no match') + + const originalCwd = process.cwd() + + try { + process.chdir(root) + + const resolvedPath = await cacheUtils.resolvePaths(patterns) + + const expectedPath: string[] = [] + expect(resolvedPath).toStrictEqual(expectedPath) + } finally { + process.chdir(originalCwd) + } +}) + +test('unlinkFile unlinks file', async () => { + const testDirectory = await fs.mkdtemp('unlinkFileTest') + const testFile = path.join(testDirectory, 'test.txt') + await fs.writeFile(testFile, 'hello world') + + await cacheUtils.unlinkFile(testFile) + + // This should throw as testFile should not exist + await expect(fs.stat(testFile)).rejects.toThrow() + + await fs.rmdir(testDirectory) +}) diff --git a/packages/cache/__tests__/restoreCache.test.ts b/packages/cache/__tests__/restoreCache.test.ts new file mode 100644 index 0000000000..d1f016d6cc --- /dev/null +++ b/packages/cache/__tests__/restoreCache.test.ts @@ -0,0 +1,303 @@ +import * as core from '@actions/core' +import * as path from 'path' +import {restoreCache} from '../src/cache' +import * as cacheHttpClient from '../src/internal/cacheHttpClient' +import * as cacheUtils from '../src/internal/cacheUtils' +import {CacheFilename, CompressionMethod} from '../src/internal/constants' +import {ArtifactCacheEntry} from '../src/internal/contracts' +import * as tar from '../src/internal/tar' + +jest.mock('../src/internal/cacheHttpClient') +jest.mock('../src/internal/cacheUtils') +jest.mock('../src/internal/tar') + +beforeAll(() => { + // eslint-disable-next-line @typescript-eslint/promise-function-async + jest.spyOn(cacheUtils, 'getCacheFileName').mockImplementation(cm => { + const actualUtils = jest.requireActual('../src/internal/cacheUtils') + return actualUtils.getCacheFileName(cm) + }) +}) + +test('restore with no path should fail', async () => { + const inputPath = '' + const key = 'node-test' + const failedMock = jest.spyOn(core, 'setFailed') + await restoreCache(inputPath, key) + expect(failedMock).toHaveBeenCalledWith( + 'Input required and not supplied: path' + ) +}) + +test('restore with too many keys should fail', async () => { + const inputPath = 'node_modules' + const key = 'node-test' + const restoreKeys = [...Array(20).keys()].map(x => x.toString()) + const failedMock = jest.spyOn(core, 'setFailed') + await restoreCache(inputPath, key, restoreKeys) + expect(failedMock).toHaveBeenCalledWith( + `Key Validation Error: Keys are limited to a maximum of 10.` + ) +}) + +test('restore with large key should fail', async () => { + const inputPath = 'node_modules' + const key = 'foo'.repeat(512) // Over the 512 character limit + const failedMock = jest.spyOn(core, 'setFailed') + await restoreCache(inputPath, key) + expect(failedMock).toHaveBeenCalledWith( + `Key Validation Error: ${key} cannot be larger than 512 characters.` + ) +}) + +test('restore with invalid key should fail', async () => { + const inputPath = 'node_modules' + const key = 'comma,comma' + const failedMock = jest.spyOn(core, 'setFailed') + await restoreCache(inputPath, key) + expect(failedMock).toHaveBeenCalledWith( + `Key Validation Error: ${key} cannot contain commas.` + ) +}) + +test('restore with no cache found', async () => { + const inputPath = 'node_modules' + const key = 'node-test' + + const infoMock = jest.spyOn(core, 'info') + const failedMock = jest.spyOn(core, 'setFailed') + + const clientMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') + clientMock.mockImplementation(async () => { + return Promise.resolve(null) + }) + + await restoreCache(inputPath, key) + + expect(failedMock).toHaveBeenCalledTimes(0) + expect(infoMock).toHaveBeenCalledWith( + `Cache not found for input keys: ${key}` + ) +}) + +test('restore with server error should fail', async () => { + const inputPath = 'node_modules' + const key = 'node-test' + + const logWarningMock = jest.spyOn(cacheUtils, 'logWarning') + const failedMock = jest.spyOn(core, 'setFailed') + + const clientMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') + clientMock.mockImplementation(() => { + throw new Error('HTTP Error Occurred') + }) + + await restoreCache(inputPath, key) + + expect(logWarningMock).toHaveBeenCalledTimes(1) + expect(logWarningMock).toHaveBeenCalledWith('HTTP Error Occurred') + expect(failedMock).toHaveBeenCalledTimes(0) +}) + +test('restore with restore keys and no cache found', async () => { + const inputPath = 'node_modules' + const key = 'node-test' + const restoreKey = 'node-' + + const infoMock = jest.spyOn(core, 'info') + const failedMock = jest.spyOn(core, 'setFailed') + + const clientMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') + clientMock.mockImplementation(async () => { + return Promise.resolve(null) + }) + + await restoreCache(inputPath, key, [restoreKey]) + + expect(failedMock).toHaveBeenCalledTimes(0) + expect(infoMock).toHaveBeenCalledWith( + `Cache not found for input keys: ${key}, ${restoreKey}` + ) +}) + +test('restore with gzip compressed cache found', async () => { + const inputPath = 'node_modules' + const key = 'node-test' + + const infoMock = jest.spyOn(core, 'info') + const failedMock = jest.spyOn(core, 'setFailed') + + const cacheEntry: ArtifactCacheEntry = { + cacheKey: key, + scope: 'refs/heads/master', + archiveLocation: 'www.actionscache.test/download' + } + const getCacheMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') + getCacheMock.mockImplementation(async () => { + return Promise.resolve(cacheEntry) + }) + + const tempPath = '/foo/bar' + + const createTempDirectoryMock = jest.spyOn(cacheUtils, 'createTempDirectory') + createTempDirectoryMock.mockImplementation(async () => { + return Promise.resolve(tempPath) + }) + + const archivePath = path.join(tempPath, CacheFilename.Gzip) + const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache') + + const fileSize = 142 + const getArchiveFileSizeMock = jest + .spyOn(cacheUtils, 'getArchiveFileSize') + .mockReturnValue(fileSize) + + const extractTarMock = jest.spyOn(tar, 'extractTar') + const unlinkFileMock = jest.spyOn(cacheUtils, 'unlinkFile') + + const compression = CompressionMethod.Gzip + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValue(Promise.resolve(compression)) + + await restoreCache(inputPath, key) + + expect(getCacheMock).toHaveBeenCalledWith([key], inputPath, { + compressionMethod: compression + }) + expect(createTempDirectoryMock).toHaveBeenCalledTimes(1) + expect(downloadCacheMock).toHaveBeenCalledWith( + cacheEntry.archiveLocation, + archivePath + ) + expect(getArchiveFileSizeMock).toHaveBeenCalledWith(archivePath) + + expect(extractTarMock).toHaveBeenCalledTimes(1) + expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression) + + expect(unlinkFileMock).toHaveBeenCalledTimes(1) + expect(unlinkFileMock).toHaveBeenCalledWith(archivePath) + + expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`) + expect(failedMock).toHaveBeenCalledTimes(0) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) + +test('restore with a pull request event and zstd compressed cache found', async () => { + const inputPath = 'node_modules' + const key = 'node-test' + + const infoMock = jest.spyOn(core, 'info') + const failedMock = jest.spyOn(core, 'setFailed') + + const cacheEntry: ArtifactCacheEntry = { + cacheKey: key, + scope: 'refs/heads/master', + archiveLocation: 'www.actionscache.test/download' + } + const getCacheMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') + getCacheMock.mockImplementation(async () => { + return Promise.resolve(cacheEntry) + }) + const tempPath = '/foo/bar' + + const createTempDirectoryMock = jest.spyOn(cacheUtils, 'createTempDirectory') + createTempDirectoryMock.mockImplementation(async () => { + return Promise.resolve(tempPath) + }) + + const archivePath = path.join(tempPath, CacheFilename.Zstd) + const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache') + + const fileSize = 62915000 + const getArchiveFileSizeMock = jest + .spyOn(cacheUtils, 'getArchiveFileSize') + .mockReturnValue(fileSize) + + const extractTarMock = jest.spyOn(tar, 'extractTar') + const compression = CompressionMethod.Zstd + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValue(Promise.resolve(compression)) + + await restoreCache(inputPath, key) + + expect(getCacheMock).toHaveBeenCalledWith([key], inputPath, { + compressionMethod: compression + }) + expect(createTempDirectoryMock).toHaveBeenCalledTimes(1) + expect(downloadCacheMock).toHaveBeenCalledWith( + cacheEntry.archiveLocation, + archivePath + ) + expect(getArchiveFileSizeMock).toHaveBeenCalledWith(archivePath) + expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~60 MB (62915000 B)`) + + expect(extractTarMock).toHaveBeenCalledTimes(1) + expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression) + + expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`) + expect(failedMock).toHaveBeenCalledTimes(0) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) + +test('restore with cache found for restore key', async () => { + const inputPath = 'node_modules' + const key = 'node-test' + const restoreKey = 'node-' + + const infoMock = jest.spyOn(core, 'info') + const failedMock = jest.spyOn(core, 'setFailed') + + const cacheEntry: ArtifactCacheEntry = { + cacheKey: restoreKey, + scope: 'refs/heads/master', + archiveLocation: 'www.actionscache.test/download' + } + const getCacheMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') + getCacheMock.mockImplementation(async () => { + return Promise.resolve(cacheEntry) + }) + const tempPath = '/foo/bar' + + const createTempDirectoryMock = jest.spyOn(cacheUtils, 'createTempDirectory') + createTempDirectoryMock.mockImplementation(async () => { + return Promise.resolve(tempPath) + }) + + const archivePath = path.join(tempPath, CacheFilename.Zstd) + const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache') + + const fileSize = 142 + const getArchiveFileSizeMock = jest + .spyOn(cacheUtils, 'getArchiveFileSize') + .mockReturnValue(fileSize) + + const extractTarMock = jest.spyOn(tar, 'extractTar') + const compression = CompressionMethod.Zstd + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValue(Promise.resolve(compression)) + + await restoreCache(inputPath, key, [restoreKey]) + + expect(getCacheMock).toHaveBeenCalledWith([key, restoreKey], inputPath, { + compressionMethod: compression + }) + expect(createTempDirectoryMock).toHaveBeenCalledTimes(1) + expect(downloadCacheMock).toHaveBeenCalledWith( + cacheEntry.archiveLocation, + archivePath + ) + expect(getArchiveFileSizeMock).toHaveBeenCalledWith(archivePath) + expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~0 MB (142 B)`) + + expect(extractTarMock).toHaveBeenCalledTimes(1) + expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression) + + expect(infoMock).toHaveBeenCalledWith( + `Cache restored from key: ${restoreKey}` + ) + expect(failedMock).toHaveBeenCalledTimes(0) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) diff --git a/packages/cache/__tests__/saveCache.test.ts b/packages/cache/__tests__/saveCache.test.ts new file mode 100644 index 0000000000..f134663459 --- /dev/null +++ b/packages/cache/__tests__/saveCache.test.ts @@ -0,0 +1,219 @@ +import * as core from '@actions/core' +import * as path from 'path' +import {saveCache} from '../src/cache' +import * as cacheHttpClient from '../src/internal/cacheHttpClient' +import * as cacheUtils from '../src/internal/cacheUtils' +import {CacheFilename, CompressionMethod} from '../src/internal/constants' +import * as tar from '../src/internal/tar' + +jest.mock('@actions/core') +jest.mock('../src/internal/cacheHttpClient') +jest.mock('../src/internal/cacheUtils') +jest.mock('../src/internal/tar') + +beforeAll(() => { + // eslint-disable-next-line @typescript-eslint/promise-function-async + jest.spyOn(cacheUtils, 'getCacheFileName').mockImplementation(cm => { + const actualUtils = jest.requireActual('../src/internal/cacheUtils') + return actualUtils.getCacheFileName(cm) + }) + + jest.spyOn(cacheUtils, 'resolvePaths').mockImplementation(async filePaths => { + return filePaths.map(x => path.resolve(x)) + }) + + jest.spyOn(cacheUtils, 'createTempDirectory').mockImplementation(async () => { + return Promise.resolve('/foo/bar') + }) +}) + +test('save with missing input outputs warning', async () => { + const logWarningMock = jest.spyOn(cacheUtils, 'logWarning') + const failedMock = jest.spyOn(core, 'setFailed') + + const inputPath = '' + const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' + + await saveCache(inputPath, primaryKey) + + expect(logWarningMock).toHaveBeenCalledWith( + 'Input required and not supplied: path' + ) + expect(logWarningMock).toHaveBeenCalledTimes(1) + expect(failedMock).toHaveBeenCalledTimes(0) +}) + +test('save with large cache outputs warning', async () => { + const logWarningMock = jest.spyOn(cacheUtils, 'logWarning') + const failedMock = jest.spyOn(core, 'setFailed') + + const inputPath = 'node_modules' + const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' + const cachePaths = [path.resolve(inputPath)] + + const createTarMock = jest.spyOn(tar, 'createTar') + + const cacheSize = 6 * 1024 * 1024 * 1024 //~6GB, over the 5GB limit + jest.spyOn(cacheUtils, 'getArchiveFileSize').mockImplementationOnce(() => { + return cacheSize + }) + const compression = CompressionMethod.Gzip + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValue(Promise.resolve(compression)) + + await saveCache(inputPath, primaryKey) + + const archiveFolder = '/foo/bar' + + expect(createTarMock).toHaveBeenCalledTimes(1) + expect(createTarMock).toHaveBeenCalledWith( + archiveFolder, + cachePaths, + compression + ) + expect(logWarningMock).toHaveBeenCalledTimes(1) + expect(logWarningMock).toHaveBeenCalledWith( + 'Cache size of ~6144 MB (6442450944 B) is over the 5GB limit, not saving cache.' + ) + expect(failedMock).toHaveBeenCalledTimes(0) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) + +test('save with reserve cache failure outputs warning', async () => { + const infoMock = jest.spyOn(core, 'info') + const logWarningMock = jest.spyOn(cacheUtils, 'logWarning') + const failedMock = jest.spyOn(core, 'setFailed') + + const inputPath = 'node_modules' + const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' + + const reserveCacheMock = jest + .spyOn(cacheHttpClient, 'reserveCache') + .mockImplementation(async () => { + return -1 + }) + + const createTarMock = jest.spyOn(tar, 'createTar') + const saveCacheMock = jest.spyOn(cacheHttpClient, 'saveCache') + const compression = CompressionMethod.Zstd + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValue(Promise.resolve(compression)) + + await saveCache(inputPath, primaryKey) + + expect(reserveCacheMock).toHaveBeenCalledTimes(1) + expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, inputPath, { + compressionMethod: compression + }) + + expect(infoMock).toHaveBeenCalledWith( + `Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.` + ) + + expect(createTarMock).toHaveBeenCalledTimes(0) + expect(saveCacheMock).toHaveBeenCalledTimes(0) + expect(logWarningMock).toHaveBeenCalledTimes(0) + expect(failedMock).toHaveBeenCalledTimes(0) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) + +test('save with server error outputs warning', async () => { + const logWarningMock = jest.spyOn(cacheUtils, 'logWarning') + const failedMock = jest.spyOn(core, 'setFailed') + + const inputPath = 'node_modules' + const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' + const cachePaths = [path.resolve(inputPath)] + + const cacheId = 4 + const reserveCacheMock = jest + .spyOn(cacheHttpClient, 'reserveCache') + .mockImplementation(async () => { + return cacheId + }) + + const createTarMock = jest.spyOn(tar, 'createTar') + + const saveCacheMock = jest + .spyOn(cacheHttpClient, 'saveCache') + .mockImplementationOnce(async () => { + throw new Error('HTTP Error Occurred') + }) + const compression = CompressionMethod.Zstd + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValue(Promise.resolve(compression)) + + await saveCache(inputPath, primaryKey) + + expect(reserveCacheMock).toHaveBeenCalledTimes(1) + expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, inputPath, { + compressionMethod: compression + }) + + const archiveFolder = '/foo/bar' + const archiveFile = path.join(archiveFolder, CacheFilename.Zstd) + + expect(createTarMock).toHaveBeenCalledTimes(1) + expect(createTarMock).toHaveBeenCalledWith( + archiveFolder, + cachePaths, + compression + ) + + expect(saveCacheMock).toHaveBeenCalledTimes(1) + expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archiveFile) + + expect(logWarningMock).toHaveBeenCalledTimes(1) + expect(logWarningMock).toHaveBeenCalledWith('HTTP Error Occurred') + + expect(failedMock).toHaveBeenCalledTimes(0) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) + +test('save with valid inputs uploads a cache', async () => { + const failedMock = jest.spyOn(core, 'setFailed') + + const inputPath = 'node_modules' + const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' + const cachePaths = [path.resolve(inputPath)] + + const cacheId = 4 + const reserveCacheMock = jest + .spyOn(cacheHttpClient, 'reserveCache') + .mockImplementation(async () => { + return cacheId + }) + const createTarMock = jest.spyOn(tar, 'createTar') + + const saveCacheMock = jest.spyOn(cacheHttpClient, 'saveCache') + const compression = CompressionMethod.Zstd + const getCompressionMock = jest + .spyOn(cacheUtils, 'getCompressionMethod') + .mockReturnValue(Promise.resolve(compression)) + + await saveCache(inputPath, primaryKey) + + expect(reserveCacheMock).toHaveBeenCalledTimes(1) + expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, inputPath, { + compressionMethod: compression + }) + + const archiveFolder = '/foo/bar' + const archiveFile = path.join(archiveFolder, CacheFilename.Zstd) + + expect(createTarMock).toHaveBeenCalledTimes(1) + expect(createTarMock).toHaveBeenCalledWith( + archiveFolder, + cachePaths, + compression + ) + + expect(saveCacheMock).toHaveBeenCalledTimes(1) + expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archiveFile) + + expect(failedMock).toHaveBeenCalledTimes(0) + expect(getCompressionMock).toHaveBeenCalledTimes(1) +}) diff --git a/packages/cache/__tests__/tar.test.ts b/packages/cache/__tests__/tar.test.ts new file mode 100644 index 0000000000..0aa6c78435 --- /dev/null +++ b/packages/cache/__tests__/tar.test.ts @@ -0,0 +1,191 @@ +import * as exec from '@actions/exec' +import * as io from '@actions/io' +import * as path from 'path' +import {CacheFilename, CompressionMethod} from '../src/internal/constants' +import * as tar from '../src/internal/tar' +import * as utils from '../src/internal/cacheUtils' +// eslint-disable-next-line @typescript-eslint/no-require-imports +import fs = require('fs') + +jest.mock('@actions/exec') +jest.mock('@actions/io') + +const IS_WINDOWS = process.platform === 'win32' + +function getTempDir(): string { + return path.join(__dirname, '_temp', 'tar') +} + +beforeAll(async () => { + jest.spyOn(io, 'which').mockImplementation(async tool => { + return tool + }) + + process.env['GITHUB_WORKSPACE'] = process.cwd() + await jest.requireActual('@actions/io').rmRF(getTempDir()) +}) + +afterAll(async () => { + delete process.env['GITHUB_WORKSPACE'] + await jest.requireActual('@actions/io').rmRF(getTempDir()) +}) + +test('zstd extract tar', async () => { + const mkdirMock = jest.spyOn(io, 'mkdirP') + const execMock = jest.spyOn(exec, 'exec') + + const archivePath = IS_WINDOWS + ? `${process.env['windir']}\\fakepath\\cache.tar` + : 'cache.tar' + const workspace = process.env['GITHUB_WORKSPACE'] + + await tar.extractTar(archivePath, CompressionMethod.Zstd) + + expect(mkdirMock).toHaveBeenCalledWith(workspace) + const tarPath = IS_WINDOWS + ? `${process.env['windir']}\\System32\\tar.exe` + : 'tar' + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + `"${tarPath}"`, + [ + '--use-compress-program', + 'zstd -d --long=30', + '-xf', + IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, + '-P', + '-C', + IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace + ], + {cwd: undefined} + ) +}) + +test('gzip extract tar', async () => { + const mkdirMock = jest.spyOn(io, 'mkdirP') + const execMock = jest.spyOn(exec, 'exec') + const archivePath = IS_WINDOWS + ? `${process.env['windir']}\\fakepath\\cache.tar` + : 'cache.tar' + const workspace = process.env['GITHUB_WORKSPACE'] + + await tar.extractTar(archivePath, CompressionMethod.Gzip) + + expect(mkdirMock).toHaveBeenCalledWith(workspace) + const tarPath = IS_WINDOWS + ? `${process.env['windir']}\\System32\\tar.exe` + : 'tar' + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + `"${tarPath}"`, + [ + '-z', + '-xf', + IS_WINDOWS ? archivePath.replace(/\\/g, '/') : archivePath, + '-P', + '-C', + IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace + ], + {cwd: undefined} + ) +}) + +test('gzip extract GNU tar on windows', async () => { + if (IS_WINDOWS) { + jest.spyOn(fs, 'existsSync').mockReturnValueOnce(false) + + const isGnuMock = jest + .spyOn(utils, 'useGnuTar') + .mockReturnValue(Promise.resolve(true)) + const execMock = jest.spyOn(exec, 'exec') + const archivePath = `${process.env['windir']}\\fakepath\\cache.tar` + const workspace = process.env['GITHUB_WORKSPACE'] + + await tar.extractTar(archivePath, CompressionMethod.Gzip) + + expect(isGnuMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + `"tar"`, + [ + '-z', + '-xf', + archivePath.replace(/\\/g, '/'), + '-P', + '-C', + workspace?.replace(/\\/g, '/'), + '--force-local' + ], + {cwd: undefined} + ) + } +}) + +test('zstd create tar', async () => { + const execMock = jest.spyOn(exec, 'exec') + + const archiveFolder = getTempDir() + const workspace = process.env['GITHUB_WORKSPACE'] + const sourceDirectories = ['~/.npm/cache', `${workspace}/dist`] + + await fs.promises.mkdir(archiveFolder, {recursive: true}) + + await tar.createTar(archiveFolder, sourceDirectories, CompressionMethod.Zstd) + + const tarPath = IS_WINDOWS + ? `${process.env['windir']}\\System32\\tar.exe` + : 'tar' + + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + `"${tarPath}"`, + [ + '--use-compress-program', + 'zstd -T0 --long=30', + '-cf', + IS_WINDOWS ? CacheFilename.Zstd.replace(/\\/g, '/') : CacheFilename.Zstd, + '-P', + '-C', + IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace, + '--files-from', + 'manifest.txt' + ], + { + cwd: archiveFolder + } + ) +}) + +test('gzip create tar', async () => { + const execMock = jest.spyOn(exec, 'exec') + + const archiveFolder = getTempDir() + const workspace = process.env['GITHUB_WORKSPACE'] + const sourceDirectories = ['~/.npm/cache', `${workspace}/dist`] + + await fs.promises.mkdir(archiveFolder, {recursive: true}) + + await tar.createTar(archiveFolder, sourceDirectories, CompressionMethod.Gzip) + + const tarPath = IS_WINDOWS + ? `${process.env['windir']}\\System32\\tar.exe` + : 'tar' + + expect(execMock).toHaveBeenCalledTimes(1) + expect(execMock).toHaveBeenCalledWith( + `"${tarPath}"`, + [ + '-z', + '-cf', + IS_WINDOWS ? CacheFilename.Gzip.replace(/\\/g, '/') : CacheFilename.Gzip, + '-P', + '-C', + IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace, + '--files-from', + 'manifest.txt' + ], + { + cwd: archiveFolder + } + ) +}) diff --git a/packages/cache/package-lock.json b/packages/cache/package-lock.json new file mode 100644 index 0000000000..4a263beb75 --- /dev/null +++ b/packages/cache/package-lock.json @@ -0,0 +1,32 @@ +{ + "name": "@actions/cache", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@actions/http-client": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz", + "integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==", + "requires": { + "tunnel": "0.0.6" + } + }, + "@types/uuid": { + "version": "3.4.9", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.9.tgz", + "integrity": "sha512-XDwyIlt/47l2kWLTzw/mtrpLdB+GPSskR2n/PIcPn+VYhVO77rGhRncIR5GPU0KRzXuqkDO+J5qqrG0Y8P6jzQ==", + "dev": true + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } +} diff --git a/packages/cache/package.json b/packages/cache/package.json new file mode 100644 index 0000000000..1454048cfa --- /dev/null +++ b/packages/cache/package.json @@ -0,0 +1,49 @@ +{ + "name": "@actions/cache", + "version": "0.0.0", + "preview": true, + "description": "Actions artifact cache lib", + "keywords": [ + "github", + "actions", + "cache" + ], + "homepage": "https://github.com/actions/toolkit/tree/master/packages/cache", + "license": "MIT", + "main": "lib/cache.js", + "types": "lib/cache.d.ts", + "directories": { + "lib": "lib", + "test": "__tests__" + }, + "files": [ + "lib" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/actions/toolkit.git", + "directory": "packages/cache" + }, + "scripts": { + "audit-moderate": "npm install && npm audit --audit-level=moderate", + "test": "echo \"Error: run tests from root\" && exit 1", + "tsc": "tsc" + }, + "bugs": { + "url": "https://github.com/actions/toolkit/issues" + }, + "dependencies": { + "@actions/core": "^1.2.4", + "@actions/exec": "^1.0.1", + "@actions/glob": "^0.1.0", + "@actions/http-client": "^1.0.8", + "@actions/io": "^1.0.1", + "uuid": "^3.3.3" + }, + "devDependencies": { + "@types/uuid": "^3.4.5" + } +} diff --git a/packages/cache/src/cache.ts b/packages/cache/src/cache.ts new file mode 100644 index 0000000000..4aab741e37 --- /dev/null +++ b/packages/cache/src/cache.ts @@ -0,0 +1,169 @@ +import * as core from '@actions/core' +import * as pathUtils from 'path' +import * as utils from './internal/cacheUtils' +import * as cacheHttpClient from './internal/cacheHttpClient' +import {createTar, extractTar} from './internal/tar' + +/** + * Restores cache from keys + * + * @param path a string representing files that were cached + * @param primaryKey an explicit key for restoring the cache + * @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key + * @returns string returns the key for the cache hit, otherwise return undefined + */ +export async function restoreCache( + path: string, + primaryKey: string, + restoreKeys?: string[] +): Promise { + try { + if (!path || path.length === 0) { + throw new Error('Input required and not supplied: path') + } + + restoreKeys = restoreKeys || [] + const keys = [primaryKey, ...restoreKeys] + + core.debug('Resolved Keys:') + core.debug(JSON.stringify(keys)) + + if (keys.length > 10) { + core.setFailed( + `Key Validation Error: Keys are limited to a maximum of 10.` + ) + return undefined + } + for (const key of keys) { + if (key.length > 512) { + core.setFailed( + `Key Validation Error: ${key} cannot be larger than 512 characters.` + ) + return undefined + } + const regex = /^[^,]*$/ + if (!regex.test(key)) { + core.setFailed(`Key Validation Error: ${key} cannot contain commas.`) + return undefined + } + } + + const compressionMethod = await utils.getCompressionMethod() + + try { + // path are needed to compute version + const cacheEntry = await cacheHttpClient.getCacheEntry(keys, path, { + compressionMethod + }) + if (!cacheEntry?.archiveLocation) { + core.info(`Cache not found for input keys: ${keys.join(', ')}`) + return undefined + } + + const archivePath = pathUtils.join( + await utils.createTempDirectory(), + utils.getCacheFileName(compressionMethod) + ) + core.debug(`Archive Path: ${archivePath}`) + + try { + // Download the cache from the cache entry + await cacheHttpClient.downloadCache( + cacheEntry.archiveLocation, + archivePath + ) + + const archiveFileSize = utils.getArchiveFileSize(archivePath) + core.info( + `Cache Size: ~${Math.round( + archiveFileSize / (1024 * 1024) + )} MB (${archiveFileSize} B)` + ) + + await extractTar(archivePath, compressionMethod) + } finally { + // Try to delete the archive to save space + try { + await utils.unlinkFile(archivePath) + } catch (error) { + core.debug(`Failed to delete archive: ${error}`) + } + } + + core.info(`Cache restored from key: ${cacheEntry && cacheEntry.cacheKey}`) + + return cacheEntry.cacheKey + } catch (error) { + utils.logWarning(error.message) + return undefined + } + } catch (error) { + core.setFailed(error.message) + return undefined + } +} + +/** + * Saves a file with the specified key + * + * @param path a string representing files to be cached + * @param key an explicit key for restoring the cache + * @returns number returns cacheId if the cache was saved successfully, otherwise return -1 + */ +export async function saveCache(path: string, key: string): Promise { + try { + if (!path || path.length === 0) { + throw new Error('Input required and not supplied: path') + } + + const compressionMethod = await utils.getCompressionMethod() + + core.debug('Reserving Cache') + const cacheId = await cacheHttpClient.reserveCache(key, path, { + compressionMethod + }) + if (cacheId === -1) { + core.info( + `Unable to reserve cache with key ${key}, another job may be creating this cache.` + ) + return -1 + } + core.debug(`Cache ID: ${cacheId}`) + const cachePaths = await utils.resolvePaths( + path.split('\n').filter(x => x !== '') + ) + + core.debug('Cache Paths:') + core.debug(`${JSON.stringify(cachePaths)}`) + + const archiveFolder = await utils.createTempDirectory() + const archivePath = pathUtils.join( + archiveFolder, + utils.getCacheFileName(compressionMethod) + ) + + core.debug(`Archive Path: ${archivePath}`) + + await createTar(archiveFolder, cachePaths, compressionMethod) + + const fileSizeLimit = 5 * 1024 * 1024 * 1024 // 5GB per repo limit + const archiveFileSize = utils.getArchiveFileSize(archivePath) + core.debug(`File Size: ${archiveFileSize}`) + if (archiveFileSize > fileSizeLimit) { + utils.logWarning( + `Cache size of ~${Math.round( + archiveFileSize / (1024 * 1024) + )} MB (${archiveFileSize} B) is over the 5GB limit, not saving cache.` + ) + return -1 + } + + core.debug(`Saving Cache (ID: ${cacheId})`) + await cacheHttpClient.saveCache(cacheId, archivePath) + + return cacheId + } catch (error) { + utils.logWarning(error.message) + return -1 + } +} diff --git a/packages/cache/src/internal/cacheHttpClient.ts b/packages/cache/src/internal/cacheHttpClient.ts new file mode 100644 index 0000000000..92e9498a67 --- /dev/null +++ b/packages/cache/src/internal/cacheHttpClient.ts @@ -0,0 +1,339 @@ +import * as core from '@actions/core' +import {HttpClient, HttpCodes} from '@actions/http-client' +import {BearerCredentialHandler} from '@actions/http-client/auth' +import { + IHttpClientResponse, + IRequestOptions, + ITypedResponse +} from '@actions/http-client/interfaces' +import * as crypto from 'crypto' +import * as fs from 'fs' +import * as stream from 'stream' +import * as util from 'util' + +import * as utils from './cacheUtils' +import {CompressionMethod, SocketTimeout} from './constants' +import { + ArtifactCacheEntry, + CacheOptions, + CommitCacheRequest, + ReserveCacheRequest, + ReserveCacheResponse +} from './contracts' + +const versionSalt = '1.0' + +function isSuccessStatusCode(statusCode?: number): boolean { + if (!statusCode) { + return false + } + return statusCode >= 200 && statusCode < 300 +} + +function isRetryableStatusCode(statusCode?: number): boolean { + if (!statusCode) { + return false + } + const retryableStatusCodes = [ + HttpCodes.BadGateway, + HttpCodes.ServiceUnavailable, + HttpCodes.GatewayTimeout + ] + return retryableStatusCodes.includes(statusCode) +} + +function getCacheApiUrl(resource: string): string { + // Ideally we just use ACTIONS_CACHE_URL + const baseUrl: string = ( + process.env['ACTIONS_CACHE_URL'] || + process.env['ACTIONS_RUNTIME_URL'] || + '' + ).replace('pipelines', 'artifactcache') + if (!baseUrl) { + throw new Error('Cache Service Url not found, unable to restore cache.') + } + + const url = `${baseUrl}_apis/artifactcache/${resource}` + core.debug(`Resource Url: ${url}`) + return url +} + +function createAcceptHeader(type: string, apiVersion: string): string { + return `${type};api-version=${apiVersion}` +} + +function getRequestOptions(): IRequestOptions { + const requestOptions: IRequestOptions = { + headers: { + Accept: createAcceptHeader('application/json', '6.0-preview.1') + } + } + + return requestOptions +} + +function createHttpClient(): HttpClient { + const token = process.env['ACTIONS_RUNTIME_TOKEN'] || '' + const bearerCredentialHandler = new BearerCredentialHandler(token) + + return new HttpClient( + 'actions/cache', + [bearerCredentialHandler], + getRequestOptions() + ) +} + +export function getCacheVersion( + inputPath: string, + compressionMethod?: CompressionMethod +): string { + const components = [inputPath].concat( + compressionMethod === CompressionMethod.Zstd ? [compressionMethod] : [] + ) + + // Add salt to cache version to support breaking changes in cache entry + components.push(versionSalt) + + return crypto + .createHash('sha256') + .update(components.join('|')) + .digest('hex') +} + +export async function getCacheEntry( + keys: string[], + inputPath: string, + options?: CacheOptions +): Promise { + const httpClient = createHttpClient() + const version = getCacheVersion(inputPath, options?.compressionMethod) + const resource = `cache?keys=${encodeURIComponent( + keys.join(',') + )}&version=${version}` + + const response = await httpClient.getJson( + getCacheApiUrl(resource) + ) + if (response.statusCode === 204) { + return null + } + if (!isSuccessStatusCode(response.statusCode)) { + throw new Error(`Cache service responded with ${response.statusCode}`) + } + + const cacheResult = response.result + const cacheDownloadUrl = cacheResult?.archiveLocation + if (!cacheDownloadUrl) { + throw new Error('Cache not found.') + } + core.setSecret(cacheDownloadUrl) + core.debug(`Cache Result:`) + core.debug(JSON.stringify(cacheResult)) + + return cacheResult +} + +async function pipeResponseToStream( + response: IHttpClientResponse, + output: NodeJS.WritableStream +): Promise { + const pipeline = util.promisify(stream.pipeline) + await pipeline(response.message, output) +} + +export async function downloadCache( + archiveLocation: string, + archivePath: string +): Promise { + const writableStream = fs.createWriteStream(archivePath) + const httpClient = new HttpClient('actions/cache') + const downloadResponse = await httpClient.get(archiveLocation) + + // Abort download if no traffic received over the socket. + downloadResponse.message.socket.setTimeout(SocketTimeout, () => { + downloadResponse.message.destroy() + core.debug(`Aborting download, socket timed out after ${SocketTimeout} ms`) + }) + + await pipeResponseToStream(downloadResponse, writableStream) + + // Validate download size. + const contentLengthHeader = downloadResponse.message.headers['content-length'] + + if (contentLengthHeader) { + const expectedLength = parseInt(contentLengthHeader) + const actualLength = utils.getArchiveFileSize(archivePath) + + if (actualLength !== expectedLength) { + throw new Error( + `Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}` + ) + } + } else { + core.debug('Unable to validate download, no Content-Length header') + } +} + +// Reserve Cache +export async function reserveCache( + key: string, + inputPath: string, + options?: CacheOptions +): Promise { + const httpClient = createHttpClient() + const version = getCacheVersion(inputPath, options?.compressionMethod) + + const reserveCacheRequest: ReserveCacheRequest = { + key, + version + } + const response = await httpClient.postJson( + getCacheApiUrl('caches'), + reserveCacheRequest + ) + return response?.result?.cacheId ?? -1 +} + +function getContentRange(start: number, end: number): string { + // Format: `bytes start-end/filesize + // start and end are inclusive + // filesize can be * + // For a 200 byte chunk starting at byte 0: + // Content-Range: bytes 0-199/* + return `bytes ${start}-${end}/*` +} + +async function uploadChunk( + httpClient: HttpClient, + resourceUrl: string, + data: NodeJS.ReadableStream, + start: number, + end: number +): Promise { + core.debug( + `Uploading chunk of size ${end - + start + + 1} bytes at offset ${start} with content range: ${getContentRange( + start, + end + )}` + ) + const additionalHeaders = { + 'Content-Type': 'application/octet-stream', + 'Content-Range': getContentRange(start, end) + } + + const uploadChunkRequest = async (): Promise => { + return await httpClient.sendStream( + 'PATCH', + resourceUrl, + data, + additionalHeaders + ) + } + + const response = await uploadChunkRequest() + if (isSuccessStatusCode(response.message.statusCode)) { + return + } + + if (isRetryableStatusCode(response.message.statusCode)) { + core.debug( + `Received ${response.message.statusCode}, retrying chunk at offset ${start}.` + ) + const retryResponse = await uploadChunkRequest() + if (isSuccessStatusCode(retryResponse.message.statusCode)) { + return + } + } + + throw new Error( + `Cache service responded with ${response.message.statusCode} during chunk upload.` + ) +} + +function parseEnvNumber(key: string): number | undefined { + const value = Number(process.env[key]) + if (Number.isNaN(value) || value < 0) { + return undefined + } + return value +} + +async function uploadFile( + httpClient: HttpClient, + cacheId: number, + archivePath: string +): Promise { + // Upload Chunks + const fileSize = fs.statSync(archivePath).size + const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`) + const fd = fs.openSync(archivePath, 'r') + + const concurrency = parseEnvNumber('CACHE_UPLOAD_CONCURRENCY') ?? 4 // # of HTTP requests in parallel + const MAX_CHUNK_SIZE = + parseEnvNumber('CACHE_UPLOAD_CHUNK_SIZE') ?? 32 * 1024 * 1024 // 32 MB Chunks + core.debug(`Concurrency: ${concurrency} and Chunk Size: ${MAX_CHUNK_SIZE}`) + + const parallelUploads = [...new Array(concurrency).keys()] + core.debug('Awaiting all uploads') + let offset = 0 + + try { + await Promise.all( + parallelUploads.map(async () => { + while (offset < fileSize) { + const chunkSize = Math.min(fileSize - offset, MAX_CHUNK_SIZE) + const start = offset + const end = offset + chunkSize - 1 + offset += MAX_CHUNK_SIZE + const chunk = fs.createReadStream(archivePath, { + fd, + start, + end, + autoClose: false + }) + + await uploadChunk(httpClient, resourceUrl, chunk, start, end) + } + }) + ) + } finally { + fs.closeSync(fd) + } + return +} + +async function commitCache( + httpClient: HttpClient, + cacheId: number, + filesize: number +): Promise> { + const commitCacheRequest: CommitCacheRequest = {size: filesize} + return await httpClient.postJson( + getCacheApiUrl(`caches/${cacheId.toString()}`), + commitCacheRequest + ) +} + +export async function saveCache( + cacheId: number, + archivePath: string +): Promise { + const httpClient = createHttpClient() + + core.debug('Upload cache') + await uploadFile(httpClient, cacheId, archivePath) + + // Commit Cache + core.debug('Commiting cache') + const cacheSize = utils.getArchiveFileSize(archivePath) + const commitCacheResponse = await commitCache(httpClient, cacheId, cacheSize) + if (!isSuccessStatusCode(commitCacheResponse.statusCode)) { + throw new Error( + `Cache service responded with ${commitCacheResponse.statusCode} during commit cache.` + ) + } + + core.info('Cache saved successfully') +} diff --git a/packages/cache/src/internal/cacheUtils.ts b/packages/cache/src/internal/cacheUtils.ts new file mode 100644 index 0000000000..8cce071f28 --- /dev/null +++ b/packages/cache/src/internal/cacheUtils.ts @@ -0,0 +1,104 @@ +import * as core from '@actions/core' +import * as exec from '@actions/exec' +import * as glob from '@actions/glob' +import * as io from '@actions/io' +import * as fs from 'fs' +import * as path from 'path' +import * as util from 'util' +import {v4 as uuidV4} from 'uuid' +import {CacheFilename, CompressionMethod} from './constants' + +// From https://github.com/actions/toolkit/blob/master/packages/tool-cache/src/tool-cache.ts#L23 +export async function createTempDirectory(): Promise { + const IS_WINDOWS = process.platform === 'win32' + + let tempDirectory: string = process.env['RUNNER_TEMP'] || '' + + if (!tempDirectory) { + let baseLocation: string + if (IS_WINDOWS) { + // On Windows use the USERPROFILE env variable + baseLocation = process.env['USERPROFILE'] || 'C:\\' + } else { + if (process.platform === 'darwin') { + baseLocation = '/Users' + } else { + baseLocation = '/home' + } + } + tempDirectory = path.join(baseLocation, 'actions', 'temp') + } + + const dest = path.join(tempDirectory, uuidV4()) + await io.mkdirP(dest) + return dest +} + +export function getArchiveFileSize(filePath: string): number { + return fs.statSync(filePath).size +} + +export function logWarning(message: string): void { + const warningPrefix = '[warning]' + core.info(`${warningPrefix}${message}`) +} + +export async function resolvePaths(patterns: string[]): Promise { + const paths: string[] = [] + const workspace = process.env['GITHUB_WORKSPACE'] ?? process.cwd() + const globber = await glob.create(patterns.join('\n'), { + implicitDescendants: false + }) + + for await (const file of globber.globGenerator()) { + const relativeFile = path.relative(workspace, file) + core.debug(`Matched: ${relativeFile}`) + // Paths are made relative so the tar entries are all relative to the root of the workspace. + paths.push(`${relativeFile}`) + } + + return paths +} + +export async function unlinkFile(filePath: fs.PathLike): Promise { + return util.promisify(fs.unlink)(filePath) +} + +async function getVersion(app: string): Promise { + core.debug(`Checking ${app} --version`) + let versionOutput = '' + try { + await exec.exec(`${app} --version`, [], { + ignoreReturnCode: true, + silent: true, + listeners: { + stdout: (data: Buffer): string => (versionOutput += data.toString()), + stderr: (data: Buffer): string => (versionOutput += data.toString()) + } + }) + } catch (err) { + core.debug(err.message) + } + + versionOutput = versionOutput.trim() + core.debug(versionOutput) + return versionOutput +} + +export async function getCompressionMethod(): Promise { + const versionOutput = await getVersion('zstd') + return versionOutput.toLowerCase().includes('zstd command line interface') + ? CompressionMethod.Zstd + : CompressionMethod.Gzip +} + +export function getCacheFileName(compressionMethod: CompressionMethod): string { + return compressionMethod === CompressionMethod.Zstd + ? CacheFilename.Zstd + : CacheFilename.Gzip +} + +export async function useGnuTar(): Promise { + const versionOutput = await getVersion('tar') + return versionOutput.toLowerCase().includes('gnu tar') +} diff --git a/packages/cache/src/internal/constants.ts b/packages/cache/src/internal/constants.ts new file mode 100644 index 0000000000..b3d2a57746 --- /dev/null +++ b/packages/cache/src/internal/constants.ts @@ -0,0 +1,14 @@ +export enum CacheFilename { + Gzip = 'cache.tgz', + Zstd = 'cache.tzst' +} + +export enum CompressionMethod { + Gzip = 'gzip', + Zstd = 'zstd' +} + +// Socket timeout in milliseconds during download. If no traffic is received +// over the socket during this period, the socket is destroyed and the download +// is aborted. +export const SocketTimeout = 5000 diff --git a/packages/cache/src/internal/contracts.d.ts b/packages/cache/src/internal/contracts.d.ts new file mode 100644 index 0000000000..ca3f36206f --- /dev/null +++ b/packages/cache/src/internal/contracts.d.ts @@ -0,0 +1,25 @@ +import {CompressionMethod} from './constants' + +export interface ArtifactCacheEntry { + cacheKey?: string + scope?: string + creationTime?: string + archiveLocation?: string +} + +export interface CommitCacheRequest { + size: number +} + +export interface ReserveCacheRequest { + key: string + version?: string +} + +export interface ReserveCacheResponse { + cacheId: number +} + +export interface CacheOptions { + compressionMethod?: CompressionMethod +} diff --git a/packages/cache/src/internal/tar.ts b/packages/cache/src/internal/tar.ts new file mode 100644 index 0000000000..221c7c704d --- /dev/null +++ b/packages/cache/src/internal/tar.ts @@ -0,0 +1,86 @@ +import {exec} from '@actions/exec' +import * as io from '@actions/io' +import {existsSync, writeFileSync} from 'fs' +import * as path from 'path' +import * as utils from './cacheUtils' +import {CompressionMethod} from './constants' + +async function getTarPath(args: string[]): Promise { + // Explicitly use BSD Tar on Windows + const IS_WINDOWS = process.platform === 'win32' + if (IS_WINDOWS) { + const systemTar = `${process.env['windir']}\\System32\\tar.exe` + if (existsSync(systemTar)) { + return systemTar + } else if (await utils.useGnuTar()) { + args.push('--force-local') + } + } + return await io.which('tar', true) +} + +async function execTar(args: string[], cwd?: string): Promise { + try { + await exec(`"${await getTarPath(args)}"`, args, {cwd}) + } catch (error) { + throw new Error(`Tar failed with error: ${error?.message}`) + } +} + +function getWorkingDirectory(): string { + return process.env['GITHUB_WORKSPACE'] ?? process.cwd() +} + +export async function extractTar( + archivePath: string, + compressionMethod: CompressionMethod +): Promise { + // Create directory to extract tar into + const workingDirectory = getWorkingDirectory() + await io.mkdirP(workingDirectory) + // --d: Decompress. + // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. + // Using 30 here because we also support 32-bit self-hosted runners. + const args = [ + ...(compressionMethod === CompressionMethod.Zstd + ? ['--use-compress-program', 'zstd -d --long=30'] + : ['-z']), + '-xf', + archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '-P', + '-C', + workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/') + ] + await execTar(args) +} + +export async function createTar( + archiveFolder: string, + sourceDirectories: string[], + compressionMethod: CompressionMethod +): Promise { + // Write source directories to manifest.txt to avoid command length limits + const manifestFilename = 'manifest.txt' + const cacheFileName = utils.getCacheFileName(compressionMethod) + writeFileSync( + path.join(archiveFolder, manifestFilename), + sourceDirectories.join('\n') + ) + // -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores. + // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. + // Using 30 here because we also support 32-bit self-hosted runners. + const workingDirectory = getWorkingDirectory() + const args = [ + ...(compressionMethod === CompressionMethod.Zstd + ? ['--use-compress-program', 'zstd -T0 --long=30'] + : ['-z']), + '-cf', + cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '-P', + '-C', + workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), + '--files-from', + manifestFilename + ] + await execTar(args, archiveFolder) +} diff --git a/packages/cache/tsconfig.json b/packages/cache/tsconfig.json new file mode 100644 index 0000000000..a8b812a6ff --- /dev/null +++ b/packages/cache/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "./lib", + "rootDir": "./src" + }, + "include": [ + "./src" + ] +} \ No newline at end of file From 7409ad5faea73337d7cf3081e29b8e34f7ae0e44 Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Wed, 6 May 2020 17:53:22 -0400 Subject: [PATCH 141/192] Change variable path to a list --- README.md | 2 +- packages/cache/README.md | 12 + .../cache/__tests__/cacheHttpClient.test.ts | 22 +- packages/cache/__tests__/cacheUtils.test.ts | 140 ---------- packages/cache/__tests__/restoreCache.test.ts | 81 +++--- packages/cache/__tests__/saveCache.test.ts | 100 +++---- packages/cache/src/cache.ts | 250 ++++++++---------- .../cache/src/internal/cacheHttpClient.ts | 12 +- packages/cache/src/internal/cacheUtils.ts | 5 - 9 files changed, 213 insertions(+), 411 deletions(-) diff --git a/README.md b/README.md index 8701ab5c6f..22fbc27c0a 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ $ npm install @actions/artifact --save Provides functions to interact with actions cache. Read more [here](packages/cache) ```bash -$ npm install @actions/artifact --save +$ npm install @actions/cache --save ```
diff --git a/packages/cache/README.md b/packages/cache/README.md index b65c7f3440..402a7f507b 100644 --- a/packages/cache/README.md +++ b/packages/cache/README.md @@ -1 +1,13 @@ # `@actions/cache` + +> Functions necessary for caching dependencies and build outputs to improve workflow execution time. + +## Usage + +#### Restore Cache + +#### Save Cache + +## Additional Documentation + +See ["Caching dependencies to speed up workflows"](https://help.github.com/github/automating-your-workflow-with-github-actions/caching-dependencies-to-speed-up-workflows). \ No newline at end of file diff --git a/packages/cache/__tests__/cacheHttpClient.test.ts b/packages/cache/__tests__/cacheHttpClient.test.ts index d21652801b..e5d7eacf92 100644 --- a/packages/cache/__tests__/cacheHttpClient.test.ts +++ b/packages/cache/__tests__/cacheHttpClient.test.ts @@ -1,17 +1,25 @@ import {getCacheVersion} from '../src/internal/cacheHttpClient' import {CompressionMethod} from '../src/internal/constants' -test('getCacheVersion with path input and compression method undefined returns version', async () => { - const inputPath = 'node_modules' - const result = getCacheVersion(inputPath) +test('getCacheVersion with one path returns version', async () => { + const paths = ['node_modules'] + const result = getCacheVersion(paths) expect(result).toEqual( 'b3e0c6cb5ecf32614eeb2997d905b9c297046d7cbf69062698f25b14b4cb0985' ) }) +test('getCacheVersion with multiple paths returns version', async () => { + const paths = ['node_modules', 'dist'] + const result = getCacheVersion(paths) + expect(result).toEqual( + '165c3053bc646bf0d4fac17b1f5731caca6fe38e0e464715c0c3c6b6318bf436' + ) +}) + test('getCacheVersion with zstd compression returns version', async () => { - const inputPath = 'node_modules' - const result = getCacheVersion(inputPath, CompressionMethod.Zstd) + const paths = ['node_modules'] + const result = getCacheVersion(paths, CompressionMethod.Zstd) expect(result).toEqual( '273877e14fd65d270b87a198edbfa2db5a43de567c9a548d2a2505b408befe24' @@ -19,8 +27,8 @@ test('getCacheVersion with zstd compression returns version', async () => { }) test('getCacheVersion with gzip compression does not change vesion', async () => { - const inputPath = 'node_modules' - const result = getCacheVersion(inputPath, CompressionMethod.Gzip) + const paths = ['node_modules'] + const result = getCacheVersion(paths, CompressionMethod.Gzip) expect(result).toEqual( 'b3e0c6cb5ecf32614eeb2997d905b9c297046d7cbf69062698f25b14b4cb0985' diff --git a/packages/cache/__tests__/cacheUtils.test.ts b/packages/cache/__tests__/cacheUtils.test.ts index b09eed1388..0a4b0f4c04 100644 --- a/packages/cache/__tests__/cacheUtils.test.ts +++ b/packages/cache/__tests__/cacheUtils.test.ts @@ -1,9 +1,6 @@ -import * as core from '@actions/core' import * as io from '@actions/io' import {promises as fs} from 'fs' -import * as os from 'os' import * as path from 'path' -import {v4 as uuidV4} from 'uuid' import * as cacheUtils from '../src/internal/cacheUtils' jest.mock('@actions/core') @@ -26,143 +23,6 @@ test('getArchiveFileSize returns file size', () => { expect(size).toBe(11) }) -test('logWarning logs a message with a warning prefix', () => { - const message = 'A warning occurred.' - - const infoMock = jest.spyOn(core, 'info') - - cacheUtils.logWarning(message) - - expect(infoMock).toHaveBeenCalledWith(`[warning]${message}`) -}) - -test('resolvePaths with no ~ in path', async () => { - const filePath = '.cache' - - // Create the following layout: - // cwd - // cwd/.cache - // cwd/.cache/file.txt - - const root = path.join(getTempDir(), 'no-tilde') - // tarball entries will be relative to workspace - process.env['GITHUB_WORKSPACE'] = root - - await fs.mkdir(root, {recursive: true}) - const cache = path.join(root, '.cache') - await fs.mkdir(cache, {recursive: true}) - await fs.writeFile(path.join(cache, 'file.txt'), 'cached') - - const originalCwd = process.cwd() - - try { - process.chdir(root) - - const resolvedPath = await cacheUtils.resolvePaths([filePath]) - - const expectedPath = [filePath] - expect(resolvedPath).toStrictEqual(expectedPath) - } finally { - process.chdir(originalCwd) - } -}) - -test('resolvePaths with ~ in path', async () => { - const cacheDir = uuidV4() - const filePath = `~/${cacheDir}` - // Create the following layout: - // ~/uuid - // ~/uuid/file.txt - - const homedir = jest.requireActual('os').homedir() - const homedirMock = jest.spyOn(os, 'homedir') - homedirMock.mockReturnValue(homedir) - - const target = path.join(homedir, cacheDir) - await fs.mkdir(target, {recursive: true}) - await fs.writeFile(path.join(target, 'file.txt'), 'cached') - - const root = getTempDir() - process.env['GITHUB_WORKSPACE'] = root - - try { - const resolvedPath = await cacheUtils.resolvePaths([filePath]) - - const expectedPath = [path.relative(root, target)] - expect(resolvedPath).toStrictEqual(expectedPath) - } finally { - await io.rmRF(target) - } -}) - -test('resolvePaths with home not found', async () => { - const filePath = '~/.cache/yarn' - const homedirMock = jest.spyOn(os, 'homedir') - homedirMock.mockReturnValue('') - - await expect(cacheUtils.resolvePaths([filePath])).rejects.toThrow( - 'Unable to determine HOME directory' - ) -}) - -test('resolvePaths inclusion pattern returns found', async () => { - const pattern = '*.ts' - // Create the following layout: - // inclusion-patterns - // inclusion-patterns/miss.txt - // inclusion-patterns/test.ts - - const root = path.join(getTempDir(), 'inclusion-patterns') - // tarball entries will be relative to workspace - process.env['GITHUB_WORKSPACE'] = root - - await fs.mkdir(root, {recursive: true}) - await fs.writeFile(path.join(root, 'miss.txt'), 'no match') - await fs.writeFile(path.join(root, 'test.ts'), 'match') - - const originalCwd = process.cwd() - - try { - process.chdir(root) - - const resolvedPath = await cacheUtils.resolvePaths([pattern]) - - const expectedPath = ['test.ts'] - expect(resolvedPath).toStrictEqual(expectedPath) - } finally { - process.chdir(originalCwd) - } -}) - -test('resolvePaths exclusion pattern returns not found', async () => { - const patterns = ['*.ts', '!test.ts'] - // Create the following layout: - // exclusion-patterns - // exclusion-patterns/miss.txt - // exclusion-patterns/test.ts - - const root = path.join(getTempDir(), 'exclusion-patterns') - // tarball entries will be relative to workspace - process.env['GITHUB_WORKSPACE'] = root - - await fs.mkdir(root, {recursive: true}) - await fs.writeFile(path.join(root, 'miss.txt'), 'no match') - await fs.writeFile(path.join(root, 'test.ts'), 'no match') - - const originalCwd = process.cwd() - - try { - process.chdir(root) - - const resolvedPath = await cacheUtils.resolvePaths(patterns) - - const expectedPath: string[] = [] - expect(resolvedPath).toStrictEqual(expectedPath) - } finally { - process.chdir(originalCwd) - } -}) - test('unlinkFile unlinks file', async () => { const testDirectory = await fs.mkdtemp('unlinkFileTest') const testFile = path.join(testDirectory, 'test.txt') diff --git a/packages/cache/__tests__/restoreCache.test.ts b/packages/cache/__tests__/restoreCache.test.ts index d1f016d6cc..5c807fcf52 100644 --- a/packages/cache/__tests__/restoreCache.test.ts +++ b/packages/cache/__tests__/restoreCache.test.ts @@ -20,112 +20,95 @@ beforeAll(() => { }) test('restore with no path should fail', async () => { - const inputPath = '' + const paths: string[] = [] const key = 'node-test' - const failedMock = jest.spyOn(core, 'setFailed') - await restoreCache(inputPath, key) - expect(failedMock).toHaveBeenCalledWith( - 'Input required and not supplied: path' + await expect(restoreCache(paths, key)).rejects.toThrowError( + `Path Validation Error: At least one directory or file path is required` ) }) test('restore with too many keys should fail', async () => { - const inputPath = 'node_modules' + const paths = ['node_modules'] const key = 'node-test' const restoreKeys = [...Array(20).keys()].map(x => x.toString()) - const failedMock = jest.spyOn(core, 'setFailed') - await restoreCache(inputPath, key, restoreKeys) - expect(failedMock).toHaveBeenCalledWith( + await expect(restoreCache(paths, key, restoreKeys)).rejects.toThrowError( `Key Validation Error: Keys are limited to a maximum of 10.` ) }) test('restore with large key should fail', async () => { - const inputPath = 'node_modules' + const paths = ['node_modules'] const key = 'foo'.repeat(512) // Over the 512 character limit - const failedMock = jest.spyOn(core, 'setFailed') - await restoreCache(inputPath, key) - expect(failedMock).toHaveBeenCalledWith( + await expect(restoreCache(paths, key)).rejects.toThrowError( `Key Validation Error: ${key} cannot be larger than 512 characters.` ) }) test('restore with invalid key should fail', async () => { - const inputPath = 'node_modules' + const paths = ['node_modules'] const key = 'comma,comma' - const failedMock = jest.spyOn(core, 'setFailed') - await restoreCache(inputPath, key) - expect(failedMock).toHaveBeenCalledWith( + await expect(restoreCache(paths, key)).rejects.toThrowError( `Key Validation Error: ${key} cannot contain commas.` ) }) test('restore with no cache found', async () => { - const inputPath = 'node_modules' + const paths = ['node_modules'] const key = 'node-test' const infoMock = jest.spyOn(core, 'info') - const failedMock = jest.spyOn(core, 'setFailed') - const clientMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') clientMock.mockImplementation(async () => { return Promise.resolve(null) }) - await restoreCache(inputPath, key) + const cacheKey = await restoreCache(paths, key) - expect(failedMock).toHaveBeenCalledTimes(0) + expect(cacheKey).toBe(undefined) expect(infoMock).toHaveBeenCalledWith( `Cache not found for input keys: ${key}` ) }) test('restore with server error should fail', async () => { - const inputPath = 'node_modules' + const paths = ['node_modules'] const key = 'node-test' - const logWarningMock = jest.spyOn(cacheUtils, 'logWarning') - const failedMock = jest.spyOn(core, 'setFailed') - const clientMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') clientMock.mockImplementation(() => { throw new Error('HTTP Error Occurred') }) - await restoreCache(inputPath, key) - - expect(logWarningMock).toHaveBeenCalledTimes(1) - expect(logWarningMock).toHaveBeenCalledWith('HTTP Error Occurred') - expect(failedMock).toHaveBeenCalledTimes(0) + await expect(restoreCache(paths, key)).rejects.toThrowError( + 'HTTP Error Occurred' + ) }) test('restore with restore keys and no cache found', async () => { - const inputPath = 'node_modules' + const paths = ['node_modules'] const key = 'node-test' const restoreKey = 'node-' const infoMock = jest.spyOn(core, 'info') - const failedMock = jest.spyOn(core, 'setFailed') const clientMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') clientMock.mockImplementation(async () => { return Promise.resolve(null) }) - await restoreCache(inputPath, key, [restoreKey]) + const cacheKey = await restoreCache(paths, key, [restoreKey]) - expect(failedMock).toHaveBeenCalledTimes(0) + expect(cacheKey).toBe(undefined) expect(infoMock).toHaveBeenCalledWith( `Cache not found for input keys: ${key}, ${restoreKey}` ) }) test('restore with gzip compressed cache found', async () => { - const inputPath = 'node_modules' + const paths = ['node_modules'] const key = 'node-test' const infoMock = jest.spyOn(core, 'info') - const failedMock = jest.spyOn(core, 'setFailed') const cacheEntry: ArtifactCacheEntry = { cacheKey: key, @@ -160,9 +143,10 @@ test('restore with gzip compressed cache found', async () => { .spyOn(cacheUtils, 'getCompressionMethod') .mockReturnValue(Promise.resolve(compression)) - await restoreCache(inputPath, key) + const cacheKey = await restoreCache(paths, key) - expect(getCacheMock).toHaveBeenCalledWith([key], inputPath, { + expect(cacheKey).toBe(key) + expect(getCacheMock).toHaveBeenCalledWith([key], paths, { compressionMethod: compression }) expect(createTempDirectoryMock).toHaveBeenCalledTimes(1) @@ -179,16 +163,14 @@ test('restore with gzip compressed cache found', async () => { expect(unlinkFileMock).toHaveBeenCalledWith(archivePath) expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`) - expect(failedMock).toHaveBeenCalledTimes(0) expect(getCompressionMock).toHaveBeenCalledTimes(1) }) test('restore with a pull request event and zstd compressed cache found', async () => { - const inputPath = 'node_modules' + const paths = ['node_modules'] const key = 'node-test' const infoMock = jest.spyOn(core, 'info') - const failedMock = jest.spyOn(core, 'setFailed') const cacheEntry: ArtifactCacheEntry = { cacheKey: key, @@ -220,9 +202,10 @@ test('restore with a pull request event and zstd compressed cache found', async .spyOn(cacheUtils, 'getCompressionMethod') .mockReturnValue(Promise.resolve(compression)) - await restoreCache(inputPath, key) + const cacheKey = await restoreCache(paths, key) - expect(getCacheMock).toHaveBeenCalledWith([key], inputPath, { + expect(cacheKey).toBe(key) + expect(getCacheMock).toHaveBeenCalledWith([key], paths, { compressionMethod: compression }) expect(createTempDirectoryMock).toHaveBeenCalledTimes(1) @@ -237,17 +220,15 @@ test('restore with a pull request event and zstd compressed cache found', async expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression) expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`) - expect(failedMock).toHaveBeenCalledTimes(0) expect(getCompressionMock).toHaveBeenCalledTimes(1) }) test('restore with cache found for restore key', async () => { - const inputPath = 'node_modules' + const paths = ['node_modules'] const key = 'node-test' const restoreKey = 'node-' const infoMock = jest.spyOn(core, 'info') - const failedMock = jest.spyOn(core, 'setFailed') const cacheEntry: ArtifactCacheEntry = { cacheKey: restoreKey, @@ -279,9 +260,10 @@ test('restore with cache found for restore key', async () => { .spyOn(cacheUtils, 'getCompressionMethod') .mockReturnValue(Promise.resolve(compression)) - await restoreCache(inputPath, key, [restoreKey]) + const cacheKey = await restoreCache(paths, key, [restoreKey]) - expect(getCacheMock).toHaveBeenCalledWith([key, restoreKey], inputPath, { + expect(cacheKey).toBe(restoreKey) + expect(getCacheMock).toHaveBeenCalledWith([key, restoreKey], paths, { compressionMethod: compression }) expect(createTempDirectoryMock).toHaveBeenCalledTimes(1) @@ -298,6 +280,5 @@ test('restore with cache found for restore key', async () => { expect(infoMock).toHaveBeenCalledWith( `Cache restored from key: ${restoreKey}` ) - expect(failedMock).toHaveBeenCalledTimes(0) expect(getCompressionMock).toHaveBeenCalledTimes(1) }) diff --git a/packages/cache/__tests__/saveCache.test.ts b/packages/cache/__tests__/saveCache.test.ts index f134663459..2339a1973f 100644 --- a/packages/cache/__tests__/saveCache.test.ts +++ b/packages/cache/__tests__/saveCache.test.ts @@ -1,4 +1,3 @@ -import * as core from '@actions/core' import * as path from 'path' import {saveCache} from '../src/cache' import * as cacheHttpClient from '../src/internal/cacheHttpClient' @@ -27,42 +26,31 @@ beforeAll(() => { }) }) -test('save with missing input outputs warning', async () => { - const logWarningMock = jest.spyOn(cacheUtils, 'logWarning') - const failedMock = jest.spyOn(core, 'setFailed') - - const inputPath = '' +test('save with missing input should fail', async () => { + const paths: string[] = [] const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' - - await saveCache(inputPath, primaryKey) - - expect(logWarningMock).toHaveBeenCalledWith( - 'Input required and not supplied: path' + await expect(saveCache(paths, primaryKey)).rejects.toThrowError( + `Path Validation Error: At least one directory or file path is required` ) - expect(logWarningMock).toHaveBeenCalledTimes(1) - expect(failedMock).toHaveBeenCalledTimes(0) }) -test('save with large cache outputs warning', async () => { - const logWarningMock = jest.spyOn(cacheUtils, 'logWarning') - const failedMock = jest.spyOn(core, 'setFailed') - - const inputPath = 'node_modules' +test('save with large cache outputs should fail', async () => { + const filePath = 'node_modules' const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' - const cachePaths = [path.resolve(inputPath)] + const cachePaths = [path.resolve(filePath)] const createTarMock = jest.spyOn(tar, 'createTar') const cacheSize = 6 * 1024 * 1024 * 1024 //~6GB, over the 5GB limit - jest.spyOn(cacheUtils, 'getArchiveFileSize').mockImplementationOnce(() => { - return cacheSize - }) + jest.spyOn(cacheUtils, 'getArchiveFileSize').mockReturnValue(cacheSize) const compression = CompressionMethod.Gzip const getCompressionMock = jest .spyOn(cacheUtils, 'getCompressionMethod') - .mockReturnValue(Promise.resolve(compression)) + .mockReturnValueOnce(Promise.resolve(compression)) - await saveCache(inputPath, primaryKey) + await expect(saveCache([filePath], primaryKey)).rejects.toThrowError( + 'Cache size of ~6144 MB (6442450944 B) is over the 5GB limit, not saving cache.' + ) const archiveFolder = '/foo/bar' @@ -72,20 +60,11 @@ test('save with large cache outputs warning', async () => { cachePaths, compression ) - expect(logWarningMock).toHaveBeenCalledTimes(1) - expect(logWarningMock).toHaveBeenCalledWith( - 'Cache size of ~6144 MB (6442450944 B) is over the 5GB limit, not saving cache.' - ) - expect(failedMock).toHaveBeenCalledTimes(0) expect(getCompressionMock).toHaveBeenCalledTimes(1) }) -test('save with reserve cache failure outputs warning', async () => { - const infoMock = jest.spyOn(core, 'info') - const logWarningMock = jest.spyOn(cacheUtils, 'logWarning') - const failedMock = jest.spyOn(core, 'setFailed') - - const inputPath = 'node_modules' +test('save with reserve cache failure should fail', async () => { + const paths = ['node_modules'] const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' const reserveCacheMock = jest @@ -99,33 +78,24 @@ test('save with reserve cache failure outputs warning', async () => { const compression = CompressionMethod.Zstd const getCompressionMock = jest .spyOn(cacheUtils, 'getCompressionMethod') - .mockReturnValue(Promise.resolve(compression)) - - await saveCache(inputPath, primaryKey) + .mockReturnValueOnce(Promise.resolve(compression)) + await expect(saveCache(paths, primaryKey)).rejects.toThrowError( + `Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.` + ) expect(reserveCacheMock).toHaveBeenCalledTimes(1) - expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, inputPath, { + expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, paths, { compressionMethod: compression }) - - expect(infoMock).toHaveBeenCalledWith( - `Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.` - ) - expect(createTarMock).toHaveBeenCalledTimes(0) expect(saveCacheMock).toHaveBeenCalledTimes(0) - expect(logWarningMock).toHaveBeenCalledTimes(0) - expect(failedMock).toHaveBeenCalledTimes(0) expect(getCompressionMock).toHaveBeenCalledTimes(1) }) -test('save with server error outputs warning', async () => { - const logWarningMock = jest.spyOn(cacheUtils, 'logWarning') - const failedMock = jest.spyOn(core, 'setFailed') - - const inputPath = 'node_modules' +test('save with server error should fail', async () => { + const filePath = 'node_modules' const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' - const cachePaths = [path.resolve(inputPath)] + const cachePaths = [path.resolve(filePath)] const cacheId = 4 const reserveCacheMock = jest @@ -144,12 +114,13 @@ test('save with server error outputs warning', async () => { const compression = CompressionMethod.Zstd const getCompressionMock = jest .spyOn(cacheUtils, 'getCompressionMethod') - .mockReturnValue(Promise.resolve(compression)) - - await saveCache(inputPath, primaryKey) + .mockReturnValueOnce(Promise.resolve(compression)) + await expect(await saveCache([filePath], primaryKey)).rejects.toThrowError( + 'HTTP Error Occurred' + ) expect(reserveCacheMock).toHaveBeenCalledTimes(1) - expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, inputPath, { + expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, [filePath], { compressionMethod: compression }) @@ -165,20 +136,13 @@ test('save with server error outputs warning', async () => { expect(saveCacheMock).toHaveBeenCalledTimes(1) expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archiveFile) - - expect(logWarningMock).toHaveBeenCalledTimes(1) - expect(logWarningMock).toHaveBeenCalledWith('HTTP Error Occurred') - - expect(failedMock).toHaveBeenCalledTimes(0) expect(getCompressionMock).toHaveBeenCalledTimes(1) }) test('save with valid inputs uploads a cache', async () => { - const failedMock = jest.spyOn(core, 'setFailed') - - const inputPath = 'node_modules' + const filePath = 'node_modules' const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43' - const cachePaths = [path.resolve(inputPath)] + const cachePaths = [path.resolve(filePath)] const cacheId = 4 const reserveCacheMock = jest @@ -194,10 +158,10 @@ test('save with valid inputs uploads a cache', async () => { .spyOn(cacheUtils, 'getCompressionMethod') .mockReturnValue(Promise.resolve(compression)) - await saveCache(inputPath, primaryKey) + await saveCache([filePath], primaryKey) expect(reserveCacheMock).toHaveBeenCalledTimes(1) - expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, inputPath, { + expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, [filePath], { compressionMethod: compression }) @@ -213,7 +177,5 @@ test('save with valid inputs uploads a cache', async () => { expect(saveCacheMock).toHaveBeenCalledTimes(1) expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archiveFile) - - expect(failedMock).toHaveBeenCalledTimes(0) expect(getCompressionMock).toHaveBeenCalledTimes(1) }) diff --git a/packages/cache/src/cache.ts b/packages/cache/src/cache.ts index 4aab741e37..cf9be5eb77 100644 --- a/packages/cache/src/cache.ts +++ b/packages/cache/src/cache.ts @@ -1,169 +1,153 @@ import * as core from '@actions/core' -import * as pathUtils from 'path' +import * as path from 'path' import * as utils from './internal/cacheUtils' import * as cacheHttpClient from './internal/cacheHttpClient' import {createTar, extractTar} from './internal/tar' +function checkPaths(paths: string[]): void { + if (!paths || paths.length === 0) { + throw new Error( + `Path Validation Error: At least one directory or file path is required` + ) + } +} + +function checkKey(key: string): void { + if (key.length > 512) { + throw new Error( + `Key Validation Error: ${key} cannot be larger than 512 characters.` + ) + } + const regex = /^[^,]*$/ + if (!regex.test(key)) { + throw new Error(`Key Validation Error: ${key} cannot contain commas.`) + } +} + /** * Restores cache from keys * - * @param path a string representing files that were cached + * @param paths a list of file paths to restore from the cache * @param primaryKey an explicit key for restoring the cache * @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key * @returns string returns the key for the cache hit, otherwise return undefined */ export async function restoreCache( - path: string, + paths: string[], primaryKey: string, restoreKeys?: string[] ): Promise { - try { - if (!path || path.length === 0) { - throw new Error('Input required and not supplied: path') - } + checkPaths(paths) - restoreKeys = restoreKeys || [] - const keys = [primaryKey, ...restoreKeys] + restoreKeys = restoreKeys || [] + const keys = [primaryKey, ...restoreKeys] - core.debug('Resolved Keys:') - core.debug(JSON.stringify(keys)) + core.debug('Resolved Keys:') + core.debug(JSON.stringify(keys)) - if (keys.length > 10) { - core.setFailed( - `Key Validation Error: Keys are limited to a maximum of 10.` - ) - return undefined - } - for (const key of keys) { - if (key.length > 512) { - core.setFailed( - `Key Validation Error: ${key} cannot be larger than 512 characters.` - ) - return undefined - } - const regex = /^[^,]*$/ - if (!regex.test(key)) { - core.setFailed(`Key Validation Error: ${key} cannot contain commas.`) - return undefined - } - } + if (keys.length > 10) { + throw new Error( + `Key Validation Error: Keys are limited to a maximum of 10.` + ) + } + for (const key of keys) { + checkKey(key) + } + + const compressionMethod = await utils.getCompressionMethod() + + // path are needed to compute version + const cacheEntry = await cacheHttpClient.getCacheEntry(keys, paths, { + compressionMethod + }) + if (!cacheEntry?.archiveLocation) { + core.info(`Cache not found for input keys: ${keys.join(', ')}`) + return undefined + } + + const archivePath = path.join( + await utils.createTempDirectory(), + utils.getCacheFileName(compressionMethod) + ) + core.debug(`Archive Path: ${archivePath}`) - const compressionMethod = await utils.getCompressionMethod() + try { + // Download the cache from the cache entry + await cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath) + + const archiveFileSize = utils.getArchiveFileSize(archivePath) + core.info( + `Cache Size: ~${Math.round( + archiveFileSize / (1024 * 1024) + )} MB (${archiveFileSize} B)` + ) + await extractTar(archivePath, compressionMethod) + } finally { + // Try to delete the archive to save space try { - // path are needed to compute version - const cacheEntry = await cacheHttpClient.getCacheEntry(keys, path, { - compressionMethod - }) - if (!cacheEntry?.archiveLocation) { - core.info(`Cache not found for input keys: ${keys.join(', ')}`) - return undefined - } - - const archivePath = pathUtils.join( - await utils.createTempDirectory(), - utils.getCacheFileName(compressionMethod) - ) - core.debug(`Archive Path: ${archivePath}`) - - try { - // Download the cache from the cache entry - await cacheHttpClient.downloadCache( - cacheEntry.archiveLocation, - archivePath - ) - - const archiveFileSize = utils.getArchiveFileSize(archivePath) - core.info( - `Cache Size: ~${Math.round( - archiveFileSize / (1024 * 1024) - )} MB (${archiveFileSize} B)` - ) - - await extractTar(archivePath, compressionMethod) - } finally { - // Try to delete the archive to save space - try { - await utils.unlinkFile(archivePath) - } catch (error) { - core.debug(`Failed to delete archive: ${error}`) - } - } - - core.info(`Cache restored from key: ${cacheEntry && cacheEntry.cacheKey}`) - - return cacheEntry.cacheKey + await utils.unlinkFile(archivePath) } catch (error) { - utils.logWarning(error.message) - return undefined + core.debug(`Failed to delete archive: ${error}`) } - } catch (error) { - core.setFailed(error.message) - return undefined } + + core.info(`Cache restored from key: ${cacheEntry && cacheEntry.cacheKey}`) + + return cacheEntry.cacheKey } /** - * Saves a file with the specified key + * Saves a list of files with the specified key * - * @param path a string representing files to be cached + * @param paths a list of file paths to be cached * @param key an explicit key for restoring the cache - * @returns number returns cacheId if the cache was saved successfully, otherwise return -1 + * @returns number returns cacheId if the cache was saved successfully */ -export async function saveCache(path: string, key: string): Promise { - try { - if (!path || path.length === 0) { - throw new Error('Input required and not supplied: path') - } - - const compressionMethod = await utils.getCompressionMethod() - - core.debug('Reserving Cache') - const cacheId = await cacheHttpClient.reserveCache(key, path, { - compressionMethod - }) - if (cacheId === -1) { - core.info( - `Unable to reserve cache with key ${key}, another job may be creating this cache.` - ) - return -1 - } - core.debug(`Cache ID: ${cacheId}`) - const cachePaths = await utils.resolvePaths( - path.split('\n').filter(x => x !== '') +export async function saveCache(paths: string[], key: string): Promise { + checkPaths(paths) + checkKey(key) + + const compressionMethod = await utils.getCompressionMethod() + + core.debug('Reserving Cache') + const cacheId = await cacheHttpClient.reserveCache(key, paths, { + compressionMethod + }) + if (cacheId === -1) { + throw new Error( + `Unable to reserve cache with key ${key}, another job may be creating this cache.` ) - - core.debug('Cache Paths:') - core.debug(`${JSON.stringify(cachePaths)}`) - - const archiveFolder = await utils.createTempDirectory() - const archivePath = pathUtils.join( - archiveFolder, - utils.getCacheFileName(compressionMethod) + } + core.debug(`Cache ID: ${cacheId}`) + const cachePaths = await utils.resolvePaths(paths) + + core.debug('Cache Paths:') + core.debug(`${JSON.stringify(cachePaths)}`) + + const archiveFolder = await utils.createTempDirectory() + const archivePath = path.join( + archiveFolder, + utils.getCacheFileName(compressionMethod) + ) + + core.debug(`Archive Path: ${archivePath}`) + + await createTar(archiveFolder, cachePaths, compressionMethod) + + const fileSizeLimit = 5 * 1024 * 1024 * 1024 // 5GB per repo limit + const archiveFileSize = utils.getArchiveFileSize(archivePath) + core.debug(`File Size: ${archiveFileSize}`) + if (archiveFileSize > fileSizeLimit) { + throw new Error( + `Cache size of ~${Math.round( + archiveFileSize / (1024 * 1024) + )} MB (${archiveFileSize} B) is over the 5GB limit, not saving cache.` ) + } - core.debug(`Archive Path: ${archivePath}`) - - await createTar(archiveFolder, cachePaths, compressionMethod) - - const fileSizeLimit = 5 * 1024 * 1024 * 1024 // 5GB per repo limit - const archiveFileSize = utils.getArchiveFileSize(archivePath) - core.debug(`File Size: ${archiveFileSize}`) - if (archiveFileSize > fileSizeLimit) { - utils.logWarning( - `Cache size of ~${Math.round( - archiveFileSize / (1024 * 1024) - )} MB (${archiveFileSize} B) is over the 5GB limit, not saving cache.` - ) - return -1 - } - - core.debug(`Saving Cache (ID: ${cacheId})`) - await cacheHttpClient.saveCache(cacheId, archivePath) + core.debug(`Saving Cache (ID: ${cacheId})`) + await cacheHttpClient.saveCache(cacheId, archivePath) - return cacheId - } catch (error) { - utils.logWarning(error.message) - return -1 - } + return cacheId } diff --git a/packages/cache/src/internal/cacheHttpClient.ts b/packages/cache/src/internal/cacheHttpClient.ts index 92e9498a67..f4f8c4d1a4 100644 --- a/packages/cache/src/internal/cacheHttpClient.ts +++ b/packages/cache/src/internal/cacheHttpClient.ts @@ -84,10 +84,10 @@ function createHttpClient(): HttpClient { } export function getCacheVersion( - inputPath: string, + paths: string[], compressionMethod?: CompressionMethod ): string { - const components = [inputPath].concat( + const components = paths.concat( compressionMethod === CompressionMethod.Zstd ? [compressionMethod] : [] ) @@ -102,11 +102,11 @@ export function getCacheVersion( export async function getCacheEntry( keys: string[], - inputPath: string, + paths: string[], options?: CacheOptions ): Promise { const httpClient = createHttpClient() - const version = getCacheVersion(inputPath, options?.compressionMethod) + const version = getCacheVersion(paths, options?.compressionMethod) const resource = `cache?keys=${encodeURIComponent( keys.join(',') )}&version=${version}` @@ -177,11 +177,11 @@ export async function downloadCache( // Reserve Cache export async function reserveCache( key: string, - inputPath: string, + paths: string[], options?: CacheOptions ): Promise { const httpClient = createHttpClient() - const version = getCacheVersion(inputPath, options?.compressionMethod) + const version = getCacheVersion(paths, options?.compressionMethod) const reserveCacheRequest: ReserveCacheRequest = { key, diff --git a/packages/cache/src/internal/cacheUtils.ts b/packages/cache/src/internal/cacheUtils.ts index 8cce071f28..8743963aec 100644 --- a/packages/cache/src/internal/cacheUtils.ts +++ b/packages/cache/src/internal/cacheUtils.ts @@ -38,11 +38,6 @@ export function getArchiveFileSize(filePath: string): number { return fs.statSync(filePath).size } -export function logWarning(message: string): void { - const warningPrefix = '[warning]' - core.info(`${warningPrefix}${message}`) -} - export async function resolvePaths(patterns: string[]): Promise { const paths: string[] = [] const workspace = process.env['GITHUB_WORKSPACE'] ?? process.cwd() From 15fefd93366e98ede1b0739e6e461e85cfbb04c4 Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Wed, 6 May 2020 20:07:39 -0400 Subject: [PATCH 142/192] Fix tests --- README.md | 2 +- packages/cache/RELEASES.md | 2 +- packages/cache/__tests__/restoreCache.test.ts | 9 +++------ packages/cache/__tests__/saveCache.test.ts | 6 +++--- packages/cache/package-lock.json | 2 +- packages/cache/package.json | 4 ++-- 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 22fbc27c0a..df36e70bf3 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ $ npm install @actions/artifact --save :dart: [@actions/cache](packages/cache) -Provides functions to interact with actions cache. Read more [here](packages/cache) +Provides functions to cache dependencies and build outputs to improve workflow execution time.. Read more [here](packages/cache) ```bash $ npm install @actions/cache --save diff --git a/packages/cache/RELEASES.md b/packages/cache/RELEASES.md index b47fc55002..1250c0507c 100644 --- a/packages/cache/RELEASES.md +++ b/packages/cache/RELEASES.md @@ -1,5 +1,5 @@ # @actions/cache Releases -### 0.0.0 +### 1.0.0 - Initial release \ No newline at end of file diff --git a/packages/cache/__tests__/restoreCache.test.ts b/packages/cache/__tests__/restoreCache.test.ts index 5c807fcf52..b466b9afff 100644 --- a/packages/cache/__tests__/restoreCache.test.ts +++ b/packages/cache/__tests__/restoreCache.test.ts @@ -57,8 +57,7 @@ test('restore with no cache found', async () => { const key = 'node-test' const infoMock = jest.spyOn(core, 'info') - const clientMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') - clientMock.mockImplementation(async () => { + jest.spyOn(cacheHttpClient, 'getCacheEntry').mockImplementation(async () => { return Promise.resolve(null) }) @@ -74,8 +73,7 @@ test('restore with server error should fail', async () => { const paths = ['node_modules'] const key = 'node-test' - const clientMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') - clientMock.mockImplementation(() => { + jest.spyOn(cacheHttpClient, 'getCacheEntry').mockImplementation(() => { throw new Error('HTTP Error Occurred') }) @@ -91,8 +89,7 @@ test('restore with restore keys and no cache found', async () => { const infoMock = jest.spyOn(core, 'info') - const clientMock = jest.spyOn(cacheHttpClient, 'getCacheEntry') - clientMock.mockImplementation(async () => { + jest.spyOn(cacheHttpClient, 'getCacheEntry').mockImplementation(async () => { return Promise.resolve(null) }) diff --git a/packages/cache/__tests__/saveCache.test.ts b/packages/cache/__tests__/saveCache.test.ts index 2339a1973f..2fc379b345 100644 --- a/packages/cache/__tests__/saveCache.test.ts +++ b/packages/cache/__tests__/saveCache.test.ts @@ -42,7 +42,7 @@ test('save with large cache outputs should fail', async () => { const createTarMock = jest.spyOn(tar, 'createTar') const cacheSize = 6 * 1024 * 1024 * 1024 //~6GB, over the 5GB limit - jest.spyOn(cacheUtils, 'getArchiveFileSize').mockReturnValue(cacheSize) + jest.spyOn(cacheUtils, 'getArchiveFileSize').mockReturnValueOnce(cacheSize) const compression = CompressionMethod.Gzip const getCompressionMock = jest .spyOn(cacheUtils, 'getCompressionMethod') @@ -108,7 +108,7 @@ test('save with server error should fail', async () => { const saveCacheMock = jest .spyOn(cacheHttpClient, 'saveCache') - .mockImplementationOnce(async () => { + .mockImplementationOnce(() => { throw new Error('HTTP Error Occurred') }) const compression = CompressionMethod.Zstd @@ -116,7 +116,7 @@ test('save with server error should fail', async () => { .spyOn(cacheUtils, 'getCompressionMethod') .mockReturnValueOnce(Promise.resolve(compression)) - await expect(await saveCache([filePath], primaryKey)).rejects.toThrowError( + await expect(saveCache([filePath], primaryKey)).rejects.toThrowError( 'HTTP Error Occurred' ) expect(reserveCacheMock).toHaveBeenCalledTimes(1) diff --git a/packages/cache/package-lock.json b/packages/cache/package-lock.json index 4a263beb75..569bab49b5 100644 --- a/packages/cache/package-lock.json +++ b/packages/cache/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/cache", - "version": "0.0.0", + "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/cache/package.json b/packages/cache/package.json index 1454048cfa..2ee2d9bbc8 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -1,8 +1,8 @@ { "name": "@actions/cache", - "version": "0.0.0", + "version": "1.0.0", "preview": true, - "description": "Actions artifact cache lib", + "description": "Actions cache lib", "keywords": [ "github", "actions", From c534ad2cbd9ce38d33cdeaee134869afd430876d Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Thu, 7 May 2020 15:03:20 -0400 Subject: [PATCH 143/192] Add docs and tests --- .github/workflows/cache-tests.yml | 61 +++++++++++++++++++ README.md | 4 +- packages/cache/CONTRIBUTIONS.md | 0 packages/cache/README.md | 28 +++++++++ .../cache/__tests__/__fixtures__/action.yml | 5 ++ .../cache/__tests__/__fixtures__/index.js | 5 ++ packages/cache/package-lock.json | 54 ++++++++++++++++ scripts/create-cache-files.sh | 17 ++++++ scripts/verify-cache-files.sh | 36 +++++++++++ 9 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/cache-tests.yml delete mode 100644 packages/cache/CONTRIBUTIONS.md create mode 100644 packages/cache/__tests__/__fixtures__/action.yml create mode 100644 packages/cache/__tests__/__fixtures__/index.js create mode 100755 scripts/create-cache-files.sh create mode 100755 scripts/verify-cache-files.sh diff --git a/.github/workflows/cache-tests.yml b/.github/workflows/cache-tests.yml new file mode 100644 index 0000000000..aa382ffc52 --- /dev/null +++ b/.github/workflows/cache-tests.yml @@ -0,0 +1,61 @@ +name: cache-unit-tests +on: push + +jobs: + build: + name: Build + + strategy: + matrix: + runs-on: [ubuntu-latest, windows-latest, macOS-latest] + fail-fast: false + + runs-on: ${{ matrix.runs-on }} + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Set Node.js 12.x + uses: actions/setup-node@v1 + with: + node-version: 12.x + + # In order to save & restore cache artifacts from a shell script, certain env variables need to be set that are only available in the + # node context. This runs a local action that gets and sets the necessary env variables that are needed + - name: Set env variables + uses: ./packages/cache/__tests__/__fixtures__/ + + # Need root node_modules because certain npm packages like jest are configured for the entire repository and it won't be possible + # without these to just compile the cache package + - name: Install root npm packages + run: npm ci + + - name: Compile cache package + run: | + npm ci + npm run tsc + working-directory: packages/cache + + - name: Generate files in working directory + shell: bash + run: scripts/create-cache-files.sh ${{ runner.os }} test-cache + + - name: Generate files outside working directory + shell: bash + run: scripts/create-cache-files.sh ${{ runner.os }} ~/test-cache + + # We're using node -e to call the functions directly available in the @actions/cache package + - name: Save cache using saveCache() + run: | + node -e "Promise.resolve(require('./packages/cache/lib/cache').saveCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}'))" + + - name: Restore cache using restoreCache() + run: | + node -e "Promise.resolve(require('./packages/cache/lib/cache').restoreCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}'))" + + - name: Verify cache + shell: bash + run: | + scripts/verify-cache-files.sh ${{ runner.os }} test-cache + scripts/verify-cache-files.sh ${{ runner.os }} ~/test-cache \ No newline at end of file diff --git a/README.md b/README.md index df36e70bf3..4ffb0d4421 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,8 @@ $ npm install @actions/io --save Provides functions for downloading and caching tools. e.g. setup-* actions. Read more [here](packages/tool-cache) +See @actions/cache for caching workflow dependencies. + ```bash $ npm install @actions/tool-cache --save ``` @@ -84,7 +86,7 @@ $ npm install @actions/artifact --save :dart: [@actions/cache](packages/cache) -Provides functions to cache dependencies and build outputs to improve workflow execution time.. Read more [here](packages/cache) +Provides functions to cache dependencies and build outputs to improve workflow execution time. Read more [here](packages/cache) ```bash $ npm install @actions/cache --save diff --git a/packages/cache/CONTRIBUTIONS.md b/packages/cache/CONTRIBUTIONS.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/cache/README.md b/packages/cache/README.md index 402a7f507b..f15d0b1672 100644 --- a/packages/cache/README.md +++ b/packages/cache/README.md @@ -6,8 +6,36 @@ #### Restore Cache +Restores a cache based on `key` and `restoreKeys` to the `paths` provided. Function returns the cache key for cache hit and returns undefined if cache not found. + +```js +const cache = require('@actions/cache'); +const paths = [ + 'node_modules', + 'packages/*/node_modules/' +] +const key = 'npm-foobar-d5ea0750' +const restoreKeys = [ + 'npm-foobar-', + 'npm-' +] +const cacheKey = await cache.restoreCache(paths, key, restoreKeys) +``` + #### Save Cache +Saves a cache containing the files in `paths` using the `key` provided. Function returns the cache id if the cache was save succesfully. + +```js +const cache = require('@actions/cache'); +const paths = [ + 'node_modules', + 'packages/*/node_modules/' +] +const key = 'npm-foobar-d5ea0750' +const cacheId = await cache.saveCache(paths, key) +``` + ## Additional Documentation See ["Caching dependencies to speed up workflows"](https://help.github.com/github/automating-your-workflow-with-github-actions/caching-dependencies-to-speed-up-workflows). \ No newline at end of file diff --git a/packages/cache/__tests__/__fixtures__/action.yml b/packages/cache/__tests__/__fixtures__/action.yml new file mode 100644 index 0000000000..7cd9850203 --- /dev/null +++ b/packages/cache/__tests__/__fixtures__/action.yml @@ -0,0 +1,5 @@ +name: 'Set env variables' +description: 'Sets certain env variables so that e2e restore and save cache can be tested in a shell' +runs: + using: 'node12' + main: 'index.js' \ No newline at end of file diff --git a/packages/cache/__tests__/__fixtures__/index.js b/packages/cache/__tests__/__fixtures__/index.js new file mode 100644 index 0000000000..82bc6c43e5 --- /dev/null +++ b/packages/cache/__tests__/__fixtures__/index.js @@ -0,0 +1,5 @@ +// Certain env variables are not set by default in a shell context and are only available in a node context from a running action +// In order to be able to restore and save cache e2e in a shell when running CI tests, we need these env variables set +console.log(`::set-env name=ACTIONS_RUNTIME_URL::${process.env.ACTIONS_RUNTIME_URL}`) +console.log(`::set-env name=ACTIONS_RUNTIME_TOKEN::${process.env.ACTIONS_RUNTIME_TOKEN}`) +console.log(`::set-env name=GITHUB_RUN_ID::${process.env.GITHUB_RUN_ID}`) \ No newline at end of file diff --git a/packages/cache/package-lock.json b/packages/cache/package-lock.json index 569bab49b5..4e2de82adf 100644 --- a/packages/cache/package-lock.json +++ b/packages/cache/package-lock.json @@ -4,6 +4,28 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@actions/core": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.4.tgz", + "integrity": "sha512-YJCEq8BE3CdN8+7HPZ/4DxJjk/OkZV2FFIf+DlZTC/4iBlzYCD5yjRR6eiOS5llO11zbRltIRuKAjMKaWTE6cg==" + }, + "@actions/exec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.4.tgz", + "integrity": "sha512-4DPChWow9yc9W3WqEbUj8Nr86xkpyE29ZzWjXucHItclLbEW6jr80Zx4nqv18QL6KK65+cifiQZXvnqgTV6oHw==", + "requires": { + "@actions/io": "^1.0.1" + } + }, + "@actions/glob": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.0.tgz", + "integrity": "sha512-lx8SzyQ2FE9+UUvjqY1f28QbTJv+w8qP7kHHbfQRhphrlcx0Mdmm1tZdGJzfxv1jxREa/sLW4Oy8CbGQKCJySA==", + "requires": { + "@actions/core": "^1.2.0", + "minimatch": "^3.0.4" + } + }, "@actions/http-client": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz", @@ -12,12 +34,44 @@ "tunnel": "0.0.6" } }, + "@actions/io": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", + "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==" + }, "@types/uuid": { "version": "3.4.9", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.9.tgz", "integrity": "sha512-XDwyIlt/47l2kWLTzw/mtrpLdB+GPSskR2n/PIcPn+VYhVO77rGhRncIR5GPU0KRzXuqkDO+J5qqrG0Y8P6jzQ==", "dev": true }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, "tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", diff --git a/scripts/create-cache-files.sh b/scripts/create-cache-files.sh new file mode 100755 index 0000000000..0ce4140fbc --- /dev/null +++ b/scripts/create-cache-files.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# Validate args +prefix="$1" +if [ -z "$prefix" ]; then + echo "Must supply prefix argument" + exit 1 +fi + +path="$2" +if [ -z "$path" ]; then + echo "Must supply path argument" + exit 1 +fi + +mkdir -p $path +echo "$prefix $GITHUB_RUN_ID" > $path/test-file.txt diff --git a/scripts/verify-cache-files.sh b/scripts/verify-cache-files.sh new file mode 100755 index 0000000000..3ee8a84235 --- /dev/null +++ b/scripts/verify-cache-files.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +# Validate args +prefix="$1" +if [ -z "$prefix" ]; then + echo "Must supply prefix argument" + exit 1 +fi + +path="$2" +if [ -z "$path" ]; then + echo "Must specify path argument" + exit 1 +fi + +# Sanity check GITHUB_RUN_ID defined +if [ -z "$GITHUB_RUN_ID" ]; then + echo "GITHUB_RUN_ID not defined" + exit 1 +fi + +# Verify file exists +file="$path/test-file.txt" +echo "Checking for $file" +if [ ! -e $file ]; then + echo "File does not exist" + exit 1 +fi + +# Verify file content +content="$(cat $file)" +echo "File content:\n$content" +if [ -z "$(echo $content | grep --fixed-strings "$prefix $GITHUB_RUN_ID")" ]; then + echo "Unexpected file content" + exit 1 +fi From 1413cd0e32562e29ebe9aa04d4b7176120890791 Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Tue, 12 May 2020 12:37:03 -0400 Subject: [PATCH 144/192] Add cache upload options and pull from latest actions/cache master --- .github/workflows/cache-tests.yml | 12 +- README.md | 2 +- .../cache/__tests__/cacheHttpClient.test.ts | 141 ++++++++++++- packages/cache/__tests__/saveCache.test.ts | 4 +- packages/cache/src/cache.ts | 10 +- .../cache/src/internal/cacheHttpClient.ts | 190 ++++++++++++------ packages/cache/src/options.ts | 17 ++ 7 files changed, 309 insertions(+), 67 deletions(-) create mode 100644 packages/cache/src/options.ts diff --git a/.github/workflows/cache-tests.yml b/.github/workflows/cache-tests.yml index aa382ffc52..e63ac90c9a 100644 --- a/.github/workflows/cache-tests.yml +++ b/.github/workflows/cache-tests.yml @@ -1,5 +1,13 @@ name: cache-unit-tests -on: push +on: + push: + branches: + - master + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' jobs: build: @@ -21,7 +29,7 @@ jobs: with: node-version: 12.x - # In order to save & restore cache artifacts from a shell script, certain env variables need to be set that are only available in the + # In order to save & restore cache from a shell script, certain env variables need to be set that are only available in the # node context. This runs a local action that gets and sets the necessary env variables that are needed - name: Set env variables uses: ./packages/cache/__tests__/__fixtures__/ diff --git a/README.md b/README.md index 4ffb0d4421..6fd01f8c98 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ $ npm install @actions/artifact --save Provides functions to cache dependencies and build outputs to improve workflow execution time. Read more [here](packages/cache) ```bash -$ npm install @actions/cache --save +$ npm install @actions/cache ```
diff --git a/packages/cache/__tests__/cacheHttpClient.test.ts b/packages/cache/__tests__/cacheHttpClient.test.ts index e5d7eacf92..a7f3fec177 100644 --- a/packages/cache/__tests__/cacheHttpClient.test.ts +++ b/packages/cache/__tests__/cacheHttpClient.test.ts @@ -1,4 +1,4 @@ -import {getCacheVersion} from '../src/internal/cacheHttpClient' +import {getCacheVersion, retry} from '../src/internal/cacheHttpClient' import {CompressionMethod} from '../src/internal/constants' test('getCacheVersion with one path returns version', async () => { @@ -34,3 +34,142 @@ test('getCacheVersion with gzip compression does not change vesion', async () => 'b3e0c6cb5ecf32614eeb2997d905b9c297046d7cbf69062698f25b14b4cb0985' ) }) + +interface TestResponse { + statusCode: number + result: string | null +} + +async function handleResponse( + response: TestResponse | undefined +): Promise { + if (!response) { + // eslint-disable-next-line no-undef + fail('Retry method called too many times') + } + + if (response.statusCode === 999) { + throw Error('Test Error') + } else { + return Promise.resolve(response) + } +} + +async function testRetryExpectingResult( + responses: TestResponse[], + expectedResult: string | null +): Promise { + responses = responses.reverse() // Reverse responses since we pop from end + + const actualResult = await retry( + 'test', + async () => handleResponse(responses.pop()), + (response: TestResponse) => response.statusCode + ) + + expect(actualResult.result).toEqual(expectedResult) +} + +async function testRetryExpectingError( + responses: TestResponse[] +): Promise { + responses = responses.reverse() // Reverse responses since we pop from end + + expect( + retry( + 'test', + async () => handleResponse(responses.pop()), + (response: TestResponse) => response.statusCode + ) + ).rejects.toBeInstanceOf(Error) +} + +test('retry works on successful response', async () => { + await testRetryExpectingResult( + [ + { + statusCode: 200, + result: 'Ok' + } + ], + 'Ok' + ) +}) + +test('retry works after retryable status code', async () => { + await testRetryExpectingResult( + [ + { + statusCode: 503, + result: null + }, + { + statusCode: 200, + result: 'Ok' + } + ], + 'Ok' + ) +}) + +test('retry fails after exhausting retries', async () => { + await testRetryExpectingError([ + { + statusCode: 503, + result: null + }, + { + statusCode: 503, + result: null + }, + { + statusCode: 200, + result: 'Ok' + } + ]) +}) + +test('retry fails after non-retryable status code', async () => { + await testRetryExpectingError([ + { + statusCode: 500, + result: null + }, + { + statusCode: 200, + result: 'Ok' + } + ]) +}) + +test('retry works after error', async () => { + await testRetryExpectingResult( + [ + { + statusCode: 999, + result: null + }, + { + statusCode: 200, + result: 'Ok' + } + ], + 'Ok' + ) +}) + +test('retry returns after client error', async () => { + await testRetryExpectingResult( + [ + { + statusCode: 400, + result: null + }, + { + statusCode: 200, + result: 'Ok' + } + ], + null + ) +}) diff --git a/packages/cache/__tests__/saveCache.test.ts b/packages/cache/__tests__/saveCache.test.ts index 2fc379b345..720d2ad634 100644 --- a/packages/cache/__tests__/saveCache.test.ts +++ b/packages/cache/__tests__/saveCache.test.ts @@ -135,7 +135,7 @@ test('save with server error should fail', async () => { ) expect(saveCacheMock).toHaveBeenCalledTimes(1) - expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archiveFile) + expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archiveFile, undefined) expect(getCompressionMock).toHaveBeenCalledTimes(1) }) @@ -176,6 +176,6 @@ test('save with valid inputs uploads a cache', async () => { ) expect(saveCacheMock).toHaveBeenCalledTimes(1) - expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archiveFile) + expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archiveFile, undefined) expect(getCompressionMock).toHaveBeenCalledTimes(1) }) diff --git a/packages/cache/src/cache.ts b/packages/cache/src/cache.ts index cf9be5eb77..39eaf6de85 100644 --- a/packages/cache/src/cache.ts +++ b/packages/cache/src/cache.ts @@ -3,6 +3,7 @@ import * as path from 'path' import * as utils from './internal/cacheUtils' import * as cacheHttpClient from './internal/cacheHttpClient' import {createTar, extractTar} from './internal/tar' +import {UploadOptions} from './options' function checkPaths(paths: string[]): void { if (!paths || paths.length === 0) { @@ -102,9 +103,14 @@ export async function restoreCache( * * @param paths a list of file paths to be cached * @param key an explicit key for restoring the cache + * @param options cache upload options * @returns number returns cacheId if the cache was saved successfully */ -export async function saveCache(paths: string[], key: string): Promise { +export async function saveCache( + paths: string[], + key: string, + options?: UploadOptions +): Promise { checkPaths(paths) checkKey(key) @@ -147,7 +153,7 @@ export async function saveCache(paths: string[], key: string): Promise { } core.debug(`Saving Cache (ID: ${cacheId})`) - await cacheHttpClient.saveCache(cacheId, archivePath) + await cacheHttpClient.saveCache(cacheId, archivePath, options) return cacheId } diff --git a/packages/cache/src/internal/cacheHttpClient.ts b/packages/cache/src/internal/cacheHttpClient.ts index f4f8c4d1a4..a3b9633a89 100644 --- a/packages/cache/src/internal/cacheHttpClient.ts +++ b/packages/cache/src/internal/cacheHttpClient.ts @@ -20,6 +20,7 @@ import { ReserveCacheRequest, ReserveCacheResponse } from './contracts' +import {UploadOptions} from '../options' const versionSalt = '1.0' @@ -30,6 +31,13 @@ function isSuccessStatusCode(statusCode?: number): boolean { return statusCode >= 200 && statusCode < 300 } +function isServerErrorStatusCode(statusCode?: number): boolean { + if (!statusCode) { + return true + } + return statusCode >= 500 +} + function isRetryableStatusCode(statusCode?: number): boolean { if (!statusCode) { return false @@ -100,6 +108,75 @@ export function getCacheVersion( .digest('hex') } +export async function retry( + name: string, + method: () => Promise, + getStatusCode: (arg0: T) => number | undefined, + maxAttempts = 2 +): Promise { + let response: T | undefined = undefined + let statusCode: number | undefined = undefined + let isRetryable = false + let errorMessage = '' + let attempt = 1 + + while (attempt <= maxAttempts) { + try { + response = await method() + statusCode = getStatusCode(response) + + if (!isServerErrorStatusCode(statusCode)) { + return response + } + + isRetryable = isRetryableStatusCode(statusCode) + errorMessage = `Cache service responded with ${statusCode}` + } catch (error) { + isRetryable = true + errorMessage = error.message + } + + core.debug( + `${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}` + ) + + if (!isRetryable) { + core.debug(`${name} - Error is not retryable`) + break + } + + attempt++ + } + + throw Error(`${name} failed: ${errorMessage}`) +} + +export async function retryTypedResponse( + name: string, + method: () => Promise>, + maxAttempts = 2 +): Promise> { + return await retry( + name, + method, + (response: ITypedResponse) => response.statusCode, + maxAttempts + ) +} + +export async function retryHttpClientResponse( + name: string, + method: () => Promise, + maxAttempts = 2 +): Promise { + return await retry( + name, + method, + (response: IHttpClientResponse) => response.message.statusCode, + maxAttempts + ) +} + export async function getCacheEntry( keys: string[], paths: string[], @@ -111,8 +188,8 @@ export async function getCacheEntry( keys.join(',') )}&version=${version}` - const response = await httpClient.getJson( - getCacheApiUrl(resource) + const response = await retryTypedResponse('getCacheEntry', async () => + httpClient.getJson(getCacheApiUrl(resource)) ) if (response.statusCode === 204) { return null @@ -145,9 +222,12 @@ export async function downloadCache( archiveLocation: string, archivePath: string ): Promise { - const writableStream = fs.createWriteStream(archivePath) + const writeStream = fs.createWriteStream(archivePath) const httpClient = new HttpClient('actions/cache') - const downloadResponse = await httpClient.get(archiveLocation) + const downloadResponse = await retryHttpClientResponse( + 'downloadCache', + async () => httpClient.get(archiveLocation) + ) // Abort download if no traffic received over the socket. downloadResponse.message.socket.setTimeout(SocketTimeout, () => { @@ -155,7 +235,7 @@ export async function downloadCache( core.debug(`Aborting download, socket timed out after ${SocketTimeout} ms`) }) - await pipeResponseToStream(downloadResponse, writableStream) + await pipeResponseToStream(downloadResponse, writeStream) // Validate download size. const contentLengthHeader = downloadResponse.message.headers['content-length'] @@ -187,9 +267,11 @@ export async function reserveCache( key, version } - const response = await httpClient.postJson( - getCacheApiUrl('caches'), - reserveCacheRequest + const response = await retryTypedResponse('reserveCache', async () => + httpClient.postJson( + getCacheApiUrl('caches'), + reserveCacheRequest + ) ) return response?.result?.cacheId ?? -1 } @@ -206,7 +288,7 @@ function getContentRange(start: number, end: number): string { async function uploadChunk( httpClient: HttpClient, resourceUrl: string, - data: NodeJS.ReadableStream, + openStream: () => NodeJS.ReadableStream, start: number, end: number ): Promise { @@ -223,56 +305,31 @@ async function uploadChunk( 'Content-Range': getContentRange(start, end) } - const uploadChunkRequest = async (): Promise => { - return await httpClient.sendStream( - 'PATCH', - resourceUrl, - data, - additionalHeaders - ) - } - - const response = await uploadChunkRequest() - if (isSuccessStatusCode(response.message.statusCode)) { - return - } - - if (isRetryableStatusCode(response.message.statusCode)) { - core.debug( - `Received ${response.message.statusCode}, retrying chunk at offset ${start}.` - ) - const retryResponse = await uploadChunkRequest() - if (isSuccessStatusCode(retryResponse.message.statusCode)) { - return - } - } - - throw new Error( - `Cache service responded with ${response.message.statusCode} during chunk upload.` + await retryHttpClientResponse( + `uploadChunk (start: ${start}, end: ${end})`, + async () => + httpClient.sendStream( + 'PATCH', + resourceUrl, + openStream(), + additionalHeaders + ) ) } -function parseEnvNumber(key: string): number | undefined { - const value = Number(process.env[key]) - if (Number.isNaN(value) || value < 0) { - return undefined - } - return value -} - async function uploadFile( httpClient: HttpClient, cacheId: number, - archivePath: string + archivePath: string, + options?: UploadOptions ): Promise { // Upload Chunks const fileSize = fs.statSync(archivePath).size const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`) const fd = fs.openSync(archivePath, 'r') - const concurrency = parseEnvNumber('CACHE_UPLOAD_CONCURRENCY') ?? 4 // # of HTTP requests in parallel - const MAX_CHUNK_SIZE = - parseEnvNumber('CACHE_UPLOAD_CHUNK_SIZE') ?? 32 * 1024 * 1024 // 32 MB Chunks + const concurrency = options?.uploadConcurrency ?? 4 // # of HTTP requests in parallel + const MAX_CHUNK_SIZE = options?.uploadChunkSize ?? 32 * 1024 * 1024 // 32 MB Chunks core.debug(`Concurrency: ${concurrency} and Chunk Size: ${MAX_CHUNK_SIZE}`) const parallelUploads = [...new Array(concurrency).keys()] @@ -287,14 +344,26 @@ async function uploadFile( const start = offset const end = offset + chunkSize - 1 offset += MAX_CHUNK_SIZE - const chunk = fs.createReadStream(archivePath, { - fd, - start, - end, - autoClose: false - }) - await uploadChunk(httpClient, resourceUrl, chunk, start, end) + await uploadChunk( + httpClient, + resourceUrl, + () => + fs + .createReadStream(archivePath, { + fd, + start, + end, + autoClose: false + }) + .on('error', error => { + throw new Error( + `Cache upload failed because file read failed with ${error.Message}` + ) + }), + start, + end + ) } }) ) @@ -310,20 +379,23 @@ async function commitCache( filesize: number ): Promise> { const commitCacheRequest: CommitCacheRequest = {size: filesize} - return await httpClient.postJson( - getCacheApiUrl(`caches/${cacheId.toString()}`), - commitCacheRequest + return await retryTypedResponse('commitCache', async () => + httpClient.postJson( + getCacheApiUrl(`caches/${cacheId.toString()}`), + commitCacheRequest + ) ) } export async function saveCache( cacheId: number, - archivePath: string + archivePath: string, + options?: UploadOptions ): Promise { const httpClient = createHttpClient() core.debug('Upload cache') - await uploadFile(httpClient, cacheId, archivePath) + await uploadFile(httpClient, cacheId, archivePath, options) // Commit Cache core.debug('Commiting cache') diff --git a/packages/cache/src/options.ts b/packages/cache/src/options.ts new file mode 100644 index 0000000000..c5ecdad5d9 --- /dev/null +++ b/packages/cache/src/options.ts @@ -0,0 +1,17 @@ +/** + * Options to control cache upload + */ +export interface UploadOptions { + /** + * Number of parallel cache upload + * + * @default 4 + */ + uploadConcurrency?: number + /** + * Maximum chunk size for cache upload + * + * @default 32MB + */ + uploadChunkSize?: number +} From b3c8e19a7aec4fd27a7afc5760cc309f6b1308b2 Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Tue, 12 May 2020 14:47:31 -0400 Subject: [PATCH 145/192] Attempt to fix the test --- packages/cache/package-lock.json | 6 ++++ packages/cache/package.json | 1 + packages/cache/src/cache.ts | 28 +++++++++++++++---- .../cache/src/internal/cacheHttpClient.ts | 6 ++-- packages/cache/src/internal/contracts.d.ts | 2 +- 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/packages/cache/package-lock.json b/packages/cache/package-lock.json index 4e2de82adf..9d8b6cbf5e 100644 --- a/packages/cache/package-lock.json +++ b/packages/cache/package-lock.json @@ -77,6 +77,12 @@ "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "dev": true + }, "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", diff --git a/packages/cache/package.json b/packages/cache/package.json index 2ee2d9bbc8..fea57f0b91 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -44,6 +44,7 @@ "uuid": "^3.3.3" }, "devDependencies": { + "typescript": "^3.8.3", "@types/uuid": "^3.4.5" } } diff --git a/packages/cache/src/cache.ts b/packages/cache/src/cache.ts index 39eaf6de85..5f55c82b06 100644 --- a/packages/cache/src/cache.ts +++ b/packages/cache/src/cache.ts @@ -5,9 +5,23 @@ import * as cacheHttpClient from './internal/cacheHttpClient' import {createTar, extractTar} from './internal/tar' import {UploadOptions} from './options' +export class ValidationError extends Error { + constructor(message: string) { + super(message) + this.name = 'ValidationError' + } +} + +export class ReserveCacheError extends Error { + constructor(message: string) { + super(message) + this.name = 'ReserveCacheError' + } +} + function checkPaths(paths: string[]): void { if (!paths || paths.length === 0) { - throw new Error( + throw new ValidationError( `Path Validation Error: At least one directory or file path is required` ) } @@ -15,13 +29,15 @@ function checkPaths(paths: string[]): void { function checkKey(key: string): void { if (key.length > 512) { - throw new Error( + throw new ValidationError( `Key Validation Error: ${key} cannot be larger than 512 characters.` ) } const regex = /^[^,]*$/ if (!regex.test(key)) { - throw new Error(`Key Validation Error: ${key} cannot contain commas.`) + throw new ValidationError( + `Key Validation Error: ${key} cannot contain commas.` + ) } } @@ -47,7 +63,7 @@ export async function restoreCache( core.debug(JSON.stringify(keys)) if (keys.length > 10) { - throw new Error( + throw new ValidationError( `Key Validation Error: Keys are limited to a maximum of 10.` ) } @@ -121,13 +137,13 @@ export async function saveCache( compressionMethod }) if (cacheId === -1) { - throw new Error( + throw new ReserveCacheError( `Unable to reserve cache with key ${key}, another job may be creating this cache.` ) } core.debug(`Cache ID: ${cacheId}`) - const cachePaths = await utils.resolvePaths(paths) + const cachePaths = await utils.resolvePaths(paths) core.debug('Cache Paths:') core.debug(`${JSON.stringify(cachePaths)}`) diff --git a/packages/cache/src/internal/cacheHttpClient.ts b/packages/cache/src/internal/cacheHttpClient.ts index a3b9633a89..43692fa76b 100644 --- a/packages/cache/src/internal/cacheHttpClient.ts +++ b/packages/cache/src/internal/cacheHttpClient.ts @@ -15,7 +15,7 @@ import * as utils from './cacheUtils' import {CompressionMethod, SocketTimeout} from './constants' import { ArtifactCacheEntry, - CacheOptions, + InternalCacheOptions, CommitCacheRequest, ReserveCacheRequest, ReserveCacheResponse @@ -180,7 +180,7 @@ export async function retryHttpClientResponse( export async function getCacheEntry( keys: string[], paths: string[], - options?: CacheOptions + options?: InternalCacheOptions ): Promise { const httpClient = createHttpClient() const version = getCacheVersion(paths, options?.compressionMethod) @@ -258,7 +258,7 @@ export async function downloadCache( export async function reserveCache( key: string, paths: string[], - options?: CacheOptions + options?: InternalCacheOptions ): Promise { const httpClient = createHttpClient() const version = getCacheVersion(paths, options?.compressionMethod) diff --git a/packages/cache/src/internal/contracts.d.ts b/packages/cache/src/internal/contracts.d.ts index ca3f36206f..8048476975 100644 --- a/packages/cache/src/internal/contracts.d.ts +++ b/packages/cache/src/internal/contracts.d.ts @@ -20,6 +20,6 @@ export interface ReserveCacheResponse { cacheId: number } -export interface CacheOptions { +export interface InternalCacheOptions { compressionMethod?: CompressionMethod } From 36e732155e521874637327a845b689ddc7a20645 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Wed, 13 May 2020 14:39:10 -0400 Subject: [PATCH 146/192] tool-cache 1.3.5 release (#454) * tc 1.3.5 release --- packages/tool-cache/RELEASES.md | 5 +++++ packages/tool-cache/package-lock.json | 2 +- packages/tool-cache/package.json | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/tool-cache/RELEASES.md b/packages/tool-cache/RELEASES.md index 3bbe764c87..8a10023bfc 100644 --- a/packages/tool-cache/RELEASES.md +++ b/packages/tool-cache/RELEASES.md @@ -1,5 +1,10 @@ # @actions/tool-cache Releases +### 1.3.5 + +- [Check if tool path exists before executing](https://github.com/actions/toolkit/pull/385) +- [Make extract functions quiet by default](https://github.com/actions/toolkit/pull/206) + ### 1.3.4 - [Update the http-client to 1.0.8 which had a security fix](https://github.com/actions/toolkit/pull/429) diff --git a/packages/tool-cache/package-lock.json b/packages/tool-cache/package-lock.json index ac351347c7..6aadb28909 100644 --- a/packages/tool-cache/package-lock.json +++ b/packages/tool-cache/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.3.4", + "version": "1.3.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index a5a77c24b9..78b71778b4 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.3.4", + "version": "1.3.5", "description": "Actions tool-cache lib", "keywords": [ "github", @@ -36,7 +36,7 @@ "url": "https://github.com/actions/toolkit/issues" }, "dependencies": { - "@actions/core": "^1.2.0", + "@actions/core": "^1.2.3", "@actions/exec": "^1.0.0", "@actions/http-client": "^1.0.8", "@actions/io": "^1.0.1", From 6d83c79964f2fc1bc1624d9dc5be53763c0df78b Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Thu, 14 May 2020 17:58:46 +0200 Subject: [PATCH 147/192] Remove --save in README (#455) --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3dab6d39e9..01f72cdb55 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ The GitHub Actions ToolKit provides a set of packages to make creating actions e Provides functions for inputs, outputs, results, logging, secrets and variables. Read more [here](packages/core) ```bash -$ npm install @actions/core --save +$ npm install @actions/core ```
@@ -33,7 +33,7 @@ $ npm install @actions/core --save Provides functions to exec cli tools and process output. Read more [here](packages/exec) ```bash -$ npm install @actions/exec --save +$ npm install @actions/exec ```
@@ -42,7 +42,7 @@ $ npm install @actions/exec --save Provides functions to search for files matching glob patterns. Read more [here](packages/glob) ```bash -$ npm install @actions/glob --save +$ npm install @actions/glob ```
@@ -51,7 +51,7 @@ $ npm install @actions/glob --save Provides disk i/o functions like cp, mv, rmRF, find etc. Read more [here](packages/io) ```bash -$ npm install @actions/io --save +$ npm install @actions/io ```
@@ -60,7 +60,7 @@ $ npm install @actions/io --save Provides functions for downloading and caching tools. e.g. setup-* actions. Read more [here](packages/tool-cache) ```bash -$ npm install @actions/tool-cache --save +$ npm install @actions/tool-cache ```
@@ -69,7 +69,7 @@ $ npm install @actions/tool-cache --save Provides an Octokit client hydrated with the context that the current action is being run in. Read more [here](packages/github) ```bash -$ npm install @actions/github --save +$ npm install @actions/github ```
@@ -78,7 +78,7 @@ $ npm install @actions/github --save Provides functions to interact with actions artifacts. Read more [here](packages/artifact) ```bash -$ npm install @actions/artifact --save +$ npm install @actions/artifact ```
From 628f82f22156d17814bfe7876fe3581456e9a9f3 Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Thu, 14 May 2020 22:18:21 +0200 Subject: [PATCH 148/192] Correctly reset chunk during artifact upload on retry (#458) * Correctly reset chunk during artifact upload on retry * Update workflow * Implementation details around the passthrough stream --- .github/workflows/artifact-tests.yml | 12 +++++-- .../artifact/docs/implementation-details.md | 4 +++ .../src/internal/upload-http-client.ts | 34 +++++++++++-------- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/.github/workflows/artifact-tests.yml b/.github/workflows/artifact-tests.yml index 8b45404a59..20890ed000 100644 --- a/.github/workflows/artifact-tests.yml +++ b/.github/workflows/artifact-tests.yml @@ -1,5 +1,13 @@ name: artifact-unit-tests -on: push +on: + push: + branches: + - master + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' jobs: build: @@ -7,7 +15,7 @@ jobs: strategy: matrix: - runs-on: [ubuntu-latest, windows-latest, macOS-latest] + runs-on: [ubuntu-latest, windows-latest, macos-latest] fail-fast: false runs-on: ${{ matrix.runs-on }} diff --git a/packages/artifact/docs/implementation-details.md b/packages/artifact/docs/implementation-details.md index bed96f0bf4..9ab1b0065e 100644 --- a/packages/artifact/docs/implementation-details.md +++ b/packages/artifact/docs/implementation-details.md @@ -6,6 +6,10 @@ Warning: Implementation details may change at any time without notice. This is m ![image](https://user-images.githubusercontent.com/16109154/79765587-19522b00-8327-11ea-9679-410bb10e1b13.png) +During artifact upload, gzip is used to compress individual files that then get uploaded. This is used to minimize the amount of data that gets uploaded which reduces the total amount of HTTP calls (upload happens in 4MB chunks). This results in considerably faster uploads with huge performance implications especially on self-hosted runners. + +If a file is less than 64KB in size, a passthrough stream (readable and writable) is used to convert an in-memory buffer into a readable stream without any extra streams or pipping. + ## Retry Logic when downloading an individual file ![image](https://user-images.githubusercontent.com/16109154/78555461-5be71400-780d-11ea-9abd-b05b77a95a3f.png) diff --git a/packages/artifact/src/internal/upload-http-client.ts b/packages/artifact/src/internal/upload-http-client.ts index dc2d331899..3733e1aed5 100644 --- a/packages/artifact/src/internal/upload-http-client.ts +++ b/packages/artifact/src/internal/upload-http-client.ts @@ -208,25 +208,30 @@ export class UploadHttpClient { // for creating a new GZip file, an in-memory buffer is used for compression if (totalFileSize < 65536) { const buffer = await createGZipFileInBuffer(parameters.file) - let uploadStream: NodeJS.ReadableStream + + //An open stream is needed in the event of a failure and we need to retry. If a NodeJS.ReadableStream is directly passed in, + // it will not properly get reset to the start of the stream if a chunk upload needs to be retried + let openUploadStream: () => NodeJS.ReadableStream if (totalFileSize < buffer.byteLength) { // compression did not help with reducing the size, use a readable stream from the original file for upload - uploadStream = fs.createReadStream(parameters.file) + openUploadStream = () => fs.createReadStream(parameters.file) isGzip = false uploadFileSize = totalFileSize } else { // create a readable stream using a PassThrough stream that is both readable and writable - const passThrough = new stream.PassThrough() - passThrough.end(buffer) - uploadStream = passThrough + openUploadStream = () => { + const passThrough = new stream.PassThrough() + passThrough.end(buffer) + return passThrough + } uploadFileSize = buffer.byteLength } const result = await this.uploadChunk( httpClientIndex, parameters.resourceUrl, - uploadStream, + openUploadStream, 0, uploadFileSize - 1, uploadFileSize, @@ -296,11 +301,12 @@ export class UploadHttpClient { const result = await this.uploadChunk( httpClientIndex, parameters.resourceUrl, - fs.createReadStream(uploadFilePath, { - start, - end, - autoClose: false - }), + () => + fs.createReadStream(uploadFilePath, { + start, + end, + autoClose: false + }), start, end, uploadFileSize, @@ -335,7 +341,7 @@ export class UploadHttpClient { * indicates a retryable status, we try to upload the chunk as well * @param {number} httpClientIndex The index of the httpClient being used to make all the necessary calls * @param {string} resourceUrl Url of the resource that the chunk will be uploaded to - * @param {NodeJS.ReadableStream} data Stream of the file that will be uploaded + * @param {NodeJS.ReadableStream} openStream Stream of the file that will be uploaded * @param {number} start Starting byte index of file that the chunk belongs to * @param {number} end Ending byte index of file that the chunk belongs to * @param {number} uploadFileSize Total size of the file in bytes that is being uploaded @@ -346,7 +352,7 @@ export class UploadHttpClient { private async uploadChunk( httpClientIndex: number, resourceUrl: string, - data: NodeJS.ReadableStream, + openStream: () => NodeJS.ReadableStream, start: number, end: number, uploadFileSize: number, @@ -365,7 +371,7 @@ export class UploadHttpClient { const uploadChunkRequest = async (): Promise => { const client = this.uploadHttpManager.getClient(httpClientIndex) - return await client.sendStream('PUT', resourceUrl, data, headers) + return await client.sendStream('PUT', resourceUrl, openStream(), headers) } let retryCount = 0 From d2b2399bd2fce877e9dd828711f74b83306a602b Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Fri, 15 May 2020 12:18:50 -0400 Subject: [PATCH 149/192] React to feedback --- .github/workflows/artifact-tests.yml | 8 ++-- .github/workflows/cache-tests.yml | 8 ++-- .../artifact/__tests__}/test-artifact-file.sh | 0 packages/cache/README.md | 12 ++--- packages/cache/RELEASES.md | 2 +- packages/cache/__tests__/cacheUtils.test.ts | 19 ++------ .../cache/__tests__}/create-cache-files.sh | 0 packages/cache/__tests__/restoreCache.test.ts | 44 +++++++------------ packages/cache/__tests__/saveCache.test.ts | 12 ++++- .../cache/__tests__}/verify-cache-files.sh | 0 packages/cache/package-lock.json | 2 +- packages/cache/package.json | 2 +- packages/cache/src/cache.ts | 14 +++--- .../cache/src/internal/cacheHttpClient.ts | 4 +- packages/cache/src/internal/cacheUtils.ts | 3 +- packages/cache/src/options.ts | 2 +- 16 files changed, 59 insertions(+), 73 deletions(-) rename {scripts => packages/artifact/__tests__}/test-artifact-file.sh (100%) rename {scripts => packages/cache/__tests__}/create-cache-files.sh (100%) rename {scripts => packages/cache/__tests__}/verify-cache-files.sh (100%) diff --git a/.github/workflows/artifact-tests.yml b/.github/workflows/artifact-tests.yml index 8b45404a59..5fc98f7e1b 100644 --- a/.github/workflows/artifact-tests.yml +++ b/.github/workflows/artifact-tests.yml @@ -64,8 +64,8 @@ jobs: - name: Verify downloadArtifact() shell: bash run: | - scripts/test-artifact-file.sh "artifact-1-directory/artifact-path/world.txt" "${{ env.non-gzip-artifact-content }}" - scripts/test-artifact-file.sh "artifact-2-directory/artifact-path/gzip.txt" "${{ env.gzip-artifact-content }}" + packages/artifact/__tests__/test-artifact-file.sh "artifact-1-directory/artifact-path/world.txt" "${{ env.non-gzip-artifact-content }}" + packages/artifact/__tests__/test-artifact-file.sh "artifact-2-directory/artifact-path/gzip.txt" "${{ env.gzip-artifact-content }}" - name: Download artifacts using downloadAllArtifacts() run: | @@ -75,5 +75,5 @@ jobs: - name: Verify downloadAllArtifacts() shell: bash run: | - scripts/test-artifact-file.sh "multi-artifact-directory/my-artifact-1/artifact-path/world.txt" "${{ env.non-gzip-artifact-content }}" - scripts/test-artifact-file.sh "multi-artifact-directory/my-artifact-2/artifact-path/gzip.txt" "${{ env.gzip-artifact-content }}" \ No newline at end of file + packages/artifact/__tests__/test-artifact-file.sh "multi-artifact-directory/my-artifact-1/artifact-path/world.txt" "${{ env.non-gzip-artifact-content }}" + packages/artifact/__tests__/test-artifact-file.sh "multi-artifact-directory/my-artifact-2/artifact-path/gzip.txt" "${{ env.gzip-artifact-content }}" \ No newline at end of file diff --git a/.github/workflows/cache-tests.yml b/.github/workflows/cache-tests.yml index e63ac90c9a..10120ddc46 100644 --- a/.github/workflows/cache-tests.yml +++ b/.github/workflows/cache-tests.yml @@ -47,11 +47,11 @@ jobs: - name: Generate files in working directory shell: bash - run: scripts/create-cache-files.sh ${{ runner.os }} test-cache + run: packages/cache/__tests__/create-cache-files.sh ${{ runner.os }} test-cache - name: Generate files outside working directory shell: bash - run: scripts/create-cache-files.sh ${{ runner.os }} ~/test-cache + run: packages/cache/__tests__/create-cache-files.sh ${{ runner.os }} ~/test-cache # We're using node -e to call the functions directly available in the @actions/cache package - name: Save cache using saveCache() @@ -65,5 +65,5 @@ jobs: - name: Verify cache shell: bash run: | - scripts/verify-cache-files.sh ${{ runner.os }} test-cache - scripts/verify-cache-files.sh ${{ runner.os }} ~/test-cache \ No newline at end of file + packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} test-cache + packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} ~/test-cache \ No newline at end of file diff --git a/scripts/test-artifact-file.sh b/packages/artifact/__tests__/test-artifact-file.sh similarity index 100% rename from scripts/test-artifact-file.sh rename to packages/artifact/__tests__/test-artifact-file.sh diff --git a/packages/cache/README.md b/packages/cache/README.md index f15d0b1672..de0893d057 100644 --- a/packages/cache/README.md +++ b/packages/cache/README.md @@ -2,6 +2,10 @@ > Functions necessary for caching dependencies and build outputs to improve workflow execution time. +See ["Caching dependencies to speed up workflows"](https://help.github.com/github/automating-your-workflow-with-github-actions/caching-dependencies-to-speed-up-workflows) for how caching works. + +Note that GitHub will remove any cache entries that have not been accessed in over 7 days. There is no limit on the number of caches you can store, but the total size of all caches in a repository is limited to 5 GB. If you exceed this limit, GitHub will save your cache but will begin evicting caches until the total size is less than 5 GB. + ## Usage #### Restore Cache @@ -24,7 +28,7 @@ const cacheKey = await cache.restoreCache(paths, key, restoreKeys) #### Save Cache -Saves a cache containing the files in `paths` using the `key` provided. Function returns the cache id if the cache was save succesfully. +Saves a cache containing the files in `paths` using the `key` provided. The files would be compressed using zstandard compression algorithm if zstd is installed, otherwise gzip is used. Function returns the cache id if the cache was saved succesfully and throws an error if cache upload fails. ```js const cache = require('@actions/cache'); @@ -34,8 +38,4 @@ const paths = [ ] const key = 'npm-foobar-d5ea0750' const cacheId = await cache.saveCache(paths, key) -``` - -## Additional Documentation - -See ["Caching dependencies to speed up workflows"](https://help.github.com/github/automating-your-workflow-with-github-actions/caching-dependencies-to-speed-up-workflows). \ No newline at end of file +``` \ No newline at end of file diff --git a/packages/cache/RELEASES.md b/packages/cache/RELEASES.md index 1250c0507c..920a20e8ed 100644 --- a/packages/cache/RELEASES.md +++ b/packages/cache/RELEASES.md @@ -1,5 +1,5 @@ # @actions/cache Releases -### 1.0.0 +### 0.1.0 - Initial release \ No newline at end of file diff --git a/packages/cache/__tests__/cacheUtils.test.ts b/packages/cache/__tests__/cacheUtils.test.ts index 0a4b0f4c04..25d7ca8278 100644 --- a/packages/cache/__tests__/cacheUtils.test.ts +++ b/packages/cache/__tests__/cacheUtils.test.ts @@ -1,24 +1,11 @@ -import * as io from '@actions/io' import {promises as fs} from 'fs' import * as path from 'path' import * as cacheUtils from '../src/internal/cacheUtils' -jest.mock('@actions/core') -jest.mock('os') - -function getTempDir(): string { - return path.join(__dirname, '_temp', 'cacheUtils') -} - -afterAll(async () => { - delete process.env['GITHUB_WORKSPACE'] - await io.rmRF(getTempDir()) -}) - -test('getArchiveFileSize returns file size', () => { +test('getArchiveFileSizeIsBytes returns file size', () => { const filePath = path.join(__dirname, '__fixtures__', 'helloWorld.txt') - const size = cacheUtils.getArchiveFileSize(filePath) + const size = cacheUtils.getArchiveFileSizeIsBytes(filePath) expect(size).toBe(11) }) @@ -28,6 +15,8 @@ test('unlinkFile unlinks file', async () => { const testFile = path.join(testDirectory, 'test.txt') await fs.writeFile(testFile, 'hello world') + await expect(fs.stat(testFile)).resolves.not.toThrow() + await cacheUtils.unlinkFile(testFile) // This should throw as testFile should not exist diff --git a/scripts/create-cache-files.sh b/packages/cache/__tests__/create-cache-files.sh similarity index 100% rename from scripts/create-cache-files.sh rename to packages/cache/__tests__/create-cache-files.sh diff --git a/packages/cache/__tests__/restoreCache.test.ts b/packages/cache/__tests__/restoreCache.test.ts index b466b9afff..2e0fd06805 100644 --- a/packages/cache/__tests__/restoreCache.test.ts +++ b/packages/cache/__tests__/restoreCache.test.ts @@ -12,6 +12,12 @@ jest.mock('../src/internal/cacheUtils') jest.mock('../src/internal/tar') beforeAll(() => { + jest.spyOn(console, 'log').mockImplementation(() => {}) + jest.spyOn(core, 'debug').mockImplementation(() => {}) + jest.spyOn(core, 'info').mockImplementation(() => {}) + jest.spyOn(core, 'warning').mockImplementation(() => {}) + jest.spyOn(core, 'error').mockImplementation(() => {}) + // eslint-disable-next-line @typescript-eslint/promise-function-async jest.spyOn(cacheUtils, 'getCacheFileName').mockImplementation(cm => { const actualUtils = jest.requireActual('../src/internal/cacheUtils') @@ -56,7 +62,6 @@ test('restore with no cache found', async () => { const paths = ['node_modules'] const key = 'node-test' - const infoMock = jest.spyOn(core, 'info') jest.spyOn(cacheHttpClient, 'getCacheEntry').mockImplementation(async () => { return Promise.resolve(null) }) @@ -64,9 +69,6 @@ test('restore with no cache found', async () => { const cacheKey = await restoreCache(paths, key) expect(cacheKey).toBe(undefined) - expect(infoMock).toHaveBeenCalledWith( - `Cache not found for input keys: ${key}` - ) }) test('restore with server error should fail', async () => { @@ -87,8 +89,6 @@ test('restore with restore keys and no cache found', async () => { const key = 'node-test' const restoreKey = 'node-' - const infoMock = jest.spyOn(core, 'info') - jest.spyOn(cacheHttpClient, 'getCacheEntry').mockImplementation(async () => { return Promise.resolve(null) }) @@ -96,17 +96,12 @@ test('restore with restore keys and no cache found', async () => { const cacheKey = await restoreCache(paths, key, [restoreKey]) expect(cacheKey).toBe(undefined) - expect(infoMock).toHaveBeenCalledWith( - `Cache not found for input keys: ${key}, ${restoreKey}` - ) }) test('restore with gzip compressed cache found', async () => { const paths = ['node_modules'] const key = 'node-test' - const infoMock = jest.spyOn(core, 'info') - const cacheEntry: ArtifactCacheEntry = { cacheKey: key, scope: 'refs/heads/master', @@ -128,8 +123,8 @@ test('restore with gzip compressed cache found', async () => { const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache') const fileSize = 142 - const getArchiveFileSizeMock = jest - .spyOn(cacheUtils, 'getArchiveFileSize') + const getArchiveFileSizeIsBytesMock = jest + .spyOn(cacheUtils, 'getArchiveFileSizeIsBytes') .mockReturnValue(fileSize) const extractTarMock = jest.spyOn(tar, 'extractTar') @@ -151,7 +146,7 @@ test('restore with gzip compressed cache found', async () => { cacheEntry.archiveLocation, archivePath ) - expect(getArchiveFileSizeMock).toHaveBeenCalledWith(archivePath) + expect(getArchiveFileSizeIsBytesMock).toHaveBeenCalledWith(archivePath) expect(extractTarMock).toHaveBeenCalledTimes(1) expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression) @@ -159,11 +154,10 @@ test('restore with gzip compressed cache found', async () => { expect(unlinkFileMock).toHaveBeenCalledTimes(1) expect(unlinkFileMock).toHaveBeenCalledWith(archivePath) - expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`) expect(getCompressionMock).toHaveBeenCalledTimes(1) }) -test('restore with a pull request event and zstd compressed cache found', async () => { +test('restore with zstd compressed cache found', async () => { const paths = ['node_modules'] const key = 'node-test' @@ -189,8 +183,8 @@ test('restore with a pull request event and zstd compressed cache found', async const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache') const fileSize = 62915000 - const getArchiveFileSizeMock = jest - .spyOn(cacheUtils, 'getArchiveFileSize') + const getArchiveFileSizeIsBytesMock = jest + .spyOn(cacheUtils, 'getArchiveFileSizeIsBytes') .mockReturnValue(fileSize) const extractTarMock = jest.spyOn(tar, 'extractTar') @@ -210,13 +204,11 @@ test('restore with a pull request event and zstd compressed cache found', async cacheEntry.archiveLocation, archivePath ) - expect(getArchiveFileSizeMock).toHaveBeenCalledWith(archivePath) + expect(getArchiveFileSizeIsBytesMock).toHaveBeenCalledWith(archivePath) expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~60 MB (62915000 B)`) expect(extractTarMock).toHaveBeenCalledTimes(1) expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression) - - expect(infoMock).toHaveBeenCalledWith(`Cache restored from key: ${key}`) expect(getCompressionMock).toHaveBeenCalledTimes(1) }) @@ -247,8 +239,8 @@ test('restore with cache found for restore key', async () => { const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache') const fileSize = 142 - const getArchiveFileSizeMock = jest - .spyOn(cacheUtils, 'getArchiveFileSize') + const getArchiveFileSizeIsBytesMock = jest + .spyOn(cacheUtils, 'getArchiveFileSizeIsBytes') .mockReturnValue(fileSize) const extractTarMock = jest.spyOn(tar, 'extractTar') @@ -268,14 +260,10 @@ test('restore with cache found for restore key', async () => { cacheEntry.archiveLocation, archivePath ) - expect(getArchiveFileSizeMock).toHaveBeenCalledWith(archivePath) + expect(getArchiveFileSizeIsBytesMock).toHaveBeenCalledWith(archivePath) expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~0 MB (142 B)`) expect(extractTarMock).toHaveBeenCalledTimes(1) expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression) - - expect(infoMock).toHaveBeenCalledWith( - `Cache restored from key: ${restoreKey}` - ) expect(getCompressionMock).toHaveBeenCalledTimes(1) }) diff --git a/packages/cache/__tests__/saveCache.test.ts b/packages/cache/__tests__/saveCache.test.ts index 720d2ad634..711328782c 100644 --- a/packages/cache/__tests__/saveCache.test.ts +++ b/packages/cache/__tests__/saveCache.test.ts @@ -1,3 +1,4 @@ +import * as core from '@actions/core' import * as path from 'path' import {saveCache} from '../src/cache' import * as cacheHttpClient from '../src/internal/cacheHttpClient' @@ -5,12 +6,17 @@ import * as cacheUtils from '../src/internal/cacheUtils' import {CacheFilename, CompressionMethod} from '../src/internal/constants' import * as tar from '../src/internal/tar' -jest.mock('@actions/core') jest.mock('../src/internal/cacheHttpClient') jest.mock('../src/internal/cacheUtils') jest.mock('../src/internal/tar') beforeAll(() => { + jest.spyOn(console, 'log').mockImplementation(() => {}) + jest.spyOn(core, 'debug').mockImplementation(() => {}) + jest.spyOn(core, 'info').mockImplementation(() => {}) + jest.spyOn(core, 'warning').mockImplementation(() => {}) + jest.spyOn(core, 'error').mockImplementation(() => {}) + // eslint-disable-next-line @typescript-eslint/promise-function-async jest.spyOn(cacheUtils, 'getCacheFileName').mockImplementation(cm => { const actualUtils = jest.requireActual('../src/internal/cacheUtils') @@ -42,7 +48,9 @@ test('save with large cache outputs should fail', async () => { const createTarMock = jest.spyOn(tar, 'createTar') const cacheSize = 6 * 1024 * 1024 * 1024 //~6GB, over the 5GB limit - jest.spyOn(cacheUtils, 'getArchiveFileSize').mockReturnValueOnce(cacheSize) + jest + .spyOn(cacheUtils, 'getArchiveFileSizeIsBytes') + .mockReturnValueOnce(cacheSize) const compression = CompressionMethod.Gzip const getCompressionMock = jest .spyOn(cacheUtils, 'getCompressionMethod') diff --git a/scripts/verify-cache-files.sh b/packages/cache/__tests__/verify-cache-files.sh similarity index 100% rename from scripts/verify-cache-files.sh rename to packages/cache/__tests__/verify-cache-files.sh diff --git a/packages/cache/package-lock.json b/packages/cache/package-lock.json index 9d8b6cbf5e..08f18a7c8a 100644 --- a/packages/cache/package-lock.json +++ b/packages/cache/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/cache", - "version": "1.0.0", + "version": "0.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/cache/package.json b/packages/cache/package.json index fea57f0b91..69e9318121 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -1,6 +1,6 @@ { "name": "@actions/cache", - "version": "1.0.0", + "version": "0.1.0", "preview": true, "description": "Actions cache lib", "keywords": [ diff --git a/packages/cache/src/cache.ts b/packages/cache/src/cache.ts index 5f55c82b06..fc04a297b2 100644 --- a/packages/cache/src/cache.ts +++ b/packages/cache/src/cache.ts @@ -9,6 +9,7 @@ export class ValidationError extends Error { constructor(message: string) { super(message) this.name = 'ValidationError' + Object.setPrototypeOf(this, ValidationError.prototype) } } @@ -16,6 +17,7 @@ export class ReserveCacheError extends Error { constructor(message: string) { super(message) this.name = 'ReserveCacheError' + Object.setPrototypeOf(this, ReserveCacheError.prototype) } } @@ -47,7 +49,7 @@ function checkKey(key: string): void { * @param paths a list of file paths to restore from the cache * @param primaryKey an explicit key for restoring the cache * @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key - * @returns string returns the key for the cache hit, otherwise return undefined + * @returns string returns the key for the cache hit, otherwise returns undefined */ export async function restoreCache( paths: string[], @@ -78,7 +80,7 @@ export async function restoreCache( compressionMethod }) if (!cacheEntry?.archiveLocation) { - core.info(`Cache not found for input keys: ${keys.join(', ')}`) + // Cache not found return undefined } @@ -92,7 +94,7 @@ export async function restoreCache( // Download the cache from the cache entry await cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath) - const archiveFileSize = utils.getArchiveFileSize(archivePath) + const archiveFileSize = utils.getArchiveFileSizeIsBytes(archivePath) core.info( `Cache Size: ~${Math.round( archiveFileSize / (1024 * 1024) @@ -109,8 +111,6 @@ export async function restoreCache( } } - core.info(`Cache restored from key: ${cacheEntry && cacheEntry.cacheKey}`) - return cacheEntry.cacheKey } @@ -120,7 +120,7 @@ export async function restoreCache( * @param paths a list of file paths to be cached * @param key an explicit key for restoring the cache * @param options cache upload options - * @returns number returns cacheId if the cache was saved successfully + * @returns number returns cacheId if the cache was saved successfully and throws an error if save fails */ export async function saveCache( paths: string[], @@ -158,7 +158,7 @@ export async function saveCache( await createTar(archiveFolder, cachePaths, compressionMethod) const fileSizeLimit = 5 * 1024 * 1024 * 1024 // 5GB per repo limit - const archiveFileSize = utils.getArchiveFileSize(archivePath) + const archiveFileSize = utils.getArchiveFileSizeIsBytes(archivePath) core.debug(`File Size: ${archiveFileSize}`) if (archiveFileSize > fileSizeLimit) { throw new Error( diff --git a/packages/cache/src/internal/cacheHttpClient.ts b/packages/cache/src/internal/cacheHttpClient.ts index 43692fa76b..b7d34448d3 100644 --- a/packages/cache/src/internal/cacheHttpClient.ts +++ b/packages/cache/src/internal/cacheHttpClient.ts @@ -242,7 +242,7 @@ export async function downloadCache( if (contentLengthHeader) { const expectedLength = parseInt(contentLengthHeader) - const actualLength = utils.getArchiveFileSize(archivePath) + const actualLength = utils.getArchiveFileSizeIsBytes(archivePath) if (actualLength !== expectedLength) { throw new Error( @@ -399,7 +399,7 @@ export async function saveCache( // Commit Cache core.debug('Commiting cache') - const cacheSize = utils.getArchiveFileSize(archivePath) + const cacheSize = utils.getArchiveFileSizeIsBytes(archivePath) const commitCacheResponse = await commitCache(httpClient, cacheId, cacheSize) if (!isSuccessStatusCode(commitCacheResponse.statusCode)) { throw new Error( diff --git a/packages/cache/src/internal/cacheUtils.ts b/packages/cache/src/internal/cacheUtils.ts index 8743963aec..f3f8500641 100644 --- a/packages/cache/src/internal/cacheUtils.ts +++ b/packages/cache/src/internal/cacheUtils.ts @@ -34,7 +34,7 @@ export async function createTempDirectory(): Promise { return dest } -export function getArchiveFileSize(filePath: string): number { +export function getArchiveFileSizeIsBytes(filePath: string): number { return fs.statSync(filePath).size } @@ -80,6 +80,7 @@ async function getVersion(app: string): Promise { return versionOutput } +// Use zstandard if possible to maximize cache performance export async function getCompressionMethod(): Promise { const versionOutput = await getVersion('zstd') return versionOutput.toLowerCase().includes('zstd command line interface') diff --git a/packages/cache/src/options.ts b/packages/cache/src/options.ts index c5ecdad5d9..97441c1e07 100644 --- a/packages/cache/src/options.ts +++ b/packages/cache/src/options.ts @@ -9,7 +9,7 @@ export interface UploadOptions { */ uploadConcurrency?: number /** - * Maximum chunk size for cache upload + * Maximum chunk size in bytes for cache upload * * @default 32MB */ From 2fdf3b71f8b439f6a344675b45012927283e1cd2 Mon Sep 17 00:00:00 2001 From: Konrad Pabjan Date: Fri, 15 May 2020 18:34:09 +0200 Subject: [PATCH 150/192] @actions/artifact 0.3.2 --- packages/artifact/RELEASES.md | 6 +++++- packages/artifact/package-lock.json | 2 +- packages/artifact/package.json | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/artifact/RELEASES.md b/packages/artifact/RELEASES.md index cd83e65a26..3f4359eeda 100644 --- a/packages/artifact/RELEASES.md +++ b/packages/artifact/RELEASES.md @@ -23,4 +23,8 @@ ### 0.3.1 - Fix to ensure temporary gzip files get correctly deleted during artifact upload -- Remove spaces as a forbidden character during upload +- Remove spaces as a forbidden character during upload + +### 0.3.2 + +- Fix to ensure readstreams get correctly reset in the event of a retry diff --git a/packages/artifact/package-lock.json b/packages/artifact/package-lock.json index f48f1b823e..ba9dcfbc8f 100644 --- a/packages/artifact/package-lock.json +++ b/packages/artifact/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/artifact", - "version": "0.3.1", + "version": "0.3.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/artifact/package.json b/packages/artifact/package.json index 1068489d23..12fed5a023 100644 --- a/packages/artifact/package.json +++ b/packages/artifact/package.json @@ -1,6 +1,6 @@ { "name": "@actions/artifact", - "version": "0.3.1", + "version": "0.3.2", "preview": true, "description": "Actions artifact lib", "keywords": [ From 77761a4dc9dcbf58536d67169004a2ad8647a2c3 Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Mon, 18 May 2020 16:33:15 -0400 Subject: [PATCH 151/192] Fix issue with using zstd long range mode on ubuntu-16.04 --- packages/cache/package-lock.json | 11 +++++++ packages/cache/package.json | 2 ++ .../cache/src/internal/cacheHttpClient.ts | 4 ++- packages/cache/src/internal/cacheUtils.ts | 23 +++++++++---- packages/cache/src/internal/constants.ts | 1 + packages/cache/src/internal/tar.ts | 32 +++++++++++++++---- 6 files changed, 58 insertions(+), 15 deletions(-) diff --git a/packages/cache/package-lock.json b/packages/cache/package-lock.json index 08f18a7c8a..a6a72274e5 100644 --- a/packages/cache/package-lock.json +++ b/packages/cache/package-lock.json @@ -39,6 +39,12 @@ "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==" }, + "@types/semver": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.1.tgz", + "integrity": "sha512-+beqKQOh9PYxuHvijhVl+tIHvT6tuwOrE9m14zd+MT2A38KoKZhh7pYJ0SNleLtwDsiIxHDsIk9bv01oOxvSvA==", + "dev": true + }, "@types/uuid": { "version": "3.4.9", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.9.tgz", @@ -72,6 +78,11 @@ "brace-expansion": "^1.1.7" } }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, "tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", diff --git a/packages/cache/package.json b/packages/cache/package.json index 69e9318121..25be002e88 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -41,10 +41,12 @@ "@actions/glob": "^0.1.0", "@actions/http-client": "^1.0.8", "@actions/io": "^1.0.1", + "semver": "^6.1.0", "uuid": "^3.3.3" }, "devDependencies": { "typescript": "^3.8.3", + "@types/semver": "^6.0.0", "@types/uuid": "^3.4.5" } } diff --git a/packages/cache/src/internal/cacheHttpClient.ts b/packages/cache/src/internal/cacheHttpClient.ts index b7d34448d3..21af8031d0 100644 --- a/packages/cache/src/internal/cacheHttpClient.ts +++ b/packages/cache/src/internal/cacheHttpClient.ts @@ -96,7 +96,9 @@ export function getCacheVersion( compressionMethod?: CompressionMethod ): string { const components = paths.concat( - compressionMethod === CompressionMethod.Zstd ? [compressionMethod] : [] + !compressionMethod || compressionMethod === CompressionMethod.Gzip + ? [] + : [compressionMethod] ) // Add salt to cache version to support breaking changes in cache entry diff --git a/packages/cache/src/internal/cacheUtils.ts b/packages/cache/src/internal/cacheUtils.ts index f3f8500641..bd698286e1 100644 --- a/packages/cache/src/internal/cacheUtils.ts +++ b/packages/cache/src/internal/cacheUtils.ts @@ -4,6 +4,7 @@ import * as glob from '@actions/glob' import * as io from '@actions/io' import * as fs from 'fs' import * as path from 'path' +import * as semver from 'semver' import * as util from 'util' import {v4 as uuidV4} from 'uuid' import {CacheFilename, CompressionMethod} from './constants' @@ -82,16 +83,24 @@ async function getVersion(app: string): Promise { // Use zstandard if possible to maximize cache performance export async function getCompressionMethod(): Promise { - const versionOutput = await getVersion('zstd') - return versionOutput.toLowerCase().includes('zstd command line interface') - ? CompressionMethod.Zstd - : CompressionMethod.Gzip + if (process.platform === 'win32') { + // Disable zstd on windows due to bug https://github.com/actions/cache/issues/301 + return CompressionMethod.Gzip + } else { + const versionOutput = await getVersion('zstd') + const version = semver.clean(versionOutput) + return !versionOutput.toLowerCase().includes('zstd command line interface') + ? CompressionMethod.Gzip + : !version || semver.lt(version, 'v1.3.2') + ? CompressionMethod.ZstdOld + : CompressionMethod.Zstd + } } export function getCacheFileName(compressionMethod: CompressionMethod): string { - return compressionMethod === CompressionMethod.Zstd - ? CacheFilename.Zstd - : CacheFilename.Gzip + return compressionMethod === CompressionMethod.Gzip + ? CacheFilename.Gzip + : CacheFilename.Zstd } export async function useGnuTar(): Promise { diff --git a/packages/cache/src/internal/constants.ts b/packages/cache/src/internal/constants.ts index b3d2a57746..ec1d494402 100644 --- a/packages/cache/src/internal/constants.ts +++ b/packages/cache/src/internal/constants.ts @@ -5,6 +5,7 @@ export enum CacheFilename { export enum CompressionMethod { Gzip = 'gzip', + ZstdOld = 'zstd-old', Zstd = 'zstd' } diff --git a/packages/cache/src/internal/tar.ts b/packages/cache/src/internal/tar.ts index 221c7c704d..27fef40fc9 100644 --- a/packages/cache/src/internal/tar.ts +++ b/packages/cache/src/internal/tar.ts @@ -41,10 +41,18 @@ export async function extractTar( // --d: Decompress. // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. // Using 30 here because we also support 32-bit self-hosted runners. + function getProg(): string[] { + switch (compressionMethod) { + case CompressionMethod.Zstd: + return ['--use-compress-program', 'zstd -d --long=30'] + case CompressionMethod.ZstdOld: + return ['--use-compress-program', 'zstd -d'] + default: + return ['-z'] + } + } const args = [ - ...(compressionMethod === CompressionMethod.Zstd - ? ['--use-compress-program', 'zstd -d --long=30'] - : ['-z']), + ...getProg(), '-xf', archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', @@ -66,14 +74,24 @@ export async function createTar( path.join(archiveFolder, manifestFilename), sourceDirectories.join('\n') ) + const workingDirectory = getWorkingDirectory() + // -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores. // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. // Using 30 here because we also support 32-bit self-hosted runners. - const workingDirectory = getWorkingDirectory() + // Long range mode is added to zstd in v1.3.2 release, so we will not use --long in older version of zstd. + function getProg(): string[] { + switch (compressionMethod) { + case CompressionMethod.Zstd: + return ['--use-compress-program', 'zstd -T0 --long=30'] + case CompressionMethod.ZstdOld: + return ['--use-compress-program', 'zstd -T0'] + default: + return ['-z'] + } + } const args = [ - ...(compressionMethod === CompressionMethod.Zstd - ? ['--use-compress-program', 'zstd -T0 --long=30'] - : ['-z']), + ...getProg(), '-cf', cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', From 5112dc231eebbed7ab77fa2d285bdc5c1f368ed0 Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Mon, 18 May 2020 16:35:13 -0400 Subject: [PATCH 152/192] Only use zstd on windows when gnu tar is installed, otherwise use gzip due to bug #301 --- packages/cache/__tests__/tar.test.ts | 2 +- packages/cache/src/internal/cacheUtils.ts | 6 +++--- packages/cache/src/internal/tar.ts | 26 ++++++++++++++++------- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/packages/cache/__tests__/tar.test.ts b/packages/cache/__tests__/tar.test.ts index 0aa6c78435..63bd69ef4b 100644 --- a/packages/cache/__tests__/tar.test.ts +++ b/packages/cache/__tests__/tar.test.ts @@ -95,7 +95,7 @@ test('gzip extract GNU tar on windows', async () => { jest.spyOn(fs, 'existsSync').mockReturnValueOnce(false) const isGnuMock = jest - .spyOn(utils, 'useGnuTar') + .spyOn(utils, 'isGnuTarInstalled') .mockReturnValue(Promise.resolve(true)) const execMock = jest.spyOn(exec, 'exec') const archivePath = `${process.env['windir']}\\fakepath\\cache.tar` diff --git a/packages/cache/src/internal/cacheUtils.ts b/packages/cache/src/internal/cacheUtils.ts index bd698286e1..ed230426fe 100644 --- a/packages/cache/src/internal/cacheUtils.ts +++ b/packages/cache/src/internal/cacheUtils.ts @@ -83,8 +83,8 @@ async function getVersion(app: string): Promise { // Use zstandard if possible to maximize cache performance export async function getCompressionMethod(): Promise { - if (process.platform === 'win32') { - // Disable zstd on windows due to bug https://github.com/actions/cache/issues/301 + if (process.platform === 'win32' && !isGnuTarInstalled()) { + // Disable zstd due to bug https://github.com/actions/cache/issues/301 return CompressionMethod.Gzip } else { const versionOutput = await getVersion('zstd') @@ -103,7 +103,7 @@ export function getCacheFileName(compressionMethod: CompressionMethod): string { : CacheFilename.Zstd } -export async function useGnuTar(): Promise { +export async function isGnuTarInstalled(): Promise { const versionOutput = await getVersion('tar') return versionOutput.toLowerCase().includes('gnu tar') } diff --git a/packages/cache/src/internal/tar.ts b/packages/cache/src/internal/tar.ts index 27fef40fc9..823139c138 100644 --- a/packages/cache/src/internal/tar.ts +++ b/packages/cache/src/internal/tar.ts @@ -5,23 +5,33 @@ import * as path from 'path' import * as utils from './cacheUtils' import {CompressionMethod} from './constants' -async function getTarPath(args: string[]): Promise { - // Explicitly use BSD Tar on Windows +async function getTarPath( + args: string[], + compressionMethod: CompressionMethod +): Promise { const IS_WINDOWS = process.platform === 'win32' if (IS_WINDOWS) { const systemTar = `${process.env['windir']}\\System32\\tar.exe` - if (existsSync(systemTar)) { + if (compressionMethod !== CompressionMethod.Gzip) { + // We only use zstandard compression on windows when gnu tar is installed due to + // a bug with compressing large files with bsdtar + zstd + args.push('--force-local') + } else if (existsSync(systemTar)) { return systemTar - } else if (await utils.useGnuTar()) { + } else if (await utils.isGnuTarInstalled()) { args.push('--force-local') } } return await io.which('tar', true) } -async function execTar(args: string[], cwd?: string): Promise { +async function execTar( + args: string[], + compressionMethod: CompressionMethod, + cwd?: string +): Promise { try { - await exec(`"${await getTarPath(args)}"`, args, {cwd}) + await exec(`"${await getTarPath(args, compressionMethod)}"`, args, {cwd}) } catch (error) { throw new Error(`Tar failed with error: ${error?.message}`) } @@ -59,7 +69,7 @@ export async function extractTar( '-C', workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/') ] - await execTar(args) + await execTar(args, compressionMethod) } export async function createTar( @@ -100,5 +110,5 @@ export async function createTar( '--files-from', manifestFilename ] - await execTar(args, archiveFolder) + await execTar(args, compressionMethod, archiveFolder) } From 9a3466a094f06bb1325e8c0e4dd2fa82912aa85d Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Tue, 19 May 2020 11:46:50 -0400 Subject: [PATCH 153/192] Fix windows tests --- packages/cache/__tests__/tar.test.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/cache/__tests__/tar.test.ts b/packages/cache/__tests__/tar.test.ts index 63bd69ef4b..ecb6799bc5 100644 --- a/packages/cache/__tests__/tar.test.ts +++ b/packages/cache/__tests__/tar.test.ts @@ -38,13 +38,11 @@ test('zstd extract tar', async () => { ? `${process.env['windir']}\\fakepath\\cache.tar` : 'cache.tar' const workspace = process.env['GITHUB_WORKSPACE'] + const tarPath = 'tar' await tar.extractTar(archivePath, CompressionMethod.Zstd) expect(mkdirMock).toHaveBeenCalledWith(workspace) - const tarPath = IS_WINDOWS - ? `${process.env['windir']}\\System32\\tar.exe` - : 'tar' expect(execMock).toHaveBeenCalledTimes(1) expect(execMock).toHaveBeenCalledWith( `"${tarPath}"`, @@ -56,7 +54,7 @@ test('zstd extract tar', async () => { '-P', '-C', IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace - ], + ].concat(IS_WINDOWS ? ['--force-local'] : []), {cwd: undefined} ) }) @@ -127,15 +125,12 @@ test('zstd create tar', async () => { const archiveFolder = getTempDir() const workspace = process.env['GITHUB_WORKSPACE'] const sourceDirectories = ['~/.npm/cache', `${workspace}/dist`] + const tarPath = 'tar' await fs.promises.mkdir(archiveFolder, {recursive: true}) await tar.createTar(archiveFolder, sourceDirectories, CompressionMethod.Zstd) - const tarPath = IS_WINDOWS - ? `${process.env['windir']}\\System32\\tar.exe` - : 'tar' - expect(execMock).toHaveBeenCalledTimes(1) expect(execMock).toHaveBeenCalledWith( `"${tarPath}"`, @@ -149,7 +144,7 @@ test('zstd create tar', async () => { IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace, '--files-from', 'manifest.txt' - ], + ].concat(IS_WINDOWS ? ['--force-local'] : []), { cwd: archiveFolder } From fde62212283ef55cec53470e275b8e9d2024a826 Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Tue, 19 May 2020 12:38:45 -0400 Subject: [PATCH 154/192] React to feedback --- packages/cache/src/internal/cacheUtils.ts | 20 +++++++++++++------- packages/cache/src/internal/constants.ts | 4 +++- packages/cache/src/internal/tar.ts | 12 ++++++------ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/packages/cache/src/internal/cacheUtils.ts b/packages/cache/src/internal/cacheUtils.ts index ed230426fe..53b35afae7 100644 --- a/packages/cache/src/internal/cacheUtils.ts +++ b/packages/cache/src/internal/cacheUtils.ts @@ -86,14 +86,20 @@ export async function getCompressionMethod(): Promise { if (process.platform === 'win32' && !isGnuTarInstalled()) { // Disable zstd due to bug https://github.com/actions/cache/issues/301 return CompressionMethod.Gzip + } + + const versionOutput = await getVersion('zstd') + const version = semver.clean(versionOutput) + + if (!versionOutput.toLowerCase().includes('zstd command line interface')) { + // zstd is not installed + return CompressionMethod.Gzip + } else if (!version || semver.lt(version, 'v1.3.2')) { + // zstd is installed but using a version earlier than v1.3.2 + // v1.3.2 is required to use the `--long` options in zstd + return CompressionMethod.ZstdWithoutLong } else { - const versionOutput = await getVersion('zstd') - const version = semver.clean(versionOutput) - return !versionOutput.toLowerCase().includes('zstd command line interface') - ? CompressionMethod.Gzip - : !version || semver.lt(version, 'v1.3.2') - ? CompressionMethod.ZstdOld - : CompressionMethod.Zstd + return CompressionMethod.Zstd } } diff --git a/packages/cache/src/internal/constants.ts b/packages/cache/src/internal/constants.ts index ec1d494402..06c8c8f0bb 100644 --- a/packages/cache/src/internal/constants.ts +++ b/packages/cache/src/internal/constants.ts @@ -5,7 +5,9 @@ export enum CacheFilename { export enum CompressionMethod { Gzip = 'gzip', - ZstdOld = 'zstd-old', + // Long range mode was added to zstd in v1.3.2. + // This enum is for earlier version of zstd that does not have --long support + ZstdWithoutLong = 'zstd-without-long', Zstd = 'zstd' } diff --git a/packages/cache/src/internal/tar.ts b/packages/cache/src/internal/tar.ts index 823139c138..7f836b83d3 100644 --- a/packages/cache/src/internal/tar.ts +++ b/packages/cache/src/internal/tar.ts @@ -51,18 +51,18 @@ export async function extractTar( // --d: Decompress. // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. // Using 30 here because we also support 32-bit self-hosted runners. - function getProg(): string[] { + function getCompressionProgram(): string[] { switch (compressionMethod) { case CompressionMethod.Zstd: return ['--use-compress-program', 'zstd -d --long=30'] - case CompressionMethod.ZstdOld: + case CompressionMethod.ZstdWithoutLong: return ['--use-compress-program', 'zstd -d'] default: return ['-z'] } } const args = [ - ...getProg(), + ...getCompressionProgram(), '-xf', archivePath.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', @@ -90,18 +90,18 @@ export async function createTar( // --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit. // Using 30 here because we also support 32-bit self-hosted runners. // Long range mode is added to zstd in v1.3.2 release, so we will not use --long in older version of zstd. - function getProg(): string[] { + function getCompressionProgram(): string[] { switch (compressionMethod) { case CompressionMethod.Zstd: return ['--use-compress-program', 'zstd -T0 --long=30'] - case CompressionMethod.ZstdOld: + case CompressionMethod.ZstdWithoutLong: return ['--use-compress-program', 'zstd -T0'] default: return ['-z'] } } const args = [ - ...getProg(), + ...getCompressionProgram(), '-cf', cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'), '-P', From 0dea61a3a89c82e6cb29548ab0fd8b887cc2d731 Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Tue, 19 May 2020 12:59:38 -0400 Subject: [PATCH 155/192] Bump cache package version --- packages/cache/RELEASES.md | 5 +- packages/cache/package-lock.json | 202 +++++++++++++++---------------- packages/cache/package.json | 2 +- 3 files changed, 106 insertions(+), 103 deletions(-) diff --git a/packages/cache/RELEASES.md b/packages/cache/RELEASES.md index 920a20e8ed..66c248ca10 100644 --- a/packages/cache/RELEASES.md +++ b/packages/cache/RELEASES.md @@ -2,4 +2,7 @@ ### 0.1.0 -- Initial release \ No newline at end of file +- Initial release + +### 0.2.0 +- Fixes two issues around using zstd compression algorithm \ No newline at end of file diff --git a/packages/cache/package-lock.json b/packages/cache/package-lock.json index a6a72274e5..80ef7664b0 100644 --- a/packages/cache/package-lock.json +++ b/packages/cache/package-lock.json @@ -1,103 +1,103 @@ { - "name": "@actions/cache", - "version": "0.1.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@actions/core": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.4.tgz", - "integrity": "sha512-YJCEq8BE3CdN8+7HPZ/4DxJjk/OkZV2FFIf+DlZTC/4iBlzYCD5yjRR6eiOS5llO11zbRltIRuKAjMKaWTE6cg==" - }, - "@actions/exec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.4.tgz", - "integrity": "sha512-4DPChWow9yc9W3WqEbUj8Nr86xkpyE29ZzWjXucHItclLbEW6jr80Zx4nqv18QL6KK65+cifiQZXvnqgTV6oHw==", - "requires": { - "@actions/io": "^1.0.1" - } - }, - "@actions/glob": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.0.tgz", - "integrity": "sha512-lx8SzyQ2FE9+UUvjqY1f28QbTJv+w8qP7kHHbfQRhphrlcx0Mdmm1tZdGJzfxv1jxREa/sLW4Oy8CbGQKCJySA==", - "requires": { - "@actions/core": "^1.2.0", - "minimatch": "^3.0.4" - } - }, - "@actions/http-client": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz", - "integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==", - "requires": { - "tunnel": "0.0.6" - } - }, - "@actions/io": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", - "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==" - }, - "@types/semver": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.1.tgz", - "integrity": "sha512-+beqKQOh9PYxuHvijhVl+tIHvT6tuwOrE9m14zd+MT2A38KoKZhh7pYJ0SNleLtwDsiIxHDsIk9bv01oOxvSvA==", - "dev": true - }, - "@types/uuid": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.9.tgz", - "integrity": "sha512-XDwyIlt/47l2kWLTzw/mtrpLdB+GPSskR2n/PIcPn+VYhVO77rGhRncIR5GPU0KRzXuqkDO+J5qqrG0Y8P6jzQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" - }, - "typescript": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", - "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", - "dev": true - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - } - } + "name": "@actions/cache", + "version": "0.2.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@actions/core": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.4.tgz", + "integrity": "sha512-YJCEq8BE3CdN8+7HPZ/4DxJjk/OkZV2FFIf+DlZTC/4iBlzYCD5yjRR6eiOS5llO11zbRltIRuKAjMKaWTE6cg==" + }, + "@actions/exec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.4.tgz", + "integrity": "sha512-4DPChWow9yc9W3WqEbUj8Nr86xkpyE29ZzWjXucHItclLbEW6jr80Zx4nqv18QL6KK65+cifiQZXvnqgTV6oHw==", + "requires": { + "@actions/io": "^1.0.1" + } + }, + "@actions/glob": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.0.tgz", + "integrity": "sha512-lx8SzyQ2FE9+UUvjqY1f28QbTJv+w8qP7kHHbfQRhphrlcx0Mdmm1tZdGJzfxv1jxREa/sLW4Oy8CbGQKCJySA==", + "requires": { + "@actions/core": "^1.2.0", + "minimatch": "^3.0.4" + } + }, + "@actions/http-client": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz", + "integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==", + "requires": { + "tunnel": "0.0.6" + } + }, + "@actions/io": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", + "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==" + }, + "@types/semver": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.1.tgz", + "integrity": "sha512-+beqKQOh9PYxuHvijhVl+tIHvT6tuwOrE9m14zd+MT2A38KoKZhh7pYJ0SNleLtwDsiIxHDsIk9bv01oOxvSvA==", + "dev": true + }, + "@types/uuid": { + "version": "3.4.9", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.9.tgz", + "integrity": "sha512-XDwyIlt/47l2kWLTzw/mtrpLdB+GPSskR2n/PIcPn+VYhVO77rGhRncIR5GPU0KRzXuqkDO+J5qqrG0Y8P6jzQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } } diff --git a/packages/cache/package.json b/packages/cache/package.json index 25be002e88..928a02831a 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -1,6 +1,6 @@ { "name": "@actions/cache", - "version": "0.1.0", + "version": "0.2.0", "preview": true, "description": "Actions cache lib", "keywords": [ From e3a666f5b7c8945c9e4006445d22b47d42fbb58f Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Tue, 19 May 2020 13:20:27 -0400 Subject: [PATCH 156/192] Minor edit to cache release note --- packages/cache/RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cache/RELEASES.md b/packages/cache/RELEASES.md index 66c248ca10..62ed2a29c0 100644 --- a/packages/cache/RELEASES.md +++ b/packages/cache/RELEASES.md @@ -5,4 +5,4 @@ - Initial release ### 0.2.0 -- Fixes two issues around using zstd compression algorithm \ No newline at end of file +- Fixes issues with the zstd compression algorithm on Windows and Ubuntu 16.04 [#469](https://github.com/actions/toolkit/pull/469) \ No newline at end of file From 4e9375da095a1d1f59b3314589fab6cd0d215139 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Tue, 19 May 2020 13:25:57 -0400 Subject: [PATCH 157/192] Tool cache install from a manifest file (#382) --- .eslintrc.json | 2 +- packages/github/__tests__/lib.test.ts | 1 - .../__tests__/data/versions-manifest.json | 86 ++++++ .../tool-cache/__tests__/manifest.test.ts | 272 ++++++++++++++++++ packages/tool-cache/package-lock.json | 2 +- packages/tool-cache/package.json | 2 +- packages/tool-cache/src/manifest.ts | 158 ++++++++++ packages/tool-cache/src/tool-cache.ts | 120 +++++++- 8 files changed, 633 insertions(+), 10 deletions(-) create mode 100644 packages/tool-cache/__tests__/data/versions-manifest.json create mode 100644 packages/tool-cache/__tests__/manifest.test.ts create mode 100644 packages/tool-cache/src/manifest.ts diff --git a/.eslintrc.json b/.eslintrc.json index 2f73a3cf2a..5833878cc5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -18,7 +18,7 @@ "@typescript-eslint/await-thenable": "error", "@typescript-eslint/ban-ts-ignore": "error", "camelcase": "off", - "@typescript-eslint/camelcase": "error", + "@typescript-eslint/camelcase": "off", "@typescript-eslint/class-name-casing": "error", "@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}], "@typescript-eslint/func-call-spacing": ["error", "never"], diff --git a/packages/github/__tests__/lib.test.ts b/packages/github/__tests__/lib.test.ts index dae2a8cdc2..d2754729ee 100644 --- a/packages/github/__tests__/lib.test.ts +++ b/packages/github/__tests__/lib.test.ts @@ -55,7 +55,6 @@ describe('@actions/context', () => { it('works with pull_request payloads', () => { delete process.env.GITHUB_REPOSITORY context.payload = { - // eslint-disable-next-line @typescript-eslint/camelcase pull_request: {number: 2}, repository: {owner: {login: 'user'}, name: 'test'} } diff --git a/packages/tool-cache/__tests__/data/versions-manifest.json b/packages/tool-cache/__tests__/data/versions-manifest.json new file mode 100644 index 0000000000..0c45b81a91 --- /dev/null +++ b/packages/tool-cache/__tests__/data/versions-manifest.json @@ -0,0 +1,86 @@ +[ + { + "version": "3.0.1", + "stable": false, + "release_url": "https://github.com/actions/sometool/releases/tag/3.0.1-20200402.6", + "files": [ + { + "filename": "sometool-3.0.1-linux-x64.tar.gz", + "arch": "x64", + "platform": "linux", + "download_url": "https://github.com/actions/sometool/releases/tag/3.0.1-20200402.6/sometool-1.2.3-linux-x64.tar.gz" + } + ] + }, + { + "version": "2.0.2", + "stable": true, + "release_url": "https://github.com/actions/sometool/releases/tag/2.0.2-20200402.6", + "files": [ + { + "filename": "sometool-2.0.2-linux-x64.tar.gz", + "arch": "x64", + "platform": "linux", + "download_url": "https://github.com/actions/sometool/releases/tag/2.0.2-20200402.6/sometool-2.0.2-linux-x64.tar.gz" + }, + { + "filename": "sometool-2.0.2-linux-x32.tar.gz", + "arch": "x32", + "platform": "linux", + "download_url": "https://github.com/actions/sometool/releases/tag/2.0.2-20200402.6/sometool-2.0.2-linux-x32.tar.gz" + }, + { + "filename": "sometool-2.0.2-linux-x32.tar.gz", + "arch": "x64", + "platform": "windows", + "download_url": "https://github.com/actions/sometool/releases/tag/2.0.2-20200402.6/sometool-2.0.2-linux-x32.tar.gz" + } + ] + }, + { + "version": "1.2.4", + "stable": true, + "release_url": "https://github.com/actions/sometool/releases/tag/1.2.4-20200402.6", + "files": [ + { + "filename": "sometool-1.2.4-ubuntu1804-x64.tar.gz", + "arch": "x64", + "platform": "linux", + "platform_version": "18.04", + "download_url": "https://github.com/actions/sometool/releases/tag/1.2.4-20200402.6/sometool-1.2.4-ubuntu1804-x64.tar.gz" + }, + { + "filename": "sometool-1.2.4-darwin1015-x64.tar.gz", + "arch": "x64", + "platform": "darwin", + "platform_version": "10.15", + "download_url": "https://github.com/actions/sometool/releases/tag/1.2.4-20200402.6/sometool-1.2.4-darwin1015-x64.tar.gz" + } + ] + }, + { + "version": "1.2.3", + "stable": true, + "release_url": "https://github.com/actions/sometool/releases/tag/1.2.3-20200402.6", + "files": [ + { + "filename": "sometool-1.2.3-linux-x64.tar.gz", + "arch": "x64", + "platform": "linux", + "download_url": "https://github.com/actions/sometool/releases/tag/1.2.3-20200402.6/sometool-1.2.3-linux-x64.tar.gz" + }, + { + "filename": "sometool-1.2.3-linux-x32.tar.gz", + "arch": "x32", + "platform": "linux", + "download_url": "https://github.com/actions/sometool/releases/tag/1.2.3-20200402.6/sometool-1.2.3-linux-x32.tar.gz" + }, + { + "filename": "sometool-1.2.3-linux-x32.zip", + "arch": "x64", + "platform": "windows", + "download_url": "https://github.com/actions/sometool/releases/tag/1.2.3-20200402.6/sometool-1.2.3-linux-x32.zip" + } + ] + } +] \ No newline at end of file diff --git a/packages/tool-cache/__tests__/manifest.test.ts b/packages/tool-cache/__tests__/manifest.test.ts new file mode 100644 index 0000000000..1c93b62790 --- /dev/null +++ b/packages/tool-cache/__tests__/manifest.test.ts @@ -0,0 +1,272 @@ +import * as tc from '../src/tool-cache' +import * as mm from '../src/manifest' // --> OFF + +// needs to be require for core node modules to be mocked +// eslint-disable-next-line @typescript-eslint/no-require-imports +import osm = require('os') +// eslint-disable-next-line @typescript-eslint/no-require-imports +import cp = require('child_process') +//import {coerce} from 'semver' + +// we fetch the manifest file from master of a repo +const owner = 'actions' +const repo = 'some-tool' +const fakeToken = 'notrealtoken' + +// just loading data and require handles BOMs etc. +// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires +const manifestData = require('./data/versions-manifest.json') + +describe('@actions/tool-cache-manifest', () => { + let os: {platform: string; arch: string} + + let getSpy: jest.SpyInstance + let platSpy: jest.SpyInstance + let archSpy: jest.SpyInstance + let execSpy: jest.SpyInstance + let readLsbSpy: jest.SpyInstance + + beforeEach(() => { + // node + os = {platform: '', arch: ''} + platSpy = jest.spyOn(osm, 'platform') + + platSpy.mockImplementation(() => os.platform) + archSpy = jest.spyOn(osm, 'arch') + archSpy.mockImplementation(() => os.arch) + + execSpy = jest.spyOn(cp, 'execSync') + readLsbSpy = jest.spyOn(mm, '_readLinuxVersionFile') + + getSpy = jest.spyOn(tc, 'getManifestFromRepo') + getSpy.mockImplementation(() => manifestData) + }) + + afterEach(() => { + jest.resetAllMocks() + jest.clearAllMocks() + //jest.restoreAllMocks(); + }) + + afterAll(async () => {}, 100000) + + it('can query versions', async () => { + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + + expect(manifest).toBeDefined() + const l: number = manifest ? manifest.length : 0 + expect(l).toBe(4) + }) + + it('can match stable major version for linux x64', async () => { + os.platform = 'linux' + os.arch = 'x64' + + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + const release: tc.IToolRelease | undefined = await tc.findFromManifest( + '2.x', + true, + manifest + ) + expect(release).toBeDefined() + expect(release?.version).toBe('2.0.2') + expect(release?.files.length).toBe(1) + const file = release?.files[0] + expect(file).toBeDefined() + expect(file?.arch).toBe('x64') + expect(file?.platform).toBe('linux') + expect(file?.download_url).toBe( + 'https://github.com/actions/sometool/releases/tag/2.0.2-20200402.6/sometool-2.0.2-linux-x64.tar.gz' + ) + expect(file?.filename).toBe('sometool-2.0.2-linux-x64.tar.gz') + }) + + it('can match stable exact version for linux x64', async () => { + os.platform = 'linux' + os.arch = 'x64' + + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + const release: tc.IToolRelease | undefined = await tc.findFromManifest( + '1.2.3', + true, + manifest + ) + expect(release).toBeDefined() + expect(release?.version).toBe('1.2.3') + expect(release?.files.length).toBe(1) + const file = release?.files[0] + expect(file).toBeDefined() + expect(file?.arch).toBe('x64') + expect(file?.platform).toBe('linux') + expect(file?.download_url).toBe( + 'https://github.com/actions/sometool/releases/tag/1.2.3-20200402.6/sometool-1.2.3-linux-x64.tar.gz' + ) + expect(file?.filename).toBe('sometool-1.2.3-linux-x64.tar.gz') + }) + + it('can match with linux platform version spec', async () => { + os.platform = 'linux' + os.arch = 'x64' + + readLsbSpy.mockImplementation(() => { + return `DISTRIB_ID=Ubuntu + DISTRIB_RELEASE=18.04 + DISTRIB_CODENAME=bionic + DISTRIB_DESCRIPTION=Ubuntu 18.04.4 LTS` + }) + + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + const release: tc.IToolRelease | undefined = await tc.findFromManifest( + '1.2.4', + true, + manifest + ) + expect(release).toBeDefined() + expect(release?.version).toBe('1.2.4') + expect(release?.files.length).toBe(1) + const file = release?.files[0] + expect(file).toBeDefined() + expect(file?.arch).toBe('x64') + expect(file?.platform).toBe('linux') + expect(file?.download_url).toBe( + 'https://github.com/actions/sometool/releases/tag/1.2.4-20200402.6/sometool-1.2.4-ubuntu1804-x64.tar.gz' + ) + expect(file?.filename).toBe('sometool-1.2.4-ubuntu1804-x64.tar.gz') + }) + + it('can match with darwin platform version spec', async () => { + os.platform = 'darwin' + os.arch = 'x64' + + execSpy.mockImplementation(() => '10.15.1') + + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + const release: tc.IToolRelease | undefined = await tc.findFromManifest( + '1.2.4', + true, + manifest + ) + expect(release).toBeDefined() + expect(release?.version).toBe('1.2.4') + expect(release?.files.length).toBe(1) + const file = release?.files[0] + expect(file).toBeDefined() + expect(file?.arch).toBe('x64') + expect(file?.platform).toBe('darwin') + expect(file?.download_url).toBe( + 'https://github.com/actions/sometool/releases/tag/1.2.4-20200402.6/sometool-1.2.4-darwin1015-x64.tar.gz' + ) + expect(file?.filename).toBe('sometool-1.2.4-darwin1015-x64.tar.gz') + }) + + it('does not match with unmatched linux platform version spec', async () => { + os.platform = 'linux' + os.arch = 'x64' + + readLsbSpy.mockImplementation(() => { + return `DISTRIB_ID=Ubuntu + DISTRIB_RELEASE=16.04 + DISTRIB_CODENAME=xenial + DISTRIB_DESCRIPTION=Ubuntu 16.04.4 LTS` + }) + + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + const release: tc.IToolRelease | undefined = await tc.findFromManifest( + '1.2.4', + true, + manifest + ) + expect(release).toBeUndefined() + }) + + it('does not match with unmatched darwin platform version spec', async () => { + os.platform = 'darwin' + os.arch = 'x64' + + execSpy.mockImplementation(() => '10.14.6') + + const manifest: mm.IToolRelease[] | null = await tc.getManifestFromRepo( + owner, + repo, + fakeToken + ) + const release: tc.IToolRelease | undefined = await tc.findFromManifest( + '1.2.4', + true, + manifest + ) + expect(release).toBeUndefined() + }) + + it('can get version from lsb on ubuntu-18.04', async () => { + os.platform = 'linux' + os.arch = 'x64' + + //existsSpy.mockImplementation(() => true) + readLsbSpy.mockImplementation(() => { + return `DISTRIB_ID=Ubuntu + DISTRIB_RELEASE=18.04 + DISTRIB_CODENAME=bionic + DISTRIB_DESCRIPTION=Ubuntu 18.04.4 LTS` + }) + + const version = mm._getOsVersion() + + expect(osm.platform()).toBe('linux') + expect(version).toBe('18.04') + }) + + it('can get version from lsb on ubuntu-16.04', async () => { + os.platform = 'linux' + os.arch = 'x64' + + readLsbSpy.mockImplementation(() => { + return `DISTRIB_ID=Ubuntu + DISTRIB_RELEASE=16.04 + DISTRIB_CODENAME=xenial + DISTRIB_DESCRIPTION="Ubuntu 16.04.6 LTS"` + }) + + const version = mm._getOsVersion() + + expect(osm.platform()).toBe('linux') + expect(version).toBe('16.04') + }) + + // sw_vers -productVersion + it('can get version on macOS', async () => { + os.platform = 'darwin' + os.arch = 'x64' + + execSpy.mockImplementation(() => '10.14.6') + + const version = mm._getOsVersion() + + expect(osm.platform()).toBe('darwin') + expect(version).toBe('10.14.6') + }) +}) diff --git a/packages/tool-cache/package-lock.json b/packages/tool-cache/package-lock.json index 6aadb28909..6be15c3d2d 100644 --- a/packages/tool-cache/package-lock.json +++ b/packages/tool-cache/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.3.5", + "version": "1.5.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index 78b71778b4..e892310474 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.3.5", + "version": "1.5.4", "description": "Actions tool-cache lib", "keywords": [ "github", diff --git a/packages/tool-cache/src/manifest.ts b/packages/tool-cache/src/manifest.ts new file mode 100644 index 0000000000..a85e771c8a --- /dev/null +++ b/packages/tool-cache/src/manifest.ts @@ -0,0 +1,158 @@ +import * as semver from 'semver' +import {debug} from '@actions/core' + +// needs to be require for core node modules to be mocked +/* eslint @typescript-eslint/no-require-imports: 0 */ + +import os = require('os') +import cp = require('child_process') +import fs = require('fs') + +/* +NOTE: versions must be sorted descending by version in the manifest + this library short circuits on first semver spec match + + platform_version is an optional filter and can be a semver spec or range +[ + { + "version": "1.2.3", + "stable": true, + "release_url": "https://github.com/actions/sometool/releases/tag/1.2.3-20200402.6", + "files": [ + { + "filename": "sometool-1.2.3-linux-x64.zip", + "arch": "x64", + "platform": "linux", + "platform_version": "18.04" + "download_url": "https://github.com/actions/sometool/releases/tag/1.2.3-20200402.6/sometool-1.2.3-linux-x64.zip" + }, + ... + ] + }, + ... +] +*/ + +export interface IToolReleaseFile { + filename: string + // 'aix', 'darwin', 'freebsd', 'linux', 'openbsd', + // 'sunos', and 'win32' + // platform_version is an optional semver filter + // TODO: do we need distribution (e.g. ubuntu). + // not adding yet but might need someday. + // right now, 16.04 and 18.04 work + platform: string + platform_version?: string + + // 'arm', 'arm64', 'ia32', 'mips', 'mipsel', + // 'ppc', 'ppc64', 's390', 's390x', + // 'x32', and 'x64'. + arch: string + + download_url: string +} + +export interface IToolRelease { + version: string + stable: boolean + release_url: string + files: IToolReleaseFile[] +} + +export async function _findMatch( + versionSpec: string, + stable: boolean, + candidates: IToolRelease[], + archFilter: string +): Promise { + const platFilter = os.platform() + + let result: IToolRelease | undefined + let match: IToolRelease | undefined + + let file: IToolReleaseFile | undefined + for (const candidate of candidates) { + const version = candidate.version + + debug(`check ${version} satisfies ${versionSpec}`) + if ( + semver.satisfies(version, versionSpec) && + (!stable || candidate.stable === stable) + ) { + file = candidate.files.find(item => { + debug( + `${item.arch}===${archFilter} && ${item.platform}===${platFilter}` + ) + + let chk = item.arch === archFilter && item.platform === platFilter + if (chk && item.platform_version) { + const osVersion = module.exports._getOsVersion() + + if (osVersion === item.platform_version) { + chk = true + } else { + chk = semver.satisfies(osVersion, item.platform_version) + } + } + + return chk + }) + + if (file) { + debug(`matched ${candidate.version}`) + match = candidate + break + } + } + } + + if (match && file) { + // clone since we're mutating the file list to be only the file that matches + result = Object.assign({}, match) + result.files = [file] + } + + return result +} + +export function _getOsVersion(): string { + // TODO: add windows and other linux, arm variants + // right now filtering on version is only an ubuntu and macos scenario for tools we build for hosted (python) + const plat = os.platform() + let version = '' + + if (plat === 'darwin') { + version = cp.execSync('sw_vers -productVersion').toString() + } else if (plat === 'linux') { + // lsb_release process not in some containers, readfile + // Run cat /etc/lsb-release + // DISTRIB_ID=Ubuntu + // DISTRIB_RELEASE=18.04 + // DISTRIB_CODENAME=bionic + // DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS" + const lsbContents = module.exports._readLinuxVersionFile() + if (lsbContents) { + const lines = lsbContents.split('\n') + for (const line of lines) { + const parts = line.split('=') + if (parts.length === 2 && parts[0].trim() === 'DISTRIB_RELEASE') { + version = parts[1].trim() + break + } + } + } + } + + return version +} + +export function _readLinuxVersionFile(): string { + const lsbFile = '/etc/lsb-release' + let contents = '' + + if (fs.existsSync(lsbFile)) { + contents = fs.readFileSync(lsbFile).toString() + } + + return contents +} diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index 6fa2d510eb..385569ca14 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -1,6 +1,7 @@ import * as core from '@actions/core' import * as io from '@actions/io' import * as fs from 'fs' +import * as mm from './manifest' import * as os from 'os' import * as path from 'path' import * as httpm from '@actions/http-client' @@ -12,6 +13,7 @@ import {exec} from '@actions/exec/lib/exec' import {ExecOptions} from '@actions/exec/lib/interfaces' import {ok} from 'assert' import {RetryHelper} from './retry-helper' +import {IHeaders} from '@actions/http-client/interfaces' export class HTTPError extends Error { constructor(readonly httpStatusCode: number | undefined) { @@ -28,11 +30,13 @@ const userAgent = 'actions/tool-cache' * * @param url url of tool to download * @param dest path to download tool + * @param auth authorization header * @returns path to downloaded tool */ export async function downloadTool( url: string, - dest?: string + dest?: string, + auth?: string ): Promise { dest = dest || path.join(_getTempDirectory(), uuidV4()) await io.mkdirP(path.dirname(dest)) @@ -51,7 +55,7 @@ export async function downloadTool( const retryHelper = new RetryHelper(maxAttempts, minSeconds, maxSeconds) return await retryHelper.execute( async () => { - return await downloadToolAttempt(url, dest || '') + return await downloadToolAttempt(url, dest || '', auth) }, (err: Error) => { if (err instanceof HTTPError && err.httpStatusCode) { @@ -71,7 +75,11 @@ export async function downloadTool( ) } -async function downloadToolAttempt(url: string, dest: string): Promise { +async function downloadToolAttempt( + url: string, + dest: string, + auth?: string +): Promise { if (fs.existsSync(dest)) { throw new Error(`Destination file path ${dest} already exists`) } @@ -80,7 +88,16 @@ async function downloadToolAttempt(url: string, dest: string): Promise { const http = new httpm.HttpClient(userAgent, [], { allowRetries: false }) - const response: httpm.HttpClientResponse = await http.get(url) + + let headers: IHeaders | undefined + if (auth) { + core.debug('set auth') + headers = { + authorization: auth + } + } + + const response: httpm.HttpClientResponse = await http.get(url, headers) if (response.message.statusCode !== 200) { const err = new HTTPError(response.message.statusCode) core.debug( @@ -202,7 +219,7 @@ export async function extract7z( export async function extractTar( file: string, dest?: string, - flags: string = 'xz' + flags: string | string[] = 'xz' ): Promise { if (!file) { throw new Error("parameter 'file' is required") @@ -226,7 +243,12 @@ export async function extractTar( const isGnuTar = versionOutput.toUpperCase().includes('GNU TAR') // Initialize args - const args = [flags] + let args: string[] + if (flags instanceof Array) { + args = flags + } else { + args = [flags] + } if (core.isDebug() && !flags.includes('v')) { args.push('-v') @@ -463,6 +485,92 @@ export function findAllVersions(toolName: string, arch?: string): string[] { return versions } +// versions-manifest +// +// typical pattern of a setup-* action that supports JIT would be: +// 1. resolve semver against local cache +// +// 2. if no match, download +// a. query versions manifest to match +// b. if no match, fall back to source if exists (tool distribution) +// c. with download url, download, install and preprent path + +export type IToolRelease = mm.IToolRelease +export type IToolReleaseFile = mm.IToolReleaseFile + +interface GitHubTreeItem { + path: string + size: string + url: string +} + +interface GitHubTree { + tree: GitHubTreeItem[] + truncated: boolean +} + +export async function getManifestFromRepo( + owner: string, + repo: string, + auth?: string, + branch = 'master' +): Promise { + let releases: IToolRelease[] = [] + const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${branch}` + + const http: httpm.HttpClient = new httpm.HttpClient('tool-cache') + const headers: IHeaders = {} + if (auth) { + core.debug('set auth') + headers.authorization = auth + } + + const response = await http.getJson(treeUrl, headers) + if (!response.result) { + return releases + } + + let manifestUrl = '' + for (const item of response.result.tree) { + if (item.path === 'versions-manifest.json') { + manifestUrl = item.url + break + } + } + + headers['accept'] = 'application/vnd.github.VERSION.raw' + let versionsRaw = await (await http.get(manifestUrl, headers)).readBody() + + if (versionsRaw) { + // shouldn't be needed but protects against invalid json saved with BOM + versionsRaw = versionsRaw.replace(/^\uFEFF/, '') + try { + releases = JSON.parse(versionsRaw) + } catch { + core.debug('Invalid json') + } + } + + return releases +} + +export async function findFromManifest( + versionSpec: string, + stable: boolean, + manifest: IToolRelease[], + archFilter: string = os.arch() +): Promise { + // wrap the internal impl + const match: mm.IToolRelease | undefined = await mm._findMatch( + versionSpec, + stable, + manifest, + archFilter + ) + + return match +} + async function _createExtractFolder(dest?: string): Promise { if (!dest) { // create a temp dir From f4aa8241356d449ddb0b48376c8159b3096ff4c4 Mon Sep 17 00:00:00 2001 From: Bryan MacFarlane Date: Tue, 19 May 2020 13:38:19 -0400 Subject: [PATCH 158/192] bump tool-cache version --- packages/tool-cache/package-lock.json | 2 +- packages/tool-cache/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/tool-cache/package-lock.json b/packages/tool-cache/package-lock.json index 6be15c3d2d..2dd920bc0c 100644 --- a/packages/tool-cache/package-lock.json +++ b/packages/tool-cache/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.5.3", + "version": "1.5.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index e892310474..536a8e92de 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.5.4", + "version": "1.5.5", "description": "Actions tool-cache lib", "keywords": [ "github", From 2c693984a88237db529728f65325feedd73ae6c0 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Tue, 19 May 2020 13:43:20 -0400 Subject: [PATCH 159/192] Update Bug Report Template (#466) --- .github/ISSUE_TEMPLATE/bug_report.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index f3d5c415e0..424c96158c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,6 +7,14 @@ assignees: '' --- +Thank you 🙇‍♀ for wanting to create an issue in this repository. Before you do, please ensure you are filing the issue in the right place. Issues should only be opened on if the issue **relates to code in this repository**. + +* If you have found a security issue [please submit it here](https://hackerone.com/github) +* If you have questions about writing workflows or action files, then please [visit the GitHub Community Forum's Actions Board](https://github.community/t5/GitHub-Actions/bd-p/actions) +* If you are having an issue or question about GitHub Actions then please [contact customer support](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-github-actions#contacting-support) + +If your issue is relevant to this repository, please include the information below: + **Describe the bug** A clear and concise description of what the bug is. From f84d1a2ae2e7d0961050c3957f3daeb73de07223 Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Tue, 19 May 2020 16:12:28 -0400 Subject: [PATCH 160/192] Fix a bug with getCompressionMethod --- packages/cache/RELEASES.md | 2 +- packages/cache/package-lock.json | 2 +- packages/cache/package.json | 2 +- packages/cache/src/internal/cacheUtils.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cache/RELEASES.md b/packages/cache/RELEASES.md index 62ed2a29c0..c6f6ad29ee 100644 --- a/packages/cache/RELEASES.md +++ b/packages/cache/RELEASES.md @@ -4,5 +4,5 @@ - Initial release -### 0.2.0 +### 0.2.1 - Fixes issues with the zstd compression algorithm on Windows and Ubuntu 16.04 [#469](https://github.com/actions/toolkit/pull/469) \ No newline at end of file diff --git a/packages/cache/package-lock.json b/packages/cache/package-lock.json index 80ef7664b0..a05581c189 100644 --- a/packages/cache/package-lock.json +++ b/packages/cache/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/cache", - "version": "0.2.0", + "version": "0.2.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/cache/package.json b/packages/cache/package.json index 928a02831a..c9c9eba1ba 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -1,6 +1,6 @@ { "name": "@actions/cache", - "version": "0.2.0", + "version": "0.2.1", "preview": true, "description": "Actions cache lib", "keywords": [ diff --git a/packages/cache/src/internal/cacheUtils.ts b/packages/cache/src/internal/cacheUtils.ts index 53b35afae7..b279369514 100644 --- a/packages/cache/src/internal/cacheUtils.ts +++ b/packages/cache/src/internal/cacheUtils.ts @@ -83,7 +83,7 @@ async function getVersion(app: string): Promise { // Use zstandard if possible to maximize cache performance export async function getCompressionMethod(): Promise { - if (process.platform === 'win32' && !isGnuTarInstalled()) { + if (process.platform === 'win32' && !(await isGnuTarInstalled())) { // Disable zstd due to bug https://github.com/actions/cache/issues/301 return CompressionMethod.Gzip } From 44a99f6c4363791031fa25860f29e150c0afe452 Mon Sep 17 00:00:00 2001 From: Aiqiao Yan Date: Tue, 19 May 2020 16:36:18 -0400 Subject: [PATCH 161/192] Add section for 0.2.1 --- packages/cache/RELEASES.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/cache/RELEASES.md b/packages/cache/RELEASES.md index c6f6ad29ee..0e623d9e4c 100644 --- a/packages/cache/RELEASES.md +++ b/packages/cache/RELEASES.md @@ -4,5 +4,8 @@ - Initial release +### 0.2.0 +- Fixes issues with the zstd compression algorithm on Windows and Ubuntu 16.04 [#469](https://github.com/actions/toolkit/pull/469) + ### 0.2.1 -- Fixes issues with the zstd compression algorithm on Windows and Ubuntu 16.04 [#469](https://github.com/actions/toolkit/pull/469) \ No newline at end of file +- Fix to await async function getCompressionMethod \ No newline at end of file From 8ae8acce725ed5023ea203e2143df34ea255f3c8 Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Wed, 27 May 2020 23:32:54 +0900 Subject: [PATCH 162/192] Update unit-tests.yml (#477) --- .github/workflows/unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index bbcb866c31..efe5c435ab 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - runs-on: [ubuntu-latest, macOS-latest, windows-latest] + runs-on: [ubuntu-latest, macos-latest, windows-latest] fail-fast: false runs-on: ${{ matrix.runs-on }} From c94bc40d8486c9edc6b583ea4bebd7d5cff8495d Mon Sep 17 00:00:00 2001 From: Sora Morimoto Date: Wed, 27 May 2020 23:33:23 +0900 Subject: [PATCH 163/192] Remove unnecessary trailing spaces (#474) --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 549b566c45..d671a244cf 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ The GitHub Actions ToolKit provides a set of packages to make creating actions e ## Packages -:heavy_check_mark: [@actions/core](packages/core) +:heavy_check_mark: [@actions/core](packages/core) Provides functions for inputs, outputs, results, logging, secrets and variables. Read more [here](packages/core) @@ -28,7 +28,7 @@ $ npm install @actions/core ```
-:runner: [@actions/exec](packages/exec) +:runner: [@actions/exec](packages/exec) Provides functions to exec cli tools and process output. Read more [here](packages/exec) @@ -46,7 +46,7 @@ $ npm install @actions/glob ```
-:pencil2: [@actions/io](packages/io) +:pencil2: [@actions/io](packages/io) Provides disk i/o functions like cp, mv, rmRF, find etc. Read more [here](packages/io) @@ -55,18 +55,18 @@ $ npm install @actions/io ```
-:hammer: [@actions/tool-cache](packages/tool-cache) +:hammer: [@actions/tool-cache](packages/tool-cache) Provides functions for downloading and caching tools. e.g. setup-* actions. Read more [here](packages/tool-cache) -See @actions/cache for caching workflow dependencies. +See @actions/cache for caching workflow dependencies. ```bash $ npm install @actions/tool-cache ```
-:octocat: [@actions/github](packages/github) +:octocat: [@actions/github](packages/github) Provides an Octokit client hydrated with the context that the current action is being run in. Read more [here](packages/github) @@ -75,7 +75,7 @@ $ npm install @actions/github ```
-:floppy_disk: [@actions/artifact](packages/artifact) +:floppy_disk: [@actions/artifact](packages/artifact) Provides functions to interact with actions artifacts. Read more [here](packages/artifact) @@ -84,7 +84,7 @@ $ npm install @actions/artifact ```
-:dart: [@actions/cache](packages/cache) +:dart: [@actions/cache](packages/cache) Provides functions to cache dependencies and build outputs to improve workflow execution time. Read more [here](packages/cache) @@ -115,7 +115,7 @@ Problem Matchers are a way to scan the output of actions for a specified regex p :warning: [Proxy Server Support](docs/proxy-support.md) -Self-hosted runners can be configured to run behind proxy servers. +Self-hosted runners can be configured to run behind proxy servers.

@@ -132,23 +132,23 @@ Illustrates how to create a simple hello world javascript action.

JavaScript Action Walkthrough

- + Walkthrough and template for creating a JavaScript Action with tests, linting, workflow, publishing, and versioning. ```javascript async function run() { - try { + try { const ms = core.getInput('milliseconds'); console.log(`Waiting ${ms} milliseconds ...`) ... ``` ```javascript PASS ./index.test.js - ✓ throws invalid number - ✓ wait 500 ms + ✓ throws invalid number + ✓ wait 500 ms ✓ test runs -Test Suites: 1 passed, 1 total +Test Suites: 1 passed, 1 total Tests: 3 passed, 3 total ```
@@ -168,11 +168,11 @@ async function run() { ``` ```javascript PASS ./index.test.js - ✓ throws invalid number - ✓ wait 500 ms + ✓ throws invalid number + ✓ wait 500 ms ✓ test runs -Test Suites: 1 passed, 1 total +Test Suites: 1 passed, 1 total Tests: 3 passed, 3 total ```
@@ -205,7 +205,7 @@ const myInput = core.getInput('myInput'); core.debug(`Hello ${myInput} from inside a container`); const context = github.context; -console.log(`We can even get context data, like the repo: ${context.repo.repo}`) +console.log(`We can even get context data, like the repo: ${context.repo.repo}`) ```
From 9ba7c679ad74f6a80017feeb94b7a7c19b590796 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Wed, 27 May 2020 10:40:32 -0400 Subject: [PATCH 164/192] Update Save State Docs (#467) * Update Save State Docs --- docs/commands.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/commands.md b/docs/commands.md index bc91edc7e9..0edf2f0767 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -114,12 +114,14 @@ echo "::remove-matcher owner=eslint-compact::" `remove-matcher` removes a Problem Matcher by owner ### Save State -Save state to be used in the corresponding wrapper (finally) post job entry point. +Save a state to an environmental variable that can later be used in the main or post action. ```bash echo "::save-state name=FOO::foovalue" ``` +An environmental variable named `STATE_FOO` will be available to use in the post or main action. See [Sending Values to the pre and post actions](https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#sending-values-to-the-pre-and-post-actions) for more information. + ### Log Level There are several commands to emit different levels of log output: From 4a89cf72de68713d3a465254e90537d8ea5b421e Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Tue, 2 Jun 2020 21:39:46 -0400 Subject: [PATCH 165/192] @actions/github v3 using Octokit/core (#453) * Rebuild to use @Octokit/Core --- packages/github/README.md | 31 ++- .../github/__tests__/github.proxy.test.ts | 109 ++++++++++ packages/github/__tests__/github.test.ts | 55 ++--- packages/github/package-lock.json | 193 +++++++----------- packages/github/package.json | 7 +- packages/github/src/github.ts | 166 ++------------- packages/github/src/internal/utils.ts | 25 +++ packages/github/src/utils.ts | 44 ++++ 8 files changed, 311 insertions(+), 319 deletions(-) create mode 100644 packages/github/__tests__/github.proxy.test.ts create mode 100644 packages/github/src/internal/utils.ts create mode 100644 packages/github/src/utils.ts diff --git a/packages/github/README.md b/packages/github/README.md index ce606c1945..eb034088d5 100644 --- a/packages/github/README.md +++ b/packages/github/README.md @@ -4,7 +4,7 @@ ## Usage -Returns an authenticated Octokit client that follows the machine [proxy settings](https://help.github.com/en/actions/hosting-your-own-runners/using-a-proxy-server-with-self-hosted-runners). See https://octokit.github.io/rest.js for the API. +Returns an authenticated Octokit client that follows the machine [proxy settings](https://help.github.com/en/actions/hosting-your-own-runners/using-a-proxy-server-with-self-hosted-runners) and correctly sets GHES base urls. See https://octokit.github.io/rest.js for the API. ```js const github = require('@actions/github'); @@ -17,7 +17,10 @@ async function run() { // https://help.github.com/en/actions/automating-your-workflow-with-github-actions/authenticating-with-the-github_token#about-the-github_token-secret const myToken = core.getInput('myToken'); - const octokit = new github.GitHub(myToken); + const octokit = github.getOctokit(token) + + // You can also pass in additional options as a second parameter to getOctokit + // const octokit = github.getOctokit(myToken, {userAgent: "MyActionVersion1"}); const { data: pullRequest } = await octokit.pulls.get({ owner: 'octokit', @@ -34,8 +37,6 @@ async function run() { run(); ``` -You can pass client options, as specified by [Octokit](https://octokit.github.io/rest.js/), as a second argument to the `GitHub` constructor. - You can also make GraphQL requests. See https://github.com/octokit/graphql.js for the API. ```js @@ -72,3 +73,25 @@ if (github.context.eventName === 'push') { core.info(`The head commit is: ${pushPayload.head}`) } ``` + +## Extending the Octokit instance +`@octokit/core` now supports the [plugin architecture](https://github.com/octokit/core.js#plugins). You can extend the GitHub instance using plugins. + +For example, using the `@octokit/plugin-enterprise-server` you can now access enterprise admin apis on GHES instances. + +```ts +import { GitHub, getOctokitOptions } from '@actions/github/lib/utils' +import { enterpriseServer220Admin } from '@octokit/plugin-enterprise-server' + +const octokit = GitHub.plugin(enterpriseServer220Admin) +// or override some of the default values as well +// const octokit = GitHub.plugin(enterpriseServer220Admin).defaults({userAgent: "MyNewUserAgent"}) + +const myToken = core.getInput('myToken'); +const myOctokit = new octokit(getOctokitOptions(token)) +// Create a new user +myOctokit.enterpriseAdmin.createUser({ + login: "testuser", + email: "testuser@test.com", +}); +``` diff --git a/packages/github/__tests__/github.proxy.test.ts b/packages/github/__tests__/github.proxy.test.ts new file mode 100644 index 0000000000..5da06582cd --- /dev/null +++ b/packages/github/__tests__/github.proxy.test.ts @@ -0,0 +1,109 @@ +import * as http from 'http' +import * as https from 'https' +import proxy from 'proxy' + +// Default values are set when the module is imported, so we need to set proxy first. +const proxyUrl = 'http://127.0.0.1:8081' +const originalProxyUrl = process.env['https_proxy'] +process.env['https_proxy'] = proxyUrl +// eslint-disable-next-line import/first +import {getOctokit} from '../src/github' + +describe('@actions/github', () => { + let proxyConnects: string[] + let proxyServer: http.Server + let first = true + + beforeAll(async () => { + // Start proxy server + proxyServer = proxy() + await new Promise(resolve => { + const port = Number(proxyUrl.split(':')[2]) + proxyServer.listen(port, () => resolve()) + }) + proxyServer.on('connect', req => { + proxyConnects.push(req.url) + }) + }) + + beforeEach(() => { + proxyConnects = [] + }) + + afterAll(async () => { + // Stop proxy server + await new Promise(resolve => { + proxyServer.once('close', () => resolve()) + proxyServer.close() + }) + + if (originalProxyUrl) { + process.env['https_proxy'] = originalProxyUrl + } + }) + + it('basic REST client with proxy', async () => { + const token = getToken() + if (!token) { + return + } + + const octokit = getOctokit(token) + const branch = await octokit.repos.getBranch({ + owner: 'actions', + repo: 'toolkit', + branch: 'master' + }) + expect(branch.data.name).toBe('master') + expect(proxyConnects).toEqual(['api.github.com:443']) + }) + + it('basic GraphQL client with proxy', async () => { + const token = getToken() + if (!token) { + return + } + process.env['https_proxy'] = proxyUrl + const octokit = getOctokit(token) + + const repository = await octokit.graphql( + '{repository(owner:"actions", name:"toolkit"){name}}' + ) + expect(repository).toEqual({repository: {name: 'toolkit'}}) + expect(proxyConnects).toEqual(['api.github.com:443']) + }) + + it('should only use default agent if one is not provided', async () => { + const token = getToken() + if (!token) { + return + } + + // Valid token + const octokit = getOctokit(token, { + request: { + agent: new https.Agent() + } + }) + const branch = await octokit.repos.getBranch({ + owner: 'actions', + repo: 'toolkit', + branch: 'master' + }) + expect(branch.data.name).toBe('master') + expect(proxyConnects).toHaveLength(0) + }) + + function getToken(): string { + const token = process.env['GITHUB_TOKEN'] || '' + if (!token && first) { + /* eslint-disable-next-line no-console */ + console.warn( + 'Skipping GitHub tests. Set $GITHUB_TOKEN to run REST client and GraphQL client tests' + ) + first = false + } + + return token + } +}) diff --git a/packages/github/__tests__/github.test.ts b/packages/github/__tests__/github.test.ts index bf81b07ace..4e02c7b316 100644 --- a/packages/github/__tests__/github.test.ts +++ b/packages/github/__tests__/github.test.ts @@ -1,6 +1,7 @@ import * as http from 'http' import proxy from 'proxy' -import {GitHub} from '../src/github' +import {getOctokit} from '../src/github' +import {GitHub, getOctokitOptions} from '../src/utils' describe('@actions/github', () => { const proxyUrl = 'http://127.0.0.1:8080' @@ -43,8 +44,22 @@ describe('@actions/github', () => { if (!token) { return } + const octokit = new GitHub(getOctokitOptions(token)) + const branch = await octokit.repos.getBranch({ + owner: 'actions', + repo: 'toolkit', + branch: 'master' + }) + expect(branch.data.name).toBe('master') + expect(proxyConnects).toHaveLength(0) + }) - const octokit = new GitHub(token) + it('basic getOctokit client', async () => { + const token = getToken() + if (!token) { + return + } + const octokit = getOctokit(token) const branch = await octokit.repos.getBranch({ owner: 'actions', repo: 'toolkit', @@ -85,30 +100,13 @@ describe('@actions/github', () => { expect(failed).toBeTruthy() }) - it('basic REST client with proxy', async () => { - const token = getToken() - if (!token) { - return - } - - process.env['https_proxy'] = proxyUrl - const octokit = new GitHub(token) - const branch = await octokit.repos.getBranch({ - owner: 'actions', - repo: 'toolkit', - branch: 'master' - }) - expect(branch.data.name).toBe('master') - expect(proxyConnects).toEqual(['api.github.com:443']) - }) - it('basic GraphQL client', async () => { const token = getToken() if (!token) { return } - const octokit = new GitHub(token) + const octokit = getOctokit(token) const repository = await octokit.graphql( '{repository(owner:"actions", name:"toolkit"){name}}' ) @@ -123,7 +121,7 @@ describe('@actions/github', () => { } // Valid token - let octokit = new GitHub(token) + let octokit = getOctokit(token) const repository = await octokit.graphql( '{repository(owner:"actions", name:"toolkit"){name}}' ) @@ -143,21 +141,6 @@ describe('@actions/github', () => { expect(failed).toBeTruthy() }) - it('basic GraphQL client with proxy', async () => { - const token = getToken() - if (!token) { - return - } - - process.env['https_proxy'] = proxyUrl - const octokit = new GitHub(token) - const repository = await octokit.graphql( - '{repository(owner:"actions", name:"toolkit"){name}}' - ) - expect(repository).toEqual({repository: {name: 'toolkit'}}) - expect(proxyConnects).toEqual(['api.github.com:443']) - }) - function getToken(): string { const token = process.env['GITHUB_TOKEN'] || '' if (!token && first) { diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index a075eac212..5d0ed1b3d6 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "2.2.0", + "version": "3.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -464,130 +464,105 @@ "@octokit/types": "^2.0.0" } }, - "@octokit/endpoint": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.1.tgz", - "integrity": "sha512-nBFhRUb5YzVTCX/iAK1MgQ4uWo89Gu0TH00qQHoYRCsE12dWcG1OiLd7v2EIo2+tpUKPMOQ62QFy9hy9Vg2ULg==", + "@octokit/core": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-2.5.2.tgz", + "integrity": "sha512-+hEKCEGvbS902uuSk/TyVnI7eWDEKVm1BIPnbKy8gsxjTYaybEU8EG73B3Ju5cMj2/OIZ0uHoQWD1nGWhWsj2g==", "requires": { + "@octokit/auth-token": "^2.4.0", + "@octokit/graphql": "^4.3.1", + "@octokit/request": "^5.4.0", "@octokit/types": "^2.0.0", + "before-after-hook": "^2.1.0", + "universal-user-agent": "^5.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.1.tgz", + "integrity": "sha512-pOPHaSz57SFT/m3R5P8MUu4wLPszokn5pXcB/pzavLTQf2jbU+6iayTvzaY6/BiotuRS0qyEUkx3QglT4U958A==", + "requires": { + "@octokit/types": "^2.11.1", "is-plain-object": "^3.0.0", - "universal-user-agent": "^4.0.0" - }, - "dependencies": { - "universal-user-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", - "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", - "requires": { - "os-name": "^3.1.0" - } - } + "universal-user-agent": "^5.0.0" } }, "@octokit/graphql": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.3.1.tgz", - "integrity": "sha512-hCdTjfvrK+ilU2keAdqNBWOk+gm1kai1ZcdjRfB30oA3/T6n53UVJb7w0L5cR3/rhU91xT3HSqCd+qbvH06yxA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.4.0.tgz", + "integrity": "sha512-Du3hAaSROQ8EatmYoSAJjzAz3t79t9Opj/WY1zUgxVUGfIKn0AEjg+hlOLscF6fv6i/4y/CeUvsWgIfwMkTccw==", "requires": { "@octokit/request": "^5.3.0", "@octokit/types": "^2.0.0", - "universal-user-agent": "^4.0.0" + "universal-user-agent": "^5.0.0" + } + }, + "@octokit/plugin-paginate-rest": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.2.0.tgz", + "integrity": "sha512-KoNxC3PLNar8UJwR+1VMQOw2IoOrrFdo5YOiDKnBhpVbKpw+zkBKNMNKwM44UWL25Vkn0Sl3nYIEGKY+gW5ebw==", + "requires": { + "@octokit/types": "^2.12.1" }, "dependencies": { - "universal-user-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", - "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", + "@octokit/types": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.12.2.tgz", + "integrity": "sha512-1GHLI/Jll3j6F0GbYyZPFTcHZMGjAiRfkTEoRUyaVVk2IWbDdwEiClAJvXzfXCDayuGSNCqAUH8lpjZtqW9GDw==", "requires": { - "os-name": "^3.1.0" + "@types/node": ">= 8" } } } }, - "@octokit/plugin-paginate-rest": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz", - "integrity": "sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q==", - "requires": { - "@octokit/types": "^2.0.1" - } - }, - "@octokit/plugin-request-log": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz", - "integrity": "sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw==" - }, "@octokit/plugin-rest-endpoint-methods": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz", - "integrity": "sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-3.10.0.tgz", + "integrity": "sha512-Z2DBsdnkWKuVBVFiLoEUKP/82ylH4Ij5F1Mss106hnQYXTxDfCWAyHW+hJ6ophuHVJ9Flaaue3fYn4CggzkHTg==", "requires": { - "@octokit/types": "^2.0.1", + "@octokit/types": "^2.14.0", "deprecation": "^2.3.1" + }, + "dependencies": { + "@octokit/types": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.14.0.tgz", + "integrity": "sha512-1w2wxpN45rEXPDFeB7rGain7wcJ/aTRg8bdILITVnS0O7a4zEGELa3JmIe+jeLdekQjvZRbVfNPqS+mi5fKCKQ==", + "requires": { + "@types/node": ">= 8" + } + } } }, "@octokit/request": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.1.tgz", - "integrity": "sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.2.tgz", + "integrity": "sha512-zKdnGuQ2TQ2vFk9VU8awFT4+EYf92Z/v3OlzRaSh4RIP0H6cvW1BFPXq4XYvNez+TPQjqN+0uSkCYnMFFhcFrw==", "requires": { - "@octokit/endpoint": "^5.5.0", - "@octokit/request-error": "^1.0.1", - "@octokit/types": "^2.0.0", + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.0.0", + "@octokit/types": "^2.11.1", "deprecation": "^2.0.0", "is-plain-object": "^3.0.0", "node-fetch": "^2.3.0", "once": "^1.4.0", - "universal-user-agent": "^4.0.0" - }, - "dependencies": { - "universal-user-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", - "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", - "requires": { - "os-name": "^3.1.0" - } - } + "universal-user-agent": "^5.0.0" } }, "@octokit/request-error": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.0.tgz", - "integrity": "sha512-DNBhROBYjjV/I9n7A8kVkmQNkqFAMem90dSxqvPq57e2hBr7mNTX98y3R2zDpqMQHVRpBDjsvsfIGgBzy+4PAg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.0.tgz", + "integrity": "sha512-rtYicB4Absc60rUv74Rjpzek84UbVHGHJRu4fNVlZ1mCcyUPPuzFfG9Rn6sjHrd95DEsmjSt1Axlc699ZlbDkw==", "requires": { "@octokit/types": "^2.0.0", "deprecation": "^2.0.0", "once": "^1.4.0" } }, - "@octokit/rest": { - "version": "16.43.1", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz", - "integrity": "sha512-gfFKwRT/wFxq5qlNjnW2dh+qh74XgTQ2B179UX5K1HYCluioWj8Ndbgqw2PVqa1NnVJkGHp2ovMpVn/DImlmkw==", - "requires": { - "@octokit/auth-token": "^2.4.0", - "@octokit/plugin-paginate-rest": "^1.1.1", - "@octokit/plugin-request-log": "^1.0.0", - "@octokit/plugin-rest-endpoint-methods": "2.4.0", - "@octokit/request": "^5.2.0", - "@octokit/request-error": "^1.0.2", - "atob-lite": "^2.0.0", - "before-after-hook": "^2.0.0", - "btoa-lite": "^1.0.0", - "deprecation": "^2.0.0", - "lodash.get": "^4.4.2", - "lodash.set": "^4.3.2", - "lodash.uniq": "^4.5.0", - "octokit-pagination-methods": "^1.1.0", - "once": "^1.4.0", - "universal-user-agent": "^4.0.0" - } - }, "@octokit/types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.0.2.tgz", - "integrity": "sha512-StASIL2lgT3TRjxv17z9pAqbnI7HGu9DrJlg3sEBFfCLaMEqp+O3IQPUF6EZtQ4xkAu2ml6kMBBCtGxjvmtmuQ==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", + "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", "requires": { "@types/node": ">= 8" } @@ -921,11 +896,6 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, - "atob-lite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", - "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=" - }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -1124,11 +1094,6 @@ "node-int64": "^0.4.0" } }, - "btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=" - }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -3031,27 +2996,12 @@ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", "dev": true }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, - "lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" - }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" - }, "lolex": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", @@ -3413,11 +3363,6 @@ } } }, - "octokit-pagination-methods": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz", - "integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==" - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4615,9 +4560,9 @@ } }, "universal-user-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz", - "integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-5.0.0.tgz", + "integrity": "sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q==", "requires": { "os-name": "^3.1.0" } @@ -4813,9 +4758,9 @@ "dev": true }, "windows-release": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", - "integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.0.tgz", + "integrity": "sha512-2HetyTg1Y+R+rUgrKeUEhAG/ZuOmTrI1NBb3ZyAGQMYmOJjBBPe4MTodghRkmLJZHwkuPi02anbeGP+Zf401LQ==", "requires": { "execa": "^1.0.0" } diff --git a/packages/github/package.json b/packages/github/package.json index ef58523ba6..1eab9a75c2 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "2.2.0", + "version": "3.0.0", "description": "Actions github lib", "keywords": [ "github", @@ -38,8 +38,9 @@ }, "dependencies": { "@actions/http-client": "^1.0.3", - "@octokit/graphql": "^4.3.1", - "@octokit/rest": "^16.43.1" + "@octokit/core": "^2.5.1", + "@octokit/plugin-paginate-rest": "^2.2.0", + "@octokit/plugin-rest-endpoint-methods": "^3.10.0" }, "devDependencies": { "jest": "^25.1.0", diff --git a/packages/github/src/github.ts b/packages/github/src/github.ts index 699998dc69..bada7e522d 100644 --- a/packages/github/src/github.ts +++ b/packages/github/src/github.ts @@ -1,158 +1,20 @@ -// Originally pulled from https://github.com/JasonEtco/actions-toolkit/blob/master/src/github.ts -import {graphql} from '@octokit/graphql' - -// we need this type to set up a property on the GitHub object -// that has token authorization -// (it is not exported from octokit by default) -import { - graphql as GraphQL, - RequestParameters as GraphQLRequestParameters -} from '@octokit/graphql/dist-types/types' - -import {Octokit} from '@octokit/rest' import * as Context from './context' -import * as http from 'http' -import * as httpClient from '@actions/http-client' +import {GitHub, getOctokitOptions} from './utils' -// We need this in order to extend Octokit -Octokit.prototype = new Octokit() +// octokit + plugins +import {OctokitOptions} from '@octokit/core/dist-types/types' export const context = new Context.Context() -export class GitHub extends Octokit { - graphql: GraphQL - - /* eslint-disable no-dupe-class-members */ - // Disable no-dupe-class-members due to false positive for method overload - // https://github.com/typescript-eslint/typescript-eslint/issues/291 - - /** - * Sets up the REST client and GraphQL client with auth and proxy support. - * The parameter `token` or `opts.auth` must be supplied. The GraphQL client - * authorization is not setup when `opts.auth` is a function or object. - * - * @param token Auth token - * @param opts Octokit options - */ - constructor(token: string, opts?: Omit) - constructor(opts: Octokit.Options) - constructor(token: string | Octokit.Options, opts?: Octokit.Options) { - super(GitHub.getOctokitOptions(GitHub.disambiguate(token, opts))) - - this.graphql = GitHub.getGraphQL(GitHub.disambiguate(token, opts)) - } - - /** - * Disambiguates the constructor overload parameters - */ - private static disambiguate( - token: string | Octokit.Options, - opts?: Octokit.Options - ): [string, Octokit.Options] { - return [ - typeof token === 'string' ? token : '', - typeof token === 'object' ? token : opts || {} - ] - } - - private static getOctokitOptions( - args: [string, Octokit.Options] - ): Octokit.Options { - const token = args[0] - const options = {...args[1]} // Shallow clone - don't mutate the object provided by the caller - - // Base URL - GHES or Dotcom - options.baseUrl = options.baseUrl || this.getApiBaseUrl() - - // Auth - const auth = GitHub.getAuthString(token, options) - if (auth) { - options.auth = auth - } - - // Proxy - const agent = GitHub.getProxyAgent(options.baseUrl, options) - if (agent) { - // Shallow clone - don't mutate the object provided by the caller - options.request = options.request ? {...options.request} : {} - - // Set the agent - options.request.agent = agent - } - - return options - } - - private static getGraphQL(args: [string, Octokit.Options]): GraphQL { - const defaults: GraphQLRequestParameters = {} - defaults.baseUrl = this.getGraphQLBaseUrl() - const token = args[0] - const options = args[1] - - // Authorization - const auth = this.getAuthString(token, options) - if (auth) { - defaults.headers = { - authorization: auth - } - } - - // Proxy - const agent = GitHub.getProxyAgent(defaults.baseUrl, options) - if (agent) { - defaults.request = {agent} - } - - return graphql.defaults(defaults) - } - - private static getAuthString( - token: string, - options: Octokit.Options - ): string | undefined { - // Validate args - if (!token && !options.auth) { - throw new Error('Parameter token or opts.auth is required') - } else if (token && options.auth) { - throw new Error( - 'Parameters token and opts.auth may not both be specified' - ) - } - - return typeof options.auth === 'string' ? options.auth : `token ${token}` - } - - private static getProxyAgent( - destinationUrl: string, - options: Octokit.Options - ): http.Agent | undefined { - if (!options.request?.agent) { - if (httpClient.getProxyUrl(destinationUrl)) { - const hc = new httpClient.HttpClient() - return hc.getAgent(destinationUrl) - } - } - - return undefined - } - - private static getApiBaseUrl(): string { - return process.env['GITHUB_API_URL'] || 'https://api.github.com' - } - - private static getGraphQLBaseUrl(): string { - let url = - process.env['GITHUB_GRAPHQL_URL'] || 'https://api.github.com/graphql' - - // Shouldn't be a trailing slash, but remove if so - if (url.endsWith('/')) { - url = url.substr(0, url.length - 1) - } - - // Remove trailing "/graphql" - if (url.toUpperCase().endsWith('/GRAPHQL')) { - url = url.substr(0, url.length - '/graphql'.length) - } - return url - } +/** + * Returns a hydrated octokit ready to use for GitHub Actions + * + * @param token the repo PAT or GITHUB_TOKEN + * @param options other options to set + */ +export function getOctokit( + token: string, + options?: OctokitOptions +): InstanceType { + return new GitHub(getOctokitOptions(token, options)) } diff --git a/packages/github/src/internal/utils.ts b/packages/github/src/internal/utils.ts new file mode 100644 index 0000000000..ea5ec382fe --- /dev/null +++ b/packages/github/src/internal/utils.ts @@ -0,0 +1,25 @@ +import * as http from 'http' +import * as httpClient from '@actions/http-client' +import {OctokitOptions} from '@octokit/core/dist-types/types' + +export function getAuthString( + token: string, + options: OctokitOptions +): string | undefined { + if (!token && !options.auth) { + throw new Error('Parameter token or opts.auth is required') + } else if (token && options.auth) { + throw new Error('Parameters token and opts.auth may not both be specified') + } + + return typeof options.auth === 'string' ? options.auth : `token ${token}` +} + +export function getProxyAgent(destinationUrl: string): http.Agent { + const hc = new httpClient.HttpClient() + return hc.getAgent(destinationUrl) +} + +export function getApiBaseUrl(): string { + return process.env['GITHUB_API_URL'] || 'https://api.github.com' +} diff --git a/packages/github/src/utils.ts b/packages/github/src/utils.ts new file mode 100644 index 0000000000..bbc71c10fe --- /dev/null +++ b/packages/github/src/utils.ts @@ -0,0 +1,44 @@ +import * as Context from './context' +import * as Utils from './internal/utils' + +// octokit + plugins +import {Octokit} from '@octokit/core' +import {OctokitOptions} from '@octokit/core/dist-types/types' +import {restEndpointMethods} from '@octokit/plugin-rest-endpoint-methods' +import {paginateRest} from '@octokit/plugin-paginate-rest' + +export const context = new Context.Context() + +const baseUrl = Utils.getApiBaseUrl() +const defaults = { + baseUrl, + request: { + agent: Utils.getProxyAgent(baseUrl) + } +} + +export const GitHub = Octokit.plugin( + restEndpointMethods, + paginateRest +).defaults(defaults) + +/** + * Convience function to correctly format Octokit Options to pass into the constructor. + * + * @param token the repo PAT or GITHUB_TOKEN + * @param options other options to set + */ +export function getOctokitOptions( + token: string, + options?: OctokitOptions +): OctokitOptions { + const opts = Object.assign({}, options || {}) // Shallow clone - don't mutate the object provided by the caller + + // Auth + const auth = Utils.getAuthString(token, opts) + if (auth) { + opts.auth = auth + } + + return opts +} From d69e699ab956dac3d3d25bb3c3feda23c1cb3a6c Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Wed, 3 Jun 2020 10:12:22 -0400 Subject: [PATCH 166/192] Update release information for @actions/github 3.0.0 (#489) --- packages/github/RELEASES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/github/RELEASES.md b/packages/github/RELEASES.md index 00db5e66f1..b523ba3a20 100644 --- a/packages/github/RELEASES.md +++ b/packages/github/RELEASES.md @@ -1,5 +1,9 @@ # @actions/github Releases +### 3.0.0 +- [Swap to @octokit/core and use plugins to leverage lastest octokit apis](https://github.com/actions/toolkit/pull/453) +- [Add comment field to payload context](https://github.com/actions/toolkit/pull/375) + ### 2.2.0 - [Support GHES: Use GITHUB_API_URL and GITHUB_GRAPHQL_URL to determine baseUrl](https://github.com/actions/toolkit/pull/449) From d4340966b7e9262da886a97ce8e959522e55c576 Mon Sep 17 00:00:00 2001 From: Ryo Ota Date: Thu, 4 Jun 2020 00:54:01 +0900 Subject: [PATCH 167/192] Add type of context.payload.comment (#375) --- packages/github/src/interfaces.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/github/src/interfaces.ts b/packages/github/src/interfaces.ts index c20aadd827..8ab08bfeb0 100644 --- a/packages/github/src/interfaces.ts +++ b/packages/github/src/interfaces.ts @@ -36,4 +36,8 @@ export interface WebhookPayload { id: number [key: string]: any } + comment?: { + id: number + [key: string]: any + } } From c65fe87e339d3dd203274c62d0f36f405d78e8a0 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Wed, 3 Jun 2020 13:51:19 -0400 Subject: [PATCH 168/192] Minor readme clarification --- packages/github/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/github/README.md b/packages/github/README.md index eb034088d5..02e9be077d 100644 --- a/packages/github/README.md +++ b/packages/github/README.md @@ -17,7 +17,7 @@ async function run() { // https://help.github.com/en/actions/automating-your-workflow-with-github-actions/authenticating-with-the-github_token#about-the-github_token-secret const myToken = core.getInput('myToken'); - const octokit = github.getOctokit(token) + const octokit = github.getOctokit(myToken) // You can also pass in additional options as a second parameter to getOctokit // const octokit = github.getOctokit(myToken, {userAgent: "MyActionVersion1"}); From 3e40dd39cc56303a2451f5b175068dbefdc11c18 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Thu, 4 Jun 2020 15:14:19 -0400 Subject: [PATCH 169/192] Explicitly exclude DS store files (#492) --- packages/artifact/package.json | 3 ++- packages/cache/package.json | 3 ++- packages/core/package.json | 3 ++- packages/exec/package.json | 3 ++- packages/github/package.json | 3 ++- packages/glob/package.json | 3 ++- 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/artifact/package.json b/packages/artifact/package.json index 12fed5a023..0c7baf925b 100644 --- a/packages/artifact/package.json +++ b/packages/artifact/package.json @@ -17,7 +17,8 @@ "test": "__tests__" }, "files": [ - "lib" + "lib", + "!.DS_Store" ], "publishConfig": { "access": "public" diff --git a/packages/cache/package.json b/packages/cache/package.json index c9c9eba1ba..6e0a31818d 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -17,7 +17,8 @@ "test": "__tests__" }, "files": [ - "lib" + "lib", + "!.DS_Store" ], "publishConfig": { "access": "public" diff --git a/packages/core/package.json b/packages/core/package.json index 22f3a7ac3c..79e6df0bb5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -16,7 +16,8 @@ "test": "__tests__" }, "files": [ - "lib" + "lib", + "!.DS_Store" ], "publishConfig": { "access": "public" diff --git a/packages/exec/package.json b/packages/exec/package.json index 09e438e427..a16c14c361 100644 --- a/packages/exec/package.json +++ b/packages/exec/package.json @@ -16,7 +16,8 @@ "test": "__tests__" }, "files": [ - "lib" + "lib", + "!.DS_Store" ], "publishConfig": { "access": "public" diff --git a/packages/github/package.json b/packages/github/package.json index 1eab9a75c2..4d72fee609 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -15,7 +15,8 @@ "test": "__tests__" }, "files": [ - "lib" + "lib", + "!.DS_Store" ], "publishConfig": { "access": "public" diff --git a/packages/glob/package.json b/packages/glob/package.json index 7b4c780064..58eff04db7 100644 --- a/packages/glob/package.json +++ b/packages/glob/package.json @@ -17,7 +17,8 @@ "test": "__tests__" }, "files": [ - "lib" + "lib", + "!.DS_Store" ], "publishConfig": { "access": "public" From 8e14ff9f0aabd6e860974c56b3aab9ef8a06957c Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Fri, 19 Jun 2020 14:35:19 -0400 Subject: [PATCH 170/192] Setup Weekly Automation to Update @actions/github (#498) * create automation to update Octokit for actions toolkit Co-authored-by: Dependency Update Bot Co-authored-by: Shohei Ueda <30958501+peaceiris@users.noreply.github.com> Co-authored-by: github-actions[bot] --- .github/workflows/update-github.yaml | 46 ++++++++++++ packages/github/package-lock.json | 102 +++++++++++---------------- packages/github/package.json | 8 +-- 3 files changed, 91 insertions(+), 65 deletions(-) create mode 100644 .github/workflows/update-github.yaml diff --git a/.github/workflows/update-github.yaml b/.github/workflows/update-github.yaml new file mode 100644 index 0000000000..b68eaf31fb --- /dev/null +++ b/.github/workflows/update-github.yaml @@ -0,0 +1,46 @@ +name: "UpdateOctokit" + +on: + schedule: + - cron: '0 18 * * 0' # sunday at 18 UTC + +jobs: + UpdateOctokit: + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'actions' }} + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: Update Octokit + working-directory: packages/github + run: | + npx npm-check-updates -u --dep prod + npm install + - name: Check Status + id: status + working-directory: packages/github + run: | + if [[ "$(git status --porcelain)" != "" ]]; then + echo "::set-output name=createPR::true" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git checkout -b bots/updateGitHubDependencies-${{github.run_number}} + git add . + git commit -m "Update Dependencies" + git push --set-upstream origin bots/updateGitHubDependencies-${{github.run_number}} + fi + - name: Create PR + if: ${{steps.status.outputs.createPR}} + uses: actions/github-script@v2 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + github.pulls.create( + { + base: "master", + owner: "${{github.repository_owner}}", + repo: "toolkit", + title: "Update Octokit dependencies", + body: "Update Octokit dependencies", + head: "bots/updateGitHubDependencies-${{github.run_number}}" + }) diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index 5d0ed1b3d6..d2281bcde1 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -457,91 +457,71 @@ } }, "@octokit/auth-token": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.0.tgz", - "integrity": "sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.2.tgz", + "integrity": "sha512-jE/lE/IKIz2v1+/P0u4fJqv0kYwXOTujKemJMFr6FeopsxlIK3+wKDCJGnysg81XID5TgZQbIfuJ5J0lnTiuyQ==", "requires": { - "@octokit/types": "^2.0.0" + "@octokit/types": "^5.0.0" } }, "@octokit/core": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-2.5.2.tgz", - "integrity": "sha512-+hEKCEGvbS902uuSk/TyVnI7eWDEKVm1BIPnbKy8gsxjTYaybEU8EG73B3Ju5cMj2/OIZ0uHoQWD1nGWhWsj2g==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.0.0.tgz", + "integrity": "sha512-FGUUqZbIwl5UPvuUTWq8ly2B12gJGWjYh1DviBzZLXp5LzHUgyzL+NDGsXeE4vszXoGsD/JfpZS+kjkLiD2T9w==", "requires": { "@octokit/auth-token": "^2.4.0", "@octokit/graphql": "^4.3.1", "@octokit/request": "^5.4.0", - "@octokit/types": "^2.0.0", + "@octokit/types": "^5.0.0", "before-after-hook": "^2.1.0", "universal-user-agent": "^5.0.0" } }, "@octokit/endpoint": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.1.tgz", - "integrity": "sha512-pOPHaSz57SFT/m3R5P8MUu4wLPszokn5pXcB/pzavLTQf2jbU+6iayTvzaY6/BiotuRS0qyEUkx3QglT4U958A==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.3.tgz", + "integrity": "sha512-Y900+r0gIz+cWp6ytnkibbD95ucEzDSKzlEnaWS52hbCDNcCJYO5mRmWW7HRAnDc7am+N/5Lnd8MppSaTYx1Yg==", "requires": { - "@octokit/types": "^2.11.1", + "@octokit/types": "^5.0.0", "is-plain-object": "^3.0.0", "universal-user-agent": "^5.0.0" } }, "@octokit/graphql": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.4.0.tgz", - "integrity": "sha512-Du3hAaSROQ8EatmYoSAJjzAz3t79t9Opj/WY1zUgxVUGfIKn0AEjg+hlOLscF6fv6i/4y/CeUvsWgIfwMkTccw==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.1.tgz", + "integrity": "sha512-qgMsROG9K2KxDs12CO3bySJaYoUu2aic90qpFrv7A8sEBzZ7UFGvdgPKiLw5gOPYEYbS0Xf8Tvf84tJutHPulQ==", "requires": { "@octokit/request": "^5.3.0", - "@octokit/types": "^2.0.0", + "@octokit/types": "^5.0.0", "universal-user-agent": "^5.0.0" } }, "@octokit/plugin-paginate-rest": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.2.0.tgz", - "integrity": "sha512-KoNxC3PLNar8UJwR+1VMQOw2IoOrrFdo5YOiDKnBhpVbKpw+zkBKNMNKwM44UWL25Vkn0Sl3nYIEGKY+gW5ebw==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.2.3.tgz", + "integrity": "sha512-eKTs91wXnJH8Yicwa30jz6DF50kAh7vkcqCQ9D7/tvBAP5KKkg6I2nNof8Mp/65G0Arjsb4QcOJcIEQY+rK1Rg==", "requires": { - "@octokit/types": "^2.12.1" - }, - "dependencies": { - "@octokit/types": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.12.2.tgz", - "integrity": "sha512-1GHLI/Jll3j6F0GbYyZPFTcHZMGjAiRfkTEoRUyaVVk2IWbDdwEiClAJvXzfXCDayuGSNCqAUH8lpjZtqW9GDw==", - "requires": { - "@types/node": ">= 8" - } - } + "@octokit/types": "^5.0.0" } }, "@octokit/plugin-rest-endpoint-methods": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-3.10.0.tgz", - "integrity": "sha512-Z2DBsdnkWKuVBVFiLoEUKP/82ylH4Ij5F1Mss106hnQYXTxDfCWAyHW+hJ6ophuHVJ9Flaaue3fYn4CggzkHTg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.0.0.tgz", + "integrity": "sha512-emS6gysz4E9BNi9IrCl7Pm4kR+Az3MmVB0/DoDCmF4U48NbYG3weKyDlgkrz6Jbl4Mu4nDx8YWZwC4HjoTdcCA==", "requires": { - "@octokit/types": "^2.14.0", + "@octokit/types": "^5.0.0", "deprecation": "^2.3.1" - }, - "dependencies": { - "@octokit/types": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.14.0.tgz", - "integrity": "sha512-1w2wxpN45rEXPDFeB7rGain7wcJ/aTRg8bdILITVnS0O7a4zEGELa3JmIe+jeLdekQjvZRbVfNPqS+mi5fKCKQ==", - "requires": { - "@types/node": ">= 8" - } - } } }, "@octokit/request": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.2.tgz", - "integrity": "sha512-zKdnGuQ2TQ2vFk9VU8awFT4+EYf92Z/v3OlzRaSh4RIP0H6cvW1BFPXq4XYvNez+TPQjqN+0uSkCYnMFFhcFrw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.5.tgz", + "integrity": "sha512-atAs5GAGbZedvJXXdjtKljin+e2SltEs48B3naJjqWupYl2IUBbB/CJisyjbNHcKpHzb3E+OYEZ46G8eakXgQg==", "requires": { "@octokit/endpoint": "^6.0.1", "@octokit/request-error": "^2.0.0", - "@octokit/types": "^2.11.1", + "@octokit/types": "^5.0.0", "deprecation": "^2.0.0", "is-plain-object": "^3.0.0", "node-fetch": "^2.3.0", @@ -550,19 +530,19 @@ } }, "@octokit/request-error": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.0.tgz", - "integrity": "sha512-rtYicB4Absc60rUv74Rjpzek84UbVHGHJRu4fNVlZ1mCcyUPPuzFfG9Rn6sjHrd95DEsmjSt1Axlc699ZlbDkw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.2.tgz", + "integrity": "sha512-2BrmnvVSV1MXQvEkrb9zwzP0wXFNbPJij922kYBTLIlIafukrGOb+ABBT2+c6wZiuyWDH1K1zmjGQ0toN/wMWw==", "requires": { - "@octokit/types": "^2.0.0", + "@octokit/types": "^5.0.1", "deprecation": "^2.0.0", "once": "^1.4.0" } }, "@octokit/types": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", - "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.0.1.tgz", + "integrity": "sha512-GorvORVwp244fGKEt3cgt/P+M0MGy4xEDbckw+K5ojEezxyMDgCaYPKVct+/eWQfZXOT7uq0xRpmrl/+hliabA==", "requires": { "@types/node": ">= 8" } @@ -649,9 +629,9 @@ } }, "@types/node": { - "version": "12.12.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.11.tgz", - "integrity": "sha512-O+x6uIpa6oMNTkPuHDa9MhMMehlxLAd5QcOvKRjAFsBVpeFWTOPnXbDvILvFgFFZfQ1xh1EZi1FbXxUix+zpsQ==" + "version": "14.0.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.13.tgz", + "integrity": "sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA==" }, "@types/stack-utils": { "version": "1.0.1", @@ -4758,9 +4738,9 @@ "dev": true }, "windows-release": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.0.tgz", - "integrity": "sha512-2HetyTg1Y+R+rUgrKeUEhAG/ZuOmTrI1NBb3ZyAGQMYmOJjBBPe4MTodghRkmLJZHwkuPi02anbeGP+Zf401LQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.1.tgz", + "integrity": "sha512-Pngk/RDCaI/DkuHPlGTdIkDiTAnAkyMjoQMZqRsxydNl1qGXNIoZrB7RK8g53F2tEgQBMqQJHQdYZuQEEAu54A==", "requires": { "execa": "^1.0.0" } diff --git a/packages/github/package.json b/packages/github/package.json index 4d72fee609..4cb518c93c 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -38,10 +38,10 @@ "url": "https://github.com/actions/toolkit/issues" }, "dependencies": { - "@actions/http-client": "^1.0.3", - "@octokit/core": "^2.5.1", - "@octokit/plugin-paginate-rest": "^2.2.0", - "@octokit/plugin-rest-endpoint-methods": "^3.10.0" + "@actions/http-client": "^1.0.8", + "@octokit/core": "^3.0.0", + "@octokit/plugin-paginate-rest": "^2.2.3", + "@octokit/plugin-rest-endpoint-methods": "^4.0.0" }, "devDependencies": { "jest": "^25.1.0", From 66931ff48175c77be06cbe8be37cccda14cdd8b3 Mon Sep 17 00:00:00 2001 From: Aiqiao Yan <55104035+aiqiaoy@users.noreply.github.com> Date: Fri, 19 Jun 2020 14:43:10 -0400 Subject: [PATCH 171/192] Update cache readme to include a link to cache action (#478) Co-authored-by: Aiqiao Yan --- packages/cache/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/cache/README.md b/packages/cache/README.md index de0893d057..2d5aa1a95d 100644 --- a/packages/cache/README.md +++ b/packages/cache/README.md @@ -8,6 +8,8 @@ Note that GitHub will remove any cache entries that have not been accessed in ov ## Usage +This package is used by the v2+ versions of our first party cache action. You can find an example implementation in the cache repo [here](https://github.com/actions/cache). + #### Restore Cache Restores a cache based on `key` and `restoreKeys` to the `paths` provided. Function returns the cache key for cache hit and returns undefined if cache not found. @@ -38,4 +40,5 @@ const paths = [ ] const key = 'npm-foobar-d5ea0750' const cacheId = await cache.saveCache(paths, key) -``` \ No newline at end of file +``` + From 9e2d61e548a6edd163b05e31b69a8639417cdcac Mon Sep 17 00:00:00 2001 From: David Hadka Date: Fri, 19 Jun 2020 13:43:38 -0500 Subject: [PATCH 172/192] Delete cache folders prior to restore in tests (#486) * Delete cache folders prior to restore in tests * Update cache-tests.yml --- .github/workflows/cache-tests.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cache-tests.yml b/.github/workflows/cache-tests.yml index 10120ddc46..1ac811ea27 100644 --- a/.github/workflows/cache-tests.yml +++ b/.github/workflows/cache-tests.yml @@ -58,6 +58,12 @@ jobs: run: | node -e "Promise.resolve(require('./packages/cache/lib/cache').saveCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}'))" + - name: Delete cache folders prior to restore + shell: bash + run: | + rm -rf test-cache + rm -rf ~/test-cache + - name: Restore cache using restoreCache() run: | node -e "Promise.resolve(require('./packages/cache/lib/cache').restoreCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}'))" @@ -66,4 +72,4 @@ jobs: shell: bash run: | packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} test-cache - packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} ~/test-cache \ No newline at end of file + packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} ~/test-cache From 7a1a0dd6fcb0fd35ec32d445689039886ad1a252 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Wed, 24 Jun 2020 10:48:13 -0400 Subject: [PATCH 173/192] Add core.info to Logging section --- packages/core/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/README.md b/packages/core/README.md index 5ad27eedee..95428cf33d 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -89,6 +89,7 @@ try { } // Do stuff + core.info('Output to the actions build log') } catch (err) { core.error(`Error ${err}, action may still succeed though`); @@ -143,4 +144,4 @@ const core = require('@actions/core'); var pid = core.getState("pidToKill"); process.kill(pid); -``` \ No newline at end of file +``` From 2d47f7b7f647962eaf6292ba53aded7d7dda3b8a Mon Sep 17 00:00:00 2001 From: 839 <8398a7@gmail.com> Date: Fri, 26 Jun 2020 12:25:13 +0900 Subject: [PATCH 174/192] Add execution state information (#499) * Add execution state information (#371) * Type conformance to REST API (#371) * Changed to get the job name (#371) --- packages/github/src/context.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/github/src/context.ts b/packages/github/src/context.ts index 21f14c9c09..52efabb068 100644 --- a/packages/github/src/context.ts +++ b/packages/github/src/context.ts @@ -15,6 +15,9 @@ export class Context { workflow: string action: string actor: string + job: string + runNumber: number + runId: number /** * Hydrate the context from the environment @@ -37,6 +40,9 @@ export class Context { this.workflow = process.env.GITHUB_WORKFLOW as string this.action = process.env.GITHUB_ACTION as string this.actor = process.env.GITHUB_ACTOR as string + this.job = process.env.GITHUB_JOB as string + this.runNumber = parseInt(process.env.GITHUB_RUN_NUMBER as string, 10) + this.runId = parseInt(process.env.GITHUB_RUN_ID as string, 10) } get issue(): {owner: string; repo: string; number: number} { From ea503a235e2ad9ef6aada2f72d62cbed17d31c76 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Fri, 26 Jun 2020 13:42:57 -0400 Subject: [PATCH 175/192] Add release notes for `actions/github` 4.0.0 (#507) * Add release notes for 4.0.0 --- packages/github/RELEASES.md | 6 ++++++ packages/github/package-lock.json | 2 +- packages/github/package.json | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/github/RELEASES.md b/packages/github/RELEASES.md index b523ba3a20..11c47bd23b 100644 --- a/packages/github/RELEASES.md +++ b/packages/github/RELEASES.md @@ -1,5 +1,11 @@ # @actions/github Releases +### 4.0.0 +- [Add execution state information to context](https://github.com/actions/toolkit/pull/499) +- [Update Octokit Dependencies with some api breaking changes](https://github.com/actions/toolkit/pull/498) + - The full list of api changes are [here](https://github.com/octokit/plugin-rest-endpoint-methods.js/releases/tag/v4.0.0) + - `GitHub.plugin()` no longer supports an array as first argument. Multiple args must be passed in instead. + ### 3.0.0 - [Swap to @octokit/core and use plugins to leverage lastest octokit apis](https://github.com/actions/toolkit/pull/453) - [Add comment field to payload context](https://github.com/actions/toolkit/pull/375) diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index d2281bcde1..ce9e95448c 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "3.0.0", + "version": "4.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/github/package.json b/packages/github/package.json index 4cb518c93c..0288f178d8 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -1,6 +1,6 @@ { "name": "@actions/github", - "version": "3.0.0", + "version": "4.0.0", "description": "Actions github lib", "keywords": [ "github", From cee7d92d1d02e3107c9b1387b9690b89096b67be Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2020 16:36:08 -0400 Subject: [PATCH 176/192] Update Dependencies (#509) Co-authored-by: github-actions[bot] --- packages/github/package-lock.json | 26 +++++++++----------------- packages/github/package.json | 2 +- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index ce9e95448c..725cabd903 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -465,9 +465,9 @@ } }, "@octokit/core": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.0.0.tgz", - "integrity": "sha512-FGUUqZbIwl5UPvuUTWq8ly2B12gJGWjYh1DviBzZLXp5LzHUgyzL+NDGsXeE4vszXoGsD/JfpZS+kjkLiD2T9w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.1.0.tgz", + "integrity": "sha512-yPyQSmxIXLieEIRikk2w8AEtWkFdfG/LXcw1KvEtK3iP0ENZLW/WYQmdzOKqfSaLhooz4CJ9D+WY79C8ZliACw==", "requires": { "@octokit/auth-token": "^2.4.0", "@octokit/graphql": "^4.3.1", @@ -2200,12 +2200,9 @@ "dev": true }, "is-plain-object": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", - "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", - "requires": { - "isobject": "^4.0.0" - } + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", + "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==" }, "is-regex": { "version": "1.0.5", @@ -2260,11 +2257,6 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==" - }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -2992,9 +2984,9 @@ } }, "macos-release": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", - "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.4.0.tgz", + "integrity": "sha512-ko6deozZYiAkqa/0gmcsz+p4jSy3gY7/ZsCEokPaYd8k+6/aXGkiTgr61+Owup7Sf+xjqW8u2ElhoM9SEcEfuA==" }, "make-dir": { "version": "3.0.2", diff --git a/packages/github/package.json b/packages/github/package.json index 0288f178d8..60283670de 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -39,7 +39,7 @@ }, "dependencies": { "@actions/http-client": "^1.0.8", - "@octokit/core": "^3.0.0", + "@octokit/core": "^3.1.0", "@octokit/plugin-paginate-rest": "^2.2.3", "@octokit/plugin-rest-endpoint-methods": "^4.0.0" }, From 4964b0cc7c5aaa2557b79cfe4bdb42dd657f6df0 Mon Sep 17 00:00:00 2001 From: David Hadka Date: Fri, 10 Jul 2020 10:09:32 -0500 Subject: [PATCH 177/192] Use Azure storage SDK to download cache (#497) * Adds option to download using AzCopy * Bump version number and add release notes * Ensure we use at least v10 * Negate env var so it disables AzCopy * Use Azure storage SDK to download cache * Use same level of parallelism as AzCopy * Fix naming of variable * React to feedback * Bump Node types to Node 12 * Make linter happy * Pass options into restoreCache method * Fix tests * Restructure files and add tests * Add method to get the default download and upload options * Include breaking changes in RELEASES.md Co-authored-by: Josh Gross --- .github/workflows/cache-tests.yml | 22 +- package-lock.json | 6 +- package.json | 2 +- packages/cache/RELEASES.md | 7 +- .../cache/__tests__/cacheHttpClient.test.ts | 218 +++++++-------- packages/cache/__tests__/cacheUtils.test.ts | 8 + packages/cache/__tests__/options.test.ts | 54 ++++ packages/cache/__tests__/requestUtils.test.ts | 140 ++++++++++ packages/cache/__tests__/restoreCache.test.ts | 9 +- packages/cache/package-lock.json | 253 +++++++++++++++++- packages/cache/package.json | 3 +- packages/cache/src/cache.ts | 12 +- .../cache/src/internal/cacheHttpClient.ts | 189 +++---------- packages/cache/src/internal/cacheUtils.ts | 8 + packages/cache/src/internal/downloadUtils.ts | 134 ++++++++++ packages/cache/src/internal/requestUtils.ts | 101 +++++++ packages/cache/src/options.ts | 92 +++++++ 17 files changed, 968 insertions(+), 290 deletions(-) create mode 100644 packages/cache/__tests__/options.test.ts create mode 100644 packages/cache/__tests__/requestUtils.test.ts create mode 100644 packages/cache/src/internal/downloadUtils.ts create mode 100644 packages/cache/src/internal/requestUtils.ts diff --git a/.github/workflows/cache-tests.yml b/.github/workflows/cache-tests.yml index 1ac811ea27..a2daa7ebae 100644 --- a/.github/workflows/cache-tests.yml +++ b/.github/workflows/cache-tests.yml @@ -58,17 +58,33 @@ jobs: run: | node -e "Promise.resolve(require('./packages/cache/lib/cache').saveCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}'))" - - name: Delete cache folders prior to restore + - name: Delete cache folders before restoring shell: bash run: | rm -rf test-cache rm -rf ~/test-cache - - name: Restore cache using restoreCache() + - name: Restore cache using restoreCache() with http-client + run: | + node -e "Promise.resolve(require('./packages/cache/lib/cache').restoreCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}',[],{useAzureSdk: false}))" + + - name: Verify cache restored with http-client + shell: bash + run: | + packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} test-cache + packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} ~/test-cache + + - name: Delete cache folders before restoring + shell: bash + run: | + rm -rf test-cache + rm -rf ~/test-cache + + - name: Restore cache using restoreCache() with Azure SDK run: | node -e "Promise.resolve(require('./packages/cache/lib/cache').restoreCache(['test-cache','~/test-cache'],'test-${{ runner.os }}-${{ github.run_id }}'))" - - name: Verify cache + - name: Verify cache restored with Azure SDK shell: bash run: | packages/cache/__tests__/verify-cache-files.sh ${{ runner.os }} test-cache diff --git a/package-lock.json b/package-lock.json index f6a43d03e6..87069cb9a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3962,9 +3962,9 @@ "dev": true }, "@types/node": { - "version": "11.13.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.5.tgz", - "integrity": "sha512-/OMMBnjVtDuwX1tg2pkYVSqRIDSmNTnvVvmvP/2xiMAAWf4a5+JozrApCrO4WCAILmXVxfNoQ3E+0HJbNpFVGg==", + "version": "12.12.47", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.47.tgz", + "integrity": "sha512-yzBInQFhdY8kaZmqoL2+3U5dSTMrKaYcb561VU+lDzAYvqt+2lojvBEy+hmpSNuXnPTx7m9+04CzWYOUqWME2A==", "dev": true }, "@types/signale": { diff --git a/package.json b/package.json index f8e3b17c0e..8efab7898a 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@types/jest": "^24.0.11", - "@types/node": "^11.13.5", + "@types/node": "^12.12.47", "@types/signale": "^1.2.1", "@typescript-eslint/parser": "^2.2.7", "concurrently": "^4.1.0", diff --git a/packages/cache/RELEASES.md b/packages/cache/RELEASES.md index 0e623d9e4c..bc18cea2c5 100644 --- a/packages/cache/RELEASES.md +++ b/packages/cache/RELEASES.md @@ -8,4 +8,9 @@ - Fixes issues with the zstd compression algorithm on Windows and Ubuntu 16.04 [#469](https://github.com/actions/toolkit/pull/469) ### 0.2.1 -- Fix to await async function getCompressionMethod \ No newline at end of file +- Fix to await async function getCompressionMethod + +### 1.0.0 +- Downloads Azure-hosted caches using the Azure SDK for speed and reliability +- Includes changes that break compatibility with earlier versions, including: + - `retry`, `retryTypedResponse`, and `retryHttpClientResponse` moved from `cacheHttpClient` to `requestUtils` \ No newline at end of file diff --git a/packages/cache/__tests__/cacheHttpClient.test.ts b/packages/cache/__tests__/cacheHttpClient.test.ts index a7f3fec177..90dd5cfc5a 100644 --- a/packages/cache/__tests__/cacheHttpClient.test.ts +++ b/packages/cache/__tests__/cacheHttpClient.test.ts @@ -1,5 +1,9 @@ -import {getCacheVersion, retry} from '../src/internal/cacheHttpClient' +import {downloadCache, getCacheVersion} from '../src/internal/cacheHttpClient' import {CompressionMethod} from '../src/internal/constants' +import * as downloadUtils from '../src/internal/downloadUtils' +import {DownloadOptions, getDownloadOptions} from '../src/options' + +jest.mock('../src/internal/downloadUtils') test('getCacheVersion with one path returns version', async () => { const paths = ['node_modules'] @@ -35,141 +39,103 @@ test('getCacheVersion with gzip compression does not change vesion', async () => ) }) -interface TestResponse { - statusCode: number - result: string | null -} - -async function handleResponse( - response: TestResponse | undefined -): Promise { - if (!response) { - // eslint-disable-next-line no-undef - fail('Retry method called too many times') - } - - if (response.statusCode === 999) { - throw Error('Test Error') - } else { - return Promise.resolve(response) - } -} - -async function testRetryExpectingResult( - responses: TestResponse[], - expectedResult: string | null -): Promise { - responses = responses.reverse() // Reverse responses since we pop from end - - const actualResult = await retry( - 'test', - async () => handleResponse(responses.pop()), - (response: TestResponse) => response.statusCode - ) - - expect(actualResult.result).toEqual(expectedResult) -} - -async function testRetryExpectingError( - responses: TestResponse[] -): Promise { - responses = responses.reverse() // Reverse responses since we pop from end - - expect( - retry( - 'test', - async () => handleResponse(responses.pop()), - (response: TestResponse) => response.statusCode - ) - ).rejects.toBeInstanceOf(Error) -} - -test('retry works on successful response', async () => { - await testRetryExpectingResult( - [ - { - statusCode: 200, - result: 'Ok' - } - ], - 'Ok' +test('downloadCache uses http-client for non-Azure URLs', async () => { + const downloadCacheHttpClientMock = jest.spyOn( + downloadUtils, + 'downloadCacheHttpClient' ) -}) + const downloadCacheStorageSDKMock = jest.spyOn( + downloadUtils, + 'downloadCacheStorageSDK' + ) + + const archiveLocation = 'http://www.actionscache.test/download' + const archivePath = '/foo/bar' -test('retry works after retryable status code', async () => { - await testRetryExpectingResult( - [ - { - statusCode: 503, - result: null - }, - { - statusCode: 200, - result: 'Ok' - } - ], - 'Ok' + await downloadCache(archiveLocation, archivePath) + + expect(downloadCacheHttpClientMock).toHaveBeenCalledTimes(1) + expect(downloadCacheHttpClientMock).toHaveBeenCalledWith( + archiveLocation, + archivePath ) -}) -test('retry fails after exhausting retries', async () => { - await testRetryExpectingError([ - { - statusCode: 503, - result: null - }, - { - statusCode: 503, - result: null - }, - { - statusCode: 200, - result: 'Ok' - } - ]) + expect(downloadCacheStorageSDKMock).toHaveBeenCalledTimes(0) }) -test('retry fails after non-retryable status code', async () => { - await testRetryExpectingError([ - { - statusCode: 500, - result: null - }, - { - statusCode: 200, - result: 'Ok' - } - ]) +test('downloadCache uses storage SDK for Azure storage URLs', async () => { + const downloadCacheHttpClientMock = jest.spyOn( + downloadUtils, + 'downloadCacheHttpClient' + ) + const downloadCacheStorageSDKMock = jest.spyOn( + downloadUtils, + 'downloadCacheStorageSDK' + ) + + const archiveLocation = 'http://foo.blob.core.windows.net/bar/baz' + const archivePath = '/foo/bar' + + await downloadCache(archiveLocation, archivePath) + + expect(downloadCacheStorageSDKMock).toHaveBeenCalledTimes(1) + expect(downloadCacheStorageSDKMock).toHaveBeenCalledWith( + archiveLocation, + archivePath, + getDownloadOptions() + ) + + expect(downloadCacheHttpClientMock).toHaveBeenCalledTimes(0) }) -test('retry works after error', async () => { - await testRetryExpectingResult( - [ - { - statusCode: 999, - result: null - }, - { - statusCode: 200, - result: 'Ok' - } - ], - 'Ok' +test('downloadCache passes options to download methods', async () => { + const downloadCacheHttpClientMock = jest.spyOn( + downloadUtils, + 'downloadCacheHttpClient' ) + const downloadCacheStorageSDKMock = jest.spyOn( + downloadUtils, + 'downloadCacheStorageSDK' + ) + + const archiveLocation = 'http://foo.blob.core.windows.net/bar/baz' + const archivePath = '/foo/bar' + const options: DownloadOptions = {downloadConcurrency: 4} + + await downloadCache(archiveLocation, archivePath, options) + + expect(downloadCacheStorageSDKMock).toHaveBeenCalledTimes(1) + expect(downloadCacheStorageSDKMock).toHaveBeenCalled() + expect(downloadCacheStorageSDKMock).toHaveBeenCalledWith( + archiveLocation, + archivePath, + getDownloadOptions(options) + ) + + expect(downloadCacheHttpClientMock).toHaveBeenCalledTimes(0) }) -test('retry returns after client error', async () => { - await testRetryExpectingResult( - [ - { - statusCode: 400, - result: null - }, - { - statusCode: 200, - result: 'Ok' - } - ], - null +test('downloadCache uses http-client when overridden', async () => { + const downloadCacheHttpClientMock = jest.spyOn( + downloadUtils, + 'downloadCacheHttpClient' ) + const downloadCacheStorageSDKMock = jest.spyOn( + downloadUtils, + 'downloadCacheStorageSDK' + ) + + const archiveLocation = 'http://foo.blob.core.windows.net/bar/baz' + const archivePath = '/foo/bar' + const options: DownloadOptions = {useAzureSdk: false} + + await downloadCache(archiveLocation, archivePath, options) + + expect(downloadCacheHttpClientMock).toHaveBeenCalledTimes(1) + expect(downloadCacheHttpClientMock).toHaveBeenCalledWith( + archiveLocation, + archivePath + ) + + expect(downloadCacheStorageSDKMock).toHaveBeenCalledTimes(0) }) diff --git a/packages/cache/__tests__/cacheUtils.test.ts b/packages/cache/__tests__/cacheUtils.test.ts index 25d7ca8278..32a958608c 100644 --- a/packages/cache/__tests__/cacheUtils.test.ts +++ b/packages/cache/__tests__/cacheUtils.test.ts @@ -24,3 +24,11 @@ test('unlinkFile unlinks file', async () => { await fs.rmdir(testDirectory) }) + +test('assertDefined throws if undefined', () => { + expect(() => cacheUtils.assertDefined('test', undefined)).toThrowError() +}) + +test('assertDefined returns value', () => { + expect(cacheUtils.assertDefined('test', 5)).toBe(5) +}) diff --git a/packages/cache/__tests__/options.test.ts b/packages/cache/__tests__/options.test.ts new file mode 100644 index 0000000000..3075b43b5b --- /dev/null +++ b/packages/cache/__tests__/options.test.ts @@ -0,0 +1,54 @@ +import { + DownloadOptions, + UploadOptions, + getDownloadOptions, + getUploadOptions +} from '../src/options' + +const useAzureSdk = true +const downloadConcurrency = 8 +const timeoutInMs = 30000 +const uploadConcurrency = 4 +const uploadChunkSize = 32 * 1024 * 1024 + +test('getDownloadOptions sets defaults', async () => { + const actualOptions = getDownloadOptions() + + expect(actualOptions).toEqual({ + useAzureSdk, + downloadConcurrency, + timeoutInMs + }) +}) + +test('getDownloadOptions overrides all settings', async () => { + const expectedOptions: DownloadOptions = { + useAzureSdk: false, + downloadConcurrency: 14, + timeoutInMs: 20000 + } + + const actualOptions = getDownloadOptions(expectedOptions) + + expect(actualOptions).toEqual(expectedOptions) +}) + +test('getUploadOptions sets defaults', async () => { + const actualOptions = getUploadOptions() + + expect(actualOptions).toEqual({ + uploadConcurrency, + uploadChunkSize + }) +}) + +test('getUploadOptions overrides all settings', async () => { + const expectedOptions: UploadOptions = { + uploadConcurrency: 2, + uploadChunkSize: 16 * 1024 * 1024 + } + + const actualOptions = getUploadOptions(expectedOptions) + + expect(actualOptions).toEqual(expectedOptions) +}) diff --git a/packages/cache/__tests__/requestUtils.test.ts b/packages/cache/__tests__/requestUtils.test.ts new file mode 100644 index 0000000000..27fef955ff --- /dev/null +++ b/packages/cache/__tests__/requestUtils.test.ts @@ -0,0 +1,140 @@ +import {retry} from '../src/internal/requestUtils' + +interface TestResponse { + statusCode: number + result: string | null +} + +async function handleResponse( + response: TestResponse | undefined +): Promise { + if (!response) { + // eslint-disable-next-line no-undef + fail('Retry method called too many times') + } + + if (response.statusCode === 999) { + throw Error('Test Error') + } else { + return Promise.resolve(response) + } +} + +async function testRetryExpectingResult( + responses: TestResponse[], + expectedResult: string | null +): Promise { + responses = responses.reverse() // Reverse responses since we pop from end + + const actualResult = await retry( + 'test', + async () => handleResponse(responses.pop()), + (response: TestResponse) => response.statusCode + ) + + expect(actualResult.result).toEqual(expectedResult) +} + +async function testRetryExpectingError( + responses: TestResponse[] +): Promise { + responses = responses.reverse() // Reverse responses since we pop from end + + expect( + retry( + 'test', + async () => handleResponse(responses.pop()), + (response: TestResponse) => response.statusCode + ) + ).rejects.toBeInstanceOf(Error) +} + +test('retry works on successful response', async () => { + await testRetryExpectingResult( + [ + { + statusCode: 200, + result: 'Ok' + } + ], + 'Ok' + ) +}) + +test('retry works after retryable status code', async () => { + await testRetryExpectingResult( + [ + { + statusCode: 503, + result: null + }, + { + statusCode: 200, + result: 'Ok' + } + ], + 'Ok' + ) +}) + +test('retry fails after exhausting retries', async () => { + await testRetryExpectingError([ + { + statusCode: 503, + result: null + }, + { + statusCode: 503, + result: null + }, + { + statusCode: 200, + result: 'Ok' + } + ]) +}) + +test('retry fails after non-retryable status code', async () => { + await testRetryExpectingError([ + { + statusCode: 500, + result: null + }, + { + statusCode: 200, + result: 'Ok' + } + ]) +}) + +test('retry works after error', async () => { + await testRetryExpectingResult( + [ + { + statusCode: 999, + result: null + }, + { + statusCode: 200, + result: 'Ok' + } + ], + 'Ok' + ) +}) + +test('retry returns after client error', async () => { + await testRetryExpectingResult( + [ + { + statusCode: 400, + result: null + }, + { + statusCode: 200, + result: 'Ok' + } + ], + null + ) +}) diff --git a/packages/cache/__tests__/restoreCache.test.ts b/packages/cache/__tests__/restoreCache.test.ts index 2e0fd06805..c8a52ea436 100644 --- a/packages/cache/__tests__/restoreCache.test.ts +++ b/packages/cache/__tests__/restoreCache.test.ts @@ -144,7 +144,8 @@ test('restore with gzip compressed cache found', async () => { expect(createTempDirectoryMock).toHaveBeenCalledTimes(1) expect(downloadCacheMock).toHaveBeenCalledWith( cacheEntry.archiveLocation, - archivePath + archivePath, + undefined ) expect(getArchiveFileSizeIsBytesMock).toHaveBeenCalledWith(archivePath) @@ -202,7 +203,8 @@ test('restore with zstd compressed cache found', async () => { expect(createTempDirectoryMock).toHaveBeenCalledTimes(1) expect(downloadCacheMock).toHaveBeenCalledWith( cacheEntry.archiveLocation, - archivePath + archivePath, + undefined ) expect(getArchiveFileSizeIsBytesMock).toHaveBeenCalledWith(archivePath) expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~60 MB (62915000 B)`) @@ -258,7 +260,8 @@ test('restore with cache found for restore key', async () => { expect(createTempDirectoryMock).toHaveBeenCalledTimes(1) expect(downloadCacheMock).toHaveBeenCalledWith( cacheEntry.archiveLocation, - archivePath + archivePath, + undefined ) expect(getArchiveFileSizeIsBytesMock).toHaveBeenCalledWith(archivePath) expect(infoMock).toHaveBeenCalledWith(`Cache Size: ~0 MB (142 B)`) diff --git a/packages/cache/package-lock.json b/packages/cache/package-lock.json index a05581c189..774a5e5a58 100644 --- a/packages/cache/package-lock.json +++ b/packages/cache/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/cache", - "version": "0.2.1", + "version": "0.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -39,18 +39,169 @@ "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz", "integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg==" }, + "@azure/abort-controller": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.1.tgz", + "integrity": "sha512-wP2Jw6uPp8DEDy0n4KNidvwzDjyVV2xnycEIq7nPzj1rHyb/r+t3OPeNT1INZePP2wy5ZqlwyuyOMTi0ePyY1A==", + "requires": { + "tslib": "^1.9.3" + } + }, + "@azure/core-asynciterator-polyfill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz", + "integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg==" + }, + "@azure/core-auth": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.1.2.tgz", + "integrity": "sha512-IUbP/f3v96dpHgXUwsAjUwDzjlUjawyUhWhGKKB6Qxy+iqppC/pVBPyc6kdpyTe7H30HN+4H3f0lar7Wp9Hx6A==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-tracing": "1.0.0-preview.8", + "@opentelemetry/api": "^0.6.1", + "tslib": "^1.10.0" + } + }, + "@azure/core-http": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-1.1.3.tgz", + "integrity": "sha512-GysW3+BRVV4L9cs3GsuCbnlyibrQU6hh5mcJ7hlnk7tdUBzWybUvJ8/P/nHX49PgwRmi81pD5v1ht2jF0IzxAQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.1.2", + "@azure/core-tracing": "1.0.0-preview.8", + "@azure/logger": "^1.0.0", + "@opentelemetry/api": "^0.6.1", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.1", + "form-data": "^3.0.0", + "node-fetch": "^2.6.0", + "process": "^0.11.10", + "tough-cookie": "^4.0.0", + "tslib": "^1.10.0", + "tunnel": "^0.0.6", + "uuid": "^8.1.0", + "xml2js": "^0.4.19" + }, + "dependencies": { + "uuid": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.1.0.tgz", + "integrity": "sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg==" + } + } + }, + "@azure/core-lro": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-1.0.2.tgz", + "integrity": "sha512-Yr0JD7GKryOmbcb5wHCQoQ4KCcH5QJWRNorofid+UvudLaxnbCfvKh/cUfQsGUqRjO9L/Bw4X7FP824DcHdMxw==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^1.1.1", + "events": "^3.0.0", + "tslib": "^1.10.0" + } + }, + "@azure/core-paging": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.1.1.tgz", + "integrity": "sha512-hqEJBEGKan4YdOaL9ZG/GRG6PXaFd/Wb3SSjQW4LWotZzgl6xqG00h6wmkrpd2NNkbBkD1erLHBO3lPHApv+iQ==", + "requires": { + "@azure/core-asynciterator-polyfill": "^1.0.0" + } + }, + "@azure/core-tracing": { + "version": "1.0.0-preview.8", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.8.tgz", + "integrity": "sha512-ZKUpCd7Dlyfn7bdc+/zC/sf0aRIaNQMDuSj2RhYRFe3p70hVAnYGp3TX4cnG2yoEALp/LTj/XnZGQ8Xzf6Ja/Q==", + "requires": { + "@opencensus/web-types": "0.0.7", + "@opentelemetry/api": "^0.6.1", + "tslib": "^1.10.0" + } + }, + "@azure/logger": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.0.tgz", + "integrity": "sha512-g2qLDgvmhyIxR3JVS8N67CyIOeFRKQlX/llxYJQr1OSGQqM3HTpVP8MjmjcEKbL/OIt2N9C9UFaNQuKOw1laOA==", + "requires": { + "tslib": "^1.9.3" + } + }, + "@azure/storage-blob": { + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.1.2.tgz", + "integrity": "sha512-PCHgG4r3xLt5FaFj+uiMqrRpuzD3TD17cvxCeA1JKK2bJEf8b07H3QRLQVf0DM1MmvYY8FgQagkWZTp+jr9yew==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^1.1.1", + "@azure/core-lro": "^1.0.2", + "@azure/core-paging": "^1.1.1", + "@azure/core-tracing": "1.0.0-preview.8", + "@azure/logger": "^1.0.0", + "@opentelemetry/api": "^0.6.1", + "events": "^3.0.0", + "tslib": "^1.10.0" + } + }, + "@opencensus/web-types": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@opencensus/web-types/-/web-types-0.0.7.tgz", + "integrity": "sha512-xB+w7ZDAu3YBzqH44rCmG9/RlrOmFuDPt/bpf17eJr8eZSrLt7nc7LnWdxM9Mmoj/YKMHpxRg28txu3TcpiL+g==" + }, + "@opentelemetry/api": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-0.6.1.tgz", + "integrity": "sha512-wpufGZa7tTxw7eAsjXJtiyIQ42IWQdX9iUQp7ACJcKo1hCtuhLU+K2Nv1U6oRwT1oAlZTE6m4CgWKZBhOiau3Q==", + "requires": { + "@opentelemetry/context-base": "^0.6.1" + } + }, + "@opentelemetry/context-base": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-base/-/context-base-0.6.1.tgz", + "integrity": "sha512-5bHhlTBBq82ti3qPT15TRxkYTFPPQWbnkkQkmHPtqiS1XcTB69cEKd3Jm7Cfi/vkPoyxapmePE9tyA7EzLt8SQ==" + }, + "@types/node": { + "version": "14.0.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.11.tgz", + "integrity": "sha512-lCvvI24L21ZVeIiyIUHZ5Oflv1hhHQ5E1S25IRlKIXaRkVgmXpJMI3wUJkmym2bTbCe+WoIibQnMVAU3FguaOg==" + }, + "@types/node-fetch": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz", + "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==", + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, "@types/semver": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.1.tgz", "integrity": "sha512-+beqKQOh9PYxuHvijhVl+tIHvT6tuwOrE9m14zd+MT2A38KoKZhh7pYJ0SNleLtwDsiIxHDsIk9bv01oOxvSvA==", "dev": true }, + "@types/tunnel": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.1.tgz", + "integrity": "sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A==", + "requires": { + "@types/node": "*" + } + }, "@types/uuid": { "version": "3.4.9", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.9.tgz", "integrity": "sha512-XDwyIlt/47l2kWLTzw/mtrpLdB+GPSskR2n/PIcPn+VYhVO77rGhRncIR5GPU0KRzXuqkDO+J5qqrG0Y8P6jzQ==", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -65,11 +216,52 @@ "concat-map": "0.0.1" } }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "events": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==" + }, + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -78,11 +270,51 @@ "brace-expansion": "^1.1.7" } }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, + "tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.1.2" + } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + }, "tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", @@ -94,10 +326,29 @@ "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", "dev": true }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" } } } diff --git a/packages/cache/package.json b/packages/cache/package.json index 6e0a31818d..3299349b1c 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -1,6 +1,6 @@ { "name": "@actions/cache", - "version": "0.2.1", + "version": "1.0.0", "preview": true, "description": "Actions cache lib", "keywords": [ @@ -42,6 +42,7 @@ "@actions/glob": "^0.1.0", "@actions/http-client": "^1.0.8", "@actions/io": "^1.0.1", + "@azure/storage-blob": "^12.1.2", "semver": "^6.1.0", "uuid": "^3.3.3" }, diff --git a/packages/cache/src/cache.ts b/packages/cache/src/cache.ts index fc04a297b2..57ea1527bf 100644 --- a/packages/cache/src/cache.ts +++ b/packages/cache/src/cache.ts @@ -3,7 +3,7 @@ import * as path from 'path' import * as utils from './internal/cacheUtils' import * as cacheHttpClient from './internal/cacheHttpClient' import {createTar, extractTar} from './internal/tar' -import {UploadOptions} from './options' +import {DownloadOptions, UploadOptions} from './options' export class ValidationError extends Error { constructor(message: string) { @@ -49,12 +49,14 @@ function checkKey(key: string): void { * @param paths a list of file paths to restore from the cache * @param primaryKey an explicit key for restoring the cache * @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key + * @param downloadOptions cache download options * @returns string returns the key for the cache hit, otherwise returns undefined */ export async function restoreCache( paths: string[], primaryKey: string, - restoreKeys?: string[] + restoreKeys?: string[], + options?: DownloadOptions ): Promise { checkPaths(paths) @@ -92,7 +94,11 @@ export async function restoreCache( try { // Download the cache from the cache entry - await cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath) + await cacheHttpClient.downloadCache( + cacheEntry.archiveLocation, + archivePath, + options + ) const archiveFileSize = utils.getArchiveFileSizeIsBytes(archivePath) core.info( diff --git a/packages/cache/src/internal/cacheHttpClient.ts b/packages/cache/src/internal/cacheHttpClient.ts index 21af8031d0..77954e94a8 100644 --- a/packages/cache/src/internal/cacheHttpClient.ts +++ b/packages/cache/src/internal/cacheHttpClient.ts @@ -1,18 +1,13 @@ import * as core from '@actions/core' -import {HttpClient, HttpCodes} from '@actions/http-client' +import {HttpClient} from '@actions/http-client' import {BearerCredentialHandler} from '@actions/http-client/auth' -import { - IHttpClientResponse, - IRequestOptions, - ITypedResponse -} from '@actions/http-client/interfaces' +import {IRequestOptions, ITypedResponse} from '@actions/http-client/interfaces' import * as crypto from 'crypto' import * as fs from 'fs' -import * as stream from 'stream' -import * as util from 'util' +import {URL} from 'url' import * as utils from './cacheUtils' -import {CompressionMethod, SocketTimeout} from './constants' +import {CompressionMethod} from './constants' import { ArtifactCacheEntry, InternalCacheOptions, @@ -20,36 +15,21 @@ import { ReserveCacheRequest, ReserveCacheResponse } from './contracts' -import {UploadOptions} from '../options' +import {downloadCacheHttpClient, downloadCacheStorageSDK} from './downloadUtils' +import { + DownloadOptions, + UploadOptions, + getDownloadOptions, + getUploadOptions +} from '../options' +import { + isSuccessStatusCode, + retryHttpClientResponse, + retryTypedResponse +} from './requestUtils' const versionSalt = '1.0' -function isSuccessStatusCode(statusCode?: number): boolean { - if (!statusCode) { - return false - } - return statusCode >= 200 && statusCode < 300 -} - -function isServerErrorStatusCode(statusCode?: number): boolean { - if (!statusCode) { - return true - } - return statusCode >= 500 -} - -function isRetryableStatusCode(statusCode?: number): boolean { - if (!statusCode) { - return false - } - const retryableStatusCodes = [ - HttpCodes.BadGateway, - HttpCodes.ServiceUnavailable, - HttpCodes.GatewayTimeout - ] - return retryableStatusCodes.includes(statusCode) -} - function getCacheApiUrl(resource: string): string { // Ideally we just use ACTIONS_CACHE_URL const baseUrl: string = ( @@ -110,75 +90,6 @@ export function getCacheVersion( .digest('hex') } -export async function retry( - name: string, - method: () => Promise, - getStatusCode: (arg0: T) => number | undefined, - maxAttempts = 2 -): Promise { - let response: T | undefined = undefined - let statusCode: number | undefined = undefined - let isRetryable = false - let errorMessage = '' - let attempt = 1 - - while (attempt <= maxAttempts) { - try { - response = await method() - statusCode = getStatusCode(response) - - if (!isServerErrorStatusCode(statusCode)) { - return response - } - - isRetryable = isRetryableStatusCode(statusCode) - errorMessage = `Cache service responded with ${statusCode}` - } catch (error) { - isRetryable = true - errorMessage = error.message - } - - core.debug( - `${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}` - ) - - if (!isRetryable) { - core.debug(`${name} - Error is not retryable`) - break - } - - attempt++ - } - - throw Error(`${name} failed: ${errorMessage}`) -} - -export async function retryTypedResponse( - name: string, - method: () => Promise>, - maxAttempts = 2 -): Promise> { - return await retry( - name, - method, - (response: ITypedResponse) => response.statusCode, - maxAttempts - ) -} - -export async function retryHttpClientResponse( - name: string, - method: () => Promise, - maxAttempts = 2 -): Promise { - return await retry( - name, - method, - (response: IHttpClientResponse) => response.message.statusCode, - maxAttempts - ) -} - export async function getCacheEntry( keys: string[], paths: string[], @@ -212,47 +123,23 @@ export async function getCacheEntry( return cacheResult } -async function pipeResponseToStream( - response: IHttpClientResponse, - output: NodeJS.WritableStream -): Promise { - const pipeline = util.promisify(stream.pipeline) - await pipeline(response.message, output) -} - export async function downloadCache( archiveLocation: string, - archivePath: string + archivePath: string, + options?: DownloadOptions ): Promise { - const writeStream = fs.createWriteStream(archivePath) - const httpClient = new HttpClient('actions/cache') - const downloadResponse = await retryHttpClientResponse( - 'downloadCache', - async () => httpClient.get(archiveLocation) - ) - - // Abort download if no traffic received over the socket. - downloadResponse.message.socket.setTimeout(SocketTimeout, () => { - downloadResponse.message.destroy() - core.debug(`Aborting download, socket timed out after ${SocketTimeout} ms`) - }) - - await pipeResponseToStream(downloadResponse, writeStream) - - // Validate download size. - const contentLengthHeader = downloadResponse.message.headers['content-length'] - - if (contentLengthHeader) { - const expectedLength = parseInt(contentLengthHeader) - const actualLength = utils.getArchiveFileSizeIsBytes(archivePath) - - if (actualLength !== expectedLength) { - throw new Error( - `Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}` - ) - } + const archiveUrl = new URL(archiveLocation) + const downloadOptions = getDownloadOptions(options) + + if ( + downloadOptions.useAzureSdk && + archiveUrl.hostname.endsWith('.blob.core.windows.net') + ) { + // Use Azure storage SDK to download caches hosted on Azure to improve speed and reliability. + await downloadCacheStorageSDK(archiveLocation, archivePath, downloadOptions) } else { - core.debug('Unable to validate download, no Content-Length header') + // Otherwise, download using the Actions http-client. + await downloadCacheHttpClient(archiveLocation, archivePath) } } @@ -329,10 +216,16 @@ async function uploadFile( const fileSize = fs.statSync(archivePath).size const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`) const fd = fs.openSync(archivePath, 'r') + const uploadOptions = getUploadOptions(options) - const concurrency = options?.uploadConcurrency ?? 4 // # of HTTP requests in parallel - const MAX_CHUNK_SIZE = options?.uploadChunkSize ?? 32 * 1024 * 1024 // 32 MB Chunks - core.debug(`Concurrency: ${concurrency} and Chunk Size: ${MAX_CHUNK_SIZE}`) + const concurrency = utils.assertDefined( + 'uploadConcurrency', + uploadOptions.uploadConcurrency + ) + const maxChunkSize = utils.assertDefined( + 'uploadChunkSize', + uploadOptions.uploadChunkSize + ) const parallelUploads = [...new Array(concurrency).keys()] core.debug('Awaiting all uploads') @@ -342,10 +235,10 @@ async function uploadFile( await Promise.all( parallelUploads.map(async () => { while (offset < fileSize) { - const chunkSize = Math.min(fileSize - offset, MAX_CHUNK_SIZE) + const chunkSize = Math.min(fileSize - offset, maxChunkSize) const start = offset const end = offset + chunkSize - 1 - offset += MAX_CHUNK_SIZE + offset += maxChunkSize await uploadChunk( httpClient, @@ -360,7 +253,7 @@ async function uploadFile( }) .on('error', error => { throw new Error( - `Cache upload failed because file read failed with ${error.Message}` + `Cache upload failed because file read failed with ${error.message}` ) }), start, diff --git a/packages/cache/src/internal/cacheUtils.ts b/packages/cache/src/internal/cacheUtils.ts index b279369514..0628ffa4a0 100644 --- a/packages/cache/src/internal/cacheUtils.ts +++ b/packages/cache/src/internal/cacheUtils.ts @@ -113,3 +113,11 @@ export async function isGnuTarInstalled(): Promise { const versionOutput = await getVersion('tar') return versionOutput.toLowerCase().includes('gnu tar') } + +export function assertDefined(name: string, value?: T): T { + if (value === undefined) { + throw Error(`Expected ${name} but value was undefiend`) + } + + return value +} diff --git a/packages/cache/src/internal/downloadUtils.ts b/packages/cache/src/internal/downloadUtils.ts new file mode 100644 index 0000000000..839b1fa3fc --- /dev/null +++ b/packages/cache/src/internal/downloadUtils.ts @@ -0,0 +1,134 @@ +import * as core from '@actions/core' +import {HttpClient} from '@actions/http-client' +import {IHttpClientResponse} from '@actions/http-client/interfaces' +import {BlockBlobClient} from '@azure/storage-blob' +import * as buffer from 'buffer' +import * as fs from 'fs' +import * as stream from 'stream' +import * as util from 'util' + +import * as utils from './cacheUtils' +import {SocketTimeout} from './constants' +import {DownloadOptions} from '../options' +import {retryHttpClientResponse} from './requestUtils' + +/** + * Pipes the body of a HTTP response to a stream + * + * @param response the HTTP response + * @param output the writable stream + */ +async function pipeResponseToStream( + response: IHttpClientResponse, + output: NodeJS.WritableStream +): Promise { + const pipeline = util.promisify(stream.pipeline) + await pipeline(response.message, output) +} + +/** + * Download the cache using the Actions toolkit http-client + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + */ +export async function downloadCacheHttpClient( + archiveLocation: string, + archivePath: string +): Promise { + const writeStream = fs.createWriteStream(archivePath) + const httpClient = new HttpClient('actions/cache') + const downloadResponse = await retryHttpClientResponse( + 'downloadCache', + async () => httpClient.get(archiveLocation) + ) + + // Abort download if no traffic received over the socket. + downloadResponse.message.socket.setTimeout(SocketTimeout, () => { + downloadResponse.message.destroy() + core.debug(`Aborting download, socket timed out after ${SocketTimeout} ms`) + }) + + await pipeResponseToStream(downloadResponse, writeStream) + + // Validate download size. + const contentLengthHeader = downloadResponse.message.headers['content-length'] + + if (contentLengthHeader) { + const expectedLength = parseInt(contentLengthHeader) + const actualLength = utils.getArchiveFileSizeIsBytes(archivePath) + + if (actualLength !== expectedLength) { + throw new Error( + `Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}` + ) + } + } else { + core.debug('Unable to validate download, no Content-Length header') + } +} + +/** + * Download the cache using the Azure Storage SDK. Only call this method if the + * URL points to an Azure Storage endpoint. + * + * @param archiveLocation the URL for the cache + * @param archivePath the local path where the cache is saved + * @param options the download options with the defaults set + */ +export async function downloadCacheStorageSDK( + archiveLocation: string, + archivePath: string, + options: DownloadOptions +): Promise { + const client = new BlockBlobClient(archiveLocation, undefined, { + retryOptions: { + // Override the timeout used when downloading each 4 MB chunk + // The default is 2 min / MB, which is way too slow + tryTimeoutInMs: options.timeoutInMs + } + }) + + const properties = await client.getProperties() + const contentLength = properties.contentLength ?? -1 + + if (contentLength < 0) { + // We should never hit this condition, but just in case fall back to downloading the + // file as one large stream + core.debug( + 'Unable to determine content length, downloading file with http-client...' + ) + + await downloadCacheHttpClient(archiveLocation, archivePath) + } else { + // Use downloadToBuffer for faster downloads, since internally it splits the + // file into 4 MB chunks which can then be parallelized and retried independently + // + // If the file exceeds the buffer maximum length (~1 GB on 32-bit systems and ~2 GB + // on 64-bit systems), split the download into multiple segments + const maxSegmentSize = buffer.constants.MAX_LENGTH + let offset = 0 + + const fd = fs.openSync(archivePath, 'w') + + try { + while (offset < contentLength) { + const segmentSize = Math.min(maxSegmentSize, contentLength - offset) + core.debug( + `Downloading segment at offset ${offset} with length ${segmentSize}...` + ) + + const result = await client.downloadToBuffer(offset, segmentSize, { + concurrency: options.downloadConcurrency + }) + + fs.writeFileSync(fd, result) + + core.debug(`Finished segment at offset ${offset}`) + offset += segmentSize + } + } finally { + fs.closeSync(fd) + } + } +} diff --git a/packages/cache/src/internal/requestUtils.ts b/packages/cache/src/internal/requestUtils.ts new file mode 100644 index 0000000000..c72728cb70 --- /dev/null +++ b/packages/cache/src/internal/requestUtils.ts @@ -0,0 +1,101 @@ +import * as core from '@actions/core' +import {HttpCodes} from '@actions/http-client' +import { + IHttpClientResponse, + ITypedResponse +} from '@actions/http-client/interfaces' + +export function isSuccessStatusCode(statusCode?: number): boolean { + if (!statusCode) { + return false + } + return statusCode >= 200 && statusCode < 300 +} + +export function isServerErrorStatusCode(statusCode?: number): boolean { + if (!statusCode) { + return true + } + return statusCode >= 500 +} + +export function isRetryableStatusCode(statusCode?: number): boolean { + if (!statusCode) { + return false + } + const retryableStatusCodes = [ + HttpCodes.BadGateway, + HttpCodes.ServiceUnavailable, + HttpCodes.GatewayTimeout + ] + return retryableStatusCodes.includes(statusCode) +} + +export async function retry( + name: string, + method: () => Promise, + getStatusCode: (arg0: T) => number | undefined, + maxAttempts = 2 +): Promise { + let response: T | undefined = undefined + let statusCode: number | undefined = undefined + let isRetryable = false + let errorMessage = '' + let attempt = 1 + + while (attempt <= maxAttempts) { + try { + response = await method() + statusCode = getStatusCode(response) + + if (!isServerErrorStatusCode(statusCode)) { + return response + } + + isRetryable = isRetryableStatusCode(statusCode) + errorMessage = `Cache service responded with ${statusCode}` + } catch (error) { + isRetryable = true + errorMessage = error.message + } + + core.debug( + `${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}` + ) + + if (!isRetryable) { + core.debug(`${name} - Error is not retryable`) + break + } + + attempt++ + } + + throw Error(`${name} failed: ${errorMessage}`) +} + +export async function retryTypedResponse( + name: string, + method: () => Promise>, + maxAttempts = 2 +): Promise> { + return await retry( + name, + method, + (response: ITypedResponse) => response.statusCode, + maxAttempts + ) +} + +export async function retryHttpClientResponse( + name: string, + method: () => Promise, + maxAttempts = 2 +): Promise { + return await retry( + name, + method, + (response: IHttpClientResponse) => response.message.statusCode, + maxAttempts + ) +} diff --git a/packages/cache/src/options.ts b/packages/cache/src/options.ts index 97441c1e07..94642fd3c6 100644 --- a/packages/cache/src/options.ts +++ b/packages/cache/src/options.ts @@ -1,3 +1,5 @@ +import * as core from '@actions/core' + /** * Options to control cache upload */ @@ -15,3 +17,93 @@ export interface UploadOptions { */ uploadChunkSize?: number } + +/** + * Options to control cache download + */ +export interface DownloadOptions { + /** + * Indicates whether to use the Azure Blob SDK to download caches + * that are stored on Azure Blob Storage to improve reliability and + * performance + * + * @default true + */ + useAzureSdk?: boolean + + /** + * Number of parallel downloads (this option only applies when using + * the Azure SDK) + * + * @default 8 + */ + downloadConcurrency?: number + + /** + * Maximum time for each download request, in milliseconds (this + * option only applies when using the Azure SDK) + * + * @default 30000 + */ + timeoutInMs?: number +} + +/** + * Returns a copy of the upload options with defaults filled in. + * + * @param copy the original upload options + */ +export function getUploadOptions(copy?: UploadOptions): UploadOptions { + const result: UploadOptions = { + uploadConcurrency: 4, + uploadChunkSize: 32 * 1024 * 1024 + } + + if (copy) { + if (typeof copy.uploadConcurrency === 'number') { + result.uploadConcurrency = copy.uploadConcurrency + } + + if (typeof copy.uploadChunkSize === 'number') { + result.uploadChunkSize = copy.uploadChunkSize + } + } + + core.debug(`Upload concurrency: ${result.uploadConcurrency}`) + core.debug(`Upload chunk size: ${result.uploadChunkSize}`) + + return result +} + +/** + * Returns a copy of the download options with defaults filled in. + * + * @param copy the original download options + */ +export function getDownloadOptions(copy?: DownloadOptions): DownloadOptions { + const result: DownloadOptions = { + useAzureSdk: true, + downloadConcurrency: 8, + timeoutInMs: 30000 + } + + if (copy) { + if (typeof copy.useAzureSdk === 'boolean') { + result.useAzureSdk = copy.useAzureSdk + } + + if (typeof copy.downloadConcurrency === 'number') { + result.downloadConcurrency = copy.downloadConcurrency + } + + if (typeof copy.timeoutInMs === 'number') { + result.timeoutInMs = copy.timeoutInMs + } + } + + core.debug(`Use Azure SDK: ${result.useAzureSdk}`) + core.debug(`Download concurrency: ${result.downloadConcurrency}`) + core.debug(`Request timeout (ms): ${result.timeoutInMs}`) + + return result +} From cc474239c98664b8d36c09b90083548ca61a1edc Mon Sep 17 00:00:00 2001 From: Dave Hadka Date: Sat, 20 Jun 2020 16:53:27 -0500 Subject: [PATCH 178/192] Display download progress --- packages/cache/RELEASES.md | 1 + .../cache/__tests__/downloadUtils.test.ts | 159 +++++++++++++++++ packages/cache/package-lock.json | 86 ++++++++- packages/cache/package.json | 1 + packages/cache/src/internal/downloadUtils.ts | 165 ++++++++++++++++-- packages/cache/tsconfig.json | 3 +- 6 files changed, 402 insertions(+), 13 deletions(-) create mode 100644 packages/cache/__tests__/downloadUtils.test.ts diff --git a/packages/cache/RELEASES.md b/packages/cache/RELEASES.md index bc18cea2c5..f61e7a416a 100644 --- a/packages/cache/RELEASES.md +++ b/packages/cache/RELEASES.md @@ -12,5 +12,6 @@ ### 1.0.0 - Downloads Azure-hosted caches using the Azure SDK for speed and reliability +- Displays download progress - Includes changes that break compatibility with earlier versions, including: - `retry`, `retryTypedResponse`, and `retryHttpClientResponse` moved from `cacheHttpClient` to `requestUtils` \ No newline at end of file diff --git a/packages/cache/__tests__/downloadUtils.test.ts b/packages/cache/__tests__/downloadUtils.test.ts new file mode 100644 index 0000000000..c5179d331e --- /dev/null +++ b/packages/cache/__tests__/downloadUtils.test.ts @@ -0,0 +1,159 @@ +import * as core from '@actions/core' +import {DownloadProgress} from '../src/internal/downloadUtils' + +test('download progress tracked correctly', () => { + const progress = new DownloadProgress(1000) + + expect(progress.contentLength).toBe(1000) + expect(progress.receivedBytes).toBe(0) + expect(progress.segmentIndex).toBe(0) + expect(progress.segmentOffset).toBe(0) + expect(progress.segmentSize).toBe(0) + expect(progress.displayedComplete).toBe(false) + expect(progress.timeoutHandle).toBeUndefined() + expect(progress.getTransferredBytes()).toBe(0) + expect(progress.isDone()).toBe(false) + + progress.nextSegment(500) + + expect(progress.contentLength).toBe(1000) + expect(progress.receivedBytes).toBe(0) + expect(progress.segmentIndex).toBe(1) + expect(progress.segmentOffset).toBe(0) + expect(progress.segmentSize).toBe(500) + expect(progress.displayedComplete).toBe(false) + expect(progress.timeoutHandle).toBeUndefined() + expect(progress.getTransferredBytes()).toBe(0) + expect(progress.isDone()).toBe(false) + + progress.setReceivedBytes(250) + + expect(progress.contentLength).toBe(1000) + expect(progress.receivedBytes).toBe(250) + expect(progress.segmentIndex).toBe(1) + expect(progress.segmentOffset).toBe(0) + expect(progress.segmentSize).toBe(500) + expect(progress.displayedComplete).toBe(false) + expect(progress.timeoutHandle).toBeUndefined() + expect(progress.getTransferredBytes()).toBe(250) + expect(progress.isDone()).toBe(false) + + progress.setReceivedBytes(500) + + expect(progress.contentLength).toBe(1000) + expect(progress.receivedBytes).toBe(500) + expect(progress.segmentIndex).toBe(1) + expect(progress.segmentOffset).toBe(0) + expect(progress.segmentSize).toBe(500) + expect(progress.displayedComplete).toBe(false) + expect(progress.timeoutHandle).toBeUndefined() + expect(progress.getTransferredBytes()).toBe(500) + expect(progress.isDone()).toBe(false) + + progress.nextSegment(500) + + expect(progress.contentLength).toBe(1000) + expect(progress.receivedBytes).toBe(0) + expect(progress.segmentIndex).toBe(2) + expect(progress.segmentOffset).toBe(500) + expect(progress.segmentSize).toBe(500) + expect(progress.displayedComplete).toBe(false) + expect(progress.timeoutHandle).toBeUndefined() + expect(progress.getTransferredBytes()).toBe(500) + expect(progress.isDone()).toBe(false) + + progress.setReceivedBytes(250) + + expect(progress.contentLength).toBe(1000) + expect(progress.receivedBytes).toBe(250) + expect(progress.segmentIndex).toBe(2) + expect(progress.segmentOffset).toBe(500) + expect(progress.segmentSize).toBe(500) + expect(progress.displayedComplete).toBe(false) + expect(progress.timeoutHandle).toBeUndefined() + expect(progress.getTransferredBytes()).toBe(750) + expect(progress.isDone()).toBe(false) + + progress.setReceivedBytes(500) + + expect(progress.contentLength).toBe(1000) + expect(progress.receivedBytes).toBe(500) + expect(progress.segmentIndex).toBe(2) + expect(progress.segmentOffset).toBe(500) + expect(progress.segmentSize).toBe(500) + expect(progress.displayedComplete).toBe(false) + expect(progress.timeoutHandle).toBeUndefined() + expect(progress.getTransferredBytes()).toBe(1000) + expect(progress.isDone()).toBe(true) +}) + +test('display timer works correctly', () => { + const progress = new DownloadProgress(1000) + + const infoMock = jest.spyOn(core, 'info') + infoMock.mockImplementation(() => {}) + + const check = (): void => { + expect(infoMock).toHaveBeenLastCalledWith( + expect.stringContaining('Received 500 of 1000') + ) + } + + // Validate no further updates are displayed after stopping the timer. + const test2 = (): void => { + check() + expect(progress.timeoutHandle).toBeUndefined() + } + + // Validate the progress is displayed, stop the timer, and call test2. + const test1 = (): void => { + check() + + progress.stopDisplayTimer() + progress.setReceivedBytes(1000) + + setTimeout(() => test2(), 100) + } + + // Start the timer, update the received bytes, and call test1. + const start = (): void => { + progress.startDisplayTimer(10) + expect(progress.timeoutHandle).toBeDefined() + + progress.setReceivedBytes(500) + + setTimeout(() => test1(), 100) + } + + start() +}) + +test('display does not print completed line twice', () => { + const progress = new DownloadProgress(1000) + + const infoMock = jest.spyOn(core, 'info') + infoMock.mockImplementation(() => {}) + + progress.display() + + expect(progress.displayedComplete).toBe(false) + expect(infoMock).toHaveBeenCalledTimes(1) + + progress.nextSegment(1000) + progress.setReceivedBytes(500) + progress.display() + + expect(progress.displayedComplete).toBe(false) + expect(infoMock).toHaveBeenCalledTimes(2) + + progress.setReceivedBytes(1000) + progress.display() + + expect(progress.displayedComplete).toBe(true) + expect(infoMock).toHaveBeenCalledTimes(3) + + progress.display() + + expect(progress.displayedComplete).toBe(true) + expect(infoMock).toHaveBeenCalledTimes(3) +}) diff --git a/packages/cache/package-lock.json b/packages/cache/package-lock.json index 774a5e5a58..234b64cb9f 100644 --- a/packages/cache/package-lock.json +++ b/packages/cache/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/cache", - "version": "0.3.0", + "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -129,6 +129,45 @@ "tslib": "^1.9.3" } }, + "@azure/ms-rest-js": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.0.7.tgz", + "integrity": "sha512-rQpNxDhyOIyS4E+4sUCBMvjrtbNwB32wH06cC2SFoQM4TR29bIKaTlIC1tMe0K07w9c5tNk/2uUHs6/ld/Z3+A==", + "requires": { + "@types/node-fetch": "^2.3.7", + "@types/tunnel": "0.0.1", + "abort-controller": "^3.0.0", + "form-data": "^2.5.0", + "node-fetch": "^2.6.0", + "tough-cookie": "^3.0.1", + "tslib": "^1.10.0", + "tunnel": "0.0.6", + "uuid": "^3.3.2", + "xml2js": "^0.4.19" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, "@azure/storage-blob": { "version": "12.1.2", "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.1.2.tgz", @@ -177,6 +216,28 @@ "form-data": "^3.0.0" } }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + }, + "@types/react": { + "version": "16.9.38", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.38.tgz", + "integrity": "sha512-pHAeZbjjNRa/hxyNuLrvbxhhnKyKNiLC6I5fRF2Zr/t/S6zS41MiyzH4+c+1I9vVfvuRt1VS2Lodjr4ZWnxrdA==", + "requires": { + "@types/prop-types": "*", + "csstype": "^2.2.0" + } + }, + "@types/react-native": { + "version": "0.62.13", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.62.13.tgz", + "integrity": "sha512-hs4/tSABhcJx+J8pZhVoXHrOQD89WFmbs8QiDLNSA9zNrD46pityAuBWuwk1aMjPk9I3vC5ewkJroVRHgRIfdg==", + "requires": { + "@types/react": "*" + } + }, "@types/semver": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.1.tgz", @@ -197,6 +258,14 @@ "integrity": "sha512-XDwyIlt/47l2kWLTzw/mtrpLdB+GPSskR2n/PIcPn+VYhVO77rGhRncIR5GPU0KRzXuqkDO+J5qqrG0Y8P6jzQ==", "dev": true }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -229,11 +298,21 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "csstype": { + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz", + "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==" + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, "events": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", @@ -249,6 +328,11 @@ "mime-types": "^2.1.12" } }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" + }, "mime-db": { "version": "1.44.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", diff --git a/packages/cache/package.json b/packages/cache/package.json index 3299349b1c..4768d8e9bb 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -42,6 +42,7 @@ "@actions/glob": "^0.1.0", "@actions/http-client": "^1.0.8", "@actions/io": "^1.0.1", + "@azure/ms-rest-js": "^2.0.7", "@azure/storage-blob": "^12.1.2", "semver": "^6.1.0", "uuid": "^3.3.3" diff --git a/packages/cache/src/internal/downloadUtils.ts b/packages/cache/src/internal/downloadUtils.ts index 839b1fa3fc..6286322ffc 100644 --- a/packages/cache/src/internal/downloadUtils.ts +++ b/packages/cache/src/internal/downloadUtils.ts @@ -2,6 +2,7 @@ import * as core from '@actions/core' import {HttpClient} from '@actions/http-client' import {IHttpClientResponse} from '@actions/http-client/interfaces' import {BlockBlobClient} from '@azure/storage-blob' +import {TransferProgressEvent} from '@azure/ms-rest-js' import * as buffer from 'buffer' import * as fs from 'fs' import * as stream from 'stream' @@ -26,6 +27,139 @@ async function pipeResponseToStream( await pipeline(response.message, output) } +/** + * Class for tracking the download state and displaying stats. + */ +export class DownloadProgress { + contentLength: number + segmentIndex: number + segmentSize: number + segmentOffset: number + receivedBytes: number + startTime: number + displayedComplete: boolean + timeoutHandle?: ReturnType + + constructor(contentLength: number) { + this.contentLength = contentLength + this.segmentIndex = 0 + this.segmentSize = 0 + this.segmentOffset = 0 + this.receivedBytes = 0 + this.displayedComplete = false + this.startTime = Date.now() + } + + /** + * Progress to the next segment. Only call this method when the previous segment + * is complete. + * + * @param segmentSize the length of the next segment + */ + nextSegment(segmentSize: number): void { + this.segmentOffset = this.segmentOffset + this.segmentSize + this.segmentIndex = this.segmentIndex + 1 + this.segmentSize = segmentSize + this.receivedBytes = 0 + + core.debug( + `Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...` + ) + } + + /** + * Sets the number of bytes received for the current segment. + * + * @param receivedBytes the number of bytes received + */ + setReceivedBytes(receivedBytes: number): void { + this.receivedBytes = receivedBytes + } + + /** + * Returns the total number of bytes transferred. + */ + getTransferredBytes(): number { + return this.segmentOffset + this.receivedBytes + } + + /** + * Returns true if the download is complete. + */ + isDone(): boolean { + return this.getTransferredBytes() === this.contentLength + } + + /** + * Prints the current download stats. Once the download completes, this will print one + * last line and then stop. + */ + display(): void { + if (this.displayedComplete) { + return + } + + const transferredBytes = this.segmentOffset + this.receivedBytes + const percentage = (100 * (transferredBytes / this.contentLength)).toFixed( + 1 + ) + const elapsedTime = Date.now() - this.startTime + const downloadSpeed = ( + transferredBytes / + (1024 * 1024) / + (elapsedTime / 1000) + ).toFixed(1) + + core.info( + `Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec` + ) + + if (this.isDone()) { + this.displayedComplete = true + } + } + + /** + * Callback used to update the progress. + * + * @param progress the progress event + */ + onProgressCallback(progress: TransferProgressEvent): void { + this.setReceivedBytes(progress.loadedBytes) + } + + /** + * Starts the timer that displays the stats. + * + * @param delayInMs the delay between each write + */ + startDisplayTimer(delayInMs: number = 1000): void { + const displayCallback = (): void => { + this.display() + + if (!this.isDone()) { + this.timeoutHandle = setTimeout(displayCallback, delayInMs) + } + } + + this.timeoutHandle = setTimeout(displayCallback, delayInMs) + } + + /** + * Stops the timer that displays the stats. As this typically indicates the download + * is complete, this will display one last line, unless the last line has already + * been written. + */ + stopDisplayTimer(): void { + if (this.timeoutHandle) { + clearTimeout(this.timeoutHandle) + this.timeoutHandle = undefined + } + + this.display() + } +} + /** * Download the cache using the Actions toolkit http-client * @@ -107,27 +241,36 @@ export async function downloadCacheStorageSDK( // If the file exceeds the buffer maximum length (~1 GB on 32-bit systems and ~2 GB // on 64-bit systems), split the download into multiple segments const maxSegmentSize = buffer.constants.MAX_LENGTH - let offset = 0 + const downloadProgress = new DownloadProgress(contentLength) const fd = fs.openSync(archivePath, 'w') try { - while (offset < contentLength) { - const segmentSize = Math.min(maxSegmentSize, contentLength - offset) - core.debug( - `Downloading segment at offset ${offset} with length ${segmentSize}...` + downloadProgress.startDisplayTimer() + + while (!downloadProgress.isDone()) { + const segmentSize = Math.min( + maxSegmentSize, + contentLength - downloadProgress.segmentOffset ) - const result = await client.downloadToBuffer(offset, segmentSize, { - concurrency: options.downloadConcurrency - }) + downloadProgress.nextSegment(segmentSize) - fs.writeFileSync(fd, result) + const result = await client.downloadToBuffer( + downloadProgress.segmentOffset, + segmentSize, + { + concurrency: options.downloadConcurrency, + onProgress: downloadProgress.onProgressCallback.bind( + downloadProgress + ) + } + ) - core.debug(`Finished segment at offset ${offset}`) - offset += segmentSize + fs.writeFileSync(fd, result) } } finally { + downloadProgress.stopDisplayTimer() fs.closeSync(fd) } } diff --git a/packages/cache/tsconfig.json b/packages/cache/tsconfig.json index a8b812a6ff..2cddd408b8 100644 --- a/packages/cache/tsconfig.json +++ b/packages/cache/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "baseUrl": "./", "outDir": "./lib", - "rootDir": "./src" + "rootDir": "./src", + "lib": ["es6", "dom"] }, "include": [ "./src" From c4a92b0b601bf880afc7315db986e9209ff01f5c Mon Sep 17 00:00:00 2001 From: Dave Hadka Date: Mon, 13 Jul 2020 16:07:58 -0500 Subject: [PATCH 179/192] Update with reviewer feedback --- packages/cache/src/internal/downloadUtils.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/cache/src/internal/downloadUtils.ts b/packages/cache/src/internal/downloadUtils.ts index 6286322ffc..69f162e947 100644 --- a/packages/cache/src/internal/downloadUtils.ts +++ b/packages/cache/src/internal/downloadUtils.ts @@ -120,12 +120,12 @@ export class DownloadProgress { } /** - * Callback used to update the progress. - * - * @param progress the progress event + * Returns a function used to handle TransferProgressEvents. */ - onProgressCallback(progress: TransferProgressEvent): void { - this.setReceivedBytes(progress.loadedBytes) + onProgress(): (progress: TransferProgressEvent) => void { + return (progress: TransferProgressEvent) => { + this.setReceivedBytes(progress.loadedBytes) + } } /** @@ -261,9 +261,7 @@ export async function downloadCacheStorageSDK( segmentSize, { concurrency: options.downloadConcurrency, - onProgress: downloadProgress.onProgressCallback.bind( - downloadProgress - ) + onProgress: downloadProgress.onProgress() } ) From 95a10d23fa9210e90937cf87de872954ac0ec107 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Tue, 14 Jul 2020 16:05:53 -0400 Subject: [PATCH 180/192] Pipe audit results to a json file so lerna does not overflow (#515) * Pipe audit results to a json file so lerna does not overflow * reorder flags and args --- .gitignore | 1 + packages/artifact/package.json | 2 +- packages/cache/package.json | 2 +- packages/core/package.json | 2 +- packages/exec/package.json | 2 +- packages/github/package.json | 2 +- packages/glob/package.json | 2 +- packages/io/package.json | 2 +- packages/tool-cache/package.json | 2 +- 9 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 255bac5731..c13ba4b55e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ packages/*/node_modules/ packages/*/lib/ packages/*/__tests__/_temp/ .DS_Store +packages/*/audit.json diff --git a/packages/artifact/package.json b/packages/artifact/package.json index 0c7baf925b..42891a153a 100644 --- a/packages/artifact/package.json +++ b/packages/artifact/package.json @@ -29,7 +29,7 @@ "directory": "packages/artifact" }, "scripts": { - "audit-moderate": "npm install && npm audit --audit-level=moderate", + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", "test": "echo \"Error: run tests from root\" && exit 1", "tsc": "tsc" }, diff --git a/packages/cache/package.json b/packages/cache/package.json index 4768d8e9bb..435e4f390f 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -29,7 +29,7 @@ "directory": "packages/cache" }, "scripts": { - "audit-moderate": "npm install && npm audit --audit-level=moderate", + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", "test": "echo \"Error: run tests from root\" && exit 1", "tsc": "tsc" }, diff --git a/packages/core/package.json b/packages/core/package.json index 79e6df0bb5..05f03cb9ab 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -28,7 +28,7 @@ "directory": "packages/core" }, "scripts": { - "audit-moderate": "npm install && npm audit --audit-level=moderate", + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", "test": "echo \"Error: run tests from root\" && exit 1", "tsc": "tsc" }, diff --git a/packages/exec/package.json b/packages/exec/package.json index a16c14c361..fde021d3df 100644 --- a/packages/exec/package.json +++ b/packages/exec/package.json @@ -28,7 +28,7 @@ "directory": "packages/exec" }, "scripts": { - "audit-moderate": "npm install && npm audit --audit-level=moderate", + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", "test": "echo \"Error: run tests from root\" && exit 1", "tsc": "tsc" }, diff --git a/packages/github/package.json b/packages/github/package.json index 60283670de..3d93545b1b 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -27,7 +27,7 @@ "directory": "packages/github" }, "scripts": { - "audit-moderate": "npm install && npm audit --audit-level=moderate", + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", "test": "jest", "build": "tsc", "format": "prettier --write **/*.ts", diff --git a/packages/glob/package.json b/packages/glob/package.json index 58eff04db7..e75dae1c9a 100644 --- a/packages/glob/package.json +++ b/packages/glob/package.json @@ -29,7 +29,7 @@ "directory": "packages/glob" }, "scripts": { - "audit-moderate": "npm install && npm audit --audit-level=moderate", + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", "test": "echo \"Error: run tests from root\" && exit 1", "tsc": "tsc" }, diff --git a/packages/io/package.json b/packages/io/package.json index 0fd128ef5e..9d4213e184 100644 --- a/packages/io/package.json +++ b/packages/io/package.json @@ -27,7 +27,7 @@ "directory": "packages/io" }, "scripts": { - "audit-moderate": "npm install && npm audit --audit-level=moderate", + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", "test": "echo \"Error: run tests from root\" && exit 1", "tsc": "tsc" }, diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index 536a8e92de..c16195ba1c 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -28,7 +28,7 @@ "directory": "packages/tool-cache" }, "scripts": { - "audit-moderate": "npm install && npm audit --audit-level=moderate", + "audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json", "test": "echo \"Error: run tests from root\" && exit 1", "tsc": "tsc" }, From 7e1c59c51ef072ba52b43164522be1f3f0b78275 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Tue, 14 Jul 2020 16:46:07 -0400 Subject: [PATCH 181/192] Update email in octokit upgrade job (#516) --- .github/workflows/update-github.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-github.yaml b/.github/workflows/update-github.yaml index b68eaf31fb..2d6b028033 100644 --- a/.github/workflows/update-github.yaml +++ b/.github/workflows/update-github.yaml @@ -22,7 +22,7 @@ jobs: run: | if [[ "$(git status --porcelain)" != "" ]]; then echo "::set-output name=createPR::true" - git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.email "github-actions@github.com" git config --global user.name "github-actions[bot]" git checkout -b bots/updateGitHubDependencies-${{github.run_number}} git add . From 2710592b73f7e015cecbd600f264fee9f6d55511 Mon Sep 17 00:00:00 2001 From: Frederik Wallner Date: Wed, 15 Jul 2020 20:49:23 +0200 Subject: [PATCH 182/192] tool-cache: Support for extracting xar compatible archives (#207) * Test xar extraction * Support for extracting xar compatible archives * Only allow extractXar on mac * Create xar during test instead of using prebuilt * Update lockfiles * Add verbose flag if debug * Add extractXar example to readme * Revert "Update lockfiles" This reverts commit a6cbddccf6bf8f7278948e719dc9e28329608cdf. * Use node pkg in example * Remove and ignore prebuilt xar * Tests for non-existing dir and without flags * Better arguments handling * Make sure that target directory exists Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com> --- .gitignore | 1 + packages/tool-cache/README.md | 4 + .../file-with-\303\247-character.txt" | 1 + .../__tests__/data/archive-content/file.txt | 1 + .../archive-content/folder/nested-file.txt | 1 + .../tool-cache/__tests__/tool-cache.test.ts | 105 ++++++++++++++++++ packages/tool-cache/src/tool-cache.ts | 46 ++++++++ 7 files changed, 159 insertions(+) create mode 100644 "packages/tool-cache/__tests__/data/archive-content/file-with-\303\247-character.txt" create mode 100644 packages/tool-cache/__tests__/data/archive-content/file.txt create mode 100644 packages/tool-cache/__tests__/data/archive-content/folder/nested-file.txt diff --git a/.gitignore b/.gitignore index c13ba4b55e..f543c3aefe 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ packages/*/node_modules/ packages/*/lib/ packages/*/__tests__/_temp/ .DS_Store +*.xar packages/*/audit.json diff --git a/packages/tool-cache/README.md b/packages/tool-cache/README.md index f45cedd8a6..d3da0c35f4 100644 --- a/packages/tool-cache/README.md +++ b/packages/tool-cache/README.md @@ -29,6 +29,10 @@ if (process.platform === 'win32') { const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.7z'); const node12ExtractedFolder = await tc.extract7z(node12Path, 'path/to/extract/to'); } +else if (process.platform === 'darwin') { + const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0.pkg'); + const node12ExtractedFolder = await tc.extractXar(node12Path, 'path/to/extract/to'); +} else { const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz'); const node12ExtractedFolder = await tc.extractTar(node12Path, 'path/to/extract/to'); diff --git "a/packages/tool-cache/__tests__/data/archive-content/file-with-\303\247-character.txt" "b/packages/tool-cache/__tests__/data/archive-content/file-with-\303\247-character.txt" new file mode 100644 index 0000000000..5803ea1514 --- /dev/null +++ "b/packages/tool-cache/__tests__/data/archive-content/file-with-\303\247-character.txt" @@ -0,0 +1 @@ +file-with--character.txt \ No newline at end of file diff --git a/packages/tool-cache/__tests__/data/archive-content/file.txt b/packages/tool-cache/__tests__/data/archive-content/file.txt new file mode 100644 index 0000000000..f2da98ad30 --- /dev/null +++ b/packages/tool-cache/__tests__/data/archive-content/file.txt @@ -0,0 +1 @@ +file.txt contents \ No newline at end of file diff --git a/packages/tool-cache/__tests__/data/archive-content/folder/nested-file.txt b/packages/tool-cache/__tests__/data/archive-content/folder/nested-file.txt new file mode 100644 index 0000000000..379224267a --- /dev/null +++ b/packages/tool-cache/__tests__/data/archive-content/folder/nested-file.txt @@ -0,0 +1 @@ +folder/nested-file.txt contents \ No newline at end of file diff --git a/packages/tool-cache/__tests__/tool-cache.test.ts b/packages/tool-cache/__tests__/tool-cache.test.ts index 7dc17c67f6..3722d18b91 100644 --- a/packages/tool-cache/__tests__/tool-cache.test.ts +++ b/packages/tool-cache/__tests__/tool-cache.test.ts @@ -15,6 +15,7 @@ process.env['RUNNER_TOOL_CACHE'] = cachePath import * as tc from '../src/tool-cache' const IS_WINDOWS = process.platform === 'win32' +const IS_MAC = process.platform === 'darwin' describe('@actions/tool-cache', function() { beforeAll(function() { @@ -346,6 +347,110 @@ describe('@actions/tool-cache', function() { await io.rmRF(tempDir) } }) + } else if (IS_MAC) { + it('extract .xar', async () => { + const tempDir = path.join(tempPath, 'test-install.xar') + const sourcePath = path.join(__dirname, 'data', 'archive-content') + const targetPath = path.join(tempDir, 'test.xar') + await io.mkdirP(tempDir) + + // Create test archive + const xarPath = await io.which('xar', true) + await exec.exec(`${xarPath}`, ['-cf', targetPath, '.'], { + cwd: sourcePath + }) + + // extract/cache + const extPath: string = await tc.extractXar(targetPath, undefined, '-x') + await tc.cacheDir(extPath, 'my-xar-contents', '1.1.0') + const toolPath: string = tc.find('my-xar-contents', '1.1.0') + + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + expect( + fs.readFileSync( + path.join(toolPath, 'folder', 'nested-file.txt'), + 'utf8' + ) + ).toBe('folder/nested-file.txt contents') + }) + + it('extract .xar to a directory that does not exist', async () => { + const tempDir = path.join(tempPath, 'test-install.xar') + const sourcePath = path.join(__dirname, 'data', 'archive-content') + const targetPath = path.join(tempDir, 'test.xar') + await io.mkdirP(tempDir) + + const destDir = path.join(tempDir, 'not-exist') + + // Create test archive + const xarPath = await io.which('xar', true) + await exec.exec(`${xarPath}`, ['-cf', targetPath, '.'], { + cwd: sourcePath + }) + + // extract/cache + const extPath: string = await tc.extractXar(targetPath, destDir, '-x') + await tc.cacheDir(extPath, 'my-xar-contents', '1.1.0') + const toolPath: string = tc.find('my-xar-contents', '1.1.0') + + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + expect( + fs.readFileSync( + path.join(toolPath, 'folder', 'nested-file.txt'), + 'utf8' + ) + ).toBe('folder/nested-file.txt contents') + }) + + it('extract .xar without flags', async () => { + const tempDir = path.join(tempPath, 'test-install.xar') + const sourcePath = path.join(__dirname, 'data', 'archive-content') + const targetPath = path.join(tempDir, 'test.xar') + await io.mkdirP(tempDir) + + // Create test archive + const xarPath = await io.which('xar', true) + await exec.exec(`${xarPath}`, ['-cf', targetPath, '.'], { + cwd: sourcePath + }) + + // extract/cache + const extPath: string = await tc.extractXar(targetPath, undefined) + await tc.cacheDir(extPath, 'my-xar-contents', '1.1.0') + const toolPath: string = tc.find('my-xar-contents', '1.1.0') + + expect(fs.existsSync(toolPath)).toBeTruthy() + expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy() + expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt')) + ).toBeTruthy() + expect( + fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt')) + ).toBeTruthy() + expect( + fs.readFileSync( + path.join(toolPath, 'folder', 'nested-file.txt'), + 'utf8' + ) + ).toBe('folder/nested-file.txt contents') + }) } it('extract .tar.gz', async () => { diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts index 385569ca14..3e9ecab409 100644 --- a/packages/tool-cache/src/tool-cache.ts +++ b/packages/tool-cache/src/tool-cache.ts @@ -23,6 +23,7 @@ export class HTTPError extends Error { } const IS_WINDOWS = process.platform === 'win32' +const IS_MAC = process.platform === 'darwin' const userAgent = 'actions/tool-cache' /** @@ -276,6 +277,43 @@ export async function extractTar( return dest } +/** + * Extract a xar compatible archive + * + * @param file path to the archive + * @param dest destination directory. Optional. + * @param flags flags for the xar. Optional. + * @returns path to the destination directory + */ +export async function extractXar( + file: string, + dest?: string, + flags: string | string[] = [] +): Promise { + ok(IS_MAC, 'extractXar() not supported on current OS') + ok(file, 'parameter "file" is required') + + dest = await _createExtractFolder(dest) + + let args: string[] + if (flags instanceof Array) { + args = flags + } else { + args = [flags] + } + + args.push('-x', '-C', dest, '-f', file) + + if (core.isDebug()) { + args.push('-v') + } + + const xarPath: string = await io.which('xar', true) + await exec(`"${xarPath}"`, _unique(args)) + + return dest +} + /** * Extract a zip * @@ -675,3 +713,11 @@ function _getGlobal(key: string, defaultValue: T): T { /* eslint-enable @typescript-eslint/no-explicit-any */ return value !== undefined ? value : defaultValue } + +/** + * Returns an array of unique values. + * @param values Values to make unique. + */ +function _unique(values: T[]): T[] { + return Array.from(new Set(values)) +} From 32f15666bd7e024932ea0dcf2e9827c628ab05e9 Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Thu, 16 Jul 2020 11:51:00 -0400 Subject: [PATCH 183/192] Fix Issue where we can no longer create zip files on windows during cli tests. (#520) * Try using pwsh instead of powershell * Fallback to powershell * Format files correctly --- packages/tool-cache/__tests__/tool-cache.test.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/tool-cache/__tests__/tool-cache.test.ts b/packages/tool-cache/__tests__/tool-cache.test.ts index 3722d18b91..d03c9b392c 100644 --- a/packages/tool-cache/__tests__/tool-cache.test.ts +++ b/packages/tool-cache/__tests__/tool-cache.test.ts @@ -553,7 +553,9 @@ describe('@actions/tool-cache', function() { if (IS_WINDOWS) { const escapedStagingPath = stagingDir.replace(/'/g, "''") // double-up single quotes const escapedZipFile = zipFile.replace(/'/g, "''") - const powershellPath = await io.which('powershell', true) + const powershellPath = + (await io.which('pwsh', false)) || + (await io.which('powershell', true)) const args = [ '-NoLogo', '-Sta', @@ -604,7 +606,9 @@ describe('@actions/tool-cache', function() { if (IS_WINDOWS) { const escapedStagingPath = stagingDir.replace(/'/g, "''") // double-up single quotes const escapedZipFile = zipFile.replace(/'/g, "''") - const powershellPath = await io.which('powershell', true) + const powershellPath = + (await io.which('pwsh', false)) || + (await io.which('powershell', true)) const args = [ '-NoLogo', '-Sta', @@ -661,7 +665,9 @@ describe('@actions/tool-cache', function() { if (IS_WINDOWS) { const escapedStagingPath = stagingDir.replace(/'/g, "''") // double-up single quotes const escapedZipFile = zipFile.replace(/'/g, "''") - const powershellPath = await io.which('powershell', true) + const powershellPath = + (await io.which('pwsh', false)) || + (await io.which('powershell', true)) const args = [ '-NoLogo', '-Sta', From 8f6ddeb087c8c7df1931ae2a48ddd54cebdf471b Mon Sep 17 00:00:00 2001 From: Thomas Boop <52323235+thboop@users.noreply.github.com> Date: Thu, 16 Jul 2020 15:46:02 -0400 Subject: [PATCH 184/192] Tool cache 1.6.0 Release Notes (#519) * Tool-cache 1.6.0 release notes * Adjust spacing --- packages/tool-cache/RELEASES.md | 3 +++ packages/tool-cache/package-lock.json | 2 +- packages/tool-cache/package.json | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/tool-cache/RELEASES.md b/packages/tool-cache/RELEASES.md index 8a10023bfc..9dd4c9c43c 100644 --- a/packages/tool-cache/RELEASES.md +++ b/packages/tool-cache/RELEASES.md @@ -1,5 +1,8 @@ # @actions/tool-cache Releases +### 1.6.0 +- [Add extractXar function to extract XAR files](https://github.com/actions/toolkit/pull/207) + ### 1.3.5 - [Check if tool path exists before executing](https://github.com/actions/toolkit/pull/385) diff --git a/packages/tool-cache/package-lock.json b/packages/tool-cache/package-lock.json index 2dd920bc0c..6012112dab 100644 --- a/packages/tool-cache/package-lock.json +++ b/packages/tool-cache/package-lock.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.5.5", + "version": "1.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json index c16195ba1c..2a6cca2fe0 100644 --- a/packages/tool-cache/package.json +++ b/packages/tool-cache/package.json @@ -1,6 +1,6 @@ { "name": "@actions/tool-cache", - "version": "1.5.5", + "version": "1.6.0", "description": "Actions tool-cache lib", "keywords": [ "github", From b152907c1fe4eab0575e0f85db8c0ecd04248b92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2020 15:54:18 -0400 Subject: [PATCH 185/192] Bump lodash from 4.17.15 to 4.17.19 in /packages/github (#522) Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- packages/github/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index 725cabd903..1ebf3e15a4 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -2963,9 +2963,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "lodash.sortby": { From 0bf9897205aa22619fd765a7f927983aae8f3d82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2020 16:56:22 -0400 Subject: [PATCH 186/192] Bump lodash from 4.17.15 to 4.17.19 (#524) Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- packages/tool-cache/package-lock.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 87069cb9a0..b3f83689fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16341,9 +16341,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "lodash._reinterpolate": { diff --git a/packages/tool-cache/package-lock.json b/packages/tool-cache/package-lock.json index 6012112dab..115583e664 100644 --- a/packages/tool-cache/package-lock.json +++ b/packages/tool-cache/package-lock.json @@ -123,9 +123,9 @@ "dev": true }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "dev": true }, "minimist": { From 1cc56db0ff126f4d65aeb83798852e02a2c180c3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2020 10:38:14 -0400 Subject: [PATCH 187/192] Update Dependencies (#526) Co-authored-by: github-actions[bot] --- packages/github/package-lock.json | 116 +++++++++++++++--------------- packages/github/package.json | 4 +- 2 files changed, 61 insertions(+), 59 deletions(-) diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json index 1ebf3e15a4..39c99bbd2c 100644 --- a/packages/github/package-lock.json +++ b/packages/github/package-lock.json @@ -465,36 +465,36 @@ } }, "@octokit/core": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.1.0.tgz", - "integrity": "sha512-yPyQSmxIXLieEIRikk2w8AEtWkFdfG/LXcw1KvEtK3iP0ENZLW/WYQmdzOKqfSaLhooz4CJ9D+WY79C8ZliACw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.1.1.tgz", + "integrity": "sha512-cQ2HGrtyNJ1IBxpTP1U5m/FkMAJvgw7d2j1q3c9P0XUuYilEgF6e4naTpsgm4iVcQeOnccZlw7XHRIUBy0ymcg==", "requires": { "@octokit/auth-token": "^2.4.0", "@octokit/graphql": "^4.3.1", "@octokit/request": "^5.4.0", "@octokit/types": "^5.0.0", "before-after-hook": "^2.1.0", - "universal-user-agent": "^5.0.0" + "universal-user-agent": "^6.0.0" } }, "@octokit/endpoint": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.3.tgz", - "integrity": "sha512-Y900+r0gIz+cWp6ytnkibbD95ucEzDSKzlEnaWS52hbCDNcCJYO5mRmWW7HRAnDc7am+N/5Lnd8MppSaTYx1Yg==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.4.tgz", + "integrity": "sha512-ZJHIsvsClEE+6LaZXskDvWIqD3Ao7+2gc66pRG5Ov4MQtMvCU9wGu1TItw9aGNmRuU9x3Fei1yb+uqGaQnm0nw==", "requires": { "@octokit/types": "^5.0.0", "is-plain-object": "^3.0.0", - "universal-user-agent": "^5.0.0" + "universal-user-agent": "^6.0.0" } }, "@octokit/graphql": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.1.tgz", - "integrity": "sha512-qgMsROG9K2KxDs12CO3bySJaYoUu2aic90qpFrv7A8sEBzZ7UFGvdgPKiLw5gOPYEYbS0Xf8Tvf84tJutHPulQ==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.2.tgz", + "integrity": "sha512-SpB/JGdB7bxRj8qowwfAXjMpICUYSJqRDj26MKJAryRQBqp/ZzARsaO2LEFWzDaps0FLQoPYVGppS0HQXkBhdg==", "requires": { "@octokit/request": "^5.3.0", "@octokit/types": "^5.0.0", - "universal-user-agent": "^5.0.0" + "universal-user-agent": "^6.0.0" } }, "@octokit/plugin-paginate-rest": { @@ -506,18 +506,28 @@ } }, "@octokit/plugin-rest-endpoint-methods": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.0.0.tgz", - "integrity": "sha512-emS6gysz4E9BNi9IrCl7Pm4kR+Az3MmVB0/DoDCmF4U48NbYG3weKyDlgkrz6Jbl4Mu4nDx8YWZwC4HjoTdcCA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.1.0.tgz", + "integrity": "sha512-zbRTjm+xplSNlixotTVMvLJe8aRogUXS+r37wZK5EjLsNYH4j02K5XLMOWyYaSS4AJEZtPmzCcOcui4VzVGq+A==", "requires": { - "@octokit/types": "^5.0.0", + "@octokit/types": "^5.1.0", "deprecation": "^2.3.1" + }, + "dependencies": { + "@octokit/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.1.0.tgz", + "integrity": "sha512-OFxUBgrEllAbdEmWp/wNmKIu5EuumKHG4sgy56vjZ8lXPgMhF05c76hmulfOdFHHYRpPj49ygOZJ8wgVsPecuA==", + "requires": { + "@types/node": ">= 8" + } + } } }, "@octokit/request": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.5.tgz", - "integrity": "sha512-atAs5GAGbZedvJXXdjtKljin+e2SltEs48B3naJjqWupYl2IUBbB/CJisyjbNHcKpHzb3E+OYEZ46G8eakXgQg==", + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.6.tgz", + "integrity": "sha512-9r8Sn4CvqFI9LDLHl9P17EZHwj3ehwQnTpTE+LEneb0VBBqSiI/VS4rWIBfBhDrDs/aIGEGZRSB0QWAck8u+2g==", "requires": { "@octokit/endpoint": "^6.0.1", "@octokit/request-error": "^2.0.0", @@ -526,7 +536,7 @@ "is-plain-object": "^3.0.0", "node-fetch": "^2.3.0", "once": "^1.4.0", - "universal-user-agent": "^5.0.0" + "universal-user-agent": "^6.0.0" } }, "@octokit/request-error": { @@ -1271,6 +1281,7 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, "requires": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -1457,6 +1468,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, "requires": { "once": "^1.4.0" } @@ -1538,6 +1550,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, "requires": { "cross-spawn": "^6.0.0", "get-stream": "^4.0.0", @@ -1844,6 +1857,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, "requires": { "pump": "^3.0.0" } @@ -2216,7 +2230,8 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true }, "is-symbol": { "version": "1.0.3", @@ -2255,7 +2270,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "isstream": { "version": "0.1.2", @@ -2983,11 +2999,6 @@ "@sinonjs/commons": "^1.7.0" } }, - "macos-release": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.4.0.tgz", - "integrity": "sha512-ko6deozZYiAkqa/0gmcsz+p4jSy3gY7/ZsCEokPaYd8k+6/aXGkiTgr61+Owup7Sf+xjqW8u2ElhoM9SEcEfuA==" - }, "make-dir": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", @@ -3168,7 +3179,8 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true }, "node-fetch": { "version": "2.6.0", @@ -3220,6 +3232,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, "requires": { "path-key": "^2.0.0" } @@ -3366,15 +3379,6 @@ "word-wrap": "~1.2.3" } }, - "os-name": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", - "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", - "requires": { - "macos-release": "^2.2.0", - "windows-release": "^3.1.0" - } - }, "p-each-series": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", @@ -3384,7 +3388,8 @@ "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true }, "p-limit": { "version": "2.2.2", @@ -3437,7 +3442,8 @@ "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true }, "path-parse": { "version": "1.0.6", @@ -3559,6 +3565,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -3934,7 +3941,8 @@ "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true }, "set-blocking": { "version": "2.0.0", @@ -3984,6 +3992,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, "requires": { "shebang-regex": "^1.0.0" } @@ -3991,7 +4000,8 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true }, "shellwords": { "version": "0.1.1", @@ -4003,7 +4013,8 @@ "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true }, "sisteransi": { "version": "1.0.4", @@ -4336,7 +4347,8 @@ "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true }, "strip-final-newline": { "version": "2.0.0", @@ -4532,12 +4544,9 @@ } }, "universal-user-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-5.0.0.tgz", - "integrity": "sha512-B5TPtzZleXyPrUMKCpEHFmVhMN6EhmJYjG5PQna9s7mXeSqGTLap4OpqLl5FCEFUI3UBmllkETwKf/db66Y54Q==", - "requires": { - "os-name": "^3.1.0" - } + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" }, "unset-value": { "version": "1.0.0", @@ -4719,6 +4728,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -4729,14 +4739,6 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "windows-release": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.1.tgz", - "integrity": "sha512-Pngk/RDCaI/DkuHPlGTdIkDiTAnAkyMjoQMZqRsxydNl1qGXNIoZrB7RK8g53F2tEgQBMqQJHQdYZuQEEAu54A==", - "requires": { - "execa": "^1.0.0" - } - }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", diff --git a/packages/github/package.json b/packages/github/package.json index 3d93545b1b..c7f899317e 100644 --- a/packages/github/package.json +++ b/packages/github/package.json @@ -39,9 +39,9 @@ }, "dependencies": { "@actions/http-client": "^1.0.8", - "@octokit/core": "^3.1.0", + "@octokit/core": "^3.1.1", "@octokit/plugin-paginate-rest": "^2.2.3", - "@octokit/plugin-rest-endpoint-methods": "^4.0.0" + "@octokit/plugin-rest-endpoint-methods": "^4.1.0" }, "devDependencies": { "jest": "^25.1.0", From 4bf916289e5e32bb7d1bd7f21842c3afeab3b25a Mon Sep 17 00:00:00 2001 From: madhead Date: Fri, 19 Feb 2021 18:02:58 +0300 Subject: [PATCH 188/192] =?UTF-8?q?master=20=E2=86=92=20main=20(#596)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/action-versioning.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/action-versioning.md b/docs/action-versioning.md index 4a8e816897..39a9be98a4 100644 --- a/docs/action-versioning.md +++ b/docs/action-versioning.md @@ -17,13 +17,13 @@ Binding to a major version is the latest of that major version ( e.g. `v1` == "1 Major versions should guarantee compatibility. A major version can add net new capabilities but should not break existing input compatibility or break existing workflows. -Major version binding allows you to take advantage of bug fixes and critical functionality and security fixes. The `master` branch has the latest code and is unstable to bind to since changes get committed to master and released to the market place by creating a tag. In addition, a new major version carrying breaking changes will get implemented in master after branching off the previous major version. +Major version binding allows you to take advantage of bug fixes and critical functionality and security fixes. The `main` branch has the latest code and is unstable to bind to since changes get committed to `main` and released to the market place by creating a tag. In addition, a new major version carrying breaking changes will get implemented in `main` after branching off the previous major version. -> Warning: do not reference `master` since that is the latest code and can be carrying breaking changes of the next major version. +> Warning: do not reference `main` since that is the latest code and can be carrying breaking changes of the next major version. ```yaml steps: - - uses: actions/javascript-action@master # do not do this + - uses: actions/javascript-action@main # do not do this ``` Binding to the immutable full sha1 may offer more reliability. However, note that the hosted images toolsets (e.g. ubuntu-latest) move forward and if there is a tool breaking issue, actions may react with a patch to a major version to compensate so binding to a specific SHA may prevent you from getting fixes. From e9c6ee99a59ec34586d4374b68efe5cabfcee9db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20Unneb=C3=A4ck?= Date: Wed, 28 Apr 2021 20:24:23 +0200 Subject: [PATCH 189/192] Simplify mkdirP implementation (#444) --- packages/io/__tests__/io.test.ts | 26 ------------------ packages/io/src/io-util.ts | 47 -------------------------------- packages/io/src/io.ts | 4 ++- 3 files changed, 3 insertions(+), 74 deletions(-) diff --git a/packages/io/__tests__/io.test.ts b/packages/io/__tests__/io.test.ts index 76d07f5b17..714716e784 100644 --- a/packages/io/__tests__/io.test.ts +++ b/packages/io/__tests__/io.test.ts @@ -3,7 +3,6 @@ import {promises as fs} from 'fs' import * as os from 'os' import * as path from 'path' import * as io from '../src/io' -import * as ioUtil from '../src/io-util' describe('cp', () => { it('copies file with no flags', async () => { @@ -813,31 +812,6 @@ describe('mkdirP', () => { (await fs.lstat(path.join(realDirPath, 'sub_dir'))).isDirectory() ).toBe(true) }) - - it('breaks if mkdirP loop out of control', async () => { - const testPath = path.join( - getTestTemp(), - 'mkdirP_failsafe', - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10' - ) - - expect.assertions(1) - - try { - await ioUtil.mkdirP(testPath, 10) - } catch (err) { - expect(err.code).toBe('ENOENT') - } - }) }) describe('which', () => { diff --git a/packages/io/src/io-util.ts b/packages/io/src/io-util.ts index f97752178a..98d7042923 100644 --- a/packages/io/src/io-util.ts +++ b/packages/io/src/io-util.ts @@ -1,4 +1,3 @@ -import {ok} from 'assert' import * as fs from 'fs' import * as path from 'path' @@ -59,52 +58,6 @@ export function isRooted(p: string): boolean { return p.startsWith('/') } -/** - * Recursively create a directory at `fsPath`. - * - * This implementation is optimistic, meaning it attempts to create the full - * path first, and backs up the path stack from there. - * - * @param fsPath The path to create - * @param maxDepth The maximum recursion depth - * @param depth The current recursion depth - */ -export async function mkdirP( - fsPath: string, - maxDepth: number = 1000, - depth: number = 1 -): Promise { - ok(fsPath, 'a path argument must be provided') - - fsPath = path.resolve(fsPath) - - if (depth >= maxDepth) return mkdir(fsPath) - - try { - await mkdir(fsPath) - return - } catch (err) { - switch (err.code) { - case 'ENOENT': { - await mkdirP(path.dirname(fsPath), maxDepth, depth + 1) - await mkdir(fsPath) - return - } - default: { - let stats: fs.Stats - - try { - stats = await stat(fsPath) - } catch (err2) { - throw err - } - - if (!stats.isDirectory()) throw err - } - } - } -} - /** * Best effort attempt to determine whether a file exists and is executable. * @param filePath file path to check diff --git a/packages/io/src/io.ts b/packages/io/src/io.ts index d6649945f9..870ab74108 100644 --- a/packages/io/src/io.ts +++ b/packages/io/src/io.ts @@ -1,3 +1,4 @@ +import {ok} from 'assert' import * as childProcess from 'child_process' import * as path from 'path' import {promisify} from 'util' @@ -161,7 +162,8 @@ export async function rmRF(inputPath: string): Promise { * @returns Promise */ export async function mkdirP(fsPath: string): Promise { - await ioUtil.mkdirP(fsPath) + ok(fsPath, 'a path argument must be provided') + await ioUtil.mkdir(fsPath, {recursive: true}) } /** From dc8e2904053bb2030162c6a6e4df4fce14dd18bd Mon Sep 17 00:00:00 2001 From: Federico Grandi Date: Mon, 24 May 2021 16:23:40 +0200 Subject: [PATCH 190/192] docs(README): fix minor formatting issue (#819) --- packages/core/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/README.md b/packages/core/README.md index 95428cf33d..c1a16abdd2 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -62,11 +62,10 @@ catch (err) { // setFailed logs the message and sets a failing exit code core.setFailed(`Action failed with error ${err}`); } +``` Note that `setNeutral` is not yet implemented in actions V2 but equivalent functionality is being planned. -``` - #### Logging Finally, this library provides some utilities for logging. Note that debug logging is hidden from the logs by default. This behavior can be toggled by enabling the [Step Debug Logs](../../docs/action-debugging.md#step-debug-logs). From 2f164000dcd42fb08287824a3bc3030dbed33687 Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Tue, 25 May 2021 14:28:45 -0400 Subject: [PATCH 191/192] Fix debug logging link (#820) --- docs/problem-matchers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/problem-matchers.md b/docs/problem-matchers.md index f88678494d..eea1003626 100644 --- a/docs/problem-matchers.md +++ b/docs/problem-matchers.md @@ -124,6 +124,6 @@ Use ECMAScript regular expression syntax when testing patterns. ### File property getting dropped -[Enable debug logging](https://help.github.com/en/actions/configuring-and-managing-workflows/managing-a-workflow-run#enabling-debug-logging) to determine why the file is getting dropped. +[Enable debug logging](https://docs.github.com/en/actions/managing-workflow-runs/enabling-debug-logging) to determine why the file is getting dropped. This usually happens when the file does not exist or is not under the workflow repo. From 74906bea83a0dbf6aaba2d00b732deb0c3aefd2d Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 11 Jan 2023 19:09:01 +0000 Subject: [PATCH 192/192] Add documentation for notice (#1105) * Add documentation for notice This is described in documentation elsewhere. * Update docs/commands.md Co-authored-by: Konrad Pabjan Co-authored-by: Konrad Pabjan --- docs/commands.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/commands.md b/docs/commands.md index 0edf2f0767..8913fd1d00 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -129,9 +129,12 @@ There are several commands to emit different levels of log output: | log level | example usage | |---|---| | [debug](action-debugging.md) | `echo "::debug::My debug message"` | +| notice | `echo "::notice::My notice message"` | | warning | `echo "::warning::My warning message"` | | error | `echo "::error::My error message"` | +Additional syntax options are described at [the workflow command documentation](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-debug-message). + ### Command Echoing By default, the echoing of commands to stdout only occurs if [Step Debugging is enabled](./action-debugging.md#How-to-Access-Step-Debug-Logs)